From aa9079838648e9656a1bf8d10151713686e1c0dd Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 12 Apr 2017 12:22:38 -0400 Subject: [PATCH] Webfinger resource to extract username from resource string (#1607) * Add WebfingerResource class to extract usernames * Use WebfingerResource in xrd#webfinger --- app/controllers/xrd_controller.rb | 10 +--- app/lib/webfinger_resource.rb | 66 ++++++++++++++++++++++ spec/lib/webfinger_resource_spec.rb | 88 +++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 app/lib/webfinger_resource.rb create mode 100644 spec/lib/webfinger_resource_spec.rb diff --git a/app/controllers/xrd_controller.rb b/app/controllers/xrd_controller.rb index ba5032abd..2886315ac 100644 --- a/app/controllers/xrd_controller.rb +++ b/app/controllers/xrd_controller.rb @@ -31,15 +31,7 @@ class XrdController < ApplicationController end def username_from_resource - if resource_param =~ /\Ahttps?:\/\// - path_params = Rails.application.routes.recognize_path(resource_param) - raise ActiveRecord::RecordNotFound unless path_params[:controller] == 'users' && path_params[:action] == 'show' - path_params[:username] - else - username, domain = resource_param.gsub(/\Aacct:/, '').split('@') - raise ActiveRecord::RecordNotFound unless TagManager.instance.local_domain?(domain) - username - end + WebfingerResource.new(resource_param).username end def pem_to_magic_key(public_key) diff --git a/app/lib/webfinger_resource.rb b/app/lib/webfinger_resource.rb new file mode 100644 index 000000000..8c5db795d --- /dev/null +++ b/app/lib/webfinger_resource.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +class WebfingerResource + attr_reader :resource + + def initialize(resource) + @resource = resource + end + + def username + case resource + when /\Ahttps?/i + username_from_url + when /\@/ + username_from_acct + else + raise(ActiveRecord::RecordNotFound) + end + end + + private + + def username_from_url + if account_show_page? + path_params[:username] + else + raise ActiveRecord::RecordNotFound + end + end + + def account_show_page? + path_params[:controller] == 'accounts' && path_params[:action] == 'show' + end + + def path_params + Rails.application.routes.recognize_path(resource) + end + + def username_from_acct + if domain_matches_local? + local_username + else + raise ActiveRecord::RecordNotFound + end + end + + def split_acct + resource_without_acct_string.split('@') + end + + def resource_without_acct_string + resource.gsub(/\Aacct:/, '') + end + + def local_username + split_acct.first + end + + def local_domain + split_acct.last + end + + def domain_matches_local? + TagManager.instance.local_domain?(local_domain) + end +end diff --git a/spec/lib/webfinger_resource_spec.rb b/spec/lib/webfinger_resource_spec.rb new file mode 100644 index 000000000..6c9a5ff2c --- /dev/null +++ b/spec/lib/webfinger_resource_spec.rb @@ -0,0 +1,88 @@ +require 'rails_helper' + +describe WebfingerResource do + describe '#username' do + describe 'with a URL value' do + it 'raises with an unrecognized route' do + resource = 'https://example.com/users/alice/other' + + expect { + WebfingerResource.new(resource).username + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises with a string that doesnt start with URL' do + resource = 'website for http://example.com/users/alice/other' + + expect { + WebfingerResource.new(resource).username + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'finds the username in a valid https route' do + resource = 'https://example.com/users/alice' + + result = WebfingerResource.new(resource).username + expect(result).to eq 'alice' + end + + it 'finds the username in a mixed case http route' do + resource = 'HTTp://exAMPLEe.com/users/alice' + + result = WebfingerResource.new(resource).username + expect(result).to eq 'alice' + end + + it 'finds the username in a valid http route' do + resource = 'http://example.com/users/alice' + + result = WebfingerResource.new(resource).username + expect(result).to eq 'alice' + end + end + + describe 'with a username and hostname value' do + it 'raises on a non-local domain' do + resource = 'user@remote-host.com' + + expect { + WebfingerResource.new(resource).username + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'finds username for a local domain' do + Rails.configuration.x.local_domain = 'example.com' + resource = 'alice@example.com' + + result = WebfingerResource.new(resource).username + expect(result).to eq 'alice' + end + end + + describe 'with an acct value' do + it 'raises on a non-local domain' do + resource = 'acct:user@remote-host.com' + + expect { + WebfingerResource.new(resource).username + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises on a nonsense domain' do + resource = 'acct:user@remote-host@remote-hostess.remote.local@remote' + + expect { + WebfingerResource.new(resource).username + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'finds the username for a local account' do + Rails.configuration.x.local_domain = 'example.com' + resource = 'acct:alice@example.com' + + result = WebfingerResource.new(resource).username + expect(result).to eq 'alice' + end + end + end +end