private messages wip

This commit is contained in:
danielvincent 2011-02-28 19:26:04 -08:00
parent f4e6d0d82b
commit c62e9db397
11 changed files with 247 additions and 193 deletions

View file

@ -18,7 +18,7 @@ class Comment < ActiveRecord::Base
xml_attr :diaspora_handle
belongs_to :post, :touch => true
belongs_to :person
belongs_to :author, :class_name => 'Person'
validates_presence_of :text, :post
validates_length_of :text, :maximum => 2500

View file

@ -12,6 +12,15 @@ class Conversation < ActiveRecord::Base
has_many :participants, :class_name => 'Person', :through => :conversation_visibilities, :source => :person
has_many :messages, :order => 'created_at ASC'
def self.create(opts={})
msg_opts = opts.delete(:message)
cnv = super(opts)
message = Message.new(msg_opts.merge({:conversation_id => cnv.id}))
message.save
cnv
end
def author
self.messages.first.author
end
@ -29,10 +38,11 @@ class Conversation < ActiveRecord::Base
self.participants << Webfinger.new(handle).fetch
end
end
def subscribers(user)
self.recipients
end
def receive(user, person)
cnv = Conversation.find_or_create_by_guid(self.attributes)
self.messages.each do |msg|

View file

@ -1,7 +1,9 @@
class Message < ActiveRecord::Base
include ROXML
include Diaspora::Guid
include Diaspora::Webhooks
include Diaspora::Relayable
xml_attr :text
xml_attr :created_at
@ -11,6 +13,16 @@ class Message < ActiveRecord::Base
belongs_to :author, :class_name => 'Person'
belongs_to :conversation
after_initialize do
#sign comment as commenter
self.author_signature = self.sign_with_key(self.author.owner.encryption_key) if self.author.owner
if !self.parent.blank? && self.parent.author.person.owns?(self.parent)
#sign comment as post owner
self.parent_author_signature = self.sign_with_key( self.parent.author.owner.encryption_key) if self.parent.author.owner
end
end
def diaspora_handle
self.author.diaspora_handle
end
@ -29,15 +41,15 @@ class Message < ActiveRecord::Base
end
end
def receive(user, person)
Message.find_or_create_by_guid(self.attributes)
def parent_class
Conversation
end
def subscribers(user)
if self.conversation.author == user.person
p = self.conversation.subscribers(user)
else
p = self.conversation.author
end
def parent
self.conversation
end
def parent= parent
self.conversation = parent
end
end

View file

@ -93,8 +93,8 @@ class Person < ActiveRecord::Base
end
end
def owns?(post)
self == post.person
def owns?(obj)
self == obj.author
end
def url

View file

@ -0,0 +1,11 @@
class AddSignaturesToMessage < ActiveRecord::Migration
def self.up
add_column(:messages, :author_signature, :text)
add_column(:messages, :parent_author_signature, :text)
end
def self.down
remove_column(:messages, :author_signature)
remove_column(:messages, :parent_author_signature)
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20110228220810) do
ActiveRecord::Schema.define(:version => 20110228233419) do
create_table "aspect_memberships", :force => true do |t|
t.integer "aspect_id", :null => false
@ -124,12 +124,14 @@ ActiveRecord::Schema.define(:version => 20110228220810) do
add_index "mentions", ["post_id"], :name => "index_mentions_on_post_id"
create_table "messages", :force => true do |t|
t.integer "conversation_id", :null => false
t.integer "author_id", :null => false
t.string "guid", :null => false
t.text "text", :null => false
t.integer "conversation_id", :null => false
t.integer "author_id", :null => false
t.string "guid", :null => false
t.text "text", :null => false
t.datetime "created_at"
t.datetime "updated_at"
t.text "author_signature"
t.text "parent_author_signature"
end
add_index "messages", ["author_id"], :name => "index_messages_on_author_id"

View file

