Merge branch 'master' of github.com:diaspora/diaspora

This commit is contained in:
ilya 2010-10-05 12:17:07 -07:00
commit 05968db7be
11 changed files with 172 additions and 104 deletions

View file

@ -166,26 +166,28 @@ class User
aspect.save aspect.save
target_people = target_people | aspect.people target_people = target_people | aspect.people
} }
push_to_people(post, target_people) push_to_people(post, target_people)
end end
def push_to_people(post, people) def push_to_people(post, people)
salmon = salmon(post)
people.each{|person| people.each{|person|
salmon(post, :to => person) xml = salmon.xml_for person
push_to_person( person, xml)
} }
end end
def push_to_person( person, xml ) def push_to_person( person, xml )
Rails.logger.debug("Adding xml for #{self} to message queue to #{url}") 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 QUEUE.process
end end
def salmon( post, opts = {} ) def salmon( post )
salmon = Salmon::SalmonSlap.create(self, post.to_diaspora_xml) created_salmon = Salmon::SalmonSlap.create(self, post.to_diaspora_xml)
push_to_person( opts[:to], salmon.to_xml) created_salmon
salmon
end end
######## Commenting ######## ######## Commenting ########
@ -217,7 +219,7 @@ class User
push_to_people comment, people_in_aspects(aspects_with_post(comment.post.id)) push_to_people comment, people_in_aspects(aspects_with_post(comment.post.id))
elsif owns? comment elsif owns? comment
comment.save comment.save
salmon comment, :to => comment.post.person push_to_people comment, [comment.post.person]
end end
end end

View file

@ -6,7 +6,7 @@ cross_server:
deploy_to: '/usr/local/app/diaspora' deploy_to: '/usr/local/app/diaspora'
user: 'root' user: 'root'
repo: 'git://github.com/diaspora/diaspora.git' repo: 'git://github.com/diaspora/diaspora.git'
branch: 'master' branch: 'salmon_refactor'
default_env: 'development' default_env: 'development'
servers: servers:
tom: tom:

View file

@ -9,10 +9,9 @@
#end #end
package :mongodb, :provides => :database do package :mongodb, :provides => :database do
description 'Mongodb debian package.' description 'Mongodb binary.'
version '1.4.3' version '1.6.3'
binary "http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-static-legacy-#{version}.tgz" do
binary "http://downloads.mongodb.org/linux/mongodb-linux-x86_64-static-legacy-#{version}.tgz" do
post :install, "ln -s -f /usr/local/bin/mongodb-linux-x86_64-static-#{version}/bin/mongod /usr/bin/mongod" post :install, "ln -s -f /usr/local/bin/mongodb-linux-x86_64-static-#{version}/bin/mongod /usr/bin/mongod"
end end
end end

View file

@ -6,9 +6,9 @@
package :ruby do package :ruby do
description 'Ruby Virtual Machine' description 'Ruby Virtual Machine'
version '1.8.7' version '1.9.2'
patchlevel '249' patchlevel '0'
source "ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-#{version}-p#{patchlevel}.tar.gz" # implicit :style => :gnu source "ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-#{version}-p#{patchlevel}.tar.gz"
requires :ruby_dependencies requires :ruby_dependencies
end end
@ -23,21 +23,21 @@ package :rubygems do
source "http://production.cf.rubygems.org/rubygems/rubygems-#{version}.tgz" do source "http://production.cf.rubygems.org/rubygems/rubygems-#{version}.tgz" do
custom_install 'ruby setup.rb' custom_install 'ruby setup.rb'
end end
run( "PATH=$PATH:/var/lib/gems/1.8/bin") run( "PATH=$PATH:/var/lib/gems/1.9/bin")
run( "export PATH") run( "export PATH")
requires :ruby requires :ruby
end end
package :bundler do package :bundler do
description 'bundler' description 'bundler'
version '0.9.26' version '1.0.0'
gem 'bundler' gem 'bundler'
requires :rubygems requires :rubygems
end end
package :diaspora_dependencies do package :diaspora_dependencies do
description 'random dependencies' description 'random dependencies'
apt %w(libxslt1.1 libxslt1-dev libxml2 libgpgme11-dev imagemagick libmagick9-dev) apt %w(libxslt1.1 libxslt1-dev libxml2 imagemagick libmagick9-dev)
end end
#package :diaspora do #package :diaspora do
# description 'Diaspora' # description 'Diaspora'

