diaspora_federation 0.2.1 → 0.2.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/Changelog.md +27 -2
- data/lib/diaspora_federation.rb +16 -0
- data/lib/diaspora_federation/discovery/discovery.rb +23 -8
- data/lib/diaspora_federation/discovery/exceptions.rb +6 -6
- data/lib/diaspora_federation/discovery/web_finger.rb +13 -1
- data/lib/diaspora_federation/discovery/xrd_document.rb +35 -3
- data/lib/diaspora_federation/entities/event.rb +1 -1
- data/lib/diaspora_federation/entities/photo.rb +2 -2
- data/lib/diaspora_federation/entities/post.rb +5 -0
- data/lib/diaspora_federation/entities/profile.rb +4 -1
- data/lib/diaspora_federation/entities/related_entity.rb +8 -0
- data/lib/diaspora_federation/entities/relayable.rb +3 -3
- data/lib/diaspora_federation/entities/reshare.rb +28 -8
- data/lib/diaspora_federation/entities/retraction.rb +1 -1
- data/lib/diaspora_federation/entities/status_message.rb +0 -5
- data/lib/diaspora_federation/entity.rb +4 -1
- data/lib/diaspora_federation/federation.rb +1 -0
- data/lib/diaspora_federation/federation/diaspora_url_parser.rb +34 -0
- data/lib/diaspora_federation/federation/fetcher.rb +23 -11
- data/lib/diaspora_federation/federation/receiver.rb +2 -2
- data/lib/diaspora_federation/federation/receiver/abstract_receiver.rb +6 -1
- data/lib/diaspora_federation/federation/receiver/public.rb +10 -3
- data/lib/diaspora_federation/parsers/base_parser.rb +1 -1
- data/lib/diaspora_federation/properties_dsl.rb +0 -2
- data/lib/diaspora_federation/validators.rb +1 -0
- data/lib/diaspora_federation/validators/account_deletion_validator.rb +2 -2
- data/lib/diaspora_federation/validators/account_migration_validator.rb +2 -2
- data/lib/diaspora_federation/validators/comment_validator.rb +1 -1
- data/lib/diaspora_federation/validators/contact_validator.rb +3 -3
- data/lib/diaspora_federation/validators/conversation_validator.rb +2 -2
- data/lib/diaspora_federation/validators/event_participation_validator.rb +1 -1
- data/lib/diaspora_federation/validators/event_validator.rb +2 -2
- data/lib/diaspora_federation/validators/h_card_validator.rb +1 -1
- data/lib/diaspora_federation/validators/like_validator.rb +1 -1
- data/lib/diaspora_federation/validators/location_validator.rb +1 -1
- data/lib/diaspora_federation/validators/message_validator.rb +2 -2
- data/lib/diaspora_federation/validators/optional_aware_validator.rb +23 -0
- data/lib/diaspora_federation/validators/participation_validator.rb +2 -2
- data/lib/diaspora_federation/validators/person_validator.rb +2 -2
- data/lib/diaspora_federation/validators/photo_validator.rb +3 -3
- data/lib/diaspora_federation/validators/poll_answer_validator.rb +1 -1
- data/lib/diaspora_federation/validators/poll_participation_validator.rb +1 -1
- data/lib/diaspora_federation/validators/poll_validator.rb +1 -1
- data/lib/diaspora_federation/validators/profile_validator.rb +1 -1
- data/lib/diaspora_federation/validators/related_entity_validator.rb +1 -1
- data/lib/diaspora_federation/validators/relayable_validator.rb +1 -1
- data/lib/diaspora_federation/validators/reshare_validator.rb +3 -5
- data/lib/diaspora_federation/validators/retraction_validator.rb +2 -2
- data/lib/diaspora_federation/validators/rules/diaspora_id.rb +18 -13
- data/lib/diaspora_federation/validators/rules/guid.rb +10 -15
- data/lib/diaspora_federation/validators/status_message_validator.rb +2 -2
- data/lib/diaspora_federation/validators/web_finger_validator.rb +1 -1
- data/lib/diaspora_federation/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79f5ac9d463c824a36b9769994431ac6e66625fd
|
|
4
|
+
data.tar.gz: 270ed5a33de71c405bdcd584c620d3f1d97422c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 14ed9bc7669bf07166bebacfe43bbb0cb78821010964181f94f3f395838ba7836755ebdc913048f7455fa6ca6cf0e78e281190ec1f914591c5233a641d986040
|
|
7
|
+
data.tar.gz: 91b6f07218394e0e066aefd973de6ddc4fa7c0d694c4ee25e53504d15f5371036e8b765f1d911f6008464c9f86e23e66a2a9010c32e3fb4f30da8c064099668e
|
data/Changelog.md
CHANGED
|
@@ -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
|
|
data/lib/diaspora_federation.rb
CHANGED
|
@@ -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
|
|
57
|
-
domain
|
|
58
|
-
|
|
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
|
|
64
|
-
host_meta.webfinger_template_url.gsub("{uri}",
|
|
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
|
|
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
|
|
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 <
|
|
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 <
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
)
|
|
13
|
-
|
|
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
|
|
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
|
-
|
|
10
|
+
validate_public_flag
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def
|
|
14
|
-
entity.respond_to?(: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
|
|
@@ -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
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module DiasporaFederation
|
|
2
2
|
module Validators
|
|
3
3
|
# This validates a {Entities::AccountDeletion}.
|
|
4
|
-
class AccountDeletionValidator <
|
|
4
|
+
class AccountDeletionValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
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 <
|
|
4
|
+
class AccountMigrationValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
7
|
+
rule :author, :diaspora_id
|
|
8
8
|
|
|
9
9
|
rule :profile, :not_nil
|
|
10
10
|
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module DiasporaFederation
|
|
2
2
|
module Validators
|
|
3
3
|
# This validates a {Entities::Contact}.
|
|
4
|
-
class ContactValidator <
|
|
4
|
+
class ContactValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
8
|
-
rule :recipient,
|
|
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 <
|
|
4
|
+
class ConversationValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
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 <
|
|
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 <
|
|
4
|
+
class EventValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
7
|
+
rule :author, :diaspora_id
|
|
8
8
|
|
|
9
9
|
rule :guid, :guid
|
|
10
10
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module DiasporaFederation
|
|
2
2
|
module Validators
|
|
3
3
|
# This validates a {Entities::Message}.
|
|
4
|
-
class MessageValidator <
|
|
4
|
+
class MessageValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
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 <
|
|
4
|
+
class ParticipationValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
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 <
|
|
4
|
+
class PersonValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
7
|
rule :guid, :guid
|
|
8
8
|
|
|
9
|
-
rule :author,
|
|
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 <
|
|
4
|
+
class PhotoValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
7
|
rule :guid, :guid
|
|
8
8
|
|
|
9
|
-
rule :author,
|
|
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
|
|
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::PollParticipation}.
|
|
4
|
-
class PollParticipationValidator <
|
|
4
|
+
class PollParticipationValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
7
|
include RelayableValidator
|
|
@@ -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,
|
|
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 <
|
|
4
|
+
class ReshareValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :root_author,
|
|
7
|
+
rule :root_author, :diaspora_id
|
|
8
8
|
|
|
9
9
|
rule :root_guid, :guid
|
|
10
10
|
|
|
11
|
-
rule :author,
|
|
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 <
|
|
4
|
+
class RetractionValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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 = "(
|
|
20
|
-
addr_spec = "(#{username}\\@#{domain}#{port})?"
|
|
19
|
+
port = "(?::[\\d]+)?"
|
|
21
20
|
|
|
22
|
-
|
|
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.
|
|
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
|
-
#
|
|
11
|
-
|
|
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
|
-
|
|
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 <
|
|
4
|
+
class StatusMessageValidator < OptionalAwareValidator
|
|
5
5
|
include Validation
|
|
6
6
|
|
|
7
|
-
rule :author,
|
|
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 <
|
|
7
|
+
class WebFingerValidator < OptionalAwareValidator
|
|
8
8
|
include Validation
|
|
9
9
|
|
|
10
10
|
rule :acct_uri, :not_empty
|
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.
|
|
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-
|
|
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.
|
|
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.
|
|
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.
|
|
233
|
+
rubygems_version: 2.6.13
|
|
232
234
|
signing_key:
|
|
233
235
|
specification_version: 4
|
|
234
236
|
summary: diaspora* federation library
|