diaspora_federation 0.0.3 → 0.0.4

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/lib/diaspora_federation.rb +23 -4
  4. data/lib/diaspora_federation/discovery.rb +14 -0
  5. data/lib/diaspora_federation/discovery/discovery.rb +94 -0
  6. data/lib/diaspora_federation/{web_finger → discovery}/exceptions.rb +5 -1
  7. data/lib/diaspora_federation/{web_finger → discovery}/h_card.rb +29 -25
  8. data/lib/diaspora_federation/{web_finger → discovery}/host_meta.rb +13 -13
  9. data/lib/diaspora_federation/{web_finger → discovery}/web_finger.rb +24 -28
  10. data/lib/diaspora_federation/{web_finger → discovery}/xrd_document.rb +10 -11
  11. data/lib/diaspora_federation/entities.rb +12 -0
  12. data/lib/diaspora_federation/entities/person.rb +33 -0
  13. data/lib/diaspora_federation/entities/profile.rb +54 -0
  14. data/lib/diaspora_federation/entity.rb +62 -30
  15. data/lib/diaspora_federation/fetcher.rb +42 -0
  16. data/lib/diaspora_federation/logging.rb +4 -2
  17. data/lib/diaspora_federation/properties_dsl.rb +11 -2
  18. data/lib/diaspora_federation/validators.rb +38 -0
  19. data/lib/diaspora_federation/validators/h_card_validator.rb +30 -0
  20. data/lib/diaspora_federation/validators/person_validator.rb +18 -0
  21. data/lib/diaspora_federation/validators/profile_validator.rb +33 -0
  22. data/lib/diaspora_federation/validators/rules/birthday.rb +38 -0
  23. data/lib/diaspora_federation/validators/rules/boolean.rb +38 -0
  24. data/lib/diaspora_federation/validators/rules/diaspora_id.rb +46 -0
  25. data/lib/diaspora_federation/validators/rules/guid.rb +28 -0
  26. data/lib/diaspora_federation/validators/rules/nilable_uri.rb +19 -0
  27. data/lib/diaspora_federation/validators/rules/not_nil.rb +23 -0
  28. data/lib/diaspora_federation/validators/rules/public_key.rb +33 -0
  29. data/lib/diaspora_federation/validators/rules/tag_count.rb +34 -0
  30. data/lib/diaspora_federation/validators/web_finger_validator.rb +20 -0
  31. data/lib/diaspora_federation/version.rb +1 -1
  32. metadata +82 -8
  33. data/lib/diaspora_federation/web_finger.rb +0 -13
@@ -1,5 +1,5 @@
1
1
  module DiasporaFederation
2
- module WebFinger
2
+ module Discovery
3
3
  # This class implements basic handling of XRD documents as far as it is
4
4
  # necessary in the context of the protocols used with Diaspora* federation.
5
5
  #
@@ -92,19 +92,18 @@ module DiasporaFederation
92
92
  # @raise [InvalidDocument] if the XRD is malformed
93
93
  def self.xml_data(xrd_doc)
94
94
  doc = parse_xrd_document(xrd_doc)
95
- data = {}
96
95
 
97
- exp_elem = doc.at_xpath("xrd:XRD/xrd:Expires", NS)
98
- data[:expires] = DateTime.strptime(exp_elem.content, DATETIME_FORMAT) unless exp_elem.nil?
96
+ {}.tap do |data|
97
+ exp_elem = doc.at_xpath("xrd:XRD/xrd:Expires", NS)
98
+ data[:expires] = DateTime.strptime(exp_elem.content, DATETIME_FORMAT) unless exp_elem.nil?
99
99
 
100
- subj_elem = doc.at_xpath("xrd:XRD/xrd:Subject", NS)
101
- data[:subject] = subj_elem.content unless subj_elem.nil?
100
+ subj_elem = doc.at_xpath("xrd:XRD/xrd:Subject", NS)
101
+ data[:subject] = subj_elem.content unless subj_elem.nil?
102
102
 
