cf-uaa-lib 3.11.0 → 3.14.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/gem-push.yml +29 -0
- data/.github/workflows/ruby.yml +27 -0
- data/README.md +1 -1
- data/cf-uaa-lib.gemspec +9 -9
- data/lib/uaa/http.rb +16 -1
- data/lib/uaa/info.rb +3 -5
- data/lib/uaa/scim.rb +14 -15
- data/lib/uaa/token_coder.rb +4 -4
- data/lib/uaa/token_issuer.rb +12 -14
- data/lib/uaa/util.rb +1 -1
- data/lib/uaa/version.rb +1 -1
- data/spec/http_spec.rb +28 -4
- data/spec/info_spec.rb +3 -3
- data/spec/integration_spec.rb +45 -17
- data/spec/scim_spec.rb +12 -11
- data/spec/spec_helper.rb +7 -1
- data/spec/token_coder_spec.rb +14 -14
- data/spec/token_issuer_spec.rb +20 -20
- metadata +74 -46
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2e2f468e6b75cdce97cb600bb02246bc498b91bd75b27cf5a34effa789eed4eb
|
4
|
+
data.tar.gz: 766c4f441814d7131291b78c975d0ddb75c1b0bcbd63fb912aeefd5f2f05746e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23495a14122b6fb9c250da921c509e7c440f4800c0c37e961961f2e3441a2932912fe9b8f3ff6d42d918447275d6ee194efb14e037244f429db41a778e088195
|
7
|
+
data.tar.gz: 34a60f7355afe29e0b5a7c1d69a43b9a1eae897acb197e167438ec7f95934d8e52ba022425bfbd0c0e8e574a4042c66930f30f00a9e9ecd61679dcda564a64c4
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on: workflow_dispatch
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
name: Build + Publish
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
permissions:
|
10
|
+
contents: read
|
11
|
+
packages: write
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- name: Set up Ruby 2.6
|
16
|
+
uses: actions/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: 2.6.x
|
19
|
+
|
20
|
+
- name: Publish to RubyGems
|
21
|
+
run: |
|
22
|
+
mkdir -p $HOME/.gem
|
23
|
+
touch $HOME/.gem/credentials
|
24
|
+
chmod 0600 $HOME/.gem/credentials
|
25
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
26
|
+
gem build *.gemspec
|
27
|
+
gem push *.gem
|
28
|
+
env:
|
29
|
+
GEM_HOST_API_KEY: "${{ secrets.RUBYGEMS_AUTH_TOKEN }}"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby-version: ['2.5', '2.7', '3.0', '3.1']
|
16
|
+
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- name: Set up Ruby
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby-version }}
|
23
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
24
|
+
- name: Run tests
|
25
|
+
run: bundle exec rake
|
26
|
+
- name: Run coverage
|
27
|
+
run: bundle exec rake cov
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ For documentation see: https://rubygems.org/gems/cf-uaa-lib
|
|
23
23
|
token_issuer = CF::UAA::TokenIssuer.new("https://uaa.cloudfoundry.com", "vmc")
|
24
24
|
puts token_issuer.prompts.inspect
|
25
25
|
token = token_issuer.implicit_grant_with_creds(username: "<your_username>", password: "<your_password>")
|
26
|
-
token_info = TokenCoder.decode(token.info["access_token"], nil, nil, false) #token signature not verified
|
26
|
+
token_info = CF::UAA::TokenCoder.decode(token.info["access_token"], nil, nil, false) #token signature not verified
|
27
27
|
puts token_info["user_name"]
|
28
28
|
|
29
29
|
## Tests
|
data/cf-uaa-lib.gemspec
CHANGED
@@ -24,8 +24,6 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.summary = %q{Client library for CloudFoundry UAA}
|
25
25
|
s.description = %q{Client library for interacting with the CloudFoundry User Account and Authorization (UAA) server. The UAA is an OAuth2 Authorization Server so it can be used by webapps and command line apps to obtain access tokens to act on behalf of users. The tokens can then be used to access protected resources in a Resource Server. This library is for use by UAA client applications or resource servers.}
|
26
26
|
|
27
|
-
s.rubyforge_project = "cf-uaa-lib"
|
28
|
-
|
29
27
|
s.license = "Apache-2.0"
|
30
28
|
s.files = `git ls-files`.split("\n")
|
31
29
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -33,15 +31,17 @@ Gem::Specification.new do |s|
|
|
33
31
|
s.require_paths = ['lib']
|
34
32
|
|
35
33
|
# dependencies
|
36
|
-
s.add_dependency 'multi_json', '
|
34
|
+
s.add_dependency 'multi_json', '>= 1.12.1', '< 1.16'
|
37
35
|
s.add_dependency 'httpclient', '~> 2.8', '>= 2.8.2.4'
|
36
|
+
s.add_dependency 'addressable', '~> 2.8', '>= 2.8.0'
|
38
37
|
|
39
|
-
s.add_development_dependency 'bundler', '~>
|
40
|
-
s.add_development_dependency 'rake', '
|
41
|
-
s.add_development_dependency 'rspec', '
|
42
|
-
s.add_development_dependency 'simplecov', '~> 0.
|
38
|
+
s.add_development_dependency 'bundler', '~> 2.2'
|
39
|
+
s.add_development_dependency 'rake', '>= 10.3.2', '~> 13.0'
|
40
|
+
s.add_development_dependency 'rspec', '>= 2.14.1', '~> 3.9'
|
41
|
+
s.add_development_dependency 'simplecov', '~> 0.21.2'
|
43
42
|
s.add_development_dependency 'simplecov-rcov', '~> 0.2.3'
|
44
|
-
s.add_development_dependency 'ci_reporter', '
|
45
|
-
s.add_development_dependency 'json_pure', '
|
43
|
+
s.add_development_dependency 'ci_reporter', '>= 1.9.2', '~> 2.0'
|
44
|
+
s.add_development_dependency 'json_pure', '>= 1.8.1', '~> 2.5'
|
45
|
+
s.add_development_dependency 'ci_reporter_rspec', '~> 1.0'
|
46
46
|
|
47
47
|
end
|
data/lib/uaa/http.rb
CHANGED
@@ -48,10 +48,17 @@ module Http
|
|
48
48
|
|
49
49
|
def self.included(base)
|
50
50
|
base.class_eval do
|
51
|
-
|
51
|
+
attr_reader :skip_ssl_validation, :ssl_ca_file, :ssl_cert_store, :http_timeout
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
def initialize_http_options(options)
|
56
|
+
@skip_ssl_validation = options[:skip_ssl_validation]
|
57
|
+
@ssl_ca_file = options[:ssl_ca_file]
|
58
|
+
@ssl_cert_store = options[:ssl_cert_store]
|
59
|
+
@http_timeout = options[:http_timeout]
|
60
|
+
end
|
61
|
+
|
55
62
|
# Sets the current logger instance to recieve error messages.
|
56
63
|
# @param [Logger] logr
|
57
64
|
# @return [Logger]
|
@@ -176,6 +183,8 @@ module Http
|
|
176
183
|
raise SSLException, "Invalid SSL Cert for #{url}. Use '--skip-ssl-validation' to continue with an insecure target"
|
177
184
|
rescue URI::Error, SocketError, SystemCallError => e
|
178
185
|
raise BadTarget, "error: #{e.message}"
|
186
|
+
rescue HTTPClient::ConnectTimeoutError => e
|
187
|
+
raise HTTPException.new "http timeout"
|
179
188
|
end
|
180
189
|
|
181
190
|
def http_request(uri)
|
@@ -201,6 +210,12 @@ module Http
|
|
201
210
|
http = HTTPClient.new
|
202
211
|
end
|
203
212
|
|
213
|
+
if http_timeout
|
214
|
+
http.connect_timeout = http_timeout
|
215
|
+
http.send_timeout = http_timeout
|
216
|
+
http.receive_timeout = http_timeout
|
217
|
+
end
|
218
|
+
|
204
219
|
@http_cache[cache_key] = http
|
205
220
|
end
|
206
221
|
|
data/lib/uaa/info.rb
CHANGED
@@ -31,10 +31,8 @@ class Info
|
|
31
31
|
# string keys are returned.
|
32
32
|
def initialize(target, options = {})
|
33
33
|
self.target = target
|
34
|
-
self.skip_ssl_validation = options[:skip_ssl_validation]
|
35
|
-
self.ssl_ca_file = options[:ssl_ca_file]
|
36
|
-
self.ssl_cert_store = options[:ssl_cert_store]
|
37
34
|
self.symbolize_keys = options[:symbolize_keys]
|
35
|
+
initialize_http_options(options)
|
38
36
|
end
|
39
37
|
|
40
38
|
# sets whether the keys in returned hashes should be symbols.
|
@@ -131,7 +129,7 @@ class Info
|
|
131
129
|
# @return [Hash] contents of the token
|
132
130
|
def decode_token(client_id, client_secret, token, token_type = "bearer", audience_ids = nil)
|
133
131
|
reply = json_parse_reply(key_style, *request(target, :post, '/check_token',
|
134
|
-
Util.encode_form(:
|
132
|
+
Util.encode_form(token: token),
|
135
133
|
"authorization" => Http.basic_auth(client_id, client_secret),
|
136
134
|
"content-type" => Http::FORM_UTF8,"accept" => Http::JSON_UTF8))
|
137
135
|
|
@@ -148,7 +146,7 @@ class Info
|
|
148
146
|
# @return [Hash]
|
149
147
|
def password_strength(password)
|
150
148
|
json_parse_reply(key_style, *request(target, :post, '/password/score',
|
151
|
-
Util.encode_form(:
|
149
|
+
Util.encode_form(password: password), "content-type" => Http::FORM_UTF8,
|
152
150
|
"accept" => Http::JSON_UTF8))
|
153
151
|
end
|
154
152
|
|
data/lib/uaa/scim.rb
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
#++
|
13
13
|
|
14
14
|
require 'uaa/http'
|
15
|
-
require 'uri'
|
15
|
+
require 'addressable/uri'
|
16
16
|
|
17
17
|
module CF::UAA
|
18
18
|
|
@@ -61,7 +61,8 @@ class Scim
|
|
61
61
|
'externalid' => 'externalId',
|
62
62
|
'phonenumbers' => 'phoneNumbers',
|
63
63
|
'startindex' => 'startIndex',
|
64
|
-
'zoneid' => 'zoneId'
|
64
|
+
'zoneid' => 'zoneId',
|
65
|
+
'includeinactive' => 'includeInactive'
|
65
66
|
}[kd]
|
66
67
|
kc || kd
|
67
68
|
end
|
@@ -148,10 +149,8 @@ class Scim
|
|
148
149
|
def initialize(target, auth_header, options = {})
|
149
150
|
@target, @auth_header = target, auth_header
|
150
151
|
@key_style = options[:symbolize_keys] ? :downsym : :down
|
151
|
-
self.skip_ssl_validation = options[:skip_ssl_validation]
|
152
|
-
self.ssl_ca_file = options[:ssl_ca_file]
|
153
|
-
self.ssl_cert_store = options[:ssl_cert_store]
|
154
152
|
@zone = options[:zone]
|
153
|
+
initialize_http_options(options)
|
155
154
|
end
|
156
155
|
|
157
156
|
# Convenience method to get the naming attribute, e.g. userName for user,
|
@@ -178,7 +177,7 @@ class Scim
|
|
178
177
|
# @param [String] id the id attribute of the SCIM object
|
179
178
|
# @return [nil]
|
180
179
|
def delete(type, id)
|
181
|
-
http_delete @target, "#{type_info(type, :path)}/#{URI.encode(id)}", @auth_header, @zone
|
180
|
+
http_delete @target, "#{type_info(type, :path)}/#{Addressable::URI.encode(id)}", @auth_header, @zone
|
182
181
|
end
|
183
182
|
|
184
183
|
# Replaces the contents of a SCIM object.
|
@@ -193,7 +192,7 @@ class Scim
|
|
193
192
|
hdrs.merge!('if-match' => etag)
|
194
193
|
end
|
195
194
|
reply = json_parse_reply(@key_style,
|
196
|
-
*json_put(@target, "#{path}/#{URI.encode(id)}", info, hdrs))
|
195
|
+
*json_put(@target, "#{path}/#{Addressable::URI.encode(id)}", info, hdrs))
|
197
196
|
|
198
197
|
# hide client endpoints that are not quite scim compatible
|
199
198
|
type == :client && !reply ? get(type, info['client_id']): reply
|
@@ -211,7 +210,7 @@ class Scim
|
|
211
210
|
hdrs.merge!('if-match' => etag)
|
212
211
|
end
|
213
212
|
reply = json_parse_reply(@key_style,
|
214
|
-
*json_patch(@target, "#{path}/#{URI.encode(id)}", info, hdrs))
|
213
|
+
*json_patch(@target, "#{path}/#{Addressable::URI.encode(id)}", info, hdrs))
|
215
214
|
|
216
215
|
# hide client endpoints that are not quite scim compatible
|
217
216
|
type == :client && !reply ? get(type, info['client_id']): reply
|
@@ -259,7 +258,7 @@ class Scim
|
|
259
258
|
# @param (see #delete)
|
260
259
|
# @return (see #add)
|
261
260
|
def get(type, id)
|
262
|
-
info = json_get(@target, "#{type_info(type, :path)}/#{URI.encode(id)}",
|
261
|
+
info = json_get(@target, "#{type_info(type, :path)}/#{Addressable::URI.encode(id)}",
|
263
262
|
@key_style, headers)
|
264
263
|
|
265
264
|
fake_client_id(info) if type == :client # hide client reply, not quite scim
|
@@ -271,7 +270,7 @@ class Scim
|
|
271
270
|
# @return (client meta)
|
272
271
|
def get_client_meta(client_id)
|
273
272
|
path = type_info(:client, :path)
|
274
|
-
json_get(@target, "#{path}/#{URI.encode(client_id)}/meta", @key_style, headers)
|
273
|
+
json_get(@target, "#{path}/#{Addressable::URI.encode(client_id)}/meta", @key_style, headers)
|
275
274
|
end
|
276
275
|
|
277
276
|
# Collects all pages of entries from a query
|
@@ -351,7 +350,7 @@ class Scim
|
|
351
350
|
req = {"password" => new_password}
|
352
351
|
req["oldPassword"] = old_password if old_password
|
353
352
|
json_parse_reply(@key_style, *json_put(@target,
|
354
|
-
"#{type_info(:user, :path)}/#{URI.encode(user_id)}/password", req, headers))
|
353
|
+
"#{type_info(:user, :path)}/#{Addressable::URI.encode(user_id)}/password", req, headers))
|
355
354
|
end
|
356
355
|
|
357
356
|
# Change client secret.
|
@@ -367,18 +366,18 @@ class Scim
|
|
367
366
|
req = {"secret" => new_secret }
|
368
367
|
req["oldSecret"] = old_secret if old_secret
|
369
368
|
json_parse_reply(@key_style, *json_put(@target,
|
370
|
-
"#{type_info(:client, :path)}/#{URI.encode(client_id)}/secret", req, headers))
|
369
|
+
"#{type_info(:client, :path)}/#{Addressable::URI.encode(client_id)}/secret", req, headers))
|
371
370
|
end
|
372
371
|
|
373
372
|
def unlock_user(user_id)
|
374
373
|
req = {"locked" => false}
|
375
374
|
json_parse_reply(@key_style, *json_patch(@target,
|
376
|
-
"#{type_info(:user, :path)}/#{URI.encode(user_id)}/status", req, headers))
|
375
|
+
"#{type_info(:user, :path)}/#{Addressable::URI.encode(user_id)}/status", req, headers))
|
377
376
|
end
|
378
377
|
|
379
378
|
def map_group(group, is_id, external_group, origin = "ldap")
|
380
379
|
key_name = is_id ? :groupId : :displayName
|
381
|
-
request = {key_name => group, :
|
380
|
+
request = {key_name => group, externalGroup: external_group, schemas: ["urn:scim:schemas:core:1.0"], origin: origin }
|
382
381
|
result = json_parse_reply(@key_style, *json_post(@target,
|
383
382
|
"#{type_info(:group_mapping, :path)}", request,
|
384
383
|
headers))
|
@@ -386,7 +385,7 @@ class Scim
|
|
386
385
|
end
|
387
386
|
|
388
387
|
def unmap_group(group_id, external_group, origin = "ldap")
|
389
|
-
http_delete(@target, "#{type_info(:group_mapping, :path)}/groupId/#{group_id}/externalGroup/#{URI.encode(external_group)}/origin/#{origin}",
|
388
|
+
http_delete(@target, "#{type_info(:group_mapping, :path)}/groupId/#{group_id}/externalGroup/#{Addressable::URI.encode(external_group)}/origin/#{origin}",
|
390
389
|
@auth_header, @zone)
|
391
390
|
end
|
392
391
|
|
data/lib/uaa/token_coder.rb
CHANGED
@@ -65,7 +65,7 @@ class TokenCoder
|
|
65
65
|
unless options.is_a?(Hash) && obsolete1.nil? && obsolete2.nil?
|
66
66
|
# deprecated: def self.encode(token_body, skey, pkey = nil, algo = 'HS256')
|
67
67
|
warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
|
68
|
-
options = {:
|
68
|
+
options = {skey: options }
|
69
69
|
options[:pkey], options[:algorithm] = obsolete1, obsolete2
|
70
70
|
end
|
71
71
|
options = normalize_options(options)
|
@@ -96,7 +96,7 @@ class TokenCoder
|
|
96
96
|
unless options.is_a?(Hash) && obsolete1.nil? && obsolete2.nil?
|
97
97
|
# deprecated: def self.decode(token, skey = nil, pkey = nil, verify = true)
|
98
98
|
warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
|
99
|
-
options = {:
|
99
|
+
options = {skey: options }
|
100
100
|
options[:pkey], options[:verify] = obsolete1, obsolete2
|
101
101
|
end
|
102
102
|
options = normalize_options(options)
|
@@ -170,7 +170,7 @@ class TokenCoder
|
|
170
170
|
unless options.is_a?(Hash) && obsolete1.nil? && obsolete2.nil?
|
171
171
|
# deprecated: def initialize(audience_ids, skey, pkey = nil)
|
172
172
|
warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
|
173
|
-
options = {:
|
173
|
+
options = {audience_ids: options }
|
174
174
|
options[:skey], options[:pkey] = obsolete1, obsolete2
|
175
175
|
end
|
176
176
|
@options = self.class.normalize_options(options)
|
@@ -184,7 +184,7 @@ class TokenCoder
|
|
184
184
|
def encode(token_body = {}, algorithm = nil)
|
185
185
|
token_body[:aud] = @options[:audience_ids] if @options[:audience_ids] && !token_body[:aud] && !token_body['aud']
|
186
186
|
token_body[:exp] = Time.now.to_i + 7 * 24 * 60 * 60 unless token_body[:exp] || token_body['exp']
|
187
|
-
self.class.encode(token_body, algorithm ? @options.merge(:
|
187
|
+
self.class.encode(token_body, algorithm ? @options.merge(algorithm: algorithm) : @options)
|
188
188
|
end
|
189
189
|
|
190
190
|
# Returns hash of values decoded from the token contents. If the
|
data/lib/uaa/token_issuer.rb
CHANGED
@@ -81,8 +81,8 @@ class TokenIssuer
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def authorize_path_args(response_type, redirect_uri, scope, state = random_state, args = {})
|
84
|
-
params = args.merge(:
|
85
|
-
:
|
84
|
+
params = args.merge(client_id: @client_id, response_type: response_type,
|
85
|
+
redirect_uri: redirect_uri, state: state)
|
86
86
|
params[:scope] = scope = Util.strlist(scope) if scope = Util.arglist(scope)
|
87
87
|
params[:nonce] = state
|
88
88
|
"/oauth/authorize?#{Util.encode_form(params)}"
|
@@ -109,9 +109,7 @@ class TokenIssuer
|
|
109
109
|
@target, @client_id, @client_secret = target, client_id, client_secret
|
110
110
|
@token_target = options[:token_target] || target
|
111
111
|
@key_style = options[:symbolize_keys] ? :sym : nil
|
112
|
-
|
113
|
-
self.ssl_ca_file = options[:ssl_ca_file]
|
114
|
-
self.ssl_cert_store = options[:ssl_cert_store]
|
112
|
+
initialize_http_options(options)
|
115
113
|
end
|
116
114
|
|
117
115
|
# Allows an app to discover what credentials are required for
|
@@ -139,7 +137,7 @@ class TokenIssuer
|
|
139
137
|
|
140
138
|
# the accept header is only here so the uaa will issue error replies in json to aid debugging
|
141
139
|
headers = {'content-type' => FORM_UTF8, 'accept' => JSON_UTF8 }
|
142
|
-
body = Util.encode_form(credentials.merge(:
|
140
|
+
body = Util.encode_form(credentials.merge(source: 'credentials'))
|
143
141
|
status, body, headers = request(@target, :post, uri, body, headers)
|
144
142
|
raise BadResponse, "status #{status}" unless status == 302
|
145
143
|
req_uri, reply_uri = URI.parse(redir_uri), URI.parse(headers['location'])
|
@@ -196,7 +194,7 @@ class TokenIssuer
|
|
196
194
|
reply = json_parse_reply(nil, *request(@target, :post, "/autologin", body, headers))
|
197
195
|
raise BadResponse, "no autologin code in reply" unless reply['code']
|
198
196
|
@target + authorize_path_args('code', redirect_uri, scope,
|
199
|
-
random_state, :
|
197
|
+
random_state, code: reply['code'])
|
200
198
|
end
|
201
199
|
|
202
200
|
# Constructs a uri that the client is to return to the browser to direct
|
@@ -230,8 +228,8 @@ class TokenIssuer
|
|
230
228
|
rescue URI::InvalidURIError, ArgumentError, BadResponse
|
231
229
|
raise BadResponse, "received invalid response from target #{@target}"
|
232
230
|
end
|
233
|
-
request_token(:
|
234
|
-
:
|
231
|
+
request_token(grant_type: 'authorization_code', code: authcode,
|
232
|
+
redirect_uri: ac_params['redirect_uri'])
|
235
233
|
end
|
236
234
|
|
237
235
|
# Uses the instance client credentials in addition to the +username+
|
@@ -239,15 +237,15 @@ class TokenIssuer
|
|
239
237
|
# See {http://tools.ietf.org/html/rfc6749#section-4.3}.
|
240
238
|
# @return [TokenInfo]
|
241
239
|
def owner_password_grant(username, password, scope = nil)
|
242
|
-
request_token(:
|
243
|
-
:
|
240
|
+
request_token(grant_type: 'password', username: username,
|
241
|
+
password: password, scope: scope)
|
244
242
|
end
|
245
243
|
|
246
244
|
# Uses a one-time passcode obtained from the UAA to get a
|
247
245
|
# token.
|
248
246
|
# @return [TokenInfo]
|
249
247
|
def passcode_grant(passcode, scope = nil)
|
250
|
-
request_token(:
|
248
|
+
request_token(grant_type: 'password', passcode: passcode, scope: scope)
|
251
249
|
end
|
252
250
|
|
253
251
|
# Gets an access token with the user credentials used for authentication
|
@@ -266,14 +264,14 @@ class TokenIssuer
|
|
266
264
|
# credentials grant. See http://tools.ietf.org/html/rfc6749#section-4.4
|
267
265
|
# @return [TokenInfo]
|
268
266
|
def client_credentials_grant(scope = nil)
|
269
|
-
request_token(:
|
267
|
+
request_token(grant_type: 'client_credentials', scope: scope)
|
270
268
|
end
|
271
269
|
|
272
270
|
# Uses the instance client credentials and the given +refresh_token+ to get
|
273
271
|
# a new access token. See http://tools.ietf.org/html/rfc6749#section-6
|
274
272
|
# @return [TokenInfo] which may include a new refresh token as well as an access token.
|
275
273
|
def refresh_token_grant(refresh_token, scope = nil)
|
276
|
-
request_token(:
|
274
|
+
request_token(grant_type: 'refresh_token', refresh_token: refresh_token, scope: scope)
|
277
275
|
end
|
278
276
|
|
279
277
|
end
|
data/lib/uaa/util.rb
CHANGED
@@ -145,7 +145,7 @@ class Util
|
|
145
145
|
|
146
146
|
# Converts +obj+ to nicely formatted JSON
|
147
147
|
# @return [String] obj in formatted json
|
148
|
-
def self.json_pretty(obj) MultiJson.dump(obj, :
|
148
|
+
def self.json_pretty(obj) MultiJson.dump(obj, pretty: true) end
|
149
149
|
|
150
150
|
# Converts +obj+ to a URL-safe base 64 encoded string
|
151
151
|
# @return [String]
|
data/lib/uaa/version.rb
CHANGED
data/spec/http_spec.rb
CHANGED
@@ -69,7 +69,7 @@ describe CF::UAA::Http do
|
|
69
69
|
let(:ssl_config) { double('ssl_config') }
|
70
70
|
|
71
71
|
it 'sets verify mode to VERIFY_NONE' do
|
72
|
-
http_instance.skip_ssl_validation
|
72
|
+
http_instance.initialize_http_options({skip_ssl_validation: true})
|
73
73
|
|
74
74
|
expect(http_double).to receive(:ssl_config).and_return(ssl_config)
|
75
75
|
expect(ssl_config).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
@@ -91,7 +91,7 @@ describe CF::UAA::Http do
|
|
91
91
|
let(:ssl_config) { double('ssl_config') }
|
92
92
|
|
93
93
|
it 'passes it' do
|
94
|
-
http_instance.ssl_ca_file
|
94
|
+
http_instance.initialize_http_options({ssl_ca_file: '/fake-ca-file'})
|
95
95
|
|
96
96
|
expect(http_double).to receive(:ssl_config).and_return(ssl_config).twice
|
97
97
|
expect(ssl_config).to receive(:set_trust_ca).with('/fake-ca-file')
|
@@ -105,7 +105,7 @@ describe CF::UAA::Http do
|
|
105
105
|
let(:ssl_config) { double('ssl_config') }
|
106
106
|
|
107
107
|
it 'passes it' do
|
108
|
-
http_instance.ssl_cert_store
|
108
|
+
http_instance.initialize_http_options({ssl_cert_store: cert_store})
|
109
109
|
|
110
110
|
expect(http_double).to receive(:ssl_config).and_return(ssl_config).twice
|
111
111
|
expect(ssl_config).to receive(:cert_store=).with(cert_store)
|
@@ -114,5 +114,29 @@ describe CF::UAA::Http do
|
|
114
114
|
http_instance.http_get('https://uncached.example.com')
|
115
115
|
end
|
116
116
|
end
|
117
|
+
|
118
|
+
context 'when an http request timeout is provided' do
|
119
|
+
it 'sets all timeouts on the http clien to the http_timeout' do
|
120
|
+
http_instance.initialize_http_options({http_timeout: 10})
|
121
|
+
|
122
|
+
expect(http_double).to receive(:connect_timeout=).with(10)
|
123
|
+
expect(http_double).to receive(:send_timeout=).with(10)
|
124
|
+
expect(http_double).to receive(:receive_timeout=).with(10)
|
125
|
+
|
126
|
+
http_instance.http_get('https://uncached.example.com')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when an http request timeout is not provided' do
|
131
|
+
it 'does not override the default' do
|
132
|
+
http_instance.initialize_http_options({})
|
133
|
+
|
134
|
+
expect(http_double).not_to receive(:connect_timeout=)
|
135
|
+
expect(http_double).not_to receive(:send_timeout=)
|
136
|
+
expect(http_double).not_to receive(:receive_timeout=)
|
137
|
+
|
138
|
+
http_instance.http_get('https://uncached.example.com')
|
139
|
+
end
|
140
|
+
end
|
117
141
|
end
|
118
|
-
end
|
142
|
+
end
|
data/spec/info_spec.rb
CHANGED
@@ -34,7 +34,7 @@ module CF::UAA
|
|
34
34
|
end
|
35
35
|
|
36
36
|
describe 'initialize' do
|
37
|
-
let(:options) { {:
|
37
|
+
let(:options) { {skip_ssl_validation: true} }
|
38
38
|
|
39
39
|
it 'sets proxy information' do
|
40
40
|
uaa_info.skip_ssl_validation == true
|
@@ -52,7 +52,7 @@ module CF::UAA
|
|
52
52
|
end
|
53
53
|
|
54
54
|
context 'with symbolize_keys keys true' do
|
55
|
-
let(:options) { {:
|
55
|
+
let(:options) { {symbolize_keys: true} }
|
56
56
|
|
57
57
|
it 'gets server info' do
|
58
58
|
result = uaa_info.server
|
@@ -84,7 +84,7 @@ module CF::UAA
|
|
84
84
|
end
|
85
85
|
|
86
86
|
context 'with symbolize_keys keys true' do
|
87
|
-
let(:options) { {:
|
87
|
+
let(:options) { {symbolize_keys: true} }
|
88
88
|
|
89
89
|
it 'gets UAA target' do
|
90
90
|
result = uaa_info.discover_uaa
|
data/spec/integration_spec.rb
CHANGED
@@ -41,7 +41,35 @@ module CF::UAA
|
|
41
41
|
target = ENV['UAA_CLIENT_TARGET']
|
42
42
|
|
43
43
|
admin_token_issuer = TokenIssuer.new(target, admin_client, admin_secret, options)
|
44
|
-
Scim.new(target, admin_token_issuer.client_credentials_grant.auth_header, options.merge(:
|
44
|
+
Scim.new(target, admin_token_issuer.client_credentials_grant.auth_header, options.merge(symbolize_keys: true))
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'when UAA does not respond' do
|
48
|
+
let(:http_timeout) { 0.01 }
|
49
|
+
let(:default_http_client_timeout) { 60 }
|
50
|
+
let(:scim) { Scim.new(@target, "", {http_timeout: http_timeout}) }
|
51
|
+
let(:token_issuer) { TokenIssuer.new(@target, "", "", {http_timeout: http_timeout}) }
|
52
|
+
let(:blackhole_ip) { '10.255.255.1'}
|
53
|
+
|
54
|
+
before do
|
55
|
+
@target = "http://#{blackhole_ip}"
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'times out the connection at the configured time for the scim' do
|
59
|
+
expect {
|
60
|
+
Timeout.timeout(default_http_client_timeout - 1) do
|
61
|
+
scim.get(:user, "admin")
|
62
|
+
end
|
63
|
+
}.to raise_error HTTPException
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'times out the connection at the configured time for the token issuer' do
|
67
|
+
expect {
|
68
|
+
Timeout.timeout(default_http_client_timeout - 1) do
|
69
|
+
token_issuer.client_credentials_grant
|
70
|
+
end
|
71
|
+
}.to raise_error HTTPException
|
72
|
+
end
|
45
73
|
end
|
46
74
|
|
47
75
|
if ENV['UAA_CLIENT_TARGET']
|
@@ -49,20 +77,20 @@ module CF::UAA
|
|
49
77
|
|
50
78
|
let(:options) { @options }
|
51
79
|
let(:token_issuer) { TokenIssuer.new(@target, @test_client, @test_secret, options) }
|
52
|
-
let(:scim) { Scim.new(@target, token_issuer.client_credentials_grant.auth_header, options.merge(:
|
80
|
+
let(:scim) { Scim.new(@target, token_issuer.client_credentials_grant.auth_header, options.merge(symbolize_keys: true)) }
|
53
81
|
|
54
82
|
before :all do
|
55
83
|
@options = {}
|
56
84
|
if ENV['SKIP_SSL_VALIDATION']
|
57
|
-
@options = {:
|
85
|
+
@options = {skip_ssl_validation: true}
|
58
86
|
end
|
59
87
|
@target = ENV['UAA_CLIENT_TARGET']
|
60
88
|
@test_client = "test_client_#{Time.now.to_i}"
|
61
89
|
@test_secret = '+=tEsTsEcRet~!@'
|
62
90
|
gids = ['clients.read', 'scim.read', 'scim.write', 'uaa.resource', 'password.write']
|
63
|
-
test_client = CF::UAA::admin_scim(@options).add(:client, :
|
64
|
-
:
|
65
|
-
:
|
91
|
+
test_client = CF::UAA::admin_scim(@options).add(:client, client_id: @test_client, client_secret: @test_secret,
|
92
|
+
authorities: gids, authorized_grant_types: ['client_credentials', 'password'],
|
93
|
+
scope: ['openid', 'password.write'])
|
66
94
|
expect(test_client[:client_id]).to eq(@test_client)
|
67
95
|
end
|
68
96
|
|
@@ -74,7 +102,7 @@ module CF::UAA
|
|
74
102
|
|
75
103
|
if ENV['SKIP_SSL_VALIDATION']
|
76
104
|
context 'when ssl certificate is self-signed' do
|
77
|
-
let(:options) { {:
|
105
|
+
let(:options) { {skip_ssl_validation: false} }
|
78
106
|
|
79
107
|
it 'fails if skip_ssl_validation is false' do
|
80
108
|
expect{ scim }.to raise_exception(CF::UAA::SSLException)
|
@@ -85,7 +113,7 @@ module CF::UAA
|
|
85
113
|
if ENV['SSL_CA_FILE']
|
86
114
|
context 'when you do not skip SSL validation' do
|
87
115
|
context 'when you provide cert' do
|
88
|
-
let(:options) { {:
|
116
|
+
let(:options) { {ssl_ca_file: ENV['SSL_CA_FILE']} }
|
89
117
|
|
90
118
|
it 'works' do
|
91
119
|
expect(token_issuer.prompts).to_not be_nil
|
@@ -111,7 +139,7 @@ module CF::UAA
|
|
111
139
|
cert_store
|
112
140
|
end
|
113
141
|
|
114
|
-
let(:options) { {:
|
142
|
+
let(:options) { {ssl_cert_store: cert_store} }
|
115
143
|
it 'works' do
|
116
144
|
expect(token_issuer.prompts).to_not be_nil
|
117
145
|
end
|
@@ -128,7 +156,7 @@ module CF::UAA
|
|
128
156
|
end
|
129
157
|
|
130
158
|
it 'should report the uaa client version' do
|
131
|
-
expect(VERSION).to match(/\d
|
159
|
+
expect(VERSION).to match(/\d+.\d+.\d+/)
|
132
160
|
end
|
133
161
|
|
134
162
|
it 'makes sure the server is there by getting the prompts for an implicit grant' do
|
@@ -138,7 +166,7 @@ module CF::UAA
|
|
138
166
|
it 'gets a token with client credentials' do
|
139
167
|
tkn = token_issuer.client_credentials_grant
|
140
168
|
expect(tkn.auth_header).to match(/^bearer\s/i)
|
141
|
-
info = TokenCoder.decode(tkn.info['access_token'], :
|
169
|
+
info = TokenCoder.decode(tkn.info['access_token'], verify: false, symbolize_keys: true)
|
142
170
|
expect(info[:exp]).to be
|
143
171
|
expect(info[:jti]).to be
|
144
172
|
end
|
@@ -151,9 +179,9 @@ module CF::UAA
|
|
151
179
|
before :each do
|
152
180
|
@username = "sam_#{Time.now.to_i}"
|
153
181
|
@user_pwd = "sam's P@55w0rd~!`@\#\$%^&*()_/{}[]\\|:\";',.<>?/"
|
154
|
-
usr = scim.add(:user, :
|
155
|
-
:
|
156
|
-
:
|
182
|
+
usr = scim.add(:user, username: @username, password: @user_pwd,
|
183
|
+
emails: [{value: 'sam@example.com'}],
|
184
|
+
name: {givenname: 'none', familyname: 'none'})
|
157
185
|
@user_id = usr[:id]
|
158
186
|
end
|
159
187
|
|
@@ -194,8 +222,8 @@ module CF::UAA
|
|
194
222
|
|
195
223
|
it 'should get a uri to be sent to the user agent to initiate autologin' do
|
196
224
|
redir_uri = 'http://call.back/uri_path'
|
197
|
-
uri_parts = token_issuer.autologin_uri(redir_uri, :
|
198
|
-
:
|
225
|
+
uri_parts = token_issuer.autologin_uri(redir_uri, username: @username,
|
226
|
+
password: @user_pwd ).split('?')
|
199
227
|
expect(uri_parts[0]).to eq("#{ENV['UAA_CLIENT_TARGET']}/oauth/authorize")
|
200
228
|
params = Util.decode_form(uri_parts[1], :sym)
|
201
229
|
expect(params[:response_type]).to eq('code')
|
@@ -209,4 +237,4 @@ module CF::UAA
|
|
209
237
|
end
|
210
238
|
end
|
211
239
|
end
|
212
|
-
end
|
240
|
+
end
|
data/spec/scim_spec.rb
CHANGED
@@ -39,7 +39,7 @@ describe Scim do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
describe 'initialize' do
|
42
|
-
let(:options) { {:
|
42
|
+
let(:options) { {http_proxy: 'http-proxy.com', https_proxy: 'https-proxy.com', skip_ssl_validation: true} }
|
43
43
|
|
44
44
|
it 'sets skip_ssl_validation' do
|
45
45
|
subject.skip_ssl_validation == true
|
@@ -53,8 +53,8 @@ describe Scim do
|
|
53
53
|
check_headers(headers, :json, :json, nil)
|
54
54
|
[200, '{"ID":"id12345"}', {'content-type' => 'application/json'}]
|
55
55
|
end
|
56
|
-
result = subject.add(:user, :
|
57
|
-
:
|
56
|
+
result = subject.add(:user, hair: 'brown', shoe_size: 'large',
|
57
|
+
eye_color: ['blue', 'green'], name: 'fred')
|
58
58
|
result['id'].should == 'id12345'
|
59
59
|
end
|
60
60
|
|
@@ -71,8 +71,8 @@ describe Scim do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'replaces an object' do
|
74
|
-
obj = {:
|
75
|
-
:
|
74
|
+
obj = {hair: 'black', shoe_size: 'medium', eye_color: ['hazel', 'brown'],
|
75
|
+
name: 'fredrick', meta: {version: 'v567'}, id: 'id12345'}
|
76
76
|
subject.set_request_handler do |url, method, body, headers|
|
77
77
|
url.should == "#{@target}/Users/id12345"
|
78
78
|
method.should == :put
|
@@ -85,8 +85,8 @@ describe Scim do
|
|
85
85
|
end
|
86
86
|
|
87
87
|
it 'modifies an object' do
|
88
|
-
obj = {:
|
89
|
-
:
|
88
|
+
obj = {hair: 'black', shoe_size: 'medium', eye_color: ['hazel', 'brown'],
|
89
|
+
name: 'fredrick', meta: {version: 'v567'}, id: 'id12345'}
|
90
90
|
subject.set_request_handler do |url, method, body, headers|
|
91
91
|
url.should == "#{@target}/Users/id12345"
|
92
92
|
method.should == :patch
|
@@ -113,6 +113,7 @@ describe Scim do
|
|
113
113
|
subject.set_request_handler do |url, method, body, headers|
|
114
114
|
url.should =~ %r{^#{@target}/Users\?}
|
115
115
|
url.should =~ %r{[\?&]attributes=id(&|$)}
|
116
|
+
url.should =~ %r{[\?&]includeInactive=true(&|$)}
|
116
117
|
url.should =~ %r{[\?&]startIndex=[12](&|$)}
|
117
118
|
method.should == :get
|
118
119
|
check_headers(headers, nil, :json, nil)
|
@@ -121,7 +122,7 @@ describe Scim do
|
|
121
122
|
'{"TotalResults":2,"ItemsPerPage":1,"StartIndex":2,"RESOURCES":[{"id":"id67890"}]}'
|
122
123
|
[200, reply, {'content-type' => 'application/json'}]
|
123
124
|
end
|
124
|
-
result = subject.all_pages(:user, :
|
125
|
+
result = subject.all_pages(:user, attributes: 'id', includeInactive: true)
|
125
126
|
[result[0]['id'], result[1]['id']].to_set.should == ['id12345', 'id67890'].to_set
|
126
127
|
end
|
127
128
|
|
@@ -220,7 +221,7 @@ describe Scim do
|
|
220
221
|
end
|
221
222
|
|
222
223
|
describe 'users in a zone' do
|
223
|
-
let(:options) { {:
|
224
|
+
let(:options) { {http_proxy: 'http-proxy.com', https_proxy: 'https-proxy.com', skip_ssl_validation: true, zone: 'derpzone'} }
|
224
225
|
|
225
226
|
it 'sends zone header' do
|
226
227
|
subject.set_request_handler do |url, method, body, headers|
|
@@ -229,8 +230,8 @@ describe Scim do
|
|
229
230
|
check_headers(headers, :json, :json, 'derpzone')
|
230
231
|
[200, '{"ID":"id12345"}', {'content-type' => 'application/json'}]
|
231
232
|
end
|
232
|
-
result = subject.add(:user, :
|
233
|
-
:
|
233
|
+
result = subject.add(:user, hair: 'brown', shoe_size: 'large',
|
234
|
+
eye_color: ['blue', 'green'], name: 'fred')
|
234
235
|
result['id'].should == 'id12345'
|
235
236
|
end
|
236
237
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/token_coder_spec.rb
CHANGED
@@ -18,8 +18,8 @@ module CF::UAA
|
|
18
18
|
|
19
19
|
describe TokenCoder do
|
20
20
|
|
21
|
-
subject { TokenCoder.new(:
|
22
|
-
:
|
21
|
+
subject { TokenCoder.new(audience_ids: "test_resource",
|
22
|
+
skey: "test_secret", pkey: OpenSSL::PKey::RSA.generate(512) ) }
|
23
23
|
|
24
24
|
before :each do
|
25
25
|
@tkn_body = {'foo' => "bar"}
|
@@ -57,7 +57,7 @@ describe TokenCoder do
|
|
57
57
|
2yrlT5h164jGCxqe7++1kIl4ollFCgz6QJ8lcmb/2Q==
|
58
58
|
-----END RSA PRIVATE KEY-----
|
59
59
|
DATA
|
60
|
-
coder = TokenCoder.new(:
|
60
|
+
coder = TokenCoder.new(audience_ids: "test_resource", pkey: pem)
|
61
61
|
tkn = coder.encode(@tkn_body, 'RS256')
|
62
62
|
result = coder.decode("bEaReR #{tkn}")
|
63
63
|
result.should_not be_nil
|
@@ -66,7 +66,7 @@ describe TokenCoder do
|
|
66
66
|
|
67
67
|
it "encodes/decodes with 'none' signature if explicitly accepted" do
|
68
68
|
tkn = subject.encode(@tkn_body, 'none')
|
69
|
-
result = TokenCoder.decode(tkn, :
|
69
|
+
result = TokenCoder.decode(tkn, accept_algorithms: "none")
|
70
70
|
result.should_not be_nil
|
71
71
|
result["foo"].should == "bar"
|
72
72
|
end
|
@@ -86,7 +86,7 @@ describe TokenCoder do
|
|
86
86
|
end
|
87
87
|
|
88
88
|
it "raises an error if the token is signed by an unknown signing key" do
|
89
|
-
other = TokenCoder.new(:
|
89
|
+
other = TokenCoder.new(audience_ids: "test_resource", skey: "other_secret")
|
90
90
|
tkn = other.encode(@tkn_body)
|
91
91
|
expect { subject.decode("bEaReR #{tkn}") }.to raise_exception(InvalidSignature)
|
92
92
|
end
|
@@ -103,8 +103,8 @@ describe TokenCoder do
|
|
103
103
|
2yrlT5h164jGCxqe7++1kIl4ollFCgz6QJ8lcmb/2Q==
|
104
104
|
-----END RSA PRIVATE KEY-----
|
105
105
|
DATA
|
106
|
-
coder = TokenCoder.new(:
|
107
|
-
coder2 = TokenCoder.new(:
|
106
|
+
coder = TokenCoder.new(audience_ids: "test_resource", pkey: pem)
|
107
|
+
coder2 = TokenCoder.new(audience_ids: "test_resource", skey: 'randomness')
|
108
108
|
|
109
109
|
tkn = coder.encode(@tkn_body, 'RS256')
|
110
110
|
|
@@ -123,21 +123,21 @@ describe TokenCoder do
|
|
123
123
|
2yrlT5h164jGCxqe7++1kIl4ollFCgz6QJ8lcmb/2Q==
|
124
124
|
-----END RSA PRIVATE KEY-----
|
125
125
|
DATA
|
126
|
-
coder = TokenCoder.new(:
|
127
|
-
coder2 = TokenCoder.new(:
|
126
|
+
coder = TokenCoder.new(audience_ids: "test_resource", pkey: pem)
|
127
|
+
coder2 = TokenCoder.new(audience_ids: "test_resource", skey: 'randomness')
|
128
128
|
tkn = coder2.encode(@tkn_body)
|
129
129
|
|
130
130
|
expect { coder.decode("bEaReR #{tkn}") }.to raise_exception(InvalidSignature)
|
131
131
|
end
|
132
132
|
|
133
133
|
it "raises an error if the token is an unknown signing algorithm" do
|
134
|
-
segments = [Util.json_encode64(:
|
134
|
+
segments = [Util.json_encode64(typ: "JWT", alg:"BADALGO")]
|
135
135
|
segments << Util.json_encode64(@tkn_body)
|
136
136
|
segments << Util.encode64("BADSIG")
|
137
137
|
tkn = segments.join('.')
|
138
|
-
tc = TokenCoder.new(:
|
139
|
-
:
|
140
|
-
:
|
138
|
+
tc = TokenCoder.new(audience_ids: "test_resource",
|
139
|
+
skey: "test_secret", pkey: OpenSSL::PKey::RSA.generate(512),
|
140
|
+
accept_algorithms: "BADALGO")
|
141
141
|
expect { tc.decode("bEaReR #{tkn}") }.to raise_exception(SignatureNotSupported)
|
142
142
|
end
|
143
143
|
|
@@ -179,7 +179,7 @@ describe TokenCoder do
|
|
179
179
|
|
180
180
|
it "decodes a token without validation" do
|
181
181
|
token = "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6ImY1MTgwMjExLWVkYjItNGQ4OS1hNmQwLThmNGVjMTE0NTE4YSIsInJlc291cmNlX2lkcyI6WyJjbG91ZF9jb250cm9sbGVyIiwicGFzc3dvcmQiXSwiZXhwaXJlc19hdCI6MTMzNjU1MTc2Niwic2NvcGUiOlsicmVhZCJdLCJlbWFpbCI6Im9sZHNAdm13YXJlLmNvbSIsImNsaWVudF9hdXRob3JpdGllcyI6WyJST0xFX1VOVFJVU1RFRCJdLCJleHBpcmVzX2luIjo0MzIwMCwidXNlcl9hdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwidXNlcl9pZCI6Im9sZHNAdm13YXJlLmNvbSIsImNsaWVudF9pZCI6InZtYyIsInRva2VuX2lkIjoiZWRlYmYzMTctNWU2Yi00YmYwLWFmM2ItMTA0OWRjNmFlYjc1In0.XoirrePfEujnZ9Vm7SRRnj3vZEfRp2tkjkS_OCVz5Bs"
|
182
|
-
info = TokenCoder.decode(token, :
|
182
|
+
info = TokenCoder.decode(token, verify: false)
|
183
183
|
info["id"].should_not be_nil
|
184
184
|
info["email"].should == "olds@vmware.com"
|
185
185
|
end
|
data/spec/token_issuer_spec.rb
CHANGED
@@ -29,7 +29,7 @@ describe TokenIssuer do
|
|
29
29
|
subject { @issuer }
|
30
30
|
|
31
31
|
describe 'initialize' do
|
32
|
-
let(:options) { {:
|
32
|
+
let(:options) { {http_proxy: 'http-proxy.com', https_proxy: 'https-proxy.com', skip_ssl_validation: true} }
|
33
33
|
|
34
34
|
it 'sets skip_ssl_validation' do
|
35
35
|
subject.skip_ssl_validation == true
|
@@ -45,8 +45,8 @@ describe TokenIssuer do
|
|
45
45
|
# TODO check basic auth header
|
46
46
|
url.should == 'http://test.uaa.target/oauth/token'
|
47
47
|
method.should == :post
|
48
|
-
reply = {:
|
49
|
-
:
|
48
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
49
|
+
scope: 'logs.read', expires_in: 98765}
|
50
50
|
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
51
51
|
end
|
52
52
|
token = subject.client_credentials_grant('logs.read')
|
@@ -59,8 +59,8 @@ describe TokenIssuer do
|
|
59
59
|
|
60
60
|
it 'gets all granted scopes if none specified' do
|
61
61
|
subject.set_request_handler do |url, method, body, headers|
|
62
|
-
reply = {:
|
63
|
-
:
|
62
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
63
|
+
scope: 'openid logs.read', expires_in: 98765}
|
64
64
|
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
65
65
|
end
|
66
66
|
token = subject.client_credentials_grant
|
@@ -92,8 +92,8 @@ describe TokenIssuer do
|
|
92
92
|
# TODO check basic auth header
|
93
93
|
url.should == 'http://test.uaa.target/oauth/token'
|
94
94
|
method.should == :post
|
95
|
-
reply = {:
|
96
|
-
:
|
95
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
96
|
+
scope: 'openid', expires_in: 98765}
|
97
97
|
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
98
98
|
end
|
99
99
|
token = subject.owner_password_grant('joe+admin', "?joe's%password$@ ", 'openid')
|
@@ -113,8 +113,8 @@ describe TokenIssuer do
|
|
113
113
|
body.should =~ /(^|&)passcode=12345($|&)/
|
114
114
|
body.should =~ /(^|&)grant_type=password($|&)/
|
115
115
|
method.should == :post
|
116
|
-
reply = {:
|
117
|
-
:
|
116
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
117
|
+
scope: 'openid', expires_in: 98765}
|
118
118
|
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
119
119
|
end
|
120
120
|
token = subject.passcode_grant('12345')
|
@@ -135,8 +135,8 @@ describe TokenIssuer do
|
|
135
135
|
url.should == 'http://test.uaa.target/oauth/token'
|
136
136
|
method.should == :post
|
137
137
|
body.split('&').should =~ ['passcode=fake-passcode', 'grant_type=password']
|
138
|
-
reply = {:
|
139
|
-
:
|
138
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
139
|
+
scope: 'openid', expires_in: 98765}
|
140
140
|
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
141
141
|
end
|
142
142
|
token = subject.owner_password_credentials_grant({passcode: 'fake-passcode'})
|
@@ -153,7 +153,7 @@ describe TokenIssuer do
|
|
153
153
|
|
154
154
|
it 'gets the prompts for credentials used to authenticate implicit grant' do
|
155
155
|
subject.set_request_handler do |url, method, body, headers|
|
156
|
-
info = { :
|
156
|
+
info = { prompts: {username: ['text', 'Username'], password: ['password', 'Password']} }
|
157
157
|
[200, Util.json(info), {'content-type' => 'application/json'}]
|
158
158
|
end
|
159
159
|
result = subject.prompts
|
@@ -182,10 +182,10 @@ describe TokenIssuer do
|
|
182
182
|
end
|
183
183
|
|
184
184
|
expect(subject).to receive(:authorize_path_args).with('token', 'https://uaa.cloudfoundry.com/redirect/test_client', 'logs.read', anything)
|
185
|
-
subject.
|
186
|
-
subject.
|
185
|
+
allow(subject).to receive(:random_state).and_return('1234')
|
186
|
+
allow(subject).to receive(:authorize_path_args).and_return('/oauth/authorize?state=1234&scope=logs.read')
|
187
187
|
|
188
|
-
token = subject.implicit_grant_with_creds({:
|
188
|
+
token = subject.implicit_grant_with_creds({username: 'joe+admin', password: "?joe's%password$@ "}, 'logs.read')
|
189
189
|
token.should be_an_instance_of TokenInfo
|
190
190
|
token.info['access_token'].should == 'test_access_token'
|
191
191
|
token.info['token_type'].should =~ /^bearer$/i
|
@@ -202,7 +202,7 @@ describe TokenIssuer do
|
|
202
202
|
end
|
203
203
|
|
204
204
|
expect(subject).to receive(:authorize_path_args).with('token id_token', 'https://uaa.cloudfoundry.com/redirect/test_client', 'openid logs.read', anything)
|
205
|
-
subject.
|
205
|
+
allow(subject).to receive(:random_state).and_return('1234')
|
206
206
|
subject.implicit_grant_with_creds({:username => 'joe+admin', :password => "?joe's%password$@ "}, 'openid logs.read')
|
207
207
|
end
|
208
208
|
end
|
@@ -214,8 +214,8 @@ describe TokenIssuer do
|
|
214
214
|
'expires_in=98765&scope=openid+logs.read&state=bad_state'
|
215
215
|
[302, nil, {'content-type' => 'application/json', 'location' => location}]
|
216
216
|
end
|
217
|
-
expect {token = subject.implicit_grant_with_creds(:
|
218
|
-
:
|
217
|
+
expect {token = subject.implicit_grant_with_creds(username: 'joe+admin',
|
218
|
+
password: "?joe's%password$@ ")}.to raise_exception BadResponse
|
219
219
|
end
|
220
220
|
|
221
221
|
it 'asks for an id_token with openid scope' do
|
@@ -253,8 +253,8 @@ describe TokenIssuer do
|
|
253
253
|
# TODO check basic auth header
|
254
254
|
url.should match 'http://test.uaa.target/oauth/token'
|
255
255
|
method.should == :post
|
256
|
-
reply = {:
|
257
|
-
:
|
256
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
257
|
+
scope: 'openid', expires_in: 98765}
|
258
258
|
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
259
259
|
end
|
260
260
|
cburi = 'http://call.back/uri_path'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cf-uaa-lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.14.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Syer
|
@@ -12,28 +12,28 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2022-01-14 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: multi_json
|
19
19
|
requirement: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
|
-
- - "~>"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 1.12.0
|
24
21
|
- - ">="
|
25
22
|
- !ruby/object:Gem::Version
|
26
23
|
version: 1.12.1
|
24
|
+
- - "<"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
27
|
type: :runtime
|
28
28
|
prerelease: false
|
29
29
|
version_requirements: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.12.0
|
34
31
|
- - ">="
|
35
32
|
- !ruby/object:Gem::Version
|
36
33
|
version: 1.12.1
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '1.16'
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: httpclient
|
39
39
|
requirement: !ruby/object:Gem::Requirement
|
@@ -54,74 +54,94 @@ dependencies:
|
|
54
54
|
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: 2.8.2.4
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: addressable
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 2.8.0
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '2.8'
|
67
|
+
type: :runtime
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 2.8.0
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '2.8'
|
57
77
|
- !ruby/object:Gem::Dependency
|
58
78
|
name: bundler
|
59
79
|
requirement: !ruby/object:Gem::Requirement
|
60
80
|
requirements:
|
61
81
|
- - "~>"
|
62
82
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
83
|
+
version: '2.2'
|
64
84
|
type: :development
|
65
85
|
prerelease: false
|
66
86
|
version_requirements: !ruby/object:Gem::Requirement
|
67
87
|
requirements:
|
68
88
|
- - "~>"
|
69
89
|
- !ruby/object:Gem::Version
|
70
|
-
version: '
|
90
|
+
version: '2.2'
|
71
91
|
- !ruby/object:Gem::Dependency
|
72
92
|
name: rake
|
73
93
|
requirement: !ruby/object:Gem::Requirement
|
74
94
|
requirements:
|
75
|
-
- - "~>"
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '10.3'
|
78
95
|
- - ">="
|
79
96
|
- !ruby/object:Gem::Version
|
80
97
|
version: 10.3.2
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '13.0'
|
81
101
|
type: :development
|
82
102
|
prerelease: false
|
83
103
|
version_requirements: !ruby/object:Gem::Requirement
|
84
104
|
requirements:
|
85
|
-
- - "~>"
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
version: '10.3'
|
88
105
|
- - ">="
|
89
106
|
- !ruby/object:Gem::Version
|
90
107
|
version: 10.3.2
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '13.0'
|
91
111
|
- !ruby/object:Gem::Dependency
|
92
112
|
name: rspec
|
93
113
|
requirement: !ruby/object:Gem::Requirement
|
94
114
|
requirements:
|
95
|
-
- - "~>"
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '2.14'
|
98
115
|
- - ">="
|
99
116
|
- !ruby/object:Gem::Version
|
100
117
|
version: 2.14.1
|
118
|
+
- - "~>"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '3.9'
|
101
121
|
type: :development
|
102
122
|
prerelease: false
|
103
123
|
version_requirements: !ruby/object:Gem::Requirement
|
104
124
|
requirements:
|
105
|
-
- - "~>"
|
106
|
-
- !ruby/object:Gem::Version
|
107
|
-
version: '2.14'
|
108
125
|
- - ">="
|
109
126
|
- !ruby/object:Gem::Version
|
110
127
|
version: 2.14.1
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '3.9'
|
111
131
|
- !ruby/object:Gem::Dependency
|
112
132
|
name: simplecov
|
113
133
|
requirement: !ruby/object:Gem::Requirement
|
114
134
|
requirements:
|
115
135
|
- - "~>"
|
116
136
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
137
|
+
version: 0.21.2
|
118
138
|
type: :development
|
119
139
|
prerelease: false
|
120
140
|
version_requirements: !ruby/object:Gem::Requirement
|
121
141
|
requirements:
|
122
142
|
- - "~>"
|
123
143
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.
|
144
|
+
version: 0.21.2
|
125
145
|
- !ruby/object:Gem::Dependency
|
126
146
|
name: simplecov-rcov
|
127
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,42 +160,56 @@ dependencies:
|
|
140
160
|
name: ci_reporter
|
141
161
|
requirement: !ruby/object:Gem::Requirement
|
142
162
|
requirements:
|
143
|
-
- - "~>"
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '1.9'
|
146
163
|
- - ">="
|
147
164
|
- !ruby/object:Gem::Version
|
148
165
|
version: 1.9.2
|
166
|
+
- - "~>"
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '2.0'
|
149
169
|
type: :development
|
150
170
|
prerelease: false
|
151
171
|
version_requirements: !ruby/object:Gem::Requirement
|
152
172
|
requirements:
|
153
|
-
- - "~>"
|
154
|
-
- !ruby/object:Gem::Version
|
155
|
-
version: '1.9'
|
156
173
|
- - ">="
|
157
174
|
- !ruby/object:Gem::Version
|
158
175
|
version: 1.9.2
|
176
|
+
- - "~>"
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: '2.0'
|
159
179
|
- !ruby/object:Gem::Dependency
|
160
180
|
name: json_pure
|
161
181
|
requirement: !ruby/object:Gem::Requirement
|
162
182
|
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: 1.8.1
|
163
186
|
- - "~>"
|
164
187
|
- !ruby/object:Gem::Version
|
165
|
-
version: '
|
188
|
+
version: '2.5'
|
189
|
+
type: :development
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
166
193
|
- - ">="
|
167
194
|
- !ruby/object:Gem::Version
|
168
195
|
version: 1.8.1
|
196
|
+
- - "~>"
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '2.5'
|
199
|
+
- !ruby/object:Gem::Dependency
|
200
|
+
name: ci_reporter_rspec
|
201
|
+
requirement: !ruby/object:Gem::Requirement
|
202
|
+
requirements:
|
203
|
+
- - "~>"
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '1.0'
|
169
206
|
type: :development
|
170
207
|
prerelease: false
|
171
208
|
version_requirements: !ruby/object:Gem::Requirement
|
172
209
|
requirements:
|
173
210
|
- - "~>"
|
174
211
|
- !ruby/object:Gem::Version
|
175
|
-
version: '1.
|
176
|
-
- - ">="
|
177
|
-
- !ruby/object:Gem::Version
|
178
|
-
version: 1.8.1
|
212
|
+
version: '1.0'
|
179
213
|
description: Client library for interacting with the CloudFoundry User Account and
|
180
214
|
Authorization (UAA) server. The UAA is an OAuth2 Authorization Server so it can
|
181
215
|
be used by webapps and command line apps to obtain access tokens to act on behalf
|
@@ -191,8 +225,10 @@ executables: []
|
|
191
225
|
extensions: []
|
192
226
|
extra_rdoc_files: []
|
193
227
|
files:
|
228
|
+
- ".github/dependabot.yml"
|
229
|
+
- ".github/workflows/gem-push.yml"
|
230
|
+
- ".github/workflows/ruby.yml"
|
194
231
|
- ".gitignore"
|
195
|
-
- ".travis.yml"
|
196
232
|
- ".yardopts"
|
197
233
|
- CHANGELOG.md
|
198
234
|
- Gemfile
|
@@ -235,16 +271,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
235
271
|
- !ruby/object:Gem::Version
|
236
272
|
version: '0'
|
237
273
|
requirements: []
|
238
|
-
|
239
|
-
rubygems_version: 2.6.11
|
274
|
+
rubygems_version: 3.0.3.1
|
240
275
|
signing_key:
|
241
276
|
specification_version: 4
|
242
277
|
summary: Client library for CloudFoundry UAA
|
243
|
-
test_files:
|
244
|
-
- spec/http_spec.rb
|
245
|
-
- spec/info_spec.rb
|
246
|
-
- spec/integration_spec.rb
|
247
|
-
- spec/scim_spec.rb
|
248
|
-
- spec/spec_helper.rb
|
249
|
-
- spec/token_coder_spec.rb
|
250
|
-
- spec/token_issuer_spec.rb
|
278
|
+
test_files: []
|