cf-uaa-lib 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/cf-uaa-lib.gemspec +1 -0
- data/lib/uaa/http.rb +45 -42
- data/lib/uaa/info.rb +0 -2
- data/lib/uaa/scim.rb +0 -2
- data/lib/uaa/token_coder.rb +19 -1
- data/lib/uaa/token_issuer.rb +0 -2
- data/lib/uaa/version.rb +1 -1
- data/spec/http_spec.rb +74 -55
- data/spec/info_spec.rb +36 -38
- data/spec/integration_spec.rb +173 -110
- data/spec/scim_spec.rb +75 -80
- data/spec/token_issuer_spec.rb +130 -135
- metadata +16 -3
- data/lib/uaa/proxy_options.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41d1d29707fa6dd64486407764cfaa01dddf0b39
|
4
|
+
data.tar.gz: 41a335fbc12c82462b3ca18ffbdd73613f89a7e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 171ff7b4dabbfe88d3939204fe5ea7d6142a9f5912d191979c78920d56b697d43810df85d80a6798702f3684c3414f1b0c0637d41fd772e08ab83a1f4cd50b56
|
7
|
+
data.tar.gz: 076de82f0fc91e1cb8388a310324fb66c86a017d24dbab52313167742400941b4b4865ccdb09f91844750f88edcbc8d2b88991cea7fbdbd7c92a75d5c8c68733
|
data/cf-uaa-lib.gemspec
CHANGED
data/lib/uaa/http.rb
CHANGED
@@ -12,9 +12,8 @@
|
|
12
12
|
#++
|
13
13
|
|
14
14
|
require 'base64'
|
15
|
-
require 'net/http'
|
16
15
|
require 'uaa/util'
|
17
|
-
require '
|
16
|
+
require 'httpclient'
|
18
17
|
|
19
18
|
module CF::UAA
|
20
19
|
|
@@ -46,11 +45,10 @@ class InvalidToken < TargetError; end
|
|
46
45
|
|
47
46
|
# Utility accessors and methods for objects that want to access JSON web APIs.
|
48
47
|
module Http
|
49
|
-
include ProxyOptions
|
50
48
|
|
51
49
|
def self.included(base)
|
52
50
|
base.class_eval do
|
53
|
-
attr_accessor :
|
51
|
+
attr_accessor :skip_ssl_validation, :ssl_ca_file, :ssl_cert_store, :zone
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
@@ -78,30 +76,30 @@ module Http
|
|
78
76
|
# @return [String]
|
79
77
|
def self.basic_auth(name, password)
|
80
78
|
str = "#{name}:#{password}"
|
81
|
-
|
82
|
-
Base64.strict_encode64(str): [str].pack(
|
79
|
+
'Basic ' + (Base64.respond_to?(:strict_encode64)?
|
80
|
+
Base64.strict_encode64(str): [str].pack('m').gsub(/\n/, ''))
|
83
81
|
end
|
84
82
|
|
85
|
-
JSON_UTF8 =
|
86
|
-
FORM_UTF8 =
|
83
|
+
JSON_UTF8 = 'application/json;charset=utf-8'
|
84
|
+
FORM_UTF8 = 'application/x-www-form-urlencoded;charset=utf-8'
|
87
85
|
|
88
86
|
private
|
89
87
|
|
90
88
|
def json_get(target, path = nil, style = nil, headers = {})
|
91
89
|
raise ArgumentError unless style.nil? || style.is_a?(Symbol)
|
92
|
-
json_parse_reply(style, *http_get(target, path, headers.merge(
|
90
|
+
json_parse_reply(style, *http_get(target, path, headers.merge('accept' => JSON_UTF8)))
|
93
91
|
end
|
94
92
|
|
95
93
|
def json_post(target, path, body, headers = {})
|
96
|
-
http_post(target, path, Util.json(body), headers.merge(
|
94
|
+
http_post(target, path, Util.json(body), headers.merge('content-type' => JSON_UTF8))
|
97
95
|
end
|
98
96
|
|
99
97
|
def json_put(target, path, body, headers = {})
|
100
|
-
http_put(target, path, Util.json(body), headers.merge(
|
98
|
+
http_put(target, path, Util.json(body), headers.merge('content-type' => JSON_UTF8))
|
101
99
|
end
|
102
100
|
|
103
101
|
def json_patch(target, path, body, headers = {})
|
104
|
-
http_patch(target, path, Util.json(body), headers.merge(
|
102
|
+
http_patch(target, path, Util.json(body), headers.merge('content-type' => JSON_UTF8))
|
105
103
|
end
|
106
104
|
|
107
105
|
def json_parse_reply(style, status, body, headers)
|
@@ -110,17 +108,17 @@ module Http
|
|
110
108
|
raise (status == 404 ? NotFound : BadResponse), "invalid status response: #{status}"
|
111
109
|
end
|
112
110
|
if body && !body.empty? && (status == 204 || headers.nil? ||
|
113
|
-
headers[
|
114
|
-
raise BadResponse,
|
111
|
+
headers['content-type'] !~ /application\/json/i)
|
112
|
+
raise BadResponse, 'received invalid response content or type'
|
115
113
|
end
|
116
114
|
parsed_reply = Util.json_parse(body, style)
|
117
115
|
if status >= 400
|
118
|
-
raise parsed_reply && parsed_reply[
|
119
|
-
InvalidToken.new(parsed_reply) : TargetError.new(parsed_reply),
|
116
|
+
raise parsed_reply && parsed_reply['error'] == 'invalid_token' ?
|
117
|
+
InvalidToken.new(parsed_reply) : TargetError.new(parsed_reply), 'error response'
|
120
118
|
end
|
121
119
|
parsed_reply
|
122
120
|
rescue DecodeError
|
123
|
-
raise BadResponse,
|
121
|
+
raise BadResponse, 'invalid JSON response'
|
124
122
|
end
|
125
123
|
|
126
124
|
def http_get(target, path = nil, headers = {}) request(target, :get, path, nil, headers) end
|
@@ -129,7 +127,7 @@ module Http
|
|
129
127
|
def http_patch(target, path, body, headers = {}) request(target, :patch, path, body, headers) end
|
130
128
|
|
131
129
|
def http_delete(target, path, authorization, zone = nil)
|
132
|
-
hdrs = {
|
130
|
+
hdrs = { 'authorization' => authorization }
|
133
131
|
hdrs['X-Identity-Zone-Subdomain'] = zone if zone
|
134
132
|
status = request(target, :delete, path, nil, hdrs)[0]
|
135
133
|
unless [200, 204].include?(status)
|
@@ -138,7 +136,7 @@ module Http
|
|
138
136
|
end
|
139
137
|
|
140
138
|
def request(target, method, path, body = nil, headers = {})
|
141
|
-
headers[
|
139
|
+
headers['accept'] = headers['content-type'] if headers['content-type'] && !headers['accept']
|
142
140
|
url = "#{target}#{path}"
|
143
141
|
|
144
142
|
logger.debug { "--->\nrequest: #{method} #{url}\n" +
|
@@ -156,44 +154,49 @@ module Http
|
|
156
154
|
end
|
157
155
|
|
158
156
|
def net_http_request(url, method, body, headers)
|
159
|
-
raise ArgumentError unless reqtype = {:delete => Net::HTTP::Delete,
|
160
|
-
:get => Net::HTTP::Get, :post => Net::HTTP::Post, :put => Net::HTTP::Put, :patch => Net::HTTP::Patch}[method]
|
161
|
-
headers["content-length"] = body.length if body
|
162
157
|
uri = URI.parse(url)
|
163
|
-
req = reqtype.new(uri.request_uri)
|
164
|
-
headers.each { |k, v| req[k] = v }
|
165
158
|
http = http_request(uri)
|
166
|
-
|
167
|
-
|
168
|
-
|
159
|
+
headers['content-length'] = body.length.to_s if body
|
160
|
+
case method
|
161
|
+
when :get, :delete
|
162
|
+
response = http.send(method, uri, nil, headers)
|
163
|
+
when :post, :put, :patch
|
164
|
+
response = http.send(method, uri, body, headers)
|
165
|
+
else
|
166
|
+
raise ArgumentError
|
167
|
+
end
|
169
168
|
|
169
|
+
unless response.status
|
170
|
+
raise HTTPException.new "Can't parse response from the server #{response.content}"
|
171
|
+
end
|
172
|
+
response_headers = {}
|
173
|
+
response.header.all.each { |k, v| response_headers[k.downcase] = v }
|
174
|
+
return [response.status.to_i, response.content, response_headers]
|
170
175
|
rescue OpenSSL::SSL::SSLError => e
|
171
176
|
raise SSLException, "Invalid SSL Cert for #{url}. Use '--skip-ssl-validation' to continue with an insecure target"
|
172
177
|
rescue URI::Error, SocketError, SystemCallError => e
|
173
178
|
raise BadTarget, "error: #{e.message}"
|
174
|
-
rescue Net::HTTPBadResponse => e
|
175
|
-
raise HTTPException, "HTTP exception: #{e.class}: #{e}"
|
176
179
|
end
|
177
180
|
|
178
181
|
def http_request(uri)
|
179
|
-
cache_key = URI.join(uri.to_s,
|
182
|
+
cache_key = URI.join(uri.to_s, '/')
|
180
183
|
@http_cache ||= {}
|
181
184
|
return @http_cache[cache_key] if @http_cache[cache_key]
|
182
185
|
|
183
|
-
http = Net::HTTP.new(uri.host, uri.port, *proxy_options_for(uri))
|
184
|
-
|
185
186
|
if uri.is_a?(URI::HTTPS)
|
186
|
-
http
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
187
|
+
http = HTTPClient.new.tap do |c|
|
188
|
+
if skip_ssl_validation
|
189
|
+
c.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
190
|
+
elsif ssl_ca_file
|
191
|
+
c.ssl_config.set_trust_ca File.expand_path(ssl_ca_file)
|
192
|
+
c.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
193
|
+
elsif ssl_cert_store
|
194
|
+
c.ssl_config.cert_store = ssl_cert_store
|
195
|
+
c.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
196
|
+
end
|
196
197
|
end
|
198
|
+
else
|
199
|
+
http = HTTPClient.new
|
197
200
|
end
|
198
201
|
|
199
202
|
@http_cache[cache_key] = http
|
data/lib/uaa/info.rb
CHANGED
@@ -35,8 +35,6 @@ class Info
|
|
35
35
|
self.ssl_ca_file = options[:ssl_ca_file]
|
36
36
|
self.ssl_cert_store = options[:ssl_cert_store]
|
37
37
|
self.symbolize_keys = options[:symbolize_keys]
|
38
|
-
self.http_proxy = options[:http_proxy]
|
39
|
-
self.https_proxy = options[:https_proxy]
|
40
38
|
end
|
41
39
|
|
42
40
|
# sets whether the keys in returned hashes should be symbols.
|
data/lib/uaa/scim.rb
CHANGED
@@ -109,8 +109,6 @@ class Scim
|
|
109
109
|
self.skip_ssl_validation = options[:skip_ssl_validation]
|
110
110
|
self.ssl_ca_file = options[:ssl_ca_file]
|
111
111
|
self.ssl_cert_store = options[:ssl_cert_store]
|
112
|
-
self.http_proxy = options[:http_proxy]
|
113
|
-
self.https_proxy = options[:https_proxy]
|
114
112
|
@zone = options[:zone]
|
115
113
|
end
|
116
114
|
|
data/lib/uaa/token_coder.rb
CHANGED
@@ -113,7 +113,7 @@ class TokenCoder
|
|
113
113
|
signature = Util.decode64(crypto_segment)
|
114
114
|
if ["HS256", "HS384", "HS512"].include?(algo)
|
115
115
|
raise InvalidSignature, "Signature verification failed" unless
|
116
|
-
options[:skey] && signature
|
116
|
+
options[:skey] && constant_time_compare(signature, OpenSSL::HMAC.digest(init_digest(algo), options[:skey], signing_input))
|
117
117
|
elsif ["RS256", "RS384", "RS512"].include?(algo)
|
118
118
|
raise InvalidSignature, "Signature verification failed" unless
|
119
119
|
options[:pkey] && options[:pkey].verify(init_digest(algo), signature, signing_input)
|
@@ -123,6 +123,24 @@ class TokenCoder
|
|
123
123
|
payload
|
124
124
|
end
|
125
125
|
|
126
|
+
# Takes constant time to compare 2 strings (HMAC digests in this case)
|
127
|
+
# to avoid timing attacks while comparing the HMAC digests
|
128
|
+
# @param [String] a: the first digest to compare
|
129
|
+
# @param [String] b: the second digest to compare
|
130
|
+
# @return [boolean] true if they are equal, false otherwise
|
131
|
+
def self.constant_time_compare(a, b)
|
132
|
+
if a.length != b.length
|
133
|
+
return false
|
134
|
+
end
|
135
|
+
|
136
|
+
result = 0
|
137
|
+
a.chars.zip(b.chars).each do |x, y|
|
138
|
+
result |= x.ord ^ y.ord
|
139
|
+
end
|
140
|
+
|
141
|
+
result == 0
|
142
|
+
end
|
143
|
+
|
126
144
|
# Creates a new token en/decoder for a service that is associated with
|
127
145
|
# the the audience_ids, the symmetrical token validation key, and the
|
128
146
|
# public and/or private keys.
|
data/lib/uaa/token_issuer.rb
CHANGED
@@ -112,8 +112,6 @@ class TokenIssuer
|
|
112
112
|
self.skip_ssl_validation = options[:skip_ssl_validation]
|
113
113
|
self.ssl_ca_file = options[:ssl_ca_file]
|
114
114
|
self.ssl_cert_store = options[:ssl_cert_store]
|
115
|
-
self.http_proxy = options[:http_proxy]
|
116
|
-
self.https_proxy = options[:https_proxy]
|
117
115
|
end
|
118
116
|
|
119
117
|
# Allows an app to discover what credentials are required for
|
data/lib/uaa/version.rb
CHANGED
data/spec/http_spec.rb
CHANGED
@@ -15,83 +15,102 @@ require 'spec_helper'
|
|
15
15
|
require 'uaa/http'
|
16
16
|
require 'uaa/version'
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
describe Http do
|
18
|
+
describe CF::UAA::Http do
|
21
19
|
|
22
20
|
class HttpTest
|
23
|
-
include Http
|
21
|
+
include CF::UAA::Http
|
24
22
|
|
25
23
|
public :http_get
|
26
24
|
end
|
27
25
|
|
28
26
|
let(:http_instance) { HttpTest.new }
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
let(:http_double) do
|
29
|
+
http_double = double('http').as_null_object
|
30
|
+
expect(HTTPClient).to receive(:new).and_return(http_double)
|
31
|
+
http_double
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:cert_store) { double('OpenSSL::X509::Store') }
|
35
|
+
|
36
|
+
describe 'set_request_handler' do
|
37
|
+
it 'sets a request handler' do
|
38
|
+
http_instance.set_request_handler do |url, method, body, headers|
|
39
|
+
[200, 'body', {'content-type' => 'text/plain'}]
|
40
|
+
end
|
41
|
+
status, body, resp_headers = http_instance.http_get('http://example.com')
|
42
|
+
status.should == 200
|
43
|
+
body.should == 'body'
|
44
|
+
resp_headers['content-type'].should == 'text/plain'
|
33
45
|
end
|
34
|
-
status, body, resp_headers = http_instance.http_get("http://example.com")
|
35
|
-
status.should == 200
|
36
|
-
body.should == "body"
|
37
|
-
resp_headers["content-type"].should == "text/plain"
|
38
46
|
end
|
39
47
|
|
40
|
-
|
41
|
-
reply_double = double('http reply', each_header: {}).as_null_object
|
42
|
-
http_double = double('http', request: reply_double, new: nil)
|
43
|
-
Net::HTTP.stub(:new).and_return(http_double)
|
44
|
-
http_instance.http_proxy = 'user:password@http-proxy.example.com:1234'
|
45
|
-
http_instance.https_proxy = 'user:password@https-proxy.example.com:1234'
|
48
|
+
describe 'http_get' do
|
46
49
|
|
47
|
-
|
50
|
+
context 'when response has no status' do
|
51
|
+
let(:response) { double('http::message') }
|
52
|
+
it 'raises an HTTPException error' do
|
53
|
+
expect(response).to receive(:status)
|
54
|
+
expect(response).to receive(:content).and_return('TEST')
|
55
|
+
expect(http_double).to receive(:get).and_return(response)
|
56
|
+
expect { http_instance.http_get('https://example.com') }.to raise_error(CF::UAA::HTTPException, "Can't parse response from the server TEST")
|
57
|
+
end
|
58
|
+
end
|
48
59
|
|
49
|
-
|
50
|
-
|
60
|
+
context 'when certificate is not valid' do
|
61
|
+
it 'raises an SSLException' do
|
62
|
+
expect(http_double).to receive(:get).and_raise(OpenSSL::SSL::SSLError)
|
51
63
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
http_double.stub(:request).and_raise(OpenSSL::SSL::SSLError)
|
64
|
+
expect { http_instance.http_get('https://example.com') }.to raise_error(CF::UAA::SSLException)
|
65
|
+
end
|
66
|
+
end
|
56
67
|
|
57
|
-
|
58
|
-
|
68
|
+
context 'when skipping ssl validation' do
|
69
|
+
let(:ssl_config) { double('ssl_config') }
|
59
70
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
71
|
+
it 'sets verify mode to VERIFY_NONE' do
|
72
|
+
http_instance.skip_ssl_validation = true
|
73
|
+
|
74
|
+
expect(http_double).to receive(:ssl_config).and_return(ssl_config)
|
75
|
+
expect(ssl_config).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
76
|
+
http_instance.http_get('https://uncached.example.com')
|
77
|
+
end
|
78
|
+
end
|
64
79
|
|
65
|
-
|
66
|
-
|
80
|
+
context 'when validating ssl' do
|
81
|
+
it 'does not set verify mode' do
|
82
|
+
expect(http_double).not_to receive(:ssl_config)
|
83
|
+
http_instance.http_get('https://example.com')
|
84
|
+
end
|
85
|
+
end
|
67
86
|
|
68
|
-
|
69
|
-
http_instance.http_get("https://uncached.example.com")
|
70
|
-
expect(http_double).to have_received(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
71
|
-
end
|
87
|
+
context 'when ssl certificate is provided' do
|
72
88
|
|
73
|
-
|
74
|
-
http_double = double('http').as_null_object
|
75
|
-
Net::HTTP.stub(:new).and_return(http_double)
|
89
|
+
let(:ssl_config) { double('ssl_config') }
|
76
90
|
|
77
|
-
|
78
|
-
|
91
|
+
it 'passes it' do
|
92
|
+
http_instance.ssl_ca_file = '/fake-ca-file'
|
79
93
|
|
80
|
-
|
81
|
-
|
82
|
-
|
94
|
+
expect(http_double).to receive(:ssl_config).and_return(ssl_config).twice
|
95
|
+
expect(ssl_config).to receive(:set_trust_ca).with('/fake-ca-file')
|
96
|
+
expect(ssl_config).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
|
83
97
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
Net::HTTP.stub(:new).and_return(http_double)
|
98
|
+
http_instance.http_get('https://uncached.example.com')
|
99
|
+
end
|
100
|
+
end
|
88
101
|
|
89
|
-
|
90
|
-
|
102
|
+
context 'when ssl cert store is provided' do
|
103
|
+
let(:ssl_config) { double('ssl_config') }
|
91
104
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
105
|
+
it 'passes it' do
|
106
|
+
http_instance.ssl_cert_store = cert_store
|
107
|
+
|
108
|
+
expect(http_double).to receive(:ssl_config).and_return(ssl_config).twice
|
109
|
+
expect(ssl_config).to receive(:cert_store=).with(cert_store)
|
110
|
+
expect(ssl_config).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
|
96
111
|
|
97
|
-
|
112
|
+
http_instance.http_get('https://uncached.example.com')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/spec/info_spec.rb
CHANGED
@@ -19,44 +19,42 @@ module CF::UAA
|
|
19
19
|
describe Info do
|
20
20
|
let(:options) { {} }
|
21
21
|
let(:uaa_info) { Info.new(target, options) }
|
22
|
-
let(:target) {
|
22
|
+
let(:target) { 'https://login.cloudfoundry.com' }
|
23
23
|
let(:authorization) { nil }
|
24
24
|
|
25
25
|
before do
|
26
26
|
uaa_info.set_request_handler do |url, method, body, headers|
|
27
27
|
url.should == target_url
|
28
28
|
method.should == :get
|
29
|
-
headers[
|
30
|
-
headers[
|
31
|
-
headers[
|
32
|
-
[200, response_body, {
|
29
|
+
headers['content-type'].should be_nil
|
30
|
+
headers['accept'].gsub(/\s/, '').should =~ /application\/json;charset=utf-8/i
|
31
|
+
headers['authorization'].should == authorization
|
32
|
+
[200, response_body, {'content-type' => 'application/json'}]
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
describe
|
37
|
-
let(:options) { {:
|
36
|
+
describe 'initialize' do
|
37
|
+
let(:options) { {:skip_ssl_validation => true} }
|
38
38
|
|
39
|
-
it
|
40
|
-
uaa_info.http_proxy.should == 'http-proxy.com'
|
41
|
-
uaa_info.https_proxy.should == 'https-proxy.com'
|
39
|
+
it 'sets proxy information' do
|
42
40
|
uaa_info.skip_ssl_validation == true
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
46
|
-
describe
|
47
|
-
let(:target_url) {
|
44
|
+
describe 'getting server info' do
|
45
|
+
let(:target_url) { 'https://login.cloudfoundry.com/login' }
|
48
46
|
let(:response_body) { '{"commit_id":"12345","prompts":["one","two"]}' }
|
49
47
|
|
50
|
-
it
|
48
|
+
it 'gets server info' do
|
51
49
|
result = uaa_info.server
|
52
|
-
result[
|
53
|
-
result[
|
50
|
+
result['prompts'].should_not be_nil
|
51
|
+
result['commit_id'].should_not be_nil
|
54
52
|
end
|
55
53
|
|
56
|
-
context
|
54
|
+
context 'with symbolize_keys keys true' do
|
57
55
|
let(:options) { {:symbolize_keys => true} }
|
58
56
|
|
59
|
-
it
|
57
|
+
it 'gets server info' do
|
60
58
|
result = uaa_info.server
|
61
59
|
result[:prompts].should_not be_nil
|
62
60
|
result[:commit_id].should_not be_nil
|
@@ -64,63 +62,63 @@ module CF::UAA
|
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
67
|
-
describe
|
68
|
-
let(:target) {
|
69
|
-
let(:target_url) {
|
65
|
+
describe 'getting UAA target' do
|
66
|
+
let(:target) { 'https://login.cloudfoundry.com' }
|
67
|
+
let(:target_url) { 'https://login.cloudfoundry.com/login' }
|
70
68
|
let(:response_body) { '{"links":{"uaa":"https://uaa.cloudfoundry.com"},"prompts":["one","two"]}' }
|
71
69
|
|
72
|
-
it
|
70
|
+
it 'gets UAA target' do
|
73
71
|
result = uaa_info.discover_uaa
|
74
|
-
result.should ==
|
72
|
+
result.should == 'https://uaa.cloudfoundry.com'
|
75
73
|
end
|
76
74
|
|
77
75
|
context "when there is no 'links' key present" do
|
78
|
-
let(:target) {
|
79
|
-
let(:target_url) {
|
76
|
+
let(:target) { 'https://uaa.cloudfoundry.com' }
|
77
|
+
let(:target_url) { 'https://uaa.cloudfoundry.com/login' }
|
80
78
|
let(:response_body) { '{ "prompts" : ["one","two"]} ' }
|
81
79
|
|
82
|
-
it
|
80
|
+
it 'returns the target url' do
|
83
81
|
result = uaa_info.discover_uaa
|
84
|
-
result.should ==
|
82
|
+
result.should == 'https://uaa.cloudfoundry.com'
|
85
83
|
end
|
86
84
|
end
|
87
85
|
|
88
|
-
context
|
86
|
+
context 'with symbolize_keys keys true' do
|
89
87
|
let(:options) { {:symbolize_keys => true} }
|
90
88
|
|
91
|
-
it
|
89
|
+
it 'gets UAA target' do
|
92
90
|
result = uaa_info.discover_uaa
|
93
|
-
result.should ==
|
91
|
+
result.should == 'https://uaa.cloudfoundry.com'
|
94
92
|
end
|
95
93
|
end
|
96
94
|
end
|
97
95
|
|
98
|
-
describe
|
99
|
-
let(:target_url) {
|
96
|
+
describe 'whoami' do
|
97
|
+
let(:target_url) { 'https://login.cloudfoundry.com/userinfo?schema=openid' }
|
100
98
|
let(:response_body) { '{"user_id":"1111-1111-1111-1111","user_name":"user","given_name":"first","family_name":"last","name":"first last","email":"email@example.com"}' }
|
101
99
|
let(:authorization) { 'authentication_token' }
|
102
100
|
|
103
|
-
it
|
101
|
+
it 'returns the user info' do
|
104
102
|
result = uaa_info.whoami(authorization)
|
105
103
|
result['email'].should == 'email@example.com'
|
106
104
|
end
|
107
105
|
end
|
108
106
|
|
109
|
-
describe
|
110
|
-
let(:target_url) {
|
107
|
+
describe 'validation_key' do
|
108
|
+
let(:target_url) { 'https://login.cloudfoundry.com/token_key' }
|
111
109
|
let(:response_body) { '{"alg":"SHA256withRSA","value":"-----BEGIN PUBLIC KEY-----\nabc123\n-----END PUBLIC KEY-----\n"}' }
|
112
110
|
|
113
|
-
it
|
111
|
+
it 'returns the key data' do
|
114
112
|
result = uaa_info.validation_key(authorization)
|
115
113
|
result['alg'].should == 'SHA256withRSA'
|
116
114
|
end
|
117
115
|
end
|
118
116
|
|
119
|
-
describe
|
120
|
-
let(:target_url) {
|
117
|
+
describe 'validation keys' do
|
118
|
+
let(:target_url) { 'https://login.cloudfoundry.com/token_keys' }
|
121
119
|
let(:response_body) { '{ "keys": [ { "kid": "the_key", "alg": "SHA256withRSA", "value": "-----BEGIN PUBLIC KEY-----\nabc123\n-----END PUBLIC KEY-----\n", "kty": "RSA", "use": "sig", "n": "Ufn7Qc", "e": "EEXZ" }, { "kid": "the_other_key", "alg": "SHA256withRSA", "value": "-----BEGIN PUBLIC KEY-----\ndef456\n-----END PUBLIC KEY-----\n", "kty": "RSA", "use": "sig", "n": "AMcW9/P", "e": "AQAB" } ] }' }
|
122
120
|
|
123
|
-
it
|
121
|
+
it 'returns a hash of keys' do
|
124
122
|
result = uaa_info.validation_keys_hash(authorization)
|
125
123
|
|
126
124
|
the_key = result['the_key']
|