View file

@ -22,7 +22,7 @@ module Diaspora
aspect.requests << request aspect.requests << request
aspect.save aspect.save
salmon request, :to => desired_friend push_to_people request, [desired_friend]
end end
request request
end end
@ -80,7 +80,7 @@ module Diaspora
def unfriend(bad_friend) def unfriend(bad_friend)
Rails.logger.info("#{self.real_name} is unfriending #{bad_friend.inspect}") Rails.logger.info("#{self.real_name} is unfriending #{bad_friend.inspect}")
retraction = Retraction.for(self) retraction = Retraction.for(self)
salmon( retraction, :to => bad_friend) push_to_people retraction, [bad_friend]
remove_friend(bad_friend) remove_friend(bad_friend)
end end

View file

@ -1,12 +1,11 @@
module Diaspora module Diaspora
module UserModules module UserModules
module Receiving module Receiving
def receive_salmon ciphertext def receive_salmon salmon_xml
cleartext = decrypt( ciphertext) salmon = Salmon::SalmonSlap.parse salmon_xml, self
salmon = Salmon::SalmonSlap.parse cleartext
if salmon.verified_for_key?(salmon.author.public_key) if salmon.verified_for_key?(salmon.author.public_key)
Rails.logger.info("data in salmon: #{salmon.data}") Rails.logger.info("data in salmon: #{salmon.parsed_data}")
self.receive(salmon.data) self.receive(salmon.parsed_data)
end end
end end

View file

@ -41,16 +41,37 @@ end
module Salmon module Salmon
class SalmonSlap class SalmonSlap
attr_accessor :magic_sig, :author, :author_email, :data, :data_type, :sig attr_accessor :magic_sig, :author, :author_email, :aes_key, :iv, :parsed_data,
def self.parse(xml) :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 slap = self.new
doc = Nokogiri::XML(xml) doc = Nokogiri::XML(xml)
sig_doc = doc.search('entry') 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 slap.magic_sig = MagicSigEnvelope.parse sig_doc
if 'base64url' == slap.magic_sig.encoding 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 slap.sig = slap.magic_sig.sig
else else
raise ArgumentError, "Magic Signature data must be encoded with base64url, was #{slap.magic_sig.encoding}" raise ArgumentError, "Magic Signature data must be encoded with base64url, was #{slap.magic_sig.encoding}"
@ -65,17 +86,11 @@ module Salmon
slap slap
end end
def self.create(user, activity) def xml_for person
salmon = self.new
salmon.author = user.person
salmon.magic_sig = MagicSigEnvelope.create(user , activity)
salmon
end
def to_xml
xml =<<ENTRY xml =<<ENTRY
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom'> <entry xmlns='http://www.w3.org/2005/Atom'>
<encrypted_header>#{person.encrypt(decrypted_header)}</encrypted_header>
<author> <author>
<name>#{@author.real_name}</name> <name>#{@author.real_name}</name>
<uri>acct:#{@author.diaspora_handle}</uri> <uri>acct:#{@author.diaspora_handle}</uri>
@ -86,6 +101,19 @@ ENTRY
end 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 def author
if @author if @author
@author @author

View file

