Fix link verification for remote accounts (#8868)
parent
49b182cd51
commit
7fe137d2f7
|
@ -312,8 +312,8 @@ class Account < ApplicationRecord
|
||||||
def initialize(account, attributes)
|
def initialize(account, attributes)
|
||||||
@account = account
|
@account = account
|
||||||
@attributes = attributes
|
@attributes = attributes
|
||||||
@name = attributes['name'].strip[0, 255]
|
@name = attributes['name'].strip[0, string_limit]
|
||||||
@value = attributes['value'].strip[0, 255]
|
@value = attributes['value'].strip[0, string_limit]
|
||||||
@verified_at = attributes['verified_at']&.to_datetime
|
@verified_at = attributes['verified_at']&.to_datetime
|
||||||
@errors = {}
|
@errors = {}
|
||||||
end
|
end
|
||||||
|
@ -322,8 +322,18 @@ class Account < ApplicationRecord
|
||||||
verified_at.present?
|
verified_at.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def value_for_verification
|
||||||
|
@value_for_verification ||= begin
|
||||||
|
if account.local?
|
||||||
|
value
|
||||||
|
else
|
||||||
|
ActionController::Base.helpers.strip_tags(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def verifiable?
|
def verifiable?
|
||||||
value.present? && value.start_with?('http://', 'https://')
|
value_for_verification.present? && value_for_verification.start_with?('http://', 'https://')
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_verified!
|
def mark_verified!
|
||||||
|
@ -334,6 +344,16 @@ class Account < ApplicationRecord
|
||||||
def to_h
|
def to_h
|
||||||
{ name: @name, value: @value, verified_at: @verified_at }
|
{ name: @name, value: @value, verified_at: @verified_at }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def string_limit
|
||||||
|
if account.local?
|
||||||
|
255
|
||||||
|
else
|
||||||
|
2047
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
|
@ -11,11 +11,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
||||||
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
||||||
|
|
||||||
class FieldSerializer < ActiveModel::Serializer
|
class FieldSerializer < ActiveModel::Serializer
|
||||||
attributes :name, :value
|
attributes :name, :value, :verified_at
|
||||||
|
|
||||||
attribute :verified_at, if: :verifiable?
|
|
||||||
|
|
||||||
delegate :verifiable?, to: :object
|
|
||||||
|
|
||||||
def value
|
def value
|
||||||
Formatter.instance.format_field(object.account, object.value)
|
Formatter.instance.format_field(object.account, object.value)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class VerifyLinkService < BaseService
|
class VerifyLinkService < BaseService
|
||||||
def call(field)
|
def call(field)
|
||||||
@link_back = ActivityPub::TagManager.instance.url_for(field.account)
|
@link_back = ActivityPub::TagManager.instance.url_for(field.account)
|
||||||
@url = field.value
|
@url = field.value_for_verification
|
||||||
|
|
||||||
perform_request!
|
perform_request!
|
||||||
|
|
||||||
|
|
|
@ -3,80 +3,107 @@ require 'rails_helper'
|
||||||
RSpec.describe VerifyLinkService, type: :service do
|
RSpec.describe VerifyLinkService, type: :service do
|
||||||
subject { described_class.new }
|
subject { described_class.new }
|
||||||
|
|
||||||
let(:account) { Fabricate(:account, username: 'alice') }
|
context 'given a local account' do
|
||||||
let(:field) { Account::Field.new(account, 'name' => 'Website', 'value' => 'http://example.com') }
|
let(:account) { Fabricate(:account, username: 'alice') }
|
||||||
|
let(:field) { Account::Field.new(account, 'name' => 'Website', 'value' => 'http://example.com') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_request(:head, 'https://redirect.me/abc').to_return(status: 301, headers: { 'Location' => ActivityPub::TagManager.instance.url_for(account) })
|
stub_request(:head, 'https://redirect.me/abc').to_return(status: 301, headers: { 'Location' => ActivityPub::TagManager.instance.url_for(account) })
|
||||||
stub_request(:get, 'http://example.com').to_return(status: 200, body: html)
|
stub_request(:get, 'http://example.com').to_return(status: 200, body: html)
|
||||||
subject.call(field)
|
subject.call(field)
|
||||||
end
|
|
||||||
|
|
||||||
context 'when a link contains an <a> back' do
|
|
||||||
let(:html) do
|
|
||||||
<<-HTML
|
|
||||||
<!doctype html>
|
|
||||||
<body>
|
|
||||||
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me">Follow me on Mastodon</a>
|
|
||||||
</body>
|
|
||||||
HTML
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'marks the field as verified' do
|
context 'when a link contains an <a> back' do
|
||||||
expect(field.verified?).to be true
|
let(:html) do
|
||||||
|
<<-HTML
|
||||||
|
<!doctype html>
|
||||||
|
<body>
|
||||||
|
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me">Follow me on Mastodon</a>
|
||||||
|
</body>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'marks the field as verified' do
|
||||||
|
expect(field.verified?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a link contains an <a rel="noopener"> back' do
|
||||||
|
let(:html) do
|
||||||
|
<<-HTML
|
||||||
|
<!doctype html>
|
||||||
|
<body>
|
||||||
|
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="noopener me" target="_blank">Follow me on Mastodon</a>
|
||||||
|
</body>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'marks the field as verified' do
|
||||||
|
expect(field.verified?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a link contains a <link> back' do
|
||||||
|
let(:html) do
|
||||||
|
<<-HTML
|
||||||
|
<!doctype html>
|
||||||
|
<head>
|
||||||
|
<link type="text/html" href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me" />
|
||||||
|
</head>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'marks the field as verified' do
|
||||||
|
expect(field.verified?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a link goes through a redirect back' do
|
||||||
|
let(:html) do
|
||||||
|
<<-HTML
|
||||||
|
<!doctype html>
|
||||||
|
<head>
|
||||||
|
<link type="text/html" href="https://redirect.me/abc" rel="me" />
|
||||||
|
</head>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'marks the field as verified' do
|
||||||
|
expect(field.verified?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a link does not contain a link back' do
|
||||||
|
let(:html) { '' }
|
||||||
|
|
||||||
|
it 'marks the field as verified' do
|
||||||
|
expect(field.verified?).to be false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when a link contains an <a rel="noopener"> back' do
|
context 'given a remote account' do
|
||||||
let(:html) do
|
let(:account) { Fabricate(:account, username: 'alice', domain: 'example.com', url: 'https://profile.example.com/alice') }
|
||||||
<<-HTML
|
let(:field) { Account::Field.new(account, 'name' => 'Website', 'value' => '<a href="http://example.com" rel="me"><span class="invisible">http://</span><span class="">example.com</span><span class="invisible"></span></a>') }
|
||||||
<!doctype html>
|
|
||||||
<body>
|
before do
|
||||||
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="noopener me" target="_blank">Follow me on Mastodon</a>
|
stub_request(:get, 'http://example.com').to_return(status: 200, body: html)
|
||||||
</body>
|
subject.call(field)
|
||||||
HTML
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'marks the field as verified' do
|
context 'when a link contains an <a> back' do
|
||||||
expect(field.verified?).to be true
|
let(:html) do
|
||||||
end
|
<<-HTML
|
||||||
end
|
<!doctype html>
|
||||||
|
<body>
|
||||||
|
<a href="https://profile.example.com/alice" rel="me">Follow me on Mastodon</a>
|
||||||
|
</body>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
|
||||||
context 'when a link contains a <link> back' do
|
it 'marks the field as verified' do
|
||||||
let(:html) do
|
expect(field.verified?).to be true
|
||||||
<<-HTML
|
end
|
||||||
<!doctype html>
|
|
||||||
<head>
|
|
||||||
<link type="text/html" href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me" />
|
|
||||||
</head>
|
|
||||||
HTML
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'marks the field as verified' do
|
|
||||||
expect(field.verified?).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when a link goes through a redirect back' do
|
|
||||||
let(:html) do
|
|
||||||
<<-HTML
|
|
||||||
<!doctype html>
|
|
||||||
<head>
|
|
||||||
<link type="text/html" href="https://redirect.me/abc" rel="me" />
|
|
||||||
</head>
|
|
||||||
HTML
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'marks the field as verified' do
|
|
||||||
expect(field.verified?).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when a link does not contain a link back' do
|
|
||||||
let(:html) { '' }
|
|
||||||
|
|
||||||
it 'marks the field as verified' do
|
|
||||||
expect(field.verified?).to be false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue