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.
- data/.gitignore +24 -0
- data/Gemfile +6 -0
- data/README.md +211 -0
- data/Rakefile +23 -0
- data/bin/parse_rspec.rb +167 -0
- data/etc/omf-sfa/omf-sfa-am.yaml +12 -0
- data/examples/exogeni5nodemanifest.rspec +105 -0
- data/examples/instageni5nodemanifest.rspec +150 -0
- data/lib/omf-sfa/am/am-rest/REST_API.md +301 -0
- data/lib/omf-sfa/am/am-rest/account_handler.rb +145 -0
- data/lib/omf-sfa/am/am-rest/am_rest_server.rb +255 -0
- data/lib/omf-sfa/am/am-rest/api_template.html +48 -0
- data/lib/omf-sfa/am/am-rest/config.ru +110 -0
- data/lib/omf-sfa/am/am-rest/resource_handler.rb +178 -0
- data/lib/omf-sfa/am/am-rest/rest_handler.rb +573 -0
- data/lib/omf-sfa/am/am-rest/session_authenticator.rb +130 -0
- data/lib/omf-sfa/am/am-rpc/abstract_rpc_service.rb +60 -0
- data/lib/omf-sfa/am/am-rpc/am_authorizer.rb +161 -0
- data/lib/omf-sfa/am/am-rpc/am_rpc_api.rb +450 -0
- data/lib/omf-sfa/am/am-rpc/am_rpc_service.rb +402 -0
- data/lib/omf-sfa/am/am_liaison.rb +93 -0
- data/lib/omf-sfa/am/am_manager.rb +859 -0
- data/lib/omf-sfa/am/am_runner.rb +108 -0
- data/lib/omf-sfa/am/am_scheduler.rb +146 -0
- data/lib/omf-sfa/am/am_server.rb +194 -0
- data/lib/omf-sfa/am/config.ru +122 -0
- data/lib/omf-sfa/am/credential.rb +145 -0
- data/lib/omf-sfa/am/default_authorizer.rb +44 -0
- data/lib/omf-sfa/am/privilege_credential.rb +76 -0
- data/lib/omf-sfa/am/signature.rb +37 -0
- data/lib/omf-sfa/am/user_credential.rb +56 -0
- data/lib/omf-sfa/am.rb +7 -0
- data/lib/omf-sfa/model/abstract_prop_description.rb +87 -0
- data/lib/omf-sfa/model/model_class_description.rb +145 -0
- data/lib/omf-sfa/model/model_data_prop_description.rb +28 -0
- data/lib/omf-sfa/model/model_obj_prop_description.rb +49 -0
- data/lib/omf-sfa/model/ontology.rb +169 -0
- data/lib/omf-sfa/resource/README.md +24 -0
- data/lib/omf-sfa/resource/channel.rb +49 -0
- data/lib/omf-sfa/resource/comp_group.rb +41 -0
- data/lib/omf-sfa/resource/component_lease.rb +10 -0
- data/lib/omf-sfa/resource/constants.rb +24 -0
- data/lib/omf-sfa/resource/group_component.rb +35 -0
- data/lib/omf-sfa/resource/group_membership.rb +17 -0
- data/lib/omf-sfa/resource/gurn.rb +187 -0
- data/lib/omf-sfa/resource/interface.rb +78 -0
- data/lib/omf-sfa/resource/ip.rb +48 -0
- data/lib/omf-sfa/resource/link.rb +29 -0
- data/lib/omf-sfa/resource/node.rb +75 -0
- data/lib/omf-sfa/resource/oaccount.rb +94 -0
- data/lib/omf-sfa/resource/ocomponent.rb +134 -0
- data/lib/omf-sfa/resource/ogroup.rb +106 -0
- data/lib/omf-sfa/resource/olease.rb +61 -0
- data/lib/omf-sfa/resource/oproperty.rb +178 -0
- data/lib/omf-sfa/resource/oreference.rb +15 -0
- data/lib/omf-sfa/resource/oresource.rb +491 -0
- data/lib/omf-sfa/resource/project.rb +28 -0
- data/lib/omf-sfa/resource/project_membership.rb +13 -0
- data/lib/omf-sfa/resource/sfa_base.rb +544 -0
- data/lib/omf-sfa/resource/user.rb +25 -0
- data/lib/omf-sfa/resource.rb +20 -0
- data/lib/omf-sfa/util/create_sample_testbed.rb +68 -0
- data/lib/omf-sfa/util/load_from_sfa_xml.rb +65 -0
- data/lib/omf-sfa/version.rb +4 -0
- data/lib/omf_sfa.rb +5 -0
- data/omf_sfa.gemspec +46 -0
- data/owl/README +3 -0
- data/owl/ben-6509.rdf +1377 -0
- data/owl/ben-dell.rdf +586 -0
- data/owl/ben-dtn.rdf +1698 -0
- data/owl/ben.rdf +1335 -0
- data/owl/collections.owl +309 -0
- data/owl/compute.owl +1486 -0
- data/owl/domain.owl +444 -0
- data/owl/dtn.owl +1165 -0
- data/owl/ec2.owl +385 -0
- data/owl/ethernet.owl +466 -0
- data/owl/eucalyptus.owl +431 -0
- data/owl/id-mp-Request1.rdf +247 -0
- data/owl/itu-grid.owl +147 -0
- data/owl/kansei.owl +511 -0
- data/owl/layer.owl +645 -0
- data/owl/location.owl +117 -0
- data/owl/mass.rdf +608 -0
- data/owl/nlr.rdf +901 -0
- data/owl/orca.owl +181 -0
- data/owl/planetlab.owl +124 -0
- data/owl/protogeni.owl +467 -0
- data/owl/request-6509-2.rdf +150 -0
- data/owl/request-6509-3.rdf +158 -0
- data/owl/request-6509.rdf +199 -0
- data/owl/request.owl +222 -0
- data/owl/storage.owl +511 -0
- data/owl/topology.owl +608 -0
- data/schema/rspec-v3/ad-common.xsd +269 -0
- data/schema/rspec-v3/ad-reservation.rnc +12 -0
- data/schema/rspec-v3/ad-reservation.rng +28 -0
- data/schema/rspec-v3/ad-reservation.xsd +13 -0
- data/schema/rspec-v3/ad.rnc +151 -0
- data/schema/rspec-v3/ad.xsd +77 -0
- data/schema/rspec-v3/any-extension-schema.xsd +38 -0
- data/schema/rspec-v3/any-extension.rnc +30 -0
- data/schema/rspec-v3/common.rnc +185 -0
- data/schema/rspec-v3/manifest-common.xsd +244 -0
- data/schema/rspec-v3/manifest-request.xsd +95 -0
- data/schema/rspec-v3/manifest.rnc +62 -0
- data/schema/rspec-v3/manifest.xsd +34 -0
- data/schema/rspec-v3/request-common.xsd +219 -0
- data/schema/rspec-v3/request-reservation.rnc +12 -0
- data/schema/rspec-v3/request-reservation.xsd +13 -0
- data/schema/rspec-v3/request.rnc +118 -0
- data/schema/rspec-v3/request.xsd +94 -0
- data/share/assets/css/default.css +147 -0
- data/share/assets/css/rest_api.css +0 -0
- data/share/assets/network.html +28 -0
- data/share/assets/network.js +82 -0
- data/spec/am/am-rest/common.rb +29 -0
- data/spec/am/am-rest/resource_group_handler_XspecX.rb +97 -0
- data/spec/am/am-rest/resource_handler_spec.rb +204 -0
- data/spec/am/am-rpc/sfa_methods_spec.rb +150 -0
- data/spec/am/am_manager_spec.rb +307 -0
- data/spec/am/am_scheduler_spec.rb +57 -0
- data/spec/am/common.rb +24 -0
- data/spec/resource/common.rb +31 -0
- data/spec/resource/node_spec.rb +171 -0
- data/spec/resource/oaccount_spec.rb +92 -0
- data/spec/resource/ocomponent_spec.rb +225 -0
- data/spec/resource/ogroup_spec.rb +93 -0
- data/spec/resource/oresource_spec.rb +208 -0
- data/spec/resource_and_leases_spec.rb +377 -0
- data/test/OLD_FILES/assertion1.xml +117 -0
- data/test/OLD_FILES/greeter_spec.rb +15 -0
- data/test/OLD_FILES/mongo_test.rb +45 -0
- data/test/OLD_FILES/req-sfa-2.xml +6 -0
- data/test/OLD_FILES/req-sfa-g.xml +8 -0
- data/test/OLD_FILES/req-sfa-g2.xml +10 -0
- data/test/OLD_FILES/req-sfa-g3.xml +14 -0
- data/test/OLD_FILES/req-sfa.xml +6 -0
- data/test/OLD_FILES/req1.xml +22 -0
- data/test/OLD_FILES/req1b.xml +15 -0
- data/test/OLD_FILES/rspec-test.xml +1867 -0
- data/test/OLD_FILES/test.rb +67 -0
- data/test/OLD_FILES/test2.rb +32 -0
- data/test/am/am_manager_rspec_tests.rb +378 -0
- data/test/am/am_manager_tests.rb +518 -0
- data/test/am/am_scheduler_tests.rb +173 -0
- data/test/resource/olease_test.rb +74 -0
- data/test/sfa_requests/request.xml +5 -0
- data/test/sfa_requests/request1.xml +5 -0
- data/test/sfa_requests/request2.xml +5 -0
- data/test/sfa_requests/request3.xml +5 -0
- 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
|
+
|