diaspora_federation 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/diaspora_federation.rb +103 -18
  3. data/lib/diaspora_federation/discovery/discovery.rb +1 -1
  4. data/lib/diaspora_federation/discovery/h_card.rb +4 -5
  5. data/lib/diaspora_federation/discovery/host_meta.rb +1 -1
  6. data/lib/diaspora_federation/discovery/web_finger.rb +8 -8
  7. data/lib/diaspora_federation/discovery/xrd_document.rb +6 -7
  8. data/lib/diaspora_federation/entities.rb +21 -10
  9. data/lib/diaspora_federation/entities/account_deletion.rb +7 -3
  10. data/lib/diaspora_federation/entities/comment.rb +13 -10
  11. data/lib/diaspora_federation/entities/contact.rb +29 -0
  12. data/lib/diaspora_federation/entities/conversation.rb +5 -6
  13. data/lib/diaspora_federation/entities/like.rb +10 -18
  14. data/lib/diaspora_federation/entities/message.rb +6 -12
  15. data/lib/diaspora_federation/entities/participation.rb +8 -16
  16. data/lib/diaspora_federation/entities/person.rb +6 -2
  17. data/lib/diaspora_federation/entities/photo.rb +3 -3
  18. data/lib/diaspora_federation/entities/poll_participation.rb +6 -12
  19. data/lib/diaspora_federation/entities/post.rb +37 -0
  20. data/lib/diaspora_federation/entities/profile.rb +7 -3
  21. data/lib/diaspora_federation/entities/relayable.rb +169 -65
  22. data/lib/diaspora_federation/entities/relayable_retraction.rb +33 -32
  23. data/lib/diaspora_federation/entities/request.rb +20 -6
  24. data/lib/diaspora_federation/entities/reshare.rb +5 -27
  25. data/lib/diaspora_federation/entities/retraction.rb +6 -6
  26. data/lib/diaspora_federation/entities/signed_retraction.rb +32 -26
  27. data/lib/diaspora_federation/entities/status_message.rb +2 -22
  28. data/lib/diaspora_federation/entity.rb +137 -38
  29. data/lib/diaspora_federation/federation.rb +9 -0
  30. data/lib/diaspora_federation/federation/fetcher.rb +26 -0
  31. data/lib/diaspora_federation/federation/receiver.rb +41 -0
  32. data/lib/diaspora_federation/federation/receiver/abstract_receiver.rb +35 -0
  33. data/lib/diaspora_federation/federation/receiver/exceptions.rb +13 -0
  34. data/lib/diaspora_federation/federation/receiver/private.rb +15 -0
  35. data/lib/diaspora_federation/federation/receiver/public.rb +9 -0
  36. data/lib/diaspora_federation/federation/sender.rb +33 -0
  37. data/lib/diaspora_federation/federation/sender/hydra_wrapper.rb +92 -0
  38. data/lib/diaspora_federation/{fetcher.rb → http_client.rb} +6 -6
  39. data/lib/diaspora_federation/properties_dsl.rb +51 -14
  40. data/lib/diaspora_federation/salmon.rb +2 -1
  41. data/lib/diaspora_federation/salmon/aes.rb +1 -1
  42. data/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb +61 -0
  43. data/lib/diaspora_federation/salmon/encrypted_slap.rb +69 -50
  44. data/lib/diaspora_federation/salmon/exceptions.rb +8 -14
  45. data/lib/diaspora_federation/salmon/magic_envelope.rb +80 -39
  46. data/lib/diaspora_federation/salmon/slap.rb +20 -51
  47. data/lib/diaspora_federation/salmon/xml_payload.rb +5 -104
  48. data/lib/diaspora_federation/validators.rb +22 -16
  49. data/lib/diaspora_federation/validators/account_deletion_validator.rb +1 -1
  50. data/lib/diaspora_federation/validators/comment_validator.rb +0 -4
  51. data/lib/diaspora_federation/validators/contact_validator.rb +13 -0
  52. data/lib/diaspora_federation/validators/conversation_validator.rb +2 -2
  53. data/lib/diaspora_federation/validators/like_validator.rb +1 -3
  54. data/lib/diaspora_federation/validators/message_validator.rb +0 -4
  55. data/lib/diaspora_federation/validators/participation_validator.rb +1 -5
  56. data/lib/diaspora_federation/validators/person_validator.rb +1 -1
  57. data/lib/diaspora_federation/validators/photo_validator.rb +2 -2
  58. data/lib/diaspora_federation/validators/poll_participation_validator.rb +0 -4
  59. data/lib/diaspora_federation/validators/profile_validator.rb +1 -1
  60. data/lib/diaspora_federation/validators/relayable_retraction_validator.rb +1 -1
  61. data/lib/diaspora_federation/validators/relayable_validator.rb +2 -0
  62. data/lib/diaspora_federation/validators/request_validator.rb +3 -2
  63. data/lib/diaspora_federation/validators/reshare_validator.rb +3 -3
  64. data/lib/diaspora_federation/validators/retraction_validator.rb +2 -2
  65. data/lib/diaspora_federation/validators/rules/guid.rb +16 -7
  66. data/lib/diaspora_federation/validators/signed_retraction_validator.rb +1 -1
  67. data/lib/diaspora_federation/validators/status_message_validator.rb +2 -2
  68. data/lib/diaspora_federation/version.rb +1 -1
  69. metadata +20 -11
  70. data/lib/diaspora_federation/receiver.rb +0 -28
  71. data/lib/diaspora_federation/receiver/private.rb +0 -19
  72. data/lib/diaspora_federation/receiver/public.rb +0 -13
  73. data/lib/diaspora_federation/signing.rb +0 -56
@@ -0,0 +1,29 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity represents a contact with another person. A user issues it
4
+ # when he starts sharing/following with another user.
5
+ #
6
+ # @see Validators::ContactValidator
7
+ class Contact < Entity
8
+ # @!attribute [r] author
9
+ # The diaspora ID of the person who shares his profile
10
+ # @see Person#author
11
+ # @return [String] sender ID
12
+ property :author
13
+
14
+ # @!attribute [r] recipient
15
+ # The diaspora ID of the person who will be shared with
16
+ # @see Validation::Rule::DiasporaId
17
+ # @return [String] recipient ID
18
+ property :recipient
19
+
20
+ # @!attribute [r] following
21
+ # @return [Boolean] if the author is following the person
22
+ property :following, default: true
23
+
24
+ # @!attribute [r] sharing
25
+ # @return [Boolean] if the author is sharing with the person
26
+ property :sharing, default: true
27
+ end
28
+ end
29
+ end
@@ -22,17 +22,16 @@ module DiasporaFederation
22
22
  # @return [[Entities::Message]] Messages of this conversation
23
23
  entity :messages, [Entities::Message]
24
24
 
25
- # @!attribute [r] diaspora_id
25
+ # @!attribute [r] author
26
26
  # The diaspora ID of the person initiated the conversation.
27
- # @see Person#diaspora_id
27
+ # @see Person#author
28
28
  # @return [String] diaspora ID
29
- property :diaspora_id, xml_name: :diaspora_handle
29
+ property :author, xml_name: :diaspora_handle
30
30
 
31
- # @!attribute [r] participant_ids
31
+ # @!attribute [r] participants
32
32
  # The diaspora IDs of the persons participating the conversation separated by ";".
33
- # @see Person#diaspora_id
34
33
  # @return [String] participants diaspora IDs
35
- property :participant_ids, xml_name: :participant_handles
34
+ property :participants, xml_name: :participant_handles
36
35
  end
37
36
  end
38
37
  end
@@ -4,32 +4,24 @@ module DiasporaFederation
4
4
  #
5
5
  # @see Validators::LikeValidator
6
6
  class Like < Entity
7
+ # old signature order
8
+ # @deprecated
9
+ LEGACY_SIGNATURE_ORDER = %i(positive guid parent_type parent_guid author).freeze
10
+
11
+ include Relayable
12
+
7
13
  # @!attribute [r] positive
8
14
  # If +true+ set a like, if +false+, set a dislike (dislikes are currently not
9
15
  # implemented in the Diaspora frontend).
