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,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