diaspora_federation 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +27 -2
  3. data/lib/diaspora_federation.rb +16 -0
  4. data/lib/diaspora_federation/discovery/discovery.rb +23 -8
  5. data/lib/diaspora_federation/discovery/exceptions.rb +6 -6
  6. data/lib/diaspora_federation/discovery/web_finger.rb +13 -1
  7. data/lib/diaspora_federation/discovery/xrd_document.rb +35 -3
  8. data/lib/diaspora_federation/entities/event.rb +1 -1
  9. data/lib/diaspora_federation/entities/photo.rb +2 -2
  10. data/lib/diaspora_federation/entities/post.rb +5 -0
  11. data/lib/diaspora_federation/entities/profile.rb +4 -1
  12. data/lib/diaspora_federation/entities/related_entity.rb +8 -0
  13. data/lib/diaspora_federation/entities/relayable.rb +3 -3
  14. data/lib/diaspora_federation/entities/reshare.rb +28 -8
  15. data/lib/diaspora_federation/entities/retraction.rb +1 -1
  16. data/lib/diaspora_federation/entities/status_message.rb +0 -5
  17. data/lib/diaspora_federation/entity.rb +4 -1
  18. data/lib/diaspora_federation/federation.rb +1 -0
  19. data/lib/diaspora_federation/federation/diaspora_url_parser.rb +34 -0
  20. data/lib/diaspora_federation/federation/fetcher.rb +23 -11
  21. data/lib/diaspora_federation/federation/receiver.rb +2 -2
  22. data/lib/diaspora_federation/federation/receiver/abstract_receiver.rb +6 -1
  23. data/lib/diaspora_federation/federation/receiver/public.rb +10 -3
  24. data/lib/diaspora_federation/parsers/base_parser.rb +1 -1
  25. data/lib/diaspora_federation/properties_dsl.rb +0 -2
  26. data/lib/diaspora_federation/validators.rb +1 -0
  27. data/lib/diaspora_federation/validators/account_deletion_validator.rb +2 -2
  28. data/lib/diaspora_federation/validators/account_migration_validator.rb +2 -2
  29. data/lib/diaspora_federation/validators/comment_validator.rb +1 -1
  30. data/lib/diaspora_federation/validators/contact_validator.rb +3 -3
  31. data/lib/diaspora_federation/validators/conversation_validator.rb +2 -2
  32. data/lib/diaspora_federation/validators/event_participation_validator.rb +1 -1
  33. data/lib/diaspora_federation/validators/event_validator.rb +2 -2
  34. data/lib/diaspora_federation/validators/h_card_validator.rb +1 -1
  35. data/lib/diaspora_federation/validators/like_validator.rb +1 -1
  36. data/lib/diaspora_federation/validators/location_validator.rb +1 -1
  37. data/lib/diaspora_federation/validators/message_validator.rb +2 -2
  38. data/lib/diaspora_federation/validators/optional_aware_validator.rb +23 -0
  39. data/lib/diaspora_federation/validators/participation_validator.rb +2 -2
  40. data/lib/diaspora_federation/validators/person_validator.rb +2 -2
  41. data/lib/diaspora_federation/validators/photo_validator.rb +3 -3
  42. data/lib/diaspora_federation/validators/poll_answer_validator.rb +1 -1
  43. data/lib/diaspora_federation/validators/poll_participation_validator.rb +1 -1
  44. data/lib/diaspora_federation/validators/poll_validator.rb +1 -1
  45. data/lib/diaspora_federation/validators/profile_validator.rb +1 -1
  46. data/lib/diaspora_federation/validators/related_entity_validator.rb +1 -1
  47. data/lib/diaspora_federation/validators/relayable_validator.rb +1 -1
  48. data/lib/diaspora_federation/validators/reshare_validator.rb +3 -5
  49. data/lib/diaspora_federation/validators/retraction_validator.rb +2 -2
  50. data/lib/diaspora_federation/validators/rules/diaspora_id.rb +18 -13
  51. data/lib/diaspora_federation/validators/rules/guid.rb +10 -15
  52. data/lib/diaspora_federation/validators/status_message_validator.rb +2 -2
  53. data/lib/diaspora_federation/validators/web_finger_validator.rb +1 -1
  54. data/lib/diaspora_federation/version.rb +1 -1
  55. metadata +7 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5dbce3e434737107507881b68c2f8d51a0cf2d78
4
- data.tar.gz: 6c974f67b87f2ffa988c1286d17b920ad49fe9ef
3
+ metadata.gz: 79f5ac9d463c824a36b9769994431ac6e66625fd
4
+ data.tar.gz: 270ed5a33de71c405bdcd584c620d3f1d97422c9
5
5
  SHA512:
6
- metadata.gz: 9ac23b66e6c06e368f7dff5a190222e123a388653c2bb9983a28b80c08a9f6831fb584e86195ac627ad16f1d73fb9f185695d5b5c469efb19be70bddb2681f91
7
- data.tar.gz: 248882b3d9a8c71a94894724719f63a04a4398eabb2f13b35fb7559cae0fdf017b0010a1940390ecab70664598fe01cf168efb2ac6f44f404fc6c82360b26c38
6
+ metadata.gz: 14ed9bc7669bf07166bebacfe43bbb0cb78821010964181f94f3f395838ba7836755ebdc913048f7455fa6ca6cf0e78e281190ec1f914591c5233a641d986040
7
+ data.tar.gz: 91b6f07218394e0e066aefd973de6ddc4fa7c0d694c4ee25e53504d15f5371036e8b765f1d911f6008464c9f86e23e66a2a9010c32e3fb4f30da8c064099668e
@@ -1,12 +1,37 @@
1
+ # 0.2.2
2
+
3
+ ## Features
4
+
5
+ * Add support for [diaspora://](https://diaspora.github.io/diaspora_federation/federation/diaspora_scheme.html) URIs and fetch linked entities (see [#75](https://github.com/diaspora/diaspora_federation/pull/75)) [#78](https://github.com/diaspora/diaspora_federation/pull/78) [#85](https://github.com/diaspora/diaspora_federation/pull/85)
6
+ * Fetch RFC 7033 WebFinger with fallback to legacy WebFinger [#74](https://github.com/diaspora/diaspora_federation/pull/74)
7
+ * Add support to receive and relay likes for comments [#81](https://github.com/diaspora/diaspora_federation/pull/81)
8
+
9
+ ## Refactor
10
+
11
+ * Always raise a DiscoveryError when something with the discovery fails [#77](https://github.com/diaspora/diaspora_federation/pull/77)
12
+ * Tighten the validation of diaspora\* IDs [#86](https://github.com/diaspora/diaspora_federation/pull/86)
13
+ * Allow to receive non-public profiles without private data [#79](https://github.com/diaspora/diaspora_federation/pull/79)
14
+ * Remove `public` and `provider_display_name` from `Reshare` entity [#84](https://github.com/diaspora/diaspora_federation/pull/84)
15
+
16
+ ## Bug fixes
17
+
18
+ * Allow reshares with no root [#73](https://github.com/diaspora/diaspora_federation/pull/73)
19
+ * Make `height` and `width` optional for photos [#76](https://github.com/diaspora/diaspora_federation/pull/76)
20
+ * Detect loops when fetching entities [#87](https://github.com/diaspora/diaspora_federation/pull/87)
21
+
22
+ ## Documentation
23
+
24
+ * Add documentation for the future of the `Reshare` entity (see [#83](https://github.com/diaspora/diaspora_federation/pull/83)) [#84](https://github.com/diaspora/diaspora_federation/pull/84)
25
+
1
26
  # 0.2.1
2
27
 
3
28
  ## Features
4
29
 
5
- Add `DiasporaFederation::Schemas` to access the JSON schema [#70](https://github.com/diaspora/diaspora_federation/pull/70)
30
+ * Add `DiasporaFederation::Schemas` to access the JSON schema [#70](https://github.com/diaspora/diaspora_federation/pull/70)
6
31
 
7
32
  ## Refactor
8
33
 
9
- Don't add optional properties to generated XML and JSON when nil [#71](https://github.com/diaspora/diaspora_federation/pull/71)
34
+ * Don't add optional properties to generated XML and JSON when nil [#71](https://github.com/diaspora/diaspora_federation/pull/71)
10
35
 
11
36
  # 0.2.0
12
37
 
@@ -39,6 +39,7 @@ module DiasporaFederation
39
39
  ]
40
40
 
41
41
  # defaults
42
+ @webfinger_http_fallback = false
42
43
  @http_concurrency = 20
43
44
  @http_timeout = 30
44
45
  @http_verbose = false
@@ -74,6 +75,21 @@ module DiasporaFederation
74
75
  # @param [String] value path to certificate authorities
75
76
  attr_accessor :certificate_authorities
76
77
 
78
+ # Configure if WebFinger discovery should fallback to http if https fails (default: +false+)
79
+ #
80
+ # This is useful for example for development environments where https isn't available.
81
+ #
82
+ # This setting only applies to the WebFinger route from RFC 7033 +/.well-known/webfinger+.
83
+ # Legacy WebFinger flow unconditionally falls back to http.
84
+ #
85
+ # @overload webfinger_http_fallback
86
+ # @return [Boolean] webfinger http fallback enabled
87
+ # @overload webfinger_http_fallback=
88
+ # @example
89
+ # config.webfinger_http_fallback = AppConfig.server.rails_environment == "development"
90
+ # @param [Boolean] value webfinger http fallback enabled
91
+ attr_accessor :webfinger_http_fallback
92
+
77
93
  # Maximum number of parallel HTTP requests made to other pods (default: +20+)
78
94
  #
79
95
  # @overload http_concurrency
@@ -15,6 +15,7 @@ module DiasporaFederation
15
15
 
16
16
  # Fetches all metadata for the account and saves it via callback
17
17
  # @return [Person]
18
+ # @raise [DiscoveryError] if something with the discovery failed
18
19
  def fetch_and_save
19
20
  logger.info "Fetch data for #{diaspora_id}"
20
21
 
@@ -23,6 +24,10 @@ module DiasporaFederation
23
24
  DiasporaFederation.callbacks.trigger(:save_person_after_webfinger, person)
24
25
  logger.info "successfully webfingered #{diaspora_id}"
25
26
  person
27
+ rescue DiscoveryError
28
+ raise # simply re-raise DiscoveryError
29
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
30
+ raise DiscoveryError, "Failed discovery for #{diaspora_id}: #{e.class}: #{e.message}"
26
31
  end
27
32
 
28
33
  private
@@ -43,7 +48,7 @@ module DiasporaFederation
43
48
  response = HttpClient.get(url)
44
49
  raise "Failed to fetch #{url}: #{response.status}" unless response.success?
45
50
  response.body
46
- rescue => e
51
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
47
52
  unless http_fallback && url.start_with?("https://")
48
53
  raise DiscoveryError, "Failed to fetch #{url} for #{diaspora_id}: #{e.class}: #{e.message}"
49
54
  end
@@ -53,23 +58,33 @@ module DiasporaFederation
53
58
  retry
54
59
  end
55
60
 
56
- def host_meta_url
57
- domain = diaspora_id.split("@")[1]
58
- "https://#{domain}/.well-known/host-meta"
61
+ def domain
62
+ @domain ||= diaspora_id.split("@")[1]
63
+ end
64
+
65
+ def acct_parameter
66
+ "acct:#{diaspora_id}"
59
67
  end
60
68
 
61
69
  def legacy_webfinger_url_from_host_meta
62
70
  # This tries the xrd url with https first, then falls back to http.
63
- host_meta = HostMeta.from_xml get(host_meta_url, true)
64
- host_meta.webfinger_template_url.gsub("{uri}", "acct:#{diaspora_id}")
71
+ host_meta = HostMeta.from_xml(get("https://#{domain}/.well-known/host-meta", true))
72
+ host_meta.webfinger_template_url.gsub("{uri}", acct_parameter)
65
73
  end
66
74
 
67
75
  def webfinger
68
- @webfinger ||= WebFinger.from_xml get(legacy_webfinger_url_from_host_meta)
76
+ return @webfinger if @webfinger
77
+ webfinger_url = "https://#{domain}/.well-known/webfinger?resource=#{acct_parameter}"
78
+
79
+ # This tries the WebFinger URL with https first, then falls back to http if webfinger_http_fallback is enabled.
80
+ @webfinger = WebFinger.from_json(get(webfinger_url, DiasporaFederation.webfinger_http_fallback))
81
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
82
+ logger.warn "WebFinger failed, retrying with legacy WebFinger for #{diaspora_id}: #{e.class}: #{e.message}"
83
+ @webfinger = WebFinger.from_xml(get(legacy_webfinger_url_from_host_meta))
69
84
  end
70
85
 
71
86
  def hcard
72
- @hcard ||= HCard.from_html get(webfinger.hcard_url)
87
+ @hcard ||= HCard.from_html(get(webfinger.hcard_url))
73
88
  end
74
89
 
75
90
  def person
@@ -1,7 +1,11 @@
1
1
  module DiasporaFederation
2
2
  module Discovery
3
+ # Raised, if there is an error while discover a new person
4
+ class DiscoveryError < RuntimeError
5
+ end
6
+
3
7
  # Raised, if the XML structure is invalid
4
- class InvalidDocument < RuntimeError
8
+ class InvalidDocument < DiscoveryError
5
9
  end
6
10
 
7
11
  # Raised, if something is wrong with the webfinger data
@@ -9,11 +13,7 @@ module DiasporaFederation
9
13
  # * if the +webfinger_url+ is missing or malformed in {HostMeta.from_base_url} or {HostMeta.from_xml}
10
14
  # * if the parsed XML from {WebFinger.from_xml} is incomplete
11
15
  # * if the html passed to {HCard.from_html} in some way is malformed, invalid or incomplete.
12
- class InvalidData < RuntimeError
13
- end
14
-
15
- # Raised, if there is an error while discover a new person
16
- class DiscoveryError < RuntimeError
16
+ class InvalidData < DiscoveryError
17
17
  end
18
18
  end
19
19
  end
@@ -119,8 +119,20 @@ module DiasporaFederation
119
119
  # @return [WebFinger] WebFinger instance
120
120
  # @raise [InvalidData] if the given XML string is invalid or incomplete
121
121
  def self.from_xml(webfinger_xml)
122
- data = parse_xml_and_validate(webfinger_xml)
122
+ from_hash(parse_xml_and_validate(webfinger_xml))
123
+ end
123
124
 
125
+ # Creates a WebFinger instance from the given JSON string
126
+ # @param [String] webfinger_json WebFinger JSON string
127
+ # @return [WebFinger] WebFinger instance
128
+ def self.from_json(webfinger_json)
129
+ from_hash(XrdDocument.json_data(webfinger_json))
130
+ end
131
+
132
+ # Creates a WebFinger instance from the given data
133
+ # @param [Hash] data WebFinger data hash
134
+ # @return [WebFinger] WebFinger instance
135
+ def self.from_hash(data)
124
136
  links = data[:links]
125
137
 
126
138
  new(
@@ -83,10 +83,10 @@ module DiasporaFederation
83
83
  def to_json
84
84
  {
85
85
  subject: subject,
86
- expires: expires,
86
+ expires: (expires.strftime(DATETIME_FORMAT) if expires.instance_of?(DateTime)),
87
87
  aliases: (aliases if aliases.any?),
88
- links: (links if links.any?),
89
- properties: (properties if properties.any?)
88
+ properties: (properties if properties.any?),
89
+ links: (links if links.any?)
90
90
  }.reject {|_, v| v.nil? }
91
91
  end
92
92
 
@@ -115,6 +115,27 @@ module DiasporaFederation
115
115
  end
116
116
  end
117
117
 
118
+ # Parse the JRD document from the given string and create a hash containing
119
+ # the extracted data with symbolized keys.
120
+ #
121
+ # @param [String] jrd_doc JSON string
122
+ # @return [Hash] extracted data
123
+ # @raise [InvalidDocument] if the JRD is malformed
124
+ def self.json_data(jrd_doc)
125
+ json_hash = JSON.parse(jrd_doc)
126
+
127
+ {
128
+ subject: json_hash["subject"],
129
+ expires: (DateTime.strptime(json_hash["expires"], DATETIME_FORMAT) if json_hash.key?("expires")),
130
+ aliases: json_hash["aliases"],
131
+ properties: json_hash["properties"],
132
+ links: symbolize_keys_for_links(json_hash["links"])
133
+ }.reject {|_, v| v.nil? }
134
+ rescue JSON::JSONError => e
135
+ raise InvalidDocument,
136
+ "Not a JRD document: #{e.class}: #{e.message[0..255].encode(Encoding.default_external, undef: :replace)}"
137
+ end
138
+
118
139
  private
119
140
 
120
141
  attr_reader :expires
@@ -180,6 +201,17 @@ module DiasporaFederation
180
201
  end
181
202
  data[:links] = links unless links.empty?
182
203
  end
204
+
205
+ # symbolize link keys from JSON hash, but only convert known keys
206
+ private_class_method def self.symbolize_keys_for_links(links)
207
+ links.map do |link|
208
+ {}.tap do |hash|
209
+ LINK_ATTRS.each do |attr|
210
+ hash[attr] = link[attr.to_s] if link.key?(attr.to_s)
211
+ end
212
+ end
213
+ end
214
+ end
183
215
  end
184
216
  end
185
217
  end
@@ -24,7 +24,7 @@ module DiasporaFederation
24
24
  # @!attribute [r] description
25
25
  # Description of the event
26
26
  # @return [String] event description
27
- property :description, :string, optional: true
27
+ property :description, :string, alias: :text, optional: true
28
28
 
29
29
  # @!attribute [r] start
30
30
  # The start time of the event
@@ -48,12 +48,12 @@ module DiasporaFederation
48
48
  # @!attribute [r] height
49
49
  # Photo height
50
50
  # @return [Integer] height
51
- property :height, :integer
51
+ property :height, :integer, optional: true
52
52
 
53
53
  # @!attribute [r] width
54
54
  # Photo width
55
55
  # @return [Integer] width
56
- property :width, :integer
56
+ property :width, :integer, optional: true
57
57
  end
58
58
  end
59
59
  end
@@ -19,6 +19,10 @@ module DiasporaFederation
19
19
  # Post entity creation time
20
20
  # @return [Time] creation time
21
21
  #
22
+ # @!attribute [r] public
23
+ # Shows whether the post is visible to everyone or only to some aspects
24
+ # @return [Boolean] is it public
25
+ #
22
26
  # @!attribute [r] provider_display_name
23
27
  # A string that describes a means by which a user has posted the post
24
28
  # @return [String] provider display name
@@ -29,6 +33,7 @@ module DiasporaFederation
29
33
  property :author, :string, xml_name: :diaspora_handle
30
34
  property :guid, :string
31
35
  property :created_at, :timestamp, default: -> { Time.now.utc }
36
+ property :public, :boolean, default: false
32
37
  property :provider_display_name, :string, optional: true
33
38
  end
34
39
  end
@@ -43,9 +43,12 @@ module DiasporaFederation
43
43
  # @return [String] url to the small avatar (50x50)
44
44
  property :image_url_small, :string, optional: true
45
45
 
46
+ # @!attribute [r] bio
47
+ # @return [String] bio of the person
48
+ property :bio, :string, alias: :text, optional: true
49
+
46
50
  property :birthday, :string, optional: true
47
51
  property :gender, :string, optional: true
48
- property :bio, :string, optional: true
49
52
  property :location, :string, optional: true
50
53
 
51
54
  # @!attribute [r] searchable
@@ -24,6 +24,14 @@ module DiasporaFederation
24
24
  # @return [RelatedEntity] parent entity
25
25
  entity :parent, Entities::RelatedEntity, default: nil
26
26
 
27
+ # The root entity, this entity is responsible for relaying relayables
28
+ # @return [RelatedEntity] absolute parent entity
29
+ def root
30
+ root = self
31
+ root = root.parent until root.parent.nil?
32
+ root
33
+ end
34
+
27
35
  # Get related entity from the backend or fetch it from remote if not available locally
28
36
  # @return [RelatedEntity] fetched related entity
29
37
  def self.fetch(author, type, guid)
@@ -76,11 +76,11 @@ module DiasporaFederation
76
76
  # @raise [SignatureVerificationFailed] if the signature is not valid
77
77
  # @raise [PublicKeyNotFound] if no public key is found
78
78
  def verify_signature
79
- super(author, :author_signature) unless author == parent.author
79
+ super(author, :author_signature) unless author == parent.root.author
80
80
  end
81
81
 
82
82
  def sender_valid?(sender)
83
- (sender == author && parent.local) || sender == parent.author
83
+ (sender == author && parent.root.local) || sender == parent.root.author
84
84
  end
85
85
 
86
86
  # @return [String] string representation of this object
@@ -121,7 +121,7 @@ module DiasporaFederation
121
121
  # Sign with parent author key, if the parent author is local (if the private key is found)
122
122
  # @return [String] A Base64 encoded signature of #signature_data with key
123
123
  def sign_with_parent_author_if_available
124
- privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, parent.author)
124
+ privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, parent.root.author)
125
125
  return unless privkey
126
126
 
127
127
  sign_with_key(privkey).tap do
@@ -4,24 +4,34 @@ module DiasporaFederation
4
4
  #
5
5
  # @see Validators::ReshareValidator
6
6
  class Reshare < Entity
7
- include Post
7
+ # @!attribute [r] author
8
+ # The diaspora* ID of the person who reshares the post
9
+ # @see Person#author
10
+ # @return [String] diaspora* ID
11
+ property :author, :string, xml_name: :diaspora_handle
12
+
13
+ # @!attribute [r] guid
14
+ # A random string of at least 16 chars
15
+ # @see Validation::Rule::Guid
16
+ # @return [String] status message guid
17
+ property :guid, :string
18
+
19
+ # @!attribute [r] created_at
20
+ # Post entity creation time
21
+ # @return [Time] creation time
22
+ property :created_at, :timestamp, default: -> { Time.now.utc }
8
23
 
9
24
  # @!attribute [r] root_author
10
25
  # The diaspora* ID of the person who posted the original post
11
26
  # @see Person#author
12
27
  # @return [String] diaspora* ID
13
- property :root_author, :string, xml_name: :root_diaspora_id
28
+ property :root_author, :string, optional: true, xml_name: :root_diaspora_id
14
29
 
15
30
  # @!attribute [r] root_guid
16
31
  # Guid of the original post
17
32
  # @see StatusMessage#guid
18
33
  # @return [String] root guid
19
- property :root_guid, :string
20
-
21
- # @!attribute [r] public
22
- # Has no meaning at the moment
23
- # @return [Boolean] public
24
- property :public, :boolean, optional: true, default: true # always true? (we only reshare public posts)
34
+ property :root_guid, :string, optional: true
25
35
 
26
36
  # @return [String] string representation of this object
27
37
  def to_s
@@ -30,7 +40,17 @@ module DiasporaFederation
30
40
 
31
41
  # Fetch and receive root post from remote, if not available locally
32
42
  # and validates if it's from the correct author
43
+ # TODO: after reshares are only used to increase the reach of a post (and
44
+ # legacy reshares with own interactions are migrated to the new form),
45
+ # root_author and root_guid aren't allowed to be empty anymore, so a
46
+ # not_nil check should be added to the validator and the first few lines
47
+ # here can be removed.
33
48
  def validate_root
49
+ return if root_author.nil? && root_guid.nil?
50
+
51
+ raise Entity::ValidationError, "#{self}: root_guid can't be nil if root_author is present" if root_guid.nil?
52
+ raise Entity::ValidationError, "#{self}: root_author can't be nil if root_guid is present" if root_author.nil?
53
+
34
54
  root = RelatedEntity.fetch(root_author, "Post", root_guid)
35
55
 
36
56
  return if root_author == root.author
@@ -28,7 +28,7 @@ module DiasporaFederation
28
28
  def sender_valid?(sender)
29
29
  case target_type
30
30
  when "Comment", "Like", "PollParticipation"
31
- sender == target.author || sender == target.parent.author
31
+ sender == target.author || sender == target.root.author
32
32
  else
33
33
  sender == target.author
34
34
  end
@@ -31,11 +31,6 @@ module DiasporaFederation
31
31
  # @return [Entities::Event] event
32
32
  entity :event, Entities::Event, optional: true
33
33
 
34
- # @!attribute [r] public
35
- # Shows whether the status message is visible to everyone or only to some aspects
36
- # @return [Boolean] is it public
37
- property :public, :boolean, default: false
38
-
39
34
  private
40
35
 
41
36
  def validate
@@ -40,6 +40,9 @@ module DiasporaFederation
40
40
  # @see https://www.w3.org/TR/REC-xml/#charsets "Extensible Markup Language (XML) 1.0"
41
41
  INVALID_XML_REGEX = /[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/
42
42
 
43
+ # Regex to validate and find entity names
44
+ ENTITY_NAME_REGEX = "[a-z]*(?:_[a-z]*)*".freeze
45
+
43
46
  # Initializes the Entity with the given attribute hash and freezes the created
44
47
  # instance it returns.
45
48
  #
@@ -145,7 +148,7 @@ module DiasporaFederation
145
148
  # @param [String] entity_name "snake_case" class name
146
149
  # @return [Class] entity class
147
150
  def self.entity_class(entity_name)
148
- raise InvalidEntityName, "'#{entity_name}' is invalid" unless entity_name =~ /\A[a-z]*(_[a-z]*)*\z/
151
+ raise InvalidEntityName, "'#{entity_name}' is invalid" unless entity_name =~ /\A#{ENTITY_NAME_REGEX}\z/
149
152
  class_name = entity_name.sub(/\A[a-z]/, &:upcase)
150
153
  class_name.gsub!(/_([a-z])/) { Regexp.last_match[1].upcase }
151
154
 
@@ -4,6 +4,7 @@ module DiasporaFederation
4
4
  end
5
5
  end
6
6
 
7
+ require "diaspora_federation/federation/diaspora_url_parser"
7
8
  require "diaspora_federation/federation/fetcher"
8
9
  require "diaspora_federation/federation/receiver"
9
10
  require "diaspora_federation/federation/sender"
@@ -0,0 +1,34 @@
1
+ module DiasporaFederation
2
+ module Federation
3
+ # This module is for parsing and fetching linked entities.
4
+ module DiasporaUrlParser
5
+ include Logging
6
+
7
+ # Regex to find diaspora:// URLs
8
+ DIASPORA_URL_REGEX = %r{
9
+ diaspora://
10
+ (#{Validation::Rule::DiasporaId::DIASPORA_ID_REGEX})/
11
+ (#{Entity::ENTITY_NAME_REGEX})/
12
+ (#{Validation::Rule::Guid::VALID_CHARS})
13
+ }ux
14
+
15
+ # Parses all diaspora:// URLs from the text and fetches the entities from
16
+ # the remote server if needed.
17
+ # @param [String] sender the diaspora* ID of the sender of the entity
18
+ # @param [String] text text with diaspora:// URLs to fetch
19
+ def self.fetch_linked_entities(text)
20
+ text.scan(DIASPORA_URL_REGEX).each do |author, type, guid|
21
+ fetch_entity(author, type, guid)
22
+ end
23
+ end
24
+
25
+ private_class_method def self.fetch_entity(author, type, guid)
26
+ class_name = Entity.entity_class(type).to_s.rpartition("::").last
27
+ return if DiasporaFederation.callbacks.trigger(:fetch_related_entity, class_name, guid)
28
+ Fetcher.fetch_public(author, type, guid)
29
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
30
+ logger.error "Failed to fetch linked entity #{type}:#{guid}: #{e.class}: #{e.message}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -6,28 +6,40 @@ module DiasporaFederation
6
6
  # @param [String] author the diaspora* ID of the author of the entity
7
7
  # @param [Symbol, String] entity_type snake_case version of the entity class
8
8
  # @param [String] guid guid of the entity to fetch
9
+ # @raise [NotFetchable] if something with the fetching failed
9
10
  def self.fetch_public(author, entity_type, guid)
10
- url = DiasporaFederation.callbacks.trigger(
11
- :fetch_person_url_to, author, "/fetch/#{entity_name(entity_type)}/#{guid}"
12
- )
13
- response = HttpClient.get(url)
14
- raise "Failed to fetch #{url}: #{response.status}" unless response.success?
15
-
16
- magic_env_xml = Nokogiri::XML(response.body).root
17
- magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml)
18
- Receiver::Public.new(magic_env).receive
19
- rescue => e
11
+ type = entity_name(entity_type).to_s
12
+ raise "Already fetching ..." if fetching[type].include?(guid)
13
+ fetch_from_url(author, type, guid)
14
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
20
15
  raise NotFetchable, "Failed to fetch #{entity_type}:#{guid} from #{author}: #{e.class}: #{e.message}"
21
16
  end
22
17
 
23
18
  private_class_method def self.entity_name(class_name)
24
- return class_name if class_name =~ /\A[a-z]*(_[a-z]*)*\z/
19
+ return class_name if class_name =~ /\A#{Entity::ENTITY_NAME_REGEX}\z/
25
20
 
26
21
  raise DiasporaFederation::Entity::UnknownEntity, class_name unless Entities.const_defined?(class_name)
27
22
 
28
23
  class_name.gsub(/(.)([A-Z])/, '\1_\2').downcase
29
24
  end
30
25
 
26
+ private_class_method def self.fetch_from_url(author, type, guid)
27
+ fetching[type] << guid
28
+
29
+ url = DiasporaFederation.callbacks.trigger(:fetch_person_url_to, author, "/fetch/#{type}/#{guid}")
30
+ response = HttpClient.get(url)
31
+ raise "Failed to fetch #{url}: #{response.status}" unless response.success?
32
+
33
+ Receiver.receive_public(response.body)
34
+ ensure
35
+ fetching[type].delete(guid)
36
+ end
37
+
38
+ # currently fetching entities in the same thread
39
+ private_class_method def self.fetching
40
+ Thread.current[:fetching_entities] ||= Hash.new {|h, k| h[k] = [] }
41
+ end
42
+
31
43
  # Raised, if the entity is not fetchable
32
44
  class NotFetchable < RuntimeError
33
45
  end
@@ -15,7 +15,7 @@ module DiasporaFederation
15
15
  Salmon::MagicEnvelope.unenvelop(magic_env_xml)
16
16
  end
17
17
  Public.new(magic_env).receive
18
- rescue => e
18
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
19
19
  logger.error "failed to receive public message: #{e.class}: #{e.message}"
20
20
  logger.debug "received data:\n#{data}"
21
21
  raise e
@@ -36,7 +36,7 @@ module DiasporaFederation
36
36
  Salmon::MagicEnvelope.unenvelop(magic_env_xml)
37
37
  end
38
38
  Private.new(magic_env, recipient_id).receive
39
- rescue => e
39
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
40
40
  logger.error "failed to receive private message for #{recipient_id}: #{e.class}: #{e.message}"
41
41
  logger.debug "received data:\n#{data}"
42
42
  raise e
@@ -17,7 +17,7 @@ module DiasporaFederation
17
17
  # Validates and receives the entity
18
18
  def receive
19
19
  validate_and_receive
20
- rescue => e
20
+ rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
21
21
  logger.error "failed to receive #{entity}"
22
22
  raise e
23
23
  end
@@ -28,6 +28,7 @@ module DiasporaFederation
28
28
 
29
29
  def validate_and_receive
30
30
  validate
31
+ fetch_linked_entities_from_text
31
32
  DiasporaFederation.callbacks.trigger(:receive_entity, entity, sender, recipient_id)
32
33
  logger.info "successfully received #{entity} from person #{sender}#{" for #{recipient_id}" if recipient_id}"
33
34
  end
@@ -43,6 +44,10 @@ module DiasporaFederation
43
44
  sender == entity.author
44
45
  end
45
46
  end
47
+
48
+ def fetch_linked_entities_from_text
49
+ DiasporaUrlParser.fetch_linked_entities(entity.text) if entity.respond_to?(:text) && entity.text
50
+ end
46
51
  end
47
52
  end
48
53
  end
@@ -7,11 +7,18 @@ module DiasporaFederation
7
7
 
8
8
  def validate
9
9
  super
10
- raise NotPublic if entity_can_be_public_but_it_is_not?
10
+ validate_public_flag
11
11
  end
12
12
 
13
- def entity_can_be_public_but_it_is_not?
14
- entity.respond_to?(:public) && !entity.public
13
+ def validate_public_flag
14
+ return if !entity.respond_to?(:public) || entity.public
15
+
16
+ if entity.is_a?(Entities::Profile) &&
17
+ %i[bio birthday gender location].all? {|prop| entity.public_send(prop).nil? }
18
+ return
19
+ end
20
+
21
+ raise NotPublic, "received entity #{entity} should be public!"
15
22
  end
16
23
  end
17
24
  end
@@ -29,7 +29,7 @@ module DiasporaFederation
29
29
  when :timestamp
30
30
  begin
31
31
  Time.parse(text).utc
32
- rescue
32
+ rescue ArgumentError
33
33
  nil
34
34
  end
35
35
  when :integer
@@ -158,9 +158,7 @@ module DiasporaFederation
158
158
  # @param [Symbol] alias_name alias name
159
159
  def define_alias(name, alias_name)
160
160
  class_prop_aliases[alias_name] = name
161
- # rubocop:disable Style/Alias
162
161
  instance_eval { alias_method alias_name, name }
163
- # rubocop:enable Style/Alias
164
162
  end
165
163
 
166
164
  # Raised, if the name is of an unexpected type
@@ -37,6 +37,7 @@ end
37
37
  require "diaspora_federation/validators/related_entity_validator"
38
38
 
39
39
  # abstract types
40
+ require "diaspora_federation/validators/optional_aware_validator"
40
41
  require "diaspora_federation/validators/relayable_validator"
41
42
 
42
43
  # types
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::AccountDeletion}.
4
- class AccountDeletionValidator < Validation::Validator
4
+ class AccountDeletionValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
  end
9
9
  end
10
10
  end
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::AccountMigration}.
4
- class AccountMigrationValidator < Validation::Validator
4
+ class AccountMigrationValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
 
9
9
  rule :profile, :not_nil
10
10
  end
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Comment}.
4
- class CommentValidator < Validation::Validator
4
+ class CommentValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  include RelayableValidator
@@ -1,11 +1,11 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Contact}.
4
- class ContactValidator < Validation::Validator
4
+ class ContactValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
8
- rule :recipient, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
+ rule :recipient, :diaspora_id
9
9
  rule :following, :boolean
10
10
  rule :sharing, :boolean
11
11
  end
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Conversation}.
4
- class ConversationValidator < Validation::Validator
4
+ class ConversationValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
  rule :guid, :guid
9
9
 
10
10
  rule :subject, [:not_empty, length: {maximum: 255}]
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::EventParticipation}.
4
- class EventParticipationValidator < Validation::Validator
4
+ class EventParticipationValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  include RelayableValidator
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Event}.
4
- class EventValidator < Validation::Validator
4
+ class EventValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
 
9
9
  rule :guid, :guid
10
10
 
@@ -3,7 +3,7 @@ module DiasporaFederation
3
3
  # This validates a {Discovery::HCard}.
4
4
  #
5
5
  # @note
6
- class HCardValidator < Validation::Validator
6
+ class HCardValidator < OptionalAwareValidator
7
7
  include Validation
8
8
 
9
9
  rule :guid, :guid
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Like}.
4
- class LikeValidator < Validation::Validator
4
+ class LikeValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  include RelayableValidator
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Location}.
4
- class LocationValidator < Validation::Validator
4
+ class LocationValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  rule :lat, :not_empty
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Message}.
4
- class MessageValidator < Validation::Validator
4
+ class MessageValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
  rule :guid, :guid
9
9
  rule :conversation_guid, :guid
10
10
 
@@ -0,0 +1,23 @@
1
+ module DiasporaFederation
2
+ module Validators
3
+ # Abstract validator which only validates optional fields when they are not nil.
4
+ class OptionalAwareValidator < Validation::Validator
5
+ def rules
6
+ super.reject do |field, rules|
7
+ @obj.public_send(field).nil? &&
8
+ !rules.map(&:class).include?(Validation::Rule::NotNil) &&
9
+ optional_props.include?(field)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def optional_props
16
+ entity_name = self.class.name.split("::").last.sub("Validator", "")
17
+ return [] unless Entities.const_defined?(entity_name)
18
+
19
+ Entities.const_get(entity_name).optional_props
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Participation}.
4
- class ParticipationValidator < Validation::Validator
4
+ class ParticipationValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
  rule :guid, :guid
9
9
  rule :parent_guid, :guid
10
10
  rule :parent_type, [:not_empty, regular_expression: {regex: /\APost\z/}]
@@ -1,12 +1,12 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Person}.
4
- class PersonValidator < Validation::Validator
4
+ class PersonValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  rule :guid, :guid
8
8
 
9
- rule :author, %i[not_empty diaspora_id]
9
+ rule :author, :diaspora_id
10
10
 
11
11
  rule :url, %i[not_nil URI]
12
12
 
@@ -1,12 +1,12 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Photo}.
4
- class PhotoValidator < Validation::Validator
4
+ class PhotoValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  rule :guid, :guid
8
8
 
9
- rule :author, %i[not_empty diaspora_id]
9
+ rule :author, :diaspora_id
10
10
 
11
11
  rule :public, :boolean
12
12
 
@@ -14,7 +14,7 @@ module DiasporaFederation
14
14
 
15
15
  rule :remote_photo_name, :not_empty
16
16
 
17
- rule :status_message_guid, guid: {nilable: true}
17
+ rule :status_message_guid, :guid
18
18
 
19
19
  rule :text, length: {maximum: 65_535}
20
20
 
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::PollAnswer}.
4
- class PollAnswerValidator < Validation::Validator
4
+ class PollAnswerValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  rule :guid, :guid
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::PollParticipation}.
4
- class PollParticipationValidator < Validation::Validator
4
+ class PollParticipationValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  include RelayableValidator
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Poll}.
4
- class PollValidator < Validation::Validator
4
+ class PollValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  rule :guid, :guid
@@ -1,7 +1,7 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Profile}.
4
- class ProfileValidator < Validation::Validator
4
+ class ProfileValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
7
  rule :author, :diaspora_id
