1
0
mirror of https://github.com/mastodon/mastodon synced 2025-01-10 20:03:22 +09:00

Add support for numeric-based URIs for local accounts

Actors would be served at `/ap/users/:user_id` and statuses at `/ap/statuses/:id`
This commit is contained in:
Claire 2024-10-31 16:54:41 +01:00
parent 6afeb6e443
commit 3ffd0648df
7 changed files with 93 additions and 26 deletions

View File

@ -71,6 +71,10 @@ class AccountsController < ApplicationController
params[:username] params[:username]
end end
def account_id_param
params[:id]
end
def skip_temporary_suspension_response? def skip_temporary_suspension_response?
request.format == :json request.format == :json
end end

View File

@ -18,7 +18,11 @@ module AccountOwnedConcern
end end
def set_account def set_account
@account = Account.find_local!(username_param) @account = username_param.present? ? Account.find_local!(username_param) : Account.local.find(account_id_param)
end
def account_id_param
params[:numeric_account_id]
end end
def username_param def username_param

View File

@ -38,11 +38,23 @@ class ActivityPub::TagManager
case target.object_type case target.object_type
when :person when :person
target.instance_actor? ? instance_actor_url : account_url(target) if target.instance_actor?
instance_actor_url
elsif target.numeric_ap_id?
ap_account_url(target.id)
else
account_url(target)
end
when :note, :comment, :activity when :note, :comment, :activity
if target.account.numeric_ap_id?
return activity_ap_status_url(target.account, target) if target.reblog?
ap_status_url(target)
else
return activity_account_status_url(target.account, target) if target.reblog? return activity_account_status_url(target.account, target) if target.reblog?
account_status_url(target.account, target) account_status_url(target.account, target)
end
when :emoji when :emoji
emoji_url(target) emoji_url(target)
when :flag when :flag
@ -58,6 +70,10 @@ class ActivityPub::TagManager
account_url(username: username) account_url(username: username)
end end
def uri_for_account_id(id)
ap_account_url(id: id)
end
def generate_uri_for(_target) def generate_uri_for(_target)
URI.join(root_url, 'payloads', SecureRandom.uuid) URI.join(root_url, 'payloads', SecureRandom.uuid)
end end
@ -65,55 +81,67 @@ class ActivityPub::TagManager
def activity_uri_for(target) def activity_uri_for(target)
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local? raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
activity_account_status_url(target.account, target) target.account.numeric_ap_id? ? activity_ap_status_url(target) : activity_account_status_url(target.account, target)
end end
def replies_uri_for(target, page_params = nil) def replies_uri_for(target, page_params = nil)
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local? raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
account_status_replies_url(target.account, target, page_params) target.account.numeric_ap_id? ? ap_status_replies_url(target, page_params) : account_status_replies_url(target.account, target, page_params)
end end
def likes_uri_for(target) def likes_uri_for(target)
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local? raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
account_status_likes_url(target.account, target) target.account.numeric_ap_id? ? ap_status_likes_url(target) : account_status_likes_url(target.account, target)
end end
def shares_uri_for(target) def shares_uri_for(target)
raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local? raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
account_status_shares_url(target.account, target) target.account.numeric_ap_id? ? ap_status_shares_url(target) : account_status_shares_url(target.account, target)
end end
def following_uri_for(target, ...) def following_uri_for(target, ...)
raise ArgumentError, 'target must be a local account' unless target.local? raise ArgumentError, 'target must be a local account' unless target.local?
account_following_index_url(target, ...) target.numeric_ap_id? ? ap_account_following_index_url(target.id, ...) : account_following_index_url(target, ...)
end end
def followers_uri_for(target, ...) def followers_uri_for(target, ...)
return target.followers_url.presence unless target.local? return target.followers_url.presence unless target.local?
account_followers_url(target, ...) target.numeric_ap_id? ? ap_account_followers_url(target.id, ...) : account_followers_url(target, ...)
end end
def collection_uri_for(target, ...) def collection_uri_for(target, ...)
raise NotImplementedError unless target.local? raise ArgumentError, 'target must be a local account' unless target.local?
account_collection_url(target, ...) target.numeric_ap_id? ? ap_account_collection_url(target.id, ...) : account_collection_url(target, ...)
end end
def inbox_uri_for(target) def inbox_uri_for(target)
raise NotImplementedError unless target.local? raise ArgumentError, 'target must be a local account' unless target.local?
target.instance_actor? ? instance_actor_inbox_url : account_inbox_url(target) if target.instance_actor?
instance_actor_inbox_url
elsif target.numeric_ap_id?
ap_account_inbox_url(target.id)
else
account_inbox_url(target)
end
end end
def outbox_uri_for(target, ...) def outbox_uri_for(target, ...)
raise NotImplementedError unless target.local? raise ArgumentError, 'target must be a local account' unless target.local?
target.instance_actor? ? instance_actor_outbox_url(...) : account_outbox_url(target, ...) if target.instance_actor?
instance_actor_outbox_url(...)
elsif target.numeric_ap_id?
ap_account_outbox_url(target.id, ...)
else
account_outbox_url(target, ...)
end
end end
# Primary audience of a status # Primary audience of a status

