adal 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +7 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +25 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +97 -0
  8. data/Rakefile +39 -0
  9. data/adal.gemspec +52 -0
  10. data/contributing.md +127 -0
  11. data/lib/adal.rb +24 -0
  12. data/lib/adal/authentication_context.rb +202 -0
  13. data/lib/adal/authentication_parameters.rb +126 -0
  14. data/lib/adal/authority.rb +165 -0
  15. data/lib/adal/cache_driver.rb +171 -0
  16. data/lib/adal/cached_token_response.rb +190 -0
  17. data/lib/adal/client_assertion.rb +63 -0
  18. data/lib/adal/client_assertion_certificate.rb +89 -0
  19. data/lib/adal/client_credential.rb +46 -0
  20. data/lib/adal/core_ext.rb +26 -0
  21. data/lib/adal/core_ext/hash.rb +34 -0
  22. data/lib/adal/jwt_parameters.rb +39 -0
  23. data/lib/adal/logger.rb +90 -0
  24. data/lib/adal/logging.rb +98 -0
  25. data/lib/adal/memory_cache.rb +95 -0
  26. data/lib/adal/mex_request.rb +52 -0
  27. data/lib/adal/mex_response.rb +141 -0
  28. data/lib/adal/noop_cache.rb +38 -0
  29. data/lib/adal/oauth_request.rb +76 -0
  30. data/lib/adal/request_parameters.rb +48 -0
  31. data/lib/adal/self_signed_jwt_factory.rb +96 -0
  32. data/lib/adal/templates/rst.13.xml.erb +35 -0
  33. data/lib/adal/templates/rst.2005.xml.erb +32 -0
  34. data/lib/adal/token_request.rb +231 -0
  35. data/lib/adal/token_response.rb +144 -0
  36. data/lib/adal/user_assertion.rb +57 -0
  37. data/lib/adal/user_credential.rb +152 -0
  38. data/lib/adal/user_identifier.rb +83 -0
  39. data/lib/adal/user_information.rb +49 -0
  40. data/lib/adal/util.rb +49 -0
  41. data/lib/adal/version.rb +36 -0
  42. data/lib/adal/wstrust_request.rb +100 -0
  43. data/lib/adal/wstrust_response.rb +168 -0
  44. data/lib/adal/xml_namespaces.rb +64 -0
  45. data/samples/authorization_code_example/README.md +10 -0
  46. data/samples/authorization_code_example/web_app.rb +139 -0
  47. data/samples/client_assertion_certificate_example/README.md +42 -0
  48. data/samples/client_assertion_certificate_example/app.rb +55 -0
  49. data/samples/on_behalf_of_example/README.md +35 -0
  50. data/samples/on_behalf_of_example/native_app.rb +52 -0
  51. data/samples/on_behalf_of_example/web_api.rb +71 -0
  52. data/samples/user_credentials_example/README.md +7 -0
  53. data/samples/user_credentials_example/app.rb +52 -0
  54. data/spec/adal/authentication_context_spec.rb +186 -0
  55. data/spec/adal/authentication_parameters_spec.rb +107 -0
  56. data/spec/adal/authority_spec.rb +122 -0
  57. data/spec/adal/cache_driver_spec.rb +191 -0
  58. data/spec/adal/cached_token_response_spec.rb +148 -0
  59. data/spec/adal/client_assertion_certificate_spec.rb +113 -0
  60. data/spec/adal/client_assertion_spec.rb +38 -0
  61. data/spec/adal/core_ext/hash_spec.rb +47 -0
  62. data/spec/adal/logging_spec.rb +48 -0
  63. data/spec/adal/memory_cache_spec.rb +107 -0
  64. data/spec/adal/mex_request_spec.rb +57 -0
  65. data/spec/adal/mex_response_spec.rb +143 -0
  66. data/spec/adal/self_signed_jwt_factory_spec.rb +63 -0
  67. data/spec/adal/token_request_spec.rb +150 -0
  68. data/spec/adal/token_response_spec.rb +102 -0
  69. data/spec/adal/user_credential_spec.rb +125 -0
  70. data/spec/adal/user_identifier_spec.rb +115 -0
  71. data/spec/adal/wstrust_request_spec.rb +51 -0
  72. data/spec/adal/wstrust_response_spec.rb +152 -0
  73. data/spec/fixtures/mex/insecureaddress.xml +924 -0
  74. data/spec/fixtures/mex/invalid_namespaces.xml +916 -0
  75. data/spec/fixtures/mex/malformed.xml +914 -0
  76. data/spec/fixtures/mex/microsoft.xml +916 -0
  77. data/spec/fixtures/mex/multiple_endpoints.xml +922 -0
  78. data/spec/fixtures/mex/no_matching_bindings.xml +916 -0
  79. data/spec/fixtures/mex/no_username_token_policies.xml +914 -0
  80. data/spec/fixtures/mex/no_wstrust_endpoints.xml +838 -0
  81. data/spec/fixtures/mex/only_13.xml +842 -0
  82. data/spec/fixtures/mex/only_2005.xml +842 -0
  83. data/spec/fixtures/oauth/error.json +1 -0
  84. data/spec/fixtures/oauth/success.json +1 -0
  85. data/spec/fixtures/oauth/success_with_id_token.json +1 -0
  86. data/spec/fixtures/wstrust/error.xml +24 -0
  87. data/spec/fixtures/wstrust/invalid_namespaces.xml +136 -0
  88. data/spec/fixtures/wstrust/missing_security_tokens.xml +90 -0
  89. data/spec/fixtures/wstrust/success.xml +136 -0
  90. data/spec/fixtures/wstrust/token.xml +1 -0
  91. data/spec/fixtures/wstrust/too_many_security_tokens.xml +219 -0
  92. data/spec/fixtures/wstrust/unrecognized_token_type.xml +136 -0
  93. data/spec/fixtures/wstrust/wstrust.13.xml +1 -0
  94. data/spec/fixtures/wstrust/wstrust.2005.xml +89 -0
  95. data/spec/spec_helper.rb +53 -0
  96. data/spec/support/fake_data.rb +40 -0
  97. data/spec/support/fake_token_endpoint.rb +108 -0
  98. metadata +265 -0