@@ -4,7 +4,7 @@ module DiasporaFederation
4
4
  class RelatedEntityValidator < Validation::Validator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
  rule :local, :boolean
9
9
  rule :public, :boolean
10
10
  end
@@ -6,7 +6,7 @@ module DiasporaFederation
6
6
  # @param [Validation::Validator] validator the validator in which it is included
7
7
  def self.included(validator)
8
8
  validator.class_eval do
9
- rule :author, %i[not_empty diaspora_id]
9
+ rule :author, :diaspora_id
10
10
  rule :guid, :guid
11
11
  rule :parent_guid, :guid
12
12
  rule :parent, :not_nil
@@ -1,18 +1,16 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Reshare}.
4
- class ReshareValidator < Validation::Validator
4
+ class ReshareValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :root_author, %i[not_empty diaspora_id]
7
+ rule :root_author, :diaspora_id
8
8
 
9
9
  rule :root_guid, :guid
10
10
 
11
- rule :author, %i[not_empty diaspora_id]
11
+ rule :author, :diaspora_id
12
12
 
13
13
  rule :guid, :guid
14
-
15
- rule :public, :boolean
16
14
  end
17
15
  end
18
16
  end
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::Retraction}.
4
- class RetractionValidator < Validation::Validator
4
+ class RetractionValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
 
