roadforest 0.5 → 0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/examples/file-management.rb +70 -58
  3. data/lib/roadforest/application.rb +9 -17
  4. data/lib/roadforest/application/dispatcher.rb +76 -9
  5. data/lib/roadforest/application/parameters.rb +9 -1
  6. data/lib/roadforest/application/path-provider.rb +30 -3
  7. data/lib/roadforest/application/route-adapter.rb +96 -14
  8. data/lib/roadforest/application/services-host.rb +21 -3
  9. data/lib/roadforest/augment/affordance.rb +82 -11
  10. data/lib/roadforest/augment/augmentation.rb +24 -6
  11. data/lib/roadforest/augment/augmenter.rb +12 -3
  12. data/lib/roadforest/authorization.rb +7 -229
  13. data/lib/roadforest/authorization/auth-entity.rb +26 -0
  14. data/lib/roadforest/authorization/authentication-chain.rb +79 -0
  15. data/lib/roadforest/authorization/default-authentication-store.rb +33 -0
  16. data/lib/roadforest/authorization/grant-builder.rb +23 -0
  17. data/lib/roadforest/authorization/grants-holder.rb +58 -0
  18. data/lib/roadforest/authorization/manager.rb +85 -0
  19. data/lib/roadforest/authorization/policy.rb +19 -0
  20. data/lib/roadforest/graph/access-manager.rb +25 -2
  21. data/lib/roadforest/graph/focus-list.rb +4 -0
  22. data/lib/roadforest/graph/graph-focus.rb +30 -13
  23. data/lib/roadforest/graph/nav-affordance-builder.rb +62 -0
  24. data/lib/roadforest/graph/normalization.rb +3 -3
  25. data/lib/roadforest/graph/path-vocabulary.rb +64 -0
  26. data/lib/roadforest/graph/post-focus.rb +5 -0
  27. data/lib/roadforest/graph/vocabulary.rb +4 -1
  28. data/lib/roadforest/http/adapters/excon.rb +4 -0
  29. data/lib/roadforest/http/graph-transfer.rb +17 -1
  30. data/lib/roadforest/http/keychain.rb +121 -33
  31. data/lib/roadforest/http/user-agent.rb +5 -3
  32. data/lib/roadforest/interface/application.rb +25 -8
  33. data/lib/roadforest/interface/rdf.rb +114 -15
  34. data/lib/roadforest/interface/utility.rb +3 -0
  35. data/lib/roadforest/interface/utility/backfill.rb +63 -0
  36. data/lib/roadforest/interface/utility/grant-list.rb +45 -0
  37. data/lib/roadforest/interface/utility/grant.rb +22 -0
  38. data/lib/roadforest/interfaces.rb +1 -0
  39. data/lib/roadforest/path-matcher.rb +471 -0
  40. data/lib/roadforest/remote-host.rb +159 -35
  41. data/lib/roadforest/resource/read-only.rb +23 -4
  42. data/lib/roadforest/server.rb +32 -3
  43. data/lib/roadforest/source-rigor/graph-store.rb +0 -2
  44. data/lib/roadforest/source-rigor/rigorous-access.rb +138 -21
  45. data/lib/roadforest/templates/affordance-property-values.haml +3 -0
  46. data/lib/roadforest/templates/rdfpost-curie.haml +1 -1
  47. data/lib/roadforest/test-support/matchers.rb +41 -12
  48. data/lib/roadforest/test-support/remote-host.rb +3 -3
  49. data/lib/roadforest/type-handlers/rdfa-writer/environment-decorator.rb +1 -1
  50. data/lib/roadforest/type-handlers/rdfa-writer/render-engine.rb +40 -27
  51. data/lib/roadforest/type-handlers/rdfa.rb +10 -3
  52. data/lib/roadforest/utility/class-registry.rb +44 -4
  53. data/spec/affordance-augmenter.rb +46 -19
  54. data/spec/affordances-flow.rb +46 -30
  55. data/spec/authorization.rb +16 -4
  56. data/spec/client.rb +22 -4
  57. data/spec/focus-list.rb +24 -0
  58. data/spec/full-integration.rb +8 -3
  59. data/spec/graph-store.rb +8 -0
  60. data/spec/keychain.rb +18 -14
  61. data/spec/rdf-normalization.rb +32 -6
  62. data/spec/update-focus.rb +36 -39
  63. metadata +19 -5