103
- parse_aliases_from_xml_doc(doc, data)
104
- parse_properties_from_xml_doc(doc, data)
105
- parse_links_from_xml_doc(doc, data)
106
-
107
- data
103
+ parse_aliases_from_xml_doc(doc, data)
104
+ parse_properties_from_xml_doc(doc, data)
105
+ parse_links_from_xml_doc(doc, data)
106
+ end
108
107
  end
109
108
 
110
109
  private
@@ -0,0 +1,12 @@
1
+ module DiasporaFederation
2
+ # This namespace contains all the entities used to encapsulate data that is
3
+ # passed around in the Diaspora* network as part of the federation protocol.
4
+ #
5
+ # All entities must be defined in this namespace. otherwise the XML
6
+ # de-serialization will fail.
7
+ module Entities
8
+ end
9
+ end
10
+
11
+ require "diaspora_federation/entities/profile"
12
+ require "diaspora_federation/entities/person"
@@ -0,0 +1,33 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity contains the base data of a person
4
+ #
5
+ # @see Validators::PersonValidator
6
+ class Person < Entity
7
+ # @!attribute [r] guid
8
+ # @see HCard#guid
9
+ # @return [String] guid
10
+ property :guid
11
+
12
+ # @!attribute [r] diaspora_id
13
+ # The diaspora ID of the person
14
+ # @return [String] diaspora ID
15
+ property :diaspora_id, xml_name: :diaspora_handle
16
+
17
+ # @!attribute [r] url
18
+ # @see WebFinger#seed_url
19
+ # @return [String] link to the pod
20
+ property :url
21
+
22
+ # @!attribute [r] profile
23
+ # all profile data of the person
24
+ # @return [Profile] the profile of the person
25
+ entity :profile, Entities::Profile
26
+
27
+ # @!attribute [r] exported_key
28
+ # @see HCard#public_key
29
+ # @return [String] public key
30
+ property :exported_key
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,54 @@
1
+ module DiasporaFederation
2
+ module Entities
3
+ # this entity contains all the profile data of a person
4
+ #
5
+ # @see Validators::ProfileValidator
6
+ class Profile < Entity
7
+ # @!attribute [r] diaspora_id
8
+ # The diaspora ID of the person
9
+ # @see Person#diaspora_id
10
+ # @return [String] diaspora ID
11
+ property :diaspora_id, xml_name: :diaspora_handle
12
+
13
+ # @!attribute [r] first_name
14
+ # @deprecated
15
+ # @see #full_name
16
+ # @see HCard#first_name
17
+ # @return [String] first name
18
+ property :first_name, default: nil
19
+
20
+ # @!attribute [r] last_name
21
+ # @deprecated
22
+ # @see #full_name
23
+ # @see HCard#last_name
24
+ # @return [String] last name
25
+ property :last_name, default: nil
26
+
27
+ # @!attribute [r] image_url
28
+ # @see HCard#photo_large_url
29
+ # @return [String] url to the big avatar (300x300)
30
+ property :image_url, default: nil
31
+ # @!attribute [r] image_url_medium
32
+ # @see HCard#photo_medium_url
33
+ # @return [String] url to the medium avatar (100x100)
34
+ property :image_url_medium, default: nil
35
+ # @!attribute [r] image_url_small
36
+ # @see HCard#photo_small_url
37
+ # @return [String] url to the small avatar (50x50)
38
+ property :image_url_small, default: nil
39
+
40
+ property :birthday, default: nil
41
+ property :gender, default: nil
42
+ property :bio, default: nil
43
+ property :location, default: nil
44
+
45
+ # @!attribute [r] searchable
46
+ # @see HCard#searchable
47
+ # @return [Boolean] searchable flag
48
+ property :searchable, default: true
49
+
50
+ property :nsfw, default: false
51
+ property :tag_string, default: nil
52
+ end
53
+ end
54
+ end
@@ -15,6 +15,7 @@ module DiasporaFederation
15
15
  # property :prop
16
16
  # property :optional, default: false
17
17
  # property :dynamic_default, default: -> { Time.now }
18
+ # property :another_prop, xml_name: :another_name
18
19
  # entity :nested, NestedEntity
19
20
  # entity :multiple, [OtherEntity]
20
21
  # end