9
9
  rule :target_guid, :guid
10
10
  rule :target_type, :not_empty
@@ -4,24 +4,26 @@ module Validation
4
4
  #
5
5
  # A simple rule to validate the base structure of diaspora* IDs.
6
6
  class DiasporaId
7
+ # Maximum length of a full diaspora* ID
8
+ DIASPORA_ID_MAX_LENGTH = 255
9
+
7
10
  # The Regex for a valid diaspora* ID
8
- DIASPORA_ID = begin
9
- letter = "a-zA-Z"
10
- digit = "0-9"
11
- hexadecimal = "[a-fA-F#{digit}]"
12
- username = "[#{letter}#{digit}\\-\\_\\.]+"
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}\\]"
11
+ DIASPORA_ID_REGEX = begin
12
+ username = "[[:lower:]\\d\\-\\.\\_]+"
13
+ hostname_part = "[[:lower:]\\d\\-]"
14
+ hostname = "#{hostname_part}+(?:[.]#{hostname_part}*)*"
15
+ ipv4 = "(?:[\\d]{1,3}\\.){3}[\\d]{1,3}"
16
+ ipv6 = "\\[(?:[[:xdigit:]]{0,4}:){0,7}[[:xdigit:]]{1,4}\\]"
17
17
  ip_addr = "(?:#{ipv4}|#{ipv6})"
