diaspora_federation 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/lib/diaspora_federation.rb +66 -1
  4. data/lib/diaspora_federation/discovery/h_card.rb +2 -3
  5. data/lib/diaspora_federation/discovery/web_finger.rb +3 -6
  6. data/lib/diaspora_federation/entities.rb +18 -0
  7. data/lib/diaspora_federation/entities/account_deletion.rb +14 -0
  8. data/lib/diaspora_federation/entities/comment.rb +26 -0
  9. data/lib/diaspora_federation/entities/conversation.rb +38 -0
  10. data/lib/diaspora_federation/entities/like.rb +35 -0
  11. data/lib/diaspora_federation/entities/location.rb +23 -0
  12. data/lib/diaspora_federation/entities/message.rb +38 -0
  13. data/lib/diaspora_federation/entities/participation.rb +28 -0
  14. data/lib/diaspora_federation/entities/person.rb +6 -3
  15. data/lib/diaspora_federation/entities/photo.rb +59 -0
  16. data/lib/diaspora_federation/entities/poll.rb +24 -0
  17. data/lib/diaspora_federation/entities/poll_answer.rb +19 -0
  18. data/lib/diaspora_federation/entities/poll_participation.rb +28 -0
  19. data/lib/diaspora_federation/entities/profile.rb +10 -8
  20. data/lib/diaspora_federation/entities/relayable.rb +101 -0
  21. data/lib/diaspora_federation/entities/relayable_retraction.rb +95 -0
  22. data/lib/diaspora_federation/entities/request.rb +21 -0
  23. data/lib/diaspora_federation/entities/reshare.rb +49 -0
  24. data/lib/diaspora_federation/entities/retraction.rb +24 -0
  25. data/lib/diaspora_federation/entities/signed_retraction.rb +66 -0
  26. data/lib/diaspora_federation/entities/status_message.rb +55 -0
  27. data/lib/diaspora_federation/entity.rb +5 -6
  28. data/lib/diaspora_federation/fetcher.rb +1 -2
  29. data/lib/diaspora_federation/properties_dsl.rb +18 -8
  30. data/lib/diaspora_federation/salmon.rb +17 -0
  31. data/lib/diaspora_federation/salmon/aes.rb +58 -0
  32. data/lib/diaspora_federation/salmon/encrypted_slap.rb +187 -0
  33. data/lib/diaspora_federation/salmon/exceptions.rb +50 -0
  34. data/lib/diaspora_federation/salmon/magic_envelope.rb +191 -0
  35. data/lib/diaspora_federation/salmon/slap.rb +128 -0
  36. data/lib/diaspora_federation/salmon/xml_payload.rb +158 -0
  37. data/lib/diaspora_federation/signing.rb +56 -0
  38. data/lib/diaspora_federation/validators.rb +20 -0
  39. data/lib/diaspora_federation/validators/account_deletion_validator.rb +10 -0
  40. data/lib/diaspora_federation/validators/comment_validator.rb +17 -0
  41. data/lib/diaspora_federation/validators/conversation_validator.rb +14 -0
  42. data/lib/diaspora_federation/validators/like_validator.rb +14 -0
  43. data/lib/diaspora_federation/validators/location_validator.rb +11 -0
  44. data/lib/diaspora_federation/validators/message_validator.rb +16 -0
  45. data/lib/diaspora_federation/validators/participation_validator.rb +16 -0
  46. data/lib/diaspora_federation/validators/photo_validator.rb +24 -0
  47. data/lib/diaspora_federation/validators/poll_answer_validator.rb +11 -0
  48. data/lib/diaspora_federation/validators/poll_participation_validator.rb +16 -0
  49. data/lib/diaspora_federation/validators/poll_validator.rb +11 -0
  50. data/lib/diaspora_federation/validators/relayable_retraction_validator.rb +15 -0
  51. data/lib/diaspora_federation/validators/relayable_validator.rb +14 -0
  52. data/lib/diaspora_federation/validators/request_validator.rb +11 -0
  53. data/lib/diaspora_federation/validators/reshare_validator.rb +18 -0
  54. data/lib/diaspora_federation/validators/retraction_validator.rb +14 -0
  55. data/lib/diaspora_federation/validators/rules/diaspora_id_count.rb +37 -0
  56. data/lib/diaspora_federation/validators/signed_retraction_validator.rb +15 -0
  57. data/lib/diaspora_federation/validators/status_message_validator.rb +14 -0
  58. data/lib/diaspora_federation/version.rb +1 -1
  59. metadata +49 -4
