diff --git a/app/models/post.rb b/app/models/post.rb index 9c47b6deb..974834a4d 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -24,8 +24,6 @@ class Post < ActiveRecord::Base cattr_reader :per_page @@per_page = 10 - after_destroy :propogate_retraction - def user_refs self.post_visibilities.count end @@ -93,10 +91,5 @@ class Post < ActiveRecord::Base Rails.logger.info("event=receive payload_type=#{self.class} update=true status=abort sender=#{self.diaspora_handle} reason='update not from post owner' existing_post=#{self.id}") end end - - protected - def propogate_retraction - self.author.owner.retract(self) if self.author.owner - end end diff --git a/app/models/relayable_retraction.rb b/app/models/relayable_retraction.rb index 80d71b105..a8c17ea3c 100644 --- a/app/models/relayable_retraction.rb +++ b/app/models/relayable_retraction.rb @@ -5,6 +5,7 @@ class RelayableRetraction include ROXML include Diaspora::Webhooks + include Diaspora::Encryptable xml_attr :target_guid xml_attr :target_type @@ -18,6 +19,16 @@ class RelayableRetraction :target_author_signature, :sender + def signable_accessors + accessors = self.class.roxml_attrs.collect do |definition| + definition.accessor + end + ['target_author_signature', 'parent_author_signature'].each do |acc| + accessors.delete acc + end + accessors + end + def sender_handle= new_sender_handle @sender = Person.where(:diaspora_handle => new_sender_handle).first end @@ -27,18 +38,25 @@ class RelayableRetraction end def subscribers(user) - @subscribers ||= self.target.subscribers(user) + self.target.subscribers(user) end - def self.initialize(sender, target) - self.sender = sender - self.target = target + def self.build(sender, target) + retraction = self.new + retraction.sender = sender + retraction.target = target + retraction.target_author_signature = retraction.sign_with_key(sender.encryption_key) if sender.person == target.author + retraction.parent_author_signature = retraction.sign_with_key(sender.encryption_key) if sender.person == target.parent.author + retraction end def target @target ||= self.target_type.constantize.where(:guid => target_guid).first end + def guid + target_guid + end def target= new_target @target = new_target @target_type = new_target.class.to_s @@ -49,20 +67,18 @@ class RelayableRetraction self.target.parent end - def parent_class - self.target.parent_class - end - def perform receiving_user Rails.logger.debug "Performing retraction for #{target_guid}" self.target.unsocket_from_user receiving_user if target.respond_to? :unsocket_from_user self.target.destroy - target.post_visibilities.delete_all - Rails.logger.info("event=retraction status=complete target_type=#{self.target_type} guid=#{self.target_guid}") + Rails.logger.info(:event => :retraction, :status => :complete, :target_type => self.target_type, :guid => self.target_guid) end def receive(recipient, sender) - if self.parent.author == recipient.person && self.target_author_signature_valid? + if self.target.nil? + Rails.logger.info("event=retraction status=abort reason='no post found' sender=#{sender.diaspora_handle} target_guid=#{target_guid}") + return + elsif self.parent.author == recipient.person && self.target_author_signature_valid? #this is a retraction from the downstream object creator, and the recipient is the upstream owner self.parent_author_signature = self.sign_with_key(recipient.encryption_key) Postzord::Dispatch.new(recipient, self).post @@ -82,6 +98,6 @@ class RelayableRetraction end def target_author_signature_valid? - verify_signature(self.target_author_signature, self.author) + verify_signature(self.target_author_signature, self.target.author) end end diff --git a/app/models/user.rb b/app/models/user.rb index 4affaed8d..190f5370f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -218,14 +218,14 @@ class User < ActiveRecord::Base ######### Posts and Such ############### def retract(post) - aspects = post.aspects - if post.relayable - retraction = RelayableRetraction.new(self, post) + aspects = post.parent.aspects + retraction = RelayableRetraction.build(self, post) else + aspects = post.aspects retraction = Retraction.for(post) end - post.unsocket_from_user(self, :aspect_ids => aspects.map { |a| a.id.to_s }) if post.respond_to? :unsocket_from_user + retraction.perform(self) mailman = Postzord::Dispatch.new(self, retraction) mailman.post diff --git a/lib/diaspora/encryptable.rb b/lib/diaspora/encryptable.rb index b79c33b86..53cf4fc1d 100644 --- a/lib/diaspora/encryptable.rb +++ b/lib/diaspora/encryptable.rb @@ -2,16 +2,16 @@ module Diaspora module Encryptable def verify_signature(signature, person) if person.nil? - Rails.logger.info("event=verify_signature status=abort reason=no_person guid=#{self.guid} model_id=#{self.id}") + Rails.logger.info("event=verify_signature status=abort reason=no_person guid=#{self.guid}") return false elsif person.public_key.nil? - Rails.logger.info("event=verify_signature status=abort reason=no_key guid=#{self.guid} model_id=#{self.id}") + Rails.logger.info("event=verify_signature status=abort reason=no_key guid=#{self.guid}") return false elsif signature.nil? - Rails.logger.info("event=verify_signature status=abort reason=no_signature guid=#{self.guid} model_id=#{self.id}") + Rails.logger.info("event=verify_signature status=abort reason=no_signature guid=#{self.guid}") return false end - log_string = "event=verify_signature status=complete model_id=#{id}" + log_string = "event=verify_signature status=complete guid=#{self.guid}" validity = person.public_key.verify "SHA", Base64.decode64(signature), signable_string log_string += " validity=#{validity}" Rails.logger.info(log_string) @@ -20,7 +20,9 @@ module Diaspora def sign_with_key(key) sig = Base64.encode64(key.sign "SHA", signable_string) - Rails.logger.info("event=sign_with_key status=complete model_id=#{id}") + log_hash = {:event => :sign_with_key, :status => :complete} + log_hash.merge(:model_id => self.id) if self.respond_to?(:persisted?) + Rails.logger.info(log_hash) sig end diff --git a/spec/models/relayable_retraction_spec.rb b/spec/models/relayable_retraction_spec.rb index 5d1b2d385..9056a35b0 100644 --- a/spec/models/relayable_retraction_spec.rb +++ b/spec/models/relayable_retraction_spec.rb @@ -10,52 +10,110 @@ describe RelayableRetraction do @local_luke, @local_leia, @remote_raphael = set_up_friends @remote_parent = Factory.create(:status_message, :author => @remote_raphael) @local_parent = @local_luke.post :status_message, :text => "hi", :to => @local_luke.aspects.first - @comment_by_parent_author = @local_luke.comment("yo", :on => @local_parent) - @retraction_by_parent_author = @local_luke.retract(@comment_by_parent_author) - - @comment_by_recipient = @local_leia.build_comment("yo", :on => @local_parent) - @retraction_by_recipient = @local_leia.retract(@comment_by_recipient) - - @comment_on_remote_parent = @local_luke.comment("Yeah, it was great", :on => @remote_parent) - @retraction_from_remote_author = RelayableRetraction.new(@remote_raphael, @comment_on_remote_parent) end describe '#subscribers' do + before do + @comment= @local_luke.comment("yo", :on => @local_parent) + @retraction= @local_luke.retract(@comment) + end it 'delegates it to target' do arg = mock() - @retraction_by_parent_author.target.should_receive(:subscribers).with(arg) - @retraction_by_parent_author.subscribers(arg) + @retraction.target.should_receive(:subscribers).with(arg) + @retraction.subscribers(arg) end end describe '#receive' do - context 'from the downstream author' do - it 'signs' do + it 'discards a retraction with a nil target' do + @comment= @local_luke.comment("yo", :on => @local_parent) + @retraction= @local_luke.retract(@comment) + @retraction.instance_variable_set(:@target, nil) + @retraction.target_guid = 135245 + @retraction.should_not_receive(:perform) + @retraction.receive(@local_luke, @remote_raphael) + end + context 'from the downstream author' do + before do + @comment = @local_leia.comment("yo", :on => @local_parent) + @retraction = @local_leia.retract(@comment) + @recipient = @local_luke + end + it 'signs' do + @retraction.should_receive(:sign_with_key) do |key| + key.to_s.should == @recipient.encryption_key.to_s + end + @retraction.receive(@recipient, @comment.author) end it 'dispatches' do - + zord = mock() + zord.should_receive(:post) + Postzord::Dispatch.should_receive(:new).with(@local_luke, @retraction).and_return zord + @retraction.receive(@recipient, @comment.author) end it 'performs' do - + @retraction.should_receive(:perform).with(@local_luke) + @retraction.receive(@recipient, @comment.author) end end context 'from the upstream owner' do + before do + @comment = @local_luke.comment("Yeah, it was great", :on => @remote_parent) + @retraction = RelayableRetraction.allocate + @retraction.sender = @remote_raphael + @retraction.target = @comment + @retraction.stub!(:parent_author_signature_valid?).and_return(true) + @recipient = @local_luke + end it 'performs' do - + @retraction.should_receive(:perform).with(@recipient) + @retraction.receive(@recipient, @remote_raphael) end it 'does not dispatch' do - + Postzord::Dispatch.should_not_receive(:new) + @retraction.receive(@recipient, @remote_raphael) end end end describe 'xml' do + before do + @comment = @local_leia.comment("yo", :on => @local_parent) + @retraction = RelayableRetraction.build(@local_leia, @comment) + @retraction.parent_author_signature = 'PARENTSIGNATURE' + @retraction.target_author_signature = 'TARGETSIGNATURE' + @xml = @retraction.to_xml.to_s + end describe '#to_xml' do - + it 'serializes target_guid' do + @xml.should include(@comment.guid) + end + it 'serializes target_type' do + @xml.should include(@comment.class.to_s) + end + it 'serializes sender_handle' do + @xml.should include(@local_leia.diaspora_handle) + end + it 'serializes signatures' do + @xml.should include('TARGETSIGNATURE') + @xml.should include('PARENTSIGNATURE') + end end describe '.from_xml' do - + before do + @marshalled = RelayableRetraction.from_xml(@xml) + end + it 'marshals the target' do + @marshalled.target.should == @comment + end + it 'marshals the sender' do + @marshalled.sender.should == @local_leia.person + end + it 'marshals the signature' do + @marshalled.target_author_signature.should == 'TARGETSIGNATURE' + @marshalled.parent_author_signature.should == 'PARENTSIGNATURE' + end end end end