@ -5,31 +5,32 @@
require 'spec_helper' require 'spec_helper'
describe PublicsController do describe PublicsController do
render_views render_views
let(:user) {Factory.create :user}
let(:user2){Factory.create :user}
before do before do
@user = Factory.create(:user) sign_in :user, user
sign_in :user, @user
end end
describe 'receive endpoint' do describe 'receive endpoint' do
it 'should have a and endpoint and return a 200 on successful receipt of a request' 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' response.code.should == '200'
end end
it 'should accept a post from another node and save the information' do 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") message = user2.build_post(:status_message, :message => "hi")
@user.reload user.reload
@user.visible_post_ids.include?(message.id).should be false user.visible_post_ids.include?(message.id).should be false
xml = @user.person.encrypt(user2.salmon(message, :to => @user.person).to_xml)
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.reload
@user.visible_post_ids.include?(message.id).should be true user.visible_post_ids.include?(message.id).should be true
end end
end end
@ -41,35 +42,29 @@ describe PublicsController do
end end
describe 'friend requests' do 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 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 req.delete
@user2.reload user2.reload
@user2.pending_requests.count.should be 1 user2.pending_requests.count.should be 1
end end
it 'should add the pending request to the right user if the target person exists locally' do it 'should add the pending request to the right user if the target person exists locally' do
@user2.delete user2.delete
post :receive, :id => @user.person.id, :xml => @xml post :receive, :id => user.person.id, :xml => xml
assigns(:user).should eq(@user) assigns(:user).should eq(user)
end end
it 'should add the pending request to the right user if the target person does not exist locally' do 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) Person.should_receive(:by_webfinger).with(user2.person.diaspora_handle).and_return(user2.person)
@user2.person.delete user2.person.delete
@user2.delete user2.delete
post :receive, :id => @user.person.id, :xml => @xml post :receive, :id => user.person.id, :xml => xml
assigns(:user).should eq(@user) assigns(:user).should eq(user)
end end
end end
end end

View file

@ -5,52 +5,97 @@
require 'spec_helper' require 'spec_helper'
describe Salmon do describe Salmon do
before do 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 }
@user = Factory.create :user let!(:created_salmon) {Salmon::SalmonSlap.create(user, post.to_diaspora_xml)}
@post = @user.post :status_message, :message => "hi", :to => @user.aspect(:name => "sdg").id
@sent_salmon = Salmon::SalmonSlap.create(@user, @post.to_diaspora_xml) describe '#create' do
@parsed_salmon = Salmon::SalmonSlap.parse @sent_salmon.to_xml
stub_success("tom@tom.joindiaspora.com") 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 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 it 'should parse out the iv' do
@sent_salmon.magic_sig.signable_string.should == @parsed_salmon.magic_sig.signable_string 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 end
@sent_salmon.verified_for_key?(OpenSSL::PKey::RSA.new(@user.exported_key)).should be true
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 it 'should reference a remote author' do
end 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 it 'should fail to reference a nonexistent remote author' do
@parsed_salmon.author_email.should == @user.person.diaspora_handle 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
it 'verifies the signature for the sender' do
parsed_salmon.verified_for_key?(user.public_key).should be true
end
it 'contains the original data' do
parsed_salmon.parsed_data.should == post.to_diaspora_xml
end
end end
it 'should reference a local author' do
@parsed_salmon.author.should == @user.person
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 end

View file

@ -71,19 +71,19 @@ describe User do
describe '#push_to_aspects' do describe '#push_to_aspects' do
it 'should push a post to a aspect' 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) user.push_to_aspects(post, aspect.id)
end end
it 'should push a post to all aspects' do 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) user.push_to_aspects(post, :all)
end end
end end
describe '#push_to_people' do describe '#push_to_people' do
it 'should 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]) user.push_to_people(post, [user2.person, user3.person])
end end
end end

View file

@ -173,11 +173,11 @@ describe User do
describe 'salmon' do describe 'salmon' do
before do before do
@post = @user.post :status_message, :message => "hello", :to => @aspect.id @post = @user.post :status_message, :message => "hello", :to => @aspect.id
@salmon = @user.salmon( @post, :to => @user2.person ) @salmon = @user.salmon( @post )
end end
it 'should receive a salmon for a post' do 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 @user2.visible_post_ids.include?(@post.id).should be true
end end
end end