require 'rails_helper' RSpec.describe "WebFinger", type: :request do describe "User does not exist" do it "returns a 404 status" do get "/.well-known/webfinger?resource=acct%3Ajane.doe%40kosmos.org" expect(response).to have_http_status(:not_found) end end context "User exists" do let(:user) { create :user, cn: 'tony', ou: 'kosmos.org' } before do allow_any_instance_of(User).to receive(:ldap_entry).and_return({ uid: user.cn, ou: user.ou, mail: user.email, admin: nil, services_enabled: ["mastodon", "remotestorage"] }) end describe "Avatar" do context "not available" do it "does not include an avatar link" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) link = res["links"].find{|l| l["rel"] == "http://webfinger.net/rel/avatar"} expect(link).to be_nil end end context "available" do let(:fixture_path) { Rails.root.join("spec/fixtures/files/taipei.jpg") } let(:img_data) { File.read(fixture_path) } let(:img_hash) { Digest::SHA256.hexdigest(img_data) } before do ActiveStorage::Blob.create_and_upload!( io: File.open(fixture_path), filename: "#{img_hash}.jpg", content_type: "image/jpeg" ).tap do |blob| user.avatar.attach(blob) end end it "includes a public avatar link" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) link = res["links"].find { |l| l["rel"] == "http://webfinger.net/rel/avatar" } expect(link).to be_present expect(link["type"]).to eq("image/jpeg") expect(link["href"]).to match(%r{users/tony/avatars/#{img_hash}.jpg}) end end end describe "Mastodon entries" do context "Mastodon available" do it "includes the Mastodon aliases and links for the user" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) expect(res["aliases"]).to include("http://example.social/@tony") expect(res["aliases"]).to include("http://example.social/users/tony") profile_link = res["links"].find{|l| l["rel"] == "http://webfinger.net/rel/profile-page"} self_link = res["links"].find{|l| l["rel"] == "self"} ostatus_link = res["links"].find{|l| l["rel"] == "http://ostatus.org/schema/1.0/subscribe"} expect(profile_link["type"]).to eql("text/html") expect(profile_link["href"]).to eql("http://example.social/@tony") expect(self_link["type"]).to eql("application/activity+json") expect(self_link["href"]).to eql("http://example.social/users/tony") expect(ostatus_link["template"]).to eql("http://example.social/authorize_interaction?uri={uri}") end end context "Mastodon not enabled for user" do before do allow_any_instance_of(User).to receive(:ldap_entry).and_return({ uid: user.cn, ou: user.ou, mail: user.email, admin: nil, services_enabled: ["ejabberd"] }) end it "does not include Mastodon aliases or links" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) expect(res["aliases"]).not_to include("http://example.social/@tony") expect(res["aliases"]).not_to include("http://example.social/users/tony") expect(res["links"].find{|l| l["rel"] == "http://webfinger.net/rel/profile-page"}).to be(nil) expect(res["links"].find{|l| l["rel"] == "self"}).to be(nil) expect(res["links"].find{|l| l["rel"] == "http://ostatus.org/schema/1.0/subscribe"}).to be(nil) end end context "Mastodon not available" do before do Setting.mastodon_enabled = false end it "does not include Mastodon aliases or links" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) expect(res["aliases"]).not_to include("http://example.social/@tony") expect(res["aliases"]).not_to include("http://example.social/users/tony") expect(res["links"].find{|l| l["rel"] == "http://webfinger.net/rel/profile-page"}).to be(nil) expect(res["links"].find{|l| l["rel"] == "self"}).to be(nil) expect(res["links"].find{|l| l["rel"] == "http://ostatus.org/schema/1.0/subscribe"}).to be(nil) end end end describe "remoteStorage entries" do context "remoteStorage available" do it "includes the remoteStorage link for the user" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) rs_link = res["links"].find {|l| l["rel"] == "http://tools.ietf.org/id/draft-dejong-remotestorage"} expect(rs_link["href"]).to eql("#{Setting.rs_storage_url}/tony") oauth_url = rs_link["properties"]["http://tools.ietf.org/html/rfc6749#section-4.2"] expect(oauth_url).to eql("http://localhost/rs/oauth/tony") end it "returns CORS headers" do get "/.well-known/nostr.json?name=bobdylan" expect(response.headers['Access-Control-Allow-Origin']).to eq("*") expect(response.headers['Access-Control-Allow-Methods']).to eq('GET') end end context "remoteStorage not enabled for user" do before do allow_any_instance_of(User).to receive(:ldap_entry).and_return({ uid: user.cn, ou: user.ou, mail: user.email, admin: nil, services_enabled: ["ejabberd"] }) end it "does not include the remoteStorage link" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) rs_link = res["links"].find {|l| l["rel"] == "http://tools.ietf.org/id/draft-dejong-remotestorage"} expect(rs_link).to be_nil end end context "remoteStorage not available" do before do Setting.remotestorage_enabled = false end it "does not include the remoteStorage link" do get "/.well-known/webfinger?resource=acct%3Atony%40kosmos.org" expect(response).to have_http_status(:ok) res = JSON.parse(response.body) rs_link = res["links"].find {|l| l["rel"] == "http://tools.ietf.org/id/draft-dejong-remotestorage"} expect(rs_link).to be_nil end end end end end