Merge branch 'salmon-refactor'
This commit is contained in:
commit
bb88a6fb8c
11 changed files with 176 additions and 95 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,17 @@ namespace :db do
|
||||||
}
|
}
|
||||||
puts "everything should be peachy"
|
puts "everything should be peachy"
|
||||||
end
|
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
|
end
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ namespace :generate do
|
||||||
Rails.application.config.secret_token = '#{secret}'
|
Rails.application.config.secret_token = '#{secret}'
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
puts "YAY!!"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue