diff --git a/app/models/user.rb b/app/models/user.rb index ca248a8f29ecd51a1184eee7425b2484c1f26918..2f63f4b5f124195f52879f55257373b3449fe4d4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -166,26 +166,28 @@ class User aspect.save target_people = target_people | aspect.people } + push_to_people(post, target_people) end def push_to_people(post, people) + salmon = salmon(post) people.each{|person| - salmon(post, :to => person) + xml = salmon.xml_for person + push_to_person( person, xml) } end def push_to_person( person, xml ) Rails.logger.debug("Adding xml for #{self} to message queue to #{url}") - QUEUE.add_post_request( person.receive_url, person.encrypt(xml) ) + QUEUE.add_post_request( person.receive_url, xml ) QUEUE.process end - def salmon( post, opts = {} ) - salmon = Salmon::SalmonSlap.create(self, post.to_diaspora_xml) - push_to_person( opts[:to], salmon.to_xml) - salmon + def salmon( post ) + created_salmon = Salmon::SalmonSlap.create(self, post.to_diaspora_xml) + created_salmon end ######## Commenting ######## @@ -217,7 +219,7 @@ class User push_to_people comment, people_in_aspects(aspects_with_post(comment.post.id)) elsif owns? comment comment.save - salmon comment, :to => comment.post.person + push_to_people comment, [comment.post.person] end end diff --git a/config/deploy_config.yml b/config/deploy_config.yml index c7477314a2b359c2e9b6b15dafbda3602d4a8eed..c53f90d60b15e233e73eb0952252adacc21099ac 100644 --- a/config/deploy_config.yml +++ b/config/deploy_config.yml @@ -6,7 +6,7 @@ cross_server: deploy_to: '/usr/local/app/diaspora' user: 'root' repo: 'git://github.com/diaspora/diaspora.git' - branch: 'master' + branch: 'salmon_refactor' default_env: 'development' servers: tom: diff --git a/lib/diaspora/user/friending.rb b/lib/diaspora/user/friending.rb index 0f6cc6c5ce5fb9bc82be625cfb2c417802f4c4f0..36850c745473a41750c3f5ce00988edf8a164c08 100644 --- a/lib/diaspora/user/friending.rb +++ b/lib/diaspora/user/friending.rb @@ -22,7 +22,7 @@ module Diaspora aspect.requests << request aspect.save - salmon request, :to => desired_friend + push_to_people request, [desired_friend] end request end @@ -80,7 +80,7 @@ module Diaspora def unfriend(bad_friend) Rails.logger.info("#{self.real_name} is unfriending #{bad_friend.inspect}") retraction = Retraction.for(self) - salmon( retraction, :to => bad_friend) + push_to_people retraction, [bad_friend] remove_friend(bad_friend) end diff --git a/lib/diaspora/user/receiving.rb b/lib/diaspora/user/receiving.rb index 5d737d8e91d157bf76fce88c290ed8b49259493e..a16727c2681dfb8ea77eaf2e99bc04bac4cf88f3 100644 --- a/lib/diaspora/user/receiving.rb +++ b/lib/diaspora/user/receiving.rb @@ -1,12 +1,11 @@ module Diaspora module UserModules module Receiving - def receive_salmon ciphertext - cleartext = decrypt( ciphertext) - salmon = Salmon::SalmonSlap.parse cleartext + def receive_salmon salmon_xml + salmon = Salmon::SalmonSlap.parse salmon_xml, self if salmon.verified_for_key?(salmon.author.public_key) - Rails.logger.info("data in salmon: #{salmon.data}") - self.receive(salmon.data) + Rails.logger.info("data in salmon: #{salmon.parsed_data}") + self.receive(salmon.parsed_data) end end diff --git a/lib/salmon/salmon.rb b/lib/salmon/salmon.rb index 5d285719691a8f80f56c8cf846dd78c8b44ede97..4258a8d88406289bd4c4baaadfcc718ed14fb056 100644 --- a/lib/salmon/salmon.rb +++ b/lib/salmon/salmon.rb @@ -41,16 +41,37 @@ end module Salmon class SalmonSlap - attr_accessor :magic_sig, :author, :author_email, :data, :data_type, :sig - def self.parse(xml) + attr_accessor :magic_sig, :author, :author_email, :aes_key, :iv, :parsed_data, + :data_type, :sig + + def self.create(user, activity) + salmon = self.new + salmon.author = user.person + aes_key_hash = user.person.gen_aes_key + salmon.aes_key = aes_key_hash['key'] + salmon.iv = aes_key_hash['iv'] + salmon.magic_sig = MagicSigEnvelope.create(user , user.person.aes_encrypt(activity, aes_key_hash)) + salmon + end + + def self.parse(xml, user) slap = self.new doc = Nokogiri::XML(xml) sig_doc = doc.search('entry') + + ### Header ## + decrypted_header = user.decrypt(doc.search('encrypted_header').text) + header_doc = Nokogiri::XML(decrypted_header) + slap.aes_key = header_doc.search('aes_key').text + slap.iv = header_doc.search('iv').text + slap.magic_sig = MagicSigEnvelope.parse sig_doc if 'base64url' == slap.magic_sig.encoding - slap.data = decode64url(slap.magic_sig.data) + + key_hash = {'key' => slap.aes_key, 'iv' => slap.iv} + slap.parsed_data = user.aes_decrypt(decode64url(slap.magic_sig.data), key_hash) slap.sig = slap.magic_sig.sig else raise ArgumentError, "Magic Signature data must be encoded with base64url, was #{slap.magic_sig.encoding}" @@ -65,17 +86,11 @@ module Salmon slap end - def self.create(user, activity) - salmon = self.new - salmon.author = user.person - salmon.magic_sig = MagicSigEnvelope.create(user , activity) - salmon - end - - def to_xml + def xml_for person xml =<<ENTRY <?xml version='1.0' encoding='UTF-8'?> <entry xmlns='http://www.w3.org/2005/Atom'> + <encrypted_header>#{person.encrypt(decrypted_header)}</encrypted_header> <author> <name>#{@author.real_name}</name> <uri>acct:#{@author.diaspora_handle}</uri> @@ -86,6 +101,19 @@ ENTRY end + def decrypted_header + header =<<HEADER + <decrypted_header> + <iv>#{iv}</iv> + <aes_key>#{aes_key}</aes_key> + <author> + <name>#{@author.real_name}</name> + <uri>acct:#{@author.diaspora_handle}</uri> + </author> + </decrypted_header> +HEADER + end + def author if @author @author diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 396bb145de622eaa5e8a9b30b83e9828c8db54a6..82fc6fa9f6e2b2c3a9b5f83c7d1536c2241a4c35 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -65,4 +65,17 @@ namespace :db do } puts "everything should be peachy" end + + task :move_private_key do + User.all.each do |user| + if user.private_key.nil? + user.private_key = user.person.serialized_key + user.save + person = user.person + person.serialized_key = nil + person.serialized_public_key = user.encryption_key.public_key + person.save + end + end + end end diff --git a/lib/tasks/generate_session_secret.rake b/lib/tasks/generate_session_secret.rake index 90274dff34246b3b612b423fce4758d32ca32e4d..c9fc129de4dbdb7e25243cba27c4e7814784016b 100644 --- a/lib/tasks/generate_session_secret.rake +++ b/lib/tasks/generate_session_secret.rake @@ -19,7 +19,6 @@ namespace :generate do Rails.application.config.secret_token = '#{secret}' EOF -puts "YAY!!" end end diff --git a/spec/controllers/publics_controller_spec.rb b/spec/controllers/publics_controller_spec.rb index 70d851a3e066523d1a3986fa58c278e273a853ae..d8d700bd8504fc2581ca480cdb4a74d3c13606b9 100644 --- a/spec/controllers/publics_controller_spec.rb +++ b/spec/controllers/publics_controller_spec.rb @@ -5,31 +5,32 @@ require 'spec_helper' describe PublicsController do - render_views + render_views + let(:user) {Factory.create :user} + let(:user2){Factory.create :user} before do - @user = Factory.create(:user) - sign_in :user, @user + sign_in :user, user end describe 'receive endpoint' do it 'should have a and endpoint and return a 200 on successful receipt of a request' do - post :receive, :id =>@user.person.id + post :receive, :id =>user.person.id response.code.should == '200' end it 'should accept a post from another node and save the information' do - user2 = Factory.create(:user) message = user2.build_post(:status_message, :message => "hi") - @user.reload - @user.visible_post_ids.include?(message.id).should be false - xml = @user.person.encrypt(user2.salmon(message, :to => @user.person).to_xml) + user.reload + user.visible_post_ids.include?(message.id).should be false + + xml = user2.salmon(message).xml_for(user.person) - post :receive, :id => @user.person.id, :xml => xml + post :receive, :id => user.person.id, :xml => xml - @user.reload - @user.visible_post_ids.include?(message.id).should be true + user.reload + user.visible_post_ids.include?(message.id).should be true end end @@ -41,35 +42,29 @@ describe PublicsController do end describe 'friend requests' do + let(:aspect2) {user2.aspect(:name => 'disciples')} + let!(:req) {user2.send_friend_request_to(user.person, aspect2)} + let!(:xml) {user2.salmon(req).xml_for(user.person)} before do - @user2 = Factory.create(:user) - aspect = @user2.aspect(:name => 'disciples') - - @user3 = Factory.create(:user) - - req = @user2.send_friend_request_to(@user.person, aspect) - - @xml = @user.person.encrypt(@user2.salmon(req, :to => @user.person).to_xml) - req.delete - @user2.reload - @user2.pending_requests.count.should be 1 + user2.reload + user2.pending_requests.count.should be 1 end it 'should add the pending request to the right user if the target person exists locally' do - @user2.delete - post :receive, :id => @user.person.id, :xml => @xml + user2.delete + post :receive, :id => user.person.id, :xml => xml - assigns(:user).should eq(@user) + assigns(:user).should eq(user) end it 'should add the pending request to the right user if the target person does not exist locally' do - Person.should_receive(:by_webfinger).with(@user2.person.diaspora_handle).and_return(@user2.person) - @user2.person.delete - @user2.delete - post :receive, :id => @user.person.id, :xml => @xml + Person.should_receive(:by_webfinger).with(user2.person.diaspora_handle).and_return(user2.person) + user2.person.delete + user2.delete + post :receive, :id => user.person.id, :xml => xml - assigns(:user).should eq(@user) + assigns(:user).should eq(user) end end end diff --git a/spec/lib/salmon_salmon_spec.rb b/spec/lib/salmon_salmon_spec.rb index 6d467d4eac26338b96b2e16cea15baed58d5ba5e..e10ad5b48d5f0dd1575c3aafd7aecda6a6baf97a 100644 --- a/spec/lib/salmon_salmon_spec.rb +++ b/spec/lib/salmon_salmon_spec.rb @@ -5,52 +5,97 @@ require 'spec_helper' describe Salmon do - before do - - @user = Factory.create :user - @post = @user.post :status_message, :message => "hi", :to => @user.aspect(:name => "sdg").id - @sent_salmon = Salmon::SalmonSlap.create(@user, @post.to_diaspora_xml) - @parsed_salmon = Salmon::SalmonSlap.parse @sent_salmon.to_xml - stub_success("tom@tom.joindiaspora.com") + let(:user){Factory.create :user} + let(:user2) {Factory.create :user} + let(:user3) {Factory.create :user} + let(:post){ user.post :status_message, :message => "hi", :to => user.aspect(:name => "sdg").id } + + let!(:created_salmon) {Salmon::SalmonSlap.create(user, post.to_diaspora_xml)} + + describe '#create' do + + it 'has data in the magic envelope' do + created_salmon.magic_sig.data.should_not be nil + end + + it 'has no parsed_data' do + created_salmon.parsed_data.should be nil + end + + it 'sets aes and iv key' do + created_salmon.aes_key.should_not be nil + created_salmon.iv.should_not be nil + end + + it 'makes the data in the signature encrypted with that key' do + key_hash = {'key' => created_salmon.aes_key, 'iv' => created_salmon.iv} + decoded_string = Salmon::SalmonSlap.decode64url(created_salmon.magic_sig.data) + user.aes_decrypt(decoded_string, key_hash).should == post.to_diaspora_xml + end + end + + describe '#xml_for' do + let(:xml) {created_salmon.xml_for user2.person} + + it 'has a encrypted header field' do + xml.include?("encrypted_header").should be true + end + + it 'the encrypted_header field should contain the aes key' do + doc = Nokogiri::XML(xml) + decrypted_header = user2.decrypt(doc.search('encrypted_header').text) + decrypted_header.include?(created_salmon.aes_key).should be true + end end - it 'should verify the signature on a roundtrip' do + context 'marshaling' do + let(:xml) {created_salmon.xml_for user2.person} + let(:parsed_salmon) { Salmon::SalmonSlap.parse(xml, user2)} - @sent_salmon.magic_sig.data.should == @parsed_salmon.magic_sig.data + it 'should parse out the aes key' do + parsed_salmon.aes_key.should == created_salmon.aes_key + end - @sent_salmon.magic_sig.sig.should == @parsed_salmon.magic_sig.sig - @sent_salmon.magic_sig.signable_string.should == @parsed_salmon.magic_sig.signable_string + it 'should parse out the iv' do + parsed_salmon.iv.should == created_salmon.iv + end + it 'should parse out the authors diaspora_handle' do + parsed_salmon.author_email.should == user.person.diaspora_handle - @parsed_salmon.verified_for_key?(OpenSSL::PKey::RSA.new(@user.exported_key)).should be true - @sent_salmon.verified_for_key?(OpenSSL::PKey::RSA.new(@user.exported_key)).should be true - end + end - it 'should return the data so it can be "received"' do + describe '#author' do + before do + stub_success("tom@tom.joindiaspora.com") + end - xml = @post.to_diaspora_xml + it 'should reference a local author' do + parsed_salmon.author.should == user.person + end - @parsed_salmon.data.should == xml - end + it 'should reference a remote author' do + parsed_salmon.author_email = 'tom@tom.joindiaspora.com' + parsed_salmon.author.public_key.should_not be_nil + end - it 'should parse out the authors diaspora_handle' do - @parsed_salmon.author_email.should == @user.person.diaspora_handle + it 'should fail to reference a nonexistent remote author' do + parsed_salmon.author_email = 'idsfug@difgubhpsduh.rgd' + proc { + Redfinger.stub(:finger).and_return(nil) #Redfinger returns nil when there is no profile + parsed_salmon.author.real_name}.should raise_error /No webfinger profile found/ + end + end - end + it 'verifies the signature for the sender' do + parsed_salmon.verified_for_key?(user.public_key).should be true + end - it 'should reference a local author' do - @parsed_salmon.author.should == @user.person - end + it 'contains the original data' do + parsed_salmon.parsed_data.should == post.to_diaspora_xml + end - it 'should reference a remote author' do - @parsed_salmon.author_email = 'tom@tom.joindiaspora.com' - @parsed_salmon.author.public_key.should_not be_nil end - it 'should fail to reference a nonexistent remote author' do - @parsed_salmon.author_email = 'idsfug@difgubhpsduh.rgd' - proc { - Redfinger.stub(:finger).and_return(nil) #Redfinger returns nil when there is no profile - @parsed_salmon.author.real_name}.should raise_error /No webfinger profile found/ - end + end diff --git a/spec/models/user/posting_spec.rb b/spec/models/user/posting_spec.rb index d86433ad60fc9f7babba9a3d0c8654af50bc1980..518c03d2d88a13b618860e0256c160ac8e8546c7 100644 --- a/spec/models/user/posting_spec.rb +++ b/spec/models/user/posting_spec.rb @@ -71,19 +71,19 @@ describe User do describe '#push_to_aspects' do it 'should push a post to a aspect' do - user.should_receive(:salmon).twice + user.should_receive(:push_to_person).twice user.push_to_aspects(post, aspect.id) end it 'should push a post to all aspects' do - user.should_receive(:salmon).exactly(3).times + user.should_receive(:push_to_person).exactly(3).times user.push_to_aspects(post, :all) end end describe '#push_to_people' do it 'should push to people' do - user.should_receive(:salmon).twice + user.should_receive(:push_to_person).twice user.push_to_people(post, [user2.person, user3.person]) end end diff --git a/spec/models/user/receive_spec.rb b/spec/models/user/receive_spec.rb index 5bdddce67a5fc8ef440be1adf73ac0e3124169a2..fce699cf68bde0d97e910c7165280a47fff44ca5 100644 --- a/spec/models/user/receive_spec.rb +++ b/spec/models/user/receive_spec.rb @@ -173,11 +173,11 @@ describe User do describe 'salmon' do before do @post = @user.post :status_message, :message => "hello", :to => @aspect.id - @salmon = @user.salmon( @post, :to => @user2.person ) + @salmon = @user.salmon( @post ) end it 'should receive a salmon for a post' do - @user2.receive_salmon( @user2.person.encrypt(@salmon.to_xml) ) + @user2.receive_salmon( @salmon.xml_for @user2.person ) @user2.visible_post_ids.include?(@post.id).should be true end end