diff --git a/app/models/relayable_retraction.rb b/app/models/relayable_retraction.rb new file mode 100644 index 000000000..80d71b105 --- /dev/null +++ b/app/models/relayable_retraction.rb @@ -0,0 +1,87 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +class RelayableRetraction + include ROXML + include Diaspora::Webhooks + + xml_attr :target_guid + xml_attr :target_type + xml_attr :sender_handle + xml_attr :parent_author_signature + xml_attr :target_author_signature + + attr_accessor :target_guid, + :target_type, + :parent_author_signature, + :target_author_signature, + :sender + + def sender_handle= new_sender_handle + @sender = Person.where(:diaspora_handle => new_sender_handle).first + end + + def sender_handle + @sender.diaspora_handle + end + + def subscribers(user) + @subscribers ||= self.target.subscribers(user) + end + + def self.initialize(sender, target) + self.sender = sender + self.target = target + end + + def target + @target ||= self.target_type.constantize.where(:guid => target_guid).first + end + + def target= new_target + @target = new_target + @target_type = new_target.class.to_s + @target_guid = new_target.guid + end + + def parent + 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}") + end + + def receive(recipient, sender) + if 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 + self.perform(recipient) + elsif self.parent_author_signature_valid? + #this is a retraction from the upstream owner + self.perform(recipient) + else + Rails.logger.info("event=receive status=abort reason='object signature not valid' recipient=#{recipient.diaspora_handle} sender=#{self.parent.author.diaspora_handle} payload_type=#{self.class} parent_id=#{self.parent.id}") + return + end + self + end + + def parent_author_signature_valid? + verify_signature(self.parent_author_signature, self.parent.author) + end + + def target_author_signature_valid? + verify_signature(self.target_author_signature, self.author) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index dbc94e017..4affaed8d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -220,7 +220,11 @@ class User < ActiveRecord::Base def retract(post) aspects = post.aspects - retraction = Retraction.for(post) + if post.relayable + retraction = RelayableRetraction.new(self, post) + else + 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 mailman = Postzord::Dispatch.new(self, retraction) mailman.post diff --git a/spec/models/relayable_retraction_spec.rb b/spec/models/relayable_retraction_spec.rb new file mode 100644 index 000000000..5d1b2d385 --- /dev/null +++ b/spec/models/relayable_retraction_spec.rb @@ -0,0 +1,61 @@ +# 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, "spec", "shared_behaviors", "relayable") + +describe RelayableRetraction do + before 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 + it 'delegates it to target' do + arg = mock() + @retraction_by_parent_author.target.should_receive(:subscribers).with(arg) + @retraction_by_parent_author.subscribers(arg) + end + end + + describe '#receive' do + context 'from the downstream author' do + it 'signs' do + + end + it 'dispatches' do + + end + it 'performs' do + + end + end + context 'from the upstream owner' do + it 'performs' do + + end + it 'does not dispatch' do + + end + end + end + + describe 'xml' do + describe '#to_xml' do + + end + describe '.from_xml' do + + end + end +end