diaspora_federation 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog.md +34 -0
- data/lib/diaspora_federation/discovery/discovery.rb +2 -2
- data/lib/diaspora_federation/discovery/h_card.rb +2 -10
- data/lib/diaspora_federation/discovery/host_meta.rb +1 -1
- data/lib/diaspora_federation/discovery/web_finger.rb +47 -87
- data/lib/diaspora_federation/discovery/xrd_document.rb +20 -7
- data/lib/diaspora_federation/entities/account_migration.rb +74 -0
- data/lib/diaspora_federation/entities/comment.rb +0 -4
- data/lib/diaspora_federation/entities/event_participation.rb +0 -8
- data/lib/diaspora_federation/entities/like.rb +6 -10
- data/lib/diaspora_federation/entities/message.rb +14 -49
- data/lib/diaspora_federation/entities/participation.rb +23 -13
- data/lib/diaspora_federation/entities/poll_participation.rb +0 -4
- data/lib/diaspora_federation/entities/profile.rb +5 -0
- data/lib/diaspora_federation/entities/related_entity.rb +17 -0
- data/lib/diaspora_federation/entities/relayable.rb +82 -113
- data/lib/diaspora_federation/entities/relayable_retraction.rb +4 -43
- data/lib/diaspora_federation/entities/request.rb +4 -12
- data/lib/diaspora_federation/entities/reshare.rb +11 -7
- data/lib/diaspora_federation/entities/retraction.rb +4 -5
- data/lib/diaspora_federation/entities/signable.rb +54 -0
- data/lib/diaspora_federation/entities/signed_retraction.rb +4 -44
- data/lib/diaspora_federation/entities.rb +2 -0
- data/lib/diaspora_federation/entity.rb +74 -96
- data/lib/diaspora_federation/federation/fetcher.rb +1 -1
- data/lib/diaspora_federation/federation/receiver.rb +1 -1
- data/lib/diaspora_federation/federation/sender/hydra_wrapper.rb +37 -12
- data/lib/diaspora_federation/federation/sender.rb +2 -2
- data/lib/diaspora_federation/parsers/base_parser.rb +61 -0
- data/lib/diaspora_federation/parsers/json_parser.rb +60 -0
- data/lib/diaspora_federation/parsers/relayable_json_parser.rb +25 -0
- data/lib/diaspora_federation/parsers/relayable_xml_parser.rb +22 -0
- data/lib/diaspora_federation/parsers/xml_parser.rb +84 -0
- data/lib/diaspora_federation/parsers.rb +13 -0
- data/lib/diaspora_federation/properties_dsl.rb +1 -1
- data/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb +1 -1
- data/lib/diaspora_federation/salmon/encrypted_slap.rb +2 -99
- data/lib/diaspora_federation/salmon/magic_envelope.rb +3 -19
- data/lib/diaspora_federation/salmon/slap.rb +1 -42
- data/lib/diaspora_federation/salmon/xml_payload.rb +0 -19
- data/lib/diaspora_federation/schemas/federation_entities.json +379 -0
- data/lib/diaspora_federation/validators/account_deletion_validator.rb +1 -1
- data/lib/diaspora_federation/validators/account_migration_validator.rb +12 -0
- data/lib/diaspora_federation/validators/contact_validator.rb +2 -2
- data/lib/diaspora_federation/validators/conversation_validator.rb +1 -1
- data/lib/diaspora_federation/validators/event_validator.rb +1 -1
- data/lib/diaspora_federation/validators/h_card_validator.rb +2 -5
- data/lib/diaspora_federation/validators/message_validator.rb +1 -1
- data/lib/diaspora_federation/validators/participation_validator.rb +1 -1
- data/lib/diaspora_federation/validators/person_validator.rb +2 -2
- data/lib/diaspora_federation/validators/photo_validator.rb +1 -1
- data/lib/diaspora_federation/validators/profile_validator.rb +1 -1
- data/lib/diaspora_federation/validators/related_entity_validator.rb +1 -1
- data/lib/diaspora_federation/validators/relayable_validator.rb +1 -1
- data/lib/diaspora_federation/validators/reshare_validator.rb +2 -2
- data/lib/diaspora_federation/validators/retraction_validator.rb +1 -1
- data/lib/diaspora_federation/validators/rules/boolean.rb +2 -2
- data/lib/diaspora_federation/validators/status_message_validator.rb +1 -1
- data/lib/diaspora_federation/validators/web_finger_validator.rb +5 -6
- data/lib/diaspora_federation/validators.rb +1 -5
- data/lib/diaspora_federation/version.rb +1 -1
- data/lib/diaspora_federation.rb +6 -2
- metadata +14 -11
- data/lib/diaspora_federation/validators/relayable_retraction_validator.rb +0 -15
- data/lib/diaspora_federation/validators/request_validator.rb +0 -12
- data/lib/diaspora_federation/validators/signed_retraction_validator.rb +0 -15
- data/lib/tasks/build.rake +0 -14
- data/lib/tasks/diaspora_federation_tasks.rake +0 -4
- data/lib/tasks/rails4.rake +0 -15
- data/lib/tasks/tests.rake +0 -18
@@ -4,11 +4,22 @@ module DiasporaFederation
|
|
4
4
|
#
|
5
5
|
# @see Validators::Participation
|
6
6
|
class Participation < Entity
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
7
|
+
# @!attribute [r] author
|
8
|
+
# The diaspora* ID of the author
|
9
|
+
# @see Person#author
|
10
|
+
# @return [String] diaspora* ID
|
11
|
+
property :author, :string, xml_name: :diaspora_handle
|
10
12
|
|
11
|
-
|
13
|
+
# @!attribute [r] guid
|
14
|
+
# A random string of at least 16 chars
|
15
|
+
# @see Validation::Rule::Guid
|
16
|
+
# @return [String] guid
|
17
|
+
property :guid, :string
|
18
|
+
|
19
|
+
# @!attribute [r] parent_guid
|
20
|
+
# @see StatusMessage#guid
|
21
|
+
# @return [String] parent guid
|
22
|
+
property :parent_guid, :string
|
12
23
|
|
13
24
|
# @!attribute [r] parent_type
|
14
25
|
# A string describing a type of the target to subscribe on
|
@@ -16,10 +27,9 @@ module DiasporaFederation
|
|
16
27
|
# @return [String] parent type
|
17
28
|
property :parent_type, :string, xml_name: :target_type
|
18
29
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
sender == author
|
30
|
+
# @return [String] string representation of this object
|
31
|
+
def to_s
|
32
|
+
"#{super}:#{parent_type}:#{parent_guid}"
|
23
33
|
end
|
24
34
|
|
25
35
|
# Validates that the parent exists and the parent author is local
|
@@ -28,12 +38,12 @@ module DiasporaFederation
|
|
28
38
|
raise ParentNotLocal, "obj=#{self}" unless parent && parent.local
|
29
39
|
end
|
30
40
|
|
31
|
-
#
|
32
|
-
# @see Entity.
|
33
|
-
# @param [
|
41
|
+
# Validate that the parent is local.
|
42
|
+
# @see Entity.from_hash
|
43
|
+
# @param [Hash] hash entity initialization hash
|
34
44
|
# @return [Entity] instance
|
35
|
-
|
36
|
-
|
45
|
+
def self.from_hash(hash)
|
46
|
+
super.tap(&:validate_parent)
|
37
47
|
end
|
38
48
|
|
39
49
|
# Raised, if the parent is not owned by the receiving pod.
|
@@ -4,10 +4,6 @@ module DiasporaFederation
|
|
4
4
|
#
|
5
5
|
# @see Validators::PollParticipationValidator
|
6
6
|
class PollParticipation < Entity
|
7
|
-
# Old signature order
|
8
|
-
# @deprecated
|
9
|
-
LEGACY_SIGNATURE_ORDER = %i(guid parent_guid author poll_answer_guid).freeze
|
10
|
-
|
11
7
|
# The {PollParticipation} parent is a {Poll}
|
12
8
|
PARENT_TYPE = "Poll".freeze
|
13
9
|
|
@@ -53,6 +53,11 @@ module DiasporaFederation
|
|
53
53
|
# @return [Boolean] searchable flag
|
54
54
|
property :searchable, :boolean, default: true
|
55
55
|
|
56
|
+
# @!attribute [r] public
|
57
|
+
# Shows whether the profile is visible to everyone or only to contacts
|
58
|
+
# @return [Boolean] is it public
|
59
|
+
property :public, :boolean, default: false
|
60
|
+
|
56
61
|
property :nsfw, :boolean, default: false
|
57
62
|
property :tag_string, :string, default: nil
|
58
63
|
|
@@ -24,10 +24,27 @@ module DiasporaFederation
|
|
24
24
|
# @return [RelatedEntity] parent entity
|
25
25
|
entity :parent, Entities::RelatedEntity, default: nil
|
26
26
|
|
27
|
+
# Get related entity from the backend or fetch it from remote if not available locally
|
28
|
+
# @return [RelatedEntity] fetched related entity
|
29
|
+
def self.fetch(author, type, guid)
|
30
|
+
# Try to fetch locally
|
31
|
+
entity = DiasporaFederation.callbacks.trigger(:fetch_related_entity, type, guid)
|
32
|
+
return entity if entity
|
33
|
+
|
34
|
+
# Fetch and receive entity from remote if not available locally
|
35
|
+
Federation::Fetcher.fetch_public(author, type, guid)
|
36
|
+
DiasporaFederation.callbacks.trigger(:fetch_related_entity, type, guid)
|
37
|
+
end
|
38
|
+
|
27
39
|
# never add {RelatedEntity} to xml
|
28
40
|
def to_xml
|
29
41
|
nil
|
30
42
|
end
|
43
|
+
|
44
|
+
# never add {RelatedEntity} to json
|
45
|
+
def to_json
|
46
|
+
nil
|
47
|
+
end
|
31
48
|
end
|
32
49
|
end
|
33
50
|
end
|
@@ -5,18 +5,11 @@ module DiasporaFederation
|
|
5
5
|
# has a parent, identified by guid. Relayables are also signed and signing/verification
|
6
6
|
# logic is embedded into Salmon XML processing code.
|
7
7
|
module Relayable
|
8
|
-
include
|
8
|
+
include Signable
|
9
9
|
|
10
|
-
#
|
11
|
-
|
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
|
10
|
+
# Additional properties from parsed input object
|
11
|
+
# @return [Hash] additional elements
|
12
|
+
attr_reader :additional_data
|
20
13
|
|
21
14
|
# On inclusion of this module the required properties for a relayable are added to the object that includes it.
|
22
15
|
#
|
@@ -35,16 +28,15 @@ module DiasporaFederation
|
|
35
28
|
# @return [String] parent guid
|
36
29
|
#
|
37
30
|
# @!attribute [r] author_signature
|
38
|
-
# Contains a signature of the entity using the private key of the author of a
|
31
|
+
# Contains a signature of the entity using the private key of the author of a relayable itself.
|
39
32
|
# The presence of this signature is mandatory. Without it the entity won't be accepted by
|
40
33
|
# a target pod.
|
41
34
|
# @return [String] author signature
|
42
35
|
#
|
43
36
|
# @!attribute [r] parent_author_signature
|
44
|
-
# Contains a signature of the entity using the private key of the author of a parent post
|
45
|
-
# This signature
|
46
|
-
#
|
47
|
-
# received from one of their subscribers to all others.
|
37
|
+
# Contains a signature of the entity using the private key of the author of a parent post.
|
38
|
+
# @deprecated This signature isn't required anymore, because we can check the signature from
|
39
|
+
# the parent author in the MagicEnvelope.
|
48
40
|
# @return [String] parent author signature
|
49
41
|
#
|
50
42
|
# @!attribute [r] parent
|
@@ -62,33 +54,33 @@ module DiasporaFederation
|
|
62
54
|
entity :parent, Entities::RelatedEntity
|
63
55
|
end
|
64
56
|
|
65
|
-
klass.extend
|
57
|
+
klass.extend Parsing
|
66
58
|
end
|
67
59
|
|
68
60
|
# Initializes a new relayable Entity with order and additional xml elements
|
69
61
|
#
|
70
62
|
# @param [Hash] data entity data
|
71
|
-
# @param [Array]
|
72
|
-
# @param [Hash]
|
63
|
+
# @param [Array] signature_order order for the signature
|
64
|
+
# @param [Hash] additional_data additional xml elements
|
73
65
|
# @see DiasporaFederation::Entity#initialize
|
74
|
-
def initialize(data,
|
75
|
-
|
76
|
-
@
|
66
|
+
def initialize(data, signature_order=nil, additional_data={})
|
67
|
+
self.signature_order = signature_order if signature_order
|
68
|
+
@additional_data = additional_data
|
77
69
|
|
78
70
|
super(data)
|
79
71
|
end
|
80
72
|
|
81
|
-
# Verifies the
|
82
|
-
# @
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
73
|
+
# Verifies the +author_signature+ if needed.
|
74
|
+
# @see DiasporaFederation::Entities::Signable#verify_signature
|
75
|
+
#
|
76
|
+
# @raise [SignatureVerificationFailed] if the signature is not valid
|
77
|
+
# @raise [PublicKeyNotFound] if no public key is found
|
78
|
+
def verify_signature
|
79
|
+
super(author, :author_signature) unless author == parent.author
|
88
80
|
end
|
89
81
|
|
90
82
|
def sender_valid?(sender)
|
91
|
-
sender == author || sender == parent.author
|
83
|
+
(sender == author && parent.local) || sender == parent.author
|
92
84
|
end
|
93
85
|
|
94
86
|
# @return [String] string representation of this object
|
@@ -96,26 +88,23 @@ module DiasporaFederation
|
|
96
88
|
"#{super}#{":#{parent_type}" if respond_to?(:parent_type)}:#{parent_guid}"
|
97
89
|
end
|
98
90
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
pubkey = DiasporaFederation.callbacks.trigger(:fetch_public_key, author)
|
108
|
-
raise PublicKeyNotFound, "signature=#{signature_key} person=#{author} obj=#{self}" if pubkey.nil?
|
109
|
-
|
110
|
-
signature = public_send(signature_key)
|
111
|
-
raise SignatureVerificationFailed, "no #{signature_key} for #{self}" if signature.nil?
|
112
|
-
|
113
|
-
valid = pubkey.verify(DIGEST, Base64.decode64(signature), signature_data)
|
114
|
-
raise SignatureVerificationFailed, "wrong #{signature_key} for #{self}" unless valid
|
91
|
+
def to_json
|
92
|
+
super.merge!(property_order: signature_order).tap {|json_hash|
|
93
|
+
missing_properties = json_hash[:property_order] - json_hash[:entity_data].keys
|
94
|
+
missing_properties.each {|property|
|
95
|
+
json_hash[:entity_data][property] = nil
|
96
|
+
}
|
97
|
+
}
|
98
|
+
end
|
115
99
|
|
116
|
-
|
100
|
+
# The order for signing
|
101
|
+
# @return [Array]
|
102
|
+
def signature_order
|
103
|
+
@signature_order || self.class.class_props.keys - %i[author_signature parent_author_signature parent]
|
117
104
|
end
|
118
105
|
|
106
|
+
private
|
107
|
+
|
119
108
|
# Sign with author key
|
120
109
|
# @raise [AuthorPrivateKeyNotFound] if the author private key is not found
|
121
110
|
# @return [String] A Base64 encoded signature of #signature_data with key
|
@@ -138,22 +127,14 @@ module DiasporaFederation
|
|
138
127
|
end
|
139
128
|
end
|
140
129
|
|
141
|
-
# Sign the data with the key
|
142
|
-
#
|
143
|
-
# @param [OpenSSL::PKey::RSA] privkey An RSA key
|
144
|
-
# @return [String] A Base64 encoded signature of #signature_data with key
|
145
|
-
def sign_with_key(privkey)
|
146
|
-
Base64.strict_encode64(privkey.sign(DIGEST, signature_data))
|
147
|
-
end
|
148
|
-
|
149
130
|
# Update the signatures with the keys of the author and the parent
|
150
131
|
# if the signatures are not there yet and if the keys are available.
|
151
132
|
#
|
152
133
|
# @return [Hash] properties with updated signatures
|
153
134
|
def enriched_properties
|
154
|
-
super.merge(
|
135
|
+
super.merge(additional_data).tap do |hash|
|
155
136
|
hash[:author_signature] = author_signature || sign_with_author
|
156
|
-
hash
|
137
|
+
hash.delete(:parent_author_signature)
|
157
138
|
end
|
158
139
|
end
|
159
140
|
|
@@ -161,90 +142,78 @@ module DiasporaFederation
|
|
161
142
|
#
|
162
143
|
# @return [Hash] sorted xml elements
|
163
144
|
def xml_elements
|
164
|
-
data = super
|
165
|
-
|
145
|
+
data = super.tap do |hash|
|
146
|
+
hash[:parent_author_signature] = parent_author_signature || sign_with_parent_author_if_available.to_s
|
147
|
+
end
|
148
|
+
order = signature_order + %i[author_signature parent_author_signature]
|
166
149
|
order.map {|element| [element, data[element] || ""] }.to_h
|
167
150
|
end
|
168
151
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
prop_names = self.class.class_props.keys.map(&:to_s)
|
174
|
-
xml_order.map {|name| prop_names.include?(name) ? name.to_sym : name }
|
175
|
-
else
|
176
|
-
self.class::LEGACY_SIGNATURE_ORDER
|
177
|
-
end
|
152
|
+
def signature_order=(order)
|
153
|
+
prop_names = self.class.class_props.keys.map(&:to_s)
|
154
|
+
@signature_order = order.reject {|name| name =~ /signature/ }
|
155
|
+
.map {|name| prop_names.include?(name) ? name.to_sym : name }
|
178
156
|
end
|
179
157
|
|
180
158
|
# @return [String] signature data string
|
181
159
|
def signature_data
|
182
|
-
data = normalized_properties.merge(
|
160
|
+
data = normalized_properties.merge(additional_data)
|
183
161
|
signature_order.map {|name| data[name] }.join(";")
|
184
162
|
end
|
185
163
|
|
186
|
-
# Override class methods from {Entity} to parse
|
187
|
-
module
|
188
|
-
|
189
|
-
|
190
|
-
#
|
191
|
-
#
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
entity_data
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
if property
|
205
|
-
entity_data[property] = parse_element_from_node(xml_name, class_props[property], root_node)
|
206
|
-
property
|
207
|
-
else
|
208
|
-
additional_xml_elements[xml_name] = child.text
|
209
|
-
xml_name
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
fetch_parent(entity_data)
|
214
|
-
new(entity_data, xml_order, additional_xml_elements).tap(&:verify_signatures)
|
164
|
+
# Override class methods from {Entity} to parse serialized data
|
165
|
+
module Parsing
|
166
|
+
# Does the same job as Entity.from_hash except of the following differences:
|
167
|
+
# 1) unknown properties from the properties_hash are stored to additional_data of the relayable instance
|
168
|
+
# 2) parent entity fetch is attempted
|
169
|
+
# 3) signatures verification is performed; property_order is used as the order in which properties are composed
|
170
|
+
# to compute signatures
|
171
|
+
# 4) unknown properties' keys must be of String type
|
172
|
+
#
|
173
|
+
# @see Entity.from_hash
|
174
|
+
def from_hash(properties_hash, property_order)
|
175
|
+
# Use all known properties to build the Entity (entity_data). All additional elements
|
176
|
+
# are respected and attached to a hash as string (additional_data). This is needed
|
177
|
+
# to support receiving objects from the future versions of diaspora*, where new elements may have been added.
|
178
|
+
additional_data = properties_hash.reject {|key, _| class_props.has_key?(key) }
|
179
|
+
|
180
|
+
fetch_parent(properties_hash)
|
181
|
+
new(properties_hash, property_order, additional_data).tap(&:verify_signature)
|
215
182
|
end
|
216
183
|
|
184
|
+
private
|
185
|
+
|
217
186
|
def fetch_parent(data)
|
218
187
|
type = data.fetch(:parent_type) {
|
219
188
|
break self::PARENT_TYPE if const_defined?(:PARENT_TYPE)
|
220
189
|
|
221
|
-
raise DiasporaFederation::Entity::ValidationError, "
|
190
|
+
raise DiasporaFederation::Entity::ValidationError, error_message_missing_property(data, "parent_type")
|
222
191
|
}
|
223
192
|
guid = data.fetch(:parent_guid) {
|
224
|
-
raise DiasporaFederation::Entity::ValidationError, "
|
193
|
+
raise DiasporaFederation::Entity::ValidationError, error_message_missing_property(data, "parent_guid")
|
225
194
|
}
|
226
195
|
|
227
|
-
data[:parent] =
|
196
|
+
data[:parent] = RelatedEntity.fetch(data[:author], type, guid)
|
197
|
+
end
|
228
198
|
|
229
|
-
|
199
|
+
def error_message_missing_property(data, missing_property)
|
200
|
+
obj_str = "#{class_name}#{":#{data[:guid]}" if data.has_key?(:guid)}" \
|
201
|
+
"#{" from #{data[:author]}" if data.has_key?(:author)}"
|
202
|
+
"Invalid #{obj_str}! Missing '#{missing_property}'."
|
203
|
+
end
|
204
|
+
|
205
|
+
def xml_parser_class
|
206
|
+
DiasporaFederation::Parsers::RelayableXmlParser
|
207
|
+
end
|
230
208
|
|
231
|
-
|
232
|
-
|
233
|
-
data[:parent] = DiasporaFederation.callbacks.trigger(:fetch_related_entity, type, guid)
|
209
|
+
def json_parser_class
|
210
|
+
DiasporaFederation::Parsers::RelayableJsonParser
|
234
211
|
end
|
235
212
|
end
|
236
213
|
|
237
214
|
# Raised, if creating the author_signature fails, because the private key was not found
|
238
215
|
class AuthorPrivateKeyNotFound < RuntimeError
|
239
216
|
end
|
240
|
-
|
241
|
-
# Raised, if verify_signatures fails to verify signatures (no public key found)
|
242
|
-
class PublicKeyNotFound < RuntimeError
|
243
|
-
end
|
244
|
-
|
245
|
-
# Raised, if verify_signatures fails to verify signatures (signatures are wrong)
|
246
|
-
class SignatureVerificationFailed < RuntimeError
|
247
|
-
end
|
248
217
|
end
|
249
218
|
end
|
250
219
|
end
|
@@ -53,52 +53,13 @@ module DiasporaFederation
|
|
53
53
|
# @return [String] target author signature
|
54
54
|
property :target_author_signature, :string, default: nil
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
# @return [RelatedEntity] target entity
|
59
|
-
entity :target, Entities::RelatedEntity
|
60
|
-
|
61
|
-
# Use only {Retraction} for receive
|
62
|
-
# @return [Retraction] instance as normal retraction
|
63
|
-
def to_retraction
|
64
|
-
Retraction.new(author: author, target_guid: target_guid, target_type: target_type, target: target)
|
65
|
-
end
|
66
|
-
|
67
|
-
# @return [String] string representation of this object
|
68
|
-
def to_s
|
69
|
-
"RelayableRetraction:#{target_type}:#{target_guid}"
|
56
|
+
def initialize(*)
|
57
|
+
raise "Sending RelayableRetraction is not supported anymore! Use Retraction instead!"
|
70
58
|
end
|
71
59
|
|
72
|
-
private
|
73
|
-
|
74
|
-
# @param [Nokogiri::XML::Element] root_node xml nodes
|
75
60
|
# @return [Retraction] instance
|
76
|
-
|
77
|
-
|
78
|
-
entity_data[:target] = Retraction.send(:fetch_target, entity_data[:target_type], entity_data[:target_guid])
|
79
|
-
new(entity_data).to_retraction
|
80
|
-
end
|
81
|
-
|
82
|
-
# It updates also the signatures with the keys of the author and the parent
|
83
|
-
# if the signatures are not there yet and if the keys are available.
|
84
|
-
#
|
85
|
-
# @return [Hash] xml elements with updated signatures
|
86
|
-
def enriched_properties
|
87
|
-
privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, author)
|
88
|
-
|
89
|
-
super.tap do |hash|
|
90
|
-
fill_required_signature(privkey, hash) unless privkey.nil?
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# @param [OpenSSL::PKey::RSA] privkey private key of sender
|
95
|
-
# @param [Hash] hash hash given for a signing
|
96
|
-
def fill_required_signature(privkey, hash)
|
97
|
-
if target.parent.author == author && parent_author_signature.nil?
|
98
|
-
hash[:parent_author_signature] = SignedRetraction.sign_with_key(privkey, self)
|
99
|
-
elsif target.author == author && target_author_signature.nil?
|
100
|
-
hash[:target_author_signature] = SignedRetraction.sign_with_key(privkey, self)
|
101
|
-
end
|
61
|
+
def self.from_hash(hash)
|
62
|
+
Retraction.from_hash(hash)
|
102
63
|
end
|
103
64
|
end
|
104
65
|
end
|
@@ -18,21 +18,13 @@ module DiasporaFederation
|
|
18
18
|
# @return [String] recipient ID
|
19
19
|
property :recipient, :string, xml_name: :recipient_handle
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
def to_contact
|
24
|
-
Contact.new(author: author, recipient: recipient)
|
21
|
+
def initialize(*)
|
22
|
+
raise "Sending Request is not supported anymore! Use Contact instead!"
|
25
23
|
end
|
26
24
|
|
27
|
-
# @return [String] string representation of this object
|
28
|
-
def to_s
|
29
|
-
"Request:#{author}:#{recipient}"
|
30
|
-
end
|
31
|
-
|
32
|
-
# @param [Nokogiri::XML::Element] root_node xml nodes
|
33
25
|
# @return [Retraction] instance
|
34
|
-
|
35
|
-
|
26
|
+
def self.from_hash(hash)
|
27
|
+
Contact.new(hash)
|
36
28
|
end
|
37
29
|
end
|
38
30
|
end
|
@@ -29,17 +29,21 @@ module DiasporaFederation
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# Fetch and receive root post from remote, if not available locally
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
# and validates if it's from the correct author
|
33
|
+
def validate_root
|
34
|
+
root = RelatedEntity.fetch(root_author, "Post", root_guid)
|
35
|
+
|
36
|
+
return if root_author == root.author
|
37
|
+
|
38
|
+
raise Entity::ValidationError,
|
39
|
+
"root_author mismatch: obj=#{self} root_author=#{root_author} known_root_author=#{root.author}"
|
35
40
|
end
|
36
41
|
|
37
42
|
# Fetch root post after parse
|
38
|
-
# @see Entity.
|
39
|
-
# @param [Nokogiri::XML::Element] root_node xml nodes
|
43
|
+
# @see Entity.from_hash
|
40
44
|
# @return [Entity] instance
|
41
|
-
|
42
|
-
super
|
45
|
+
def self.from_hash(hash)
|
46
|
+
super.tap(&:validate_root)
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
@@ -39,12 +39,11 @@ module DiasporaFederation
|
|
39
39
|
"Retraction:#{target_type}:#{target_guid}"
|
40
40
|
end
|
41
41
|
|
42
|
-
# @
|
42
|
+
# @see Entity.from_hash
|
43
43
|
# @return [Retraction] instance
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
new(entity_data)
|
44
|
+
def self.from_hash(hash)
|
45
|
+
hash[:target] = fetch_target(hash[:target_type], hash[:target_guid])
|
46
|
+
new(hash)
|
48
47
|
end
|
49
48
|
|
50
49
|
private_class_method def self.fetch_target(target_type, target_guid)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
module Entities
|
3
|
+
# Signable is a module that encapsulates basic signature generation/verification flow for entities.
|
4
|
+
module Signable
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
# Digest instance used for signing
|
8
|
+
DIGEST = OpenSSL::Digest::SHA256.new
|
9
|
+
|
10
|
+
# Sign the data with the key
|
11
|
+
#
|
12
|
+
# @param [OpenSSL::PKey::RSA] privkey An RSA key
|
13
|
+
# @return [String] A Base64 encoded signature of #signature_data with key
|
14
|
+
def sign_with_key(privkey)
|
15
|
+
Base64.strict_encode64(privkey.sign(DIGEST, signature_data))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Check that signature is a correct signature
|
19
|
+
#
|
20
|
+
# @param [String] author The author of the signature
|
21
|
+
# @param [String] signature_key The signature to be verified
|
22
|
+
# @raise [SignatureVerificationFailed] if the signature is not valid
|
23
|
+
# @raise [PublicKeyNotFound] if no public key is found
|
24
|
+
def verify_signature(author, signature_key)
|
25
|
+
pubkey = DiasporaFederation.callbacks.trigger(:fetch_public_key, author)
|
26
|
+
raise PublicKeyNotFound, "signature=#{signature_key} person=#{author} obj=#{self}" if pubkey.nil?
|
27
|
+
|
28
|
+
signature = public_send(signature_key)
|
29
|
+
raise SignatureVerificationFailed, "no #{signature_key} for #{self}" if signature.nil?
|
30
|
+
|
31
|
+
valid = pubkey.verify(DIGEST, Base64.decode64(signature), signature_data)
|
32
|
+
raise SignatureVerificationFailed, "wrong #{signature_key} for #{self}" unless valid
|
33
|
+
|
34
|
+
logger.info "event=verify_signature signature=#{signature_key} status=valid obj=#{self}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# This method defines what data is used for a signature creation/verification
|
38
|
+
#
|
39
|
+
# @abstract
|
40
|
+
# @return [String] a string to sign
|
41
|
+
def signature_data
|
42
|
+
raise NotImplementedError.new("you must override this method to define signature base string")
|
43
|
+
end
|
44
|
+
|
45
|
+
# Raised, if verify_signatures fails to verify signatures (no public key found)
|
46
|
+
class PublicKeyNotFound < RuntimeError
|
47
|
+
end
|
48
|
+
|
49
|
+
# Raised, if verify_signatures fails to verify signatures (signatures are wrong)
|
50
|
+
class SignatureVerificationFailed < RuntimeError
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -30,53 +30,13 @@ module DiasporaFederation
|
|
30
30
|
# @return [String] author signature
|
31
31
|
property :target_author_signature, :string, default: nil
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
# @return [RelatedEntity] target entity
|
36
|
-
entity :target, Entities::RelatedEntity
|
37
|
-
|
38
|
-
# Use only {Retraction} for receive
|
39
|
-
# @return [Retraction] instance as normal retraction
|
40
|
-
def to_retraction
|
41
|
-
Retraction.new(author: author, target_guid: target_guid, target_type: target_type, target: target)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Create signature for a retraction
|
45
|
-
# @param [OpenSSL::PKey::RSA] privkey private key of sender
|
46
|
-
# @param [SignedRetraction, RelayableRetraction] ret the retraction to sign
|
47
|
-
# @return [String] a Base64 encoded signature of the retraction with the key
|
48
|
-
def self.sign_with_key(privkey, ret)
|
49
|
-
Base64.strict_encode64(privkey.sign(Relayable::DIGEST, [ret.target_guid, ret.target_type].join(";")))
|
50
|
-
end
|
51
|
-
|
52
|
-
# @return [String] string representation of this object
|
53
|
-
def to_s
|
54
|
-
"SignedRetraction:#{target_type}:#{target_guid}"
|
33
|
+
def initialize(*)
|
34
|
+
raise "Sending SignedRetraction is not supported anymore! Use Retraction instead!"
|
55
35
|
end
|
56
36
|
|
57
|
-
private
|
58
|
-
|
59
|
-
# @param [Nokogiri::XML::Element] root_node xml nodes
|
60
37
|
# @return [Retraction] instance
|
61
|
-
|
62
|
-
|
63
|
-
entity_data[:target] = Retraction.send(:fetch_target, entity_data[:target_type], entity_data[:target_guid])
|
64
|
-
new(entity_data).to_retraction
|
65
|
-
end
|
66
|
-
|
67
|
-
# It updates also the signatures with the keys of the author and the parent
|
68
|
-
# if the signatures are not there yet and if the keys are available.
|
69
|
-
#
|
70
|
-
# @return [Hash] xml elements with updated signatures
|
71
|
-
def enriched_properties
|
72
|
-
super.tap do |hash|
|
73
|
-
hash[:target_author_signature] = target_author_signature || sign_with_author.to_s
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def sign_with_author
|
78
|
-
privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, author)
|
79
|
-
SignedRetraction.sign_with_key(privkey, self) unless privkey.nil?
|
38
|
+
def self.from_hash(hash)
|
39
|
+
Retraction.from_hash(hash)
|
80
40
|
end
|
81
41
|
end
|
82
42
|
end
|