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.
- checksums.yaml +4 -4
- data/lib/diaspora_federation.rb +103 -18
- data/lib/diaspora_federation/discovery/discovery.rb +1 -1
- data/lib/diaspora_federation/discovery/h_card.rb +4 -5
- data/lib/diaspora_federation/discovery/host_meta.rb +1 -1
- data/lib/diaspora_federation/discovery/web_finger.rb +8 -8
- data/lib/diaspora_federation/discovery/xrd_document.rb +6 -7
- data/lib/diaspora_federation/entities.rb +21 -10
- data/lib/diaspora_federation/entities/account_deletion.rb +7 -3
- data/lib/diaspora_federation/entities/comment.rb +13 -10
- data/lib/diaspora_federation/entities/contact.rb +29 -0
- data/lib/diaspora_federation/entities/conversation.rb +5 -6
- data/lib/diaspora_federation/entities/like.rb +10 -18
- data/lib/diaspora_federation/entities/message.rb +6 -12
- data/lib/diaspora_federation/entities/participation.rb +8 -16
- data/lib/diaspora_federation/entities/person.rb +6 -2
- data/lib/diaspora_federation/entities/photo.rb +3 -3
- data/lib/diaspora_federation/entities/poll_participation.rb +6 -12
- data/lib/diaspora_federation/entities/post.rb +37 -0
- data/lib/diaspora_federation/entities/profile.rb +7 -3
- data/lib/diaspora_federation/entities/relayable.rb +169 -65
- data/lib/diaspora_federation/entities/relayable_retraction.rb +33 -32
- data/lib/diaspora_federation/entities/request.rb +20 -6
- data/lib/diaspora_federation/entities/reshare.rb +5 -27
- data/lib/diaspora_federation/entities/retraction.rb +6 -6
- data/lib/diaspora_federation/entities/signed_retraction.rb +32 -26
- data/lib/diaspora_federation/entities/status_message.rb +2 -22
- data/lib/diaspora_federation/entity.rb +137 -38
- data/lib/diaspora_federation/federation.rb +9 -0
- data/lib/diaspora_federation/federation/fetcher.rb +26 -0
- data/lib/diaspora_federation/federation/receiver.rb +41 -0
- data/lib/diaspora_federation/federation/receiver/abstract_receiver.rb +35 -0
- data/lib/diaspora_federation/federation/receiver/exceptions.rb +13 -0
- data/lib/diaspora_federation/federation/receiver/private.rb +15 -0
- data/lib/diaspora_federation/federation/receiver/public.rb +9 -0
- data/lib/diaspora_federation/federation/sender.rb +33 -0
- data/lib/diaspora_federation/federation/sender/hydra_wrapper.rb +92 -0
- data/lib/diaspora_federation/{fetcher.rb → http_client.rb} +6 -6
- data/lib/diaspora_federation/properties_dsl.rb +51 -14
- data/lib/diaspora_federation/salmon.rb +2 -1
- data/lib/diaspora_federation/salmon/aes.rb +1 -1
- data/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb +61 -0
- data/lib/diaspora_federation/salmon/encrypted_slap.rb +69 -50
- data/lib/diaspora_federation/salmon/exceptions.rb +8 -14
- data/lib/diaspora_federation/salmon/magic_envelope.rb +80 -39
- data/lib/diaspora_federation/salmon/slap.rb +20 -51
- data/lib/diaspora_federation/salmon/xml_payload.rb +5 -104
- data/lib/diaspora_federation/validators.rb +22 -16
- data/lib/diaspora_federation/validators/account_deletion_validator.rb +1 -1
- data/lib/diaspora_federation/validators/comment_validator.rb +0 -4
- data/lib/diaspora_federation/validators/contact_validator.rb +13 -0
- data/lib/diaspora_federation/validators/conversation_validator.rb +2 -2
- data/lib/diaspora_federation/validators/like_validator.rb +1 -3
- data/lib/diaspora_federation/validators/message_validator.rb +0 -4
- data/lib/diaspora_federation/validators/participation_validator.rb +1 -5
- data/lib/diaspora_federation/validators/person_validator.rb +1 -1
- data/lib/diaspora_federation/validators/photo_validator.rb +2 -2
- data/lib/diaspora_federation/validators/poll_participation_validator.rb +0 -4
- data/lib/diaspora_federation/validators/profile_validator.rb +1 -1
- data/lib/diaspora_federation/validators/relayable_retraction_validator.rb +1 -1
- data/lib/diaspora_federation/validators/relayable_validator.rb +2 -0
- data/lib/diaspora_federation/validators/request_validator.rb +3 -2
- data/lib/diaspora_federation/validators/reshare_validator.rb +3 -3
- data/lib/diaspora_federation/validators/retraction_validator.rb +2 -2
- data/lib/diaspora_federation/validators/rules/guid.rb +16 -7
- data/lib/diaspora_federation/validators/signed_retraction_validator.rb +1 -1
- data/lib/diaspora_federation/validators/status_message_validator.rb +2 -2
- data/lib/diaspora_federation/version.rb +1 -1
- metadata +20 -11
- data/lib/diaspora_federation/receiver.rb +0 -28
- data/lib/diaspora_federation/receiver/private.rb +0 -19
- data/lib/diaspora_federation/receiver/public.rb +0 -13
- 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]
|
25
|
+
# @!attribute [r] author
|
26
26
|
# The diaspora ID of the person initiated the conversation.
|
27
|
-
# @see Person#
|
27
|
+
# @see Person#author
|
28
28
|
# @return [String] diaspora ID
|
29
|
-
property :
|
29
|
+
property :author, xml_name: :diaspora_handle
|
30
30
|
|
31
|
-
# @!attribute [r]
|
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 :
|
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]
|
14
|
-
#
|
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]
|
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
|
-
#
|
8
|
-
#
|
9
|
-
|
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
|
-
|
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
|
-
#
|
8
|
-
#
|
9
|
-
|
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]
|
22
|
-
#
|
23
|
-
#
|
24
|
-
# @return [String]
|
25
|
-
property :
|
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]
|
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
|
-
|
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]
|
13
|
+
# @!attribute [r] author
|
14
14
|
# The diaspora ID of the person who uploaded the photo
|
15
|
-
# @see Person#
|
15
|
+
# @see Person#author
|
16
16
|
# @return [String] author diaspora ID
|
17
|
-
property :
|
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
|
-
#
|
8
|
-
#
|
9
|
-
|
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
|
-
|
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]
|
7
|
+
# @!attribute [r] author
|
8
8
|
# The diaspora ID of the person
|
9
|
-
# @see Person#
|
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/
|
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
|
-
|
50
|
-
xml.at_xpath("parent_author_signature").content = hash[:parent_author_signature]
|
51
|
-
end
|
61
|
+
entity.extend ParseXML
|
52
62
|
end
|
53
63
|
|
54
|
-
#
|
55
|
-
|
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
|
62
|
-
|
63
|
-
raise
|
64
|
-
raise SignatureVerificationFailed, "wrong author_signature" unless
|
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
|
-
|
69
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
#
|
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 [
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|