From 336ec503c28356a969e2987ef426063f0943b4e3 Mon Sep 17 00:00:00 2001 From: Tyler Deitz Date: Thu, 31 Aug 2023 04:46:27 -0700 Subject: [PATCH] Add avatar image to webfinger responses (#26558) --- app/serializers/webfinger_serializer.rb | 37 +++++++---- .../well_known/webfinger_controller_spec.rb | 64 +++++++++++++++++++ 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/app/serializers/webfinger_serializer.rb b/app/serializers/webfinger_serializer.rb index 3ca344116..b67cd2771 100644 --- a/app/serializers/webfinger_serializer.rb +++ b/app/serializers/webfinger_serializer.rb @@ -18,18 +18,31 @@ class WebfingerSerializer < ActiveModel::Serializer end def links - if object.instance_actor? - [ - { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: about_more_url(instance_actor: true) }, - { rel: 'self', type: 'application/activity+json', href: instance_actor_url }, - { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, - ] - else - [ - { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: short_account_url(object) }, - { rel: 'self', type: 'application/activity+json', href: account_url(object) }, - { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, - ] + [ + { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_page_href }, + { rel: 'self', type: 'application/activity+json', href: self_href }, + { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, + ].tap do |x| + x << { rel: 'http://webfinger.net/rel/avatar', type: object.avatar.content_type, href: full_asset_url(object.avatar_original_url) } if show_avatar? end end + + private + + def show_avatar? + media_present = object.avatar.present? && object.avatar.content_type.present? + + # Show avatar only if an instance shows profiles to logged out users + allowed_by_config = ENV['DISALLOW_UNAUTHENTICATED_API_ACCESS'] != 'true' && !Rails.configuration.x.limited_federation_mode + + media_present && allowed_by_config + end + + def profile_page_href + object.instance_actor? ? about_more_url(instance_actor: true) : short_account_url(object) + end + + def self_href + object.instance_actor? ? instance_actor_url : account_url(object) + end end diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb index 8dc0f329b..20770a721 100644 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' describe WellKnown::WebfingerController do + include RoutingHelper + render_views describe 'GET #show' do @@ -167,5 +169,67 @@ describe WellKnown::WebfingerController do expect(response).to have_http_status(400) end end + + context 'when an account has an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: attachment_fixture('attachment.jpg')) } + let(:resource) { alice.to_webfinger_s } + + it 'returns avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to_not be_nil + expect(avatar_link[:type]).to eq alice.avatar.content_type + expect(avatar_link[:href]).to eq full_asset_url(alice.avatar) + end + + context 'with limited federation mode' do + before do + allow(Rails.configuration.x).to receive(:limited_federation_mode).and_return(true) + end + + it 'does not return avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + + context 'when enabling DISALLOW_UNAUTHENTICATED_API_ACCESS' do + around do |example| + ClimateControl.modify DISALLOW_UNAUTHENTICATED_API_ACCESS: 'true' do + example.run + end + end + + it 'does not return avatar in response' do + perform_show! + + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + end + + context 'when an account does not have an avatar' do + let(:alice) { Fabricate(:account, username: 'alice', avatar: nil) } + let(:resource) { alice.to_webfinger_s } + + before do + perform_show! + end + + it 'does not return avatar in response' do + avatar_link = get_avatar_link(body_as_json) + expect(avatar_link).to be_nil + end + end + end + + private + + def get_avatar_link(json) + json[:links].find { |link| link[:rel] == 'http://webfinger.net/rel/avatar' } end end