@@ -37,6 +38,12 @@ module DiasporaFederation
37
38
  # Initializes the Entity with the given attribute hash and freezes the created
38
39
  # instance it returns.
39
40
  #
41
+ # After creation, the entity is validated against a Validator, if one is defined.
42
+ # The Validator needs to be in the {DiasporaFederation::Validators} namespace and
43
+ # named like "<EntityName>Validator". Only valid entities can be created.
44
+ #
45
+ # @see DiasporaFederation::Validators
46
+ #
40
47
  # @note Attributes not defined as part of the class definition ({PropertiesDSL#property},
41
48
  # {PropertiesDSL#entity}) get discarded silently.
42
49
  #
@@ -50,16 +57,18 @@ module DiasporaFederation
50
57
  end
51
58
 
52
59
  self.class.default_values.merge(data).each do |k, v|
53
- instance_variable_set("@#{k}", v) if setable?(k, v)
60
+ instance_variable_set("@#{k}", nilify(v)) if setable?(k, v)
54
61
  end
62
+
55
63
  freeze
64
+ validate
56
65
  end
57
66
 
58
67
  # Returns a Hash representing this Entity (attributes => values)
59
68
  # @return [Hash] entity data (mostly equal to the hash used for initialization).
60
69
  def to_h
61
70
  self.class.class_prop_names.each_with_object({}) do |prop, hash|
62
- hash[prop] = send(prop)
71
+ hash[prop] = public_send(prop)
63
72
  end
64
73
  end
65
74
 
@@ -76,16 +85,12 @@ module DiasporaFederation
76
85
 
77
86
  # some of this is from Rails "Inflector.demodulize" and "Inflector.undersore"
78
87
  def self.entity_name
79
- word = name.dup
80
- i = word.rindex("::")
81
- word = word[(i + 2)..-1] if i
82
-
83
- word.gsub!("::", "/")
84
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
85
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
86
- word.tr!("-", "_")
87
- word.downcase!
88
- word
88
+ name.rpartition("::").last.tap do |word|
89
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
90
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
91
+ word.tr!("-", "_")
92
+ word.downcase!
93
+ end
89
94
  end
90
95
 
91
96
  private
@@ -113,34 +118,61 @@ module DiasporaFederation
113
118
  val.all? {|v| v.instance_of?(t.first) })
114
119
  end
115
120
 
121
+ def nilify(value)
122
+ return nil if value.respond_to?(:empty?) && value.empty?
123
+ value
124
+ end
125
+
126
+ def validate
127
+ validator_name = "#{self.class.name.split('::').last}Validator"
128
+ return unless Validators.const_defined? validator_name
129
+
130
+ validator_class = Validators.const_get validator_name
131
+ validator = validator_class.new self
132
+ raise ValidationError, error_message(validator) unless validator.valid?
133
+ end
134
+
135
+ def error_message(validator)
136
+ errors = validator.errors.map do |prop, rule|
137
+ "property: #{prop}, value: #{public_send(prop).inspect}, rule: #{rule[:rule]}, with params: #{rule[:params]}"
138
+ end
139
+ "Failed validation for properties: #{errors.join(' | ')}"
140
+ end
141
+
116
142
  # Serialize the Entity into XML elements
117
143
  # @return [Nokogiri::XML::Element] root node
118
144
  def entity_xml
119
145
  doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
120
- root_element = Nokogiri::XML::Element.new(self.class.entity_name, doc)
121
-
122
- self.class.class_props.each do |prop_def|
123
- name = prop_def[:name]
124
- type = prop_def[:type]
125
- if type == String
126
- root_element << simple_node(doc, name)
127
- else
128
- # call #to_xml for each item and append to root
129
- [*send(name)].compact.each do |item|
130
- root_element << item.to_xml
131
- end
146
+ Nokogiri::XML::Element.new(self.class.entity_name, doc).tap do |root_element|
147
+ self.class.class_props.each do |prop_def|
148
+ add_property_to_xml(doc, prop_def, root_element)
132
149
  end
133
150
  end
151
+ end
134
152
 
