omf_sfa 0.1.1

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 (152) hide show
  1. data/.gitignore +24 -0
  2. data/Gemfile +6 -0
  3. data/README.md +211 -0
  4. data/Rakefile +23 -0
  5. data/bin/parse_rspec.rb +167 -0
  6. data/etc/omf-sfa/omf-sfa-am.yaml +12 -0
  7. data/examples/exogeni5nodemanifest.rspec +105 -0
  8. data/examples/instageni5nodemanifest.rspec +150 -0
  9. data/lib/omf-sfa/am/am-rest/REST_API.md +301 -0
  10. data/lib/omf-sfa/am/am-rest/account_handler.rb +145 -0
  11. data/lib/omf-sfa/am/am-rest/am_rest_server.rb +255 -0
  12. data/lib/omf-sfa/am/am-rest/api_template.html +48 -0
  13. data/lib/omf-sfa/am/am-rest/config.ru +110 -0
  14. data/lib/omf-sfa/am/am-rest/resource_handler.rb +178 -0
  15. data/lib/omf-sfa/am/am-rest/rest_handler.rb +573 -0
  16. data/lib/omf-sfa/am/am-rest/session_authenticator.rb +130 -0
  17. data/lib/omf-sfa/am/am-rpc/abstract_rpc_service.rb +60 -0
  18. data/lib/omf-sfa/am/am-rpc/am_authorizer.rb +161 -0
  19. data/lib/omf-sfa/am/am-rpc/am_rpc_api.rb +450 -0
  20. data/lib/omf-sfa/am/am-rpc/am_rpc_service.rb +402 -0
  21. data/lib/omf-sfa/am/am_liaison.rb +93 -0
  22. data/lib/omf-sfa/am/am_manager.rb +859 -0
  23. data/lib/omf-sfa/am/am_runner.rb +108 -0
  24. data/lib/omf-sfa/am/am_scheduler.rb +146 -0
  25. data/lib/omf-sfa/am/am_server.rb +194 -0
  26. data/lib/omf-sfa/am/config.ru +122 -0
  27. data/lib/omf-sfa/am/credential.rb +145 -0
  28. data/lib/omf-sfa/am/default_authorizer.rb +44 -0
  29. data/lib/omf-sfa/am/privilege_credential.rb +76 -0
  30. data/lib/omf-sfa/am/signature.rb +37 -0
  31. data/lib/omf-sfa/am/user_credential.rb +56 -0
  32. data/lib/omf-sfa/am.rb +7 -0
  33. data/lib/omf-sfa/model/abstract_prop_description.rb +87 -0
  34. data/lib/omf-sfa/model/model_class_description.rb +145 -0
  35. data/lib/omf-sfa/model/model_data_prop_description.rb +28 -0
  36. data/lib/omf-sfa/model/model_obj_prop_description.rb +49 -0
  37. data/lib/omf-sfa/model/ontology.rb +169 -0
  38. data/lib/omf-sfa/resource/README.md +24 -0
  39. data/lib/omf-sfa/resource/channel.rb +49 -0
  40. data/lib/omf-sfa/resource/comp_group.rb +41 -0
  41. data/lib/omf-sfa/resource/component_lease.rb +10 -0
  42. data/lib/omf-sfa/resource/constants.rb +24 -0
  43. data/lib/omf-sfa/resource/group_component.rb +35 -0
  44. data/lib/omf-sfa/resource/group_membership.rb +17 -0
  45. data/lib/omf-sfa/resource/gurn.rb +187 -0
  46. data/lib/omf-sfa/resource/interface.rb +78 -0
  47. data/lib/omf-sfa/resource/ip.rb +48 -0
  48. data/lib/omf-sfa/resource/link.rb +29 -0
  49. data/lib/omf-sfa/resource/node.rb +75 -0
  50. data/lib/omf-sfa/resource/oaccount.rb +94 -0
  51. data/lib/omf-sfa/resource/ocomponent.rb +134 -0
  52. data/lib/omf-sfa/resource/ogroup.rb +106 -0
  53. data/lib/omf-sfa/resource/olease.rb +61 -0
  54. data/lib/omf-sfa/resource/oproperty.rb +178 -0
  55. data/lib/omf-sfa/resource/oreference.rb +15 -0
  56. data/lib/omf-sfa/resource/oresource.rb +491 -0
  57. data/lib/omf-sfa/resource/project.rb +28 -0
  58. data/lib/omf-sfa/resource/project_membership.rb +13 -0
  59. data/lib/omf-sfa/resource/sfa_base.rb +544 -0
  60. data/lib/omf-sfa/resource/user.rb +25 -0
  61. data/lib/omf-sfa/resource.rb +20 -0
  62. data/lib/omf-sfa/util/create_sample_testbed.rb +68 -0
  63. data/lib/omf-sfa/util/load_from_sfa_xml.rb +65 -0
  64. data/lib/omf-sfa/version.rb +4 -0
  65. data/lib/omf_sfa.rb +5 -0
  66. data/omf_sfa.gemspec +46 -0
  67. data/owl/README +3 -0
  68. data/owl/ben-6509.rdf +1377 -0
  69. data/owl/ben-dell.rdf +586 -0
  70. data/owl/ben-dtn.rdf +1698 -0
  71. data/owl/ben.rdf +1335 -0
  72. data/owl/collections.owl +309 -0
  73. data/owl/compute.owl +1486 -0
  74. data/owl/domain.owl +444 -0
  75. data/owl/dtn.owl +1165 -0
  76. data/owl/ec2.owl +385 -0
  77. data/owl/ethernet.owl +466 -0
  78. data/owl/eucalyptus.owl +431 -0
  79. data/owl/id-mp-Request1.rdf +247 -0
  80. data/owl/itu-grid.owl +147 -0
  81. data/owl/kansei.owl +511 -0
  82. data/owl/layer.owl +645 -0
  83. data/owl/location.owl +117 -0
  84. data/owl/mass.rdf +608 -0
  85. data/owl/nlr.rdf +901 -0
  86. data/owl/orca.owl +181 -0
  87. data/owl/planetlab.owl +124 -0
  88. data/owl/protogeni.owl +467 -0
  89. data/owl/request-6509-2.rdf +150 -0
  90. data/owl/request-6509-3.rdf +158 -0
  91. data/owl/request-6509.rdf +199 -0
  92. data/owl/request.owl +222 -0
  93. data/owl/storage.owl +511 -0
  94. data/owl/topology.owl +608 -0
  95. data/schema/rspec-v3/ad-common.xsd +269 -0
  96. data/schema/rspec-v3/ad-reservation.rnc +12 -0
  97. data/schema/rspec-v3/ad-reservation.rng +28 -0
  98. data/schema/rspec-v3/ad-reservation.xsd +13 -0
  99. data/schema/rspec-v3/ad.rnc +151 -0
  100. data/schema/rspec-v3/ad.xsd +77 -0
  101. data/schema/rspec-v3/any-extension-schema.xsd +38 -0
  102. data/schema/rspec-v3/any-extension.rnc +30 -0
  103. data/schema/rspec-v3/common.rnc +185 -0
  104. data/schema/rspec-v3/manifest-common.xsd +244 -0
  105. data/schema/rspec-v3/manifest-request.xsd +95 -0
  106. data/schema/rspec-v3/manifest.rnc +62 -0
  107. data/schema/rspec-v3/manifest.xsd +34 -0
  108. data/schema/rspec-v3/request-common.xsd +219 -0
  109. data/schema/rspec-v3/request-reservation.rnc +12 -0
  110. data/schema/rspec-v3/request-reservation.xsd +13 -0
  111. data/schema/rspec-v3/request.rnc +118 -0
  112. data/schema/rspec-v3/request.xsd +94 -0
  113. data/share/assets/css/default.css +147 -0
  114. data/share/assets/css/rest_api.css +0 -0
  115. data/share/assets/network.html +28 -0
  116. data/share/assets/network.js +82 -0
  117. data/spec/am/am-rest/common.rb +29 -0
  118. data/spec/am/am-rest/resource_group_handler_XspecX.rb +97 -0
  119. data/spec/am/am-rest/resource_handler_spec.rb +204 -0
  120. data/spec/am/am-rpc/sfa_methods_spec.rb +150 -0
  121. data/spec/am/am_manager_spec.rb +307 -0
  122. data/spec/am/am_scheduler_spec.rb +57 -0
  123. data/spec/am/common.rb +24 -0
  124. data/spec/resource/common.rb +31 -0
  125. data/spec/resource/node_spec.rb +171 -0
  126. data/spec/resource/oaccount_spec.rb +92 -0
  127. data/spec/resource/ocomponent_spec.rb +225 -0
  128. data/spec/resource/ogroup_spec.rb +93 -0
  129. data/spec/resource/oresource_spec.rb +208 -0
  130. data/spec/resource_and_leases_spec.rb +377 -0
  131. data/test/OLD_FILES/assertion1.xml +117 -0
  132. data/test/OLD_FILES/greeter_spec.rb +15 -0
  133. data/test/OLD_FILES/mongo_test.rb +45 -0
  134. data/test/OLD_FILES/req-sfa-2.xml +6 -0
  135. data/test/OLD_FILES/req-sfa-g.xml +8 -0
  136. data/test/OLD_FILES/req-sfa-g2.xml +10 -0
  137. data/test/OLD_FILES/req-sfa-g3.xml +14 -0
  138. data/test/OLD_FILES/req-sfa.xml +6 -0
  139. data/test/OLD_FILES/req1.xml +22 -0
  140. data/test/OLD_FILES/req1b.xml +15 -0
  141. data/test/OLD_FILES/rspec-test.xml +1867 -0
  142. data/test/OLD_FILES/test.rb +67 -0
  143. data/test/OLD_FILES/test2.rb +32 -0
  144. data/test/am/am_manager_rspec_tests.rb +378 -0
  145. data/test/am/am_manager_tests.rb +518 -0
  146. data/test/am/am_scheduler_tests.rb +173 -0
  147. data/test/resource/olease_test.rb +74 -0
  148. data/test/sfa_requests/request.xml +5 -0
  149. data/test/sfa_requests/request1.xml +5 -0
  150. data/test/sfa_requests/request2.xml +5 -0
  151. data/test/sfa_requests/request3.xml +5 -0
  152. metadata +601 -0