@ -25,15 +25,15 @@ module Diaspora
if user.owns?(self.parent)
self.parent.subscribers(user)
elsif user.owns?(self)
[self.parent.person]
[self.parent.author]
end
end
def receive(user, person)
object = self.class.where(:guid => self.guid).first || self
unless object.parent.person == user.person || object.verify_parent_author_signature
Rails.logger.info("event=receive status=abort reason='object signature not valid' recipient=#{user.diaspora_handle} sender=#{self.parent.person.diaspora_handle} payload_type=#{self.class} parent_id=#{self.parent.id}")
unless object.parent.author == user.person || object.verify_parent_author_signature
Rails.logger.info("event=receive status=abort reason='object signature not valid' recipient=#{user.diaspora_handle} sender=#{self.parent.author.diaspora_handle} payload_type=#{self.class} parent_id=#{self.parent.id}")
return
end
@ -49,7 +49,7 @@ module Diaspora
Postzord::Dispatch.new(user, object).post
end
object.socket_to_user(user, :aspect_ids => object.parent.aspect_ids)
object.socket_to_user(user, :aspect_ids => object.parent.aspect_ids) if object.respond_to? :socket_to_user
object
end
@ -58,7 +58,7 @@ module Diaspora
end
def signature_valid?
verify_signature(creator_signature, person)
verify_signature(creator_signature, self.author)
end
def verify_signature(signature, person)
@ -89,7 +89,7 @@ module Diaspora
accessors = self.class.roxml_attrs.collect do |definition|
definition.accessor
end
['person', 'author_signature', 'parent_author_signature'].each do |acc|
['author', 'author_signature', 'parent_author_signature'].each do |acc|
accessors.delete acc
end
accessors
@ -102,11 +102,11 @@ module Diaspora
end
def verify_parent_author_signature
verify_signature(self.parent_author_signature, parent.person)
verify_signature(self.parent_author_signature, self.parent.author)
end
def signature_valid?
verify_signature(self.author_signature, person)
verify_signature(self.author_signature, self.author)
end

View file