10
16
  # @return [Boolean] is it a like or a dislike
11
17
  property :positive
12
18
 
13
- # @!attribute [r] guid
14
- # a random string of at least 16 chars.
15
- # @see Validation::Rule::Guid
16
- # @return [String] like guid
17
- property :guid
18
-
19
- # @!attribute [r] target_type
20
- # A string describing the type of the target.
19
+ # @!attribute [r] parent_type
20
+ # A string describing the type of the parent.
21
21
  # Can be "Post" or "Comment" (Comments are currently not implemented in the
22
22
  # Diaspora Frontend).
23
- # @return [String] target type
24
- property :target_type
25
-
26
- include Relayable
27
-
28
- # @!attribute [r] diaspora_id
29
- # The diaspora ID of the person who posts a like
30
- # @see Person#diaspora_id
31
- # @return [String] diaspora ID
32
- property :diaspora_id, xml_name: :diaspora_handle
23
+ # @return [String] parent type
24
+ property :parent_type, xml_name: :target_type
33
25
  end
34
26
  end
35
27
  end
@@ -4,11 +4,9 @@ module DiasporaFederation
4
4
  #
5
5
  # @see Validators::MessageValidator
6
6
  class Message < Entity
7
- # @!attribute [r] guid
8
- # a random string of at least 16 chars.
9
- # @see Validation::Rule::Guid
10
- # @return [String] message guid
11
- property :guid
7
+ # old signature order
8
+ # @deprecated
9
+ LEGACY_SIGNATURE_ORDER = %i(guid parent_guid text created_at author conversation_guid).freeze
12
10
 
13
11
  include Relayable
14
12
 
@@ -22,19 +20,15 @@ module DiasporaFederation
22
20
  # @return [Time] creation time
23
21
  property :created_at, default: -> { Time.now.utc }
24
22
 
25
- # @!attribute [r] diaspora_id
26
- # The diaspora ID of the message author.
27
- # @see Person#diaspora_id
28
- # @return [String] diaspora ID
29
- property :diaspora_id, xml_name: :diaspora_handle
30
-
31
23
  # @!attribute [r] conversation_guid
32
24
  # guid of a conversation this message belongs to
33
25
  # @see Conversation#guid
34
26
  # @return [String] conversation guid
35
27
  property :conversation_guid
36
28
 
37
- def self.get_target_entity_type(*)
29
+ # The {Message} parent is a {Conversation}
30
+ # @return [String] parent type
31
+ def parent_type
38
32
  "Conversation"
39
33
  end
40
34
  end
@@ -4,25 +4,17 @@ module DiasporaFederation
4
4
  #
5
5
  # @see Validators::Participation
6
6
  class Participation < Entity
7
- # @!attribute [r] guid
8
- # a random string of at least 16 chars.
9
- # @see Validation::Rule::Guid
10
- # @return [String] participation guid
11
- property :guid
12
-
13
- # @!attribute [r] target_type
14
- # a string describing a type of the target to subscribe on.
15
- # currently only "Post" is supported.
16
- # @return [String] target type
17
- property :target_type
7
+ # old signature order
8
+ # @deprecated
9
+ LEGACY_SIGNATURE_ORDER = %i(guid parent_type parent_guid author).freeze
18
10
 
19
11
  include Relayable
20
12
 
21
- # @!attribute [r] diaspora_id
22
- # The diaspora ID of the person who subscribes on a post
23
- # @see Person#diaspora_id
24
- # @return [String] diaspora ID
25
- property :diaspora_id, xml_name: :diaspora_handle
13
+ # @!attribute [r] parent_type
14
+ # a string describing a type of the target to subscribe on.
15
+ # currently only "Post" is supported.
16
+ # @return [String] parent type
17
+ property :parent_type, xml_name: :target_type
26
18
  end
27
19
  end
28
20
  end
@@ -11,11 +11,15 @@ module DiasporaFederation
11
11
  # @return [String] guid
12
12
  property :guid
13
13
 
14
- # @!attribute [r] diaspora_id
14
+ # @!attribute [r] author
15
15
  # The diaspora ID of the person
