diaspora_federation 0.0.1 → 0.0.2
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/README.md +55 -5
- data/lib/diaspora_federation.rb +43 -79
- data/lib/diaspora_federation/callbacks.rb +62 -0
- data/lib/diaspora_federation/entity.rb +146 -0
- data/lib/diaspora_federation/logging.rb +3 -4
- data/lib/diaspora_federation/properties_dsl.rb +106 -0
- data/lib/diaspora_federation/version.rb +1 -2
- data/lib/diaspora_federation/web_finger.rb +0 -1
- data/lib/diaspora_federation/web_finger/exceptions.rb +1 -5
- data/lib/diaspora_federation/web_finger/h_card.rb +101 -127
- data/lib/diaspora_federation/web_finger/host_meta.rb +0 -7
- data/lib/diaspora_federation/web_finger/web_finger.rb +79 -110
- data/lib/diaspora_federation/web_finger/xrd_document.rb +0 -3
- data/lib/tasks/build.rake +17 -0
- metadata +20 -30
- data/Rakefile +0 -26
- data/app/controllers/diaspora_federation/application_controller.rb +0 -6
- data/app/controllers/diaspora_federation/h_card_controller.rb +0 -20
- data/app/controllers/diaspora_federation/receive_controller.rb +0 -35
- data/app/controllers/diaspora_federation/webfinger_controller.rb +0 -60
- data/config/routes.rb +0 -15
- data/lib/diaspora_federation/engine.rb +0 -15
@@ -1,12 +1,10 @@
|
|
1
1
|
module DiasporaFederation
|
2
|
-
|
3
|
-
# logging module for the diaspora federation engine
|
2
|
+
# logging module for the diaspora federation
|
4
3
|
#
|
5
4
|
# it uses the logging-gem if available
|
6
5
|
module Logging
|
7
6
|
private
|
8
7
|
|
9
|
-
##
|
10
8
|
# get the logger for this class
|
11
9
|
#
|
12
10
|
# use the logging-gem if available, else use a default logger
|
@@ -17,7 +15,8 @@ module DiasporaFederation
|
|
17
15
|
|
18
16
|
# fallback logger
|
19
17
|
@logger = Logger.new(STDOUT)
|
20
|
-
|
18
|
+
loglevel = defined?(::Rails) ? ::Rails.configuration.log_level.to_s.upcase : "INFO"
|
19
|
+
@logger.level = Logger.const_get(loglevel)
|
21
20
|
@logger
|
22
21
|
end
|
23
22
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
# Provides a simple DSL for specifying {Entity} properties during class
|
3
|
+
# definition.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# property :prop
|
7
|
+
# property :optional, default: false
|
8
|
+
# property :dynamic_default, default: -> { Time.now }
|
9
|
+
# entity :nested, NestedEntity
|
10
|
+
# entity :multiple, [OtherEntity]
|
11
|
+
module PropertiesDSL
|
12
|
+
# @return [Array<Hash>] hash of declared entity properties
|
13
|
+
def class_props
|
14
|
+
@class_props ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Define a generic (string-type) property
|
18
|
+
# @param [Symbol] name property name
|
19
|
+
# @param [Hash] opts further options
|
20
|
+
# @option opts [Object, #call] :default a default value, making the
|
21
|
+
# property optional
|
22
|
+
def property(name, opts={})
|
23
|
+
define_property name, String, opts
|
24
|
+
end
|
25
|
+
|
26
|
+
# Define a property that should contain another Entity or an array of
|
27
|
+
# other Entities
|
28
|
+
# @param [Symbol] name property name
|
29
|
+
# @param [Entity, Array<Entity>] type Entity subclass or
|
30
|
+
# Array with exactly one Entity subclass constant inside
|
31
|
+
# @param [Hash] opts further options
|
32
|
+
# @option opts [Object, #call] :default a default value, making the
|
33
|
+
# property optional
|
34
|
+
def entity(name, type, opts={})
|
35
|
+
raise InvalidType unless type_valid?(type)
|
36
|
+
|
37
|
+
define_property name, type, opts
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return array of missing required property names
|
41
|
+
# @return [Array<Symbol>] missing required property names
|
42
|
+
def missing_props(args)
|
43
|
+
class_prop_names - default_props.keys - args.keys
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return a new hash of default values, with dynamic values
|
47
|
+
# resolved on each call
|
48
|
+
# @return [Hash] default values
|
49
|
+
def default_values
|
50
|
+
default_props.each_with_object({}) { |(name, prop), hash|
|
51
|
+
hash[name] = prop.respond_to?(:call) ? prop.call : prop
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns all nested Entities
|
56
|
+
# @return [Array<Hash>] nested properties
|
57
|
+
def nested_class_props
|
58
|
+
@nested_class_props ||= class_props.select {|p| p[:type] != String }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns all property names
|
62
|
+
# @return [Array] property names
|
63
|
+
def class_prop_names
|
64
|
+
@class_prop_names ||= class_props.map {|p| p[:name] }
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def define_property(name, type, opts={})
|
70
|
+
raise InvalidName unless name_valid?(name)
|
71
|
+
|
72
|
+
class_props << {name: name, type: type}
|
73
|
+
default_props[name] = opts[:default] if opts.has_key? :default
|
74
|
+
|
75
|
+
instance_eval { attr_reader name }
|
76
|
+
end
|
77
|
+
|
78
|
+
# checks if the name is a +Symbol+ or a +String+
|
79
|
+
# @param [String, Symbol] name the name to check
|
80
|
+
# @return [Boolean]
|
81
|
+
def name_valid?(name)
|
82
|
+
name.instance_of?(Symbol) || name.instance_of?(String)
|
83
|
+
end
|
84
|
+
|
85
|
+
# checks if the type extends {Entity}
|
86
|
+
# @param [Class] type the type to check
|
87
|
+
# @return [Boolean]
|
88
|
+
def type_valid?(type)
|
89
|
+
[type].flatten.all? { |type|
|
90
|
+
type.respond_to?(:ancestors) && type.ancestors.include?(Entity)
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def default_props
|
95
|
+
@default_props ||= {}
|
96
|
+
end
|
97
|
+
|
98
|
+
# Raised, if the name is of an unexpected type
|
99
|
+
class InvalidName < RuntimeError
|
100
|
+
end
|
101
|
+
|
102
|
+
# Raised, if the type is of an unexpected type
|
103
|
+
class InvalidType < RuntimeError
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -1,18 +1,14 @@
|
|
1
1
|
module DiasporaFederation
|
2
2
|
module WebFinger
|
3
|
-
##
|
4
3
|
# Raised, if the XML structure is invalid
|
5
4
|
class InvalidDocument < RuntimeError
|
6
5
|
end
|
7
6
|
|
8
|
-
##
|
9
7
|
# Raised, if something is wrong with the webfinger data
|
10
8
|
#
|
11
9
|
# * if the +webfinger_url+ is missing or malformed in {HostMeta.from_base_url} or {HostMeta.from_xml}
|
12
|
-
# * if the +data+ given to {WebFinger.from_person} is an invalid type or doesn't contain all required entries
|
13
10
|
# * if the parsed XML from {WebFinger.from_xml} is incomplete
|
14
|
-
# * if the
|
15
|
-
# are in some way malformed, invalid or incomplete.
|
11
|
+
# * if the html passed to {HCard.from_html} in some way is malformed, invalid or incomplete.
|
16
12
|
class InvalidData < RuntimeError
|
17
13
|
end
|
18
14
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module DiasporaFederation
|
2
2
|
module WebFinger
|
3
|
-
##
|
4
3
|
# This class provides the means of generating an parsing account data to and
|
5
4
|
# from the hCard format.
|
6
5
|
# hCard is based on +RFC 2426+ (vCard) which got superseded by +RFC 6350+.
|
@@ -14,20 +13,20 @@ module DiasporaFederation
|
|
14
13
|
# correctly nested according to the hCard standard and class names are
|
15
14
|
# partially wrong. Also, apart from that, it's just ugly.
|
16
15
|
#
|
17
|
-
# @example Creating a hCard document from
|
18
|
-
# hc = HCard.
|
19
|
-
# guid:
|
20
|
-
# nickname:
|
21
|
-
# full_name:
|
22
|
-
#
|
23
|
-
# photo_large_url:
|
24
|
-
# photo_medium_url:
|
25
|
-
# photo_small_url:
|
26
|
-
#
|
27
|
-
# searchable:
|
28
|
-
# first_name:
|
29
|
-
# last_name:
|
30
|
-
#
|
16
|
+
# @example Creating a hCard document from a person hash
|
17
|
+
# hc = HCard.new(
|
18
|
+
# guid: "0123456789abcdef",
|
19
|
+
# nickname: "user",
|
20
|
+
# full_name: "User Name",
|
21
|
+
# seed_url: "https://server.example/",
|
22
|
+
# photo_large_url: "https://server.example/uploads/l.jpg",
|
23
|
+
# photo_medium_url: "https://server.example/uploads/m.jpg",
|
24
|
+
# photo_small_url: "https://server.example/uploads/s.jpg",
|
25
|
+
# serialized_public_key: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----",
|
26
|
+
# searchable: true,
|
27
|
+
# first_name: "User",
|
28
|
+
# last_name: "Name"
|
29
|
+
# )
|
31
30
|
# html_string = hc.to_html
|
32
31
|
#
|
33
32
|
# @example Create a HCard instance from an hCard document
|
@@ -40,65 +39,77 @@ module DiasporaFederation
|
|
40
39
|
# @see http://microformats.org/wiki/h-card "h-card" (draft)
|
41
40
|
# @see http://www.ietf.org/rfc/rfc2426.txt "vCard MIME Directory Profile" (obsolete)
|
42
41
|
# @see http://www.ietf.org/rfc/rfc6350.txt "vCard Format Specification"
|
43
|
-
class HCard
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
#
|
42
|
+
class HCard < Entity
|
43
|
+
# @!attribute [r] guid
|
44
|
+
# This is just the guid. When a user creates an account on a pod, the pod
|
45
|
+
# MUST assign them a guid - a random hexadecimal string of at least 8
|
46
|
+
# hexadecimal digits.
|
47
|
+
# @return [String] guid
|
48
|
+
property :guid
|
49
|
+
|
50
|
+
# @!attribute [r] nickname
|
51
|
+
# the first part of the diaspora handle
|
52
|
+
# @return [String] nickname
|
53
|
+
property :nickname
|
54
|
+
|
55
|
+
# @!attribute [r] full_name
|
56
|
+
# @return [String] display name of the user
|
57
|
+
property :full_name
|
58
|
+
|
59
|
+
# @!attribute [r] url
|
60
|
+
# @deprecated should be changed to the profile url. The pod url is in
|
61
|
+
# the WebFinger (see {WebFinger#seed_url}, will affect older Diaspora*
|
62
|
+
# installations).
|
62
63
|
#
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
attr_reader :pubkey
|
72
|
-
|
73
|
-
# @return [String] url to the big avatar (300x300)
|
74
|
-
attr_reader :photo_large_url
|
75
|
-
# @return [String] url to the medium avatar (100x100)
|
76
|
-
attr_reader :photo_medium_url
|
77
|
-
# @return [String] url to the small avatar (50x50)
|
78
|
-
attr_reader :photo_small_url
|
79
|
-
|
80
|
-
# @deprecated We decided to only use one name field, these should be removed
|
81
|
-
# in later iterations (will affect older Diaspora* installations).
|
64
|
+
# @return [String] link to the pod
|
65
|
+
property :url
|
66
|
+
|
67
|
+
# @!attribute [r] public_key
|
68
|
+
# When a user is created on the pod, the pod MUST generate a pgp keypair
|
69
|
+
# for them. This key is used for signing messages. The format is a
|
70
|
+
# DER-encoded PKCS#1 key beginning with the text
|
71
|
+
# "-----BEGIN PUBLIC KEY-----" and ending with "-----END PUBLIC KEY-----".
|
82
72
|
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
|
73
|
+
# @note the public key is new in the hcard and is optional now.
|
74
|
+
#
|
75
|
+
# @return [String] public key
|
76
|
+
property :public_key, default: nil
|
77
|
+
|
78
|
+
# @!attribute [r] photo_large_url
|
79
|
+
# @return [String] url to the big avatar (300x300)
|
80
|
+
property :photo_large_url
|
81
|
+
# @!attribute [r] photo_medium_url
|
82
|
+
# @return [String] url to the medium avatar (100x100)
|
83
|
+
property :photo_medium_url
|
84
|
+
# @!attribute [r] photo_small_url
|
85
|
+
# @return [String] url to the small avatar (50x50)
|
86
|
+
property :photo_small_url
|
87
|
+
|
88
|
+
# @!attribute [r] first_name
|
89
|
+
# @deprecated We decided to only use one name field, these should be removed
|
90
|
+
# in later iterations (will affect older Diaspora* installations).
|
91
|
+
#
|
92
|
+
# @see #full_name
|
93
|
+
# @return [String] first name
|
94
|
+
property :first_name
|
86
95
|
|
87
|
-
#
|
88
|
-
#
|
96
|
+
# @!attribute [r] last_name
|
97
|
+
# @deprecated We decided to only use one name field, these should be removed
|
98
|
+
# in later iterations (will affect older Diaspora* installations).
|
89
99
|
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
100
|
+
# @see #full_name
|
101
|
+
# @return [String] last name
|
102
|
+
property :last_name
|
103
|
+
|
104
|
+
# @!attribute [r] searchable
|
105
|
+
# @deprecated As this is a simple property, consider move to WebFinger instead
|
106
|
+
# of HCard. vCard has no comparable field for this information, but
|
107
|
+
# Webfinger may declare arbitrary properties (will affect older Diaspora*
|
108
|
+
# installations).
|
98
109
|
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
|
110
|
+
# flag if a user is searchable by name
|
111
|
+
# @return [Boolean] searchable flag
|
112
|
+
property :searchable
|
102
113
|
|
103
114
|
# CSS selectors for finding all the hCard fields
|
104
115
|
SELECTORS = {
|
@@ -128,7 +139,7 @@ module DiasporaFederation
|
|
128
139
|
add_simple_property(content, :searchable, "searchable", @searchable)
|
129
140
|
|
130
141
|
add_property(content, :key) do |html|
|
131
|
-
html.pre(@
|
142
|
+
html.pre(@public_key.to_s, class: "key")
|
132
143
|
end
|
133
144
|
|
134
145
|
# TODO: remove me! ###################
|
@@ -145,33 +156,6 @@ module DiasporaFederation
|
|
145
156
|
builder.doc.to_xhtml(indent: 2, indent_text: " ")
|
146
157
|
end
|
147
158
|
|
148
|
-
# Creates a new HCard instance from the given Hash containing profile data
|
149
|
-
# @param [Hash] data account data
|
150
|
-
# @return [HCard] HCard instance
|
151
|
-
# @raise [InvalidData] if the account data Hash is invalid or incomplete
|
152
|
-
def self.from_profile(data)
|
153
|
-
raise InvalidData unless account_data_complete?(data)
|
154
|
-
|
155
|
-
hc = allocate
|
156
|
-
hc.instance_eval {
|
157
|
-
@guid = data[:guid]
|
158
|
-
@nickname = data[:nickname]
|
159
|
-
@full_name = data[:full_name]
|
160
|
-
@url = data[:url]
|
161
|
-
@photo_large_url = data[:photo_large_url]
|
162
|
-
@photo_medium_url = data[:photo_medium_url]
|
163
|
-
@photo_small_url = data[:photo_small_url]
|
164
|
-
@pubkey = data[:pubkey]
|
165
|
-
@searchable = data[:searchable]
|
166
|
-
|
167
|
-
# TODO: remove me! ###################
|
168
|
-
@first_name = data[:first_name]
|
169
|
-
@last_name = data[:last_name]
|
170
|
-
#######################################
|
171
|
-
}
|
172
|
-
hc
|
173
|
-
end
|
174
|
-
|
175
159
|
# Creates a new HCard instance from the given HTML string.
|
176
160
|
# @param html_string [String] HTML string
|
177
161
|
# @return [HCard] HCard instance
|
@@ -179,24 +163,24 @@ module DiasporaFederation
|
|
179
163
|
def self.from_html(html_string)
|
180
164
|
doc = parse_html_and_validate(html_string)
|
181
165
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
@pubkey = content_from_doc(doc, :key) unless element_from_doc(doc, :key).nil?
|
192
|
-
@searchable = content_from_doc(doc, :searchable) == "true"
|
166
|
+
data = {
|
167
|
+
guid: content_from_doc(doc, :uid),
|
168
|
+
nickname: content_from_doc(doc, :nickname),
|
169
|
+
full_name: content_from_doc(doc, :fn),
|
170
|
+
url: element_from_doc(doc, :url)["href"],
|
171
|
+
photo_large_url: photo_from_doc(doc, :photo),
|
172
|
+
photo_medium_url: photo_from_doc(doc, :photo_medium),
|
173
|
+
photo_small_url: photo_from_doc(doc, :photo_small),
|
174
|
+
searchable: (content_from_doc(doc, :searchable) == "true"),
|
193
175
|
|
194
176
|
# TODO: change me! ###################
|
195
|
-
|
196
|
-
|
177
|
+
first_name: content_from_doc(doc, :given_name),
|
178
|
+
last_name: content_from_doc(doc, :family_name)
|
197
179
|
#######################################
|
198
180
|
}
|
199
|
-
|
181
|
+
# TODO: public key is new and can be missing
|
182
|
+
data[:public_key] = content_from_doc(doc, :key) unless element_from_doc(doc, :key).nil?
|
183
|
+
new(data)
|
200
184
|
end
|
201
185
|
|
202
186
|
private
|
@@ -271,19 +255,6 @@ module DiasporaFederation
|
|
271
255
|
end
|
272
256
|
end
|
273
257
|
|
274
|
-
# Checks the given account data Hash for correct type and completeness.
|
275
|
-
# @param [Hash] data account data
|
276
|
-
# @return [Boolean] validation result
|
277
|
-
def self.account_data_complete?(data)
|
278
|
-
data.instance_of?(Hash) &&
|
279
|
-
%i(
|
280
|
-
guid nickname full_name url
|
281
|
-
photo_large_url photo_medium_url photo_small_url
|
282
|
-
pubkey searchable first_name last_name
|
283
|
-
).all? {|k| data.key? k }
|
284
|
-
end
|
285
|
-
private_class_method :account_data_complete?
|
286
|
-
|
287
258
|
# Make sure some of the most important elements are present in the parsed
|
288
259
|
# HTML document.
|
289
260
|
# @param [LibXML::XML::Document] doc HTML document
|
@@ -303,17 +274,20 @@ module DiasporaFederation
|
|
303
274
|
end
|
304
275
|
private_class_method :parse_html_and_validate
|
305
276
|
|
306
|
-
def element_from_doc(doc, selector)
|
277
|
+
def self.element_from_doc(doc, selector)
|
307
278
|
doc.at_css(SELECTORS[selector])
|
308
279
|
end
|
280
|
+
private_class_method :element_from_doc
|
309
281
|
|
310
|
-
def content_from_doc(doc, content_selector)
|
282
|
+
def self.content_from_doc(doc, content_selector)
|
311
283
|
element_from_doc(doc, content_selector).content
|
312
284
|
end
|
285
|
+
private_class_method :content_from_doc
|
313
286
|
|
314
|
-
def photo_from_doc(doc, photo_selector)
|
287
|
+
def self.photo_from_doc(doc, photo_selector)
|
315
288
|
element_from_doc(doc, photo_selector)["src"]
|
316
289
|
end
|
290
|
+
private_class_method :photo_from_doc
|
317
291
|
end
|
318
292
|
end
|
319
293
|
end
|