Commit 58a5a881 authored by Benjamin Neff's avatar Benjamin Neff

receive local

* Contact: auto-follow-back
* Shareable: create share visibilities
parent ae96b4bf
......@@ -93,7 +93,13 @@ class Contact < ActiveRecord::Base
end
end
# Follows back if user setting is set so
def receive(_recipient_user_ids)
user.share_with(person, user.auto_follow_back_aspect) if user.auto_follow_back && !receiving
end
private
def not_contact_with_closed_account
if person_id && person.closed_account?
errors[:base] << 'Cannot be in contact with a closed account'
......
......@@ -89,17 +89,4 @@ class Conversation < ActiveRecord::Base
def subscribers(user)
self.recipients
end
def receive(user, person)
cnv = Conversation.create_with(self.attributes).find_or_create_by!(guid: guid)
self.participants.each do |participant|
ConversationVisibility.find_or_create_by(conversation_id: cnv.id, person_id: participant.id)
end
self.messages.each do |msg|
msg.conversation_id = cnv.id
msg.receive(user, person)
end
end
end
......@@ -146,10 +146,6 @@ class Photo < ActiveRecord::Base
Workers::ProcessPhoto.perform_async(self.id)
end
def mutable?
true
end
def self.visible(current_user, person, limit=:all, max_time=nil)
photos = if current_user
current_user.photos_from(person, limit: limit, max_time: max_time)
......
......@@ -136,11 +136,6 @@ class Post < ActiveRecord::Base
shareable_initialize(params)
end
# @return Returns true if this Post will accept updates (i.e. updates to the caption of a photo).
def mutable?
false
end
def activity_streams?
false
end
......
......@@ -54,15 +54,6 @@ class Profile < ActiveRecord::Base
Person.joins(:contacts).where(:contacts => {:user_id => user.id})
end
def receive(user, person)
person.reload # make sure to have old profile referenced
logger.info "event=receive payload_type=profile sender=#{person.diaspora_handle} to=#{user.diaspora_handle}"
profiles_attr = self.attributes.merge('tag_string' => self.tag_string).slice('diaspora_handle', 'first_name', 'last_name', 'image_url', 'image_url_small', 'image_url_medium', 'birthday', 'gender', 'bio', 'location', 'searchable', 'nsfw', 'tag_string')
person.profile.update_attributes(profiles_attr)
person.profile
end
def diaspora_handle
#get the parent diaspora handle, unless we want to access a profile without a person
(self.person) ? self.person.diaspora_handle : self[:diaspora_handle]
......
......@@ -56,15 +56,6 @@ class Reshare < Post
absolute_root.try(:poll) || super
end
def receive(recipient, sender)
local_reshare = Reshare.where(:guid => self.guid).first
if local_reshare && local_reshare.root.author_id == recipient.person.id
recipient.participate! self
return unless recipient.has_contact_for?(sender)
end
super(recipient, sender)
end
def comment_email_subject
I18n.t('reshares.comment_email_subject', :resharer => author.name, :author => root.author_name)
end
......
......@@ -258,12 +258,6 @@ class User < ActiveRecord::Base
end
end
def notify_if_mentioned(post)
return unless self.contact_for(post.author) && post.respond_to?(:mentions?)
post.notify_person(self.person) if post.mentions? self.person
end
def add_to_streams(post, aspects_to_insert)
aspects_to_insert.each do |aspect|
aspect << post
......
......@@ -4,7 +4,9 @@ module Workers
def perform(object_class_string, object_id, recipient_user_ids)
object = object_class_string.constantize.find(object_id)
# TODO: create visibilities
object.receive(recipient_user_ids) if object.respond_to?(:receive)
NotificationService.new.notify(object, recipient_user_ids)
rescue ActiveRecord::RecordNotFound # Already deleted before the job could run
end
......
......@@ -102,7 +102,6 @@ DiasporaFederation.configure do |config|
Diaspora::Federation::Receive.comment(entity)
when DiasporaFederation::Entities::Contact
Diaspora::Federation::Receive.contact(entity)
# TODO: post receive actions (auto-follow-back and fetch posts)
when DiasporaFederation::Entities::Conversation
Diaspora::Federation::Receive.conversation(entity)
when DiasporaFederation::Entities::Like
......
......@@ -43,11 +43,6 @@ module Diaspora
raise 'You must override subscribers in order to enable federation on this model'
end
# @abstract
def receive(user, person)
raise 'You must override receive in order to enable federation on this model'
end
# @param [User] sender
# @note this is a hook(optional)
def after_dispatch(sender)
......
......@@ -38,28 +38,6 @@ class RelayableRetraction < SignedRetraction
end
end
def receive(recipient, sender)
if self.target.nil?
logger.warn "event=retraction status=abort reason='no post found' sender=#{sender.diaspora_handle} " \
"target_guid=#{target_guid}"
return
elsif self.parent.author == recipient.person && self.target_author_signature_valid?
#this is a retraction from the downstream object creator, and the recipient is the upstream owner
self.parent_author_signature = self.sign_with_key(recipient.encryption_key)
Postzord::Dispatcher.build(recipient, self).post
self.perform(recipient)
elsif self.parent_author_signature_valid?
#this is a retraction from the upstream owner
self.perform(recipient)
else
logger.warn "event=receive status=abort reason='object signature not valid' " \
"recipient=#{recipient.diaspora_handle} sender=#{parent.author.diaspora_handle} " \
"payload_type=#{self.class} parent_id=#{parent.id}"
return
end
self
end
def parent_author_signature_valid?
verify_signature(self.parent_author_signature, self.parent.author)
end
......
......@@ -68,25 +68,6 @@ class Request
[self.recipient]
end
# Finds or initializes a corresponding [Contact], and will set Contact#sharing to true
# Follows back if user setting is set so
# @note A [Contact] may already exist if the [Request]'s recipient is sharing with the sender
# @return [Request]
def receive(user, person)
logger.info("event=receive payload_type=request sender=#{sender} to=#{recipient}")
contact = user.contacts.find_or_initialize_by(person_id: self.sender.id)
contact.sharing = true
contact.save
user.share_with(person, user.auto_follow_back_aspect) if user.auto_follow_back && !contact.receiving
# also, schedule to fetch a few public posts from that person
Diaspora::Fetcher::Public.queue_for(person)
self
end
private
# Checks if a [Contact] does not already exist between the requesting [User] and receiving [Person]
......
......@@ -53,22 +53,4 @@ class Retraction
target.author == person
end
end
def receive(user, person)
if self.type == 'Person'
unless self.person.guid.to_s == self.post_guid.to_s
logger.warn "event=receive status=abort reason='sender is not the person he is trying to retract' " \
"recipient=#{diaspora_handle} sender=#{self.person.diaspora_handle} " \
"payload_type=#{self.class} retraction_type=person"
return
end
user.disconnected_by(self.target)
elsif target.nil? || !correct_authorship?
logger.warn "event=retraction status=abort reason='no post found authored by retractor' " \
"sender=#{person.diaspora_handle} post_guid=#{post_guid}"
else
self.perform(user)
end
self
end
end
......@@ -31,28 +31,6 @@ module Diaspora
self.author = Person.where(diaspora_handle: author_handle).first
end
# @param [User] user The user that is receiving this shareable.
# @param [Person] _person The sender of the shareable
# @return [void]
def receive(user, _person)
local_shareable = persisted_shareable
if local_shareable
receive_persisted(user, local_shareable) if verify_persisted_shareable(local_shareable)
else
receive_non_persisted(user)
end
end
# @return [void]
def receive_public
local_shareable = persisted_shareable
if local_shareable
update_existing_sharable(local_shareable) if verify_persisted_shareable(local_shareable)
else
save!
end
end
# The list of people that should receive this Shareable.
#
# @param [User] user The context, or dispatching user.
......@@ -64,65 +42,6 @@ module Diaspora
user.people_in_aspects(user.aspects_with_shareable(self.class, id))
end
end
protected
# @return [Shareable,void]
def persisted_shareable
self.class.where(guid: guid).first
end
# @return [Boolean]
def verify_persisted_shareable(persisted_shareable)
return true if persisted_shareable.author_id == author_id
logger.warn "event=receive payload_type=#{self.class} update=true status=abort " \
"sender=#{diaspora_handle} reason='update not from shareable owner' guid=#{guid}"
false
end
def receive_persisted(user, shareable)
known_shareable = user.find_visible_shareable_by_id(self.class.base_class, guid, key: :guid)
if known_shareable
update_existing_sharable(known_shareable)
else
receive_shareable_visibility(user, shareable)
end
end
def update_existing_sharable(shareable)
if shareable.mutable?
shareable.update_attributes(attributes.except("id"))
logger.info "event=receive payload_type=#{self.class} update=true status=complete " \
"sender=#{diaspora_handle} guid=#{shareable.guid}"
else
logger.warn "event=receive payload_type=#{self.class} update=true status=abort " \
"sender=#{diaspora_handle} reason=immutable guid=#{shareable.guid}"
end
end
def receive_shareable_visibility(user, shareable)
user.receive_shareable(shareable)
user.notify_if_mentioned(shareable)
logger.info "event=receive payload_type=#{self.class} status=complete " \
"sender=#{diaspora_handle} receiver=#{user.diaspora_handle} guid=#{shareable.guid}"
end
def receive_non_persisted(user)
if save
logger.info "event=receive payload_type=#{self.class} status=complete sender=#{diaspora_handle} " \
"guid=#{guid}"
receive_shareable_visibility(user, self)
else
logger.warn "event=receive payload_type=#{self.class} status=abort sender=#{diaspora_handle} " \
"reason=#{errors.full_messages} guid=#{guid}"
end
rescue ActiveRecord::RecordNotUnique => e
# this happens, when two share-visibilities are received parallel. Retry again with local shareable.
logger.info "event=receive payload_type=#{self.class} status=retry sender=#{diaspora_handle} guid=#{guid}"
local_shareable = persisted_shareable
raise e unless local_shareable
receive_shareable_visibility(user, local_shareable) if verify_persisted_shareable(local_shareable)
end
end
end
end
......
......@@ -84,22 +84,6 @@ class SignedRetraction
logger.info "event=retraction status=complete target_type=#{target_type} guid=#{target_guid}"
end
def receive(recipient, sender)
if self.target.nil?
logger.warn "event=retraction status=abort reason='no post found' sender=#{sender.diaspora_handle} " \
"target_guid=#{target_guid}"
return
elsif self.target_author_signature_valid?
#this is a retraction from the upstream owner
self.perform(recipient)
else
logger.warn "event=receive status=abort reason='object signature not valid' " \
"recipient=#{recipient.diaspora_handle} sender=#{sender_handle} payload_type=#{self.class}"
return
end
self
end
def target_author_signature_valid?
verify_signature(self.target_author_signature, self.target.author)
end
......
......@@ -65,45 +65,6 @@ module Diaspora
end
end
def receive(user, person=nil)
comment_or_like = self.class.where(guid: self.guid).first || self
unless comment_or_like.signature_valid?
logger.warn "event=receive status=abort reason='object signature not valid' recipient=#{user.diaspora_handle} "\
"sender=#{comment_or_like.author.diaspora_handle} payload_type=#{self.class} parent_id=#{parent.id}"
return
end
# Check to make sure the signature of the comment or like comes from the person claiming to author it
unless comment_or_like.parent_author == user.person || comment_or_like.verify_parent_author_signature
logger.warn "event=receive status=abort reason='sender is not valid' recipient=#{user.diaspora_handle} "\
"sender=#{parent.author.diaspora_handle} payload_type=#{self.class} parent_id=#{parent.id}"
return
end
# As the owner of the post being liked or commented on, you need to add your own signature in order to
# pass it to the people who received your original post
if user.owns? comment_or_like.parent
comment_or_like.parent_author_signature = comment_or_like.sign_with_key(user.encryption_key)
comment_or_like.save!
end
# Dispatch object DOWNSTREAM, received it via UPSTREAM
unless user.owns?(comment_or_like)
comment_or_like.save!
Postzord::Dispatcher.build(user, comment_or_like).post
end
if comment_or_like.after_receive(user, person)
comment_or_like
end
end
# @return [Object]
def after_receive(user, person)
self
end
def initialize_signatures
#sign relayable as model creator
self.author_signature = self.sign_with_key(author.owner.encryption_key)
......
......@@ -70,6 +70,12 @@ module Diaspora
end
end
def receive(recipient_user_ids)
return if recipient_user_ids.empty? || public?
User.where(id: recipient_user_ids).find_each {|recipient| recipient.receive_shareable(self) }
end
# @return [Integer]
def update_reshares_counter
self.class.where(id: id).update_all(reshares_count: reshares.count)
......
......@@ -29,7 +29,7 @@ class Postzord::Receiver::LocalBatch < Postzord::Receiver
def receive_relayable
if @object.parent_author.local?
# receive relayable object only for the owner of the parent object
@object.receive(@object.parent_author.owner)
# @object.receive(@object.parent_author.owner)
end
end
......
......@@ -39,7 +39,7 @@ class Postzord::Receiver::Private < Postzord::Receiver
# @return [void]
def receive_object
obj = @object.receive(@user, @author)
# obj = @object.receive(@user, @author)
# Notification.notify(@user, obj, @author) if obj.respond_to?(:notification_type)
logger.info "user:#{@user.id} successfully received #{@object.class} from person #{@author.guid}" \
"#{": #{@object.guid}" if @object.respond_to?(:guid)}"
......
......@@ -17,18 +17,6 @@ describe 'a user receives a post', :type => :request do
@eves_aspect = eve.aspects.where(:name => "generic").first
end
it 'should be able to parse and store a status message from xml' do
status_message = bob.post :status_message, :text => 'store this!', :to => @bobs_aspect.id
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(status_message)).to_xml
bob.delete
status_message.destroy
expect {
receive_with_zord(alice, bob.person, xml)
}.to change(Post,:count).by(1)
end
it 'should not create new aspects on message receive' do
num_aspects = alice.aspects.size
......@@ -52,49 +40,6 @@ describe 'a user receives a post', :type => :request do
expect(alice.visible_shareables(Post).count(:all)).to eq(1)
end
context 'update posts' do
it 'does not update posts not marked as mutable' do
status = alice.post :status_message, :text => "store this!", :to => @alices_aspect.id
status.text = 'foo'
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(status)).to_xml
receive_with_zord(bob, alice.person, xml)
expect(status.reload.text).to eq('store this!')
end
it 'updates posts marked as mutable' do
photo = alice.post(
:photo,
user_file: uploaded_photo,
text: "Original",
to: @alices_aspect.id
)
photo.text = 'foo'
photo.height = 42
photo.width = 23
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.photo(photo)).to_xml
bob.reload
receive_with_zord(bob, alice.person, xml)
expect(photo.reload.text).to match(/foo/)
end
end
describe 'profiles' do
it 'federates tags' do
luke, leia, raph = set_up_friends
raph.profile.diaspora_handle = "raph@remote.net"
raph.profile.save!
p = raph.profile
p.tag_string = "#big #rafi #style"
p.receive(luke, raph)
expect(p.tags(true).count).to eq(3)
end
end
describe 'post refs' do
before do
@status_message = bob.post(:status_message, :text => "hi", :to => @bobs_aspect.id)
......@@ -116,6 +61,7 @@ describe 'a user receives a post', :type => :request do
context 'remote' do
before do
skip # TODO
inlined_jobs do |queue|
connect_users(alice, @alices_aspect, eve, @eves_aspect)
@post = alice.post(:status_message, :text => "hello", :to => @alices_aspect.id)
......@@ -151,15 +97,6 @@ describe 'a user receives a post', :type => :request do
expect(post_in_db.comments(true).first.guid).to eq(@guid_with_whitespace)
end
it 'should correctly attach the user already on the pod' do
expect(bob.reload.visible_shareables(Post).size).to eq(1)
post_in_db = StatusMessage.find(@post.id)
expect(post_in_db.comments).to eq([])
receive_with_zord(bob, alice.person, @xml)
expect(post_in_db.comments(true).first.author).to eq(eve.person)
end
it 'should correctly marshal a stranger for the downstream user' do
remote_person = eve.person.dup
eve.person.delete
......@@ -185,6 +122,7 @@ describe 'a user receives a post', :type => :request do
context 'local' do
before do
skip # TODO
@post = alice.post :status_message, :text => "hello", :to => @alices_aspect.id
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
......@@ -224,6 +162,7 @@ describe 'a user receives a post', :type => :request do
end
it "allows two people saving the same post" do
skip # TODO
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
receive_with_zord(@local_luke, @remote_raphael, xml)
receive_with_zord(@local_leia, @remote_raphael, xml)
......@@ -232,6 +171,7 @@ describe 'a user receives a post', :type => :request do
end
it 'does not update the post if a new one is sent with a new created_at' do
skip # TODO
old_time = @post.created_at
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.status_message(@post)).to_xml
receive_with_zord(@local_luke, @remote_raphael, xml)
......@@ -264,59 +204,4 @@ describe 'a user receives a post', :type => :request do
expect(bob.visible_shareables(Post).include?(post)).to be true
end
end
context 'retractions' do
let(:message) { bob.post(:status_message, text: "cats", to: @bobs_aspect.id) }
let(:zord) { Postzord::Receiver::Private.new(alice, person: bob.person) }
it 'should accept retractions' do
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.retraction(message)).to_xml
expect {
zord.parse_and_receive(xml)
}.to change(StatusMessage, :count).by(-1)
end
it 'should accept signed retractions for public posts' do
message = bob.post(:status_message, text: "cats", public: true)
retraction = Diaspora::Federation.xml(Diaspora::Federation::Entities.signed_retraction(message, bob)).to_xml
salmon = Postzord::Dispatcher::Public.salmon(bob, retraction)
xml = salmon.xml_for alice.person
zord = Postzord::Receiver::Public.new xml
expect {
zord.receive!
}.to change(Post, :count).by(-1)
end
end
it 'should marshal a profile for a person' do
#Create person
person = bob.person
person.profile.delete
person.profile = Profile.new(
first_name: "bob",
last_name: "billytown",
image_url: "http://clown.com/image.png",
person_id: person.id
)
person.save
#Cache profile for checking against marshaled profile
new_profile = person.profile.dup
new_profile.first_name = 'boo!!!'
#Build xml for profile
xml = Diaspora::Federation.xml(Diaspora::Federation::Entities.profile(new_profile)).to_xml
#Marshal profile
zord = Postzord::Receiver::Private.new(alice, :person => person)
zord.parse_and_receive(xml)
#Check that marshaled profile is the same as old profile
person = Person.find(person.id)
expect(person.profile.first_name).to eq(new_profile.first_name)
expect(person.profile.last_name).to eq(new_profile.last_name)
expect(person.profile.image_url).to eq(new_profile.image_url)
end
end
......@@ -41,73 +41,6 @@ describe RelayableRetraction do
end
end
describe '#receive' do
it 'discards a retraction with a nil target' do
@comment= @local_luke.comment!(@local_parent, "yo")
@retraction= @local_luke.retract(@comment)
@retraction.instance_variable_set(:@target, nil)
@retraction.target_guid = '135245'
expect(@retraction).not_to receive(:perform)
@retraction.receive(@local_luke, @remote_raphael)
end
context 'from the downstream author' do
before do
@comment = @local_leia.comment!(@local_parent, "yo")
@retraction = @local_leia.retract(@comment)
@recipient = @local_luke
end
it 'signs' do
expect(@retraction).to receive(:sign_with_key) do |key|
expect(key.to_s).to eq(@recipient.encryption_key.to_s)
end
@retraction.receive(@recipient, @comment.author)
end
it 'dispatches' do
zord = double()