@@ -3,19 +3,33 @@ module RoadForest
3
3
  #XXX Worth doing some meta to get reality checking of configs here? Better
4
4
  #fail early if there's no DB configured, right?
5
5
  class ServicesHost
6
+ include Graph::Normalization
7
+
6
8
  def initialize
7
9
  end
8
10
 
9
11
  attr_writer :application
10
12
  attr_writer :router, :canonical_host, :type_handling
11
13
  attr_writer :logger, :authorization
14
+ attr_accessor :root_url
15
+
16
+ attr_accessor :default_content_engine
12
17
 
13
18
  def canonical_host
14
- @application.canonical_host
19
+ @canonical_host ||= RDF::URI.intern(@root_url)
20
+ end
21
+
22
+ def augmenter
23
+ @augmenter ||= Augment::Augmenter.new(self)
15
24
  end
16
25
 
17
- def router
18
- @router ||= PathProvider.new(@application.dispatcher)
26
+ def dispatcher
27
+ @dispatcher ||= Dispatcher.new(self)
28
+ end
29
+ alias router dispatcher
30
+
31
+ def path_provider
32
+ @path_provider ||= router.path_provider
19
33
  end
20
34
 
21
35
  def authorization
@@ -34,6 +48,10 @@ module RoadForest
34
48
  end
35
49
  end
36
50
 
51
+ def default_content_engine
52
+ @default_content_engine || ContentHandling.rdf_engine
53
+ end
54
+
37
55
  alias authz authorization
38
56
  end
39
57
  end
@@ -6,7 +6,22 @@ module RoadForest
6
6
  module Affordance
7
7
  Af = Graph::Af
8
8
 
9
+ module GrantTokens
10
+ def each_grant_token(method, term)
11
+ grant_route = term.router.mapped_route_for_name(term.route.name, :perm, {})
12
+ term.resource.required_grants(method).each do |grant|
13
+ grant_path = grant_route.build_path(:grant_name => grant)
14
+ yield ::RDF::URI.new(canonical_uri.join(grant_path)) #XXX magical route name
15
+ end
16
+ rescue KeyError
17
+ term.resource.required_grants(method).each do |grant|
18
+ yield grant
19
+ end
20
+ end
21
+ end
22
+
9
23
  class Remove < Augmentation
24
+ include GrantTokens
10
25
  register_for_subjects
11
26
 
12
27
  def apply(term)
@@ -14,11 +29,15 @@ module RoadForest
14
29
  node = ::RDF::Node.new
15
30
  yield [node, ::RDF.type, Af.Remove]
16
31
  yield [node, Af.target, term.uri]
32
+ each_grant_token("DELETE", term) do |token|
33
+ yield [node, Af.authorizedBy, token]
34
+ end
17
35
  end
18
36
  end
19
37
  end
20
38
 
21
39
  class Links < Augmentation
40
+ include GrantTokens
22
41
  register_for_subjects
23
42
  register_for_objects
24
43
 
@@ -45,32 +64,84 @@ module RoadForest
45
64
  yield [node, ::RDF.type, Af.Navigate]
46
65
  yield [node, Af.target, term.uri]
47
66
  end
67
+ each_grant_token("GET", term) do |token|
68
+ yield [node, Af.authorizedBy, token]
69
+ end
48
70
  end
49
71
  end
50
72
  end
51
73
  end
52
74
 