@ -5,54 +5,77 @@
require 'spec_helper'
describe Diaspora::Relayable do
shared_examples_for "it is relayable" do
context 'encryption' do
describe '#parent_author_signature' do
it 'should sign the comment if the user is the post author' do
@object_by_parent_author.verify_parent_author_signature.should be_true
end
before do
@alices_aspect = alice.aspects.first
@bobs_aspect = bob.aspects.first
@remote_message = bob.post :status_message, :message => "hello", :to => @bobs_aspect.id
@message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id
end
it 'does not sign as the parent author is not parent' do
@object_by_recipient.author_signature = @object_by_recipient.send(:sign_with_key, @local_leia.encryption_key)
@object_by_recipient.verify_parent_author_signature.should be_false
end
describe '#parent_author_signature' do
it 'should sign the comment if the user is the post author' do
message = alice.post :status_message, :message => "hi", :to => @alices_aspect.id
alice.comment "Yeah, it was great", :on => message
message.comments.reset
message.comments.first.signature_valid?.should be_true
message.comments.first.verify_parent_author_signature.should be_true
it 'should verify a comment made on a remote post by a different contact' do
@object_by_recipient.author_signature = @object_by_recipient.send(:sign_with_key, @local_leia.encryption_key)
@object_by_recipient.parent_author_signature = @object_by_recipient.send(:sign_with_key, @local_luke.encryption_key)
@object_by_recipient.verify_parent_author_signature.should be_true
end
end
describe '#author_signature' do
it 'should sign as the object author' do
@object_on_remote_parent.signature_valid?.should be_true
@object_by_parent_author.signature_valid?.should be_true
@object_by_recipient.signature_valid?.should be_true
end
end
end
it 'should verify a comment made on a remote post by a different contact' do
comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message)
comment.author_signature = comment.send(:sign_with_key, bob.encryption_key)
comment.signature_valid?.should be_true
comment.verify_parent_author_signature.should be_false
comment.parent_author_signature = comment.send(:sign_with_key, alice.encryption_key)
comment.verify_parent_author_signature.should be_true
context 'propagation' do
describe '#receive' do
it 'does not overwrite a comment that is already in the db' do
lambda{
@dup_object_by_parent_author.receive(@local_leia, @local_luke.person)
}.should_not change(Comment, :count)
end
it 'does not process if post_creator_signature is invalid' do
@object_by_parent_author.delete # remove comment from db so we set a creator sig
@dup_object_by_parent_author.parent_author_signature = "dsfadsfdsa"
@dup_object_by_parent_author.receive(@local_leia, @local_luke.person).should == nil
end
it 'signs when the person receiving is the parent author' do
@object_by_recipient.save
@object_by_recipient.receive(@local_luke, @local_leia.person)
@object_by_recipient.reload.parent_author_signature.should_not be_blank
end
it 'dispatches when the person receiving is the parent author' do
p = Postzord::Dispatch.new(@local_luke, @object_by_recipient)
p.should_receive(:post)
Postzord::Dispatch.stub!(:new).and_return(p)
@object_by_recipient.receive(@local_luke, @local_leia.person)
end
it 'sockets to the user' do
@object_by_recipient.should_receive(:socket_to_user).exactly(3).times
@object_by_recipient.receive(@local_luke, @local_leia.person)
end
end
describe '#subscribers' do
it 'returns the posts original audience, if the post is owned by the user' do
@object_by_parent_author.subscribers(@local_luke).map(&:id).should =~ [@local_leia.person, @remote_raphael].map(&:id)
end
it 'returns the owner of the original post, if the user owns the comment' do
@object_by_recipient.subscribers(@local_leia).map(&:id).should =~ [@local_luke.person].map(&:id)
end
end
end
end
describe '#author_signature' do
it 'should attach the author signature if the user is commenting' do
comment = alice.comment "Yeah, it was great", :on => @remote_message
@remote_message.comments.reset
@remote_message.comments.first.signature_valid?.should be_true
end
it 'should reject comments on a remote post with only a author sig' do
comment = Comment.new(:person => bob.person, :text => "cats", :post => @remote_message)
comment.author_signature = comment.send(:sign_with_key, bob.encryption_key)
comment.signature_valid?.should be_true
comment.verify_parent_author_signature.should be_false
end
it 'should receive remote comments on a user post with a author sig' do
comment = Comment.new(:person => bob.person, :text => "cats", :post => @message)
comment.author_signature = comment.send(:sign_with_key, bob.encryption_key)
comment.signature_valid?.should be_true
comment.verify_parent_author_signature.should be_false
end
end
end

View file

