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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e2f468e6b75cdce97cb600bb02246bc498b91bd75b27cf5a34effa789eed4eb
4
- data.tar.gz: 766c4f441814d7131291b78c975d0ddb75c1b0bcbd63fb912aeefd5f2f05746e
3
+ metadata.gz: 283326148ca7b9df992d6ea50b3e0161ef74d07626147979c83a5c6e63daec86
4
+ data.tar.gz: b643f26e60fa73ccdee7e9fc7b89b3b3c010e9c9007be12e98e2f7493cc53a6d
5
5
  SHA512:
6
- metadata.gz: 23495a14122b6fb9c250da921c509e7c440f4800c0c37e961961f2e3441a2932912fe9b8f3ff6d42d918447275d6ee194efb14e037244f429db41a778e088195
7
- data.tar.gz: 34a60f7355afe29e0b5a7c1d69a43b9a1eae897acb197e167438ec7f95934d8e52ba022425bfbd0c0e8e574a4042c66930f30f00a9e9ecd61679dcda564a64c4
6
+ metadata.gz: 110d6d2259dcce0a7e1cac4ffbf24625a32c6a265fc5329adf4d2cc3ade2803aed54012021d8ad2238cd0aeb5259d3b973173671c7ca1b54fe6cc775146a5001
7
+ data.tar.gz: d217c50866d14c2eac62b03dbe406f572398f9f1d747b0a079c26236662474143d6d252b609df03c228123c677b299d1f3b8e700f49af7f0651561d3e715f647
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # CloudFoundry UAA Gem
2
- [![Build Status](https://travis-ci.org/cloudfoundry/cf-uaa-lib.png)](https://travis-ci.org/cloudfoundry/cf-uaa-lib)
2
+ ![Build status](https://github.com/cloudfoundry/cf-uaa-lib/actions/workflows/ruby.yml/badge.svg?branch=master)
3
3
  [![Gem Version](https://badge.fury.io/rb/cf-uaa-lib.png)](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
- $ gem install cf-uaa-lib
11
+ ```plain
12
+ gem install cf-uaa-lib
13
+ ```
12
14
 
13
15
  ## Build from source
14
16
 
15
- $ bundle install
16
- $ gem build cf-uaa-lib.gemspec
17
- $ gem install cf-uaa-lib<version>.gem
17
+ ```plain
18
+ bundle install
19
+ rake install
20
+ ```
18
21
 
19
22
  ## Use the gem
20
23
 
21
- #!/usr/bin/env ruby
22
- require 'uaa'
23
- token_issuer = CF::UAA::TokenIssuer.new("https://uaa.cloudfoundry.com", "vmc")
24
- puts token_issuer.prompts.inspect
25
- token = token_issuer.implicit_grant_with_creds(username: "<your_username>", password: "<your_password>")
26
- token_info = CF::UAA::TokenCoder.decode(token.info["access_token"], nil, nil, false) #token signature not verified
27
- puts token_info["user_name"]
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
- $ bundle exec rake test
131
+ ```plain
132
+ bundle exec rake test
133
+ ```
34
134
 
35
135
  Run the tests and see a fancy coverage report:
36
136
 
37
- $ bundle exec rake cov
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]
@@ -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
- return payload unless options[:verify]
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
- return payload if algo == 'none'
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
@@ -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
- 'authorization' => Http.basic_auth(@client_id, @client_secret) }
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
@@ -14,6 +14,6 @@
14
14
  # Cloud Foundry namespace
15
15
  module CF
16
16
  module UAA
17
- VERSION = '3.14.4'
17
+ VERSION = '4.0.1'
18
18
  end
19
19
  end
@@ -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
@@ -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', 'test_client', 'test_secret', options)
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
- # TODO check basic auth header
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
- # TODO check basic auth header
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
- # TODO check basic auth header
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
- # TODO check basic auth header
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: 3.14.4
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-14 00:00:00.000000000 Z
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