53
- class Update < Augmentation
54
- register_for_subjects
75
+ class PayloadAugmentation < Augmentation
76
+ include GrantTokens
77
+
78
+ def get_payload(resource)
79
+
80
+ end
81
+
82
+ def applicable?(resource)
83
+
84
+ end
85
+
86
+ def affordance_type
87
+
88
+ end
89
+
90
+ def applicable?(resource)
91
+ resource.allowed_methods.include?(http_method)
92
+ end
55
93
 
56
94
  def apply(term)
57
- if term.resource.allowed_methods.include?("PUT")
95
+ resource = term.resource
96
+ if applicable?(resource)
58
97
  node = ::RDF::Node.new
59
- yield [node, ::RDF.type, Af.Update]
98
+ yield [node, ::RDF.type, affordance_type]
60
99
  yield [node, Af.target, term.uri]
100
+ each_grant_token(http_method, term) do |token|
101
+ yield [node, Af.authorizedBy, token]
102
+ end
103
+ payload = get_payload(resource)
104
+ unless payload.nil?
105
+ yield [node, Af.payload, payload.root]
106
+ unless payload.graph.nil?
107
+ payload.graph.each_statement do |stmt|
108
+ yield stmt
109
+ end
110
+ end
111
+ end
61
112
  end
62
113
  end
63
114
  end
64
115
 
65
- class Create < Augmentation
116
+ class Update < PayloadAugmentation
66
117
  register_for_subjects
67
118
 
68
- def apply(term)
69
- if term.resource.allowed_methods.include?("POST")
70
- node = ::RDF::Node.new
71
- yield [node, ::RDF.type, Af.Create]
72
- yield [node, Af.target, term.uri]
73
- end
119
+ def get_payload(resource)
120
+ resource.interface.update_payload
121
+ end
122
+
123
+ def http_method
124
+ "PUT"
125
+ end
126
+
127
+ def affordance_type
128
+ Af.Update
129
+ end
130
+ end
131
+
132
+ class Create < PayloadAugmentation
133
+ register_for_subjects
134
+
135
+ def get_payload(resource)
136
+ resource.interface.create_payload
137
+ end
138
+
139
+ def http_method
140
+ "POST"
141
+ end
142
+
143
+ def affordance_type
144
+ Af.Create
74
145
  end
75
146
  end
76
147
  end
@@ -2,12 +2,30 @@ require 'roadforest/augment/augmenter'
2
2
  module RoadForest
3
3
  module Augment
4
4
  class Augmentation
5
- def self.register_for_subjects
6
- Augmenter.subject_augmentations_registry.add(self.name, self)
7
- end
5
+ class << self
6
+ def register_for_subjects
7
+ Augmenter.subject_augmentations_registry.add(self.name, self)
8
+ end
9
+
10
+ def subject_precedes(other)
11
+ Augmenter.subject_augmentations_registry.seq(self.name, other)
12
+ end
13
+
14
+ def subject_follows(other)
15
+ Augmenter.subject_augmentations_registry.seq(other, self.name)
16
+ end
8
17
 
9
- def self.register_for_objects
10
- Augmenter.object_augmentations_registry.add(self.name, self)
18
+ def register_for_objects
19
+ Augmenter.object_augmentations_registry.add(self.name, self)
20
+ end
21
+
22
+ def object_precedes(other)
23
+ Augmenter.object_augmentations_registry.seq(self.name, other)
24
+ end
25
+
26
+ def object_follows(other)
27
+ Augmenter.object_augmentations_registry.seq(other, self.name)
28
+ end
11
29
  end
12
30
 
13
31
  def initialize(augmenter)
@@ -49,7 +67,7 @@ module RoadForest
49
67
 
50
68
  def type_list
51
69
  @type_list ||=
52
- resource.content_types_provided.inject(ContentHandling::MediaTypeList.new) do |list, (type, method)|
70
+ resource.content_types_provided.inject(ContentHandling::MediaTypeList.new) do |list, (type, _)|
53
71
  list.add_header_val(type)