@ -3,6 +3,7 @@
# the COPYRIGHT file.
require 'spec_helper'
require File.join(Rails.root, "spec", "lib", "diaspora", "relayable_spec")
describe Comment do
before do
@ -108,73 +109,19 @@ describe Comment do
end
end
context 'comment propagation' do
describe 'it is relayable' do
before do
@local_luke, @local_leia, @remote_raphael = set_up_friends
@person_status = Factory.create(:status_message, :person => @remote_raphael)
@user_status = @local_luke.post :status_message, :message => "hi", :to => @local_luke.aspects.first
@lukes_aspect = @local_luke.aspects.first
end
it 'should not clear the aspect post array on receiving a comment' do
@lukes_aspect.post_ids.include?(@user_status.id).should be_true
comment = Comment.new(:person_id => @remote_raphael.id, :text => "cats", :post => @user_status)
zord = Postzord::Receiver.new(alice, :person => @remote_raphael)
zord.parse_and_receive(comment.to_diaspora_xml)
@lukes_aspect.reload
@lukes_aspect.post_ids.include?(@user_status.id).should be_true
end
describe '#receive' do
before do
@comment = @local_luke.comment("yo", :on => @user_status)
@comment2 = @local_leia.build_comment("yo", :on => @user_status)
@new_c = @comment.dup
end
it 'does not overwrite a comment that is already in the db' do
lambda{
@new_c.receive(@local_leia, @local_luke.person)
}.should_not change(Comment, :count)
end
it 'does not process if post_creator_signature is invalid' do
@comment.delete # remove comment from db so we set a creator sig
@new_c.parent_author_signature = "dsfadsfdsa"
@new_c.receive(@local_leia, @local_luke.person).should == nil
end
it 'signs when the person receiving is the parent author' do
@comment2.save
@comment2.receive(@local_luke, @local_leia.person)
@comment2.reload.parent_author_signature.should_not be_blank
end
it 'dispatches when the person receiving is the parent author' do
p = Postzord::Dispatch.new(@local_luke, @comment2)
p.should_receive(:post)
Postzord::Dispatch.stub!(:new).and_return(p)
@comment2.receive(@local_luke, @local_leia.person)
end
it 'sockets to the user' do
@comment2.should_receive(:socket_to_user).exactly(3).times
@comment2.receive(@local_luke, @local_leia.person)
end
end
describe '#subscribers' do
it 'returns the posts original audience, if the post is owned by the user' do
comment = @local_luke.build_comment "yo", :on => @user_status
comment.subscribers(@local_luke).map(&:id).should =~ [@local_leia.person, @remote_raphael].map(&:id)
end
it 'returns the owner of the original post, if the user owns the comment' do
comment = @local_leia.build_comment "yo", :on => @user_status
comment.subscribers(@local_leia).map(&:id).should =~ [@local_luke.person].map(&:id)
end
@remote_parent = Factory.create(:status_message, :person => @remote_raphael)
@local_parent = @local_luke.post :status_message, :message => "hi", :to => @local_luke.aspects.first
@object_by_parent_author = @local_luke.comment("yo", :on => @local_parent)
@object_by_recipient = @local_leia.build_comment("yo", :on => @local_parent)
@dup_object_by_parent_author = @object_by_parent_author.dup
@object_on_remote_parent = @local_luke.comment("Yeah, it was great", :on => @remote_parent)
end
it_should_behave_like 'it is relayable'
end
end

View file

@ -1,3 +1,7 @@
# 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 Conversation do
@ -5,59 +9,75 @@ describe Conversation do
@user1 = alice
@user2 = bob
@create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff" }
@cnv = Conversation.create(@create_hash)
@create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff",
:message => {:author => @user1.person, :text => 'hey'}}
=begin
@message = Message.new(:author => @user1.person, :text => "stuff")
@cnv.messages << @message
@message.save
@xml = @cnv.to_diaspora_xml
=end
end
describe 'serialization' do
it 'serializes the message' do
@xml.gsub(/\s/, '').should include(@message.to_xml.to_s.gsub(/\s/, ''))
end
it 'serializes the participants' do
@create_hash[:participant_ids].each{|id|
@xml.should include(Person.find(id).diaspora_handle)
}
end
it 'serializes the created_at time' do
@xml.should include(@message.created_at.to_s)
end
it 'creates a message on create' do
lambda{
Conversation.create(@create_hash)
}.should change(Message, :count).by(1)
end
describe '#subscribers' do
it 'returns the recipients for the post owner' do
@cnv.subscribers(@user1).should == @user1.contacts.map{|c| c.person}
end
end
describe '#receive' do
context 'transport' do
before do
Conversation.delete_all
Message.delete_all
@cnv = Conversation.create(@create_hash)
@message = @cnv.messages.first
@xml = @cnv.to_diaspora_xml
end
it 'creates a message' do
lambda{
Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person)
}.should change(Message, :count).by(1)
describe 'serialization' do
it 'serializes the message' do
@xml.gsub(/\s/, '').should include(@message.to_xml.to_s.gsub(/\s/, ''))
end
it 'serializes the participants' do
@create_hash[:participant_ids].each{|id|
@xml.should include(Person.find(id).diaspora_handle)
}
end
it 'serializes the created_at time' do
@xml.should include(@message.created_at.to_s)
end
end
it 'creates a conversation' do
lambda{
Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person)
}.should change(Conversation, :count).by(1)
describe '#subscribers' do
it 'returns the recipients for the post owner' do
@cnv.subscribers(@user1).should == @user1.contacts.map{|c| c.person}
end
end
it 'creates appropriate visibilities' do
lambda{
Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person)
}.should change(ConversationVisibility, :count).by(@cnv.participants.count)
end
it 'does not save before receive' do
Diaspora::Parser.from_xml(@xml).persisted?.should be_false
describe '#receive' do
before do
Conversation.delete_all
Message.delete_all
end
it 'creates a message' do
lambda{
Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person)
}.should change(Message, :count).by(1)
end
it 'creates a conversation' do
lambda{
Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person)
}.should change(Conversation, :count).by(1)
end
it 'creates appropriate visibilities' do
lambda{
Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person)
}.should change(ConversationVisibility, :count).by(@cnv.participants.count)
end
it 'does not save before receive' do
Diaspora::Parser.from_xml(@xml).persisted?.should be_false
end
end
end
end