@@ -0,0 +1,107 @@
1
+ #-------------------------------------------------------------------------------
2
+ # Copyright (c) 2015 Micorosft Corporation
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #-------------------------------------------------------------------------------
22
+
23
+ require_relative '../spec_helper'
24
+
25
+ require 'uri'
26
+
27
+ describe ADAL::AuthenticationParameters do
28
+ describe '::create_from_resource_url' do
29
+ it 'should make an request to the resource server and parse the response' do
30
+ expect(Net::HTTP).to receive(:post_form).once.and_return(
31
+ 'www-authenticate' => 'Bearer authorization_uri="sample.com"')
32
+ params = ADAL::AuthenticationParameters.create_from_resource_url('a.io')
33
+ expect(params.authority_uri).to eq(URI.parse('sample.com'))
34
+ end
35
+
36
+ it 'should fail if the url is not a valid url' do
37
+ bad_url = 'this is not a valid url'
38
+ expect do
39
+ ADAL::AuthenticationParameters.create_from_resource_url(bad_url)
40
+ end.to raise_error(URI::InvalidURIError)
41
+ end
42
+
43
+ it 'should fail if the response does not contain an authenticate header' do
44
+ allow(Net::HTTP).to receive(:post_form).and_return(
45
+ body: 'abc', 'content-type' => 'application/json')
46
+ expect do
47
+ ADAL::AuthenticationParameters.create_from_resource_url('myurl.com')
48
+ end.to raise_error(ArgumentError)
49
+ end
50
+
51
+ it 'should fail if the resource server does not response' do
52
+ allow(Net::HTTP).to receive(:post_form).and_raise(Timeout::Error)
53
+ expect do
54
+ ADAL::AuthenticationParameters.create_from_resource_url('myurl.com')
55
+ end.to raise_error(Timeout::Error)
56
+ end
57
+ end
58
+
59
+ describe '::create_from_authenticate_header' do
60
+ it 'should successfully parse a valid authentication header' do
61
+ params = ADAL::AuthenticationParameters.create_from_authenticate_header(
62
+ 'Bearer authorization_uri="foobar,lj,;l,", resource="a( res&*^ource"')
63
+ expect(params.authority_uri).to eq(URI.parse('foobar,lj,;l,'))
64
+ expect(params.resource).to eq('a( res&*^ource')
65
+ end
66
+
67
+ it 'should return nil if the input is nil' do
68
+ expect(
69
+ ADAL::AuthenticationParameters.create_from_authenticate_header(nil)
70
+ ).to be_nil
71
+ end
72
+
73
+ it 'should return nil if the header does not contain an authority key' do
74
+ expect(
75
+ ADAL::AuthenticationParameters.create_from_authenticate_header(
76
+ 'Bearer: resource="http://graph.windows.net"')
77
+ ).to be_nil
78
+ end
79
+ end
80
+
81
+ describe '#create_context' do
82
+ before(:each) do
83
+ auth_uri = 'https://login.windows.net/mytenant.onmicrosoft.com'
84
+ @auth_params = ADAL::AuthenticationParameters.new(auth_uri)
85
+ end
86
+
87
+ it 'should return an AuthenticationContext object' do
88
+ expect(@auth_params.create_context).to be_a(ADAL::AuthenticationContext)
89
+ end
90
+
91
+ it 'should extract the host and tenant from the authorize uri' do
92
+ # @authority does not have a getter, so we have to use Ruby voodoo.
93
+ authority = @auth_params.create_context.instance_variable_get(:@authority)
94
+ expect(authority.host).to eq('login.windows.net')
95
+ expect(authority.tenant).to eq('mytenant.onmicrosoft.com')
96
+ end
97
+ end
98
+
99
+ describe '#initialize' do
100
+ it 'should fail if the authorize uri is not a valid uri' do
101
+ bad_auth_uri = 'not a real parsable uri'
102
+ expect do
103
+ ADAL::AuthenticationParameters.new(bad_auth_uri)
104
+ end.to raise_error(URI::InvalidURIError)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,122 @@
1
+ #-------------------------------------------------------------------------------
2
+ # Copyright (c) 2015 Micorosft Corporation
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #-------------------------------------------------------------------------------
22
+
23
+ require_relative '../spec_helper'
24
+
25
+ describe ADAL::Authority do
26
+ let(:auth_host) { 'login.windows.net' }
27
+ let(:tenant) { 'atenant.onmicrosoft.com' }
28
+
29
+ describe '#token_endpoint' do
30
+ it 'should correctly construct token endpoints' do
31
+ auth = ADAL::Authority.new(auth_host, tenant)
32
+ expect(auth.token_endpoint.to_s).to eq(
33
+ "https://#{auth_host}/#{tenant}/oauth2/token")
34
+ end
35
+ end
36
+
37
+ describe '#authorize_endpoint' do
38
+ context 'with additional params' do
39
+ it 'should correctly construct the authorize endpoint' do
40
+ auth = ADAL::Authority.new(auth_host, tenant)
41
+ expect(auth.authorize_endpoint(foo: :bar).to_s).to eq(
42
+ "https://#{auth_host}/#{tenant}/oauth2/authorize?foo=bar")
43
+ end
44
+ end
45
+
46
+ context 'with no additional params' do
47
+ it 'should correctly construct the authorize endpoint' do
48
+ auth = ADAL::Authority.new(auth_host, tenant)
49
+ expect(auth.authorize_endpoint.to_s).to eq(
50
+ "https://#{auth_host}/#{tenant}/oauth2/authorize")
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#validate' do
56
+ it 'should not do anything if validate_authority was set to false in the ' \
57
+ ' constructor' do
58
+ auth = ADAL::Authority.new(auth_host, tenant)
59
+ expect(auth).to_not receive(:validated_statically?)
60
+ expect(auth).to_not receive(:validated_dynamically?)
61
+ expect(auth.validate).to be_truthy
62
+ end
63
+
64
+ it 'should attempt static validation before dynamic validation' do
65
+ auth = ADAL::Authority.new(
66
+ auth_host, tenant, true)
67
+ expect(auth).to receive(:validated_statically?).once.and_return true
68
+ expect(auth).to_not receive(:validated_dynamically?)
69
+ expect(auth.validate).to be_truthy
70
+ end
71
+
72
+ it 'should successfully validate statically with a well known host' do
73
+ auth = ADAL::Authority.new(
74
+ auth_host, tenant, true)
75
+ expect(auth).to_not receive(:validated_dynamically?)
76
+ expect(auth.validate).to be_truthy
77
+ end
78
+
79
+ it 'should successully validate dynamically with the discovery endpoint' do
80
+ auth = ADAL::Authority.new(
81
+ 'someothersite.net', tenant, true)
82
+ expect(Net::HTTP).to receive(:get).once.and_return('{"tenant_discovery_' \
83
+ 'endpoint": "https://login.windows.net/atenant.onmicrosoft.com/.well-' \
84
+ 'known/openid-configuration"}')
85
+ expect(auth.validate).to be_truthy
86
+ end
87
+
88
+ it 'should not make a network connection after it validates once' do
89
+ auth = ADAL::Authority.new(
90
+ 'someothersite.net', tenant, true)
91
+ expect(Net::HTTP).to receive(:get).once.and_return(
92
+ '{"tenant_discovery_endpoint": "endpoint"}')
93
+ expect(auth.validate).to be_truthy
94
+ expect(auth.validate).to be_truthy
95
+ expect(auth.validate).to be_truthy
96
+ expect(auth.validate).to be_truthy
97
+ end
98
+
99
+ it 'should be false if dynamic validation does not respond' do
100
+ auth_host = 'notvalid.com'
101
+ dynamic_validation_endpoint =
102
+ 'https://login.microsoftonline.com/common/discovery/instance?api-vers' \
103
+ "ion=1.0&authorization_endpoint=https://#{auth_host}/#{tenant}/oauth2" \
104
+ '/authorize'
105
+ auth = ADAL::Authority.new('notvalid.com', tenant, true)
106
+ stub_request(:get, dynamic_validation_endpoint).to_return(status: 500)
107
+ expect(auth.validate).to be_falsey
108
+ end
109
+
110
+ it 'should be false if dynamic validation response is invalid' do
111
+ auth_host = 'notvalid.com'
112
+ dynamic_validation_endpoint =
113
+ 'https://login.microsoftonline.com/common/discovery/instance?api-vers' \
114
+ "ion=1.0&authorization_endpoint=https://#{auth_host}/#{tenant}/oauth2" \
115
+ '/authorize'
116
+ auth = ADAL::Authority.new('notvalid.com', tenant, true)
117
+ stub_request(:get, dynamic_validation_endpoint)
118
+ .to_return(status: 200, body: '{}')
119
+ expect(auth.validate).to be_falsey
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,191 @@
1
+ #-------------------------------------------------------------------------------
2
+ # Copyright (c) 2015 Micorosft Corporation
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #-------------------------------------------------------------------------------
22
+
23
+ require_relative '../spec_helper'
24
+ require_relative '../support/fake_data'
25
+
26
+ include FakeData
27
+
28
+ describe ADAL::CacheDriver do
29
+ let(:authority) { ADAL::Authority.new(AUTHORITY, TENANT) }
30
+ let(:client_id) { CLIENT_ID }
31
+ let(:client) { ADAL::ClientCredential.new(client_id) }
32
+ let(:driver) { ADAL::CacheDriver.new(authority, client, token_cache) }
33
+ let(:success_response) { ADAL::SuccessResponse.new }
34
+
35
+ describe '#add' do
36
+ let(:token_cache) { ADAL::MemoryCache.new }
37
+
38
+ context 'with an empty memory cache' do
39
+ before(:each) { driver.add(success_response) }
40
+
41
+ it 'should leave the cache with exactly one entry' do
42
+ expect(token_cache.entries.size).to eq 1
43
+ end
44
+
45
+ it 'should put the token response in the token cache' do
46
+ expect(token_cache.entries.map(&:token_response))
47
+ .to include success_response
48
+ end
49
+ end
50
+
51
+ context 'when the cache already contains the entry' do
52
+ before(:each) do
53
+ driver.add(success_response)
54
+ driver.add(success_response)
55
+ end
56
+
57
+ it 'should not put a duplicate in the cache' do
58
+ expect(token_cache.entries.uniq).to match_array(token_cache.entries)
59
+ end
60
+
61
+ it 'should leave the cache with the same number of entries' do
62
+ expect(token_cache.entries.size).to eq 1
63
+ end
64
+ end
65
+
66
+ context 'when the cache contains non-matching entries' do
67
+ let(:idtoken1) { JWT.encode({ upn: 'user1' }, '') }
68
+ let(:idtoken2) { JWT.encode({ upn: 'user2' }, '') }
69
+ let(:token1) { ADAL::SuccessResponse.new(id_token: idtoken1) }
70
+ let(:token2) { ADAL::SuccessResponse.new(id_token: idtoken2) }
71
+ before(:each) do
72
+ driver.add(token1)
73
+ driver.add(token2)
74
+ driver.add(ADAL::SuccessResponse.new(refresh_token: REFRESH_TOKEN,
75
+ resource: RESOURCE,
76
+ id_token: idtoken1))
77
+ end
78
+
79
+ it 'should update the refresh tokens of the matching entries' do
80
+ expect(token1.refresh_token).to eq(REFRESH_TOKEN)
81
+ end
82
+
83
+ it 'should not update the refresh tokens of the non-matching entries' do
84
+ expect(token2.refresh_token).to be nil
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#find' do
90
+ let(:token_cache) { ADAL::MemoryCache.new }
91
+ let(:resource1) { 'resource1' }
92
+ let(:resource2) { 'resource2' }
93
+ let(:user1) { 'user1' }
94
+ let(:user2) { 'user2' }
95
+ let(:user3) { 'user3' }
96
+ let(:expiry) { 100 }
97
+ let(:response1) do
98
+ ADAL::SuccessResponse.new(resource: resource1,
99
+ user_info: user1,
100
+ expires_in: expiry)
101
+ end
102
+ let(:response2) do
103
+ ADAL::SuccessResponse.new(resource: resource1,
104
+ user_info: user2,
105
+ expires_in: expiry,
106
+ refresh_token: REFRESH_TOKEN)
107
+ end
108
+ before(:each) do
109
+ driver.add(response1)
110
+ driver.add(response2)
111
+ end
112
+
113
+ let(:query) { { user_info: user, resource: resource } }
114
+ subject { driver.find(query) }
115
+
116
+ context 'with a user that is not in the cache' do
117
+ let(:resource) { resource1 }
118
+ let(:user) { user3 }
119
+ it { is_expected.to be nil }
120
+ end
121
+
122
+ context 'with a user that is in the cache' do
123
+ let(:user) { user2 }
124
+
125
+ context 'with a resource that is in the cache' do
126
+ let(:resource) { resource1 }
127
+
128
+ context 'which is expired' do
129
+ let(:expiry) { -10 }
130
+ let(:updated_access_token) { 'some access token' }
131
+ let(:refresh_response) do
132
+ double(body: "{\"access_token\": \"#{updated_access_token}\", \"r" \
133
+ "esource\": \"#{resource1}\"}")
134
+ end
135
+ before(:each) do
136
+ allow_any_instance_of(Net::HTTP).to receive(:request)
137
+ .and_return(refresh_response)
138
+ end
139
+
140
+ it { is_expected.to_not be_nil }
141
+ it 'should refresh the access token' do
142
+ expect(subject.access_token).to eq(updated_access_token)
143
+ end
144
+ end
145
+
146
+ context 'which is not expired' do
147
+ it { is_expected.to be response2 }
148
+ end
149
+ end
150
+
151
+ context 'with a resource that is not in the cache' do
152
+ let(:resource) { resource2 }
153
+
154
+ context 'without an mrrt' do
155
+ let(:user) { user1 }
156
+ it { is_expected.to be nil }
157
+ end
158
+
159
+ context 'with an mrrt' do
160
+ it { is_expected.to_not be nil }
161
+ it 'should fetch a new access token from OAuth' do
162
+ expect(subject.access_token).to eq(RETURNED_TOKEN)
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'with no cache' do
169
+ let(:token_cache) { ADAL::NoopCache.new }
170
+ let(:user) { user1 }
171
+ let(:resource) { resource1 }
172
+
173
+ it { is_expected.to be_nil }
174
+ it { expect { subject }.to_not raise_error }
175
+ end
176
+ end
177
+
178
+ context 'with a nonmatching client' do
179
+ describe '#find' do
180
+ let(:query) { { resource: RESOURCE, client_id: client.client_id } }
181
+ subject { driver.find(query) }
182
+
183
+ let(:token_cache) { ADAL::MemoryCache.new }
184
+ before(:each) do
185
+ driver.add(ADAL::SuccessResponse.new(client_id: 'different client id'))
186
+ end
187
+
188
+ it { is_expected.to be_nil }
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,148 @@
1
+ #-------------------------------------------------------------------------------
2
+ # Copyright (c) 2015 Micorosft Corporation
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #-------------------------------------------------------------------------------
22
+
23
+ require_relative '../support/fake_data'
24
+ require_relative '../spec_helper'
25
+
26
+ include FakeData
27
+
28
+ describe ADAL::CachedTokenResponse do
29
+ let(:authority) { ADAL::Authority.new(AUTHORITY, TENANT) }
30
+ let(:client) { ADAL::ClientCredential.new(CLIENT_ID) }
31
+ let(:expires_in) { 100 }
32
+ let(:resource) { RESOURCE }
33
+ let(:refresh_token) { REFRESH_TOKEN }
34
+ let(:response) do
35
+ ADAL::SuccessResponse.new(
36
+ resource: resource, refresh_token: refresh_token, expires_in: expires_in)
37
+ end
38
+
39
+ describe '#initialize' do
40
+ subject { -> { ADAL::CachedTokenResponse.new(client, authority, resp) } }
41
+
42
+ context 'with a SuccessResponse' do
43
+ let(:resp) { ADAL::SuccessResponse.new }
44
+
45
+ it { is_expected.to_not raise_error }
46
+ end
47
+
48
+ context 'with an ErrorResponse' do
49
+ let(:resp) { ADAL::ErrorResponse.new }
50
+
51
+ it { is_expected.to raise_error ArgumentError }
52
+ end
53
+ end
54
+
55
+ describe '#validate' do
56
+ subject do
57
+ ADAL::CachedTokenResponse.new(client, authority, response).validate
58
+ end
59
+
60
+ context 'with a non expired token' do
61
+ let(:expires_in) { 100 }
62
+
63
+ it { is_expected.to be_truthy }
64
+ end
65
+
66
+ context 'with an expired token' do
67
+ let(:expires_in) { -100 }
68
+
69
+ context 'with no refresh token' do
70
+ let(:refresh_token) { nil }
71
+
72
+ it { is_expected.to be_falsey }
73
+ end
74
+
75
+ context 'with a refresh token that fails to refresh' do
76
+ let(:refresh_token) { REFRESH_TOKEN }
77
+ before(:each) { stub_request(:post, /.*/).and_return(status: 500) }
78
+
79
+ it { is_expected.to be_falsey }
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#mrrt?' do
85
+ subject { ADAL::CachedTokenResponse.new(client, authority, response) }
86
+
87
+ context 'with a refresh_token' do
88
+ let(:refresh_token) { REFRESH_TOKEN }
89
+
90
+ context 'with a resource' do
91
+ let(:resource) { RESOURCE }
92
+ it { is_expected.to be_mrrt }
93
+ end
94
+
95
+ context 'without a resource' do
96
+ let(:resource) { nil }
97
+ it { is_expected.to_not be_mrrt }
98
+ end
99
+ end
100
+
101
+ context 'wihout a refresh_token' do
102
+ let(:refresh_token) { nil }
103
+
104
+ context 'with a resource' do
105
+ let(:resource) { RESOURCE }
106
+ it { is_expected.to_not be_mrrt }
107
+ end
108
+
109
+ context 'without a resource' do
110
+ let(:resource) { nil }
111
+ it { is_expected.to_not be_mrrt }
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '#can_refresh?' do
117
+ let(:other) { ADAL::CachedTokenResponse.new(client, oauthority, response) }
118
+ subject do
119
+ ADAL::CachedTokenResponse.new(client, authority, response)
120
+ .can_refresh?(other)
121
+ end
122
+
123
+ context 'when not an mrrt' do
124
+ let(:oauthority) { authority }
125
+ let(:refresh_token) { nil }
126
+
127
+ it { is_expected.to be_falsey }
128
+ end
129
+
130
+ context 'when an mrrt' do
131
+ context 'when fields match' do
132
+ let(:oauthority) { authority }
133
+ it { is_expected.to be_truthy }
134
+ end
135
+
136
+ context 'when fields do not match' do
137
+ let(:oauthority) { 'some other authority' }
138
+ it { is_expected.to be_falsey }
139
+ end
140
+ end
141
+ end
142
+
143
+ it 'provides proxy access to token response fields' do
144
+ expect(
145
+ ADAL::CachedTokenResponse.new(client, AUTHORITY, response).resource
146
+ ).to eq RESOURCE
147
+ end
148
+ end