diff --git a/Changelog.md b/Changelog.md index 14d4e8e9d..74901121e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -115,6 +115,7 @@ before. * Remove `StatusMessage#raw_message` [#6921](https://github.com/diaspora/diaspora/pull/6921) * Extract photo export into a service class [#6922](https://github.com/diaspora/diaspora/pull/6922) * Use handlebars template for aspect membership dropdown [#6864](https://github.com/diaspora/diaspora/pull/6864) +* Extract relayable signatures into their own tables [#6932](https://github.com/diaspora/diaspora/pull/6932) ## Bug fixes * Destroy Participation when removing interactions with a post [#5852](https://github.com/diaspora/diaspora/pull/5852) diff --git a/Gemfile b/Gemfile index 6d123c775..3f24af10e 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,7 @@ gem "unicorn-worker-killer", "0.4.4" # Federation -gem "diaspora_federation-rails", "0.1.2" +gem "diaspora_federation-rails", "0.1.3" # API and JSON @@ -287,7 +287,7 @@ group :test do gem "webmock", "2.1.0", require: false gem "shoulda-matchers", "3.1.1" - gem "diaspora_federation-test", "0.1.2" + gem "diaspora_federation-test", "0.1.3" # Coverage gem 'coveralls', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 000f3515c..9b75e5237 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -178,17 +178,17 @@ GEM devise rails (>= 3.0.4) diaspora-prosody-config (0.0.5) - diaspora_federation (0.1.2) + diaspora_federation (0.1.3) faraday (~> 0.9.0) faraday_middleware (~> 0.10.0) nokogiri (~> 1.6, >= 1.6.8) typhoeus (~> 1.0) valid (~> 1.0) - diaspora_federation-rails (0.1.2) - diaspora_federation (= 0.1.2) + diaspora_federation-rails (0.1.3) + diaspora_federation (= 0.1.3) rails (~> 4.2) - diaspora_federation-test (0.1.2) - diaspora_federation (= 0.1.2) + diaspora_federation-test (0.1.3) + diaspora_federation (= 0.1.3) factory_girl (~> 4.7) diff-lcs (1.2.5) docile (1.1.5) @@ -933,8 +933,8 @@ DEPENDENCIES devise-token_authenticatable (= 0.4.6) devise_lastseenable (= 0.0.6) diaspora-prosody-config (= 0.0.5) - diaspora_federation-rails (= 0.1.2) - diaspora_federation-test (= 0.1.2) + diaspora_federation-rails (= 0.1.3) + diaspora_federation-test (= 0.1.3) entypo-rails (= 3.0.0.pre.rc2) eye (= 0.8.1) factory_girl_rails (= 4.7.0) diff --git a/app/models/comment.rb b/app/models/comment.rb index da4f0a6f7..1fac00c8d 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -29,6 +29,8 @@ class Comment < ActiveRecord::Base has_many :reports, as: :item + has_one :signature, class_name: "CommentSignature", dependent: :delete + scope :including_author, -> { includes(:author => :profile) } scope :for_a_stream, -> { including_author.merge(order('created_at ASC')) } diff --git a/app/models/comment_signature.rb b/app/models/comment_signature.rb new file mode 100644 index 000000000..357bcb3b8 --- /dev/null +++ b/app/models/comment_signature.rb @@ -0,0 +1,7 @@ +class CommentSignature < ActiveRecord::Base + include Diaspora::Signature + + self.primary_key = :comment_id + belongs_to :comment + validates :comment, presence: true +end diff --git a/app/models/like.rb b/app/models/like.rb index c04fe7fc8..78eb9c3da 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -10,6 +10,8 @@ class Like < ActiveRecord::Base include Diaspora::Relayable + has_one :signature, class_name: "LikeSignature", dependent: :delete + alias_attribute :parent, :target class Generator < Diaspora::Federated::Generator diff --git a/app/models/like_signature.rb b/app/models/like_signature.rb new file mode 100644 index 000000000..6978704a9 --- /dev/null +++ b/app/models/like_signature.rb @@ -0,0 +1,7 @@ +class LikeSignature < ActiveRecord::Base + include Diaspora::Signature + + self.primary_key = :like_id + belongs_to :like + validates :like, presence: true +end diff --git a/app/models/poll_participation.rb b/app/models/poll_participation.rb index 67494e11d..8dba27465 100644 --- a/app/models/poll_participation.rb +++ b/app/models/poll_participation.rb @@ -7,6 +7,8 @@ class PollParticipation < ActiveRecord::Base belongs_to :poll belongs_to :poll_answer, counter_cache: :vote_count + has_one :signature, class_name: "PollParticipationSignature", dependent: :delete + alias_attribute :parent, :poll validates :poll_answer, presence: true diff --git a/app/models/poll_participation_signature.rb b/app/models/poll_participation_signature.rb new file mode 100644 index 000000000..90701f286 --- /dev/null +++ b/app/models/poll_participation_signature.rb @@ -0,0 +1,7 @@ +class PollParticipationSignature < ActiveRecord::Base + include Diaspora::Signature + + self.primary_key = :poll_participation_id + belongs_to :poll_participation + validates :poll_participation, presence: true +end diff --git a/app/models/signature_order.rb b/app/models/signature_order.rb new file mode 100644 index 000000000..83cbfb51a --- /dev/null +++ b/app/models/signature_order.rb @@ -0,0 +1,3 @@ +class SignatureOrder < ActiveRecord::Base + validates :order, presence: true, uniqueness: true +end diff --git a/db/migrate/20160720212620_create_signature_tables.rb b/db/migrate/20160720212620_create_signature_tables.rb new file mode 100644 index 000000000..8c8157d48 --- /dev/null +++ b/db/migrate/20160720212620_create_signature_tables.rb @@ -0,0 +1,87 @@ +class CreateSignatureTables < ActiveRecord::Migration + class SignatureOrder < ActiveRecord::Base + end + + RELAYABLES = %i(comment like poll_participation).freeze + + def self.up + create_table :signature_orders do |t| + t.string :order, null: false + end + add_index :signature_orders, :order, length: 191, unique: true + + RELAYABLES.each {|relayable_type| create_signature_table(relayable_type) } + + migrate_signatures + + RELAYABLES.each {|relayable_type| remove_column "#{relayable_type}s", :author_signature } + end + + def self.down + RELAYABLES.each {|relayable_type| add_column "#{relayable_type}s", :author_signature, :text } + + RELAYABLES.each {|relayable_type| restore_signatures(relayable_type) } + + drop_table :comment_signatures + drop_table :like_signatures + drop_table :poll_participation_signatures + drop_table :signature_orders + end + + private + + def create_signature_table(relayable_type) + create_table "#{relayable_type}_signatures", id: false do |t| + t.integer "#{relayable_type}_id", null: false + t.text :author_signature, null: false + t.integer :signature_order_id, null: false + t.text :additional_data + end + + add_index "#{relayable_type}_signatures", "#{relayable_type}_id", unique: true + + add_foreign_key "#{relayable_type}_signatures", :signature_orders, + name: "#{relayable_type}_signatures_signature_orders_id_fk" + add_foreign_key "#{relayable_type}_signatures", "#{relayable_type}s", + name: "#{relayable_type}_signatures_#{relayable_type}_id_fk", on_delete: :cascade + end + + def migrate_signatures + comment_order_id = SignatureOrder.create!(order: "guid parent_guid text author").id + comment_parent_join = "INNER JOIN posts AS parent ON relayable.commentable_id = parent.id" + migrate_signatures_for(:comment, comment_order_id, comment_parent_join) + + like_order_id = SignatureOrder.create!(order: "positive guid parent_type parent_guid author").id + post_like_join = "INNER JOIN posts AS parent ON relayable.target_id = parent.id AND relayable.target_type = 'Post'" + comment_like_join = "INNER JOIN comments ON relayable.target_id = comments.id " \ + "AND relayable.target_type = 'Comment' " \ + "INNER JOIN posts AS parent ON comments.commentable_id = parent.id" + migrate_signatures_for(:like, like_order_id, post_like_join) + migrate_signatures_for(:like, like_order_id, comment_like_join) + + poll_participation_order_id = SignatureOrder.create!(order: "guid parent_guid author poll_answer_guid").id + poll_participation_parent_join = "INNER JOIN polls ON relayable.poll_id = polls.id " \ + "INNER JOIN posts AS parent ON polls.status_message_id = parent.id" + migrate_signatures_for(:poll_participation, poll_participation_order_id, poll_participation_parent_join) + end + + def migrate_signatures_for(relayable_type, order_id, parent_join) + execute "INSERT INTO #{relayable_type}_signatures (#{relayable_type}_id, signature_order_id, author_signature) " \ + "SELECT relayable.id, #{order_id}, relayable.author_signature FROM #{relayable_type}s AS relayable " \ + "INNER JOIN people AS author ON relayable.author_id = author.id " \ + "#{parent_join} INNER JOIN people AS parent_author ON parent.author_id = parent_author.id " \ + "WHERE author.owner_id IS NULL AND parent_author.owner_id IS NOT NULL" + end + + def restore_signatures(relayable_type) + if AppConfig.postgres? + execute "UPDATE #{relayable_type}s SET author_signature = #{relayable_type}_signatures.author_signature " \ + "FROM #{relayable_type}_signatures " \ + "WHERE #{relayable_type}s.id = #{relayable_type}_signatures.#{relayable_type}_id " + else + execute "UPDATE #{relayable_type}s INNER JOIN #{relayable_type}_signatures " \ + "ON #{relayable_type}s.id = #{relayable_type}_signatures.#{relayable_type}_id " \ + "SET #{relayable_type}s.author_signature = #{relayable_type}_signatures.author_signature" + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 88ef2d80f..5e9267dd9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160618033455) do +ActiveRecord::Schema.define(version: 20160720212620) do create_table "account_deletions", force: :cascade do |t| t.string "diaspora_handle", limit: 255 @@ -100,12 +100,21 @@ ActiveRecord::Schema.define(version: 20160618033455) do t.datetime "created_at", null: false end + create_table "comment_signatures", id: false, force: :cascade do |t| + t.integer "comment_id", limit: 4, null: false + t.text "author_signature", limit: 65535, null: false + t.integer "signature_order_id", limit: 4, null: false + t.text "additional_data", limit: 65535 + end + + add_index "comment_signatures", ["comment_id"], name: "index_comment_signatures_on_comment_id", unique: true, using: :btree + add_index "comment_signatures", ["signature_order_id"], name: "comment_signatures_signature_orders_id_fk", using: :btree + create_table "comments", force: :cascade do |t| t.text "text", limit: 65535, null: false t.integer "commentable_id", limit: 4, null: false t.integer "author_id", limit: 4, null: false t.string "guid", limit: 255, null: false - t.text "author_signature", limit: 65535 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "likes_count", limit: 4, default: 0, null: false @@ -186,15 +195,24 @@ ActiveRecord::Schema.define(version: 20160618033455) do add_index "invitations", ["recipient_id"], name: "index_invitations_on_recipient_id", using: :btree add_index "invitations", ["sender_id"], name: "index_invitations_on_sender_id", using: :btree + create_table "like_signatures", id: false, force: :cascade do |t| + t.integer "like_id", limit: 4, null: false + t.text "author_signature", limit: 65535, null: false + t.integer "signature_order_id", limit: 4, null: false + t.text "additional_data", limit: 65535 + end + + add_index "like_signatures", ["like_id"], name: "index_like_signatures_on_like_id", unique: true, using: :btree + add_index "like_signatures", ["signature_order_id"], name: "like_signatures_signature_orders_id_fk", using: :btree + create_table "likes", force: :cascade do |t| - t.boolean "positive", default: true - t.integer "target_id", limit: 4 - t.integer "author_id", limit: 4 - t.string "guid", limit: 255 - t.text "author_signature", limit: 65535 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "target_type", limit: 60, null: false + t.boolean "positive", default: true + t.integer "target_id", limit: 4 + t.integer "author_id", limit: 4 + t.string "guid", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "target_type", limit: 60, null: false end add_index "likes", ["author_id"], name: "likes_author_id_fk", using: :btree @@ -394,12 +412,21 @@ ActiveRecord::Schema.define(version: 20160618033455) do add_index "poll_answers", ["guid"], name: "index_poll_answers_on_guid", unique: true, length: {"guid"=>191}, using: :btree add_index "poll_answers", ["poll_id"], name: "index_poll_answers_on_poll_id", using: :btree + create_table "poll_participation_signatures", id: false, force: :cascade do |t| + t.integer "poll_participation_id", limit: 4, null: false + t.text "author_signature", limit: 65535, null: false + t.integer "signature_order_id", limit: 4, null: false + t.text "additional_data", limit: 65535 + end + + add_index "poll_participation_signatures", ["poll_participation_id"], name: "index_poll_participation_signatures_on_poll_participation_id", unique: true, using: :btree + add_index "poll_participation_signatures", ["signature_order_id"], name: "poll_participation_signatures_signature_orders_id_fk", using: :btree + create_table "poll_participations", force: :cascade do |t| - t.integer "poll_answer_id", limit: 4, null: false - t.integer "author_id", limit: 4, null: false - t.integer "poll_id", limit: 4, null: false - t.string "guid", limit: 255 - t.text "author_signature", limit: 65535 + t.integer "poll_answer_id", limit: 4, null: false + t.integer "author_id", limit: 4, null: false + t.integer "poll_id", limit: 4, null: false + t.string "guid", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -557,6 +584,12 @@ ActiveRecord::Schema.define(version: 20160618033455) do add_index "share_visibilities", ["shareable_id"], name: "index_post_visibilities_on_post_id", using: :btree add_index "share_visibilities", ["user_id"], name: "index_share_visibilities_on_user_id", using: :btree + create_table "signature_orders", force: :cascade do |t| + t.string "order", limit: 255, null: false + end + + add_index "signature_orders", ["order"], name: "index_signature_orders_on_order", unique: true, length: {"order"=>191}, using: :btree + create_table "simple_captcha_data", force: :cascade do |t| t.string "key", limit: 40 t.string "value", limit: 12 @@ -662,6 +695,8 @@ ActiveRecord::Schema.define(version: 20160618033455) do add_foreign_key "aspect_visibilities", "aspects", name: "aspect_visibilities_aspect_id_fk", on_delete: :cascade add_foreign_key "authorizations", "o_auth_applications" add_foreign_key "authorizations", "users" + add_foreign_key "comment_signatures", "comments", name: "comment_signatures_comment_id_fk", on_delete: :cascade + add_foreign_key "comment_signatures", "signature_orders", name: "comment_signatures_signature_orders_id_fk" add_foreign_key "comments", "people", column: "author_id", name: "comments_author_id_fk", on_delete: :cascade add_foreign_key "contacts", "people", name: "contacts_person_id_fk", on_delete: :cascade add_foreign_key "conversation_visibilities", "conversations", name: "conversation_visibilities_conversation_id_fk", on_delete: :cascade @@ -670,6 +705,8 @@ ActiveRecord::Schema.define(version: 20160618033455) do add_foreign_key "id_tokens", "authorizations" add_foreign_key "invitations", "users", column: "recipient_id", name: "invitations_recipient_id_fk", on_delete: :cascade add_foreign_key "invitations", "users", column: "sender_id", name: "invitations_sender_id_fk", on_delete: :cascade + add_foreign_key "like_signatures", "likes", name: "like_signatures_like_id_fk", on_delete: :cascade + add_foreign_key "like_signatures", "signature_orders", name: "like_signatures_signature_orders_id_fk" add_foreign_key "likes", "people", column: "author_id", name: "likes_author_id_fk", on_delete: :cascade add_foreign_key "messages", "conversations", name: "messages_conversation_id_fk", on_delete: :cascade add_foreign_key "messages", "people", column: "author_id", name: "messages_author_id_fk", on_delete: :cascade @@ -677,6 +714,8 @@ ActiveRecord::Schema.define(version: 20160618033455) do add_foreign_key "o_auth_access_tokens", "authorizations" add_foreign_key "o_auth_applications", "users" add_foreign_key "people", "pods", name: "people_pod_id_fk", on_delete: :cascade + add_foreign_key "poll_participation_signatures", "poll_participations", name: "poll_participation_signatures_poll_participation_id_fk", on_delete: :cascade + add_foreign_key "poll_participation_signatures", "signature_orders", name: "poll_participation_signatures_signature_orders_id_fk" add_foreign_key "posts", "people", column: "author_id", name: "posts_author_id_fk", on_delete: :cascade add_foreign_key "ppid", "o_auth_applications" add_foreign_key "ppid", "users" diff --git a/lib/diaspora/federation/entities.rb b/lib/diaspora/federation/entities.rb index f18f74c2b..cf8d463b6 100644 --- a/lib/diaspora/federation/entities.rb +++ b/lib/diaspora/federation/entities.rb @@ -35,12 +35,16 @@ module Diaspora def self.comment(comment) DiasporaFederation::Entities::Comment.new( - author: comment.diaspora_handle, - guid: comment.guid, - parent_guid: comment.post.guid, - text: comment.text, - author_signature: comment.author_signature, - parent: related_entity(comment.post) + { + author: comment.diaspora_handle, + guid: comment.guid, + parent_guid: comment.post.guid, + text: comment.text, + author_signature: comment.signature.try(:author_signature), + parent: related_entity(comment.post) + }, + comment.signature.try(:order), + comment.signature.try(:additional_data) || {} ) end @@ -65,13 +69,17 @@ module Diaspora def self.like(like) DiasporaFederation::Entities::Like.new( - author: like.diaspora_handle, - guid: like.guid, - parent_guid: like.target.guid, - positive: like.positive, - parent_type: Mappings.entity_name_for(like.target), - author_signature: like.author_signature, - parent: related_entity(like.target) + { + author: like.diaspora_handle, + guid: like.guid, + parent_guid: like.target.guid, + positive: like.positive, + parent_type: Mappings.entity_name_for(like.target), + author_signature: like.signature.try(:author_signature), + parent: related_entity(like.target) + }, + like.signature.try(:order), + like.signature.try(:additional_data) || {} ) end @@ -138,12 +146,16 @@ module Diaspora def self.poll_participation(poll_participation) DiasporaFederation::Entities::PollParticipation.new( - author: poll_participation.diaspora_handle, - guid: poll_participation.guid, - parent_guid: poll_participation.poll.guid, - poll_answer_guid: poll_participation.poll_answer.guid, - author_signature: poll_participation.author_signature, - parent: related_entity(poll_participation.poll) + { + author: poll_participation.diaspora_handle, + guid: poll_participation.guid, + parent_guid: poll_participation.poll.guid, + poll_answer_guid: poll_participation.poll_answer.guid, + author_signature: poll_participation.signature.try(:author_signature), + parent: related_entity(poll_participation.poll) + }, + poll_participation.signature.try(:order), + poll_participation.signature.try(:additional_data) || {} ) end diff --git a/lib/diaspora/federation/receive.rb b/lib/diaspora/federation/receive.rb index 9b0ceea71..fb0953210 100644 --- a/lib/diaspora/federation/receive.rb +++ b/lib/diaspora/federation/receive.rb @@ -254,12 +254,20 @@ module Diaspora yield.tap do |relayable| retract_if_author_ignored(relayable) - relayable.author_signature = entity.author_signature if relayable.parent.author.local? + relayable.signature = build_signature(klass, entity) if relayable.parent.author.local? relayable.save! end end end + private_class_method def self.build_signature(klass, entity) + klass.reflect_on_association(:signature).klass.new( + author_signature: entity.author_signature, + additional_data: entity.additional_xml_elements, + signature_order: SignatureOrder.find_or_create_by!(order: entity.xml_order.join(" ")) + ) + end + private_class_method def self.retract_if_author_ignored(relayable) parent_author = relayable.parent.author.owner return unless parent_author && parent_author.ignored_people.include?(relayable.author) diff --git a/lib/diaspora/signature.rb b/lib/diaspora/signature.rb new file mode 100644 index 000000000..fb571b3cc --- /dev/null +++ b/lib/diaspora/signature.rb @@ -0,0 +1,18 @@ +module Diaspora + module Signature + def self.included(model) + model.class_eval do + belongs_to :signature_order + validates :signature_order, presence: true + + validates :author_signature, presence: true + + serialize :additional_data, Hash + + def order + signature_order.order.split + end + end + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 9964492ed..1c1c5e839 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -304,6 +304,28 @@ FactoryGirl.define do after(:build) {|m| m.conversation.participants << m.author } end + factory(:signature_order) do + order "guid parent_guid text author" + end + + factory(:comment_signature) do + author_signature "some signature" + association :signature_order, order: "guid parent_guid text author new_property" + additional_data { {"new_property" => "some text"} } + end + + factory(:like_signature) do + author_signature "some signature" + association :signature_order, order: "positive guid parent_type parent_guid author new_property" + additional_data { {"new_property" => "some text"} } + end + + factory(:poll_participation_signature) do + author_signature "some signature" + association :signature_order, order: "guid parent_guid author poll_answer_guid new_property" + additional_data { {"new_property" => "some text"} } + end + #templates factory(:status_with_photo_backdrop, :parent => :status_message_with_photo) diff --git a/spec/lib/diaspora/federation/entities_spec.rb b/spec/lib/diaspora/federation/entities_spec.rb index f5b13aba0..8e97e6705 100644 --- a/spec/lib/diaspora/federation/entities_spec.rb +++ b/spec/lib/diaspora/federation/entities_spec.rb @@ -11,7 +11,7 @@ describe Diaspora::Federation::Entities do end it "builds a comment" do - diaspora_entity = FactoryGirl.build(:comment, author_signature: "abc") + diaspora_entity = FactoryGirl.build(:comment) federation_entity = described_class.build(diaspora_entity) expect(federation_entity).to be_instance_of(DiasporaFederation::Entities::Comment) @@ -19,7 +19,23 @@ describe Diaspora::Federation::Entities do expect(federation_entity.guid).to eq(diaspora_entity.guid) expect(federation_entity.parent_guid).to eq(diaspora_entity.post.guid) expect(federation_entity.text).to eq(diaspora_entity.text) - expect(federation_entity.author_signature).to eq(diaspora_entity.author_signature) + expect(federation_entity.author_signature).to be_nil + expect(federation_entity.xml_order).to be_nil + expect(federation_entity.additional_xml_elements).to be_empty + end + + it "builds a comment with signature" do + diaspora_entity = FactoryGirl.build(:comment, signature: FactoryGirl.build(:comment_signature)) + federation_entity = described_class.build(diaspora_entity) + + expect(federation_entity).to be_instance_of(DiasporaFederation::Entities::Comment) + expect(federation_entity.author).to eq(diaspora_entity.author.diaspora_handle) + expect(federation_entity.guid).to eq(diaspora_entity.guid) + expect(federation_entity.parent_guid).to eq(diaspora_entity.post.guid) + expect(federation_entity.text).to eq(diaspora_entity.text) + expect(federation_entity.author_signature).to eq(diaspora_entity.signature.author_signature) + expect(federation_entity.xml_order).to eq(diaspora_entity.signature.signature_order.order.split) + expect(federation_entity.additional_xml_elements).to eq(diaspora_entity.signature.additional_data) end it "builds a contact (request)" do @@ -62,7 +78,7 @@ describe Diaspora::Federation::Entities do end it "builds a like" do - diaspora_entity = FactoryGirl.build(:like, author_signature: "abc") + diaspora_entity = FactoryGirl.build(:like) federation_entity = described_class.build(diaspora_entity) expect(federation_entity).to be_instance_of(DiasporaFederation::Entities::Like) @@ -70,7 +86,23 @@ describe Diaspora::Federation::Entities do expect(federation_entity.guid).to eq(diaspora_entity.guid) expect(federation_entity.parent_guid).to eq(diaspora_entity.target.guid) expect(federation_entity.positive).to eq(diaspora_entity.positive) - expect(federation_entity.author_signature).to eq(diaspora_entity.author_signature) + expect(federation_entity.author_signature).to be_nil + expect(federation_entity.xml_order).to be_nil + expect(federation_entity.additional_xml_elements).to be_empty + end + + it "builds a like with signature" do + diaspora_entity = FactoryGirl.build(:like, signature: FactoryGirl.build(:like_signature)) + federation_entity = described_class.build(diaspora_entity) + + expect(federation_entity).to be_instance_of(DiasporaFederation::Entities::Like) + expect(federation_entity.author).to eq(diaspora_entity.author.diaspora_handle) + expect(federation_entity.guid).to eq(diaspora_entity.guid) + expect(federation_entity.parent_guid).to eq(diaspora_entity.target.guid) + expect(federation_entity.positive).to eq(diaspora_entity.positive) + expect(federation_entity.author_signature).to eq(diaspora_entity.signature.author_signature) + expect(federation_entity.xml_order).to eq(diaspora_entity.signature.signature_order.order.split) + expect(federation_entity.additional_xml_elements).to eq(diaspora_entity.signature.additional_data) end it "builds a message" do @@ -114,7 +146,7 @@ describe Diaspora::Federation::Entities do end it "builds a poll participation" do - diaspora_entity = FactoryGirl.build(:poll_participation, author_signature: "abc") + diaspora_entity = FactoryGirl.build(:poll_participation) federation_entity = described_class.build(diaspora_entity) expect(federation_entity).to be_instance_of(DiasporaFederation::Entities::PollParticipation) @@ -122,7 +154,24 @@ describe Diaspora::Federation::Entities do expect(federation_entity.guid).to eq(diaspora_entity.guid) expect(federation_entity.parent_guid).to eq(diaspora_entity.poll_answer.poll.guid) expect(federation_entity.poll_answer_guid).to eq(diaspora_entity.poll_answer.guid) - expect(federation_entity.author_signature).to eq(diaspora_entity.author_signature) + expect(federation_entity.author_signature).to be_nil + expect(federation_entity.xml_order).to be_nil + expect(federation_entity.additional_xml_elements).to be_empty + end + + it "builds a poll participation with signature" do + signature = FactoryGirl.build(:poll_participation_signature) + diaspora_entity = FactoryGirl.build(:poll_participation, signature: signature) + federation_entity = described_class.build(diaspora_entity) + + expect(federation_entity).to be_instance_of(DiasporaFederation::Entities::PollParticipation) + expect(federation_entity.author).to eq(diaspora_entity.author.diaspora_handle) + expect(federation_entity.guid).to eq(diaspora_entity.guid) + expect(federation_entity.parent_guid).to eq(diaspora_entity.poll_answer.poll.guid) + expect(federation_entity.poll_answer_guid).to eq(diaspora_entity.poll_answer.guid) + expect(federation_entity.author_signature).to eq(signature.author_signature) + expect(federation_entity.xml_order).to eq(signature.signature_order.order.split) + expect(federation_entity.additional_xml_elements).to eq(signature.additional_data) end it "builds a profile" do diff --git a/spec/lib/diaspora/federation/receive_spec.rb b/spec/lib/diaspora/federation/receive_spec.rb index 6f720e0a2..b7ac57098 100644 --- a/spec/lib/diaspora/federation/receive_spec.rb +++ b/spec/lib/diaspora/federation/receive_spec.rb @@ -17,7 +17,19 @@ describe Diaspora::Federation::Receive do end describe ".comment" do - let(:comment_entity) { FactoryGirl.build(:comment_entity, author: sender.diaspora_handle, parent_guid: post.guid) } + let(:comment_data) { + FactoryGirl.attributes_for( + :comment_entity, + author: sender.diaspora_handle, + parent_guid: post.guid, + author_signature: "aa" + ) + } + let(:comment_entity) { + DiasporaFederation::Entities::Comment.new( + comment_data, [:author, :guid, :parent_guid, :text, "new_property"], "new_property" => "data" + ) + } it "saves the comment" do received = Diaspora::Federation::Receive.perform(comment_entity) @@ -39,6 +51,17 @@ describe Diaspora::Federation::Receive do expect(comment.post).to eq(post) end + it "saves the signature data" do + Diaspora::Federation::Receive.perform(comment_entity) + + comment = Comment.find_by!(guid: comment_entity.guid) + + expect(comment.signature).not_to be_nil + expect(comment.signature.author_signature).to eq("aa") + expect(comment.signature.additional_data).to eq("new_property" => "data") + expect(comment.signature.order).to eq(%w(author guid parent_guid text new_property)) + end + let(:entity) { comment_entity } it_behaves_like "it ignores existing object received twice", Comment it_behaves_like "it rejects if the parent author ignores the author", Comment @@ -164,7 +187,19 @@ describe Diaspora::Federation::Receive do end describe ".like" do - let(:like_entity) { FactoryGirl.build(:like_entity, author: sender.diaspora_handle, parent_guid: post.guid) } + let(:like_data) { + FactoryGirl.attributes_for( + :like_entity, + author: sender.diaspora_handle, + parent_guid: post.guid, + author_signature: "aa" + ) + } + let(:like_entity) { + DiasporaFederation::Entities::Like.new( + like_data, [:author, :guid, :parent_guid, :parent_type, :positive, "new_property"], "new_property" => "data" + ) + } it "saves the like" do received = Diaspora::Federation::Receive.perform(like_entity) @@ -185,6 +220,17 @@ describe Diaspora::Federation::Receive do expect(like.target).to eq(post) end + it "saves the signature data" do + Diaspora::Federation::Receive.perform(like_entity) + + like = Like.find_by!(guid: like_entity.guid) + + expect(like.signature).not_to be_nil + expect(like.signature.author_signature).to eq("aa") + expect(like.signature.additional_data).to eq("new_property" => "data") + expect(like.signature.order).to eq(%w(author guid parent_guid parent_type positive new_property)) + end + let(:entity) { like_entity } it_behaves_like "it ignores existing object received twice", Like it_behaves_like "it rejects if the parent author ignores the author", Like @@ -310,12 +356,20 @@ describe Diaspora::Federation::Receive do describe ".poll_participation" do let(:post_with_poll) { FactoryGirl.create(:status_message_with_poll, author: alice.person) } - let(:poll_participation_entity) { - FactoryGirl.build( + let(:poll_participation_data) { + FactoryGirl.attributes_for( :poll_participation_entity, author: sender.diaspora_handle, parent_guid: post_with_poll.poll.guid, - poll_answer_guid: post_with_poll.poll.poll_answers.first.guid + poll_answer_guid: post_with_poll.poll.poll_answers.first.guid, + author_signature: "aa" + ) + } + let(:poll_participation_entity) { + DiasporaFederation::Entities::PollParticipation.new( + poll_participation_data, + [:author, :guid, :parent_guid, :poll_answer_guid, "new_property"], + "new_property" => "data" ) } @@ -338,6 +392,17 @@ describe Diaspora::Federation::Receive do expect(poll_participation.poll).to eq(post_with_poll.poll) end + it "saves the signature data" do + Diaspora::Federation::Receive.perform(poll_participation_entity) + + poll_participation = PollParticipation.find_by!(guid: poll_participation_entity.guid) + + expect(poll_participation.signature).not_to be_nil + expect(poll_participation.signature.author_signature).to eq("aa") + expect(poll_participation.signature.additional_data).to eq("new_property" => "data") + expect(poll_participation.signature.order).to eq(%w(author guid parent_guid poll_answer_guid new_property)) + end + let(:entity) { poll_participation_entity } it_behaves_like "it ignores existing object received twice", PollParticipation it_behaves_like "it rejects if the parent author ignores the author", PollParticipation diff --git a/spec/models/comment_signature_spec.rb b/spec/models/comment_signature_spec.rb new file mode 100644 index 000000000..396dbb903 --- /dev/null +++ b/spec/models/comment_signature_spec.rb @@ -0,0 +1,11 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require "spec_helper" + +describe CommentSignature, type: :model do + it_behaves_like "signature data" do + let(:relayable_type) { :comment } + end +end diff --git a/spec/models/like_signature_spec.rb b/spec/models/like_signature_spec.rb new file mode 100644 index 000000000..d31a37433 --- /dev/null +++ b/spec/models/like_signature_spec.rb @@ -0,0 +1,11 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require "spec_helper" + +describe LikeSignature, type: :model do + it_behaves_like "signature data" do + let(:relayable_type) { :like } + end +end diff --git a/spec/models/poll_participation_signature_spec.rb b/spec/models/poll_participation_signature_spec.rb new file mode 100644 index 000000000..6427d40fb --- /dev/null +++ b/spec/models/poll_participation_signature_spec.rb @@ -0,0 +1,11 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require "spec_helper" + +describe PollParticipationSignature, type: :model do + it_behaves_like "signature data" do + let(:relayable_type) { :poll_participation } + end +end diff --git a/spec/models/signature_order_spec.rb b/spec/models/signature_order_spec.rb new file mode 100644 index 000000000..646bbcd2e --- /dev/null +++ b/spec/models/signature_order_spec.rb @@ -0,0 +1,25 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require "spec_helper" + +describe SignatureOrder, type: :model do + context "validation" do + it "requires an order" do + order = SignatureOrder.new + expect(order).not_to be_valid + + order.order = "author guid" + expect(order).to be_valid + end + + it "doesn't allow the same order twice" do + first = SignatureOrder.create!(order: "author guid") + expect(first).to be_valid + + second = SignatureOrder.new(order: first.order) + expect(second).not_to be_valid + end + end +end diff --git a/spec/shared_behaviors/relayable.rb b/spec/shared_behaviors/relayable.rb index 1043ca463..2fd48d904 100644 --- a/spec/shared_behaviors/relayable.rb +++ b/spec/shared_behaviors/relayable.rb @@ -68,4 +68,32 @@ shared_examples_for "it is relayable" do end end end + + describe "#signature" do + let(:signature_class) { described_class.reflect_on_association(:signature).klass } + + before do + remote_object_on_local_parent.signature = signature_class.new( + author_signature: "signature", + additional_data: {"new_property" => "some text"}, + signature_order: FactoryGirl.create(:signature_order) + ) + end + + it "returns the signature data" do + signature = described_class.find(remote_object_on_local_parent.id).signature + expect(signature).not_to be_nil + expect(signature.author_signature).to eq("signature") + expect(signature.additional_data).to eq("new_property" => "some text") + expect(signature.order).to eq(%w(guid parent_guid text author)) + end + + it "deletes the signature when destroying the relayable" do + id = remote_object_on_local_parent.id + remote_object_on_local_parent.destroy! + + signature = signature_class.find_by(signature_class.primary_key => id) + expect(signature).to be_nil + end + end end diff --git a/spec/shared_behaviors/signature.rb b/spec/shared_behaviors/signature.rb new file mode 100644 index 000000000..3b40d751e --- /dev/null +++ b/spec/shared_behaviors/signature.rb @@ -0,0 +1,57 @@ +require "spec_helper" + +shared_examples_for "signature data" do + let(:relayable) { FactoryGirl.create(relayable_type) } + let(:signature) { + described_class.new( + relayable_type => relayable, + :author_signature => "signature", + :additional_data => {"additional_data" => "some data"}, + :signature_order => SignatureOrder.new(order: "author guid parent_guid") + ) + } + + describe "#order" do + it "it returns the order as array" do + expect(signature.order).to eq(%w(author guid parent_guid)) + end + end + + describe "#additional_data" do + it "is stored as hash" do + signature.save + + entity = described_class.reflect_on_association(relayable_type).klass.find(relayable.id) + expect(entity.signature.additional_data).to eq("additional_data" => "some data") + end + + it "can be missing" do + signature.additional_data = nil + signature.save + + entity = described_class.reflect_on_association(relayable_type).klass.find(relayable.id) + expect(entity.signature.additional_data).to eq({}) + end + end + + context "validation" do + it "is valid" do + expect(signature).to be_valid + end + + it "requires a linked relayable" do + signature.public_send("#{relayable_type}=", nil) + expect(signature).not_to be_valid + end + + it "requires a signature_order" do + signature.signature_order = nil + expect(signature).not_to be_valid + end + + it "requires a author_signature" do + signature.author_signature = nil + expect(signature).not_to be_valid + end + end +end