18
18
  domain = "(?:#{hostname}|#{ip_addr})"
19
- port = "(:[#{digit}]+)?"
20
- addr_spec = "(#{username}\\@#{domain}#{port})?"
19
+ port = "(?::[\\d]+)?"
21
20
 
22
- /\A#{addr_spec}\z/u
21
+ "#{username}\\@#{domain}#{port}"
23
22
  end
24
23
 
24
+ # The Regex for validating a full diaspora* ID
25
+ DIASPORA_ID = /\A#{DIASPORA_ID_REGEX}\z/u
26
+
25
27
  # The error key for this rule
26
28
  # @return [Symbol] error key
27
29
  def error_key
@@ -30,7 +32,10 @@ module Validation
30
32
 
31
33
  # Determines if value is a valid diaspora* ID
32
34
  def valid_value?(value)
33
- value.nil? || !DIASPORA_ID.match(value).nil?
35
+ return false unless value.is_a?(String)
36
+ return false if value.length > DIASPORA_ID_MAX_LENGTH
37
+
38
+ value =~ DIASPORA_ID
34
39
  end
35
40
 
36
41
  # This rule has no params.
@@ -6,21 +6,10 @@ module Validation
6
6
  # * Letters: a-z
7
7
  # * Numbers: 0-9
8
8
  # * Special chars: '-', '_', '@', '.' and ':'