@@ -0,0 +1,573 @@
1
+
2
+
3
+ require 'nokogiri'
4
+ require 'uuid'
5
+ # require 'omf-sfa/resource/sliver'
6
+ # require 'omf-sfa/resource/node'
7
+ # require 'omf-sfa/resource/link'
8
+ # require 'omf-sfa/resource/interface'
9
+
10
+ require 'set'
11
+ require 'json'
12
+
13
+ require 'omf_base/lobject'
14
+ require 'omf-sfa/am/am_manager'
15
+
16
+
17
+ module OMF::SFA::AM::Rest
18
+
19
+ class RackException < Exception
20
+ attr_reader :reply
21
+
22
+ def initialize(err_code, reason)
23
+ super reason
24
+ body = {:exception => {
25
+ :code => err_code,
26
+ :reason => reason
27
+ }}
28
+ @reply = [err_code, {"Content-Type" => 'text/json'}, body.to_json]
29
+ end
30
+
31
+ end
32
+
33
+ class BadRequestException < RackException
34
+ def initialize(reason)
35
+ super 400, reason
36
+ end
37
+ end
38
+
39
+ class EmptyBodyException < RackException
40
+ def initialize()
41
+ super 400, "Message body is empty"
42
+ end
43
+ end
44
+
45
+ class UnsupportedBodyFormatException < RackException
46
+ def initialize(format = 'unknown')
47
+ super 400, "Message body format '#{format}' is unsupported"
48
+ end
49
+ end
50
+
51
+
52
+ class NotAuthorizedException < RackException
53
+ def initialize(reason)
54
+ super 401, reason
55
+ end
56
+ end
57
+
58
+ class IllegalMethodException < RackException
59
+ def initialize(reason)
60
+ super 403, reason
61
+ end
62
+ end
63
+
64
+ class UnsupportedMethodException < RackException
65
+ def initialize()
66
+ super 403, "Unsupported Method"
67
+ end
68
+ end
69
+
70
+ class UnknownResourceException < RackException
71
+ def initialize(reason)
72
+ super 404, reason
73
+ end
74
+ end
75
+
76
+ class MissingResourceException < RackException
77
+ def initialize(reason)
78
+ super 404, reason
79
+ end
80
+ end
81
+
82
+ class RedirectException < Exception
83
+ attr_reader :path
84
+
85
+ def initialize(path)
86
+ @path = path
87
+ end
88
+ end
89
+
90
+
91
+ class RestHandler < OMF::Base::LObject
92
+ @@service_name = nil
93
+ @@html_template = File::read(File.dirname(__FILE__) + '/api_template.html')
94
+
95
+ def self.set_service_name(name)
96
+ @@service_name = name
97
+ end
98
+
99
+ def self.load_api_template(fname)
100
+ @@html_template = File::read(fname)
101
+ end
102
+
103
+ def self.convert_to_html(body, env, opts = {}, collections = Set.new)
104
+ self.new().convert_to_html(body, env, opts, collections)
105
+ end
106
+
107
+
108
+ def initialize(opts = {})
109
+ #puts "INIT>>> #{am_manager}::#{self}"
110
+ # @am_manager = am_manager
111
+ @opts = opts
112
+ end
113
+
114
+ def call(env)
115
+ begin
116
+ Thread.current[:http_host] = env["HTTP_HOST"]
117
+ req = ::Rack::Request.new(env)
118
+ content_type, body = dispatch(req)
119
+ if req['_format'] == 'html'
120
+ #body = self.class.convert_to_html(body, env, Set.new((@coll_handlers || {}).keys))
121
+ body = convert_to_html(body, env, {}, Set.new((@coll_handlers || {}).keys))
122
+ content_type = 'text/html'
123
+ elsif content_type == 'application/json'
124
+ body = JSON.pretty_generate(body)
125
+ end
126
+ return [200 ,{'Content-Type' => content_type}, body + "\n"]
127
+ rescue RackException => rex
128
+ return rex.reply
129
+ rescue RedirectException => rex
130
+ debug "Redirecting to #{rex.path}"
131
+ return [301, {'Location' => rex.path, "Content-Type" => ""}, ['Next window!']]
132
+ rescue OMF::SFA::AM::AMManagerException => aex
133
+ return RackException.new(400, aex.to_s).reply
134
+ rescue Exception => ex
135
+ body = {
136
+ :error => {
137
+ :reason => ex.to_s,
138
+ :bt => ex.backtrace #.select {|l| !l.start_with?('/') }
139
+ }
140
+ }
141
+ warn "ERROR: #{ex}"
142
+ debug ex.backtrace.join("\n")
143
+ # root = _create_response('error', req = nil)
144
+ # doc = root.document
145
+ # reason = root.add_child(Nokogiri::XML::Element.new('reason', doc))
146
+ # reason.content = ex.to_s
147
+ # reason = root.add_child(Nokogiri::XML::Element.new('bt', doc))
148
+ # reason.content = ex.backtrace.join("\n\t")
149
+ return [500, {"Content-Type" => 'application/json'}, body]
150
+ end
151
+ end
152
+
153
+ def on_get(resource_uri, opts)
154
+ debug 'get: resource_uri: "', resource_uri, '"'
155
+ if resource_uri
156
+ resource = opts[:resource]
157
+ show_resource_status(resource, opts)
158
+ else
159
+ show_resource_list(opts)
160
+ end
161
+ end
162
+
163
+ def on_post(resource_uri, opts)
164
+ #debug 'POST: resource_uri "', resource_uri, '" - ', opts.inspect
165
+ description, format = parse_body(opts, [:json, :form])
166
+ debug 'POST(', resource_uri, '): body(', format, '): "', description, '"'
167
+
168
+ if resource = opts[:resource]
169
+ debug 'POST: Modify ', resource
170
+ modify_resource(resource, description, opts)
171
+ else
172
+ debug 'POST: Create? ', description.class
173
+ if description.is_a? Array
174
+ resources = description.map do |d|
175
+ create_resource(d, opts)
176
+ end
177
+ return show_resources(resources, nil, opts)
178
+ else
179
+ debug 'POST: Create ', resource_uri
180
+ if resource_uri
181
+ if UUID.validate(resource_uri)
182
+ description[:uuid] = resource_uri
183
+ else
184
+ description[:name] = resource_uri
185
+ end
186
+ end
187
+ resource = create_resource(description, opts, resource_uri)
188
+ end
189
+ end
190
+
191
+ if resource
192
+ show_resource_status(resource, opts)
193
+ elsif context = opts[:context]
194
+ show_resource_status(context, opts)
195
+ else
196
+ raise "Report me. Should never get here"
197
+ end
198
+ end
199
+
200
+ def on_delete(resource_uri, opts)
201
+ if resource = opts[:resource]
202
+ if (context = opts[:context])
203
+ remove_resource_from_context(resource, context)
204
+ res = show_resource_status(resource, opts)
205
+ else
206
+ debug "Delete resource #{resource}"
207
+ res = show_deleted_resource(resource.uuid)
208
+ resource.destroy
209
+ end
210
+ else
211
+ # Delete ALL resources of this type
212
+ raise OMF::SFA::AM::Rest::BadRequestException.new "I'm sorry, Dave. I'm afraid I can't do that."
213
+ end
214
+ resource.reload
215
+ return res
216
+ end
217
+
218
+
219
+ def find_handler(path, opts)
220
+ debug "find_handler: path; '#{path}' opts: #{opts}"
221
+ resource_id = opts[:resource_uri] = path.shift
222
+ opts[:resource] = nil
223
+ if resource_id
224
+ resource = opts[:resource] = find_resource(resource_id)
225
+ end
226
+ return self if path.empty?
227
+
228
+ raise OMF::SFA::AM::Rest::UnknownResourceException.new "Unknown resource '#{resource_id}'." unless resource
229
+ opts[:context] = resource
230
+ comp = path.shift
231
+ if (handler = @coll_handlers[comp.to_sym])
232
+ opts[:resource_uri] = path.join('/')
233
+ if handler.is_a? Proc
234
+ return handler.call(path, opts)
235
+ end
236
+ return handler.find_handler(path, opts)
237
+ end
238
+ raise UnknownResourceException.new "Unknown sub collection '#{comp}' for '#{resource_id}:#{resource.class}'."
239
+ end
240
+
241
+
242
+
243
+ protected
244
+
245
+ def modify_resource(resource, description, opts)
246
+ if description[:uuid]
247
+ raise "Can't change uuid" unless description[:uuid] == resource.uuid.to_s
248
+ end
249
+ description.delete(:href)
250
+ resource.update(description) ? resource : nil
251
+ #raise UnsupportedMethodException.new
252
+ end
253
+
254
+
255
+ def create_resource(description, opts, resource_uri = nil)
256
+ debug "Create: #{description.class}--#{description}"
257
+
258
+ if resource_uri
259
+ if UUID.validate(resource_uri)
260
+ description[:uuid] = resource_uri
261
+ else
262
+ description[:name] = resource_uri
263
+ end
264
+ end
265
+
266
+ # Let's find if the resource already exists. If yes, just modify it
267
+ if uuid = description[:uuid]
268
+ debug 'Trying to find resource ', uuid, "'"
269
+ resource = @resource_class.first(uuid: uuid)
270
+ end
271
+ if resource
272
+ modify_resource(resource, description, opts)
273
+ else
274
+ resource = @resource_class.create(description)
275
+ debug "Created: #{resource}"
276
+ end
277
+ if (context = opts[:context])
278
+ add_resource_to_context(resource, context)
279
+ end
280
+ return resource
281
+ end
282
+
283
+ def add_resource_to_context(user, context)
284
+ raise UnsupportedMethodException.new
285
+ end
286
+
287
+ def remove_resource_from_context(user, context)
288
+ raise UnsupportedMethodException.new
289
+ end
290
+
291
+
292
+ # Extract information from the request object and
293
+ # store them in +opts+.
294
+ #
295
+ # Extract information from the request object and
296
+ # store them in +opts+.
297
+ #
298
+ def populate_opts(req, opts)
299
+ path = req.path_info.split('/').select { |p| !p.empty? }
300
+ opts[:target] = find_handler(path, opts)
301
+ rl = req.params.delete('_level')
302
+ opts[:max_level] = rl ? rl.to_i : 0
303
+ #opts[:target].inspect
304
+ opts
305
+ end
306
+
307
+ def parse_body(opts, allowed_formats = [:json, :xml])
308
+ req = opts[:req]
309
+ body = req.body #req.POST
310
+ raise EmptyBodyException.new unless body
311
+ if body.is_a? Hash
312
+ raise UnsupportedBodyFormatException.new('Send body raw, not as form data')
313
+ end
314
+ (body = body.string) if body.is_a? StringIO
315
+ debug 'PARSE_BODY(ct: ', req.content_type, '): ', body.inspect
316
+ unless content_type = req.content_type
317
+ body.strip!
318
+ if ['/', '{', '['].include?(body[0])
319
+ content_type = 'application/json'
320
+ else
321
+ if body.empty?
322
+ params = req.params.inject({}){|h,(k,v)| h[k.to_sym] = v; h}
323
+ if allowed_formats.include?(:json)
324
+ return [params, :json]
325
+ elsif allowed_formats.include?(:form)
326
+ return [params, :form]
327
+ end
328
+ end
329
+ # default is XML
330
+ content_type = 'text/xml'
331
+ end
332
+ end
333
+ begin
334
+ case content_type
335
+ when 'application/json'
336
+ raise UnsupportedBodyFormatException.new(:json) unless allowed_formats.include?(:json)
337
+ jb = JSON.parse(body)
338
+ return [_rec_sym_keys(jb), :json]
339
+ when 'text/xml'
340
+ xb = Nokogiri::XML(body)
341
+ raise UnsupportedBodyFormatException.new(:xml) unless allowed_formats.include?(:xml)
342
+ return [xb, :xml]
343
+ when 'application/x-www-form-urlencoded'
344
+ raise UnsupportedBodyFormatException.new(:xml) unless allowed_formats.include?(:form)
345
+ fb = req.POST
346
+ #puts "FORM: #{fb.inspect}"
347
+ return [fb, :form]
348
+ end
349
+ rescue Exception => ex
350
+ raise BadRequestException.new "Problems parsing body (#{ex})"
351
+ end
352
+ raise UnsupportedBodyFormatException.new(content_type)
353
+ end
354
+
355
+ private
356
+ # Don't override
357
+
358
+
359
+ def dispatch(req)
360
+ opts = {}
361
+ populate_opts(req, opts)
362
+ opts[:req] = req
363
+ #puts "OPTS>>>> #{opts.inspect}"
364
+ method = req.request_method
365
+ target = opts[:target] #|| self
366
+ resource_uri = opts[:resource_uri]
367
+ case method
368
+ when 'GET'
369
+ res = target.on_get(resource_uri, opts)
370
+ when 'PUT'
371
+ res = target.on_put(resource_uri, opts)
372
+ when 'POST'
373
+ res = target.on_post(resource_uri, opts)
374
+ when 'DELETE'
375
+ res = target.on_delete(resource_uri, opts)
376
+ else
377
+ raise IllegalMethodException.new method
378
+ end
379
+ end
380
+
381
+ def show_resource_status(resource, opts)
382
+ if resource
383
+ about = opts[:req].path
384
+ props = resource.to_hash({}, :max_level => opts[:max_level])
385
+ props.delete(:type)
386
+ res = {
387
+ #:about => about,
388
+ :type => resource.resource_type,
389
+ }.merge!(props)
390
+ #res = {"#{resource.resource_type}_response" => res}
391
+ else
392
+ res = {:error => 'Unknown resource'}
393
+ end
394
+
395
+ ['application/json', res]
396
+ end
397
+
398
+
399
+
400
+
401
+ def find_resource(resource_uri, description = {})
402
+ descr = description.dup
403
+ descr.delete(:resource_uri)
404
+ if UUID.validate(resource_uri)
405
+ descr[:uuid] = resource_uri
406
+ else
407
+ descr[:name] = resource_uri
408
+ end
409
+ if resource_uri.start_with?('urn')
410
+ descr[:urn] = resource_uri
411
+ end
412
+ #authenticator = Thread.current["authenticator"]
413
+ debug "Finding #{@resource_class}.first(#{descr})"
414
+ @resource_class.first(descr)
415
+ end
416
+
417
+ def show_resource_list(opts)
418
+ # authenticator = Thread.current["authenticator"]
419
+ resources = @resource_class.all()
420
+ show_resources(resources, nil, opts)
421
+ end
422
+
423
+ def show_resources(resources, resource_name, opts)
424
+ #hopts = {max_level: opts[:max_level], level: 1}
425
+ hopts = {max_level: opts[:max_level], level: 0}
426
+ objs = {}
427
+ res_hash = resources.map do |a|
428
+ a.to_hash(objs, hopts)
429
+ #a.to_hash_brief(:href_use_class_prefix => true)
430
+ end
431
+ if resource_name
432
+ prefix = about = opts[:req].path
433
+ res = {
434
+ #:about => opts[:req].path,
435
+ resource_name => res_hash
436
+ }
437
+ else
438
+ res = res_hash
439
+ end
440
+ ['application/json', res]
441
+ end
442
+
443
+ def show_deleted_resource(uuid)
444
+ res = {
445
+ uuid: uuid,
446
+ deleted: true
447
+ }
448
+ ['application/json', res]
449
+ end
450
+
451
+ def show_deleted_resources(uuid_a)
452
+ res = {
453
+ uuids: uuid_a,
454
+ deleted: true
455
+ }
456
+ ['application/json', res]
457
+ end
458
+
459
+ # Recursively Symbolize keys of hash
460
+ #
461
+ def _rec_sym_keys(array_or_hash)
462
+ if array_or_hash.is_a? Array
463
+ return array_or_hash.map {|e| e.is_a?(Hash) ? _rec_sym_keys(e) : e }
464
+ end
465
+
466
+ h = {}
467
+ array_or_hash.each do |k, v|
468
+ if v.is_a? Hash
469
+ v = _rec_sym_keys(v)
470
+ elsif v.is_a? Array
471
+ v = v.map {|e| e.is_a?(Hash) ? _rec_sym_keys(e) : e }
472
+ end
473
+ h[k.to_sym] = v
474
+ end
475
+ h
476
+ end
477
+
478
+ public
479
+ def convert_to_html(body, env, opts, collections = Set.new)
480
+ req = ::Rack::Request.new(env)
481
+ opts = {
482
+ collections: collections,
483
+ level: 0,
484
+ href_prefix: "#{req.path}/"
485
+ }.merge(opts)
486
+ tmpl = html_template()
487
+ tmpl = tmpl.gsub('##JS##', JSON.pretty_generate(body))
488
+
489
+ #h1 = "<h1>#{@@service_name || env["HTTP_HOST"]}</h1>"
490
+ tmpl = tmpl.gsub('##TITLE##', @@service_name || env["HTTP_HOST"])
491
+ path = req.path.split('/').select { |p| !p.empty? }
492
+ h2 = ["<a href='/?_format=html&_level=0'>ROOT</a>"]
493
+ path.each_with_index do |s, i|
494
+ h2 << "<a href='/#{path[0 .. i].join('/')}?_format=html&_level=#{i % 2 ? 0 : 1}'>#{s}</a>"
495
+ end
496
+ tmpl = tmpl.gsub('##SERVICE##', h2.join('/'))
497
+
498
+ res = []
499
+ _convert_obj_to_html(body, nil, res, opts)
500
+ tmpl.gsub('##CONTENT##', res.join("\n"))
501
+
502
+ end
503
+
504
+ def html_template()
505
+ @@html_template
506
+ end
507
+
508
+ protected
509
+ def _convert_obj_to_html(obj, ref_name, res, opts)
510
+ klass = obj.class
511
+ #puts ">>>> #{obj.class}::#{obj}"
512
+ if obj.is_a? Array
513
+ if obj.empty?
514
+ res << '<span class="empty">empty</span>'
515
+ else
516
+ res << '<ul>'
517
+ _convert_array_to_html(obj, ref_name, res, opts)
518
+ res << '</ul>'
519
+ end
520
+ elsif obj.is_a? Hash
521
+ res << '<ul>'
522
+ _convert_hash_to_html(obj, ref_name, res, opts)
523
+ res << '</ul>'
524
+ else
525
+ if obj.to_s.start_with? 'http://'
526
+ res << _convert_link_to_html(obj)
527
+ else
528
+ res << " <span class='value'>#{obj}</span> "
529
+ end
530
+ end
531
+ end
532
+
533
+ def _convert_array_to_html(array, ref_name, res, opts)
534
+ opts = opts.merge(level: opts[:level] + 1)
535
+ array.each do |obj|
536
+ #puts "AAA>>>> #{obj}::#{opts}"
537
+ name = nil
538
+ if obj.is_a? Hash
539
+ if name = obj[:name] || obj[:uuid]
540
+ res << "<li><span class='key'>#{_convert_link_to_html obj[:href], name}:</span>"
541
+ else
542
+ res << "<li>#{_convert_link_to_html obj['href']}:"
543
+ end
544
+ else
545
+ res << '<li>'
546
+ end
547
+ _convert_obj_to_html(obj, ref_name, res, opts)
548
+ res << '</li>'
549
+ end
550
+ end
551
+
552
+ def _convert_hash_to_html(hash, ref_name, res, opts)
553
+ #puts ">>>> #{hash}::#{opts}"
554
+ hash.each do |key, obj|
555
+ #key = "#{key}-#{opts[:level]}-#{opts[:collections].to_a.inspect}"
556
+ if opts[:level] == 0 && opts[:collections].include?(key.to_sym)
557
+ key = _convert_link_to_html "#{opts[:href_prefix]}#{key}", key
558
+ end
559
+ res << "<li><span class='key'>#{key}:</span>"
560
+ _convert_obj_to_html(obj, key, res, opts)
561
+ res << '</li>'
562
+ end
563
+ end
564
+
565
+ def _convert_link_to_html(href, text = nil)
566
+ h = href.is_a?(URI) ? href.to_s : "#{href}?_format=html&_level=1"
567
+ "<a href='#{h}'>#{text || href}</a>"
568
+ end
569
+
570
+ end
571
+
572
+ end
573
+
@@ -0,0 +1,130 @@
1
+
2
+ require 'omf_base/lobject'
3
+ require 'rack'
4
+
5
+
6
+ module OMF::SFA::AM::Rest
7
+ class SessionAuthenticator < OMF::Base::LObject
8
+
9
+ def self.active?
10
+ @@active
11
+ end
12
+
13
+ def self.authenticated?
14
+ self[:authenticated]
15
+ end
16
+
17
+ def self.authenticate
18
+ self[:authenticated] = true
19
+ self[:valid_until] = Time.now + @@expire_after
20
+ end
21
+
22
+ def self.logout
23
+ self[:authenticated] = false
24
+ end
25
+
26
+ @@store = {}
27
+
28
+ def self.[](key)
29
+ (@@store[key] || {})[:value]
30
+ end
31
+
32
+ def self.[]=(key, value)
33
+ @@store[key] = {:value => value, :time => Time.now } # add time for GC
34
+ end
35
+
36
+ @@active = false
37
+ # Expire authenticated session after being idle for that many seconds
38
+ @@expire_after = 2592000
39
+
40
+ #
41
+ # opts -
42
+ # :no_session - Array of regexp to ignore
43
+ #
44
+ def initialize(app, opts = {})
45
+ @app = app
46
+ @opts = opts
47
+ @opts[:no_session] = (@opts[:no_session] || []).map { |s| Regexp.new(s) }
48
+ if @opts[:expire_after]
49
+ @@expire_after = @opts[:expire_after]
50
+ end
51
+ @@active = true
52
+ end
53
+
54
+
55
+ def call(env)
56
+ #puts env.keys.inspect
57
+ req = ::Rack::Request.new(env)
58
+ sid = nil
59
+ path_info = req.path_info
60
+ #puts "REQUEST(#{self.object_id}): #{path_info}"
61
+
62
+ unless @opts[:no_session].find {|rx| rx.match(path_info) }
63
+ unless sid = req.cookies['sid']
64
+ sid = "s#{(rand * 10000000).to_i}_#{(rand * 10000000).to_i}"
65
+ debug "Setting session for '#{req.path_info}' to '#{sid}'"
66
+ end
67
+ Thread.current["sessionID"] = sid
68
+ # If 'login_url' is defined, check if this session is authenticated
69
+ login_url = @opts[:login_url]
70
+ if login_url
71
+ unless login_url == req.path_info
72
+ puts ">>>>>> CHECKING FOR LOGIN #{login_url.class}"
73
+ if authenticated = self.class[:authenticated]
74
+ # Check if it hasn't imed out
75
+ if self.class[:valid_until] < Time.now
76
+ debug "Session '#{sid}' expired"
77
+ authenticated = false
78
+ end
79
+ end
80
+ unless authenticated
81
+ return [301, {'Location' => login_url, "Content-Type" => ""}, ['Login first']]
82
+ end
83
+ end
84
+ else
85
+ init_fake_root
86
+ end
87
+ self.class[:valid_until] = Time.now + @@expire_after
88
+ end
89
+
90
+ status, headers, body = @app.call(env)
91
+ if sid
92
+ headers['Set-Cookie'] = "sid=#{sid}" ##: name2=value2; Expires=Wed, 09-Jun-2021 ]
93
+ end
94
+ [status, headers, body]
95
+ end
96
+
97
+ @@def_authenticator = nil
98
+
99
+ def init_fake_root
100
+ unless @@def_authenticator
101
+ auth = {}
102
+ [
103
+ # ACCOUNT
104
+ :can_create_account?, # ()
105
+ :can_view_account?, # (account)
106
+ :can_renew_account?, # (account, until)
107
+ :can_close_account?, # (account)
108
+ # RESOURCE
109
+ :can_create_resource?, # (resource_descr, type)
110
+ :can_view_resource?, # (resource)
111
+ :can_release_resource?, # (resource)
112
+ # LEASE
113
+ :can_create_lease?, # (lease)
114
+ :can_view_lease?, # (lease)
115
+ :can_modify_lease?, # (lease)
116
+ :can_release_lease?, # (lease)
117
+ ].each do |m| auth[m] = true end
118
+ require 'omf-sfa/am/default_authorizer'
119
+ @@def_authenticator = OMF::SFA::AM::DefaultAuthorizer.new(auth)
120
+ end
121
+ Thread.current["authenticator"] = @@def_authenticator
122
+ end
123
+
124
+ end # class
125
+
126
+ end # module
127
+
128
+
129
+
130
+