custom-adal 1.0.1

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 +6 -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 +106 -0
  8. data/Rakefile +39 -0
  9. data/adal.gemspec +52 -0
  10. data/contributing.md +127 -0
  11. data/lib/adal/authentication_context.rb +202 -0
  12. data/lib/adal/authentication_parameters.rb +126 -0
  13. data/lib/adal/authority.rb +165 -0
  14. data/lib/adal/cache_driver.rb +171 -0
  15. data/lib/adal/cached_token_response.rb +190 -0
  16. data/lib/adal/client_assertion.rb +63 -0
  17. data/lib/adal/client_assertion_certificate.rb +89 -0
  18. data/lib/adal/client_credential.rb +46 -0
  19. data/lib/adal/core_ext/hash.rb +34 -0
  20. data/lib/adal/core_ext.rb +26 -0
  21. data/lib/adal/jwt_parameters.rb +39 -0
  22. data/lib/adal/logger.rb +90 -0
  23. data/lib/adal/logging.rb +98 -0
  24. data/lib/adal/memory_cache.rb +95 -0
  25. data/lib/adal/mex_request.rb +52 -0
  26. data/lib/adal/mex_response.rb +141 -0
  27. data/lib/adal/noop_cache.rb +38 -0
  28. data/lib/adal/oauth_request.rb +76 -0
  29. data/lib/adal/request_parameters.rb +48 -0
  30. data/lib/adal/self_signed_jwt_factory.rb +96 -0
  31. data/lib/adal/templates/rst.13.xml.erb +35 -0
  32. data/lib/adal/templates/rst.2005.xml.erb +32 -0
  33. data/lib/adal/token_request.rb +231 -0
  34. data/lib/adal/token_response.rb +144 -0
  35. data/lib/adal/user_assertion.rb +57 -0
  36. data/lib/adal/user_credential.rb +152 -0
  37. data/lib/adal/user_identifier.rb +83 -0
  38. data/lib/adal/user_information.rb +49 -0
  39. data/lib/adal/util.rb +49 -0
  40. data/lib/adal/version.rb +36 -0
  41. data/lib/adal/wstrust_request.rb +100 -0
  42. data/lib/adal/wstrust_response.rb +168 -0
  43. data/lib/adal/xml_namespaces.rb +64 -0
  44. data/lib/adal.rb +24 -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 +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
@@ -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
@@ -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