custom-adal 1.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +106 -0
- data/Rakefile +39 -0
- data/adal.gemspec +52 -0
- data/contributing.md +127 -0
- data/lib/adal/authentication_context.rb +202 -0
- data/lib/adal/authentication_parameters.rb +126 -0
- data/lib/adal/authority.rb +165 -0
- data/lib/adal/cache_driver.rb +171 -0
- data/lib/adal/cached_token_response.rb +190 -0
- data/lib/adal/client_assertion.rb +63 -0
- data/lib/adal/client_assertion_certificate.rb +89 -0
- data/lib/adal/client_credential.rb +46 -0
- data/lib/adal/core_ext/hash.rb +34 -0
- data/lib/adal/core_ext.rb +26 -0
- data/lib/adal/jwt_parameters.rb +39 -0
- data/lib/adal/logger.rb +90 -0
- data/lib/adal/logging.rb +98 -0
- data/lib/adal/memory_cache.rb +95 -0
- data/lib/adal/mex_request.rb +52 -0
- data/lib/adal/mex_response.rb +141 -0
- data/lib/adal/noop_cache.rb +38 -0
- data/lib/adal/oauth_request.rb +76 -0
- data/lib/adal/request_parameters.rb +48 -0
- data/lib/adal/self_signed_jwt_factory.rb +96 -0
- data/lib/adal/templates/rst.13.xml.erb +35 -0
- data/lib/adal/templates/rst.2005.xml.erb +32 -0
- data/lib/adal/token_request.rb +231 -0
- data/lib/adal/token_response.rb +144 -0
- data/lib/adal/user_assertion.rb +57 -0
- data/lib/adal/user_credential.rb +152 -0
- data/lib/adal/user_identifier.rb +83 -0
- data/lib/adal/user_information.rb +49 -0
- data/lib/adal/util.rb +49 -0
- data/lib/adal/version.rb +36 -0
- data/lib/adal/wstrust_request.rb +100 -0
- data/lib/adal/wstrust_response.rb +168 -0
- data/lib/adal/xml_namespaces.rb +64 -0
- data/lib/adal.rb +24 -0
- data/samples/authorization_code_example/README.md +10 -0
- data/samples/authorization_code_example/web_app.rb +139 -0
- data/samples/client_assertion_certificate_example/README.md +42 -0
- data/samples/client_assertion_certificate_example/app.rb +55 -0
- data/samples/on_behalf_of_example/README.md +35 -0
- data/samples/on_behalf_of_example/native_app.rb +52 -0
- data/samples/on_behalf_of_example/web_api.rb +71 -0
- data/samples/user_credentials_example/README.md +7 -0
- data/samples/user_credentials_example/app.rb +52 -0
- data/spec/adal/authentication_context_spec.rb +186 -0
- data/spec/adal/authentication_parameters_spec.rb +107 -0
- data/spec/adal/authority_spec.rb +122 -0
- data/spec/adal/cache_driver_spec.rb +191 -0
- data/spec/adal/cached_token_response_spec.rb +148 -0
- data/spec/adal/client_assertion_certificate_spec.rb +113 -0
- data/spec/adal/client_assertion_spec.rb +38 -0
- data/spec/adal/core_ext/hash_spec.rb +47 -0
- data/spec/adal/logging_spec.rb +48 -0
- data/spec/adal/memory_cache_spec.rb +107 -0
- data/spec/adal/mex_request_spec.rb +57 -0
- data/spec/adal/mex_response_spec.rb +143 -0
- data/spec/adal/self_signed_jwt_factory_spec.rb +63 -0
- data/spec/adal/token_request_spec.rb +150 -0
- data/spec/adal/token_response_spec.rb +102 -0
- data/spec/adal/user_credential_spec.rb +125 -0
- data/spec/adal/user_identifier_spec.rb +115 -0
- data/spec/adal/wstrust_request_spec.rb +51 -0
- data/spec/adal/wstrust_response_spec.rb +152 -0
- data/spec/fixtures/mex/insecureaddress.xml +924 -0
- data/spec/fixtures/mex/invalid_namespaces.xml +916 -0
- data/spec/fixtures/mex/malformed.xml +914 -0
- data/spec/fixtures/mex/microsoft.xml +916 -0
- data/spec/fixtures/mex/multiple_endpoints.xml +922 -0
- data/spec/fixtures/mex/no_matching_bindings.xml +916 -0
- data/spec/fixtures/mex/no_username_token_policies.xml +914 -0
- data/spec/fixtures/mex/no_wstrust_endpoints.xml +838 -0
- data/spec/fixtures/mex/only_13.xml +842 -0
- data/spec/fixtures/mex/only_2005.xml +842 -0
- data/spec/fixtures/oauth/error.json +1 -0
- data/spec/fixtures/oauth/success.json +1 -0
- data/spec/fixtures/oauth/success_with_id_token.json +1 -0
- data/spec/fixtures/wstrust/error.xml +24 -0
- data/spec/fixtures/wstrust/invalid_namespaces.xml +136 -0
- data/spec/fixtures/wstrust/missing_security_tokens.xml +90 -0
- data/spec/fixtures/wstrust/success.xml +136 -0
- data/spec/fixtures/wstrust/token.xml +1 -0
- data/spec/fixtures/wstrust/too_many_security_tokens.xml +219 -0
- data/spec/fixtures/wstrust/unrecognized_token_type.xml +136 -0
- data/spec/fixtures/wstrust/wstrust.13.xml +1 -0
- data/spec/fixtures/wstrust/wstrust.2005.xml +89 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/fake_data.rb +40 -0
- data/spec/support/fake_token_endpoint.rb +108 -0
- metadata +264 -0
|
@@ -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,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
|
|
@@ -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,39 @@
|
|
|
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
|
+
# Literals used in JWT header and payload.
|
|
25
|
+
module JwtParameters
|
|
26
|
+
ALGORITHM = 'alg'
|
|
27
|
+
AUDIENCE = 'aud'
|
|
28
|
+
EXPIRES_ON = 'exp'
|
|
29
|
+
ISSUER = 'iss'
|
|
30
|
+
JWT_ID = 'jti'
|
|
31
|
+
NOT_BEFORE = 'nbf'
|
|
32
|
+
RS256 = 'RS256'
|
|
33
|
+
SELF_SIGNED_JWT_LIFETIME = 10
|
|
34
|
+
SUBJECT = 'sub'
|
|
35
|
+
THUMBPRINT = 'x5t'
|
|
36
|
+
TYPE = 'typ'
|
|
37
|
+
TYPE_JWT = 'JWT'
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/adal/logger.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
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 'logger'
|
|
24
|
+
|
|
25
|
+
module ADAL
|
|
26
|
+
# Extended version of Ruby's base logger class to support VERBOSE logging.
|
|
27
|
+
# This is consistent with ADAL logging across platforms.
|
|
28
|
+
#
|
|
29
|
+
# The format of a log message is described in the Ruby docs at
|
|
30
|
+
# http://ruby-doc.org/stdlib-2.2.2/libdoc/logger/rdoc/Logger.html as
|
|
31
|
+
# SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message.
|
|
32
|
+
# SeverityID is the first letter of the severity. In the case of ADAL::Logger
|
|
33
|
+
# that means one of {V, I, W, E, F}. The DateTime object uses the to_s method
|
|
34
|
+
# of DateTime from stdlib which is ISO-8601. The ProgName will be the
|
|
35
|
+
# correlation id if one is sent or absent otherwise.
|
|
36
|
+
class Logger < Logger
|
|
37
|
+
SEVS = %w(VERBOSE INFO WARN ERROR FATAL)
|
|
38
|
+
VERBOSE = SEVS.index('VERBOSE')
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# Constructs a new Logger.
|
|
42
|
+
#
|
|
43
|
+
# @param String|IO logdev
|
|
44
|
+
# A filename (String) or IO object (STDOUT, STDERR).
|
|
45
|
+
# @param String correlation_id
|
|
46
|
+
# The UUID of the request context.
|
|
47
|
+
def initialize(logdev, correlation_id)
|
|
48
|
+
super(logdev)
|
|
49
|
+
@correlation_id = correlation_id
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def format_severity(severity)
|
|
53
|
+
SEVS[severity] || 'ANY'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# For some reason, the default logger implementations of #error, #fatal,
|
|
58
|
+
# etc. pass message = nil and progname = <the message> to #add, which
|
|
59
|
+
# interprets that as using the progname as the message. Instead, we will
|
|
60
|
+
# use the message as the message and the progname as the correlation_id.
|
|
61
|
+
#
|
|
62
|
+
# This is purely an internal change, the calling mechanism is exactly the
|
|
63
|
+
# same and it only affects ADAL::Logger, not Logger.
|
|
64
|
+
|
|
65
|
+
# These methods are skipped by the SimpleCov, because it is not our
|
|
66
|
+
# responsibility to test the standard library's logging framework.
|
|
67
|
+
|
|
68
|
+
#:nocov:
|
|
69
|
+
def error(message = nil, &block)
|
|
70
|
+
add(ERROR, message, @correlation_id, &block)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def fatal(message = nil, &block)
|
|
74
|
+
add(FATAL, message, @correlation_id, &block)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def info(message = nil, &block)
|
|
78
|
+
add(INFO, message, @correlation_id, &block)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def verbose(message = nil, &block)
|
|
82
|
+
add(VERBOSE, message, @correlation_id, &block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def warn(message = nil, &block)
|
|
86
|
+
add(WARN, message, @correlation_id, &block)
|
|
87
|
+
end
|
|
88
|
+
#:nocov:
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/adal/logging.rb
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
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 './logger'
|
|
24
|
+
|
|
25
|
+
require 'securerandom'
|
|
26
|
+
|
|
27
|
+
module ADAL
|
|
28
|
+
# Mix-in module for the ADAL logger. To obtain a logger in class methods the
|
|
29
|
+
# calling class will need to extend this module. To obtain a logger in
|
|
30
|
+
# instance methods the calling will need to include this Module.
|
|
31
|
+
module Logging
|
|
32
|
+
DEFAULT_LOG_LEVEL = Logger::ERROR
|
|
33
|
+
DEFAULT_LOG_OUTPUT = STDOUT
|
|
34
|
+
|
|
35
|
+
@correlation_id = SecureRandom.uuid
|
|
36
|
+
@log_level = DEFAULT_LOG_LEVEL
|
|
37
|
+
@log_output = DEFAULT_LOG_OUTPUT
|
|
38
|
+
|
|
39
|
+
# According to the style guide, class instance variables are preferable to
|
|
40
|
+
# class variables.
|
|
41
|
+
class << self
|
|
42
|
+
attr_accessor :correlation_id
|
|
43
|
+
attr_accessor :log_level
|
|
44
|
+
attr_accessor :log_output
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# Sets the ADAL log level.
|
|
49
|
+
#
|
|
50
|
+
# Example usage:
|
|
51
|
+
#
|
|
52
|
+
# ADAL::Logging.log_level = ADAL::Logger::VERBOSE
|
|
53
|
+
#
|
|
54
|
+
def self.log_level=(level)
|
|
55
|
+
unless Logger::SEVS.map.with_index { |_, i| i }.include? level
|
|
56
|
+
fail ArgumentError, "Invalid log level: #{level}."
|
|
57
|
+
end
|
|
58
|
+
@log_level = level
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Sets the ADAL log output. All future logs generated by ADAL will be sent
|
|
63
|
+
# to this location. It is not retroactive.
|
|
64
|
+
#
|
|
65
|
+
# @param IO|String output
|
|
66
|
+
# This can either be STDERR, STDOUT or a String containing a file path.
|
|
67
|
+
def self.log_output=(output)
|
|
68
|
+
output = output.to_s unless output.is_a? IO
|
|
69
|
+
@log_output = output
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Creates one ADAL logger per calling class/module with a specified output.
|
|
74
|
+
# This is to be used within ADAL. Clients will have no use for it.
|
|
75
|
+
#
|
|
76
|
+
# Examples usage:
|
|
77
|
+
#
|
|
78
|
+
# require_relative './logging'
|
|
79
|
+
#
|
|
80
|
+
# module ADAL
|
|
81
|
+
# module SomeModule
|
|
82
|
+
# include Logging
|
|
83
|
+
#
|
|
84
|
+
# def something_bad
|
|
85
|
+
# logger.error('An error message')
|
|
86
|
+
# end
|
|
87
|
+
# end
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# @param output
|
|
91
|
+
# STDERR, STDOUT or the file name as a string.
|
|
92
|
+
def logger
|
|
93
|
+
@logger ||= ADAL::Logger.new(Logging.log_output, Logging.correlation_id)
|
|
94
|
+
@logger.level = Logging.log_level || DEFAULT_LOG_LEVEL
|
|
95
|
+
@logger
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
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 './logging'
|
|
24
|
+
|
|
25
|
+
module ADAL
|
|
26
|
+
# A simple cache implementation that is not persisted across application runs.
|
|
27
|
+
class MemoryCache
|
|
28
|
+
include Logging
|
|
29
|
+
|
|
30
|
+
def initialize
|
|
31
|
+
@entries = []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_accessor :entries
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Adds an array of objects to the cache.
|
|
38
|
+
#
|
|
39
|
+
# @param Array
|
|
40
|
+
# The entries to add.
|
|
41
|
+
# @return Array
|
|
42
|
+
# The entries after the addition.
|
|
43
|
+
def add(entries)
|
|
44
|
+
entries = Array(entries) # If entries is an array, this is a no-op.
|
|
45
|
+
old_size = @entries.size
|
|
46
|
+
@entries |= entries
|
|
47
|
+
logger.verbose("Added #{entries.size - old_size} new entries to cache.")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# By default, matches all entries.
|
|
52
|
+
#
|
|
53
|
+
# @param Block
|
|
54
|
+
# A matcher on the token list.
|
|
55
|
+
# @return Array
|
|
56
|
+
# The matching tokens.
|
|
57
|
+
def find(&query)
|
|
58
|
+
query ||= proc { true }
|
|
59
|
+
@entries.select(&query)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# Removes an array of objects from the cache.
|
|
64
|
+
#
|
|
65
|
+
# @param Array
|
|
66
|
+
# The entries to remove.
|
|
67
|
+
# @return Array
|
|
68
|
+
# The remaining entries.
|
|
69
|
+
def remove(entries)
|
|
70
|
+
@entries -= Array(entries)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# Converts the cache entries into one JSON string.
|
|
75
|
+
#
|
|
76
|
+
# @param JSON::Ext::Generator::State
|
|
77
|
+
# @return String
|
|
78
|
+
def to_json(_ = nil)
|
|
79
|
+
JSON.unparse(entries)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# Reconstructs the cache from JSON that was previously serialized.
|
|
84
|
+
#
|
|
85
|
+
# @param JSON json
|
|
86
|
+
# @return MemoryCache
|
|
87
|
+
def self.from_json(json)
|
|
88
|
+
cache = MemoryCache.new
|
|
89
|
+
cache.entries = JSON.parse(json).map do |e|
|
|
90
|
+
CachedTokenResponse.from_json(e)
|
|
91
|
+
end
|
|
92
|
+
cache
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
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 './mex_response'
|
|
24
|
+
require_relative './util'
|
|
25
|
+
|
|
26
|
+
require 'net/http'
|
|
27
|
+
require 'uri'
|
|
28
|
+
|
|
29
|
+
module ADAL
|
|
30
|
+
# A request to a Metadata Exchange endpoint of an ADFS server. Used to obtain
|
|
31
|
+
# the WSTrust endpoint for username and password authentication of federated
|
|
32
|
+
# users.
|
|
33
|
+
class MexRequest
|
|
34
|
+
include Util
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Constructs a MexRequest object for a specific URL endpoint.
|
|
38
|
+
#
|
|
39
|
+
# @param String|URI endpoint
|
|
40
|
+
# The Metadata Exchange endpoint.
|
|
41
|
+
def initialize(endpoint)
|
|
42
|
+
@endpoint = URI.parse(endpoint.to_s)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return MexResponse
|
|
46
|
+
def execute
|
|
47
|
+
request = Net::HTTP::Get.new(@endpoint.path)
|
|
48
|
+
request.add_field('Content-Type', 'application/soap+xml')
|
|
49
|
+
MexResponse.parse(http(@endpoint).request(request).body)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|