cf-uaa-lib 3.14.4 → 4.0.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.
- checksums.yaml +4 -4
- data/README.md +116 -14
- data/examples/password_grant_and_decode_token.rb +53 -0
- data/lib/uaa/info.rb +0 -8
- data/lib/uaa/token_coder.rb +23 -6
- data/lib/uaa/token_issuer.rb +9 -2
- data/lib/uaa/version.rb +1 -1
- data/spec/token_coder_spec.rb +6 -0
- data/spec/token_issuer_spec.rb +38 -6
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 283326148ca7b9df992d6ea50b3e0161ef74d07626147979c83a5c6e63daec86
|
4
|
+
data.tar.gz: b643f26e60fa73ccdee7e9fc7b89b3b3c010e9c9007be12e98e2f7493cc53a6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 110d6d2259dcce0a7e1cac4ffbf24625a32c6a265fc5329adf4d2cc3ade2803aed54012021d8ad2238cd0aeb5259d3b973173671c7ca1b54fe6cc775146a5001
|
7
|
+
data.tar.gz: d217c50866d14c2eac62b03dbe406f572398f9f1d747b0a079c26236662474143d6d252b609df03c228123c677b299d1f3b8e700f49af7f0651561d3e715f647
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# CloudFoundry UAA Gem
|
2
|
-
|
2
|
+

