diaspora_federation 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 35ffb28db27478325eaf2f2d61290b125f2e20e2
4
- data.tar.gz: cfaca21b0289d178044895bbb91326b51520fed7
3
+ metadata.gz: 3b3836cd6caf68ffba2a101eb90c98932a7cfc1e
4
+ data.tar.gz: 87fcf932a0f7c2b9151af3bd12c72a1acaf6c935
5
5
  SHA512:
6
- metadata.gz: 0ef15d9756b8174572d54d926388e8dffbeabea94f342ceb62ef536f5c52f6c9003e904b208c22a277c4c4906c1723ea7bf7f82181dae3726a9b5aeaa51b77a0
7
- data.tar.gz: 1e77ba30e7745f64927b58ab0c110fa9467f0b8981e539ce24500754d5a61f548ac63e362c84ad5cfb7b6bbbca92ac065b1aa1e4a9debf1599150dbad27a1f5b
6
+ metadata.gz: 94783e21640104926dac495d63976435dee0e47f99cf0d6e9d71a07615cd645e2aecee0a3cb73f98f46fc974e4a6835d32999906824610a607fd598557cc0848
7
+ data.tar.gz: 13a49b3cb39df94c54c67289da76fb41f2c8a9db3ec44ad865ce88a4439cd9487cdf3f796a341c85293437b3a330fd40b2510f48908d8b422eb927a59ca857e0
data/README.md CHANGED
@@ -1,18 +1,68 @@
1
- # diaspora* federation rails engine
1
+ # diaspora* federation library
2
2
 
3
- #### A rails engine that adds the diaspora* federation protocol to a rails app
3
+ **A library that provides functionalities needed for the diaspora* federation protocol**
4
4
 
5
5
  [![Build Status](https://travis-ci.org/SuperTux88/diaspora_federation.svg?branch=master)](https://travis-ci.org/SuperTux88/diaspora_federation)
6
6
  [![Code Climate](https://codeclimate.com/github/SuperTux88/diaspora_federation/badges/gpa.svg)](https://codeclimate.com/github/SuperTux88/diaspora_federation)
7
7
  [![Test Coverage](https://codeclimate.com/github/SuperTux88/diaspora_federation/badges/coverage.svg)](https://codeclimate.com/github/SuperTux88/diaspora_federation/coverage)
8
8
  [![Dependency Status](https://gemnasium.com/SuperTux88/diaspora_federation.svg)](https://gemnasium.com/SuperTux88/diaspora_federation)
9
9
  [![Inline docs](https://inch-ci.org/github/SuperTux88/diaspora_federation.svg?branch=master)](https://inch-ci.org/github/SuperTux88/diaspora_federation)
10
+ [![Gem Version](https://badge.fury.io/rb/diaspora_federation.svg)](https://badge.fury.io/rb/diaspora_federation)
10
11
 
11
- [Documentation](http://www.rubydoc.info/github/SuperTux88/diaspora_federation/master) |
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.
@@ -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
- # Example:
27
+ # @example with uri
16
28
  # config.server_uri = URI("http://localhost:3000/")
17
- # or
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
- # *find_local_by_diaspora_handle*
30
- # This should return a +Person+, which is on this pod and the account is not closed.
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
- # *hcard_profile_hash*
50
- # This should return a +Hash+ with the following information:
51
- # {
52
- # guid: "0123456789abcdef",
53
- # nickname: "user",
54
- # full_name: "User Name",
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
- # DiasporaFederation.configure do |config|
73
- # config.server_uri = "http://localhost:3000/"
49
+ # @example
50
+ # config.define_callbacks do
51
+ # on :some_event do |arg1|
52
+ # # do something
53
+ # end
74
54
  # end
75
- def configure
76
- yield self
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 "missing server_uri" unless @server_uri.respond_to? :host
86
- validate_class(@person_class, "person_class", %i(
87
- find_local_by_diaspora_handle
88
- find_local_by_guid
89
- webfinger_hash
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