diaspora_federation 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +55 -5
- data/lib/diaspora_federation.rb +43 -79
- data/lib/diaspora_federation/callbacks.rb +62 -0
- data/lib/diaspora_federation/entity.rb +146 -0
- data/lib/diaspora_federation/logging.rb +3 -4
- data/lib/diaspora_federation/properties_dsl.rb +106 -0
- data/lib/diaspora_federation/version.rb +1 -2
- data/lib/diaspora_federation/web_finger.rb +0 -1
- data/lib/diaspora_federation/web_finger/exceptions.rb +1 -5
- data/lib/diaspora_federation/web_finger/h_card.rb +101 -127
- data/lib/diaspora_federation/web_finger/host_meta.rb +0 -7
- data/lib/diaspora_federation/web_finger/web_finger.rb +79 -110
- data/lib/diaspora_federation/web_finger/xrd_document.rb +0 -3
- data/lib/tasks/build.rake +17 -0
- metadata +20 -30
- data/Rakefile +0 -26
- data/app/controllers/diaspora_federation/application_controller.rb +0 -6
- data/app/controllers/diaspora_federation/h_card_controller.rb +0 -20
- data/app/controllers/diaspora_federation/receive_controller.rb +0 -35
- data/app/controllers/diaspora_federation/webfinger_controller.rb +0 -60
- data/config/routes.rb +0 -15
- data/lib/diaspora_federation/engine.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b3836cd6caf68ffba2a101eb90c98932a7cfc1e
|
4
|
+
data.tar.gz: 87fcf932a0f7c2b9151af3bd12c72a1acaf6c935
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94783e21640104926dac495d63976435dee0e47f99cf0d6e9d71a07615cd645e2aecee0a3cb73f98f46fc974e4a6835d32999906824610a607fd598557cc0848
|
7
|
+
data.tar.gz: 13a49b3cb39df94c54c67289da76fb41f2c8a9db3ec44ad865ce88a4439cd9487cdf3f796a341c85293437b3a330fd40b2510f48908d8b422eb927a59ca857e0
|
data/README.md
CHANGED
@@ -1,18 +1,68 @@
|
|
1
|
-
# diaspora* federation
|
1
|
+
# diaspora* federation library
|
2
2
|
|
3
|
-
|
3
|
+
**A library that provides functionalities needed for the diaspora* federation protocol**
|
4
4
|
|
5
5
|
[](https://travis-ci.org/SuperTux88/diaspora_federation)
|
6
6
|
[](https://codeclimate.com/github/SuperTux88/diaspora_federation)
|
7
7
|
[](https://codeclimate.com/github/SuperTux88/diaspora_federation/coverage)
|
8
8
|
[](https://gemnasium.com/SuperTux88/diaspora_federation)
|
9
9
|
[](https://inch-ci.org/github/SuperTux88/diaspora_federation)
|
10
|
+
[](https://badge.fury.io/rb/diaspora_federation)
|
10
11
|
|
11
|
-
[Documentation](http://www.rubydoc.info/
|
12
|
-
[Project site](https://diasporafoundation.org) |
|
13
|
-
[Wiki](https://wiki.diasporafoundation.org) |
|
12
|
+
[Documentation](http://www.rubydoc.info/gems/diaspora_federation/) |
|
14
13
|
[Bugtracker](https://github.com/SuperTux88/diaspora_federation/issues)
|
15
14
|
|
15
|
+
## Library
|
16
|
+
|
17
|
+
The ```diaspora_federation``` gem provides the functionality for de-/serialization and de-/encryption of Entities
|
18
|
+
in the protocols used for communication among the various installations of Diaspora*
|
19
|
+
|
20
|
+
## Rails Engine
|
21
|
+
|
22
|
+
The ```diaspora_federation-rails``` gem is a rails engine that adds the diaspora* federation protocol to a rails app.
|
23
|
+
|
24
|
+
### Usage
|
25
|
+
|
26
|
+
Add the gem to your ```Gemfile```:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem "diaspora_federation-rails"
|
30
|
+
```
|
31
|
+
|
32
|
+
Mount the routes in your ```config/routes.rb```:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
mount DiasporaFederation::Engine => "/"
|
36
|
+
```
|
37
|
+
|
38
|
+
Configure the engine in ```config/initializers/diaspora_federation.rb```:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
DiasporaFederation.configure do |config|
|
42
|
+
# the pod url
|
43
|
+
config.server_uri = AppConfig.pod_uri
|
44
|
+
|
45
|
+
# the class to be used for a person
|
46
|
+
config.person_class = Person
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
## Development
|
51
|
+
|
52
|
+
**!!! This gem is currently under heavy development, so every release can contain breaking changes !!!**
|
53
|
+
|
54
|
+
If you want to help, please contact me, help is welcome.
|
55
|
+
|
56
|
+
After the first stable release, this repo will be moved to the [diaspora organization](https://github.com/diaspora/).
|
57
|
+
|
58
|
+
## Diaspora
|
59
|
+
|
60
|
+
A privacy-aware, distributed, open source social network
|
61
|
+
|
62
|
+
Links:
|
63
|
+
[Project site](https://diasporafoundation.org) |
|
64
|
+
[Wiki](https://wiki.diasporafoundation.org)
|
65
|
+
|
16
66
|
## License
|
17
67
|
|
18
68
|
This gem is published under the terms of the "GNU Affero General Public License". See the LICENSE file for the exact wording.
|
data/lib/diaspora_federation.rb
CHANGED
@@ -1,113 +1,77 @@
|
|
1
|
-
require "diaspora_federation/engine"
|
2
1
|
require "diaspora_federation/logging"
|
3
2
|
|
3
|
+
require "diaspora_federation/callbacks"
|
4
|
+
require "diaspora_federation/properties_dsl"
|
5
|
+
require "diaspora_federation/entity"
|
6
|
+
|
4
7
|
require "diaspora_federation/web_finger"
|
5
8
|
|
6
|
-
|
7
|
-
# diaspora* federation rails engine
|
9
|
+
# diaspora* federation library
|
8
10
|
module DiasporaFederation
|
9
11
|
extend Logging
|
10
12
|
|
13
|
+
@callbacks = Callbacks.new %i(
|
14
|
+
person_webfinger_fetch
|
15
|
+
person_hcard_fetch
|
16
|
+
)
|
17
|
+
|
11
18
|
class << self
|
12
|
-
|
19
|
+
# {Callbacks} instance with defined callbacks
|
20
|
+
# @see Callbacks#on
|
21
|
+
# @see Callbacks#trigger
|
22
|
+
#
|
23
|
+
attr_reader :callbacks
|
24
|
+
|
13
25
|
# the pod url
|
14
26
|
#
|
15
|
-
#
|
27
|
+
# @example with uri
|
16
28
|
# config.server_uri = URI("http://localhost:3000/")
|
17
|
-
#
|
29
|
+
# @example with configured pod_uri
|
18
30
|
# config.server_uri = AppConfig.pod_uri
|
19
31
|
attr_accessor :server_uri
|
20
32
|
|
21
|
-
|
22
|
-
# the class to use as +Person+
|
23
|
-
#
|
24
|
-
# Example:
|
25
|
-
# config.person_class = Person.to_s
|
26
|
-
#
|
27
|
-
# This class must have the following methods:
|
33
|
+
# configure the federation library
|
28
34
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# *find_local_by_guid*
|
33
|
-
# This should return a +Person+, which is on this pod and the account is not closed.
|
34
|
-
#
|
35
|
-
# *webfinger_hash*
|
36
|
-
# This should return a +Hash+ with the following information:
|
37
|
-
# {
|
38
|
-
# acct_uri: "acct:user@server.example",
|
39
|
-
# alias_url: "https://server.example/people/0123456789abcdef",
|
40
|
-
# hcard_url: "https://server.example/hcard/users/0123456789abcdef",
|
41
|
-
# seed_url: "https://server.example/",
|
42
|
-
# profile_url: "https://server.example/u/user",
|
43
|
-
# atom_url: "https://server.example/public/user.atom",
|
44
|
-
# salmon_url: "https://server.example/receive/users/0123456789abcdef",
|
45
|
-
# guid: "0123456789abcdef",
|
46
|
-
# pubkey: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----"
|
47
|
-
# }
|
35
|
+
# @example
|
36
|
+
# DiasporaFederation.configure do |config|
|
37
|
+
# config.server_uri = URI("http://localhost:3000/")
|
48
38
|
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
# url: "https://server.example/",
|
56
|
-
# photo_large_url: "https://server.example/uploads/f.jpg",
|
57
|
-
# photo_medium_url: "https://server.example/uploads/m.jpg",
|
58
|
-
# photo_small_url: "https://server.example/uploads/s.jpg",
|
59
|
-
# pubkey: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----",
|
60
|
-
# searchable: true,
|
61
|
-
# first_name: "User",
|
62
|
-
# last_name: "Name"
|
63
|
-
# }
|
64
|
-
attr_accessor :person_class
|
65
|
-
def person_class
|
66
|
-
const_get(@person_class)
|
39
|
+
# config.define_callbacks do
|
40
|
+
# # callback configuration
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
def configure
|
44
|
+
yield self
|
67
45
|
end
|
68
46
|
|
69
|
-
|
70
|
-
# configure the federation engine
|
47
|
+
# define the callbacks
|
71
48
|
#
|
72
|
-
#
|
73
|
-
#
|
49
|
+
# @example
|
50
|
+
# config.define_callbacks do
|
51
|
+
# on :some_event do |arg1|
|
52
|
+
# # do something
|
53
|
+
# end
|
74
54
|
# end
|
75
|
-
|
76
|
-
|
55
|
+
#
|
56
|
+
# @param [Proc] block the callbacks to define
|
57
|
+
def define_callbacks(&block)
|
58
|
+
@callbacks.instance_eval(&block)
|
77
59
|
end
|
78
60
|
|
79
|
-
##
|
80
61
|
# validates if the engine is configured correctly
|
81
62
|
#
|
82
63
|
# called from after_initialize
|
83
64
|
# @raise [ConfigurationError] if the configuration is incomplete or invalid
|
84
65
|
def validate_config
|
85
|
-
configuration_error "
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
hcard_profile_hash
|
91
|
-
))
|
92
|
-
logger.info "successfully configured the federation engine"
|
66
|
+
configuration_error "Missing server_uri" unless @server_uri.respond_to? :host
|
67
|
+
unless @callbacks.definition_complete?
|
68
|
+
configuration_error "Missing handlers for #{@callbacks.missing_handlers.join(', ')}"
|
69
|
+
end
|
70
|
+
logger.info "successfully configured the federation library"
|
93
71
|
end
|
94
72
|
|
95
73
|
private
|
96
74
|
|
97
|
-
def validate_class(klass, name, methods)
|
98
|
-
configuration_error "missing #{name}" unless klass
|
99
|
-
entity = const_get(klass)
|
100
|
-
|
101
|
-
return logger.warn "table for #{entity} doesn't exist, skip validation" unless entity.table_exists?
|
102
|
-
|
103
|
-
methods.each {|method| entity_respond_to?(entity, name, method) }
|
104
|
-
end
|
105
|
-
|
106
|
-
def entity_respond_to?(entity, name, method)
|
107
|
-
valid = entity.respond_to?(method) || entity.column_names.include?(method.to_s) || entity.method_defined?(method)
|
108
|
-
configuration_error "the configured class #{entity} for #{name} doesn't respond to #{method}" unless valid
|
109
|
-
end
|
110
|
-
|
111
75
|
def configuration_error(message)
|
112
76
|
logger.fatal("diaspora federation configuration error: #{message}")
|
113
77
|
raise ConfigurationError, message
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
# Callbacks are used to communicate with the application. They are called to
|
3
|
+
# fetch data and after data is received.
|
4
|
+
class Callbacks
|
5
|
+
# Initializes a new Callbacks object with the event-keys that need to be defined.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# Callbacks.new %i(
|
9
|
+
# some_event
|
10
|
+
# another_event
|
11
|
+
# )
|
12
|
+
#
|
13
|
+
# @param [Hash] events event keys
|
14
|
+
def initialize(events)
|
15
|
+
@events = events
|
16
|
+
@handlers = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# defines a callback
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# callbacks.on :some_event do |arg1|
|
23
|
+
# # do something
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @param [Symbol] event the event key
|
27
|
+
# @param [Proc] callback the callback block
|
28
|
+
# @raise [ArgumentError] if the event key is undefined or has already a handler
|
29
|
+
def on(event, &callback)
|
30
|
+
raise ArgumentError, "Undefined event #{event}" unless @events.include? event
|
31
|
+
raise ArgumentError, "Already defined event #{event}" if @handlers.has_key? event
|
32
|
+
|
33
|
+
@handlers[event] = callback
|
34
|
+
end
|
35
|
+
|
36
|
+
# triggers a callback
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# callbacks.trigger :some_event, "foo"
|
40
|
+
#
|
41
|
+
# @param [Symbol] event the event key
|
42
|
+
# @return [Object] the return-value of the callback
|
43
|
+
# @raise [ArgumentError] if the event key is undefined
|
44
|
+
def trigger(event, *args)
|
45
|
+
raise ArgumentError, "Undefined event #{event}" unless @events.include? event
|
46
|
+
|
47
|
+
@handlers[event].call(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
# checks if all callbacks are defined
|
51
|
+
# @return [Boolean]
|
52
|
+
def definition_complete?
|
53
|
+
missing_handlers.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns all undefined callbacks
|
57
|
+
# @return [Hash] callback keys
|
58
|
+
def missing_handlers
|
59
|
+
@events - @handlers.keys
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
# +Entity+ is the base class for all other objects used to encapsulate data
|
3
|
+
# for federation messages in the Diaspora* network.
|
4
|
+
# Entity fields are specified using a simple {PropertiesDSL DSL} as part of
|
5
|
+
# the class definition.
|
6
|
+
#
|
7
|
+
# Any entity also provides the means to serialize itself and all nested
|
8
|
+
# entities to XML (for deserialization from XML to +Entity+ instances, see
|
9
|
+
# {XmlPayload}).
|
10
|
+
#
|
11
|
+
# @abstract Subclass and specify properties to implement various entities.
|
12
|
+
#
|
13
|
+
# @example Entity subclass definition
|
14
|
+
# class MyEntity < Entity
|
15
|
+
# property :prop
|
16
|
+
# property :optional, default: false
|
17
|
+
# property :dynamic_default, default: -> { Time.now }
|
18
|
+
# entity :nested, NestedEntity
|
19
|
+
# entity :multiple, [OtherEntity]
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @example Entity instantiation
|
23
|
+
# nentity = NestedEntity.new
|
24
|
+
# oe1 = OtherEntity.new
|
25
|
+
# oe2 = OtherEntity.new
|
26
|
+
#
|
27
|
+
# entity = MyEntity.new(prop: 'some property',
|
28
|
+
# nested: nentity,
|
29
|
+
# multiple: [oe1, oe2])
|
30
|
+
#
|
31
|
+
# @note Entity properties can only be set during initialization, after that the
|
32
|
+
# entity instance becomes frozen and must not be modified anymore. Instances
|
33
|
+
# are intended to be immutable data containers, only.
|
34
|
+
class Entity
|
35
|
+
extend PropertiesDSL
|
36
|
+
|
37
|
+
# Initializes the Entity with the given attribute hash and freezes the created
|
38
|
+
# instance it returns.
|
39
|
+
#
|
40
|
+
# @note Attributes not defined as part of the class definition ({PropertiesDSL#property},
|
41
|
+
# {PropertiesDSL#entity}) get discarded silently.
|
42
|
+
#
|
43
|
+
# @param [Hash] data
|
44
|
+
# @return [Entity] new instance
|
45
|
+
def initialize(data)
|
46
|
+
raise ArgumentError, "expected a Hash" unless data.is_a?(Hash)
|
47
|
+
missing_props = self.class.missing_props(data)
|
48
|
+
unless missing_props.empty?
|
49
|
+
raise ArgumentError, "missing required properties: #{missing_props.join(', ')}"
|
50
|
+
end
|
51
|
+
|
52
|
+
self.class.default_values.merge(data).each do |k, v|
|
53
|
+
instance_variable_set("@#{k}", v) if setable?(k, v)
|
54
|
+
end
|
55
|
+
freeze
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns a Hash representing this Entity (attributes => values)
|
59
|
+
# @return [Hash] entity data (mostly equal to the hash used for initialization).
|
60
|
+
def to_h
|
61
|
+
self.class.class_prop_names.each_with_object({}) do |prop, hash|
|
62
|
+
hash[prop] = send(prop)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the XML representation for this entity constructed out of
|
67
|
+
# {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}s
|
68
|
+
#
|
69
|
+
# @see Nokogiri::XML::Node.to_xml
|
70
|
+
# @see XmlPayload.pack
|
71
|
+
#
|
72
|
+
# @return [Nokogiri::XML::Element] root element containing properties as child elements
|
73
|
+
def to_xml
|
74
|
+
entity_xml
|
75
|
+
end
|
76
|
+
|
77
|
+
# some of this is from Rails "Inflector.demodulize" and "Inflector.undersore"
|
78
|
+
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
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def setable?(name, val)
|
94
|
+
prop_def = self.class.class_props.find {|p| p[:name] == name }
|
95
|
+
return false if prop_def.nil? # property undefined
|
96
|
+
|
97
|
+
setable_string?(prop_def, val) || setable_nested?(prop_def, val) || setable_multi?(prop_def, val)
|
98
|
+
end
|
99
|
+
|
100
|
+
def setable_string?(definition, val)
|
101
|
+
(definition[:type] == String && val.respond_to?(:to_s))
|
102
|
+
end
|
103
|
+
|
104
|
+
def setable_nested?(definition, val)
|
105
|
+
t = definition[:type]
|
106
|
+
(t.is_a?(Class) && t.ancestors.include?(Entity) && val.is_a?(Entity))
|
107
|
+
end
|
108
|
+
|
109
|
+
def setable_multi?(definition, val)
|
110
|
+
t = definition[:type]
|
111
|
+
(t.instance_of?(Array) &&
|
112
|
+
val.instance_of?(Array) &&
|
113
|
+
val.all? {|v| v.instance_of?(t.first) })
|
114
|
+
end
|
115
|
+
|
116
|
+
# Serialize the Entity into XML elements
|
117
|
+
# @return [Nokogiri::XML::Element] root node
|
118
|
+
def entity_xml
|
119
|
+
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
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
root_element
|
136
|
+
end
|
137
|
+
|
138
|
+
# 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
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|