|
3
3
|
[](http://badge.fury.io/rb/cf-uaa-lib)
|
4
4
|
|
5
5
|
Client gem for interacting with the [CloudFoundry UAA server](https://github.com/cloudfoundry/uaa)
|
@@ -8,31 +8,133 @@ For documentation see: https://rubygems.org/gems/cf-uaa-lib
|
|
8
8
|
|
9
9
|
## Install from rubygems
|
10
10
|
|
11
|
-
|
11
|
+
```plain
|
12
|
+
gem install cf-uaa-lib
|
13
|
+
```
|
12
14
|
|
13
15
|
## Build from source
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
```plain
|
18
|
+
bundle install
|
19
|
+
rake install
|
20
|
+
```
|
18
21
|
|
19
22
|
## Use the gem
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
Create a UAA client that allows users to authenticate with username/password and allow client application to use `openid` scope to invoke `/userinfo` endpoint for the user.
|
25
|
+
|
26
|
+
```plain
|
27
|
+
uaa create-client decode-token-demo -s decode-token-demo -v \
|
28
|
+
--authorized_grant_types password,refresh_token \
|
29
|
+
--scope "openid" \
|
30
|
+
--authorities uaa.none
|
31
|
+
```
|
32
|
+
|
33
|
+
Create a user with which to authorize our `decode-token-demo` client application.
|
34
|
+
|
35
|
+
```plain
|
36
|
+
uaa create-user myuser \
|
37
|
+
--email myuser@example.com \
|
38
|
+
--givenName "My" \
|
39
|
+
--familyName "User" \
|
40
|
+
--password myuser_secret
|
41
|
+
```
|
42
|
+
|
43
|
+
Create this Ruby script (script is available at `examples/password_grant_and_decode_token.rb`):
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
#!/usr/bin/env ruby
|
47
|
+
|
48
|
+
require 'uaa'
|
49
|
+
|
50
|
+
url = ENV["UAA_URL"]
|
51
|
+
client, secret = "decode-token-demo", "decode-token-demo"
|
52
|
+
username, password = ENV["UAA_USERNAME"], ENV["UAA_PASSWORD"]
|
53
|
+
|
54
|
+
def show(title, object)
|
55
|
+
puts "#{title}: #{object.inspect}"
|
56
|
+
puts
|
57
|
+
end
|
58
|
+
|
59
|
+
uaa_options = {}
|
60
|
+
uaa_options[:ssl_ca_file] = ENV["UAA_CA_CERT_FILE"] if ENV["UAA_CA_CERT_FILE"]
|
61
|
+
show "uaa_options", uaa_options
|
62
|
+
|
63
|
+
uaa_info = CF::UAA::Info.new(url, uaa_options)
|
64
|
+
show "UAA server info", uaa_info.server
|
65
|
+
|
66
|
+
token_keys = uaa_info.validation_keys_hash
|
67
|
+
show "Signing keys for access tokens", token_keys
|
68
|
+
|
69
|
+
token_issuer = CF::UAA::TokenIssuer.new(url, client, secret, uaa_options)
|
70
|
+
show "Login prompts", token_issuer.prompts
|
71
|
+
|
72
|
+
token = token_issuer.owner_password_grant(username, password, "openid")
|
73
|
+
show "User '#{username}' password grant", token
|
74
|
+
|
75
|
+
auth_header = "bearer #{token.info["access_token"]}"
|
76
|
+
show "Auth header for resource server API calls", auth_header
|
77
|
+
|
78
|
+
userinfo = uaa_info.whoami(auth_header)
|
79
|
+
show "User info", userinfo
|
80
|
+
|
81
|
+
last_exception = nil
|
82
|
+
token_keys.each_pair do |keyname, token_key|
|
83
|
+
begin
|
84
|
+
token_coder = CF::UAA::TokenCoder.new(uaa_options.merge(pkey: token_key["value"], verify: true))
|
85
|
+
token_info = token_coder.decode(auth_header)
|
86
|
+
show "Decoded access token", token_info
|
87
|
+
last_exception = nil
|
88
|
+
rescue CF::UAA::Decode => e
|
89
|
+
last_exception = e
|
90
|
+
end
|
91
|
+
end
|
92
|
+
raise last_exception if last_exception
|
93
|
+
```
|
94
|
+
|
95
|
+
To run the script, setup the env vars for your UAA and run the ruby script:
|
96
|
+
|
97
|
+
```bash
|
98
|
+
export UAA_URL=https://192.168.50.6:8443
|
99
|
+
export UAA_CA_CERT_FILE=/path/to/ca.pem
|
100
|
+
export UAA_USERNAME=myuser
|
101
|
+
export UAA_PASSWORD=myuser_secret
|
102
|
+
ruby examples/password_grant_and_decode_token.rb
|
103
|
+
```
|
104
|
+
|
105
|
+
The output will look similar to:
|
106
|
+
|
107
|
+
```plain
|
108
|
+
uaa_options: {:ssl_ca_file=>"/var/folders/wd/xnncwqp96rj0v1y2nms64mq80000gn/T/tmp.R6wpXYdC/ca.pem"}
|
109
|
+
|
110
|
+
UAA server info: {"app"=>{"version"=>"4.19.0"}, "links"=>{"uaa"=>"https://192.168.50.6:8443", "passwd"=>"/forgot_password", "login"=>"https://192.168.50.6:8443", "register"=>"/create_account"}, "zone_name"=>"uaa", "entityID"=>"192.168.50.6:8443", "commit_id"=>"7897100", "idpDefinitions"=>{}, "prompts"=>{"username"=>["text", "Email"], "password"=>["password", "Password"]}, "timestamp"=>"2018-06-13T12:02:09-0700"}
|
111
|
+
|
112
|
+
Cookie#domain returns dot-less domain name now. Use Cookie#dot_domain if you need "." at the beginning.
|
113
|
+
Signing keys for access tokens: {"uaa-jwt-key-1"=>{"kty"=>"RSA", "e"=>"AQAB", "use"=>"sig", "kid"=>"uaa-jwt-key-1", "alg"=>"RS256", "value"=>"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8UNioYHjhyi1qSHrnBZ9\nKE96/1jLBOX2UTShGBo8jP7eDD6zUh5DNHNPAwD1V8gNI4wvNAm+zL1MrSEDWzn2\nPvCANd+XydoNVZU1zhqxvGhoxHmgAA3JbgSS3oLLNDG/HH8wEnjAxb+G1uh2EVSF\nAe/euQ/fEmY4e7uOG34h9WMX84tD1Sf/xvVoNGAL8bTwotzBLFZ12M3P70hrKDi5\n9wEBbY5bllvvNFyjZTYwMbw97RIOdg3FQkOABu8ENCqbPks5gqSpNV33ekaX4rAd\nwYdEX5iUzDBdMyD8jqUopuqTXqBKg2/ealGitXdbSIEAvcBgZWnn1j2vFp6OEYBB\n7wIDAQAB\n-----END PUBLIC KEY-----", "n"=>"APFDYqGB44cotakh65wWfShPev9YywTl9lE0oRgaPIz-3gw-s1IeQzRzTwMA9VfIDSOMLzQJvsy9TK0hA1s59j7wgDXfl8naDVWVNc4asbxoaMR5oAANyW4Ekt6CyzQxvxx_MBJ4wMW_htbodhFUhQHv3rkP3xJmOHu7jht-IfVjF_OLQ9Un_8b1aDRgC_G08KLcwSxWddjNz-9Iayg4ufcBAW2OW5Zb7zRco2U2MDG8Pe0SDnYNxUJDgAbvBDQqmz5LOYKkqTVd93pGl-KwHcGHRF-YlMwwXTMg_I6lKKbqk16gSoNv3mpRorV3W0iBAL3AYGVp59Y9rxaejhGAQe8"}}
|
114
|
+
|
115
|
+
Login prompts: {"username"=>["text", "Email"], "password"=>["password", "Password"]}
|
116
|
+
|
117
|
+
User 'myuser' password grant: #<CF::UAA::TokenInfo:0x00007fbad5a12c18 @info={"access_token"=>"eyJhbGciOiJSUzI1NiIsImtpZCI6InVhYS1qd3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiJlMTFlZmMwNjI1OGQ0MzA0YTc4ZGIyNzliYjJjMzQ1OCIsInN1YiI6IjM5NzhmZjRkLWQ3MzgtNGI4Yi05OTA4LTdhZTE0N2YzYzNiZSIsInNjb3BlIjpbIm9wZW5pZCJdLCJjbGllbnRfaWQiOiJkZWNvZGUtdG9rZW4tZGVtbyIsImNpZCI6ImRlY29kZS10b2tlbi1kZW1vIiwiYXpwIjoiZGVjb2RlLXRva2VuLWRlbW8iLCJncmFudF90eXBlIjoicGFzc3dvcmQiLCJ1c2VyX2lkIjoiMzk3OGZmNGQtZDczOC00YjhiLTk5MDgtN2FlMTQ3ZjNjM2JlIiwib3JpZ2luIjoidWFhIiwidXNlcl9uYW1lIjoibXl1c2VyIiwiZW1haWwiOiJteXVzZXJAZXhhbXBsZS5jb20iLCJhdXRoX3RpbWUiOjE1MzE2MzAxNDgsInJldl9zaWciOiI5M2E2NzkwNCIsImlhdCI6MTUzMTYzMDE0OCwiZXhwIjoxNTMxNjczMzQ4LCJpc3MiOiJodHRwczovLzE5Mi4xNjguNTAuNjo4NDQzL29hdXRoL3Rva2VuIiwiemlkIjoidWFhIiwiYXVkIjpbIm9wZW5pZCIsImRlY29kZS10b2tlbi1kZW1vIl19.qtbzxCOW5bebTgMLK-71_zxaT7l5PSmxhXcDtCeA64dZZ6-wXXmJivopm5PFEHnHiZwRpVe43jyEsbJGzBdl8GEsYQ9YIy51-4noby7ClziJv-6rSBYZnZuU5A234QRWclATGksOcz8Ft9PTIKGKLScyLhncwas7W0uiNJ87MFBGWY6Ovvl3Ac5-jHCqiRBXD6vUhzpfmy6_OUr53i9zJgtcQQWgDrOHxnFcRABZcDnhnWdcxh-Hbagtt9dQU46QgpqLJiUvAg-7ypZPGrxnr9UQEO2Q9GrolkbrSeUcfUOkgppxaA_0b6RYpgBR1qg-Ns6jGUxFgPs6Jj8pysfVmA", "token_type"=>"bearer", "refresh_token"=>"6701ddb9397840a1bd339e9f4314448f-r", "expires_in"=>43199, "scope"=>"openid", "jti"=>"e11efc06258d4304a78db279bb2c3458"}>
|
118
|
+
|
119
|
+
Auth header for resource server API calls: "bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InVhYS1qd3Qta2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiJlMTFlZmMwNjI1OGQ0MzA0YTc4ZGIyNzliYjJjMzQ1OCIsInN1YiI6IjM5NzhmZjRkLWQ3MzgtNGI4Yi05OTA4LTdhZTE0N2YzYzNiZSIsInNjb3BlIjpbIm9wZW5pZCJdLCJjbGllbnRfaWQiOiJkZWNvZGUtdG9rZW4tZGVtbyIsImNpZCI6ImRlY29kZS10b2tlbi1kZW1vIiwiYXpwIjoiZGVjb2RlLXRva2VuLWRlbW8iLCJncmFudF90eXBlIjoicGFzc3dvcmQiLCJ1c2VyX2lkIjoiMzk3OGZmNGQtZDczOC00YjhiLTk5MDgtN2FlMTQ3ZjNjM2JlIiwib3JpZ2luIjoidWFhIiwidXNlcl9uYW1lIjoibXl1c2VyIiwiZW1haWwiOiJteXVzZXJAZXhhbXBsZS5jb20iLCJhdXRoX3RpbWUiOjE1MzE2MzAxNDgsInJldl9zaWciOiI5M2E2NzkwNCIsImlhdCI6MTUzMTYzMDE0OCwiZXhwIjoxNTMxNjczMzQ4LCJpc3MiOiJodHRwczovLzE5Mi4xNjguNTAuNjo4NDQzL29hdXRoL3Rva2VuIiwiemlkIjoidWFhIiwiYXVkIjpbIm9wZW5pZCIsImRlY29kZS10b2tlbi1kZW1vIl19.qtbzxCOW5bebTgMLK-71_zxaT7l5PSmxhXcDtCeA64dZZ6-wXXmJivopm5PFEHnHiZwRpVe43jyEsbJGzBdl8GEsYQ9YIy51-4noby7ClziJv-6rSBYZnZuU5A234QRWclATGksOcz8Ft9PTIKGKLScyLhncwas7W0uiNJ87MFBGWY6Ovvl3Ac5-jHCqiRBXD6vUhzpfmy6_OUr53i9zJgtcQQWgDrOHxnFcRABZcDnhnWdcxh-Hbagtt9dQU46QgpqLJiUvAg-7ypZPGrxnr9UQEO2Q9GrolkbrSeUcfUOkgppxaA_0b6RYpgBR1qg-Ns6jGUxFgPs6Jj8pysfVmA"
|
120
|
+
|
121
|
+
User info: {"user_id"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be", "user_name"=>"myuser", "name"=>"My User", "given_name"=>"My", "family_name"=>"User", "email"=>"myuser@example.com", "email_verified"=>true, "previous_logon_time"=>nil, "sub"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be"}
|
122
|
+
|
123
|
+
Decoded access token: {"jti"=>"e11efc06258d4304a78db279bb2c3458", "sub"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be", "scope"=>["openid"], "client_id"=>"decode-token-demo", "cid"=>"decode-token-demo", "azp"=>"decode-token-demo", "grant_type"=>"password", "user_id"=>"3978ff4d-d738-4b8b-9908-7ae147f3c3be", "origin"=>"uaa", "user_name"=>"myuser", "email"=>"myuser@example.com", "auth_time"=>1531630148, "rev_sig"=>"93a67904", "iat"=>1531630148, "exp"=>1531673348, "iss"=>"https://192.168.50.6:8443/oauth/token", "zid"=>"uaa", "aud"=>["openid", "decode-token-demo"]}
|
124
|
+
>>>>>>> 21ae635... new example script - password grant + decode using token keys
|
125
|
+
```
|
28
126
|
|
29
127
|
## Tests
|
30
128
|
|
31
129
|
Run the tests with rake:
|
32
130
|
|
33
|
-
|
131
|
+
```plain
|
132
|
+
bundle exec rake test
|
133
|
+
```
|
34
134
|
|
35
135
|
Run the tests and see a fancy coverage report:
|
36
136
|
|
37
|
-
|
137
|
+
```plain
|
138
|
+
bundle exec rake cov
|
139
|
+
```
|
38
140
|
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# uaa create-client decode-token-demo -s decode-token-demo -v \
|
4
|
+
# --authorized_grant_types password,refresh_token \
|
5
|
+
# --scope "openid" \
|
6
|
+
# --authorities uaa.none
|
7
|
+
|
8
|
+
require 'uaa'
|
9
|
+
|
10
|
+
url = ENV["UAA_URL"]
|
11
|
+
client, secret = "decode-token-demo", "decode-token-demo"
|
12
|
+
username, password = ENV["UAA_USERNAME"], ENV["UAA_PASSWORD"]
|
13
|
+
|
14
|
+
def show(title, object)
|
15
|
+
puts "#{title}: #{object.inspect}"
|
16
|
+
puts
|
17
|
+
end
|
18
|
+
|
19
|
+
uaa_options = {}
|
20
|
+
uaa_options[:ssl_ca_file] = ENV["UAA_CA_CERT_FILE"] if ENV["UAA_CA_CERT_FILE"]
|
21
|
+
show "uaa_options", uaa_options
|
22
|
+
|
23
|
+
uaa_info = CF::UAA::Info.new(url, uaa_options)
|
24
|
+
show "UAA server info", uaa_info.server
|
25
|
+
|
26
|
+
token_keys = uaa_info.validation_keys_hash
|
27
|
+
show "Signing keys for access tokens", token_keys
|
28
|
+
|
29
|
+
token_issuer = CF::UAA::TokenIssuer.new(url, client, secret, uaa_options)
|
30
|
+
show "Login prompts", token_issuer.prompts
|
31
|
+
|
32
|
+
token = token_issuer.owner_password_grant(username, password, "openid")
|
33
|
+
show "User '#{username}' password grant", token
|
34
|
+
|
35
|
+
auth_header = "bearer #{token.info["access_token"]}"
|
36
|
+
show "Auth header for resource server API calls", auth_header
|
37
|
+
|
38
|
+
userinfo = uaa_info.whoami(auth_header)
|
39
|
+
show "User info", userinfo
|
40
|
+
|
41
|
+
last_exception = nil
|
42
|
+
token_keys.each_pair do |keyname, token_key|
|
43
|
+
begin
|
44
|
+
token_coder = CF::UAA::TokenCoder.new(uaa_options.merge(pkey: token_key["value"], verify: true))
|
45
|
+
token_info = token_coder.decode(auth_header)
|
46
|
+
show "Decoded access token", token_info
|
47
|
+
last_exception = nil
|
48
|
+
rescue CF::UAA::Decode => e
|
49
|
+
last_exception = e
|
50
|
+
end
|
51
|
+
end
|
52
|
+
raise last_exception if last_exception
|
53
|
+
|
data/lib/uaa/info.rb
CHANGED
@@ -54,14 +54,6 @@ class Info
|
|
54
54
|
json_get(target, "/userinfo?schema=openid", key_style, "authorization" => auth_header)
|
55
55
|
end
|
56
56
|
|
57
|
-
# Gets various monitoring and status variables from the server.
|
58
|
-
# Authenticates using +name+ and +pwd+ for basic authentication.
|
59
|
-
# @param (see Misc.server)
|
60
|
-
# @return [Hash]
|
61
|
-
def varz(name, pwd)
|
62
|
-
json_get(target, "/varz", key_style, "authorization" => Http.basic_auth(name, pwd))
|
63
|
-
end
|
64
|
-
|
65
57
|
# Gets basic information about the target server, including version number,
|
66
58
|
# commit ID, and links to API endpoints.
|
67
59
|
# @return [Hash]
|
data/lib/uaa/token_coder.rb
CHANGED
@@ -64,8 +64,8 @@ class TokenCoder
|
|
64
64
|
def self.encode(token_body, options = {}, obsolete1 = nil, obsolete2 = nil)
|
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
|
-
warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
|
68
|
-
options = {skey: options
|
67
|
+
warn "WARNING: #{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
|
68
|
+
options = {skey: options}
|
69
69
|
options[:pkey], options[:algorithm] = obsolete1, obsolete2
|
70
70
|
end
|
71
71
|
options = normalize_options(options)
|
@@ -95,8 +95,8 @@ class TokenCoder
|
|
95
95
|
def self.decode(token, options = {}, obsolete1 = nil, obsolete2 = nil)
|
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
|
-
warn "#{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
|
99
|
-
options = {skey: options
|
98
|
+
warn "WARNING: #{self.class}##{__method__} is deprecated with these parameters. Please use options hash."
|
99
|
+
options = {skey: options}
|
100
100
|
options[:pkey], options[:verify] = obsolete1, obsolete2
|
101
101
|
end
|
102
102
|
options = normalize_options(options)
|
@@ -106,10 +106,16 @@ class TokenCoder
|
|
106
106
|
signing_input = [header_segment, payload_segment].join('.')
|
107
107
|
header = Util.json_decode64(header_segment)
|
108
108
|
payload = Util.json_decode64(payload_segment, (:sym if options[:symbolize_keys]))
|
109
|
-
|
109
|
+
unless options[:verify]
|
110
|
+
warn "WARNING: Decoding token without verifying it was signed by its authoring UAA"
|
111
|
+
return payload
|
112
|
+
end
|
110
113
|
raise SignatureNotAccepted, "Signature algorithm not accepted" unless
|
111
114
|
options[:accept_algorithms].include?(algo = header["alg"])
|
112
|
-
|
115
|
+
if algo == 'none'
|
116
|
+
warn "WARNING: Decoding token that explicitly states it has not been signed by an authoring UAA"
|
117
|
+
return payload
|
118
|
+
end
|
113
119
|
signature = Util.decode64(crypto_segment)
|
114
120
|
if ["HS256", "HS384", "HS512"].include?(algo)
|
115
121
|
raise InvalidSignature, "Signature verification failed" unless
|
@@ -123,6 +129,17 @@ class TokenCoder
|
|
123
129
|
payload
|
124
130
|
end
|
125
131
|
|
132
|
+
# Decodes a JWT token to extract its expiry time
|
133
|
+
# @param [String] token A JWT token as returned by {TokenCoder.encode}
|
134
|
+
# @return [Integer] exp expiry timestamp
|
135
|
+
def self.decode_token_expiry(token)
|
136
|
+
segments = token.split('.')
|
137
|
+
raise InvalidTokenFormat, "Not enough or too many segments" unless [2,3].include? segments.length
|
138
|
+
header_segment, payload_segment, crypto_segment = segments
|
139
|
+
payload = Util.json_decode64(payload_segment, :sym)
|
140
|
+
payload[:exp]
|
141
|
+
end
|
142
|
+
|
126
143
|
# Takes constant time to compare 2 strings (HMAC digests in this case)
|
127
144
|
# to avoid timing attacks while comparing the HMAC digests
|
128
145
|
# @param [String] a: the first digest to compare
|
data/lib/uaa/token_issuer.rb
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
|
14
14
|
require 'securerandom'
|
15
15
|
require 'uaa/http'
|
16
|
+
require 'cgi'
|
16
17
|
|
17
18
|
module CF::UAA
|
18
19
|
|
@@ -72,8 +73,13 @@ class TokenIssuer
|
|
72
73
|
if scope = Util.arglist(params.delete(:scope))
|
73
74
|
params[:scope] = Util.strlist(scope)
|
74
75
|
end
|
75
|
-
headers = {'content-type' => FORM_UTF8, 'accept' => JSON_UTF8
|
76
|
-
|
76
|
+
headers = {'content-type' => FORM_UTF8, 'accept' => JSON_UTF8}
|
77
|
+
if @basic_auth
|
78
|
+
headers['authorization'] = Http.basic_auth(@client_id, @client_secret)
|
79
|
+
else
|
80
|
+
headers['X-CF-ENCODED-CREDENTIALS'] = 'true'
|
81
|
+
headers['authorization'] = Http.basic_auth(CGI.escape(@client_id || ''), CGI.escape(@client_secret || ''))
|
82
|
+
end
|
77
83
|
reply = json_parse_reply(@key_style, *request(@token_target, :post,
|
78
84
|
'/oauth/token', Util.encode_form(params), headers))
|
79
85
|
raise BadResponse unless reply[jkey :token_type] && reply[jkey :access_token]
|
@@ -109,6 +115,7 @@ class TokenIssuer
|
|
109
115
|
@target, @client_id, @client_secret = target, client_id, client_secret
|
110
116
|
@token_target = options[:token_target] || target
|
111
117
|
@key_style = options[:symbolize_keys] ? :sym : nil
|
118
|
+
@basic_auth = options[:basic_auth] == true ? true : false
|
112
119
|
initialize_http_options(options)
|
113
120
|
end
|
114
121
|
|
data/lib/uaa/version.rb
CHANGED
data/spec/token_coder_spec.rb
CHANGED
@@ -183,6 +183,12 @@ describe TokenCoder do
|
|
183
183
|
info["id"].should_not be_nil
|
184
184
|
info["email"].should == "olds@vmware.com"
|
185
185
|
end
|
186
|
+
|
187
|
+
it "decodes only the expiry_at time" do
|
188
|
+
exp = Time.now.to_i + 60
|
189
|
+
tkn = subject.encode({'foo' => "bar", 'exp' => exp })
|
190
|
+
TokenCoder.decode_token_expiry("bEaReR #{tkn}").should == exp
|
191
|
+
end
|
186
192
|
end
|
187
193
|
|
188
194
|
end
|
data/spec/token_issuer_spec.rb
CHANGED
@@ -23,13 +23,16 @@ describe TokenIssuer do
|
|
23
23
|
|
24
24
|
before do
|
25
25
|
#Util.default_logger(:trace)
|
26
|
-
@issuer = TokenIssuer.new('http://test.uaa.target',
|
26
|
+
@issuer = TokenIssuer.new('http://test.uaa.target', client_id, client_secret, options)
|
27
27
|
end
|
28
28
|
|
29
|
+
let(:client_id) { 'test_client' }
|
30
|
+
let(:client_secret) { 'test!secret' }
|
31
|
+
|
29
32
|
subject { @issuer }
|
30
33
|
|
31
34
|
describe 'initialize' do
|
32
|
-
let(:options) { {http_proxy: 'http-proxy.com', https_proxy: 'https-proxy.com', skip_ssl_validation: true} }
|
35
|
+
let(:options) { {http_proxy: 'http-proxy.com', https_proxy: 'https-proxy.com', skip_ssl_validation: true, basic_auth: false} }
|
33
36
|
|
34
37
|
it 'sets skip_ssl_validation' do
|
35
38
|
subject.skip_ssl_validation == true
|
@@ -42,7 +45,8 @@ describe TokenIssuer do
|
|
42
45
|
subject.set_request_handler do |url, method, body, headers|
|
43
46
|
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
44
47
|
headers['accept'].should =~ /application\/json/
|
45
|
-
|
48
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
|
49
|
+
headers['authorization'].should == 'Basic dGVzdF9jbGllbnQ6dGVzdCUyMXNlY3JldA=='
|
46
50
|
url.should == 'http://test.uaa.target/oauth/token'
|
47
51
|
method.should == :post
|
48
52
|
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
@@ -89,7 +93,8 @@ describe TokenIssuer do
|
|
89
93
|
subject.set_request_handler do |url, method, body, headers|
|
90
94
|
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
91
95
|
headers['accept'].should =~ /application\/json/
|
92
|
-
|
96
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
|
97
|
+
headers['authorization'].should == 'Basic dGVzdF9jbGllbnQ6dGVzdCUyMXNlY3JldA=='
|
93
98
|
url.should == 'http://test.uaa.target/oauth/token'
|
94
99
|
method.should == :post
|
95
100
|
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
@@ -104,11 +109,37 @@ describe TokenIssuer do
|
|
104
109
|
token.info['expires_in'].should == 98765
|
105
110
|
end
|
106
111
|
|
112
|
+
context "when client & client secret are nil" do
|
113
|
+
let(:client_id) { nil }
|
114
|
+
let(:client_secret) { nil }
|
115
|
+
|
116
|
+
it 'does not error' do
|
117
|
+
subject.set_request_handler do |url, method, body, headers|
|
118
|
+
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
119
|
+
headers['accept'].should =~ /application\/json/
|
120
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
|
121
|
+
headers['authorization'].should == 'Basic Og=='
|
122
|
+
url.should == 'http://test.uaa.target/oauth/token'
|
123
|
+
method.should == :post
|
124
|
+
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
125
|
+
scope: 'openid', expires_in: 98765}
|
126
|
+
[200, Util.json(reply), {'content-type' => 'application/json'}]
|
127
|
+
end
|
128
|
+
token = subject.owner_password_grant('joe+admin', "?joe's%password$@ ", 'openid')
|
129
|
+
token.should be_an_instance_of TokenInfo
|
130
|
+
token.info['access_token'].should == 'test_access_token'
|
131
|
+
token.info['token_type'].should =~ /^bearer$/i
|
132
|
+
token.info['scope'].should == 'openid'
|
133
|
+
token.info['expires_in'].should == 98765
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
107
137
|
it 'gets a token with passcode' do
|
108
138
|
subject.set_request_handler do |url, method, body, headers|
|
109
139
|
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
110
140
|
headers['accept'].should =~ /application\/json/
|
111
|
-
|
141
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
|
142
|
+
headers['authorization'].should == 'Basic dGVzdF9jbGllbnQ6dGVzdCUyMXNlY3JldA=='
|
112
143
|
url.should == 'http://test.uaa.target/oauth/token'
|
113
144
|
body.should =~ /(^|&)passcode=12345($|&)/
|
114
145
|
body.should =~ /(^|&)grant_type=password($|&)/
|
@@ -250,7 +281,8 @@ describe TokenIssuer do
|
|
250
281
|
subject.set_request_handler do |url, method, body, headers|
|
251
282
|
headers['content-type'].should =~ /application\/x-www-form-urlencoded/
|
252
283
|
headers['accept'].should =~ /application\/json/
|
253
|
-
|
284
|
+
headers['X-CF-ENCODED-CREDENTIALS'].should == 'true'
|
285
|
+
headers['authorization'].should == 'Basic dGVzdF9jbGllbnQ6dGVzdCUyMXNlY3JldA=='
|
254
286
|
url.should match 'http://test.uaa.target/oauth/token'
|
255
287
|
method.should == :post
|
256
288
|
reply = {access_token: 'test_access_token', token_type: 'BEARER',
|
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
|
+
version: 4.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Syer
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2022-01-
|
15
|
+
date: 2022-01-28 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: multi_json
|
@@ -237,6 +237,7 @@ files:
|
|
237
237
|
- README.md
|
238
238
|
- Rakefile
|
239
239
|
- cf-uaa-lib.gemspec
|
240
|
+
- examples/password_grant_and_decode_token.rb
|
240
241
|
- lib/uaa.rb
|
241
242
|
- lib/uaa/http.rb
|
242
243
|
- lib/uaa/info.rb
|