diff --git a/Changelog.md b/Changelog.md index 678556d48..96fdbd60a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -36,6 +36,7 @@ bind to an UNIX socket at `unix:tmp/diaspora.sock`. Please change your local * Replace remaining faceboxes with Bootstrap modals [#6106](https://github.com/diaspora/diaspora/pull/6106) [#6161](https://github.com/diaspora/diaspora/pull/6161) * Rewrite header using Bootstrap 3 [#6109](https://github.com/diaspora/diaspora/pull/6109) [#6130](https://github.com/diaspora/diaspora/pull/6130) [#6132](https://github.com/diaspora/diaspora/pull/6132) * Use upstream CSS mappings for Entypo [#6158](https://github.com/diaspora/diaspora/pull/6158) +* Move webfinger and HCard generation out of the core and embed the `diaspora_federation-rails` gem [#6151](https://github.com/diaspora/diaspora/pull/6151/) ## Bug fixes * Destroy Participation when removing interactions with a post [#5852](https://github.com/diaspora/diaspora/pull/5852) diff --git a/Gemfile b/Gemfile index a83353ff0..4f0e9f748 100644 --- a/Gemfile +++ b/Gemfile @@ -3,11 +3,6 @@ source "https://rubygems.org" gem "rails", "4.2.3" # Legacy Rails features, remove me! - -# caches_page -gem "actionpack-action_caching" -gem "actionpack-page_caching" - # responders (class level) gem "responders", "2.1.0" @@ -15,6 +10,10 @@ gem "responders", "2.1.0" gem "unicorn", "4.9.0", require: false +# Federation + +gem "diaspora_federation-rails", "0.0.2" + # API and JSON gem "acts_as_api", "0.4.2" diff --git a/Gemfile.lock b/Gemfile.lock index 4caf93e3e..218830aad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,10 +16,6 @@ GEM rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionpack-action_caching (1.1.1) - actionpack (>= 4.0.0, < 5.0) - actionpack-page_caching (1.0.2) - actionpack (>= 4.0.0, < 5) actionview (4.2.3) activesupport (= 4.2.3) builder (~> 3.1) @@ -159,6 +155,11 @@ GEM eventmachine (>= 1.0.5, < 1.1) http_parser.rb (~> 0.6) nokogiri (~> 1.6) + diaspora_federation (0.0.2) + nokogiri (~> 1.6, >= 1.6.6) + diaspora_federation-rails (0.0.2) + diaspora_federation (= 0.0.2) + rails (~> 4.2) diff-lcs (1.2.5) docile (1.1.5) domain_name (0.5.24) @@ -765,8 +766,6 @@ PLATFORMS ruby DEPENDENCIES - actionpack-action_caching - actionpack-page_caching active_model_serializers (= 0.9.3) activerecord-import (= 0.8.0) acts-as-taggable-on (= 3.5.0) @@ -787,6 +786,7 @@ DEPENDENCIES devise-token_authenticatable (~> 0.4.0) devise_lastseenable (= 0.0.4) diaspora-vines (~> 0.1.27) + diaspora_federation-rails (= 0.0.2) entypo-rails (= 3.0.0.pre.rc2) eye (= 0.7.pre) factory_girl_rails (= 4.5.0) diff --git a/app/controllers/publics_controller.rb b/app/controllers/publics_controller.rb index af9d57f9c..cca482bbd 100644 --- a/app/controllers/publics_controller.rb +++ b/app/controllers/publics_controller.rb @@ -13,36 +13,8 @@ class PublicsController < ApplicationController respond_to :html respond_to :xml, :only => :post - caches_page :host_meta, :if => Proc.new{ Rails.env == 'production'} - layout false - def hcard - @person = Person.find_by_guid_and_closed_account(params[:guid], false) - - if @person.present? && @person.local? - render 'publics/hcard' - else - render :nothing => true, :status => 404 - end - end - - def host_meta - render 'host_meta', :content_type => 'application/xrd+xml' - end - - def webfinger - @person = Person.local_by_account_identifier(params[:q]) if params[:q] - - if @person.nil? || @person.closed_account? - render :nothing => true, :status => 404 - return - end - - logger.info "webfinger profile request for: #{@person.id}" - render 'webfinger', :content_type => 'application/xrd+xml' - end - def hub render :text => params['hub.challenge'], :status => 202, :layout => false end diff --git a/app/helpers/jsxc_helper.rb b/app/helpers/jsxc_helper.rb index 168faf0ed..222763529 100644 --- a/app/helpers/jsxc_helper.rb +++ b/app/helpers/jsxc_helper.rb @@ -3,11 +3,10 @@ module JsxcHelper port = AppConfig.chat.server.bosh.port bind = AppConfig.chat.server.bosh.bind host = AppConfig.pod_uri.host - scheme = AppConfig.pod_uri.scheme unless AppConfig.chat.server.bosh.proxy? return "http://#{host}:#{port}#{bind}" end - return "#{scheme}://#{host}#{bind}" + AppConfig.url_to bind end end diff --git a/app/helpers/people_helper.rb b/app/helpers/people_helper.rb index ffee117c6..f31c658cd 100644 --- a/app/helpers/people_helper.rb +++ b/app/helpers/people_helper.rb @@ -51,10 +51,6 @@ module PeopleHelper end end - def person_href(person, opts={}) - "href=\"#{local_or_remote_person_path(person, opts)}\"".html_safe - end - # Rails.application.routes.url_helpers is needed since this is indirectly called from a model def local_or_remote_person_path(person, opts={}) opts.merge!(:protocol => AppConfig.pod_uri.scheme, :host => AppConfig.pod_uri.authority) diff --git a/app/models/person.rb b/app/models/person.rb index 574258f27..511bc4728 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -28,7 +28,7 @@ class Person < ActiveRecord::Base xml_attr :profile, :as => Profile xml_attr :exported_key - has_one :profile, :dependent => :destroy + has_one :profile, dependent: :destroy delegate :last_name, :image_url, :tag_string, :bio, :location, :gender, :birthday, :formatted_birthday, :tags, :searchable, :public_details?, to: :profile @@ -222,7 +222,7 @@ class Person < ActiveRecord::Base end def public_key_hash - Base64.encode64(OpenSSL::Digest::SHA256.new(self.exported_key).to_s) + Base64.encode64(OpenSSL::Digest::SHA256.new(serialized_public_key).to_s) end def public_key @@ -238,15 +238,18 @@ class Person < ActiveRecord::Base serialized_public_key = new_key end - #database calls + # database calls def self.by_account_identifier(identifier) - identifier = identifier.strip.downcase.gsub('acct:', '') - self.where(:diaspora_handle => identifier).first + identifier = identifier.strip.downcase.sub("acct:", "") + find_by(diaspora_handle: identifier) end - def self.local_by_account_identifier(identifier) - person = self.by_account_identifier(identifier) - (person.nil? || person.remote?) ? nil : person + def self.find_local_by_diaspora_handle(handle) + where(diaspora_handle: handle, closed_account: false).where.not(owner: nil).take + end + + def self.find_local_by_guid(guid) + where(guid: guid, closed_account: false).where.not(owner: nil).take end def self.create_from_webfinger(profile, hcard) diff --git a/app/views/publics/hcard.haml b/app/views/publics/hcard.haml deleted file mode 100644 index f19bffb35..000000000 --- a/app/views/publics/hcard.haml +++ /dev/null @@ -1,50 +0,0 @@ -#content - %h1= @person.name - #content_inner - #i.entity_profile.vcard.author - %h2 User profile - - %dl.entity_nickname - %dt Nickname - %dd - %a.nickname.url.uid{:href=>@person.url, :rel=>'me'}= @person.name - - %dl.entity_given_name - %dt First name - %dd - %span.given_name= @person.profile.first_name - - %dl.entity_family_name - %dt Family name - %dd - %span.family_name= @person.last_name - - %dl.entity_fn - %dt Full name - %dd - %span.fn= @person.name - - %dl.entity_url - %dt URL - %dd - %a#pod_location.url{:href=>@person.url, :rel=>'me'}= @person.url - - %dl.entity_photo - %dt Photo - %dd - %img.photo.avatar{:src=>@person.image_url, :width=>'300px', :height=>'300px'} - - %dl.entity_photo_medium - %dt Photo - %dd - %img.photo.avatar{:src=>@person.image_url(:thumb_medium), :width=>'100px', :height=>'100px'} - - %dl.entity_photo_small - %dt Photo - %dd - %img.photo.avatar{:src=>@person.image_url(:thumb_small), :width=>'50px', :height=>'50px'} - - %dl.entity_searchable - %dt Searchable - %dd - %span.searchable= @person.searchable diff --git a/app/views/publics/host_meta.erb b/app/views/publics/host_meta.erb deleted file mode 100644 index f5efcab11..000000000 --- a/app/views/publics/host_meta.erb +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/app/views/publics/webfinger.erb b/app/views/publics/webfinger.erb deleted file mode 100644 index 3b8099441..000000000 --- a/app/views/publics/webfinger.erb +++ /dev/null @@ -1,14 +0,0 @@ - - - acct:<%=@person.diaspora_handle%> - "<%= @person.url %>" - - - - - true)%>/> - - - - - diff --git a/config/initializers/diaspora_federation.rb b/config/initializers/diaspora_federation.rb new file mode 100644 index 000000000..cedbf2e00 --- /dev/null +++ b/config/initializers/diaspora_federation.rb @@ -0,0 +1,43 @@ +# configure the federation engine +DiasporaFederation.configure do |config| + # the pod url + config.server_uri = AppConfig.pod_uri + + config.define_callbacks do + on :person_webfinger_fetch do |handle| + person = Person.find_local_by_diaspora_handle(handle) + if person + DiasporaFederation::WebFinger::WebFinger.new( + acct_uri: "acct:#{person.diaspora_handle}", + alias_url: AppConfig.url_to("/people/#{person.guid}"), + hcard_url: AppConfig.url_to(DiasporaFederation::Engine.routes.url_helpers.hcard_path(person.guid)), + seed_url: AppConfig.pod_uri, + profile_url: person.profile_url, + atom_url: person.atom_url, + salmon_url: person.receive_url, + guid: person.guid, + public_key: person.serialized_public_key + ) + end + end + + on :person_hcard_fetch do |guid| + person = Person.find_local_by_guid(guid) + if person + DiasporaFederation::WebFinger::HCard.new( + guid: person.guid, + nickname: person.username, + full_name: "#{person.profile.first_name} #{person.profile.last_name}".strip, + url: AppConfig.pod_uri, + photo_large_url: person.image_url, + photo_medium_url: person.image_url(:thumb_medium), + photo_small_url: person.image_url(:thumb_small), + public_key: person.serialized_public_key, + searchable: person.searchable, + first_name: person.profile.first_name, + last_name: person.profile.last_name + ) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 2d88dc78a..c0fd79d2b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,8 @@ Diaspora::Application.routes.draw do mount Sidekiq::Web => '/sidekiq', :as => 'sidekiq' end + mount DiasporaFederation::Engine => "/" + get "/atom.xml" => redirect('http://blog.diasporafoundation.org/feed/atom') #too many stupid redirects :() get 'oembed' => 'posts#oembed', :as => 'oembed' @@ -191,9 +193,6 @@ Diaspora::Application.routes.draw do # Federation controller :publics do - get 'webfinger' => :webfinger - get 'hcard/users/:guid' => :hcard - get '.well-known/host-meta' => :host_meta post 'receive/users/:guid' => :receive post 'receive/public' => :receive_public get 'hub' => :hub diff --git a/lib/configuration_methods.rb b/lib/configuration_methods.rb index 107fde37c..81d769b78 100644 --- a/lib/configuration_methods.rb +++ b/lib/configuration_methods.rb @@ -3,19 +3,25 @@ module Configuration module Methods def pod_uri - return @pod_uri unless @pod_uri.nil? + return @pod_uri.dup unless @pod_uri.nil? url = environment.url.get url = "http://#{url}" unless url =~ /^(https?:\/\/)/ url << "/" unless url.end_with?("/") begin - @pod_url = Addressable::URI.parse(url) + @pod_uri = Addressable::URI.parse(url) rescue puts "WARNING: pod url #{url} is not a legal URI" end - @pod_url + @pod_uri.dup + end + + # @param path [String] + # @return [String] + def url_to(path) + pod_uri.tap {|uri| uri.path = path }.to_s end def bare_pod_uri diff --git a/spec/controllers/diaspora_federation_controller_spec.rb b/spec/controllers/diaspora_federation_controller_spec.rb new file mode 100644 index 000000000..8799220ee --- /dev/null +++ b/spec/controllers/diaspora_federation_controller_spec.rb @@ -0,0 +1,36 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require "spec_helper" + +# this is temporarily needed for fixture generation +# TODO: remove this after the parsing is also in the diaspora_federation gem +describe DiasporaFederation do + routes { DiasporaFederation::Engine.routes } + + let(:fixture_path) { Rails.root.join("spec", "fixtures") } + + describe DiasporaFederation::WebfingerController, type: :controller do + it "generates the host_meta fixture", fixture: true do + get :host_meta + expect(response).to be_success + expect(response.body).to match(/webfinger/) + save_fixture(response.body, "host-meta", fixture_path) + end + + it "generates the webfinger fixture", fixture: true do + post :legacy_webfinger, "q" => alice.person.diaspora_handle + expect(response).to be_success + save_fixture(response.body, "webfinger", fixture_path) + end + end + + describe DiasporaFederation::HCardController, type: :controller do + it "generates the hCard fixture", fixture: true do + post :hcard, "guid" => alice.person.guid.to_s + expect(response).to be_success + save_fixture(response.body, "hcard", fixture_path) + end + end +end diff --git a/spec/controllers/publics_controller_spec.rb b/spec/controllers/publics_controller_spec.rb index 08415577a..5b105688b 100644 --- a/spec/controllers/publics_controller_spec.rb +++ b/spec/controllers/publics_controller_spec.rb @@ -11,15 +11,6 @@ describe PublicsController, :type => :controller do @person = FactoryGirl.create(:person) end - describe '#host_meta' do - it 'succeeds', :fixture => true do - get :host_meta - expect(response).to be_success - expect(response.body).to match(/webfinger/) - save_fixture(response.body, "host-meta", fixture_path) - end - end - describe '#receive_public' do it 'succeeds' do post :receive_public, :xml => "" @@ -79,67 +70,6 @@ describe PublicsController, :type => :controller do end end - describe '#hcard' do - it "succeeds", :fixture => true do - post :hcard, "guid" => @user.person.guid.to_s - expect(response).to be_success - save_fixture(response.body, "hcard", fixture_path) - end - - it 'sets the person' do - post :hcard, "guid" => @user.person.guid.to_s - expect(assigns[:person]).to eq(@user.person) - end - - it 'does not query by user id' do - post :hcard, "guid" => 90348257609247856.to_s - expect(assigns[:person]).to be_nil - expect(response).to be_not_found - end - - it 'finds nothing for closed accounts' do - @user.person.update_attributes(:closed_account => true) - get :hcard, :guid => @user.person.guid.to_s - expect(response).to be_not_found - end - end - - describe '#webfinger' do - it "succeeds when the person and user exist locally", :fixture => true do - post :webfinger, 'q' => @user.person.diaspora_handle - expect(response).to be_success - save_fixture(response.body, "webfinger", fixture_path) - end - - it "404s when the person exists remotely because it is local only" do - stub_success('me@mydiaspora.pod.com') - post :webfinger, 'q' => 'me@mydiaspora.pod.com' - expect(response).to be_not_found - end - - it "404s when the person is local but doesn't have an owner" do - post :webfinger, 'q' => @person.diaspora_handle - expect(response).to be_not_found - end - - it "404s when the person does not exist locally or remotely" do - stub_failure('me@mydiaspora.pod.com') - post :webfinger, 'q' => 'me@mydiaspora.pod.com' - expect(response).to be_not_found - end - - it 'has the users profile href' do - get :webfinger, :q => @user.diaspora_handle - expect(response.body).to include "http://webfinger.net/rel/profile-page" - end - - it 'finds nothing for closed accounts' do - @user.person.update_attributes(:closed_account => true) - get :webfinger, :q => @user.diaspora_handle - expect(response).to be_not_found - end - end - describe '#hub' do it 'succeeds' do get :hub diff --git a/spec/federation_callbacks_spec.rb b/spec/federation_callbacks_spec.rb new file mode 100644 index 000000000..b0502198f --- /dev/null +++ b/spec/federation_callbacks_spec.rb @@ -0,0 +1,56 @@ +require "spec_helper" + +describe "diaspora federation callbacks" do + describe ":person_webfinger_fetch" do + it "returns a WebFinger instance with the data from the person" do + person = alice.person + wf = DiasporaFederation.callbacks.trigger(:person_webfinger_fetch, alice.diaspora_handle) + expect(wf.acct_uri).to eq("acct:#{person.diaspora_handle}") + expect(wf.alias_url).to eq(AppConfig.url_to("/people/#{person.guid}")) + expect(wf.hcard_url).to eq(AppConfig.url_to("/hcard/users/#{person.guid}")) + expect(wf.seed_url).to eq(AppConfig.pod_uri) + expect(wf.profile_url).to eq(person.profile_url) + expect(wf.atom_url).to eq(person.atom_url) + expect(wf.salmon_url).to eq(person.receive_url) + expect(wf.guid).to eq(person.guid) + expect(wf.public_key).to eq(person.serialized_public_key) + end + + it "returns nil if the person was not found" do + wf = DiasporaFederation.callbacks.trigger(:person_webfinger_fetch, "unknown@example.com") + expect(wf).to be_nil + end + end + + describe ":person_hcard_fetch" do + it "returns a HCard instance with the data from the person" do + person = alice.person + hcard = DiasporaFederation.callbacks.trigger(:person_hcard_fetch, alice.guid) + expect(hcard.guid).to eq(person.guid) + expect(hcard.nickname).to eq(person.username) + expect(hcard.full_name).to eq("#{person.profile.first_name} #{person.profile.last_name}") + expect(hcard.url).to eq(AppConfig.pod_uri) + expect(hcard.photo_large_url).to eq(person.image_url) + expect(hcard.photo_medium_url).to eq(person.image_url(:thumb_medium)) + expect(hcard.photo_small_url).to eq(person.image_url(:thumb_small)) + expect(hcard.public_key).to eq(person.serialized_public_key) + expect(hcard.searchable).to eq(person.searchable) + expect(hcard.first_name).to eq(person.profile.first_name) + expect(hcard.last_name).to eq(person.profile.last_name) + end + + it "trims the full_name" do + user = FactoryGirl.create(:user) + user.person.profile.last_name = nil + user.person.profile.save + + hcard = DiasporaFederation.callbacks.trigger(:person_hcard_fetch, user.guid) + expect(hcard.full_name).to eq(user.person.profile.first_name) + end + + it "returns nil if the person was not found" do + hcard = DiasporaFederation.callbacks.trigger(:person_hcard_fetch, "1234567890abcdef") + expect(hcard).to be_nil + end + end +end diff --git a/spec/helpers/jsxc_helper_spec.rb b/spec/helpers/jsxc_helper_spec.rb index 014e2f0b6..d38a72bc4 100644 --- a/spec/helpers/jsxc_helper_spec.rb +++ b/spec/helpers/jsxc_helper_spec.rb @@ -6,6 +6,7 @@ describe JsxcHelper, :type => :helper do AppConfig.chat.server.bosh.port = 1234 AppConfig.chat.server.bosh.bind = '/bind' AppConfig.environment.url = "https://localhost/" + AppConfig.instance_variable_set(:@pod_uri, nil) end describe "#get_bosh_endpoint" do diff --git a/spec/helpers/people_helper_spec.rb b/spec/helpers/people_helper_spec.rb index 6d633097d..d127ff244 100644 --- a/spec/helpers/people_helper_spec.rb +++ b/spec/helpers/people_helper_spec.rb @@ -5,12 +5,12 @@ require 'spec_helper' describe PeopleHelper, :type => :helper do - before do + before do @user = alice @person = FactoryGirl.create(:person) end - describe "#person_image_link" do + describe "#person_image_link" do it "returns an empty string if person is nil" do expect(person_image_link(nil)).to eq("") end @@ -58,26 +58,12 @@ describe PeopleHelper, :type => :helper do @person.profile.last_name = "I'm

Evil" expect(person_link(@person)).not_to include("

") end - + it 'links by id for a local user' do expect(person_link(@user.person)).to include "href='#{person_path(@user.person)}'" end end - describe "#person_href" do - it "calls local_or_remote_person_path and passes through the options" do - opts = {:absolute => true} - - expect(self).to receive(:local_or_remote_person_path).with(@person, opts).exactly(1).times - - person_href(@person, opts) - end - - it "returns a href attribute" do - expect(person_href(@person)).to include "href=" - end - end - describe '#local_or_remote_person_path' do before do @user = FactoryGirl.create(:user) diff --git a/spec/lib/configuration_methods_spec.rb b/spec/lib/configuration_methods_spec.rb index 966b4c039..452fbfe24 100644 --- a/spec/lib/configuration_methods_spec.rb +++ b/spec/lib/configuration_methods_spec.rb @@ -21,7 +21,7 @@ describe Configuration::Methods do expect(@settings.pod_uri.host).to eq("example.org") end - it "adds a trailing slash if there isn't one" do + it "adds a trailing slash if there isn't one" do @settings.environment.url = "http://example.org" expect(@settings.pod_uri.to_s).to eq("http://example.org/") end @@ -40,6 +40,12 @@ describe Configuration::Methods do @settings.environment.url = "https://example.org/" expect(@settings.pod_uri.to_s).to eq("https://example.org/") end + + it "returns another instance everytime" do + @settings.environment.url = "https://example.org/" + uri = @settings.pod_uri + expect(@settings.pod_uri).not_to be(uri) + end end describe "#bare_pod_uri" do @@ -51,6 +57,21 @@ describe Configuration::Methods do end end + describe "#url_to" do + before do + @settings.environment.url = "https://example.org" + @settings.instance_variable_set(:@pod_uri, nil) + end + + it "appends the path to the pod url" do + expect(@settings.url_to("/any/path")).to eq("https://example.org/any/path") + end + + it "does not add double slash" do + expect(@settings.url_to("/any/path")).to eq("https://example.org/any/path") + end + end + describe "#configured_services" do it "includes the enabled services only" do services = double diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index 6433fafb3..b06056903 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -457,27 +457,45 @@ describe Person, :type => :model do f = Person.by_account_identifier("tom@tom.joindiaspora.com") expect(f).to be nil end - - end - describe '.local_by_account_identifier' do - it 'should find local users people' do - p = Person.local_by_account_identifier(user.diaspora_handle) - expect(p).to eq(user.person) + describe ".find_local_by_diaspora_handle" do + it "should find local users person" do + person = Person.find_local_by_diaspora_handle(user.diaspora_handle) + expect(person).to eq(user.person) end - it 'should not find a remote person' do - p = Person.local_by_account_identifier(@person.diaspora_handle) - expect(p).to be nil + it "should not find a remote person" do + person = Person.find_local_by_diaspora_handle(@person.diaspora_handle) + expect(person).to be nil end - it 'should call .by_account_identifier' do - expect(Person).to receive(:by_account_identifier) - Person.local_by_account_identifier(@person.diaspora_handle) + it "should not find a person with closed account" do + user.person.lock_access! + person = Person.find_local_by_diaspora_handle(user.diaspora_handle) + expect(person).to be nil + end + end + + describe ".find_local_by_guid" do + it "should find local users person" do + person = Person.find_local_by_guid(user.guid) + expect(person).to eq(user.person) + end + + it "should not find a remote person" do + person = Person.find_local_by_guid(@person.guid) + expect(person).to be nil + end + + it "should not find a person with closed account" do + user.person.lock_access! + person = Person.find_local_by_guid(user.guid) + expect(person).to be nil end end end + describe '#has_photos?' do it 'returns false if the user has no photos' do expect(alice.person.has_photos?).to be false