16
16
  # @see Validation::Rule::DiasporaId
17
17
  # @return [String] diaspora ID
18
- property :diaspora_id, xml_name: :diaspora_handle
18
+ # @!attribute [r] diaspora_id
19
+ # Alias for author
20
+ # @see Person#author
21
+ # @return [String] diaspora ID
22
+ property :author, alias: :diaspora_id, xml_name: :diaspora_handle
19
23
 
20
24
  # @!attribute [r] url
21
25
  # @see Discovery::WebFinger#seed_url
@@ -10,11 +10,11 @@ module DiasporaFederation
10
10
  # @return [String] guid
11
11
  property :guid
12
12
 
13
- # @!attribute [r] diaspora_id
13
+ # @!attribute [r] author
14
14
  # The diaspora ID of the person who uploaded the photo
15
- # @see Person#diaspora_id
15
+ # @see Person#author
16
16
  # @return [String] author diaspora ID
17
- property :diaspora_id, xml_name: :diaspora_handle
17
+ property :author, xml_name: :diaspora_handle
18
18
 
19
19
  # @!attribute [r] public
20
20
  # Points if the photo is visible to everyone or only to some aspects
@@ -4,27 +4,21 @@ module DiasporaFederation
4
4
  #
5
5
  # @see Validators::PollParticipationValidator
6
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
7
+ # old signature order
8
+ # @deprecated
9
+ LEGACY_SIGNATURE_ORDER = %i(guid parent_guid author poll_answer_guid).freeze
12
10
 
13
11
  include Relayable
14
12
 
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
13
  # @!attribute [r] poll_answer_guid
22
14
  # guid of the answer selected by the user.
23
15
  # @see PollAnswer#guid
24
16
  # @return [String] poll answer guid
25
17
  property :poll_answer_guid
26
18
 
27
- def self.get_target_entity_type(*)
19
+ # The {PollParticipation} parent is a {Poll}
20
+ # @return [String] parent type
21
+ def parent_type
28
22
  "Poll"
29
23
  end
30
24
  end
@@ -0,0 +1,37 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this is a module that defines common properties for a post which
4
+ # include {StatusMessage} and {Reshare}.
5
+ module Post
6
+ # on inclusion of this module the required properties for a post are added to the object that includes it
7
+ #
8
+ # @!attribute [r] author
9
+ # The diaspora ID of the person who posts the post
10
+ # @see Person#author
11
+ # @return [String] diaspora ID
12
+ #
13
+ # @!attribute [r] guid
14
+ # a random string of at least 16 chars.
15
+ # @see Validation::Rule::Guid
16
+ # @return [String] status message guid
17
+ #
18
+ # @!attribute [r] created_at
19
+ # post entity creation time
20
+ # @return [Time] creation time
21
+ #
22
+ # @!attribute [r] provider_display_name
23
+ # a string that describes a means by which a user has posted the post
24
+ # @return [String] provider display name
25
+ #
26
+ # @param [Entity] entity the entity in which it is included
27
+ def self.included(entity)
28
+ entity.class_eval do
29
+ property :author, xml_name: :diaspora_handle
30
+ property :guid
31
+ property :created_at, default: -> { Time.now.utc }
32
+ property :provider_display_name, default: nil
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -4,11 +4,15 @@ module DiasporaFederation
4
4
  #
5
5
  # @see Validators::ProfileValidator
6
6
  class Profile < Entity
7
- # @!attribute [r] diaspora_id
7
+ # @!attribute [r] author
8
8
  # The diaspora ID of the person
9
- # @see Person#diaspora_id
9
+ # @see Person#author
10
+ # @return [String] diaspora ID
11
+ # @!attribute [r] diaspora_id
12
+ # Alias for author
13
+ # @see Profile#author
10
14
  # @return [String] diaspora ID
11
- property :diaspora_id, xml_name: :diaspora_handle
15
+ property :author, alias: :diaspora_id, xml_name: :diaspora_handle
12
16
 
13
17
  # @!attribute [r] first_name
14
18
  # @deprecated We decided to only use one name field, these should be removed