View file

@ -1,18 +1,39 @@
# 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, "spec", "lib", "diaspora", "relayable_spec")
describe Message do
before do
@user1 = alice
@user2 = bob
@create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff" }
@create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff",
:message => {:author => @user1.person, :text => "stuff"} }
@cnv = Conversation.create(@create_hash)
@message = Message.new(:author => @user1.person, :text => "stuff")
@cnv.messages << @message
@message.save
@message = @cnv.messages.first
@xml = @message.to_diaspora_xml
end
describe '#after_initialize' do
before do
@create_hash = { :participant_ids => [@user1.contacts.first.person.id, @user1.person.id], :subject => "cool stuff"}
@cnv = Conversation.new(@create_hash)
@cnv.save
@msg = Message.new(:text => "21312", :conversation => @cnv)
end
it 'signs the message' do
@msg.author_signature.should_not be_blank
end
it 'signs the message author if author of conversation' do
@msg.parent_author_signature.should_not be_blank
end
end
describe 'serialization' do
it 'serializes the text' do
@xml.should include(@message.text)
@ -30,24 +51,32 @@ describe Message do
end
end
describe '#subscribers' do
it 'returns the recipients for the post owner' do
@message.subscribers(@user1).should == @user1.contacts.map{|c| c.person}
end
it 'returns the conversation author for the post owner' do
@message.subscribers(@user2).should == @user1.person
end
end
describe '#receive' do
describe 'it is relayable' do
before do
Message.delete_all
end
@local_luke, @local_leia, @remote_raphael = set_up_friends
it 'creates a message' do
lambda{
Diaspora::Parser.from_xml(@xml).receive(@user1, @user2.person)
}.should change(Message, :count).by(1)
cnv_hash = {:subject => 'cool story, bro', :participant_ids => [@local_luke.person, @local_leia.person, @remote_raphael].map(&:id),
:message => {:author => @remote_raphael, :text => 'hey'}}
@remote_parent = Conversation.create(cnv_hash.dup)
cnv_hash[:message][:author] = @local_luke.person
@local_parent = Conversation.create(cnv_hash)
msg_hash = {:author => @local_luke.person, :text => 'yo', :conversation => @local_parent}
@object_by_parent_author = Message.create(msg_hash.dup)
Postzord::Dispatch.new(@local_luke, @object_by_parent_author).post
msg_hash[:author] = @local_leia.person
@object_by_recipient = Message.create(msg_hash.dup)
@dup_object_by_parent_author = @object_by_parent_author.dup
msg_hash[:author] = @local_luke.person
msg_hash[:conversation] = @remote_parent
@object_on_remote_parent = Message.create(msg_hash)
Postzord::Dispatch.new(@local_luke, @object_on_remote_parent).post
end
it_should_behave_like 'it is relayable'
end
end