diff --git a/app/controllers/publics_controller.rb b/app/controllers/publics_controller.rb index 3b555bd1e4733cd29f7718416614510ba4af939a..eb67146b3a451960d3bb1d67e1a6d69e240296d5 100644 --- a/app/controllers/publics_controller.rb +++ b/app/controllers/publics_controller.rb @@ -50,7 +50,7 @@ class PublicsController < ApplicationController end def receive_public - Postzord::Receiver::Public.new(params[:xml]) + Resque.enqueue(Job::ReceivePublicSalmon, params[:xml]) render :nothing => true, :status => :ok end diff --git a/app/models/contact.rb b/app/models/contact.rb index 00b6dde9f8e679af43d59b883d62cb0c9227a23e..e4e18fd62f3ea1af1830ccab056b8706d4754627 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -19,6 +19,8 @@ class Contact < ActiveRecord::Base validates_uniqueness_of :person_id, :scope => :user_id + before_destroy :destroy_notifications + # contact.sharing is true when contact.person is sharing with contact.user scope :sharing, lambda { where(:sharing => true) @@ -33,7 +35,6 @@ class Contact < ActiveRecord::Base sharing.where(:receiving => false) } - before_destroy :destroy_notifications def destroy_notifications Notification.where(:target_type => "Person", :target_id => person_id, diff --git a/app/models/job/receive_local_batch.rb b/app/models/job/receive_local_batch.rb index d7e0801d6365697548da22998e36d293853a839e..f5689e797c1531928d88c5efe6be1619672e4ba6 100644 --- a/app/models/job/receive_local_batch.rb +++ b/app/models/job/receive_local_batch.rb @@ -2,56 +2,18 @@ # licensed under the Affero General Public License version 3 or later. See # the COPYRIGHT file. +require File.join(Rails.root, 'lib/postzord/receiver') +require File.join(Rails.root, 'lib/postzord/receiver/local_post_batch') module Job class ReceiveLocalBatch < Base - require File.join(Rails.root, 'lib/postzord/receiver') @queue = :receive + def self.perform(post_id, recipient_user_ids) post = Post.find(post_id) - create_visibilities(post, recipient_user_ids) - socket_to_users(post, recipient_user_ids) if post.respond_to?(:socket_to_user) - notify_mentioned_users(post) - notify_users(post, recipient_user_ids) - end - - def self.create_visibilities(post, recipient_user_ids) - contacts = Contact.where(:user_id => recipient_user_ids, :person_id => post.author_id) - - if postgres? - # Take the naive approach to inserting our new visibilities for now. - contacts.each do |contact| - PostVisibility.find_or_create_by_contact_id_and_post_id(contact.id, post.id) - end - else - # Use a batch insert on mySQL. - new_post_visibilities = contacts.map do |contact| - PostVisibility.new(:contact_id => contact.id, :post_id => post.id) - end - PostVisibility.import(new_post_visibilities) - end - - end - - def self.socket_to_users(post, recipient_user_ids) - recipient_user_ids.each do |id| - post.socket_to_user(id) - end - end - - def self.notify_mentioned_users(post) - post.mentions.each do |mention| - mention.notify_recipient - end - end - - def self.notify_users(post, recipient_user_ids) - if post.respond_to?(:notification_type) - recipient_user_ids.each{|id| - Notification.notify(User.find(id), post, post.author) - } - end + receiver = Postzord::Receiver::LocalPostBatch.new(post, recipient_user_ids) + receiver.perform! end end end diff --git a/app/models/job/receive_public_salmon.rb b/app/models/job/receive_public_salmon.rb new file mode 100644 index 0000000000000000000000000000000000000000..d7f700576f8052dca32c1e3359f3819706b44c7c --- /dev/null +++ b/app/models/job/receive_public_salmon.rb @@ -0,0 +1,17 @@ +# Copyright (c) 2010, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + + +module Job + class ReceivePublicSalmon < Base + require File.join(Rails.root, 'lib/postzord/receiver') + + @queue = :receive + + def self.perform(xml) + receiver = Postzord::Receiver::Public.new(xml) + receiver.perform! + end + end +end diff --git a/app/models/post_visibility.rb b/app/models/post_visibility.rb index 69122f04672ffe81187dadf0a65b5be7842829ac..086eaa8bec3bc2af92c5ea6a5b5b76e5179cae1c 100644 --- a/app/models/post_visibility.rb +++ b/app/models/post_visibility.rb @@ -5,4 +5,20 @@ class PostVisibility < ActiveRecord::Base belongs_to :contact belongs_to :post + + + def self.batch_import(contacts, post) + if postgres? + # Take the naive approach to inserting our new visibilities for now. + contacts.each do |contact| + PostVisibility.find_or_create_by_contact_id_and_post_id(contact.id, post.id) + end + else + # Use a batch insert on mySQL. + new_post_visibilities = contacts.map do |contact| + PostVisibility.new(:contact_id => contact.id, :post_id => post.id) + end + PostVisibility.import(new_post_visibilities) + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index 3fc69e017df79fb5c5b8a81ccbc20c526649bd97..4b1e8684bbd042afe271b1845a4b0c1437ca1839 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -59,6 +59,10 @@ class User < ActiveRecord::Base :invitation_identifier + def self.all_sharing_with_person(person) + User.joins(:contacts).where(:contacts => {:person_id => person.id}) + end + # @return [User] def self.find_by_invitation(invitation) service = invitation.service diff --git a/lib/postzord/dispatch.rb b/lib/postzord/dispatch.rb index 3b12e207a44ffcefc7af89cc7eee034536b02cc1..a04c65bdf3e7f700e1b3ef7e01ad06c3f46dc9b1 100644 --- a/lib/postzord/dispatch.rb +++ b/lib/postzord/dispatch.rb @@ -18,7 +18,7 @@ class Postzord::Dispatch end def salmon - @salmon_factory ||= Salmon::EncryptedSlap.create(@sender, @xml) + @salmon_factory ||= Salmon::EncryptedSlap.create_by_user_and_activity(@sender, @xml) end def post(opts = {}) diff --git a/lib/postzord/receiver.rb b/lib/postzord/receiver.rb index f39efd4105f8fe78abb8d598d9f01bf7b0ba57f4..eb41080fe8b3df679a3b232187e909ee1afb0a3d 100644 --- a/lib/postzord/receiver.rb +++ b/lib/postzord/receiver.rb @@ -45,7 +45,7 @@ module Postzord protected def salmon - @salmon ||= Salmon::EncryptedSlap.parse(@salmon_xml, @user) + @salmon ||= Salmon::EncryptedSlap.from_xml(@salmon_xml, @user) end def xml_author diff --git a/lib/postzord/receiver/local_post_batch.rb b/lib/postzord/receiver/local_post_batch.rb new file mode 100644 index 0000000000000000000000000000000000000000..7c035a1426220402d999ad9028729a1d3b51604e --- /dev/null +++ b/lib/postzord/receiver/local_post_batch.rb @@ -0,0 +1,45 @@ +module Postzord + class Receiver + class LocalPostBatch + attr_reader :post, :recipient_user_ids, :users + + def initialize(post, recipient_user_ids) + @post = post + @recipient_user_ids = recipient_user_ids + @users = User.where(:id => @recipient_user_ids) + end + + def perform! + create_visibilities + socket_to_users if @post.respond_to?(:socket_to_user) + notify_mentioned_users + notify_users + end + + def create_visibilities + contacts = Contact.where(:user_id => @recipient_user_ids, :person_id => @post.author_id) + PostVisibility.batch_import(contacts, post) + end + + def socket_to_users + @users.each do |user| + @post.socket_to_user(user) + end + end + + def notify_mentioned_users + @post.mentions.each do |mention| + mention.notify_recipient + end + end + + def notify_users + if @post.respond_to?(:notification_type) + @users.each do |user| + Notification.notify(user, @post, @post.author) + end + end + end + end + end +end diff --git a/lib/postzord/receiver/public.rb b/lib/postzord/receiver/public.rb index c2b6199ecc724885f90dce7edcbc40a14258376b..bc007ecf07e631203ba5eded64eb38a3d89dfcc2 100644 --- a/lib/postzord/receiver/public.rb +++ b/lib/postzord/receiver/public.rb @@ -5,12 +5,55 @@ module Postzord class Receiver class Public - attr_accessor :xml + attr_accessor :salmon, :author def initialize(xml) - @xml = xml - + @salmon = Salmon::Slap.from_xml(xml) + @author = Webfinger.new(@salmon.author_email).fetch end + + # @return [Boolean] + def verified_signature? + @salmon.verified_for_key?(@author.public_key) + end + + # @return [void] + def perform! + return false unless verified_signature? + return unless save_object + + if @object.respond_to?(:relayable) + receive_relayable + else + Resque.enqueue(Job::ReceiveLocalBatch, @object.id, self.recipient_user_ids) + end + end + + def receive_relayable + raise RelayableObjectWithoutParent.new("Receiving a relayable object without parent object present locally!") unless @object.parent.user.present? + + # receive relayable object only for the owner of the parent object + @object.receive(@object.parent.user, @author) + + # notify everyone who can see the parent object + receiver = Postzord::Receiver::LocalPostBatch.new(nil, self.recipient_user_ids) + receiver.notify_users + end + + # @return [Object] + def save_object + @object = Diaspora::Parser::from_xml(@salmon.parsed_data) + raise "Object is not public" unless @object.public? + @object.save! + end + + # @return [Array<Integer>] User ids + def recipient_user_ids + User.all_sharing_with_person(@author).select('users.id').map!{ |u| u.id } + end + + class RelayableObjectWithoutParent < StandardError ; ; end end end end + diff --git a/lib/salmon/magic_sig_envelope.rb b/lib/salmon/magic_sig_envelope.rb index ae93bdc3909a1a3ed8b468efbe6a7bc706fec919..063555b12cd4b0718f6a8b6adacb3e41ea436b3f 100644 --- a/lib/salmon/magic_sig_envelope.rb +++ b/lib/salmon/magic_sig_envelope.rb @@ -6,7 +6,6 @@ module Salmon class MagicSigEnvelope attr_accessor :data, :data_type, :encoding, :alg, :sig, :author def self.parse(doc) - puts doc.to_s env = self.new ns = {'me'=>'http://salmon-protocol.org/ns/magic-env'} env.encoding = doc.search('//me:env/me:encoding', ns).text.strip diff --git a/spec/controllers/publics_controller_spec.rb b/spec/controllers/publics_controller_spec.rb index 1616f09fc484aa9b4ff23dee26b1f15d1f78870a..5e48b0e74d4c814ef0cfdb42991c4c225c7b7cfe 100644 --- a/spec/controllers/publics_controller_spec.rb +++ b/spec/controllers/publics_controller_spec.rb @@ -31,9 +31,9 @@ describe PublicsController do response.code.should == '422' end - it 'calls Postzord::Receiver:Public' do + it 'enqueues a ReceivePublicSalmon job' do xml = "stuff" - Postzord::Receiver::Public.should_receive(:new).with(xml) + Resque.should_receive(:enqueue).with(Job::ReceivePublicSalmon, xml) post :receive_public, :xml => xml end end @@ -57,7 +57,7 @@ describe PublicsController do xml2 = post1.to_diaspora_xml user2 = Factory(:user) - salmon_factory = Salmon::EncryptedSlap.create(@user, xml2) + salmon_factory = Salmon::EncryptedSlap.create_by_user_and_activity(@user, xml2) enc_xml = salmon_factory.xml_for(user2.person) Resque.should_receive(:enqueue).with(Job::ReceiveSalmon, @user.id, enc_xml).once diff --git a/spec/lib/postzord/dispatch_spec.rb b/spec/lib/postzord/dispatch_spec.rb index de6b1a60d1acb5445b4db48d89b8bfe39714fdaa..68a99eb335192c818c3fe78bcd0019824986ef19 100644 --- a/spec/lib/postzord/dispatch_spec.rb +++ b/spec/lib/postzord/dispatch_spec.rb @@ -231,7 +231,7 @@ describe Postzord::Dispatch do it 'calls salmon_for each remote person' do salmon = @mailman.salmon - Salmon::EncryptedSlap.stub(:create).and_return(salmon) + Salmon::EncryptedSlap.stub(:create_by_user_and_activity).and_return(salmon) salmon.should_receive(:xml_for).with(alice.person).and_return('what') @hydra.stub!(:queue) @hydra.stub!(:run) diff --git a/spec/lib/postzord/receiver/local_post_batch_spec.rb b/spec/lib/postzord/receiver/local_post_batch_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..89db17c836add165fc42134bb8f13086b8fba990 --- /dev/null +++ b/spec/lib/postzord/receiver/local_post_batch_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' +require File.join(Rails.root, 'lib','postzord', 'receiver', 'local_post_batch') + +describe Postzord::Receiver::LocalPostBatch do + before do + @post = Factory(:status_message, :author => alice.person) + @ids = [bob.id] + + @receiver = Postzord::Receiver::LocalPostBatch.new(@post, @ids) + end + + describe '.initialize' do + it 'sets @post, @recipient_user_ids, and @user' do + [:post, :recipient_user_ids, :users].each do |instance_var| + @receiver.send(instance_var).should_not be_nil + end + end + end + + describe '#perform!' do + it 'calls .create_visibilities' do + @receiver.should_receive(:create_visibilities) + @receiver.perform! + end + + it 'sockets to users' do + @receiver.should_receive(:socket_to_users) + @receiver.perform! + end + + it 'notifies mentioned users' do + @receiver.should_receive(:notify_mentioned_users) + @receiver.perform! + end + + it 'notifies users' do + @receiver.should_receive(:notify_users) + @receiver.perform! + end + end + + describe '#create_visibilities' do + it 'calls Postvisibility.batch_import' do + PostVisibility.should_receive(:batch_import) + @receiver.create_visibilities + end + end + + describe '#socket_to_users' do + before do + @controller = mock() + SocketsController.stub(:new).and_return(@controller) + end + + it 'sockets to each user' do + @controller.should_receive(:outgoing).with(bob, @post, instance_of(Hash)) + @receiver.socket_to_users + end + end + + describe '#notify_mentioned_users' do + it 'calls notify person for a mentioned person' do + sm = Factory(:status_message, + :author => alice.person, + :text => "Hey @{Bob; #{bob.diaspora_handle}}") + + receiver = Postzord::Receiver::LocalPostBatch.new(sm, @ids) + Notification.should_receive(:notify).with(bob, anything, alice.person) + receiver.notify_mentioned_users + end + + it 'does not call notify person for a non-mentioned person' do + Notification.should_not_receive(:notify) + @receiver.notify_mentioned_users + end + end + + describe '#notify_users' do + it 'calls notify for posts with notification type' do + reshare = Factory.create(:reshare) + Notification.should_receive(:notify) + receiver = Postzord::Receiver::LocalPostBatch.new(reshare, @ids) + receiver.notify_users + end + + it 'calls notify for posts with notification type' do + sm = Factory.create(:status_message, :author => alice.person) + receiver = Postzord::Receiver::LocalPostBatch.new(sm, @ids) + Notification.should_not_receive(:notify) + receiver.notify_users + end + end +end diff --git a/spec/lib/postzord/receiver/public.rb b/spec/lib/postzord/receiver/public.rb deleted file mode 100644 index 5b9259d09151c781610bc7829248ed62fb98e280..0000000000000000000000000000000000000000 --- a/spec/lib/postzord/receiver/public.rb +++ /dev/null @@ -1,32 +0,0 @@ -# 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/public') - -describe Postzord::Receiver::Public do - - describe '#initialize' do - - end - - describe '#verify_signature' do - - end - - describe '#collect_recipients' do - - end - - describe '#batch_insert_visibilities' do - - end - - describe '#batch_notify' do - - end - -end diff --git a/spec/lib/postzord/receiver/public_spec.rb b/spec/lib/postzord/receiver/public_spec.rb index 318cc1a023374621310a4024e82664b55b6f453c..7fab3872e2d7b2ae27574ecb6ef702b9fc38e43c 100644 --- a/spec/lib/postzord/receiver/public_spec.rb +++ b/spec/lib/postzord/receiver/public_spec.rb @@ -8,52 +8,60 @@ require File.join(Rails.root, 'lib/postzord') require File.join(Rails.root, 'lib/postzord/receiver/public') describe Postzord::Receiver::Public do + before do + @post = Factory.build(:status_message, :author => alice.person, :public => true) + @created_salmon = Salmon::Slap.create_by_user_and_activity(alice, @post.to_diaspora_xml) + @xml = @created_salmon.xml_for(nil) + end describe '#initialize' do - it 'sets xml as instance variable' do - receiver = Postzord::Receiver::Public.new("blah") - receiver.xml.should == 'blah' + it 'creates a Salmon instance variable' do + receiver = Postzord::Receiver::Public.new(@xml) + receiver.salmon.should_not be_nil end end - describe '#perform' do - it 'calls verify_signature' do + describe '#perform!' do + before do + @receiver = Postzord::Receiver::Public.new(@xml) + end + it 'calls verify_signature' do + @receiver.should_receive(:verified_signature?) + @receiver.perform! end context 'if signature is valid' do - it 'calls collect_recipients' do - + it 'calls recipient_user_ids' do + @receiver.should_receive(:recipient_user_ids) + @receiver.perform! end it 'saves the parsed object' do - + @receiver.should_receive(:save_object) + @receiver.perform! end - it 'calls batch_insert_visibilities' do - - end - - it 'calls batch_notify' do - + it 'enqueues a Job::ReceiveLocalBatch' do + Resque.should_receive(:enqueue).with(Job::ReceiveLocalBatch, anything, anything) + @receiver.perform! end end end - describe '#verify_signature' do - - end - - describe '#collect_recipients' do - - end - - describe '#batch_insert_visibilities' do - + describe '#verify_signature?' do + it 'calls Slap#verified_for_key?' do + receiver = Postzord::Receiver::Public.new(@xml) + receiver.salmon.should_receive(:verified_for_key?).with(instance_of(OpenSSL::PKey::RSA)) + receiver.verified_signature? + end end - describe '#batch_notify' do - + describe '#recipient_user_ids' do + it 'calls User.all_sharing_with_person' do + User.should_receive(:all_sharing_with_person).and_return(stub(:select => [])) + receiver = Postzord::Receiver::Public.new(@xml) + receiver.perform! + end end - end diff --git a/spec/lib/postzord/receiver_spec.rb b/spec/lib/postzord/receiver_spec.rb index 434073fb4a9be69ce2617e7efb7f1aa390650aba..bcc5fe2040498db25a63519369b66bd1615ead09 100644 --- a/spec/lib/postzord/receiver_spec.rb +++ b/spec/lib/postzord/receiver_spec.rb @@ -24,7 +24,7 @@ describe Postzord::Receiver do describe '.initialize' do it 'valid for local' do Webfinger.should_not_receive(:new) - Salmon::EncryptedSlap.should_not_receive(:parse) + Salmon::EncryptedSlap.should_not_receive(:from_xml) zord = Postzord::Receiver.new(@user, :person => @person2, :object => @original_post) zord.instance_variable_get(:@user).should_not be_nil @@ -37,7 +37,7 @@ describe Postzord::Receiver do web_mock = mock() web_mock.should_receive(:fetch).and_return true salmon_mock.should_receive(:author_email).and_return(true) - Salmon::EncryptedSlap.should_receive(:parse).with(@salmon_xml, @user).and_return(salmon_mock) + Salmon::EncryptedSlap.should_receive(:from_xml).with(@salmon_xml, @user).and_return(salmon_mock) Webfinger.should_receive(:new).and_return(web_mock) zord = Postzord::Receiver.new(@user, :salmon_xml => @salmon_xml) diff --git a/spec/models/jobs/receive_local_batch_spec.rb b/spec/models/jobs/receive_local_batch_spec.rb index a6eb038e0e6f71a3bb5e80aa0035e88185540367..5b9ba09f20673c458c218961074fa3c5ee2803b4 100644 --- a/spec/models/jobs/receive_local_batch_spec.rb +++ b/spec/models/jobs/receive_local_batch_spec.rb @@ -9,73 +9,10 @@ describe Job::ReceiveLocalBatch do @post = alice.build_post(:status_message, :text => 'Hey Bob') @post.save! end - describe '.perform' do - it 'calls .create_visibilities' do - Job::ReceiveLocalBatch.should_receive(:create_visibilities).with(@post, [bob.id]) - Job::ReceiveLocalBatch.perform(@post.id, [bob.id]) - end - it 'sockets to users' do - Job::ReceiveLocalBatch.should_receive(:socket_to_users).with(@post, [bob.id]) - Job::ReceiveLocalBatch.perform(@post.id, [bob.id]) - end - it 'notifies mentioned users' do - Job::ReceiveLocalBatch.should_receive(:notify_mentioned_users).with(@post) - Job::ReceiveLocalBatch.perform(@post.id, [bob.id]) - end - it 'notifies users' do - Job::ReceiveLocalBatch.should_receive(:notify_users).with(@post, [bob.id]) - Job::ReceiveLocalBatch.perform(@post.id, [bob.id]) - end - end - describe '.create_visibilities' do - it 'creates a visibility for each user' do - PostVisibility.exists?(:contact_id => bob.contact_for(alice.person).id, :post_id => @post.id).should be_false - Job::ReceiveLocalBatch.create_visibilities(@post, [bob.id]) - PostVisibility.exists?(:contact_id => bob.contact_for(alice.person).id, :post_id => @post.id).should be_true - end - it 'does not raise if a visibility already exists' do - PostVisibility.create!(:contact_id => bob.contact_for(alice.person).id, :post_id => @post.id) - lambda { - Job::ReceiveLocalBatch.create_visibilities(@post, [bob.id]) - }.should_not raise_error - end - end - describe '.socket_to_users' do - before do - @controller = mock() - SocketsController.stub(:new).and_return(@controller) - end - it 'sockets to each user' do - @controller.should_receive(:outgoing).with(bob.id, @post, instance_of(Hash)) - Job::ReceiveLocalBatch.socket_to_users(@post, [bob.id]) - end - end - describe '.notify_mentioned_users' do - it 'calls notify person for a mentioned person' do - @post = alice.build_post(:status_message, :text => "Hey @{Bob; #{bob.diaspora_handle}}") - @post.save! - Notification.should_receive(:notify).with(bob, anything, alice.person) - Job::ReceiveLocalBatch.notify_mentioned_users(@post) - end - it 'does not call notify person for a non-mentioned person' do - Notification.should_not_receive(:notify) - Job::ReceiveLocalBatch.notify_mentioned_users(@post) - end - end - describe '.notify_users' do - it 'calls notify for posts with notification type' do - reshare = Factory.create(:reshare) - Notification.should_receive(:notify) - - Job::ReceiveLocalBatch.notify_users(reshare, [bob.id]) - end - - it 'calls notify for posts with notification type' do - sm = Factory.create(:status_message, :author => alice.person) - Notification.should_not_receive(:notify) - - Job::ReceiveLocalBatch.notify_users(sm, [bob.id]) + describe '.perform' do + it 'calls Postzord::Receiver::LocalPostBatch' do + pending end end end diff --git a/spec/models/post_visibility_spec.rb b/spec/models/post_visibility_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..29fc538a4a1f7e39feed87c97b17b8a76b9f8a93 --- /dev/null +++ b/spec/models/post_visibility_spec.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 'spec_helper' + +describe PostVisibility do + describe '.batch_import' do + before do + @post = Factory(:status_message, :author => alice.person) + @contact = bob.contact_for(alice.person) + end + + it 'creates a visibility for each user' do + lambda { + PostVisibility.batch_import([@contact], @post) + }.should change { + PostVisibility.exists?(:contact_id => @contact.id, :post_id => @post.id) + }.from(false).to(true) + end + + it 'does not raise if a visibility already exists' do + PostVisibility.create!(:contact_id => @contact.id, :post_id => @post.id) + lambda { + PostVisibility.batch_import([@contact], @post) + }.should_not raise_error + end + end +end