Fix IDN mentions not being processed, IDN domains not being rendered (#12715)

This changes the REST API to return unicode domains in the `acct`
attribute instead of punycode, and to render unicode instead of
punycode on public HTML pages as well.

Fix #7812, fix #12246
This commit is contained in:
Eugen Rochko 2019-12-30 19:20:43 +01:00 committed by GitHub
parent b2f81060b7
commit f86ee4b59f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 19 deletions

View File

@ -13,7 +13,7 @@ module AccountsHelper
if account.local? if account.local?
"@#{account.acct}@#{Rails.configuration.x.local_domain}" "@#{account.acct}@#{Rails.configuration.x.local_domain}"
else else
"@#{account.acct}" "@#{account.pretty_acct}"
end end
end end

View File

@ -50,7 +50,7 @@
class Account < ApplicationRecord class Account < ApplicationRecord
USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i
MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[a-z0-9]+)?)/i
include AccountAssociations include AccountAssociations
include AccountAvatar include AccountAvatar
@ -164,6 +164,10 @@ class Account < ApplicationRecord
local? ? username : "#{username}@#{domain}" local? ? username : "#{username}@#{domain}"
end end
def pretty_acct
local? ? username : "#{username}@#{Addressable::IDNA.to_unicode(domain)}"
end
def local_username_and_domain def local_username_and_domain
"#{username}@#{Rails.configuration.x.local_domain}" "#{username}@#{Rails.configuration.x.local_domain}"
end end

View File

@ -24,6 +24,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
object.id.to_s object.id.to_s
end end
def acct
object.pretty_acct
end
def note def note
Formatter.instance.simplified_format(object) Formatter.instance.simplified_format(object)
end end

View File

@ -14,7 +14,16 @@ class ProcessMentionsService < BaseService
mentions = [] mentions = []
status.text = status.text.gsub(Account::MENTION_RE) do |match| status.text = status.text.gsub(Account::MENTION_RE) do |match|
username, domain = Regexp.last_match(1).split('@') username, domain = Regexp.last_match(1).split('@')
domain = begin
if TagManager.instance.local_domain?(domain)
nil
else
TagManager.instance.normalize_domain(domain)
end
end
mentioned_account = Account.find_remote(username, domain) mentioned_account = Account.find_remote(username, domain)
if mention_undeliverable?(mentioned_account) if mention_undeliverable?(mentioned_account)

View File

@ -5,11 +5,11 @@ RSpec.describe ProcessMentionsService, type: :service do
let(:visibility) { :public } let(:visibility) { :public }
let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) } let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) }
subject { ProcessMentionsService.new }
context 'OStatus with public toot' do context 'OStatus with public toot' do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') } let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
subject { ProcessMentionsService.new }
before do before do
stub_request(:post, remote_user.salmon_url) stub_request(:post, remote_user.salmon_url)
subject.call(status) subject.call(status)
@ -24,8 +24,6 @@ RSpec.describe ProcessMentionsService, type: :service do
let(:visibility) { :private } let(:visibility) { :private }
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') } let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
subject { ProcessMentionsService.new }
before do before do
stub_request(:post, remote_user.salmon_url) stub_request(:post, remote_user.salmon_url)
subject.call(status) subject.call(status)
@ -41,29 +39,45 @@ RSpec.describe ProcessMentionsService, type: :service do
end end
context 'ActivityPub' do context 'ActivityPub' do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } context do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
subject { ProcessMentionsService.new } before do
stub_request(:post, remote_user.inbox_url)
subject.call(status)
end
before do it 'creates a mention' do
stub_request(:post, remote_user.inbox_url) expect(remote_user.mentions.where(status: status).count).to eq 1
subject.call(status) end
it 'sends activity to the inbox' do
expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
end
end end
it 'creates a mention' do context 'with an IDN domain' do
expect(remote_user.mentions.where(status: status).count).to eq 1 let(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
end let(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
it 'sends activity to the inbox' do before do
expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once stub_request(:post, remote_user.inbox_url)
subject.call(status)
end
it 'creates a mention' do
expect(remote_user.mentions.where(status: status).count).to eq 1
end
it 'sends activity to the inbox' do
expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
end
end end
end end
context 'Temporarily-unreachable ActivityPub user' do context 'Temporarily-unreachable ActivityPub user' do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) } let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
subject { ProcessMentionsService.new }
before do before do
stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404) stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500) stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)