diaspora_federation 0.0.13 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -32
- data/lib/diaspora_federation.rb +9 -31
- data/lib/diaspora_federation/discovery/h_card.rb +8 -15
- data/lib/diaspora_federation/discovery/host_meta.rb +2 -4
- data/lib/diaspora_federation/discovery/web_finger.rb +11 -11
- data/lib/diaspora_federation/discovery/xrd_document.rb +4 -8
- data/lib/diaspora_federation/entities.rb +2 -0
- data/lib/diaspora_federation/entities/account_deletion.rb +5 -0
- data/lib/diaspora_federation/entities/comment.rb +3 -6
- data/lib/diaspora_federation/entities/contact.rb +5 -0
- data/lib/diaspora_federation/entities/conversation.rb +10 -1
- data/lib/diaspora_federation/entities/message.rb +29 -4
- data/lib/diaspora_federation/entities/participation.rb +24 -0
- data/lib/diaspora_federation/entities/poll_participation.rb +3 -6
- data/lib/diaspora_federation/entities/profile.rb +5 -0
- data/lib/diaspora_federation/entities/related_entity.rb +33 -0
- data/lib/diaspora_federation/entities/relayable.rb +55 -40
- data/lib/diaspora_federation/entities/relayable_retraction.rb +21 -12
- data/lib/diaspora_federation/entities/request.rb +6 -2
- data/lib/diaspora_federation/entities/reshare.rb +5 -0
- data/lib/diaspora_federation/entities/retraction.rb +37 -0
- data/lib/diaspora_federation/entities/signed_retraction.rb +16 -5
- data/lib/diaspora_federation/entities/status_message.rb +11 -0
- data/lib/diaspora_federation/entity.rb +73 -30
- data/lib/diaspora_federation/federation/fetcher.rb +11 -1
- data/lib/diaspora_federation/federation/receiver.rb +10 -0
- data/lib/diaspora_federation/federation/receiver/abstract_receiver.rb +18 -4
- data/lib/diaspora_federation/federation/receiver/exceptions.rb +4 -0
- data/lib/diaspora_federation/federation/receiver/public.rb +10 -0
- data/lib/diaspora_federation/federation/sender.rb +1 -1
- data/lib/diaspora_federation/http_client.rb +1 -2
- data/lib/diaspora_federation/logging.rb +6 -0
- data/lib/diaspora_federation/properties_dsl.rb +4 -2
- data/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb +2 -2
- data/lib/diaspora_federation/salmon/encrypted_slap.rb +3 -5
- data/lib/diaspora_federation/salmon/exceptions.rb +1 -1
- data/lib/diaspora_federation/salmon/magic_envelope.rb +16 -17
- data/lib/diaspora_federation/salmon/slap.rb +1 -2
- data/lib/diaspora_federation/salmon/xml_payload.rb +1 -2
- data/lib/diaspora_federation/validators.rb +2 -0
- data/lib/diaspora_federation/validators/conversation_validator.rb +2 -0
- data/lib/diaspora_federation/validators/message_validator.rb +2 -2
- data/lib/diaspora_federation/validators/participation_validator.rb +3 -2
- data/lib/diaspora_federation/validators/poll_validator.rb +1 -0
- data/lib/diaspora_federation/validators/related_entity_validator.rb +12 -0
- data/lib/diaspora_federation/validators/relayable_retraction_validator.rb +1 -1
- data/lib/diaspora_federation/validators/relayable_validator.rb +1 -0
- data/lib/diaspora_federation/validators/retraction_validator.rb +1 -1
- data/lib/diaspora_federation/validators/rules/diaspora_id.rb +8 -11
- data/lib/diaspora_federation/validators/rules/diaspora_id_count.rb +1 -1
- data/lib/diaspora_federation/validators/signed_retraction_validator.rb +1 -1
- data/lib/diaspora_federation/validators/status_message_validator.rb +2 -0
- data/lib/diaspora_federation/validators/web_finger_validator.rb +2 -2
- data/lib/diaspora_federation/version.rb +1 -1
- metadata +9 -7
@@ -58,7 +58,7 @@ module DiasporaFederation
|
|
58
58
|
# @param [Hash] data entity data
|
59
59
|
# @return [Hash] hash with resolved aliases
|
60
60
|
def resolv_aliases(data)
|
61
|
-
|
61
|
+
data.map {|name, value|
|
62
62
|
if class_prop_aliases.has_key? name
|
63
63
|
prop_name = class_prop_aliases[name]
|
64
64
|
raise InvalidData, "only use '#{name}' OR '#{prop_name}'" if data.has_key? prop_name
|
@@ -66,7 +66,7 @@ module DiasporaFederation
|
|
66
66
|
else
|
67
67
|
[name, value]
|
68
68
|
end
|
69
|
-
}
|
69
|
+
}.to_h
|
70
70
|
end
|
71
71
|
|
72
72
|
# @return [Symbol] alias for the xml-generation/parsing
|
@@ -144,7 +144,9 @@ module DiasporaFederation
|
|
144
144
|
# @param [Symbol] alias_name alias name
|
145
145
|
def define_alias(name, alias_name)
|
146
146
|
class_prop_aliases[alias_name] = name
|
147
|
+
# rubocop:disable Style/Alias
|
147
148
|
instance_eval { alias_method alias_name, name }
|
149
|
+
# rubocop:enable Style/Alias
|
148
150
|
end
|
149
151
|
|
150
152
|
# Raised, if the name is of an unexpected type
|
@@ -36,7 +36,7 @@ module DiasporaFederation
|
|
36
36
|
key = AES.generate_key_and_iv
|
37
37
|
encrypted_env = AES.encrypt(magic_env.to_xml, key[:key], key[:iv])
|
38
38
|
|
39
|
-
encoded_key =
|
39
|
+
encoded_key = key.map {|k, v| [k, Base64.strict_encode64(v)] }.to_h
|
40
40
|
encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(JSON.generate(encoded_key)))
|
41
41
|
|
42
42
|
JSON.generate(aes_key: encrypted_key, encrypted_magic_envelope: encrypted_env)
|
@@ -51,7 +51,7 @@ module DiasporaFederation
|
|
51
51
|
encrypted_json = JSON.parse(encrypted_env)
|
52
52
|
|
53
53
|
encoded_key = JSON.parse(privkey.private_decrypt(Base64.decode64(encrypted_json["aes_key"])))
|
54
|
-
key =
|
54
|
+
key = encoded_key.map {|k, v| [k, Base64.decode64(v)] }.to_h
|
55
55
|
|
56
56
|
xml = AES.decrypt(encrypted_json["encrypted_magic_envelope"], key["key"], key["iv"])
|
57
57
|
Nokogiri::XML::Document.parse(xml).root
|
@@ -140,7 +140,7 @@ module DiasporaFederation
|
|
140
140
|
# @param [String] data base64 encoded, encrypted header data
|
141
141
|
# @param [OpenSSL::PKey::RSA] privkey private key for decryption
|
142
142
|
# @return [Hash] { iv: "...", aes_key: "...", author_id: "..." }
|
143
|
-
def self.header_data(data, privkey)
|
143
|
+
private_class_method def self.header_data(data, privkey)
|
144
144
|
header_elem = decrypt_header(data, privkey)
|
145
145
|
raise InvalidHeader unless header_elem.name == "decrypted_header"
|
146
146
|
|
@@ -150,20 +150,18 @@ module DiasporaFederation
|
|
150
150
|
|
151
151
|
{iv: iv, aes_key: key, author_id: author_id}
|
152
152
|
end
|
153
|
-
private_class_method :header_data
|
154
153
|
|
155
154
|
# decrypts the xml header
|
156
155
|
# @param [String] data base64 encoded, encrypted header data
|
157
156
|
# @param [OpenSSL::PKey::RSA] privkey private key for decryption
|
158
157
|
# @return [Nokogiri::XML::Element] header xml document
|
159
|
-
def self.decrypt_header(data, privkey)
|
158
|
+
private_class_method def self.decrypt_header(data, privkey)
|
160
159
|
cipher_header = JSON.parse(Base64.decode64(data))
|
161
160
|
key = JSON.parse(privkey.private_decrypt(Base64.decode64(cipher_header["aes_key"])))
|
162
161
|
|
163
162
|
xml = AES.decrypt(cipher_header["ciphertext"], Base64.decode64(key["key"]), Base64.decode64(key["iv"]))
|
164
163
|
Nokogiri::XML::Document.parse(xml).root
|
165
164
|
end
|
166
|
-
private_class_method :decrypt_header
|
167
165
|
|
168
166
|
# encrypt the header xml with an AES cipher and encrypt the cipher params
|
169
167
|
# with the recipients public_key
|
@@ -201,7 +199,7 @@ module DiasporaFederation
|
|
201
199
|
# @param [Hash] hash { key: "...", iv: "..." }
|
202
200
|
# @return [Hash] encoded hash: { key: "...", iv: "..." }
|
203
201
|
def strict_base64_encode(hash)
|
204
|
-
|
202
|
+
hash.map {|k, v| [k, Base64.strict_encode64(v)] }.to_h
|
205
203
|
end
|
206
204
|
end
|
207
205
|
end
|
@@ -15,7 +15,7 @@ module DiasporaFederation
|
|
15
15
|
class MissingHeader < RuntimeError
|
16
16
|
end
|
17
17
|
|
18
|
-
# Raised if the decrypted header has an unexpected XML structure
|
18
|
+
# Raised, if the decrypted header has an unexpected XML structure
|
19
19
|
# @deprecated
|
20
20
|
class InvalidHeader < RuntimeError
|
21
21
|
end
|
@@ -19,8 +19,10 @@ module DiasporaFederation
|
|
19
19
|
# When parsing the XML of an incoming Magic Envelope {MagicEnvelope.unenvelop}
|
20
20
|
# is used.
|
21
21
|
#
|
22
|
-
# @see
|
22
|
+
# @see https://cdn.rawgit.com/salmon-protocol/salmon-protocol/master/draft-panzer-magicsig-01.html
|
23
23
|
class MagicEnvelope
|
24
|
+
include Logging
|
25
|
+
|
24
26
|
# encoding used for the payload data
|
25
27
|
ENCODING = "base64url".freeze
|
26
28
|
|
@@ -123,6 +125,8 @@ module DiasporaFederation
|
|
123
125
|
|
124
126
|
data = read_and_decrypt_data(magic_env, cipher_params)
|
125
127
|
|
128
|
+
logger.debug "unenvelop message from #{sender}:\n#{data}"
|
129
|
+
|
126
130
|
new(XmlPayload.unpack(Nokogiri::XML::Document.parse(data).root), sender)
|
127
131
|
end
|
128
132
|
|
@@ -131,7 +135,9 @@ module DiasporaFederation
|
|
131
135
|
# the payload data as string
|
132
136
|
# @return [String] payload data
|
133
137
|
def payload_data
|
134
|
-
@payload_data ||= XmlPayload.pack(@payload).to_xml.strip
|
138
|
+
@payload_data ||= XmlPayload.pack(@payload).to_xml.strip.tap do |data|
|
139
|
+
logger.debug "send payload:\n#{data}"
|
140
|
+
end
|
135
141
|
end
|
136
142
|
|
137
143
|
def key_id
|
@@ -159,74 +165,67 @@ module DiasporaFederation
|
|
159
165
|
end
|
160
166
|
|
161
167
|
# @param [Nokogiri::XML::Element] env magic envelope XML
|
162
|
-
def self.envelope_valid?(env)
|
168
|
+
private_class_method def self.envelope_valid?(env)
|
163
169
|
(env.instance_of?(Nokogiri::XML::Element) &&
|
164
170
|
env.name == "env" &&
|
165
171
|
!env.at_xpath("me:data").content.empty? &&
|
166
172
|
!env.at_xpath("me:sig").content.empty?)
|
167
173
|
end
|
168
|
-
private_class_method :envelope_valid?
|
169
174
|
|
170
175
|
# @param [Nokogiri::XML::Element] env magic envelope XML
|
171
176
|
# @param [String] sender diaspora-ID of the sender or nil
|
172
177
|
# @return [Boolean]
|
173
|
-
def self.signature_valid?(env, sender)
|
178
|
+
private_class_method def self.signature_valid?(env, sender)
|
174
179
|
subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content),
|
175
180
|
env.at_xpath("me:data")["type"],
|
176
181
|
env.at_xpath("me:encoding").content,
|
177
182
|
env.at_xpath("me:alg").content])
|
178
183
|
|
179
|
-
sender_key = DiasporaFederation.callbacks.trigger(:
|
184
|
+
sender_key = DiasporaFederation.callbacks.trigger(:fetch_public_key, sender)
|
180
185
|
raise SenderKeyNotFound unless sender_key
|
181
186
|
|
182
187
|
sig = Base64.urlsafe_decode64(env.at_xpath("me:sig").content)
|
183
188
|
sender_key.verify(DIGEST, sig, subject)
|
184
189
|
end
|
185
|
-
private_class_method :signature_valid?
|
186
190
|
|
187
191
|
# reads the +key_id+ from the magic envelope
|
188
192
|
# @param [Nokogiri::XML::Element] env magic envelope XML
|
189
193
|
# @return [String] diaspora-ID of the sender
|
190
|
-
def self.sender(env)
|
194
|
+
private_class_method def self.sender(env)
|
191
195
|
key_id = env.at_xpath("me:sig")["key_id"]
|
192
196
|
raise InvalidEnvelope, "no key_id" unless key_id # TODO: move to `envelope_valid?`
|
193
197
|
Base64.urlsafe_decode64(key_id)
|
194
198
|
end
|
195
|
-
private_class_method :sender
|
196
199
|
|
197
200
|
# constructs the signature subject.
|
198
201
|
# the given array should consist of the data, data_type (mimetype), encoding
|
199
202
|
# and the algorithm
|
200
203
|
# @param [Array<String>] data_arr
|
201
204
|
# @return [String] signature subject
|
202
|
-
def self.sig_subject(data_arr)
|
205
|
+
private_class_method def self.sig_subject(data_arr)
|
203
206
|
data_arr.map {|i| Base64.urlsafe_encode64(i) }.join(".")
|
204
207
|
end
|
205
|
-
private_class_method :sig_subject
|
206
208
|
|
207
209
|
# @param [Nokogiri::XML::Element] magic_env magic envelope XML
|
208
210
|
# @return [Boolean]
|
209
|
-
def self.encoding_valid?(magic_env)
|
211
|
+
private_class_method def self.encoding_valid?(magic_env)
|
210
212
|
magic_env.at_xpath("me:encoding").content == ENCODING
|
211
213
|
end
|
212
|
-
private_class_method :encoding_valid?
|
213
214
|
|
214
215
|
# @param [Nokogiri::XML::Element] magic_env magic envelope XML
|
215
216
|
# @return [Boolean]
|
216
|
-
def self.algorithm_valid?(magic_env)
|
217
|
+
private_class_method def self.algorithm_valid?(magic_env)
|
217
218
|
magic_env.at_xpath("me:alg").content == ALGORITHM
|
218
219
|
end
|
219
|
-
private_class_method :algorithm_valid?
|
220
220
|
|
221
221
|
# @param [Nokogiri::XML::Element] magic_env magic envelope XML
|
222
222
|
# @param [Hash] cipher_params hash containing the key and iv
|
223
223
|
# @return [String] data
|
224
|
-
def self.read_and_decrypt_data(magic_env, cipher_params)
|
224
|
+
private_class_method def self.read_and_decrypt_data(magic_env, cipher_params)
|
225
225
|
data = Base64.urlsafe_decode64(magic_env.at_xpath("me:data").content)
|
226
226
|
data = AES.decrypt(data, cipher_params[:key], cipher_params[:iv]) unless cipher_params.nil?
|
227
227
|
data
|
228
228
|
end
|
229
|
-
private_class_method :read_and_decrypt_data
|
230
229
|
end
|
231
230
|
end
|
232
231
|
end
|
@@ -86,12 +86,11 @@ module DiasporaFederation
|
|
86
86
|
# Parses the magic envelop from the document.
|
87
87
|
#
|
88
88
|
# @param [Nokogiri::XML::Document] doc Salmon XML Document
|
89
|
-
def self.magic_env_from_doc(doc)
|
89
|
+
private_class_method def self.magic_env_from_doc(doc)
|
90
90
|
doc.at_xpath("d:diaspora/me:env", Slap::NS).tap do |env|
|
91
91
|
raise MissingMagicEnvelope if env.nil?
|
92
92
|
end
|
93
93
|
end
|
94
|
-
private_class_method :magic_env_from_doc
|
95
94
|
end
|
96
95
|
end
|
97
96
|
end
|
@@ -51,11 +51,10 @@ module DiasporaFederation
|
|
51
51
|
end
|
52
52
|
|
53
53
|
# @param [Nokogiri::XML::Element] element
|
54
|
-
def self.xml_wrapped?(element)
|
54
|
+
private_class_method def self.xml_wrapped?(element)
|
55
55
|
(element.name == "XML" && !element.at_xpath("post").nil? &&
|
56
56
|
!element.at_xpath("post").children.empty?)
|
57
57
|
end
|
58
|
-
private_class_method :xml_wrapped?
|
59
58
|
end
|
60
59
|
end
|
61
60
|
end
|
@@ -4,8 +4,9 @@ module DiasporaFederation
|
|
4
4
|
class ParticipationValidator < Validation::Validator
|
5
5
|
include Validation
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
rule :author, %i(not_empty diaspora_id)
|
8
|
+
rule :guid, :guid
|
9
|
+
rule :parent_guid, :guid
|
9
10
|
rule :parent_type, [:not_empty, regular_expression: {regex: /\APost\z/}]
|
10
11
|
end
|
11
12
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
module Validators
|
3
|
+
# This validates a {Entities::RelatedEntity}
|
4
|
+
class RelatedEntityValidator < Validation::Validator
|
5
|
+
include Validation
|
6
|
+
|
7
|
+
rule :author, %i(not_empty diaspora_id)
|
8
|
+
rule :local, :boolean
|
9
|
+
rule :public, :boolean
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -2,23 +2,20 @@ module Validation
|
|
2
2
|
module Rule
|
3
3
|
# Diaspora ID validation rule
|
4
4
|
#
|
5
|
-
#
|
6
|
-
# which was adapted from https://github.com/emmanuel/aequitas/blob/master/lib/aequitas/rule/format/email_address.rb
|
5
|
+
# A simple rule to validate the base structure of diaspora IDs.
|
7
6
|
class DiasporaId
|
8
7
|
# The Regex for a valid diaspora ID
|
9
8
|
DIASPORA_ID = begin
|
10
9
|
letter = "a-zA-Z"
|
11
10
|
digit = "0-9"
|
11
|
+
hexadecimal = "[a-fA-F#{digit}]"
|
12
12
|
username = "[#{letter}#{digit}\\-\\_\\.]+"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dcontent = "(?:#{dtext}|#{quoted_pair})"
|
20
|
-
domain_literal = "\\[#{dcontent}+\\]"
|
21
|
-
domain = "(?:#{dot_atom}|#{domain_literal})"
|
13
|
+
hostname_part = "[#{letter}#{digit}\\-]"
|
14
|
+
hostname = "#{hostname_part}+([.]#{hostname_part}*)*"
|
15
|
+
ipv4 = "(?:[#{digit}]{1,3}\\.){3}[#{digit}]{1,3}"
|
16
|
+
ipv6 = "\\[(?:#{hexadecimal}{0,4}:){0,7}#{hexadecimal}{1,4}\\]"
|
17
|
+
ip_addr = "(?:#{ipv4}|#{ipv6})"
|
18
|
+
domain = "(?:#{hostname}|#{ip_addr})"
|
22
19
|
port = "(:[#{digit}]+)?"
|
23
20
|
addr_spec = "(#{username}\\@#{domain}#{port})?"
|
24
21
|
|
@@ -12,8 +12,8 @@ module DiasporaFederation
|
|
12
12
|
rule :alias_url, URI: %i(host path)
|
13
13
|
rule :hcard_url, [:not_nil, URI: %i(host path)]
|
14
14
|
rule :seed_url, %i(not_nil URI)
|
15
|
-
rule :profile_url,
|
16
|
-
rule :atom_url,
|
15
|
+
rule :profile_url, URI: %i(host path)
|
16
|
+
rule :atom_url, URI: %i(host path)
|
17
17
|
rule :salmon_url, URI: %i(host path)
|
18
18
|
end
|
19
19
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: diaspora_federation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Neff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '1.6'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.6.
|
22
|
+
version: 1.6.8
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: '1.6'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 1.6.
|
32
|
+
version: 1.6.8
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: faraday
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- lib/diaspora_federation/entities/poll_participation.rb
|
123
123
|
- lib/diaspora_federation/entities/post.rb
|
124
124
|
- lib/diaspora_federation/entities/profile.rb
|
125
|
+
- lib/diaspora_federation/entities/related_entity.rb
|
125
126
|
- lib/diaspora_federation/entities/relayable.rb
|
126
127
|
- lib/diaspora_federation/entities/relayable_retraction.rb
|
127
128
|
- lib/diaspora_federation/entities/request.rb
|
@@ -166,6 +167,7 @@ files:
|
|
166
167
|
- lib/diaspora_federation/validators/poll_participation_validator.rb
|
167
168
|
- lib/diaspora_federation/validators/poll_validator.rb
|
168
169
|
- lib/diaspora_federation/validators/profile_validator.rb
|
170
|
+
- lib/diaspora_federation/validators/related_entity_validator.rb
|
169
171
|
- lib/diaspora_federation/validators/relayable_retraction_validator.rb
|
170
172
|
- lib/diaspora_federation/validators/relayable_validator.rb
|
171
173
|
- lib/diaspora_federation/validators/request_validator.rb
|
@@ -186,7 +188,7 @@ files:
|
|
186
188
|
- lib/tasks/build.rake
|
187
189
|
- lib/tasks/diaspora_federation_tasks.rake
|
188
190
|
- lib/tasks/tests.rake
|
189
|
-
homepage: https://github.com/
|
191
|
+
homepage: https://github.com/diaspora/diaspora_federation
|
190
192
|
licenses:
|
191
193
|
- AGPL 3.0 - http://www.gnu.org/licenses/agpl-3.0.html
|
192
194
|
metadata: {}
|
@@ -198,7 +200,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
198
200
|
requirements:
|
199
201
|
- - "~>"
|
200
202
|
- !ruby/object:Gem::Version
|
201
|
-
version: '2.
|
203
|
+
version: '2.1'
|
202
204
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
205
|
requirements:
|
204
206
|
- - ">="
|
@@ -206,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
208
|
version: '0'
|
207
209
|
requirements: []
|
208
210
|
rubyforge_project:
|
209
|
-
rubygems_version: 2.4.
|
211
|
+
rubygems_version: 2.4.8
|
210
212
|
signing_key:
|
211
213
|
specification_version: 4
|
212
214
|
summary: diaspora* federation library
|