@@ -2,15 +2,44 @@ module DiasporaFederation
2
2
  module Entities
3
3
  # this is a module that defines common properties for relayable entities
4
4
  # which include Like, Comment, Participation, Message, etc. Each relayable
5
- # has a parent, identified by guid. Relayables also are signed and signing/verificating
5
+ # has a parent, identified by guid. Relayables also are signed and signing/verification
6
6
  # logic is embedded into Salmon XML processing code.
7
7
  module Relayable
8
+ include Logging
9
+
10
+ # digest instance used for signing
11
+ DIGEST = OpenSSL::Digest::SHA256.new
12
+
13
+ # order from the parsed xml for signature
14
+ # @return [Array] order from xml
15
+ attr_reader :xml_order
16
+
17
+ # additional properties from parsed xml
18
+ # @return [Hash] additional xml elements
19
+ attr_reader :additional_xml_elements
20
+
8
21
  # on inclusion of this module the required properties for a relayable are added to the object that includes it
9
22
  #
23
+ # @!attribute [r] author
24
+ # The diaspora ID of the author.
25
+ # @see Person#author
26
+ # @return [String] diaspora ID
27
+ #
28
+ # @!attribute [r] guid
29
+ # a random string of at least 16 chars.
30
+ # @see Validation::Rule::Guid
31
+ # @return [String] comment guid
32
+ #
10
33
  # @!attribute [r] parent_guid
11
34
  # @see StatusMessage#guid
12
35
  # @return [String] parent guid
13
36
  #
37
+ # @!attribute [r] author_signature
38
+ # Contains a signature of the entity using the private key of the author of a post itself.
39
+ # The presence of this signature is mandatory. Without it the entity won't be accepted by
40
+ # a target pod.
41
+ # @return [String] author signature
42
+ #
14
43
  # @!attribute [r] parent_author_signature
15
44
  # Contains a signature of the entity using the private key of the author of a parent post
16
45
  # This signature is required only when federation from upstream (parent) post author to
@@ -19,95 +48,170 @@ module DiasporaFederation
19
48
  #
20
49
  # @return [String] parent author signature
21
50
  #
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
51
  # @param [Entity] entity the entity in which it is included
29
52
  def self.included(entity)
30
53
  entity.class_eval do
54
+ property :author, xml_name: :diaspora_handle
55
+ property :guid
31
56
  property :parent_guid
32
- property :parent_author_signature, default: nil
33
57
  property :author_signature, default: nil
34
-
35
- def self.get_target_entity_type(data)
36
- data[:target_type] || "Post"
37
- end
58
+ property :parent_author_signature, default: nil
38
59
  end
39
- end
40
-
41
- # Generates XML and updates signatures
42
- # @see Entity#to_xml
43
- # @return [Nokogiri::XML::Element] root element containing properties as child elements
44
- def to_xml
45
- entity_xml.tap do |xml|
46
- hash = to_h
47
- Relayable.update_signatures!(hash, self.class)
48
60
 
49
- xml.at_xpath("author_signature").content = hash[:author_signature]
50
- xml.at_xpath("parent_author_signature").content = hash[:parent_author_signature]
51
- end
61
+ entity.extend ParseXML
52
62
  end
53
63
 
54
- # Exception raised when verify_signatures fails to verify signatures (signatures are wrong)
55
- class SignatureVerificationFailed < ArgumentError
64
+ # Initializes a new relayable Entity with order and additional xml elements
65
+ #
66
+ # @param [Hash] data entity data
67
+ # @param [Array] xml_order order from xml
68
+ # @param [Hash] additional_xml_elements additional xml elements
69
+ # @see DiasporaFederation::Entity#initialize
70
+ def initialize(data, xml_order=nil, additional_xml_elements={})
71
+ @xml_order = xml_order
72
+ @additional_xml_elements = additional_xml_elements
73
+
74
+ super(data)
56
75
  end
57
76
 
58
77
  # verifies the signatures (+author_signature+ and +parent_author_signature+ if needed)
59
- # @param [Hash] data hash with data to verify
60
78
  # @raise [SignatureVerificationFailed] if the signature is not valid or no public key is found
