cf-uaa-lib 4.0.8 → 4.0.10
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.
- checksums.yaml +4 -4
- data/.github/workflows/gem-push.yml +1 -1
- data/.github/workflows/ruby.yml +3 -2
- data/cf-uaa-lib.gemspec +3 -1
- data/lib/uaa/http.rb +1 -1
- data/lib/uaa/scim.rb +7 -1
- data/lib/uaa/token_issuer.rb +18 -2
- data/lib/uaa/version.rb +1 -1
- data/spec/scim_spec.rb +15 -3
- data/spec/token_issuer_spec.rb +80 -0
- metadata +20 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 749fd6ab4d2b12f32a72cd0b15a573c051c42c779676c9c4a0f9582a74d13836
|
|
4
|
+
data.tar.gz: acbdddc308c40af517a356ffa492160ca39c5b97c84da512b2be966f184c329c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f1f81e5eff52915a9cb233b56dc00ead5ff773c4311f76434e516cce33277afa13c3956212686e43e781333451085a259c5fa7a816d2eb6e8c5b439c57e8868
|
|
7
|
+
data.tar.gz: 451e6cb18fdd7226f320bca418a5e243c6c6e9768dafa57063f6236217178d23f775fb72a176dfa7f750469a0e25c7c4524ff7598c2a5f50f0b113aad31b1497
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -11,11 +11,12 @@ jobs:
|
|
|
11
11
|
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
strategy:
|
|
14
|
+
fail-fast: false
|
|
14
15
|
matrix:
|
|
15
|
-
ruby-version: ['
|
|
16
|
+
ruby-version: ['3.3', '3.4', '4.0']
|
|
16
17
|
|
|
17
18
|
steps:
|
|
18
|
-
- uses: actions/checkout@
|
|
19
|
+
- uses: actions/checkout@v7
|
|
19
20
|
- name: Set up Ruby
|
|
20
21
|
uses: ruby/setup-ruby@v1
|
|
21
22
|
with:
|
data/cf-uaa-lib.gemspec
CHANGED
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
|
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
27
|
s.license = "Apache-2.0"
|
|
28
|
+
s.required_ruby_version = '>= 3.3'
|
|
28
29
|
s.files = `git ls-files`.split("\n")
|
|
29
30
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
30
31
|
s.executables = `git ls-files -- bin/*`.split('\n').map{ |f| File.basename(f) }
|
|
@@ -34,10 +35,11 @@ Gem::Specification.new do |s|
|
|
|
34
35
|
s.add_dependency 'json', '~>2.7'
|
|
35
36
|
s.add_dependency 'mutex_m'
|
|
36
37
|
s.add_dependency 'base64'
|
|
38
|
+
s.add_dependency 'logger'
|
|
37
39
|
s.add_dependency 'httpclient', '~> 2.8', '>= 2.8.2.4'
|
|
38
40
|
s.add_dependency 'addressable', '~> 2.8', '>= 2.8.0'
|
|
39
41
|
|
|
40
|
-
s.add_development_dependency 'bundler', '
|
|
42
|
+
s.add_development_dependency 'bundler', '>= 2.2'
|
|
41
43
|
s.add_development_dependency 'rake', '>= 10.3.2', '~> 13.0'
|
|
42
44
|
s.add_development_dependency 'rspec', '>= 2.14.1', '~> 3.9'
|
|
43
45
|
s.add_development_dependency 'simplecov', '~> 0.22.0'
|
data/lib/uaa/http.rb
CHANGED
|
@@ -184,7 +184,7 @@ module Http
|
|
|
184
184
|
raise SSLException, "Invalid SSL Cert for #{url}. Use '--skip-ssl-validation' to continue with an insecure target"
|
|
185
185
|
rescue URI::Error, SocketError, SystemCallError => e
|
|
186
186
|
raise BadTarget, "error: #{e.message}"
|
|
187
|
-
rescue HTTPClient::ConnectTimeoutError
|
|
187
|
+
rescue HTTPClient::ConnectTimeoutError, HTTPClient::ReceiveTimeoutError
|
|
188
188
|
raise HTTPException.new "http timeout"
|
|
189
189
|
end
|
|
190
190
|
|
data/lib/uaa/scim.rb
CHANGED
|
@@ -380,13 +380,19 @@ class Scim
|
|
|
380
380
|
# @param [String] jwks the JSON Web Key Set
|
|
381
381
|
# @param [String] kid If changeMode is DELETE provide the id of key
|
|
382
382
|
# @param [String] changeMode Change mode, possible is ADD, UPDATE, DELETE
|
|
383
|
+
# @param [String] iss Issuer in case of federation JWT trust
|
|
384
|
+
# @param [String] sub Subject in case of federation JWT trust
|
|
385
|
+
# @param [String] aud Audience in case of federation JWT trust
|
|
383
386
|
# @return [Hash] success message from server
|
|
384
|
-
def change_clientjwt(client_id, jwks_uri = nil, jwks = nil, kid = nil, changeMode = nil)
|
|
387
|
+
def change_clientjwt(client_id, jwks_uri = nil, jwks = nil, kid = nil, changeMode = nil, iss = nil, sub = nil, aud = nil)
|
|
385
388
|
req = {"client_id" => client_id }
|
|
386
389
|
req["jwks_uri"] = jwks_uri if jwks_uri
|
|
387
390
|
req["jwks"] = jwks if jwks
|
|
388
391
|
req["kid"] = kid if kid
|
|
389
392
|
req["changeMode"] = changeMode if changeMode
|
|
393
|
+
req["iss"] = iss if iss
|
|
394
|
+
req["sub"] = sub if sub
|
|
395
|
+
req["aud"] = aud if aud
|
|
390
396
|
json_parse_reply(@key_style, *json_put(@target,
|
|
391
397
|
"#{type_info(:client, :path)}/#{Addressable::URI.encode(client_id)}/clientjwt", req, headers))
|
|
392
398
|
end
|
data/lib/uaa/token_issuer.rb
CHANGED
|
@@ -75,6 +75,8 @@ class TokenIssuer
|
|
|
75
75
|
if scope = Util.arglist(params.delete(:scope))
|
|
76
76
|
params[:scope] = Util.strlist(scope)
|
|
77
77
|
end
|
|
78
|
+
client_assertion = params[:client_assertion]
|
|
79
|
+
params.delete(:client_assertion)
|
|
78
80
|
headers = {'content-type' => FORM_UTF8, 'accept' => JSON_UTF8}
|
|
79
81
|
if @client_auth_method == 'client_secret_basic' && @client_secret && @client_id
|
|
80
82
|
if @basic_auth
|
|
@@ -88,6 +90,10 @@ class TokenIssuer
|
|
|
88
90
|
params[:client_secret] = @client_secret
|
|
89
91
|
elsif @client_id && params[:code_verifier]
|
|
90
92
|
params[:client_id] = @client_id
|
|
93
|
+
elsif client_assertion && @client_id && @client_secret.nil?
|
|
94
|
+
params[:client_id] = @client_id
|
|
95
|
+
params[:client_assertion] = client_assertion
|
|
96
|
+
params[:client_assertion_type] = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
91
97
|
else
|
|
92
98
|
headers['X-CF-ENCODED-CREDENTIALS'] = 'true'
|
|
93
99
|
headers['authorization'] = Http.basic_auth(CGI.escape(@client_id || ''), CGI.escape(@client_secret || ''))
|
|
@@ -129,6 +135,7 @@ class TokenIssuer
|
|
|
129
135
|
# * +:symbolize_keys+, if true, returned hash keys are symbols.
|
|
130
136
|
def initialize(target, client_id, client_secret = nil, options = {})
|
|
131
137
|
@target, @client_id, @client_secret = target, client_id, client_secret
|
|
138
|
+
@client_assertion = options[:client_assertion] || nil
|
|
132
139
|
@token_target = options[:token_target] || target
|
|
133
140
|
@key_style = options[:symbolize_keys] ? :sym : nil
|
|
134
141
|
@basic_auth = options[:basic_auth] == true ? true : false
|
|
@@ -310,8 +317,8 @@ class TokenIssuer
|
|
|
310
317
|
# Uses the instance client credentials to get a token with a client
|
|
311
318
|
# credentials grant. See http://tools.ietf.org/html/rfc6749#section-4.4
|
|
312
319
|
# @return [TokenInfo]
|
|
313
|
-
def client_credentials_grant(scope = nil)
|
|
314
|
-
request_token(grant_type: 'client_credentials', scope: scope)
|
|
320
|
+
def client_credentials_grant(scope = nil, client_assertion = nil)
|
|
321
|
+
request_token(grant_type: 'client_credentials', scope: scope, client_assertion: client_assertion)
|
|
315
322
|
end
|
|
316
323
|
|
|
317
324
|
# Uses the instance client credentials and the given +refresh_token+ to get
|
|
@@ -321,6 +328,15 @@ class TokenIssuer
|
|
|
321
328
|
request_token(grant_type: 'refresh_token', refresh_token: refresh_token, scope: scope)
|
|
322
329
|
end
|
|
323
330
|
|
|
331
|
+
# Gets an access token with the user assertion used for authentication
|
|
332
|
+
# via the jwt bearer authorization grant.
|
|
333
|
+
# See {http://tools.ietf.org/html/rfc7523#section-2.1}.
|
|
334
|
+
# @param assertion should be an id_token from a previous IdP token request
|
|
335
|
+
# @return [TokenInfo]
|
|
336
|
+
def jwt_bearer_grant(assertion, scope = nil, client_assertion = nil)
|
|
337
|
+
request_token(grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: assertion, scope: scope, client_assertion: client_assertion)
|
|
338
|
+
end
|
|
339
|
+
|
|
324
340
|
end
|
|
325
341
|
|
|
326
342
|
end
|
data/lib/uaa/version.rb
CHANGED
data/spec/scim_spec.rb
CHANGED
|
@@ -184,6 +184,18 @@ describe Scim do
|
|
|
184
184
|
result['id'].should == 'id12345'
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
+
it "add federated client's jwt trust using issuer, subject and audience" do
|
|
188
|
+
subject.set_request_handler do |url, method, body, headers|
|
|
189
|
+
url.should == "#{@target}/oauth/clients/id12345/clientjwt"
|
|
190
|
+
method.should == :put
|
|
191
|
+
check_headers(headers, :json, :json, nil)
|
|
192
|
+
body.should include('"iss":"issuer"', '"sub":"subject"', '"aud":"audience"')
|
|
193
|
+
[200, '{"id":"id12345"}', {'content-type' => 'application/json'}]
|
|
194
|
+
end
|
|
195
|
+
result = subject.change_clientjwt('id12345', jwks_uri=nil, jwks=nil, kid=nil, changemod='ADD', iss='issuer', sub='subject', aud='audience')
|
|
196
|
+
result['id'].should == 'id12345'
|
|
197
|
+
end
|
|
198
|
+
|
|
187
199
|
it 'unlocks a user' do
|
|
188
200
|
subject.set_request_handler do |url, method, body, headers|
|
|
189
201
|
url.should == "#{@target}/Users/id12345/status"
|
|
@@ -285,9 +297,9 @@ describe Scim do
|
|
|
285
297
|
method.should == :get
|
|
286
298
|
check_headers(headers, nil, :json, nil)
|
|
287
299
|
|
|
288
|
-
query_params =
|
|
289
|
-
start_index = query_params['startIndex']
|
|
290
|
-
count = query_params['count']
|
|
300
|
+
query_params = URI.decode_www_form(URI.parse(url).query).to_h
|
|
301
|
+
start_index = query_params['startIndex']
|
|
302
|
+
count = query_params['count']
|
|
291
303
|
|
|
292
304
|
start_index.should == '3'
|
|
293
305
|
count.should == '10'
|
data/spec/token_issuer_spec.rb
CHANGED
|
@@ -345,6 +345,37 @@ describe TokenIssuer do
|
|
|
345
345
|
end
|
|
346
346
|
end
|
|
347
347
|
|
|
348
|
+
context 'with client_assertion using client credentials grant' do
|
|
349
|
+
let(:client_id) { 'test_client' }
|
|
350
|
+
let(:client_secret) { nil }
|
|
351
|
+
|
|
352
|
+
it 'use client_secret_post in authorization code and expect client_id and secret in body' do
|
|
353
|
+
subject.set_request_handler do |url, method, body, headers|
|
|
354
|
+
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
|
355
|
+
headers['accept'].should =~ /application\/json/
|
|
356
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should_not
|
|
357
|
+
headers['authorization'].should_not
|
|
358
|
+
params = Util.decode_form(body)
|
|
359
|
+
params['grant_type'].should == 'client_credentials'
|
|
360
|
+
params['client_id'].should == 'test_client'
|
|
361
|
+
params['client_assertion'].should == 'any-jwt-token'
|
|
362
|
+
params['client_assertion_type'].should == 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
363
|
+
params['client_secret'].should_not
|
|
364
|
+
url.should match 'http://test.uaa.target/oauth/token'
|
|
365
|
+
method.should == :post
|
|
366
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
|
367
|
+
scope: 'logs.read', expires_in: 98765}
|
|
368
|
+
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
|
369
|
+
end
|
|
370
|
+
token = subject.client_credentials_grant('logs.read', 'any-jwt-token')
|
|
371
|
+
token.should be_an_instance_of TokenInfo
|
|
372
|
+
token.info['access_token'].should == 'test_access_token'
|
|
373
|
+
token.info['token_type'].should =~ /^bearer$/i
|
|
374
|
+
token.info['scope'].should == 'logs.read'
|
|
375
|
+
token.info['expires_in'].should == 98765
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
348
379
|
context 'pkce with own code verifier' do
|
|
349
380
|
let(:options) { {basic_auth: false, code_verifier: 'umoq1e_4XMYXvfHlaO9mSlSI17OKfxnwfR5ZD-oYreFxyn8yQZ-ZHPZfUZ4n3WjY_tkOB_MAisSy4ddqsa6aoTU5ZOcX4ps3de933PczYlC8pZpKL8EQWaDZOnpOyB2W'} }
|
|
350
381
|
|
|
@@ -439,6 +470,55 @@ describe TokenIssuer do
|
|
|
439
470
|
end
|
|
440
471
|
end
|
|
441
472
|
|
|
473
|
+
context 'with jwt bearer grant' do
|
|
474
|
+
|
|
475
|
+
it 'gets a token with jwt bearer' do
|
|
476
|
+
subject.set_request_handler do |url, method, body, headers|
|
|
477
|
+
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
|
478
|
+
headers['accept'].should =~ /application\/json/
|
|
479
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
|
|
480
|
+
headers['authorization'].should == 'Basic dGVzdF9jbGllbnQ6dGVzdCUyMXNlY3JldA=='
|
|
481
|
+
url.should == 'http://test.uaa.target/oauth/token'
|
|
482
|
+
method.should == :post
|
|
483
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
|
484
|
+
scope: 'openid', expires_in: 98765}
|
|
485
|
+
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
|
486
|
+
end
|
|
487
|
+
token = subject.jwt_bearer_grant('assertion', 'openid')
|
|
488
|
+
token.should be_an_instance_of TokenInfo
|
|
489
|
+
token.info['access_token'].should == 'test_access_token'
|
|
490
|
+
token.info['token_type'].should =~ /^bearer$/i
|
|
491
|
+
token.info['scope'].should == 'openid'
|
|
492
|
+
token.info['expires_in'].should == 98765
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
context "when client & client secret are nil" do
|
|
496
|
+
let(:client_id) { nil }
|
|
497
|
+
let(:client_secret) { nil }
|
|
498
|
+
|
|
499
|
+
it 'does not error' do
|
|
500
|
+
subject.set_request_handler do |url, method, body, headers|
|
|
501
|
+
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
|
502
|
+
headers['accept'].should =~ /application\/json/
|
|
503
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
|
|
504
|
+
headers['authorization'].should == 'Basic Og=='
|
|
505
|
+
url.should == 'http://test.uaa.target/oauth/token'
|
|
506
|
+
method.should == :post
|
|
507
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
|
508
|
+
scope: 'openid', expires_in: 98765}
|
|
509
|
+
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
|
510
|
+
end
|
|
511
|
+
token = subject.jwt_bearer_grant('assertion', 'openid')
|
|
512
|
+
token.should be_an_instance_of TokenInfo
|
|
513
|
+
token.info['access_token'].should == 'test_access_token'
|
|
514
|
+
token.info['token_type'].should =~ /^bearer$/i
|
|
515
|
+
token.info['scope'].should == 'openid'
|
|
516
|
+
token.info['expires_in'].should == 98765
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
end
|
|
521
|
+
|
|
442
522
|
end
|
|
443
523
|
|
|
444
524
|
end
|
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: 4.0.
|
|
4
|
+
version: 4.0.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dave Syer
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
- Luke Taylor
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date:
|
|
14
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: json
|
|
@@ -55,6 +55,20 @@ dependencies:
|
|
|
55
55
|
- - ">="
|
|
56
56
|
- !ruby/object:Gem::Version
|
|
57
57
|
version: '0'
|
|
58
|
+
- !ruby/object:Gem::Dependency
|
|
59
|
+
name: logger
|
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '0'
|
|
65
|
+
type: :runtime
|
|
66
|
+
prerelease: false
|
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '0'
|
|
58
72
|
- !ruby/object:Gem::Dependency
|
|
59
73
|
name: httpclient
|
|
60
74
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -99,14 +113,14 @@ dependencies:
|
|
|
99
113
|
name: bundler
|
|
100
114
|
requirement: !ruby/object:Gem::Requirement
|
|
101
115
|
requirements:
|
|
102
|
-
- - "
|
|
116
|
+
- - ">="
|
|
103
117
|
- !ruby/object:Gem::Version
|
|
104
118
|
version: '2.2'
|
|
105
119
|
type: :development
|
|
106
120
|
prerelease: false
|
|
107
121
|
version_requirements: !ruby/object:Gem::Requirement
|
|
108
122
|
requirements:
|
|
109
|
-
- - "
|
|
123
|
+
- - ">="
|
|
110
124
|
- !ruby/object:Gem::Version
|
|
111
125
|
version: '2.2'
|
|
112
126
|
- !ruby/object:Gem::Dependency
|
|
@@ -266,14 +280,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
266
280
|
requirements:
|
|
267
281
|
- - ">="
|
|
268
282
|
- !ruby/object:Gem::Version
|
|
269
|
-
version: '
|
|
283
|
+
version: '3.3'
|
|
270
284
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
271
285
|
requirements:
|
|
272
286
|
- - ">="
|
|
273
287
|
- !ruby/object:Gem::Version
|
|
274
288
|
version: '0'
|
|
275
289
|
requirements: []
|
|
276
|
-
rubygems_version:
|
|
290
|
+
rubygems_version: 4.0.10
|
|
277
291
|
specification_version: 4
|
|
278
292
|
summary: Client library for CloudFoundry UAA
|
|
279
293
|
test_files: []
|