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