View File

@ -182,6 +182,11 @@ class Account < ApplicationRecord
update_index('accounts', :self) update_index('accounts', :self)
def numeric_ap_id?
# TODO: this is a placeholder for now
false
end
def local? def local?
domain.nil? domain.nil?
end end

View File

@ -283,8 +283,10 @@ module Account::Interactions
def local_followers_hash def local_followers_hash
Rails.cache.fetch("followers_hash:#{id}:local") do Rails.cache.fetch("followers_hash:#{id}:local") do
digest = "\x00" * 32 digest = "\x00" * 32
followers.where(domain: nil).pluck_each(:username) do |username| # TODO
Xorcist.xor!(digest, Digest::SHA256.digest(ActivityPub::TagManager.instance.uri_for_username(username))) followers.where(domain: nil).pluck_each('false as numeric_ap_id', :id, :username) do |numeric_ap_id, id, username|
uri = numeric_ap_id ? ActivityPub::TagManager.instance.uri_for_account_id(id) : ActivityPub::TagManager.instance.uri_for_username(username)
Xorcist.xor!(digest, Digest::SHA256.digest(uri))
end end
digest.unpack1('H*') digest.unpack1('H*')
end end

View File

@ -94,7 +94,19 @@ Rails.application.routes.draw do
get '/authorize_follow', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" } get '/authorize_follow', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }
resources :accounts, path: 'users', only: [:show], param: :username do concern :account_resources do
resources :followers, only: [:index], controller: :follower_accounts
resources :following, only: [:index], controller: :following_accounts
scope module: :activitypub do
resource :outbox, only: [:show]
resource :inbox, only: [:create]
resources :collections, only: [:show]
resource :followers_synchronization, only: [:show]
end
end
resources :accounts, path: 'users', only: [:show], param: :username, concerns: :account_resources do
resources :statuses, only: [:show] do resources :statuses, only: [:show] do
member do member do
get :activity get :activity
@ -105,15 +117,19 @@ Rails.application.routes.draw do
resources :likes, only: [:index], module: :activitypub resources :likes, only: [:index], module: :activitypub
resources :shares, only: [:index], module: :activitypub resources :shares, only: [:index], module: :activitypub
end end
end
resources :followers, only: [:index], controller: :follower_accounts scope path: 'ap', as: 'ap' do
resources :following, only: [:index], controller: :following_accounts resources :accounts, path: 'users', only: [:show], param: :id, concerns: :account_resources
scope module: :activitypub do resources :statuses, module: :activitypub, only: [:show] do
resource :outbox, only: [:show] member do
resource :inbox, only: [:create] get :activity
resources :collections, only: [:show] end
resource :followers_synchronization, only: [:show]
resources :replies, only: [:index]
resources :likes, only: [:index]
resources :shares, only: [:index]
end end
end end

View File

@ -5,6 +5,14 @@ require 'rails_helper'
RSpec.describe 'Accounts show response' do RSpec.describe 'Accounts show response' do
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
context 'with numeric-based identifiers' do
it 'returns http success' do
get "/ap/users/#{account.id}"
expect(response).to have_http_status(200)
end
end
context 'with an unapproved account' do context 'with an unapproved account' do
before { account.user.update(approved: false) } before { account.user.update(approved: false) }