61
- def self.verify_signatures(data, klass)
62
- pkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, data[:diaspora_id])
63
- raise SignatureVerificationFailed, "failed to fetch public key for #{data[:diaspora_id]}" if pkey.nil?
64
- raise SignatureVerificationFailed, "wrong author_signature" unless Signing.verify_signature(
65
- data, data[:author_signature], pkey
66
- )
79
+ def verify_signatures
80
+ pubkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, author)
81
+ raise PublicKeyNotFound, "author_signature author=#{author} guid=#{guid}" if pubkey.nil?
82
+ raise SignatureVerificationFailed, "wrong author_signature" unless verify_signature(pubkey, author_signature)
67
83
 
68
- author_is_local = DiasporaFederation.callbacks.trigger(
69
- :entity_author_is_local?,
70
- klass.get_target_entity_type(data),
71
- data[:parent_guid]
72
- )
73
- verify_parent_signature(data, klass) unless author_is_local
84
+ parent_author_local = DiasporaFederation.callbacks.trigger(:entity_author_is_local?, parent_type, parent_guid)
85
+ verify_parent_author_signature unless parent_author_local
74
86
  end
75
87
 
88
+ private
89
+
76
90
  # this happens only on downstream federation
77
- # @param [Hash] data hash with data to verify
78
- def self.verify_parent_signature(data, klass)
79
- pkey = DiasporaFederation.callbacks.trigger(
80
- :fetch_author_public_key_by_entity_guid,
81
- klass.get_target_entity_type(data),
82
- data[:parent_guid]
83
- )
84
- raise SignatureVerificationFailed,
85
- "failed to fetch public key for author of #{data[:parent_guid]}" if pkey.nil?
86
- raise SignatureVerificationFailed, "wrong parent_author_signature" unless Signing.verify_signature(
87
- data, data[:parent_author_signature], pkey
91
+ def verify_parent_author_signature
92
+ pubkey = DiasporaFederation.callbacks.trigger(:fetch_author_public_key_by_entity_guid, parent_type, parent_guid)
93
+
94
+ raise PublicKeyNotFound, "parent_author_signature parent_guid=#{parent_guid} guid=#{guid}" if pubkey.nil?
95
+ unless verify_signature(pubkey, parent_author_signature)
96
+ raise SignatureVerificationFailed, "wrong parent_author_signature parent_guid=#{parent_guid}"
97
+ end
98
+ end
99
+
100
+ # Check that signature is a correct signature
101
+ #
102
+ # @param [OpenSSL::PKey::RSA] pubkey An RSA key
103
+ # @param [String] signature The signature to be verified.
104
+ # @return [Boolean] signature valid
105
+ def verify_signature(pubkey, signature)
106
+ if signature.nil?
107
+ logger.warn "event=verify_signature status=abort reason=no_signature guid=#{guid}"
108
+ return false
109
+ end
110
+
111
+ pubkey.verify(DIGEST, Base64.decode64(signature), signature_data).tap do |valid|
112
+ logger.info "event=verify_signature status=complete guid=#{guid} valid=#{valid}"
113
+ end
114
+ end
115
+
116
+ # sign with author key
117
+ # @raise [AuthorPrivateKeyNotFound] if the author private key is not found
118
+ # @return [String] A Base64 encoded signature of #signature_data with key
119
+ def sign_with_author
120
+ privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, author)
121
+ raise AuthorPrivateKeyNotFound, "author=#{author} guid=#{guid}" if privkey.nil?
122
+ sign_with_key(privkey).tap do
123
+ logger.info "event=sign status=complete signature=author_signature author=#{author} guid=#{guid}"
124
+ end
125
+ end
126
+
127
+ # sign with parent author key, if the parent author is local (if the private key is found)
128
+ # @return [String] A Base64 encoded signature of #signature_data with key
129
+ def sign_with_parent_author_if_available
130
+ privkey = DiasporaFederation.callbacks.trigger(
131
+ :fetch_author_private_key_by_entity_guid, parent_type, parent_guid
88
132
  )
