adal 1.0.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.
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,113 @@
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 'jwt'
26
+
27
+ include FakeData
28
+
29
+ describe ADAL::ClientAssertionCertificate do
30
+ describe '#initialize' do
31
+ let(:auth) { ADAL::Authority.new(AUTHORITY, TENANT) }
32
+ let(:cert) { OpenSSL::X509::Certificate.new }
33
+
34
+ it "should fail if the public key isn't large enough" do
35
+ # The key is an integer number of bytes so we have to subtract at least 8.
36
+ too_few_bits = ADAL::ClientAssertionCertificate::MIN_KEY_SIZE_BITS - 8
37
+ key = OpenSSL::PKey::RSA.new(too_few_bits)
38
+ cert.public_key = key.public_key
39
+ pfx = OpenSSL::PKCS12.create('', '', key, cert)
40
+ expect do
41
+ ADAL::ClientAssertionCertificate.new(auth, CLIENT_ID, pfx)
42
+ end.to raise_error(ArgumentError)
43
+ end
44
+
45
+ it 'should succeed if the public key is the minimum size' do
46
+ just_enough_bits = ADAL::ClientAssertionCertificate::MIN_KEY_SIZE_BITS
47
+ key = OpenSSL::PKey::RSA.new(just_enough_bits)
48
+ cert.public_key = key.public_key
49
+ pfx = OpenSSL::PKCS12.create('', '', key, cert)
50
+ expect do
51
+ ADAL::ClientAssertionCertificate.new(auth, CLIENT_ID, pfx)
52
+ end.to_not raise_error
53
+ end
54
+
55
+ it 'should fail if the certificate is not PKCS12' do
56
+ pfx = 'Not an OpenSSL::PKCS12'
57
+ expect { ADAL::ClientAssertionCertificate.new(auth, CLIENT_ID, pfx) }
58
+ .to raise_error ArgumentError
59
+ end
60
+
61
+ it 'should fail if the pkcs12 does not use valid rsa' do
62
+ key = OpenSSL::PKey::DSA.new 2048
63
+ cert.public_key = key.public_key
64
+ pfx = OpenSSL::PKCS12.create('', '', key, cert)
65
+ expect { ADAL::ClientAssertionCertificate.new(auth, CLIENT_ID, pfx) }
66
+ .to raise_error ArgumentError
67
+ end
68
+
69
+ it 'should fail if the pkcs12 does not use valid x509' do
70
+ key = OpenSSL::PKey::RSA.new 2048
71
+ cert.public_key = key.public_key
72
+ pfx = OpenSSL::PKCS12.create('', '', key, cert)
73
+
74
+ # In practice, no one would ever do this. But we do check for it just in
75
+ # case.
76
+ pfx.instance_variable_set(:@certificate, 'Not an x509 certificate')
77
+ expect { ADAL::ClientAssertionCertificate.new(auth, CLIENT_ID, pfx) }
78
+ .to raise_error ArgumentError
79
+ end
80
+ end
81
+
82
+ describe '#request_params' do
83
+ ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365
84
+ let(:cert) { OpenSSL::X509::Certificate.new }
85
+ before(:each) do
86
+ key = OpenSSL::PKey::RSA.new 2048
87
+ cert.public_key = key.public_key
88
+ @pfx = OpenSSL::PKCS12.create('', '', key, cert)
89
+ @assertion_cert = ADAL::ClientAssertionCertificate.new(
90
+ ADAL::Authority.new(AUTHORITY, TENANT), CLIENT_ID, @pfx)
91
+ end
92
+
93
+ it 'should contain client id, client assertion and client assertion type' do
94
+ params = @assertion_cert.request_params
95
+ expect(params.keys).to contain_exactly(
96
+ :client_id, :client_assertion, :client_assertion_type)
97
+ end
98
+
99
+ it 'should have client assertion type be JWT_BEARER' do
100
+ expect(
101
+ @assertion_cert.request_params[:client_assertion_type]
102
+ ).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer')
103
+ end
104
+
105
+ it 'should have an assertion that is a decodable JWT' do
106
+ expect do
107
+ JWT.decode(@assertion_cert.request_params[:client_assertion],
108
+ cert.public_key,
109
+ options: { verify_not_before: false })
110
+ end.to_not raise_error
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,38 @@
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
+ include FakeData
26
+
27
+ describe ADAL::ClientAssertion do
28
+ describe '#initialize' do
29
+ it 'should fail if any parameters are nil' do
30
+ expect do
31
+ ADAL::ClientAssertion.new(nil, ASSERTION)
32
+ end.to raise_error(ArgumentError)
33
+ expect do
34
+ ADAL::ClientAssertion.new(CLIENT_ID, nil)
35
+ end.to raise_error(ArgumentError)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
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 'spec_helper'
24
+
25
+ using ADAL::CoreExt
26
+
27
+ describe Hash do
28
+ describe '#reverse_merge' do
29
+ it 'should work on empty hashes' do
30
+ expect({}.reverse_merge({})).to eq({})
31
+ end
32
+
33
+ it 'should work just like merge if the keys do not conflict' do
34
+ hash1 = { a: 5, b: 10 }
35
+ hash2 = { c: 8, d: 12 }
36
+ expect(hash1.reverse_merge(hash2)).to eq(hash1.merge(hash2))
37
+ expect(hash2.reverse_merge(hash1)).to eq(hash2.merge(hash1))
38
+ end
39
+
40
+ it "should prefer self's values to other_hash's values" do
41
+ hash1 = { a: 5, c: 10 }
42
+ hash2 = { a: 6, b: 15 }
43
+ expect(hash1.reverse_merge(hash2)).to eq(a: 5, b: 15, c: 10)
44
+ expect(hash2.reverse_merge(hash1)).to eq(a: 6, b: 15, c: 10)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
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::Logging do
26
+ describe '::log_level=' do
27
+ context 'with an invalid log level' do
28
+ it 'should throw an error' do
29
+ expect { ADAL::Logging.log_level = 'abc' }.to raise_error(ArgumentError)
30
+ end
31
+ end
32
+
33
+ context 'with a valid log level' do
34
+ it 'should not throw an error' do
35
+ expect { ADAL::Logging.log_level = ADAL::Logger::VERBOSE }
36
+ .to_not raise_error
37
+ expect { ADAL::Logging.log_level = ADAL::Logger::INFO }
38
+ .to_not raise_error
39
+ expect { ADAL::Logging.log_level = ADAL::Logger::WARN }
40
+ .to_not raise_error
41
+ expect { ADAL::Logging.log_level = ADAL::Logger::ERROR }
42
+ .to_not raise_error
43
+ expect { ADAL::Logging.log_level = ADAL::Logger::FATAL }
44
+ .to_not raise_error
45
+ end
46
+ end
47
+ end
48
+ end
@@ -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
+ describe ADAL::MemoryCache do
26
+ let(:authority) { ADAL::Authority.new('login.windows.net', 'contoso.org') }
27
+ let(:client) { ADAL::ClientCredential.new('my client id', 'client secret') }
28
+ let(:cache) { ADAL::MemoryCache.new }
29
+
30
+ # Note that this test also relies on the correctness of TokenResponse#to_json
31
+ # and CachedTokenResponse#to_json.
32
+ describe '#to_json' do
33
+ subject { cache.to_json }
34
+
35
+ context 'when empty' do
36
+ it { is_expected.to eq '[]' }
37
+ end
38
+
39
+ context 'with many tokens' do
40
+ let(:expected_json) do
41
+ "[{\"access_token\":\"abc\",\"token_type\":\"JWT\"},{\"access_token\"" \
42
+ ":\"abc\",\"scope\":\"openid\"},{\"access_token\":\"abc\",\"resource" \
43
+ "\":\"http://graph.windows.net\"}]"
44
+ end
45
+ let(:expected_json) do
46
+ "[{\"authority\":[\"login.windows.net\",\"contoso.org\"],\"client_id" \
47
+ "\":\"my client id\",\"token_response\":{\"access_token\":\"abc\",\"t" \
48
+ "oken_type\":\"JWT\"}},{\"authority\":[\"login.windows.net\",\"contos" \
49
+ "o.org\"],\"client_id\":\"my client id\",\"token_response\":{\"access" \
50
+ "_token\":\"abc\",\"scope\":\"openid\"}},{\"authority\":[\"login.wind" \
51
+ "ows.net\",\"contoso.org\"],\"client_id\":\"my client id\",\"token_re" \
52
+ "sponse\":{\"access_token\":\"abc\",\"resource\":\"http://graph.windo" \
53
+ "ws.net\"}}]"
54
+ end
55
+ before(:each) do
56
+ cache_driver = ADAL::CacheDriver.new(authority, client, cache)
57
+ cache_driver.add(
58
+ ADAL::SuccessResponse.new(access_token: 'abc', token_type: 'JWT'))
59
+ cache_driver.add(
60
+ ADAL::SuccessResponse.new(access_token: 'abc', scope: 'openid'))
61
+ cache_driver.add(
62
+ ADAL::SuccessResponse.new(access_token: 'abc',
63
+ resource: 'http://graph.windows.net'))
64
+ end
65
+
66
+ it 'properly serializes the cache' do
67
+ expect(subject).to eq expected_json
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#from_json' do
73
+ subject { ADAL::MemoryCache.from_json(json) }
74
+
75
+ context 'when empty' do
76
+ let(:json) { '[]' }
77
+ it 'should contain no entries' do
78
+ expect(subject.entries.length).to eq 0
79
+ end
80
+ end
81
+
82
+ context 'with many tokens' do
83
+ let(:json) { cache.to_json }
84
+ before(:each) do
85
+ cache_driver = ADAL::CacheDriver.new(authority, client, cache)
86
+ cache_driver.add(
87
+ ADAL::SuccessResponse.new(access_token: 'abc', token_type: 'JWT'))
88
+ cache_driver.add(
89
+ ADAL::SuccessResponse.new(access_token: 'abc', scope: 'openid'))
90
+ cache_driver.add(
91
+ ADAL::SuccessResponse.new(access_token: 'abc',
92
+ resource: 'http://graph.windows.net'))
93
+ end
94
+
95
+ it 'has the correct number of entries' do
96
+ expect(subject.entries.length).to eq 3
97
+ end
98
+
99
+ it 'reconstructs the entries' do
100
+ subject.entries.each do |cache_entry|
101
+ expect(cache_entry.authority.host).to eq authority.host
102
+ expect(cache_entry.authority.tenant).to eq authority.tenant
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,57 @@
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::MexRequest do
26
+ describe '#initialize' do
27
+ context 'with an invalid endpoint' do
28
+ let(:uri) { 'not a uri' }
29
+
30
+ it 'should raise an error' do
31
+ expect { ADAL::MexRequest.new(uri) }.to raise_error(
32
+ URI::InvalidURIError)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#execute' do
38
+ let(:response_body) { 'response body' }
39
+ let(:uri) { 'https://abc.def/' }
40
+
41
+ before(:each) do
42
+ @http_request = nil
43
+ expect_any_instance_of(Net::HTTP).to receive(:request) do |_, req|
44
+ @http_request = req
45
+ end.and_return(double(body: response_body))
46
+ expect(ADAL::MexResponse).to receive(:parse)
47
+ ADAL::MexRequest.new(uri).execute
48
+ end
49
+
50
+ describe 'http request' do
51
+ subject { @http_request }
52
+
53
+ it { expect(subject['Content-Type']).to eq 'application/soap+xml' }
54
+ it { expect(subject.path).to eq '/' }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,143 @@
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
+ MEX_FIXTURES = File.expand_path('../../fixtures/mex', __FILE__)
26
+
27
+ describe ADAL::MexResponse do
28
+ describe '::parse' do
29
+ let(:response) { File.read(File.expand_path(file_name, MEX_FIXTURES)) }
30
+
31
+ context 'with both 1.3 and 2005 endpoints' do
32
+ let(:file_name) { 'microsoft.xml' }
33
+ let(:wstrust_url_13) do
34
+ 'https://corp.sts.microsoft.com/adfs/services/trust/13/usernamemixed'
35
+ end
36
+
37
+ it 'should not raise an error' do
38
+ expect { ADAL::MexResponse.parse(response) }.to_not raise_error
39
+ end
40
+
41
+ it 'should prefer the 1.3 endpoint' do
42
+ expect(ADAL::MexResponse.parse(response).wstrust_url.to_s)
43
+ .to eq(wstrust_url_13)
44
+ end
45
+ end
46
+
47
+ context 'with only 1.3 wstrust endpoint' do
48
+ let(:file_name) { 'only_13.xml' }
49
+ let(:wstrust_13_url) do
50
+ 'https://fs.ajmichael.net/adfs/services/trust/13/usernamemixed'
51
+ end
52
+
53
+ it 'should parse the 1.3 wstrust endpoint' do
54
+ expect(ADAL::MexResponse.parse(response).wstrust_url.to_s)
55
+ .to eq(wstrust_13_url)
56
+ end
57
+ end
58
+
59
+ context 'with only 2005 wstrust endpoint' do
60
+ let(:file_name) { 'only_2005.xml' }
61
+ let(:wstrust_2005_url) do
62
+ 'https://fs.ajmichael.net/adfs/services/trust/2005/usernamemixed'
63
+ end
64
+
65
+ it 'should parse the 2005 wstrust endpoint' do
66
+ expect(ADAL::MexResponse.parse(response).wstrust_url.to_s)
67
+ .to eq(wstrust_2005_url)
68
+ end
69
+ end
70
+
71
+ context 'with a malformed response' do
72
+ let(:file_name) { 'malformed.xml' }
73
+
74
+ it 'should throw an error' do
75
+ expect { ADAL::MexResponse.parse(response) }
76
+ .to raise_error(
77
+ ADAL::MexResponse::MexError, /No username token policy nodes/)
78
+ end
79
+ end
80
+
81
+ context 'with insecure address' do
82
+ let(:file_name) { 'insecureaddress.xml' }
83
+
84
+ it 'should throw an error' do
85
+ expect { ADAL::MexResponse.parse(response) }
86
+ .to raise_error(ArgumentError)
87
+ end
88
+ end
89
+
90
+ context 'with invalid namespaces' do
91
+ let(:file_name) { 'invalid_namespaces.xml' }
92
+
93
+ it 'should throw an error' do
94
+ expect { ADAL::MexResponse.parse(response) }
95
+ .to raise_error(
96
+ ADAL::MexResponse::MexError, /No username token policy nodes/)
97
+ end
98
+ end
99
+
100
+ context 'with no policy nodes' do
101
+ let(:file_name) { 'no_username_token_policies.xml' }
102
+
103
+ it 'should throw an error' do
104
+ expect { ADAL::MexResponse.parse(response) }
105
+ .to raise_error(
106
+ ADAL::MexResponse::MexError, /No username token policy nodes/)
107
+ end
108
+ end
109
+
110
+ context 'with no wstrust endpoints' do
111
+ let(:file_name) { 'no_wstrust_endpoints.xml' }
112
+
113
+ it 'should throw an error' do
114
+ expect { ADAL::MexResponse.parse(response) }
115
+ .to raise_error(
116
+ ADAL::MexResponse::MexError, /No valid WS-Trust endpoints/)
117
+ end
118
+ end
119
+
120
+ context 'with no matching bindings' do
121
+ let(:file_name) { 'no_matching_bindings.xml' }
122
+
123
+ it 'should throw an error' do
124
+ expect { ADAL::MexResponse.parse(response) }
125
+ .to raise_error(ADAL::MexResponse::MexError, /No matching bindings/)
126
+ end
127
+ end
128
+
129
+ context 'with multiple valid endpoints' do
130
+ let(:file_name) { 'multiple_endpoints.xml' }
131
+ let(:first_wstrust_url) do
132
+ 'https://this.is.first.com/adfs/services/trust/13/usernamemixed'
133
+ end
134
+ subject { ADAL::MexResponse.parse(response) }
135
+
136
+ it { expect { subject }.to_not raise_error }
137
+
138
+ it 'should use the first endpoint' do
139
+ expect(subject.wstrust_url.to_s).to eq(first_wstrust_url)
140
+ end
141
+ end
142
+ end
143
+ end