diff --git a/app/controllers/publics_controller.rb b/app/controllers/publics_controller.rb index 3a9cc2290..3b555bd1e 100644 --- a/app/controllers/publics_controller.rb +++ b/app/controllers/publics_controller.rb @@ -4,12 +4,14 @@ class PublicsController < ApplicationController require File.join(Rails.root, '/lib/diaspora/parser') + require File.join(Rails.root, '/lib/postzord/receiver/public') include Diaspora::Parser skip_before_filter :set_header_data skip_before_filter :which_action_and_user skip_before_filter :set_grammatical_gender before_filter :allow_cross_origin, :only => [:hcard, :host_meta, :webfinger] + before_filter :check_for_xml, :only => [:receive, :receive_public] respond_to :html respond_to :xml, :only => :post @@ -47,12 +49,12 @@ class PublicsController < ApplicationController render :text => params['hub.challenge'], :status => 202, :layout => false end - def receive - if params[:xml].nil? - render :nothing => true, :status => 422 - return - end + def receive_public + Postzord::Receiver::Public.new(params[:xml]) + render :nothing => true, :status => :ok + end + def receive person = Person.where(:guid => params[:guid]).first if person.nil? || person.owner_id.nil? @@ -66,4 +68,15 @@ class PublicsController < ApplicationController render :nothing => true, :status => 202 end + + + private + + def check_for_xml + if params[:xml].nil? + render :nothing => true, :status => 422 + return + end + end + end diff --git a/app/models/post.rb b/app/models/post.rb index 6a04e685f..a686d31a4 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -102,7 +102,7 @@ class Post < ActiveRecord::Base return local_post end elsif !local_post - if self.save + if self.save user.contact_for(person).receive_post(self) user.notify_if_mentioned(self) Rails.logger.info("event=receive payload_type=#{self.class} update=false status=complete sender=#{self.diaspora_handle}") diff --git a/config/routes.rb b/config/routes.rb index bbdec44f0..3b3a98371 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -122,6 +122,7 @@ Diaspora::Application.routes.draw do get 'hcard/users/:guid' => :hcard get '.well-known/host-meta' => :host_meta post 'receive/users/:guid' => :receive + post 'receive/public' => :receive_public get 'hub' => :hub end diff --git a/db/schema.rb b/db/schema.rb index 96d8cda46..ea38c2f40 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -83,7 +83,7 @@ ActiveRecord::Schema.define(:version => 20110911213207) do t.datetime "updated_at" end - add_index "conversation_visibilities", ["conversation_id", "person_id"], :name => "index_conversation_visibilities_usefully", :unique => true + add_index "conversation_visibilities", ["conversation_id", "person_id"], :name => "index_conversation_visibilities_on_conversation_id_and_person_id", :unique => true add_index "conversation_visibilities", ["conversation_id"], :name => "index_conversation_visibilities_on_conversation_id" add_index "conversation_visibilities", ["person_id"], :name => "index_conversation_visibilities_on_person_id" diff --git a/lib/salmon/salmon.rb b/lib/salmon/salmon.rb index 331322024..2164ba8b9 100644 --- a/lib/salmon/salmon.rb +++ b/lib/salmon/salmon.rb @@ -54,37 +54,47 @@ module Salmon salmon end - def self.parse(xml, user) + def self.parse(xml, user=nil) 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) + header_doc = slap.salmon_header(doc, user) slap.author_email= header_doc.search('uri').text.split("acct:").last 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 - - 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}" - end - + key_hash = {'key' => slap.aes_key, 'iv' => slap.iv} + slap.parsed_data = slap.parse_data(key_hash, user) + slap.sig = slap.magic_sig.sig slap.data_type = slap.magic_sig.data_type - raise ArgumentError, "Magic Signature data must be signed with RSA-SHA256, was #{slap.magic_sig.alg}" unless 'RSA-SHA256' == slap.magic_sig.alg - slap end + def parse_data(key_hash, user) + data = SalmonSlap.decode64url(self.magic_sig.data) + if user.present? + user.aes_decrypt(data, key_hash) + else + data + end + end + + # @return [Nokogiri::Doc] + def salmon_header(doc, user) + if user.present? + decrypted_header = user.decrypt(doc.search('encrypted_header').text) + Nokogiri::XML(decrypted_header) + else + doc.search('header') + end + end + def xml_for person xml =< @@ -190,10 +200,20 @@ HEADER env = self.new ns = {'me'=>'http://salmon-protocol.org/ns/magic-env'} env.encoding = doc.search('//me:env/me:encoding', ns).text.strip + + if env.encoding != 'base64url' + raise ArgumentError, "Magic Signature data must be encoded with base64url, was #{slap.magic_sig.encoding}" + end + env.data = doc.search('//me:env/me:data', ns).text env.alg = doc.search('//me:env/me:alg', ns).text.strip env.sig = doc.search('//me:env/me:sig', ns).text env.data_type = doc.search('//me:env/me:data', ns).first['type'].strip + + unless 'RSA-SHA256' == env.alg + raise ArgumentError, "Magic Signature data must be signed with RSA-SHA256, was #{env.alg}" + end + env end diff --git a/spec/controllers/publics_controller_spec.rb b/spec/controllers/publics_controller_spec.rb index d50b42ac6..16192a4a9 100644 --- a/spec/controllers/publics_controller_spec.rb +++ b/spec/controllers/publics_controller_spec.rb @@ -20,6 +20,24 @@ describe PublicsController do end end + describe '#receive_public' do + it 'succeeds' do + post :receive_public, :xml => "" + response.should be_success + end + + it 'returns a 422 if no xml is passed' do + post :receive_public + response.code.should == '422' + end + + it 'calls Postzord::Receiver:Public' do + xml = "stuff" + Postzord::Receiver::Public.should_receive(:new).with(xml) + post :receive_public, :xml => xml + end + end + describe '#receive' do let(:xml) { "" } diff --git a/spec/lib/salmon_salmon_spec.rb b/spec/lib/salmon_salmon_spec.rb deleted file mode 100644 index 54bba6d71..000000000 --- a/spec/lib/salmon_salmon_spec.rb +++ /dev/null @@ -1,94 +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' - -describe Salmon do - let(:user){alice} - let(:user2) {eve} - let(:user3) {Factory.create(:user)} - let(:post){ user.post :status_message, :text => "hi", :to => user.aspects.create(: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 - - context 'marshaling' do - let(:xml) {created_salmon.xml_for user2.person} - let(:parsed_salmon) { Salmon::SalmonSlap.parse(xml, user2)} - - it 'should parse out the aes key' do - parsed_salmon.aes_key.should == created_salmon.aes_key - end - - 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 - - end - - describe '#author' do - it 'should reference a local author' do - parsed_salmon.author.should == user.person - end - - it 'should fail if no author is found' do - parsed_salmon.author_email = 'tom@tom.joindiaspora.com' - - - proc {parsed_salmon.author.public_key}.should raise_error "did you remember to async webfinger?" - - 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