@@ -0,0 +1,24 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a poll ant it is federated as an optional part of a status message
4
+ #
5
+ # @see Validators::PollValidator
6
+ class Poll < Entity
7
+ # @!attribute [r] guid
8
+ # a random string of at least 16 chars.
9
+ # @see Validation::Rule::Guid
10
+ # @return [String] poll guid
11
+ property :guid
12
+
13
+ # @!attribute [r] question
14
+ # Text of the question posed by a user
15
+ # @return [String] question
16
+ property :question
17
+
18
+ # @!attribute [r] poll_answers
19
+ # array of possible answer to the poll
20
+ # @return [[Entities::PollAnswer]] poll answers
21
+ entity :poll_answers, [Entities::PollAnswer]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a poll answer and is federated as a part of the Poll entity
4
+ #
5
+ # @see Validators::PollAnswerValidator
6
+ class PollAnswer < Entity
7
+ # @!attribute [r] guid
8
+ # a random string of at least 16 chars.
9
+ # @see Validation::Rule::Guid
10
+ # @return [String] guid
11
+ property :guid
12
+
13
+ # @!attribute [r] answer
14
+ # Text of the answer
15
+ # @return [String] answer
16
+ property :answer
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a participation in poll, i.e. it is issued when a user votes for an answer in a poll
4
+ #
5
+ # @see Validators::PollParticipationValidator
6
+ class PollParticipation < Entity
7
+ # @!attribute [r] guid
8
+ # a random string of at least 16 chars.
9
+ # @see Validation::Rule::Guid
10
+ # @return [String] guid
11
+ property :guid
12
+
13
+ include Relayable
14
+
15
+ # @!attribute [r] diaspora_id
16
+ # The diaspora ID of the person who voted in the poll
17
+ # @see Person#diaspora_id
18
+ # @return [String] diaspora ID
19
+ property :diaspora_id, xml_name: :diaspora_handle
20
+
21
+ # @!attribute [r] poll_answer_guid
22
+ # guid of the answer selected by the user.
23
+ # @see PollAnswer#guid
24
+ # @return [String] poll answer guid
25
+ property :poll_answer_guid
26
+ end
27
+ end
28
+ end
@@ -11,29 +11,31 @@ module DiasporaFederation
11
11
  property :diaspora_id, xml_name: :diaspora_handle
12
12
 
13
13
  # @!attribute [r] first_name
14
- # @deprecated
14
+ # @deprecated We decided to only use one name field, these should be removed
15
+ # in later iterations (will affect older Diaspora* installations).
15
16
  # @see #full_name
16
- # @see HCard#first_name
17
+ # @see Discovery::HCard#first_name
17
18
  # @return [String] first name
18
19
  property :first_name, default: nil
19
20
 
20
21
  # @!attribute [r] last_name
21
- # @deprecated
22
+ # @deprecated We decided to only use one name field, these should be removed
23
+ # in later iterations (will affect older Diaspora* installations).
22
24
  # @see #full_name
23
- # @see HCard#last_name
25
+ # @see Discovery::HCard#last_name
24
26
  # @return [String] last name
25
27
  property :last_name, default: nil
26
28
 
27
29
  # @!attribute [r] image_url