133
+ if privkey
134
+ sign_with_key(privkey).tap do
135
+ logger.info "event=sign status=complete signature=parent_author_signature guid=#{guid}"
136
+ end
137
+ end
89
138
  end
90
- private_class_method :verify_parent_signature
91
139
 
92
- # Adds signatures to a given hash with the keys of the author and the parent
93
- # if the signatures are not in the hash yet and if the keys are available.
140
+ # Sign the data with the key
94
141
  #
95
- # @param [Hash] data hash given for a signing
96
- def self.update_signatures!(data, klass)
97
- if data[:author_signature].nil?
98
- pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, data[:diaspora_id])
99
- data[:author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
142
+ # @param [OpenSSL::PKey::RSA] privkey An RSA key
143
+ # @return [String] A Base64 encoded signature of #signature_data with key
144
+ def sign_with_key(privkey)
145
+ Base64.strict_encode64(privkey.sign(DIGEST, signature_data))
146
+ end
147
+
148
+ # Sort all XML elements according to the order used for the signatures.
149
+ # It updates also the signatures with the keys of the author and the parent
150
+ # if the signatures are not there yet and if the keys are available.
151
+ #
152
+ # @return [Hash] sorted xml elements with updated signatures
153
+ def xml_elements
154
+ xml_data = super.merge(additional_xml_elements)
155
+ Hash[signature_order.map {|element| [element, xml_data[element]] }].tap do |xml_elements|
156
+ xml_elements[:author_signature] = author_signature || sign_with_author
157
+ xml_elements[:parent_author_signature] = parent_author_signature || sign_with_parent_author_if_available.to_s
100
158
  end
159
+ end
101
160
 
102
- if data[:parent_author_signature].nil?
103
- pkey = DiasporaFederation.callbacks.trigger(
104
- :fetch_author_private_key_by_entity_guid,
105
- klass.get_target_entity_type(data),
106
- data[:parent_guid]
107
- )
108
- data[:parent_author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
161
+ # the order for signing
162
+ # @return [Array]
163
+ def signature_order
164
+ xml_order.nil? ? self.class::LEGACY_SIGNATURE_ORDER : xml_order.reject {|name| name =~ /signature/ }
165
+ end
166
+
167
+ # @return [String] signature data string
168
+ def signature_data
169
+ data = to_h.merge(additional_xml_elements)
170
+ signature_order.map {|name| data[name] }.join(";")
171
+ end
172
+
173
+ # override class methods from {Entity} to parse the xml
174
+ module ParseXML
175
+ private
176
+
177
+ # @param [Nokogiri::XML::Element] root_node xml nodes
178
+ # @return [Entity] instance
179
+ def populate_entity(root_node)
180
+ # Use all known properties to build the Entity (entity_data). All additional xml elements
181
+ # are respected and attached to a hash as string (additional_xml_elements). It also remembers
182
+ # the order of the xml-nodes (xml_order). This is needed to support receiving objects from
183
+ # the future versions of Diaspora, where new elements may have been added.
184
+ entity_data = {}
185
+ additional_xml_elements = {}
186
+
187
+ xml_order = root_node.element_children.map do |child|
188
+ xml_name = child.name
189
+ property = find_property_for_xml_name(xml_name)
190
+
191
+ if property
192
+ entity_data[property] = parse_element_from_node(xml_name, class_props[property], root_node)
193
+ property
194
+ else
195
+ additional_xml_elements[xml_name] = child.text
196
+ xml_name
197
+ end
198
+ end
199
+
200
+ new(entity_data, xml_order, additional_xml_elements).tap(&:verify_signatures)
109
201
  end
110
202
  end
203
+
204
+ # Exception raised when creating the author_signature failes, because the private key was not found
205
+ class AuthorPrivateKeyNotFound < RuntimeError
206
+ end
207
+
208
+ # Exception raised when verify_signatures fails to verify signatures (no public key found)
209
+ class PublicKeyNotFound < RuntimeError
210
+ end
211
+
212
+ # Exception raised when verify_signatures fails to verify signatures (signatures are wrong)
213
+ class SignatureVerificationFailed < RuntimeError
214
+ end
111
215
  end
112
216
  end
113
217
  end