135
- root_element
153
+ def add_property_to_xml(doc, prop_def, root_element)
154
+ property = prop_def[:name]
155
+ type = prop_def[:type]
156
+ if type == String
157
+ root_element << simple_node(doc, prop_def[:xml_name], property)
158
+ else
159
+ # call #to_xml for each item and append to root
160
+ [*public_send(property)].compact.each do |item|
161
+ root_element << item.to_xml
162
+ end
163
+ end
136
164
  end
137
165
 
138
166
  # create simple node, fill it with text and append to root
139
- def simple_node(doc, name)
140
- node = Nokogiri::XML::Element.new(name.to_s, doc)
141
- data = send(name).to_s
142
- node.content = data unless data.empty?
143
- node
167
+ def simple_node(doc, name, property)
168
+ Nokogiri::XML::Element.new(name.to_s, doc).tap do |node|
169
+ data = public_send(property).to_s
170
+ node.content = data unless data.empty?
171
+ end
172
+ end
173
+
174
+ # Raised, if entity is not valid
175
+ class ValidationError < RuntimeError
144
176
  end
145
177
  end
146
178
  end
@@ -0,0 +1,42 @@
1
+ require "faraday"
2
+ require "faraday_middleware/response/follow_redirects"
3
+ require "typhoeus/adapters/faraday"
4
+
5
+ module DiasporaFederation
6
+ # A wrapper for {https://github.com/lostisland/faraday Faraday} used for
7
+ # fetching
8
+ #
9
+ # @see Discovery::Discovery
10
+ class Fetcher
11
+ # Perform a GET request
12
+ #
13
+ # @param [String] uri the URI
14
+ # @return [Faraday::Response] the response
15
+ def self.get(uri)
16
+ connection.get(uri)
17
+ end
18
+
19
+ # gets the Faraday connection
20
+ #
21
+ # @return [Faraday::Connection] the response
22
+ def self.connection
23
+ create_default_connection unless @connection
24
+ @connection.dup
25
+ end
26
+
27
+ def self.create_default_connection
28
+ options = {
29
+ request: {timeout: 30},
30
+ ssl: {ca_file: DiasporaFederation.certificate_authorities}
31
+ }
32
+
33
+ @connection = Faraday::Connection.new(options) do |builder|
34
+ builder.use FaradayMiddleware::FollowRedirects, limit: 4
35
+ builder.adapter :typhoeus
36
+ end
37
+
38
+ @connection.headers["User-Agent"] = "DiasporaFederation/#{DiasporaFederation::VERSION}"
39
+ end
40
+ private_class_method :create_default_connection
41
+ end
42
+ end
@@ -13,10 +13,12 @@ module DiasporaFederation
13
13
  # use logging-gem if available
14
14
  return ::Logging::Logger[self] if defined?(::Logging::Logger)
15
15
 
16
+ # use rails logger if running in rails and no logging-gem is available
17
+ return ::Rails.logger if defined?(::Rails)
18
+
16
19
  # fallback logger
17
20
  @logger = Logger.new(STDOUT)
18
- loglevel = defined?(::Rails) ? ::Rails.configuration.log_level.to_s.upcase : "INFO"
19
- @logger.level = Logger.const_get(loglevel)
21
+ @logger.level = Logger::INFO
20
22
  @logger
21
23
  end
22
24
  end
@@ -6,6 +6,7 @@ module DiasporaFederation
6
6
  # property :prop
7
7
  # property :optional, default: false
8
8
  # property :dynamic_default, default: -> { Time.now }
9
+ # property :another_prop, xml_name: :another_name
9
10
  # entity :nested, NestedEntity
10
11
  # entity :multiple, [OtherEntity]
11
12
  module PropertiesDSL
@@ -19,6 +20,7 @@ module DiasporaFederation
19
20
  # @param [Hash] opts further options
20
21
  # @option opts [Object, #call] :default a default value, making the
21
22
  # property optional
23
+ # @option opts [Symbol] :xml_name another name used for xml generation
22
24
  def property(name, opts={})
23
25
  define_property name, String, opts
24
26
  end
@@ -69,7 +71,14 @@ module DiasporaFederation
69
71
  def define_property(name, type, opts={})
