From daab8e80cee4ba3cd29f46913e40461f3328cc29 Mon Sep 17 00:00:00 2001 From: maxwell Date: Mon, 10 Jan 2011 13:29:00 -0800 Subject: [PATCH] basic receive cleanup --- app/models/person.rb | 2 +- app/models/post.rb | 14 +++++ lib/diaspora/user/receiving.rb | 34 ++++------ lib/diaspora/webhooks.rb | 4 ++ lib/postzord/receiver.rb | 29 +++++++++ lib/webfinger.rb | 36 ++++++----- spec/lib/diaspora/parser_spec.rb | 24 -------- spec/lib/postzord/receiver_spec.rb | 69 +++++++++++++++++++++ spec/lib/webfinger_spec.rb | 99 ++++++++++++++++++++++++++++++ 9 files changed, 248 insertions(+), 63 deletions(-) create mode 100644 lib/postzord/receiver.rb create mode 100644 spec/lib/postzord/receiver_spec.rb create mode 100644 spec/lib/webfinger_spec.rb diff --git a/app/models/person.rb b/app/models/person.rb index 858827bfa..f1cbb868a 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -122,7 +122,7 @@ class Person (person.nil? || person.remote?) ? nil : person end - def self.build_from_webfinger(profile, hcard) + def self.create_from_webfinger(profile, hcard) return nil if profile.nil? || !profile.valid_diaspora_profile? new_person = Person.new new_person.exported_key = profile.public_key diff --git a/app/models/post.rb b/app/models/post.rb index ea177398b..efe2f23d3 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -64,6 +64,20 @@ class Post user.people_in_aspects(user.aspects_with_post(self.id)) end + def receive(postzord) + xml_author = object.diaspora_handle + if (postzord.salmon_author.diaspora_handle != xml_author) + Rails.logger.info("event=receive status=abort reason='author in xml does not match retrieved person' payload_type=#{object.class} recipient=#{self.diaspora_handle} sender=#{salmon_author.diaspora_handle}") + return nil + end + + if postzord.user.contact_for(postzord.salmon_author) + self.person = postzord.salmon_author + #do post receive + end + + end + protected def destroy_comments comments.each{|c| c.destroy} diff --git a/lib/diaspora/user/receiving.rb b/lib/diaspora/user/receiving.rb index a9d8f0539..06f5559d2 100644 --- a/lib/diaspora/user/receiving.rb +++ b/lib/diaspora/user/receiving.rb @@ -24,15 +24,23 @@ module Diaspora object = Diaspora::Parser.from_xml(xml) Rails.logger.info("event=receive status=start recipient=#{self.diaspora_handle} payload_type=#{object.class} sender=#{salmon_author.diaspora_handle}") + #special casey if object.is_a?(Request) salmon_author.save object.sender_handle = salmon_author.diaspora_handle end - if object.is_a?(Comment) xml_author = (owns?(object.post))? object.diaspora_handle : object.post.person.diaspora_handle + person = Webfinger.new(object.diaspora_handle).fetch else xml_author = object.diaspora_handle + person = salmon_author + end + + #begin similar + unless object.is_a?(Request) || self.contact_for(salmon_author) + Rails.logger.info("event=receive status=abort reason='sender not connected to recipient' recipient=#{self.diaspora_handle} sender=#{salmon_author.diaspora_handle} payload_type=#{object.class}") + return end if (salmon_author.diaspora_handle != xml_author) @@ -40,27 +48,11 @@ module Diaspora return end - e = Webfinger.new(object.diaspora_handle) - - begin - person = e.fetch - rescue Exception => e - Rails.logger.info("event=receive status=abort reason='#{e.message}' payload_type=#{object.class} recipient=#{self.diaspora_handle} sender=#{salmon_author.diaspora_handle}") - return - end - if person + Rails.logger.info("event=receive status=complete recipient=#{self.diaspora_handle} sender=#{salmon_author.diaspora_handle} payload_type#{object.class}") + object.person = person if object.respond_to? :person= - - unless object.is_a?(Request) || self.contact_for(salmon_author) - Rails.logger.info("event=receive status=abort reason='sender not connected to recipient' recipient=#{self.diaspora_handle} sender=#{salmon_author.diaspora_handle} payload_type=#{object.class}") - return - else - receive_object(object, person) - Rails.logger.info("event=receive status=complete recipient=#{self.diaspora_handle} sender=#{salmon_author.diaspora_handle} payload_type#{object.class}") - - return object - end + receive_object(object, person) end end @@ -111,9 +103,7 @@ module Diaspora end def receive_comment comment - commenter = comment.person - unless comment.post.person == self.person || comment.verify_post_creator_signature Rails.logger.info("event=receive status=abort reason='comment signature not valid' recipient=#{self.diaspora_handle} sender=#{comment.post.person.diaspora_handle} payload_type=#{comment.class} post_id=#{comment.post_id}") return diff --git a/lib/diaspora/webhooks.rb b/lib/diaspora/webhooks.rb index 8804d8a61..6199b757f 100644 --- a/lib/diaspora/webhooks.rb +++ b/lib/diaspora/webhooks.rb @@ -19,5 +19,9 @@ module Diaspora def subscribers(user) raise 'you must override subscribers in order to enable federation on this model' end + + def receive(user, salmon_author) + raise 'you must override receive in order to enable federation on this model' + end end end diff --git a/lib/postzord/receiver.rb b/lib/postzord/receiver.rb new file mode 100644 index 000000000..e52cb7ba8 --- /dev/null +++ b/lib/postzord/receiver.rb @@ -0,0 +1,29 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. +# +require File.join(Rails.root, 'lib/webfinger') +require File.join(Rails.root, 'lib/diaspora/parser') + +module Postzord + class Receiver + + def initialize(user, salmon_xml) + @user = user + @salmon = Salmon::SalmonSlap.parse(salmon_xml, @user) + @salmon_author = Webfinger.new(@salmon.author_email).fetch + end + + def perform + if @salmon_author && @salmon.verified_for_key?(@salmon_author.public_key) + @object = Diaspora::Parser.from_xml(@salmon.parsed_data) + + else + Rails.logger.info("event=receive status=abort recipient=#{@user.diaspora_handle} sender=#{@salmon.author_email} reason='not_verified for key'") + nil + end + end + + protected + end +end diff --git a/lib/webfinger.rb b/lib/webfinger.rb index 1fe7a6af5..d060bce06 100644 --- a/lib/webfinger.rb +++ b/lib/webfinger.rb @@ -13,23 +13,27 @@ class Webfinger end def fetch - person = Person.by_account_identifier(@account) - if person - Rails.logger.info("event=webfinger status=success route=local target=#{@account}") - return person - end + begin + person = Person.by_account_identifier(@account) + if person + Rails.logger.info("event=webfinger status=success route=local target=#{@account}") + return person + end - profile_url = get_xrd - webfinger_profile = get_webfinger_profile(profile_url) - fingered_person = make_person_from_webfinger(webfinger_profile) - if fingered_person - Rails.logger.info("event=webfinger status=success route=remote target=#{@account}") - fingered_person - else - Rails.logger.info("event=webfinger status=failure route=remote target=#{@account}") - raise WebfingerFailedError.new(@account) + profile_url = get_xrd + webfinger_profile = get_webfinger_profile(profile_url) + fingered_person = make_person_from_webfinger(webfinger_profile) + if fingered_person + Rails.logger.info("event=webfinger status=success route=remote target=#{@account}") + fingered_person + else + Rails.logger.info("event=webfinger status=failure route=remote target=#{@account}") + raise WebfingerFailedError.new(@account) + end + rescue + Rails.logger.info("event=receive status=abort recipient=#{self.diaspora_handle} sender=#{salmon.author_email} reason='#{e.message}'") + nil end - end private @@ -77,7 +81,7 @@ class Webfinger end card = HCard.build hcard.body - p = Person.build_from_webfinger(wf_profile, card) + p = Person.create_from_webfinger(wf_profile, card) end end diff --git a/spec/lib/diaspora/parser_spec.rb b/spec/lib/diaspora/parser_spec.rb index b3c86e7e6..f26011d69 100644 --- a/spec/lib/diaspora/parser_spec.rb +++ b/spec/lib/diaspora/parser_spec.rb @@ -33,30 +33,6 @@ describe Diaspora::Parser do proc { user.receive xml, user2.person }.should change(StatusMessage, :count).by(-1) end - context "connecting" do - - let(:good_request) { FakeHttpRequest.new(:success)} - it "should create a new person upon getting a person request" do - remote_user = Factory.create(:user) - new_person = remote_user.person - - request = Request.new(:to =>user.person, :from => new_person) - xml = remote_user.salmon(request).xml_for(user.person) - request.delete - request.from.delete - remote_user.delete - new_person.delete - - Person.should_receive(:by_account_identifier).twice.and_return(new_person) - - lambda { - user.receive_salmon xml - }.should change(Person, :count).by(1) - end - - - end - it "should activate the Person if I initiated a request to that url" do user.send_contact_request_to(user2.person, aspect) request = Request.to(user2).from(user).first diff --git a/spec/lib/postzord/receiver_spec.rb b/spec/lib/postzord/receiver_spec.rb new file mode 100644 index 000000000..e01f1188f --- /dev/null +++ b/spec/lib/postzord/receiver_spec.rb @@ -0,0 +1,69 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +require File.join(Rails.root, 'lib/postzord') +require File.join(Rails.root, 'lib/postzord/receiver') + +describe Postzord::Receiver do + describe '.initialize' do + it 'has @salmon_xml and an @user' do + xml = "yeah" + user = 'faa' + salmon_mock = mock() + web_mock = mock() + web_mock.should_receive(:fetch).and_return true + salmon_mock.should_receive(:author_email).and_return(true) + Salmon::SalmonSlap.should_receive(:parse).with(xml, user).and_return(salmon_mock) + Webfinger.should_receive(:new).and_return(web_mock) + + zord = Postzord::Receiver.new(user, xml) + zord.instance_variable_get(:@user).should_not be_nil + zord.instance_variable_get(:@salmon).should_not be_nil + zord.instance_variable_get(:@salmon_author).should_not be_nil + end + end + + describe '#perform' do + before do + @user = make_user + @user2 = make_user + @person2 = @user2.person + + a = @user2.aspects.create(:name => "hey") + @original_post = @user2.build_post(:status_message, :message => "hey", :aspect_ids => [a.id]) + + salmon_xml = @user2.salmon(@original_post).xml_for(@user.person) + @zord = Postzord::Receiver.new(@user, salmon_xml) + @salmon = @zord.instance_variable_get(:@salmon) + + end + + context 'returns nil' do + it 'if the salmon author does not exist' do + @zord.instance_variable_set(:@salmon_author, nil) + @zord.perform.should be_nil + end + + it 'if the author does not match the signature' do + @zord.instance_variable_set(:@salmon_author, Factory(:person)) + @zord.perform.should be_nil + end + + end + + context 'returns the sent object' do + it 'returns the received object on success' do + object = @zord.perform + object.should respond_to(:to_diaspora_xml) + end + end + + it 'parses the salmon object' do + Diaspora::Parser.should_receive(:from_xml).with(@salmon.parsed_data) + @zord.perform + end + end +end diff --git a/spec/lib/webfinger_spec.rb b/spec/lib/webfinger_spec.rb new file mode 100644 index 000000000..bed44aede --- /dev/null +++ b/spec/lib/webfinger_spec.rb @@ -0,0 +1,99 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +require File.join(Rails.root, 'lib/webfinger') + +describe Webfinger do + let(:user1) { make_user } + let(:user2) { make_user } + + let(:account) {"foo@tom.joindiaspora.com"} + let(:person){ Factory(:person, :diaspora_handle => account)} + let(:finger){Webfinger.new(account)} + + let(:good_request) { FakeHttpRequest.new(:success)} + + let(:diaspora_xrd) {File.open(File.join(Rails.root, 'spec/fixtures/host_xrd')).read} + let(:diaspora_finger) {File.open(File.join(Rails.root, 'spec/fixtures/finger_xrd')).read} + let(:hcard_xml) {File.open(File.join(Rails.root, 'spec/fixtures/hcard_response')).read} + + + let(:non_diaspora_xrd) {File.open(File.join(Rails.root, 'spec/fixtures/nonseed_finger_xrd')).read} + let(:non_diaspora_hcard) {File.open(File.join(Rails.root, 'spec/fixtures/evan_hcard')).read} + + context 'setup' do + let(:action){ Proc.new{|person| person.inspect }} + + describe '#intialize' do + it 'sets account ' do + n = Webfinger.new("mbs348@gmail.com") + n.instance_variable_get(:@account).should_not be nil + end + + it 'should set ssl as the default' do + foo = Webfinger.new(account) + foo.instance_variable_get(:@ssl).should be true + end + end + + context 'webfinger query chain processing' do + describe '#webfinger_profile_url' do + it 'should parse out the webfinger template' do + finger.send(:webfinger_profile_url, diaspora_xrd).should == "http://tom.joindiaspora.com/webfinger/?q=#{account}" + end + + it 'should return nil if not an xrd' do + finger.send(:webfinger_profile_url, '').should be nil + end + + it 'should return the template for xrd' do + finger.send(:webfinger_profile_url, diaspora_xrd).should == 'http://tom.joindiaspora.com/webfinger/?q=foo@tom.joindiaspora.com' + end + end + + describe '#xrd_url' do + it 'should return canonical host-meta url for http' do + finger.instance_variable_set(:@ssl, false) + finger.send(:xrd_url).should == "http://tom.joindiaspora.com/.well-known/host-meta" + end + + it 'can return the https version' do + finger.send(:xrd_url).should == "https://tom.joindiaspora.com/.well-known/host-meta" + end + end + end + + + context 'webfingering local people' do + it 'should return a person from the database if it matches its handle' do + person.save + finger.fetch.id.should == person.id + end + end + it 'should fetch a diaspora webfinger and make a person for them' do + diaspora_xrd.stub!(:body).and_return(diaspora_xrd) + hcard_xml.stub!(:body).and_return(hcard_xml) + diaspora_finger.stub!(:body).and_return(diaspora_finger) + RestClient.stub!(:get).and_return(diaspora_xrd, diaspora_finger, hcard_xml) + #new_person = Factory.build(:person, :diaspora_handle => "tom@tom.joindiaspora.com") + # http://tom.joindiaspora.com/.well-known/host-meta + f = Webfinger.new("tom@tom.joindiaspora.com").fetch + + f.should be_valid + end + + it 'should retry with http if https fails' do + f = Webfinger.new("tom@tom.joindiaspora.com") + + diaspora_xrd.stub!(:body).and_return(diaspora_xrd) + RestClient.should_receive(:get).twice.and_return(nil, diaspora_xrd) + f.should_receive(:xrd_url).twice + f.send(:get_xrd) + f.instance_variable_get(:@ssl).should == false + end + + end + end