9
+ # Special chars aren't allowed at the end.
9
10
  class Guid
10
- # This rule can have a +nilable+ param.
11
- # @return [Hash] params
12
- attr_reader :params
13
-
14
- # Creates a new rule for guid validation
15
- # @param [Hash] params
16
- # @option params [Boolean] :nilable guid allowed to be nil
17
- def initialize(params={})
18
- if params.include?(:nilable) && !params[:nilable].is_a?(TrueClass) && !params[:nilable].is_a?(FalseClass)
19
- raise ArgumentError, ":nilable needs to be a boolean"
20
- end
21
-
22
- @params = params
23
- end
11
+ # Allowed chars to validate a GUID with a regex
12
+ VALID_CHARS = "[0-9A-Za-z\\-_@.:]{15,254}[0-9A-Za-z]".freeze
24
13
 
25
14
  # The error key for this rule
26
15
  # @return [Symbol] error key
@@ -30,7 +19,13 @@ module Validation
30
19
 
31
20
  # Determines if value is a valid +GUID+
32
21
  def valid_value?(value)
33
- params[:nilable] && value.nil? || value.is_a?(String) && value.downcase =~ /\A[0-9a-z\-_@.:]{16,255}\z/
22
+ value.is_a?(String) && value =~ /\A#{VALID_CHARS}\z/
23
+ end
24
+
25
+ # This rule has no params.
26
+ # @return [Hash] params
27
+ def params
28
+ {}
34
29
  end