70
72
  raise InvalidName unless name_valid?(name)
71
73
 
72
- class_props << {name: name, type: type}
74
+ xml_name = name
75
+ if opts.has_key? :xml_name
76
+ raise ArgumentError, "xml_name is not supported for nested entities" unless type == String
77
+ xml_name = opts[:xml_name]
78
+ raise InvalidName, "invalid xml_name" unless name_valid?(xml_name)
79
+ end
80
+
81
+ class_props << {name: name, xml_name: xml_name, type: type}
73
82
  default_props[name] = opts[:default] if opts.has_key? :default
74
83
 
75
84
  instance_eval { attr_reader name }
@@ -79,7 +88,7 @@ module DiasporaFederation
79
88
  # @param [String, Symbol] name the name to check
80
89
  # @return [Boolean]
81
90
  def name_valid?(name)
82
- name.instance_of?(Symbol) || name.instance_of?(String)
91
+ name.instance_of?(Symbol)
83
92
  end
84
93
 
85
94
  # checks if the type extends {Entity}
@@ -0,0 +1,38 @@
1
+ require "validation"
2
+ require "validation/rule/regular_expression"
3
+ require "validation/rule/not_empty"
4
+ require "validation/rule/uri"
5
+
6
+ # +valid+ gem namespace
7
+ module Validation
8
+ # This module contains custom validation rules for various data field types.
9
+ # That includes types for which there are no provided rules by the +valid+ gem
10
+ # or types that are very specific to Diaspora* federation and need special handling.
11
+ # The rules are used inside the {DiasporaFederation::Validators validator classes}
12
+ # to perform basic sanity-checks on {DiasporaFederation::Entities federation entities}.
13
+ module Rule
14
+ end
15
+ end
16
+
17
+ require "diaspora_federation/validators/rules/birthday"
18
+ require "diaspora_federation/validators/rules/boolean"
19
+ require "diaspora_federation/validators/rules/diaspora_id"
20
+ require "diaspora_federation/validators/rules/guid"
21
+ require "diaspora_federation/validators/rules/nilable_uri"
22
+ require "diaspora_federation/validators/rules/not_nil"
23
+ require "diaspora_federation/validators/rules/public_key"
24
+ require "diaspora_federation/validators/rules/tag_count"
25
+
26
+ module DiasporaFederation
27
+ # Validators to perform basic sanity-checks on {DiasporaFederation::Entities federation entities}.
28
+ #
29
+ # The Validators are mapped with the entities by name. The naming schema
30
+ # is "<EntityName>Validator".
31
+ module Validators
32
+ end
33
+ end
34
+
35
+ require "diaspora_federation/validators/h_card_validator"
36
+ require "diaspora_federation/validators/person_validator"
37
+ require "diaspora_federation/validators/profile_validator"
38
+ require "diaspora_federation/validators/web_finger_validator"
@@ -0,0 +1,30 @@
1
+ module DiasporaFederation
2
+ module Validators
3
+ # This validates a {Discovery::HCard}
4
+ #
5
+ # @todo activate guid and public key validation after all pod have it in
6
+ # the hcard.
7
+ #
8
+ # @note
9
+ class HCardValidator < Validation::Validator
10
+ include Validation
11
+
12
+ # rule :guid, :guid
13
+
14
+ # the name must not contain a semicolon because of mentions
15
+ # @{<full_name> ; <diaspora_id>}
16
+ rule :full_name, regular_expression: {regex: /\A[^;]{,70}\z/}
17
+ rule :first_name, regular_expression: {regex: /\A[^;]{,32}\z/}
18
+ rule :last_name, regular_expression: {regex: /\A[^;]{,32}\z/}
19
+
20
+ # this urls can be relative
21
+ rule :photo_large_url, [:not_nil, nilableURI: [:path]]
22
+ rule :photo_medium_url, [:not_nil, nilableURI: [:path]]
23
+ rule :photo_small_url, [:not_nil, nilableURI: [:path]]
24
+
25
+ # rule :exported_key, :public_key
26
+
27
+ rule :searchable, :boolean
28
+ end
29
+ end
30
+ end