28
- # @see HCard#photo_large_url
30
+ # @see Discovery::HCard#photo_large_url
29
31
  # @return [String] url to the big avatar (300x300)
30
32
  property :image_url, default: nil
31
33
  # @!attribute [r] image_url_medium
32
- # @see HCard#photo_medium_url
34
+ # @see Discovery::HCard#photo_medium_url
33
35
  # @return [String] url to the medium avatar (100x100)
34
36
  property :image_url_medium, default: nil
35
37
  # @!attribute [r] image_url_small
36
- # @see HCard#photo_small_url
38
+ # @see Discovery::HCard#photo_small_url
37
39
  # @return [String] url to the small avatar (50x50)
38
40
  property :image_url_small, default: nil
39
41
 
@@ -43,7 +45,7 @@ module DiasporaFederation
43
45
  property :location, default: nil
44
46
 
45
47
  # @!attribute [r] searchable
46
- # @see HCard#searchable
48
+ # @see Discovery::HCard#searchable
47
49
  # @return [Boolean] searchable flag
48
50
  property :searchable, default: true
49
51
 
@@ -0,0 +1,101 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this is a module that defines common properties for relayable entities
4
+ # which include Like, Comment, Participation, Message, etc. Each relayable
5
+ # has a parent, identified by guid. Relayables also are signed and signing/verificating
6
+ # logic is embedded into Salmon XML processing code.
7
+ module Relayable
8
+ # on inclusion of this module the required properties for a relayable are added to the object that includes it
9
+ #
10
+ # @!attribute [r] parent_guid
11
+ # @see StatusMessage#guid
12
+ # @return [String] parent guid
13
+ #
14
+ # @!attribute [r] parent_author_signature
15
+ # Contains a signature of the entity using the private key of the author of a parent post
16
+ # This signature is required only when federation from upstream (parent) post author to
17
+ # downstream subscribers. This is the case when the parent author has to resend a relayable
18
+ # received from one of his subscribers to all others.
19
+ #
20
+ # @return [String] parent author signature
21
+ #
22
+ # @!attribute [r] author_signature
23
+ # Contains a signature of the entity using the private key of the author of a post itself.
24
+ # The presence of this signature is mandatory. Without it the entity won't be accepted by
25
+ # a target pod.
26
+ # @return [String] author signature
27
+ #
28
+ # @param [Entity] entity the entity in which it is included
29
+ def self.included(entity)
30
+ entity.class_eval do
31
+ property :parent_guid
32
+ property :parent_author_signature, default: nil
33
+ property :author_signature, default: nil
34
+ end
35
+ end
36
+
37
+ # Generates XML and updates signatures
38
+ # @see Entity#to_xml
39
+ # @return [Nokogiri::XML::Element] root element containing properties as child elements
40
+ def to_xml
41
+ entity_xml.tap do |xml|
42
+ hash = to_h
43
+ Relayable.update_signatures!(hash)
44
+
45
+ xml.at_xpath("author_signature").content = hash[:author_signature]
46
+ xml.at_xpath("parent_author_signature").content = hash[:parent_author_signature]
47
+ end
48
+ end
49
+
50
+ # Exception raised when verify_signatures fails to verify signatures (signatures are wrong)
51
+ class SignatureVerificationFailed < ArgumentError
52
+ end
53
+
54
+ # verifies the signatures (+author_signature+ and +parent_author_signature+ if needed)
55
+ # @param [Hash] data hash with data to verify
56
+ # @raise [SignatureVerificationFailed] if the signature is not valid or no public key is found
57
+ def self.verify_signatures(data)
58
+ pkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, data[:diaspora_id])
59
+ raise SignatureVerificationFailed, "failed to fetch public key for #{data[:diaspora_id]}" if pkey.nil?
60
+ raise SignatureVerificationFailed, "wrong author_signature" unless Signing.verify_signature(
61
+ data, data[:author_signature], pkey
62
+ )
63
+
64
+ author_is_local = DiasporaFederation.callbacks.trigger(:entity_author_is_local?, "Post", data[:parent_guid])
65
+ verify_parent_signature(data) unless author_is_local
66
+ end
67
+
68
+ # this happens only on downstream federation
69
+ # @param [Hash] data hash with data to verify
70
+ def self.verify_parent_signature(data)
71
+ pkey = DiasporaFederation.callbacks.trigger(:fetch_author_public_key_by_entity_guid, "Post", data[:parent_guid])
72
+ raise SignatureVerificationFailed,
73
+ "failed to fetch public key for author of #{data[:parent_guid]}" if pkey.nil?
74
+ raise SignatureVerificationFailed, "wrong parent_author_signature" unless Signing.verify_signature(
75
+ data, data[:parent_author_signature], pkey
76
+ )
77
+ end
78
+ private_class_method :verify_parent_signature
79
+
80
+ # Adds signatures to a given hash with the keys of the author and the parent
81
+ # if the signatures are not in the hash yet and if the keys are available.
82
+ #
83
+ # @param [Hash] data hash given for a signing
84
+ def self.update_signatures!(data)
85
+ if data[:author_signature].nil?
86
+ pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, data[:diaspora_id])
87
+ data[:author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
88
+ end
89
+
90
+ if data[:parent_author_signature].nil?
91
+ pkey = DiasporaFederation.callbacks.trigger(
92
+ :fetch_author_private_key_by_entity_guid,
93
+ "Post",
94
+ data[:parent_guid]
95
+ )
96
+ data[:parent_author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,95 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a claim of deletion of a previously federated
4
+ # relayable entity ({Entities::Comment}, {Entities::Like})
5
+ #
6
+ # There are two cases of federation of the RelayableRetraction.
7
+ # Retraction from the dowstream object owner is when an author of the
8
+ # relayable (e.g. Comment) deletes it himself. In this case only target_author_signature
9
+ # is filled and retraction is sent to the commented post's author. Here
10
+ # he (upstream object owner) signes it with parent's author key and fills
11
+ # signature in parent_author_signature and sends it to other pods where
12
+ # other participating people present. This is the second case - retraction
13
+ # from the upstream object owner.
14
+ # Retraction from the upstream object owner can also be performed by the
15
+ # upstream object owner himself - he has a right to delete comments on his posts.
16
+ # In any case in the retraction by the upstream author target_author_signature
17
+ # is not checked, only parent_author_signature is checked.
18
+ #
19
+ # @see Validators::RelayableRetractionValidator
20
+ # @deprecated will be replaced with {Entities::Retraction}
21
+ class RelayableRetraction < Entity
22
+ # @!attribute [r] parent_author_signature
23
+ # Contains a signature of the entity using the private key of the author of a parent post
24
+ # This signature is mandatory only when federation from an upstream author to the subscribers.
25
+ # @see Relayable#parent_author_signature
26
+ # @return [String] parent author signature
27
+ property :parent_author_signature, default: nil
28
+
29
+ # @!attribute [r] target_guid
30
+ # guid of a relayable to be deleted
31
+ # @see Comment#guid
32
+ # @return [String] target guid
33
+ property :target_guid
34
+
35
+ # @!attribute [r] target_type
36
+ # a string describing a type of the target
37
+ # @see Retraction#target_type
38
+ # @return [String] target type
39
+ property :target_type
40
+
41
+ # @!attribute [r] diaspora_id
42
+ # The diaspora ID of the person who deletes a relayable
43
+ # @see Person#diaspora_id
44
+ # @return [String] diaspora ID
45
+ property :diaspora_id, xml_name: :sender_handle
46
+
47
+ # @!attribute [r] target_author_signature
48
+ # Contains a signature of the entity using the private key of the
49
+ # author of a federated relayable entity ({Entities::Comment}, {Entities::Like})
50
+ # This signature is mandatory only when federation from the subscriber to an upstream
51
+ # author is done.
52
+ # @see Relayable#author_signature
53
+ # @return [String] target author signature
54
+ property :target_author_signature, default: nil
55
+
56
+ # Generates XML and updates signatures
57
+ # @see Entity#to_xml
58
+ # @return [Nokogiri::XML::Element] root element containing properties as child elements
59
+ def to_xml
60
+ entity_xml.tap do |xml|
61
+ hash = to_h
62
+ RelayableRetraction.update_signatures!(hash)
63
+ xml.at_xpath("target_author_signature").content = hash[:target_author_signature]
64
+ xml.at_xpath("parent_author_signature").content = hash[:parent_author_signature]
65
+ end
66
+ end
67
+
68
+ # Adds signatures to a given hash with the keys of the author and the parent
69
+ # if the signatures are not in the hash yet and if the keys are available.
70
+ #
71
+ # @param [Hash] hash hash given for a signing
72
+ def self.update_signatures!(hash)
73
+ target_author = DiasporaFederation.callbacks.trigger(
74
+ :fetch_entity_author_id_by_guid,
75
+ hash[:target_type],
76
+ hash[:target_guid]
77
+ )
78
+ pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
79
+
80
+ fill_required_signature(target_author, pkey, hash) unless pkey.nil?
81
+ end
82
+
83
+ def self.fill_required_signature(target_author, pkey, hash)
84
+ if target_author == hash[:diaspora_id] && hash[:target_author_signature].nil?
85
+ hash[:target_author_signature] =
86
+ Signing.sign_with_key(SignedRetraction.apply_signable_exceptions(hash), pkey)
87
+ elsif target_author != hash[:diaspora_id] && hash[:parent_author_signature].nil?
88
+ hash[:parent_author_signature] =
89
+ Signing.sign_with_key(SignedRetraction.apply_signable_exceptions(hash), pkey)
90
+ end
91
+ end
92
+ private_class_method :fill_required_signature
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,21 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a sharing request for a user. A user issues it
4
+ # when he starts sharing with another user.
5
+ #
6
+ # @see Validators::RequestValidator
7
+ class Request < Entity
8
+ # @!attribute [r] sender_id
9
+ # The diaspora ID of the person who shares his profile
10
+ # @see Person#diaspora_id
11
+ # @return [String] sender ID
12
+ property :sender_id, xml_name: :sender_handle
13
+
14
+ # @!attribute [r] recipient_id
15
+ # The diaspora ID of the person who will be shared with
16
+ # @see Person#diaspora_id
17
+ # @return [String] recipient ID
18
+ property :recipient_id, xml_name: :recipient_handle
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents the fact the a user reshared some other user's post
4
+ #
5
+ # @see Validators::ReshareValidator
6
+ class Reshare < Entity
7
+ # @!attribute [r] diaspora_id
8
+ # The diaspora ID of the person who posted the original post
9
+ # @see Person#diaspora_id
10
+ # @return [String] diaspora ID
11
+ property :root_diaspora_id # inconsistent, everywhere else it's "handle"
12
+
13
+ # @!attribute [r] root_guid
14
+ # guid of the original post
15
+ # @see StatusMessage#guid
16
+ # @return [String] root guid
17
+ property :root_guid
18
+
19
+ # @!attribute [r] guid
20
+ # a random string of at least 16 chars.
21
+ # @see Validation::Rule::Guid
22
+ # @see StatusMessage#guid
23
+ # @return [String] guid
24
+ property :guid
25
+
26
+ # @!attribute [r] diaspora_id
27
+ # The diaspora ID of the person who reshares a post
28
+ # @see Person#diaspora_id
29
+ # @return [String] diaspora ID
30
+ property :diaspora_id, xml_name: :diaspora_handle
31
+
32
+ # @!attribute [r] public
33
+ # has no meaning at the moment
34
+ # @return [Boolean] public
35
+ property :public, default: true # always true? (we only reshare public posts)
36
+
37
+ # @!attribute [r] created_at
38
+ # reshare entity creation time
39
+ # @return [Time] creation time
40
+ property :created_at, default: -> { Time.now.utc }
41
+
42
+ # @!attribute [r] provider_display_name
43
+ # a string that describes a means by which a user has posted the reshare
44
+ # @see StatusMessage#provider_display_name
45
+ # @return [String] provider display name
46
+ property :provider_display_name, default: nil
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,24 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a claim of deletion of a previously federated entity
4
+ #
5
+ # @see Validators::RetractionValidator
6
+ class Retraction < Entity
7
+ # @!attribute [r] target_guid
8
+ # guid of the entity to be deleted
9
+ # @return [String] target guid
10
+ property :target_guid, xml_name: :post_guid
11
+
12
+ # @!attribute [r] diaspora_id
13
+ # The diaspora ID of the person who deletes the entity
14
+ # @see Person#diaspora_id
15
+ # @return [String] diaspora ID
16
+ property :diaspora_id, xml_name: :diaspora_handle
17
+
18
+ # @!attribute [r] target_type
19
+ # A string describing the type of the target.
20
+ # @return [String] target type
21
+ property :target_type, xml_name: :type
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a claim of deletion of a previously federated
4
+ # entity of post type ({Entities::StatusMessage})
5
+ #
6
+ # @see Validators::SignedRetractionValidator
7
+ # @deprecated will be replaced with {Entities::Retraction}
8
+ class SignedRetraction < Entity
9
+ # @!attribute [r] target_guid
10
+ # guid of a post to be deleted
11
+ # @see Retraction#target_guid
12
+ # @return [String] target guid
13
+ property :target_guid
14
+
15
+ # @!attribute [r] target_type
16
+ # A string describing the type of the target.
17
+ # @see Retraction#target_type
18
+ # @return [String] target type
19
+ property :target_type
20
+
21
+ # @!attribute [r] diaspora_id
22
+ # The diaspora ID of the person who deletes a post
23
+ # @see Person#diaspora_id
24
+ # @return [String] diaspora ID
25
+ property :diaspora_id, xml_name: :sender_handle
26
+
27
+ # @!attribute [r] author_signature
28
+ # Contains a signature of the entity using the private key of the author of a post
29
+ # This signature is mandatory.
30
+ # @return [String] author signature
31
+ property :target_author_signature, default: nil
32
+
33
+ # Generates XML and updates signatures
34
+ # @see Entity#to_xml
35
+ # @return [Nokogiri::XML::Element] root element containing properties as child elements
36
+ def to_xml
37
+ entity_xml.tap do |xml|
38
+ hash = to_h
39
+ SignedRetraction.update_signatures!(hash)
40
+
41
+ xml.at_xpath("target_author_signature").content = hash[:target_author_signature]
42
+ end
43
+ end
44
+
45
+ # Adds signature to a given hash with the key of the author
46
+ # if the signature is not in the hash yet and if the key is available.
47
+ #
48
+ # @param [Hash] data hash given for a signing
49
+ def self.update_signatures!(data)
50
+ if data[:target_author_signature].nil?
51
+ pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, data[:diaspora_id])
52
+ data[:target_author_signature] = Signing.sign_with_key(apply_signable_exceptions(data), pkey) unless pkey.nil?
53
+ end
54
+ end
55
+
56
+ # Deletes :diaspora_id (xml_name: sender_handle) from the hash in order to compute
57
+ # a signature since it is included from signable_string for SignedRetraction and RelayableRetraction
58
+ #
59
+ # @param [Hash] data hash of the retraction properties
60
+ # @return [Hash] hash copy without :diaspora_id member
61
+ def self.apply_signable_exceptions(data)
62
+ data.dup.tap {|data| data.delete(:diaspora_id) }
63
+ end
64
+ end
65
+ end
66
+ end