roadforest 0.5 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
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'