diaspora_federation 0.1.9 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|