cf-uaac 1.3.0

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/lib/stub/uaa.rb ADDED
@@ -0,0 +1,485 @@
1
+ #--
2
+ # Cloud Foundry 2012.02.03 Beta
3
+ # Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
4
+ #
5
+ # This product is licensed to you under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this product except in compliance with the License.
7
+ #
8
+ # This product includes a number of subcomponents with
9
+ # separate copyright notices and license terms. Your use of these
10
+ # subcomponents is subject to the terms and conditions of the
11
+ # subcomponent's license, as noted in the LICENSE file.
12
+ #++
13
+
14
+ require 'uaa'
15
+ require 'stub/server'
16
+ require 'stub/scim'
17
+ require 'cli/version'
18
+ require 'pp'
19
+
20
+ module CF::UAA
21
+
22
+ class StubUAAConn < Stub::Base
23
+
24
+ def inject_error(input = nil)
25
+ case server.reply_badly
26
+ when :non_json then reply.text("non-json reply")
27
+ when :bad_json then reply.body = %<{"access_token":"good.access.token" "missed a comma":"there"}>
28
+ when :bad_state then input[:state] = "badstate"
29
+ when :no_token_type then input.delete(:token_type)
30
+ end
31
+ end
32
+
33
+ def valid_token(required_scope)
34
+ return nil unless (ah = request.headers["authorization"]) && (ah = ah.split(' '))[0] =~ /^bearer$/i
35
+ contents = TokenCoder.decode(ah[1])
36
+ contents["scope"], required_scope = Util.arglist(contents["scope"]), Util.arglist(required_scope)
37
+ return contents if required_scope.nil? || !(required_scope & contents["scope"]).empty?
38
+ reply_in_kind(403, error: "insufficient_scope",
39
+ error_description: "required scope #{Util.strlist(required_scope)}")
40
+ nil
41
+ end
42
+
43
+ def ids_to_names(ids); ids ? ids.map { |id| server.scim.name(id) } : [] end
44
+ def names_to_ids(names, rtype); names ? names.map { |name| server.scim.id(name, rtype) } : [] end
45
+ def bad_request(message = nil); reply_in_kind(400, error: "bad request#{message ? ',' : ''} #{message}") end
46
+ def not_found(name = nil); reply_in_kind(404, error: "#{name} not found") end
47
+ def encode_cookie(obj = {}) Util.json_encode64(obj) end
48
+ def decode_cookie(str) Util.json.decode64(str) end
49
+
50
+ def primary_email(emails)
51
+ return unless emails
52
+ emails.each {|e| return e[:value] if e[:type] && e[:type] == "primary"}
53
+ emails[0][:value]
54
+ end
55
+
56
+ def find_user(name, pwd = nil)
57
+ user = server.scim.get_by_name(name, :user, :password, :id, :emails, :username, :groups)
58
+ user if user && (!pwd || user[:password] == pwd)
59
+ end
60
+
61
+ #----------------------------------------------------------------------------
62
+ # miscellaneous endpoints
63
+ #
64
+
65
+ def default_route; reply_in_kind(404, error: "not found", error_description: "unknown path #{request.path}") end
66
+
67
+ route :get, '/favicon.ico' do
68
+ reply.headers[:content_type] = "image/vnd.microsoft.icon"
69
+ reply.body = File.read File.expand_path(File.join(__FILE__, '..', '..', 'lib', 'cli', 'favicon.ico'))
70
+ end
71
+
72
+ route :get, '/' do reply_in_kind "welcome to stub UAA, version #{VERSION}" end
73
+ route :get, '/varz' do reply_in_kind(mem: 0, type: 'UAA', app: { version: VERSION } ) end
74
+ route :get, '/token_key' do reply_in_kind(alg: "none", value: "none") end
75
+
76
+ route :post, '/password/score', "content-type" => %r{application/x-www-form-urlencoded} do
77
+ info = Util.decode_form_to_hash(request.body)
78
+ return bad_request "no password to score" unless pwd = info["password"]
79
+ score = pwd.length > 10 || pwd.length < 0 ? 10 : pwd.length
80
+ reply_in_kind(score: score, requiredScore: 0)
81
+ end
82
+
83
+ route :get, %r{^/userinfo(\?|$)(.*)} do
84
+ return not_found unless (tokn = valid_token("openid")) &&
85
+ (info = server.scim.get(tokn["user_id"], :user, :username, :id, :emails)) && info[:username]
86
+ reply_in_kind(user_id: info[:id], user_name: info[:username], email: primary_email(info[:emails]))
87
+ end
88
+
89
+ route :get, '/login' do
90
+ return reply_in_kind(server.info) unless request.headers["accept"] =~ /text\/html/
91
+ session = decode_cookie(request.cookies["stubsession"]) || {}
92
+ if session["username"]
93
+ page = <<-DATA.gsub(/^ +/, '')
94
+ you are logged in as #{session["username"]}
95
+ <form id='logout' action='login.do' method='get' accept-charset='UTF-8'>
96
+ <input type='submit' name='submit' value='Logout' /></form>
97
+ DATA
98
+ else
99
+ page = <<-DATA.gsub(/^ +/, '')
100
+ <form id='login' action='login.do' method='post' accept-charset='UTF-8'>
101
+ <fieldset><legend>Login</legend><label for='username'>User name:</label>
102
+ <input type='text' name='username' id='username' maxlength='50' />
103
+ <label for='password'>Password:</label>
104
+ <input type='password' name='password' id='password' maxlength='50' />
105
+ <input type='submit' name='submit' value='Login' /></fieldset></form>
106
+ DATA
107
+ end
108
+ reply.html page
109
+ #reply.set_cookie(:stubsession, encode_cookie(session), httponly: nil)
110
+ end
111
+
112
+ route :post, '/login.do', "content-type" => %r{application/x-www-form-urlencoded} do
113
+ creds = Util.decode_form_to_hash(request.body)
114
+ user = find_user(creds['username'], creds['password'])
115
+ reply.headers[:location] = "login"
116
+ reply.status = 302
117
+ reply.set_cookie(:stubsession, encode_cookie(username: user[:username], httponly: nil))
118
+ end
119
+
120
+ route :get, %r{^/logout.do(\?|$)(.*)} do
121
+ query = Util.decode_form_to_hash(match[2])
122
+ reply.headers[:location] = query['redirect_uri'] || "login"
123
+ reply.status = 302
124
+ reply.set_cookie(:stubsession, encode_cookie, max_age: -1)
125
+ end
126
+
127
+
128
+ #----------------------------------------------------------------------------
129
+ # oauth2 endpoints and helpers
130
+ #
131
+
132
+ # current uaa token contents: exp, user_name, scope, email, user_id,
133
+ # client_id, client_authorities, user_authorities
134
+ def token_reply_info(client, scope, user = nil, state = nil, refresh = false)
135
+ interval = client[:access_token_validity] || 3600
136
+ token_body = { jti: SecureRandom.uuid, aud: scope, scope: scope,
137
+ client_id: client[:client_id], exp: interval + Time.now.to_i }
138
+ if user
139
+ token_body[:user_id] = user[:id]
140
+ token_body[:email] = primary_email(user[:emails])
141
+ token_body[:user_name] = user[:username]
142
+ end
143
+ info = { access_token: TokenCoder.encode(token_body, nil, nil, 'none'),
144
+ token_type: "bearer", expires_in: interval, scope: scope}
145
+ info[:state] = state if state
146
+ info[:refresh_token] = "universal_refresh_token" if refresh
147
+ inject_error(info)
148
+ info
149
+ end
150
+
151
+ def auth_client(basic_auth_header)
152
+ ah = basic_auth_header.split(' ')
153
+ return unless ah[0] =~ /^basic$/i
154
+ ah = Base64::strict_decode64(ah[1]).split(':')
155
+ client = server.scim.get_by_name(ah[0], :client)
156
+ client if client && client[:client_secret] == ah[1]
157
+ end
158
+
159
+ def valid_redir_uri?(client, redir_uri)
160
+ t = URI.parse(redir_uri)
161
+ return true unless (ruris = client[:redirect_uris]) && !ruris.empty?
162
+ false unless ruris.each { |reg_uri|
163
+ r = URI.parse(reg_uri)
164
+ return true if r.scheme == t.scheme && r.host == t.host &&
165
+ (!r.port || r.port == t.port) && (!r.path || r.path == t.path)
166
+ }
167
+ end
168
+
169
+ def redir_with_fragment(cburi, params)
170
+ reply.status = 302
171
+ uri = URI.parse(cburi)
172
+ uri.fragment = URI.encode_www_form(params)
173
+ reply.headers[:location] = uri.to_s
174
+ end
175
+
176
+ def redir_with_query(cburi, params)
177
+ reply.status = 302
178
+ uri = URI.parse(cburi)
179
+ uri.query = URI.encode_www_form(params)
180
+ reply.headers[:location] = uri.to_s
181
+ end
182
+
183
+ def redir_err_f(cburi, state, msg); redir_with_fragment(cburi, error: msg, state: state) end
184
+ def redir_err_q(cburi, state, msg); redir_with_query(cburi, error: msg, state: state) end
185
+
186
+ # returns granted scopes
187
+ # TODO: doesn't handle actual user authorization yet
188
+ def calc_scope(client, user, requested_scope)
189
+ possible_scope = ids_to_names(client[user ? :scope : :authorities])
190
+ requested_scope = Util.arglist(requested_scope) || []
191
+ return unless (requested_scope - possible_scope).empty?
192
+ requested_scope = possible_scope if requested_scope.empty?
193
+ granted_scopes = user ? (ids_to_names(user[:groups]) & requested_scope) : requested_scope # handle auto-deny
194
+ Util.strlist(granted_scopes) unless granted_scopes.empty?
195
+ end
196
+
197
+ route [:post, :get], %r{^/oauth/authorize\?(.*)} do
198
+ query = Util.decode_form_to_hash(match[1])
199
+ client = server.scim.get_by_name(query["client_id"], :client)
200
+ cburi, state = query["redirect_uri"], query["state"]
201
+
202
+ # if invalid client_id or redir_uri: inform resource owner, do not redirect
203
+ unless client && valid_redir_uri?(client, cburi)
204
+ return bad_request "invalid client_id or redirect_uri"
205
+ end
206
+ if query["response_type"] == 'token'
207
+ unless client[:authorized_grant_types].include?("implicit")
208
+ return redir_err_f(cburi, state, "unauthorized_client")
209
+ end
210
+ if request.method == "post"
211
+ unless request.headers["content-type"] =~ %r{application/x-www-form-urlencoded} &&
212
+ (creds = Util.decode_form_to_hash(request.body)) &&
213
+ creds["source"] && creds["source"] == "credentials"
214
+ return redir_err_f(cburi, state, "invalid_request")
215
+ end
216
+ unless user = find_user(creds["username"], creds["password"])
217
+ return redir_err_f(cburi, state, "access_denied")
218
+ end
219
+ else
220
+ return reply.status = 501 # TODO: how to authN user and ask for authorizations?
221
+ end
222
+ unless (granted_scope = calc_scope(client, user, query["scope"]))
223
+ return redir_err_f(cburi, state, "invalid_scope")
224
+ end
225
+ # TODO: how to stub any remaining scopes that are not auto-approve?
226
+ return redir_with_fragment(cburi, token_reply_info(client, granted_scope, user, query["state"]))
227
+ end
228
+ return redir_err_q(cburi, state, "invalid_request") unless request.method == "get"
229
+ return redir_err_q(cburi, state, "unsupported_response_type") unless query["response_type"] == 'code'
230
+ unless client[:authorized_grant_types].include?("authorization_code")
231
+ return redir_err_f(cburi, state, "unauthorized_client")
232
+ end
233
+ return reply.status = 501 unless query["emphatic_user"] # TODO: how to authN user and ask for authorizations?
234
+ return redir_err_f(cburi, state, "access_denied") unless user = find_user(query["emphatic_user"])
235
+ scope = calc_scope(client, user, query["scope"])
236
+ redir_with_query(cburi, state: state, code: assign_auth_code(client[:id], user[:id], scope, cburi))
237
+ end
238
+
239
+ # if required and optional arrays are given, extra params are an error
240
+ def bad_params?(params, required, optional = nil)
241
+ required.each {|r|
242
+ next if params[r]
243
+ reply.json(400, error: "invalid_request", error_description: "no #{r} in request")
244
+ return true
245
+ }
246
+ return false unless optional
247
+ params.each {|k, v|
248
+ next if required.include?(k) || optional.include?(k)
249
+ reply.json(400, error: "invalid_request", error_description: "#{k} not allowed")
250
+ return true
251
+ }
252
+ false
253
+ end
254
+
255
+ # TODO: need to save scope, timeout, client, redir_url, user_id, etc
256
+ # when redeeming an authcode, code and redir_url must match
257
+ @authcode_store = {}
258
+ class << self; attr_accessor :authcode_store end
259
+ def assign_auth_code(client_id, user_id, scope, redir_uri)
260
+ code = SecureRandom.base64(8)
261
+ raise "authcode collision" if self.class.authcode_store[code]
262
+ self.class.authcode_store[code] = {client_id: client_id, user_id: user_id,
263
+ scope: scope, redir_uri: redir_uri}
264
+ code
265
+ end
266
+ def redeem_auth_code(client_id, redir_uri, code)
267
+ return unless info = self.class.authcode_store.delete(code)
268
+ return unless info[:client_id] == client_id && info[:redir_uri] == redir_uri
269
+ [info[:user_id], info[:scope]]
270
+ end
271
+
272
+ route :post, "/oauth/token", "content-type" => %r{application/x-www-form-urlencoded},
273
+ "accept" => %r{application/json} do
274
+ unless client = auth_client(request.headers["authorization"])
275
+ reply.headers[:www_authenticate] = "basic"
276
+ return reply.json(401, error: "invalid_client")
277
+ end
278
+ return if bad_params?(params = Util.decode_form_to_hash(request.body), ['grant_type'])
279
+ unless client[:authorized_grant_types].include?(params['grant_type'])
280
+ return reply.json(400, error: "unauthorized_client")
281
+ end
282
+ case params.delete('grant_type')
283
+ when "authorization_code"
284
+ # TODO: need authcode store with requested scope, redir_uri must match
285
+ return if bad_params?(params, ['code', 'redirect_uri'], [])
286
+ user_id, scope = redeem_auth_code(client[:id], params['redirect_uri'], params['code'])
287
+ return reply.json(400, error: "invalid_grant") unless user_id && scope
288
+ user = server.scim.get(user, :user, :id, :emails, :username)
289
+ reply.json(token_reply_info(client, scope, user, nil, true))
290
+ when "password"
291
+ return if bad_params?(params, ['username', 'password'], ['scope'])
292
+ user = find_user(params['username'], params['password'])
293
+ return reply.json(400, error: "invalid_grant") unless user
294
+ scope = calc_scope(client, user, params['scope'])
295
+ return reply.json(400, error: "invalid_scope") unless scope
296
+ reply.json(token_reply_info(client, scope, user))
297
+ when "client_credentials"
298
+ return if bad_params?(params, [], ['scope'])
299
+ scope = calc_scope(client, nil, params['scope'])
300
+ return reply.json(400, error: "invalid_scope") unless scope
301
+ reply.json(token_reply_info(client, scope))
302
+ when "refresh_token"
303
+ return if bad_params?(params, ['refresh_token'], ['scope'])
304
+ return reply.json(400, error: "invalid_grant") unless params['refresh_token'] == "universal_refresh_token"
305
+ # TODO: max scope should come from refresh token, or user from refresh token
306
+ # this should use calc_scope when we know the user
307
+ scope = ids_to_names(client[:scope])
308
+ scope = Util.strlist(Util.arglist(params['scope'], scope) & scope)
309
+ return reply.json(400, error: "invalid_scope") if scope.empty?
310
+ reply.json(token_reply_info(client, scope))
311
+ else
312
+ reply.json(400, error: "unsupported_grant_type")
313
+ end
314
+ inject_error
315
+ end
316
+
317
+ route :post, "/alternate/oauth/token", "content-type" => %r{application/x-www-form-urlencoded},
318
+ "accept" => %r{application/json} do
319
+ request.path.replace("/oauth/token")
320
+ server.info.delete(:token_endpoint) # this indicates this was executed for a unit test
321
+ process
322
+ end
323
+
324
+ #----------------------------------------------------------------------------
325
+ # client endpoints
326
+ #
327
+ def client_to_scim(info)
328
+ ['authorities', 'scope', 'auto_approve_scope'].each { |a| info[a] = names_to_ids(info[a], :group) if info.key?(a) }
329
+ info
330
+ end
331
+
332
+ def scim_to_client(info)
333
+ [:authorities, :scope, :auto_approve_scope].each { |a| info[a] = ids_to_names(info[a]) if info.key?(a) }
334
+ info.delete(:id)
335
+ info
336
+ end
337
+
338
+ route :get, %r{^/oauth/clients(\?|$)(.*)} do
339
+ return unless valid_token("clients.read")
340
+ info, _ = server.scim.find(:client)
341
+ reply_in_kind(info.each_with_object({}) {|c, o| o[c[:client_id]] = scim_to_client(c)})
342
+ end
343
+
344
+ route :post, '/oauth/clients', "content-type" => %r{application/json} do
345
+ return unless valid_token("clients.write")
346
+ id = server.scim.add(:client, client_to_scim(Util.json_parse(request.body, :down)))
347
+ reply_in_kind scim_to_client(server.scim.get(id, :client, *StubScim::VISIBLE_ATTRS[:client]))
348
+ end
349
+
350
+ route :put, %r{^/oauth/clients/([^/]+)$}, "content-type" => %r{application/json} do
351
+ return unless valid_token("clients.write")
352
+ info = client_to_scim(Util.json_parse(request.body, :down))
353
+ server.scim.update(server.scim.id(match[1], :client), info)
354
+ reply.json(scim_to_client(info))
355
+ end
356
+
357
+ route :get, %r{^/oauth/clients/([^/]+)$} do
358
+ return unless valid_token("clients.read")
359
+ return not_found(match[1]) unless client = server.scim.get_by_name(match[1], :client, *StubScim::VISIBLE_ATTRS[:client])
360
+ reply_in_kind(scim_to_client(client))
361
+ end
362
+
363
+ route :delete, %r{^/oauth/clients/([^/]+)$} do
364
+ return unless valid_token("clients.write")
365
+ return not_found(match[1]) unless server.scim.remove(server.scim.id(match[1], :client))
366
+ end
367
+
368
+ route :put, %r{^/oauth/clients/([^/]+)/secret$}, "content-type" => %r{application/json} do
369
+ info = Util.json_parse(request.body, :down)
370
+ if oldsecret = info['oldsecret']
371
+ return unless valid_token("clients.secret")
372
+ return not_found(match[1]) unless client = server.scim.get(match[1], :client, :client_secret)
373
+ return bad_request("old secret does not match") unless oldsecret == client[:client_secret]
374
+ else
375
+ return unless valid_token("uaa.admin")
376
+ end
377
+ return bad_request("no new secret given") unless info['secret']
378
+ server.scim.set_hidden_attr(match[1], :client_secret, info['secret'])
379
+ reply.json(status: "ok", message: "secret updated")
380
+ end
381
+
382
+ #----------------------------------------------------------------------------
383
+ # users and groups endpoints
384
+ #
385
+ route :post, %r{^/(Users|Groups)$}, "content-type" => %r{application/json} do
386
+ return unless valid_token("scim.write")
387
+ rtype = match[1] == "Users"? :user : :group
388
+ id = server.scim.add(rtype, Util.json_parse(request.body, :down))
389
+ server.auto_groups.each {|g| server.scim.add_member(g, id)} if rtype == :user && server.auto_groups
390
+ reply_in_kind server.scim.get(id, rtype, *StubScim::VISIBLE_ATTRS[rtype])
391
+ end
392
+
393
+ route :put, %r{^/(Users|Groups)/([^/]+)$}, "content-type" => %r{application/json} do
394
+ return unless valid_token("scim.write")
395
+ rtype = match[1] == "Users"? :user : :group
396
+ id = server.scim.update(match[2], Util.json_parse(request.body, :down), request.headers[:match_if], rtype)
397
+ reply_in_kind server.scim.get(id, rtype, *StubScim::VISIBLE_ATTRS[rtype])
398
+ end
399
+
400
+ def sanitize_int(arg, default, min, max = nil)
401
+ return default if arg.nil?
402
+ return unless arg.to_i.to_s == arg && (i = arg.to_i) >= min
403
+ max && i > max ? max : i
404
+ end
405
+
406
+ def page_query(rtype, query, attrs)
407
+ if query['attributes']
408
+ attrs = attrs & Util.arglist(query['attributes']).each_with_object([]) {|a, o|
409
+ o << a.to_sym if StubScim::ATTR_NAMES.include?(a = a.downcase)
410
+ }
411
+ end
412
+ start = sanitize_int(query['startindex'], 1, 1)
413
+ count = sanitize_int(query['count'], 15, 1, 3000)
414
+ return bad_request("invalid startIndex or count") unless start && count
415
+ info, total = server.scim.find(rtype, start - 1, count, query['filter'], attrs)
416
+ reply_in_kind(resources: info, itemsPerPage: info.length, startIndex: start, totalResults: total)
417
+ end
418
+
419
+ route :get, %r{^/(Users|Groups)(\?|$)(.*)} do
420
+ return unless valid_token("scim.read")
421
+ rtype = match[1] == "Users"? :user : :group
422
+ page_query(rtype, Util.decode_form_to_hash(match[3], :down), StubScim::VISIBLE_ATTRS[rtype])
423
+ end
424
+
425
+ route :get, %r{^/(Users|Groups)/([^/]+)$} do
426
+ return unless valid_token("scim.read")
427
+ rtype = match[1] == "Users"? :user : :group
428
+ return not_found(match[2]) unless obj = server.scim.get(match[2], rtype, *StubScim::VISIBLE_ATTRS[rtype])
429
+ reply_in_kind(obj)
430
+ end
431
+
432
+ route :delete, %r{^/(Users|Groups)/([^/]+)$} do
433
+ return unless valid_token("scim.write")
434
+ not_found(match[2]) unless server.scim.remove(match[2], match[1] == "Users"? :user : :group)
435
+ end
436
+
437
+ route :put, %r{^/Users/([^/]+)/password$}, "content-type" => %r{application/json} do
438
+ info = Util.json_parse(request.body, :down)
439
+ if oldpwd = info['oldpassword']
440
+ return unless valid_token("password.write")
441
+ return not_found(match[1]) unless user = server.scim.get(match[1], :user, :password)
442
+ return bad_request("old password does not match") unless oldpwd == user[:password]
443
+ else
444
+ return unless valid_token("scim.write")
445
+ end
446
+ return bad_request("no new password given") unless newpwd = info['password']
447
+ server.scim.set_hidden_attr(match[1], :password, newpwd)
448
+ reply.json(status: "ok", message: "password updated")
449
+ end
450
+
451
+ route :get, %r{^/ids/Users(\?|$)(.*)} do
452
+ page_query(:user, Util.decode_form_to_hash(match[2], :down), [:username, :id])
453
+ end
454
+
455
+ end
456
+
457
+ class StubUAA < Stub::Server
458
+
459
+ attr_accessor :reply_badly
460
+ attr_reader :scim, :auto_groups
461
+
462
+ def initialize(boot_client = "admin", boot_secret = "adminsecret", logger = Util.default_logger)
463
+ @scim = StubScim.new
464
+ @auto_groups = ["password.write", "openid"]
465
+ .each_with_object([]) { |g, o| o << @scim.add(:group, 'displayname' => g) }
466
+ ["scim.read", "scim.write", "uaa.resource"]
467
+ .each { |g| @scim.add(:group, 'displayname' => g) }
468
+ gids = ["clients.write", "clients.read", "clients.secret", "uaa.admin"]
469
+ .each_with_object([]) { |s, o| o << @scim.add(:group, 'displayname' => s) }
470
+ @scim.add(:client, 'client_id' => boot_client, 'client_secret' => boot_secret,
471
+ 'authorized_grant_types' => ["client_credentials"], 'authorities' => gids,
472
+ 'access_token_validity' => 60 * 60 * 24 * 7)
473
+ @scim.add(:client, 'client_id' => "vmc", 'authorized_grant_types' => ["implicit"],
474
+ 'scope' => [@scim.id("openid", :group), @scim.id("password.write", :group)],
475
+ 'access_token_validity' => 5 * 60 )
476
+ info = { commit_id: "not implemented",
477
+ app: {name: "Stub UAA", version: CLI_VERSION, description: "User Account and Authentication Service, test server"},
478
+ prompts: {username: ["text", "Username"], password: ["password","Password"]} }
479
+ super(StubUAAConn, logger, info)
480
+ end
481
+
482
+ end
483
+
484
+ end
485
+