35
30
  end
36
31
  end
@@ -1,10 +1,10 @@
1
1
  module DiasporaFederation
2
2
  module Validators
3
3
  # This validates a {Entities::StatusMessage}.
4
- class StatusMessageValidator < Validation::Validator
4
+ class StatusMessageValidator < OptionalAwareValidator
5
5
  include Validation
6
6
 
7
- rule :author, %i[not_empty diaspora_id]
7
+ rule :author, :diaspora_id
8
8
 
9
9
  rule :guid, :guid
10
10
 
@@ -4,7 +4,7 @@ module DiasporaFederation
4
4
  #
5
5
  # @note It does not validate the guid and public key, because it will be
6
6
  # removed in the webfinger.
7
- class WebFingerValidator < Validation::Validator
7
+ class WebFingerValidator < OptionalAwareValidator
8
8
  include Validation
9
9
 
10
10
  rule :acct_uri, :not_empty
@@ -1,4 +1,4 @@
1
1
  module DiasporaFederation
2
2
  # the gem version
3
- VERSION = "0.2.1".freeze
3
+ VERSION = "0.2.2".freeze
4
4
  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.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Neff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-07 00:00:00.000000000 Z
11
+ date: 2017-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: 0.9.0
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: 0.13.0
42
+ version: 0.14.0
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,7 +49,7 @@ dependencies:
49
49
  version: 0.9.0
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: 0.13.0
52
+ version: 0.14.0
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: faraday_middleware
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -149,6 +149,7 @@ files:
149
149
  - lib/diaspora_federation/entities/status_message.rb
150
150
  - lib/diaspora_federation/entity.rb
151
151
  - lib/diaspora_federation/federation.rb
152
+ - lib/diaspora_federation/federation/diaspora_url_parser.rb
152
153
  - lib/diaspora_federation/federation/fetcher.rb
153
154
  - lib/diaspora_federation/federation/receiver.rb
154
155
  - lib/diaspora_federation/federation/receiver/abstract_receiver.rb
@@ -186,6 +187,7 @@ files:
186
187
  - lib/diaspora_federation/validators/like_validator.rb
187
188
  - lib/diaspora_federation/validators/location_validator.rb
188
189
  - lib/diaspora_federation/validators/message_validator.rb
190
+ - lib/diaspora_federation/validators/optional_aware_validator.rb
189
191
  - lib/diaspora_federation/validators/participation_validator.rb
190
192
  - lib/diaspora_federation/validators/person_validator.rb
191
193
  - lib/diaspora_federation/validators/photo_validator.rb
@@ -228,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
228
230
  version: '0'
229
231
  requirements: []
230
232
  rubyforge_project:
231
- rubygems_version: 2.6.8
233
+ rubygems_version: 2.6.13
232
234
  signing_key:
233
235
  specification_version: 4
234
236
  summary: diaspora* federation library