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,190 @@
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
+ module ADAL
24
+ # Proxy object for a token response with metadata.
25
+ class CachedTokenResponse
26
+ attr_reader :authority
27
+ attr_reader :client_id
28
+ attr_reader :token_response
29
+
30
+ ##
31
+ # Constructs a new CachedTokenResponse.
32
+ #
33
+ # @param ClientCredential|ClientAssertion|ClientAssertionCertificate
34
+ # The credentials of the calling client application.
35
+ # @param Authority authority
36
+ # The ADAL::Authority object that the response was retrieved from.
37
+ # @param SuccessResponse token_response
38
+ # The token response to be cached.
39
+ def initialize(client, authority, token_response)
40
+ unless token_response.instance_of? SuccessResponse
41
+ fail ArgumentError, 'Only SuccessResponses can be cached.'
42
+ end
43
+ @authority = authority
44
+ if client.respond_to? :client_id
45
+ @client = client
46
+ @client_id = client.client_id
47
+ else
48
+ @client = ClientCredential.new(client)
49
+ @client_id = client
50
+ end
51
+ @token_response = token_response
52
+ end
53
+
54
+ ##
55
+ # Converts the fields in this object and its proxied SuccessResponse into
56
+ # a JSON string.
57
+ #
58
+ # @param JSON::Ext::Generator::State
59
+ # We don't care about the state, but JSON::unparse requires this.
60
+ # @return String
61
+ def to_json(_ = nil)
62
+ JSON.unparse(authority: [authority.host, authority.tenant],
63
+ client_id: client_id,
64
+ token_response: token_response)
65
+ end
66
+
67
+ ##
68
+ # Reconstructs an object from JSON that was serialized with
69
+ # CachedTokenResponse#to_json.
70
+ #
71
+ # @param JSON raw_json
72
+ # @return CachedTokenResponse
73
+ def self.from_json(json)
74
+ json = JSON.parse(json) if json.instance_of? String
75
+ CachedTokenResponse.new(json['client_id'],
76
+ Authority.new(*json['authority']),
77
+ SuccessResponse.new(json['token_response']))
78
+ end
79
+
80
+ ##
81
+ # Determines if self can be used to refresh other.
82
+ #
83
+ # @param CachedTokenResponse other
84
+ # @return Boolean
85
+ def can_refresh?(other)
86
+ mrrt? && (authority == other.authority) &&
87
+ (user_info == other.user_info) && (client_id == other.client_id)
88
+ end
89
+
90
+ ##
91
+ # If the access token is within the expiration buffer of expiring, an
92
+ # attempt will be made to retrieve a new token with the refresh token.
93
+ #
94
+ # @param Fixnum expiration_buffer_sec
95
+ # The number of seconds to use as leeway in determining if the token is
96
+ # expired. A positive buffer will refresh the token early while a negative
97
+ # buffer will refresh it late. Used to counter clock skew and network
98
+ # latency.
99
+ # @return Boolean
100
+ # True if the token is still valid (even if it was refreshed). False if
101
+ # the token is expired an unable to be refreshed.
102
+ def validate(expiration_buffer_sec = 0)
103
+ return true if (Time.now + expiration_buffer_sec).to_i < expires_on
104
+ unless refresh_token
105
+ logger.verbose('Cached token is almost expired but no refresh token ' \
106
+ 'is available.')
107
+ return false
108
+ end
109
+ logger.verbose('Cached token is almost expired, attempting to refresh ' \
110
+ ' with refresh token.')
111
+ refresh_response = refresh
112
+ if refresh_response.instance_of? SuccessResponse
113
+ logger.verbose('Successfully refreshed token in cache.')
114
+ @token_response = refresh_response
115
+ true
116
+ else
117
+ logger.warn('Failed to refresh token in cache with refresh token.')
118
+ false
119
+ end
120
+ end
121
+
122
+ ##
123
+ # Attempts to refresh the access token for a given resource. Note that you
124
+ # can call this method with a different resource even if the token is not
125
+ # an MRRT, but it will fail
126
+ #
127
+ # @param String resource
128
+ # The resource that the new access token is beign requested for. Defaults
129
+ # to using the same resource as the original token.
130
+ # @return TokenResponse
131
+ def refresh(new_resource = resource)
132
+ token_response = TokenRequest
133
+ .new(authority, @client)
134
+ .get_with_refresh_token(refresh_token, new_resource)
135
+ if token_response.instance_of? SuccessResponse
136
+ token_response.parse_id_token(id_token)
137
+ end
138
+ token_response
139
+ end
140
+
141
+ ##
142
+ # Changes the refresh token of the underlying token response.
143
+ #
144
+ # @param String token
145
+ def refresh_token=(token)
146
+ token_response.instance_variable_set(:@refresh_token, token)
147
+ logger.verbose("Updated the refresh token for #{token_response}.")
148
+ end
149
+
150
+ ##
151
+ # Is the token a Multi Resource Refresh Token?
152
+ #
153
+ # @return Boolean
154
+ def mrrt?
155
+ token_response.refresh_token && token_response.resource
156
+ end
157
+
158
+ ## Since the token cache may be implemented by the user of this library,
159
+ ## all means of checking equality must be consistent.
160
+
161
+ def ==(other)
162
+ [:authority, :client_id, :token_response].all? do |field|
163
+ (other.respond_to? field) && (send(field) == other.send(field))
164
+ end
165
+ end
166
+
167
+ def eql?(other)
168
+ self == other
169
+ end
170
+
171
+ def hash
172
+ [authority, client_id, token_response].hash
173
+ end
174
+
175
+ private
176
+
177
+ # CachedTokenResponse is just a proxy for TokenResponse.
178
+ def method_missing(method, *args, &block)
179
+ if token_response.respond_to?(method)
180
+ token_response.send(method, *args, &block)
181
+ else
182
+ super(method)
183
+ end
184
+ end
185
+
186
+ def respond_to_missing?(method, include_private = false)
187
+ token_response.respond_to?(method, include_private) || super
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,63 @@
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 './request_parameters'
24
+ require_relative './token_request'
25
+ require_relative './util'
26
+
27
+ module ADAL
28
+ # A client credential that consists of the client id and a JWT bearer
29
+ # assertion. The type is 'urn:ietf:params:oauth:token-type:jwt'.
30
+ class ClientAssertion
31
+ include TokenRequest::GrantType
32
+ include RequestParameters
33
+ include Util
34
+
35
+ attr_reader :assertion
36
+ attr_reader :assertion_type
37
+ attr_reader :client_id
38
+
39
+ ##
40
+ # Creates a new ClientAssertion.
41
+ #
42
+ # @param [String] client_id
43
+ # The client id of the calling application.
44
+ # @param [String] assertion
45
+ # The JWT used as a credential.
46
+ def initialize(client_id, assertion, assertion_type = JWT_BEARER)
47
+ fail_if_arguments_nil(client_id, assertion, assertion_type)
48
+ @assertion = assertion
49
+ @assertion_type = assertion_type
50
+ @client_id = client_id
51
+ end
52
+
53
+ ##
54
+ # The relavent parameters from this credential for OAuth.
55
+ #
56
+ # @return Hash
57
+ def request_params
58
+ { CLIENT_ID => @client_id,
59
+ CLIENT_ASSERTION_TYPE => @assertion_type,
60
+ CLIENT_ASSERTION => @assertion }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,89 @@
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 'openssl'
24
+
25
+ module ADAL
26
+ # An assertion made by a client with an X509 certificate. This requires both
27
+ # the public and private keys. Technically it only requires the thumbprint
28
+ # of the public key, however OpenSSL's object model does not include
29
+ # thumbprints.
30
+ class ClientAssertionCertificate
31
+ include RequestParameters
32
+
33
+ MIN_KEY_SIZE_BITS = 2014
34
+
35
+ attr_reader :certificate
36
+ attr_reader :client_id
37
+
38
+ ##
39
+ # Creates a new ClientAssertionCertificate.
40
+ #
41
+ # @param Authority authority
42
+ # The authority object that will recognize this certificate.
43
+ # @param [String] client_id
44
+ # The client id of the calling application.
45
+ # @param [OpenSSL::PKCS12] pkcs12_file
46
+ # The PKCS12 file containing the certificate and private key.
47
+ def initialize(authority, client_id, pkcs12_file)
48
+ unless pkcs12_file.is_a? OpenSSL::PKCS12
49
+ fail ArgumentError, 'Only PKCS12 file format is supported.'
50
+ end
51
+ @authority = authority
52
+ @certificate = pkcs12_file.certificate
53
+ @client_id = client_id.to_s
54
+ @private_key = pkcs12_file.key
55
+ validate_certificate_and_key(@certificate, @private_key)
56
+ end
57
+
58
+ # The relevant parameters from this credential for OAuth.
59
+ def request_params
60
+ jwt_assertion = SelfSignedJwtFactory
61
+ .new(@client_id, @authority.token_endpoint)
62
+ .create_and_sign_jwt(@certificate, @private_key)
63
+ ClientAssertion.new(client_id, jwt_assertion).request_params
64
+ end
65
+
66
+ private
67
+
68
+ # @param [OpenSSL::X509::Certificate] certificate
69
+ # @return [Fixnum] The number of bits in the public key.
70
+ def public_key_size_bits(certificate)
71
+ certificate.public_key.n.num_bytes * 8
72
+ end
73
+
74
+ ##
75
+ # In general, Ruby code is very loose about types. However, since we are
76
+ # dealing with sensitive information here, we will be a little bit stricter
77
+ # on type safety.
78
+ def validate_certificate_and_key(certificate, private_key)
79
+ if !certificate.is_a? OpenSSL::X509::Certificate
80
+ fail ArgumentError, 'certificate must be an OpenSSL::X509::Certificate.'
81
+ elsif !private_key.is_a? OpenSSL::PKey::RSA
82
+ fail ArgumentError, 'private_key must be an OpenSSL::PKey::RSA.'
83
+ elsif public_key_size_bits(certificate) < MIN_KEY_SIZE_BITS
84
+ fail ArgumentError, 'certificate must contain a public key of at ' \
85
+ "least #{MIN_KEY_SIZE_BITS} bits."
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,46 @@
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 './request_parameters'
24
+ require_relative './util'
25
+
26
+ module ADAL
27
+ # A wrapper object for a client id and secret.
28
+ class ClientCredential
29
+ include RequestParameters
30
+ include Util
31
+
32
+ attr_reader :client_id
33
+ attr_reader :client_secret
34
+
35
+ def initialize(client_id, client_secret = nil)
36
+ fail_if_arguments_nil(client_id)
37
+ @client_id = client_id
38
+ @client_secret = client_secret
39
+ end
40
+
41
+ # The relavent parameters from this credential for OAuth.
42
+ def request_params
43
+ { CLIENT_ID => @client_id, CLIENT_SECRET => @client_secret }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
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
+ # All of the refinements used in ADAL. They are all in the namespace
24
+ # ADAL::CoreExt and will only be applied to files that start with ADAL::CoreExt
25
+ # before opening any module or class.
26
+ require_relative './core_ext/hash'
@@ -0,0 +1,34 @@
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
+ module ADAL
24
+ # Adds helper methods to Hash. These are standard in Rails and are
25
+ # commonplace in the Ruby community.
26
+ module CoreExt
27
+ # Same as #merge, but values in other_hash are prioritized over self.
28
+ refine Hash do
29
+ def reverse_merge(other_hash)
30
+ other_hash.merge(self)
31
+ end
32
+ end
33
+ end
34
+ end