54
72
  end
55
73
  end
@@ -4,7 +4,18 @@ require 'roadforest/utility/class-registry'
4
4
  module RoadForest
5
5
  module Augment
6
6
  class Augmenter
7
- attr_accessor :router
7
+ def initialize(services)
8
+ @services = services
9
+ end
10
+ attr_reader :services
11
+
12
+ def router
13
+ services.router
14
+ end
15
+
16
+ def canonical_uri
17
+ @canonical_uri ||= Addressable::URI.parse(services.root_url)
18
+ end
8
19
 
9
20
  def self.subject_augmentations_registry
10
21
  @subject_registry ||= Utility::ClassRegistry.new(self, "subject augmentation")
@@ -26,8 +37,6 @@ module RoadForest
26
37
  end
27
38
  end
28
39
 
29
- attr_accessor :canonical_uri
30
-
31
40
  def augment(graph)
32
41
  augmenting = Augment::Process.new(graph)
33
42
 
@@ -1,231 +1,9 @@
1
- require 'base64'
2
- require 'openssl'
3
1
  require 'roadforest'
4
- require 'roadforest/utility/class-registry'
5
2
 
6
- module RoadForest
7
- module Authorization
8
- class GrantBuilder
9
- def initialize(salt, cache)
10
- @salt = salt
11
- @cache = cache
12
- @list = []
13
- end
14
- attr_reader :list
15
-
16
- def add(name, params=nil)
17
- canonical =
18
- if params.nil?
19
- [@salt, name]
20
- else
21
- [@salt, name, params.keys.sort.map do |key|
22
- [key, params[key]]
23
- end]
24
- end
25
- @list << @cache[canonical]
26
- end
27
- end
28
-
29
- class GrantsHolder
30
- def initialize(salt, hash_function)
31
- @salt = salt
32
-
33
- digester = OpenSSL::Digest.new(hash_function)
34
- @grants_cache = Hash.new do |h, k| #XXX potential resource exhaustion here - only accumulate auth'd results
35
- digester.reset
36
- h[k] = digester.digest(h.inspect)
37
- end
38
- end
39
-
40
- def get(key)
41
- @grants_cache[key]
42
- end
43
- alias [] get
44
-
45
- def build_grants
46
- builder = GrantBuilder.new(@salt, self)
47
- yield builder
48
- return builder.list
49
- end
50
- end
51
-
52
- class Manager
53
- attr_accessor :authenticator
54
- attr_accessor :policy
55
- attr_reader :grants
56
-
57
- HASH_FUNCTION = "SHA256".freeze
58
-
59
- def initialize(salt = nil, authenticator = nil, policy = nil)
60
- @grants = GrantsHolder.new(salt || "roadforest-insecure", HASH_FUNCTION)
61
-
62
- @authenticator = authenticator || AuthenticationChain.new(DefaultAuthenticationStore.new)
63
- @policy = policy || AuthorizationPolicy.new
64
- @policy.grants_holder = @grants
65
- end
66
-
67
- def build_grants(&block)
68
- @grants.build_grants(&block)
69
- end
70
-
71
- def challenge(options)
72
- @authenticator.challenge(options)
73
- end
74
-
75
- # @returns [:public|:granted|:refused]
76
- #
77
- # :public means the request doesn't need authorization
78
- # :granted means that it does need authz but the credentials passed are
79
- # allowed to access the resource
80
- # :refused means that the credentials passed are not allowed to access
81
- # the resource
82
- #
83
- # TODO: Resource needs to add s-maxage=0 for :granted requests or public
84
- # for :public requests to the CacheControl header
85
- def authorization(header, required_grants)
86
- entity = authenticator.authenticate(header)
87
-
88
- return :refused if entity.nil?
89
-
90
- available_grants = policy.grants_for(entity)
91
-
92
- if required_grants.any?{|required| available_grants.include?(required)}
93
- return :granted
94
- else
95
- return :refused
96
- end
97
- end
98
- end
99
-
100
- class AuthenticationChain
101
- class Scheme
102
- def self.registry_purpose; "authentication scheme"; end
103
- extend Utility::ClassRegistry::Registrar
104
-
105
- def self.register(name)
106
- registrar.registry.add(name, self.new)
107
- end
108
-
109
- def authenticated_entity(credentials, store)
110
- nil
111
- end
112
- end
113
-
114
- class Basic < Scheme
115
- register "Basic"
116
-
117
- def challenge(options)
118
- "Basic realm=\"#{options.fetch(:realm, "Roadforest App")}\""
119
- end
120
-
121
- def authenticated_entity(credentials, store)
122
- username, password = Base64.decode64(credentials).split(':',2)
123
-
124
- entity = store.by_username(username)
125
- entity.authenticate_by_password(password)
126
- entity
127
- end
128
- end
129
-
130
- def initialize(store)
131
- @store = store
132
- end
133
- attr_reader :store
134
-
135
- def handler_for(scheme)
136
- Scheme.get(scheme)
137
- rescue
138
- nil
139
- end
140
-
141
- def challenge(options)
142
- (Scheme.registry.names.map do |scheme_name|
143
- handler_for(scheme_name).challenge(options)
144
- end).join(", ")
145
- end
146
-
147
- def add_account(user,password,token)
148
- @store.add_account(user,password,token)
149
- end
150
-
151
- def authenticate(header)
152
- return nil if header.nil?
153
- scheme, credentials = header.split(/\s+/, 2)
154
-
155
- handler = handler_for(scheme)
156
- return nil if handler.nil?
157
-
158
- entity = handler.authenticated_entity(credentials, store)
159
- return nil if entity.nil?
160
- return nil unless entity.authenticated?
161
- return entity
162
- end
163
- end
164
-
165
- class AuthEntity
166
- def initialize
167
- @authenticated = false
168
- end
169
- attr_accessor :username, :password, :token
170
-
171
- def authenticated?
172
- !!@authenticated
173
- end
174
-
175
- def authenticate_by_password(password)
176
- @authenticated = (!password.nil? and password == @password)
177
- end
178
-
179
- def authenticate_by_token(token)
180
- @authenticated = (!token.nil? and token == @token)
181
- end
182
-
183
- def authenticate!
184
- @authenticated = true
185
- end
186
- end
187
-
188
- class DefaultAuthenticationStore
189
- def initialize
190
- @accounts = []
191
- end
192
-
193
- def build_entity(account)
194
- return nil if account.nil?
195
- AuthEntity.new.tap do |entity|
196
- entity.username = account[0]
197
- entity.password = account[1]
198
- entity.token = account[2]
199
- end
200
- end
201
-
202
- def add_account(user, password, token)
203
- @accounts << [user, password, token]
204
- end
205
-
206
- def by_username(username)
207
- account = @accounts.find{|account| account[0] == username }
208
- build_entity(account)
209
- end
210
-
211
- def by_token(token)
212
- account = @accounts.find{|account| account[2] == token }
213
- build_entity(account)
214
- end
215
- end
216
-
217
- class AuthorizationPolicy
218
- attr_accessor :grants_holder
219
-
220
- def build_grants(&block)
221
- grants_holder.build_grants(&block)
222
- end
223
-
224
- def grants_for(entity)
225
- build_grants do |builder|
226
- builder.add(:admin)
227
- end
228
- end
229
- end
230
- end
231
- end
3
+ require 'roadforest/authorization/grant-builder'
4
+ require 'roadforest/authorization/grants-holder'
5
+ require 'roadforest/authorization/authentication-chain'
6
+ require 'roadforest/authorization/auth-entity'
7
+ require 'roadforest/authorization/default-authentication-store'
8
+ require 'roadforest/authorization/policy'
9
+ require 'roadforest/authorization/manager'