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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +97 -0
- data/Rakefile +39 -0
- data/adal.gemspec +52 -0
- data/contributing.md +127 -0
- data/lib/adal.rb +24 -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.rb +26 -0
- data/lib/adal/core_ext/hash.rb +34 -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/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 +265 -0
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
require 'digest'
|
|
26
|
+
require 'json'
|
|
27
|
+
require 'jwt'
|
|
28
|
+
require 'securerandom'
|
|
29
|
+
|
|
30
|
+
module ADAL
|
|
31
|
+
# The return type of all of the instance methods that return tokens.
|
|
32
|
+
class TokenResponse
|
|
33
|
+
extend Logging
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Constructs a TokenResponse from a raw hash. It will return either a
|
|
37
|
+
# SuccessResponse or an ErrorResponse depending on the fields of the hash.
|
|
38
|
+
#
|
|
39
|
+
# @param Hash raw_response
|
|
40
|
+
# The body of the HTTP response expressed as a raw hash.
|
|
41
|
+
# @return TokenResponse
|
|
42
|
+
def self.parse(raw_response)
|
|
43
|
+
logger.verbose('Attempting to create a TokenResponse from raw response.')
|
|
44
|
+
if raw_response.nil?
|
|
45
|
+
ErrorResponse.new
|
|
46
|
+
elsif raw_response['error']
|
|
47
|
+
ErrorResponse.new(JSON.parse(raw_response))
|
|
48
|
+
else
|
|
49
|
+
SuccessResponse.new(JSON.parse(raw_response))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
public
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Shorthand for checking if a token response is successful or failed.
|
|
57
|
+
#
|
|
58
|
+
# @return Boolean
|
|
59
|
+
def error?
|
|
60
|
+
self.respond_to? :error
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# A token response that contains an access token. All fields are read only
|
|
65
|
+
# and may be nil. Some fields are only populated in certain flows.
|
|
66
|
+
class SuccessResponse < TokenResponse
|
|
67
|
+
include Logging
|
|
68
|
+
|
|
69
|
+
# These fields may or may not be included in the response from the token
|
|
70
|
+
# endpoint.
|
|
71
|
+
OAUTH_FIELDS = [:access_token, :expires_in, :expires_on, :id_token,
|
|
72
|
+
:not_before, :refresh_token, :resource, :scope, :token_type]
|
|
73
|
+
OAUTH_FIELDS.each { |field| attr_reader field }
|
|
74
|
+
attr_reader :user_info
|
|
75
|
+
attr_reader :fields
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
# Constructs a SuccessResponse from a collection of fields returned from a
|
|
79
|
+
# token endpoint.
|
|
80
|
+
#
|
|
81
|
+
# @param Hash
|
|
82
|
+
def initialize(fields = {})
|
|
83
|
+
@fields = fields
|
|
84
|
+
fields.each { |k, v| instance_variable_set("@#{k}", v) }
|
|
85
|
+
parse_id_token(id_token)
|
|
86
|
+
@expires_on = @expires_in.to_i + Time.now.to_i
|
|
87
|
+
logger.info('Parsed a SuccessResponse with access token digest ' \
|
|
88
|
+
"#{Digest::SHA256.hexdigest @access_token.to_s} and " \
|
|
89
|
+
'refresh token digest ' \
|
|
90
|
+
"#{Digest::SHA256.hexdigest @refresh_token.to_s}.")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
# Converts the fields that were used to create this token response into
|
|
95
|
+
# a JSON string. This is helpful for storing then in a database.
|
|
96
|
+
#
|
|
97
|
+
# @param JSON::Ext::Generator::State
|
|
98
|
+
# We don't care about this, because the JSON representation of this
|
|
99
|
+
# object does not depend on the fields before it.
|
|
100
|
+
# @return String
|
|
101
|
+
def to_json(_ = nil)
|
|
102
|
+
JSON.unparse(fields)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
# Parses the raw id token into an ADAL::UserInformation.
|
|
107
|
+
# If the id token is missing, an ADAL::UserInformation will still be
|
|
108
|
+
# generated, it just won't contain any displayable information.
|
|
109
|
+
#
|
|
110
|
+
# @param String id_token
|
|
111
|
+
# The id token to parse
|
|
112
|
+
# Adds an id token to the token response if one is not present
|
|
113
|
+
def parse_id_token(id_token)
|
|
114
|
+
if id_token.nil?
|
|
115
|
+
logger.warn('No id token found.')
|
|
116
|
+
@user_info ||= ADAL::UserInformation.new(unique_id: SecureRandom.uuid)
|
|
117
|
+
return
|
|
118
|
+
end
|
|
119
|
+
logger.verbose('Attempting to decode id token in token response.')
|
|
120
|
+
claims = JWT.decode(id_token.to_s, nil, false).first
|
|
121
|
+
@id_token = id_token
|
|
122
|
+
@user_info = ADAL::UserInformation.new(claims)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# A token response that contains an error code.
|
|
127
|
+
class ErrorResponse < TokenResponse
|
|
128
|
+
include Logging
|
|
129
|
+
|
|
130
|
+
OAUTH_FIELDS = [:error, :error_description, :error_codes, :timestamp,
|
|
131
|
+
:trace_id, :correlation_id, :submit_url, :context]
|
|
132
|
+
OAUTH_FIELDS.each { |field| attr_reader field }
|
|
133
|
+
|
|
134
|
+
# Constructs a Error from a collection of fields returned from a
|
|
135
|
+
# token endpoint.
|
|
136
|
+
#
|
|
137
|
+
# @param Hash
|
|
138
|
+
def initialize(fields = {})
|
|
139
|
+
fields.each { |k, v| instance_variable_set("@#{k}", v) }
|
|
140
|
+
logger.error("Parsed an ErrorResponse with error: #{@error} and error " \
|
|
141
|
+
"description: #{@error_description}.")
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#-------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2015 Micorosft Corporation
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20
|
+
# THE SOFTWARE.
|
|
21
|
+
#-------------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
require_relative './token_request'
|
|
24
|
+
|
|
25
|
+
module ADAL
|
|
26
|
+
# An assertion and its representation type, stored as a JWT for
|
|
27
|
+
# the on-behalf-of flow.
|
|
28
|
+
class UserAssertion
|
|
29
|
+
attr_reader :assertion
|
|
30
|
+
attr_reader :assertion_type
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Creates a new UserAssertion.
|
|
34
|
+
#
|
|
35
|
+
# @param String assertion
|
|
36
|
+
# An OAuth assertion representing the user.
|
|
37
|
+
# @optional AssertionType assertion_type
|
|
38
|
+
# The type of the assertion being made. Currently only JWT_BEARER is
|
|
39
|
+
# supported.
|
|
40
|
+
def initialize(
|
|
41
|
+
assertion, assertion_type = ADAL::TokenRequest::GrantType::JWT_BEARER)
|
|
42
|
+
@assertion = assertion
|
|
43
|
+
@assertion_type = assertion_type
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# The relevant OAuth access token request parameters for this object.
|
|
48
|
+
#
|
|
49
|
+
# @return Hash
|
|
50
|
+
def request_params
|
|
51
|
+
{ grant_type: assertion_type,
|
|
52
|
+
assertion: assertion,
|
|
53
|
+
requested_token_use: :on_behalf_of,
|
|
54
|
+
scope: :openid }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
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 './authority'
|
|
24
|
+
require_relative './logger'
|
|
25
|
+
require_relative './mex_request'
|
|
26
|
+
require_relative './token_request'
|
|
27
|
+
require_relative './wstrust_request'
|
|
28
|
+
|
|
29
|
+
require 'base64'
|
|
30
|
+
require 'json'
|
|
31
|
+
require 'net/http'
|
|
32
|
+
require 'uri'
|
|
33
|
+
|
|
34
|
+
module ADAL
|
|
35
|
+
# A convenience class for username and password credentials.
|
|
36
|
+
class UserCredential
|
|
37
|
+
include Logging
|
|
38
|
+
|
|
39
|
+
# Federation response type from the userrealm endpoint.
|
|
40
|
+
module AccountType
|
|
41
|
+
FEDERATED = 'Federated'
|
|
42
|
+
MANAGED = 'Managed'
|
|
43
|
+
UNKNOWN = 'Unknown'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# ADAL only supports flows for managed and federated users.
|
|
47
|
+
class UnsupportedAccountTypeError < StandardError
|
|
48
|
+
def initialize(account_type)
|
|
49
|
+
super("Unsupported account type for authentication: #{account_type}.")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
attr_reader :username
|
|
54
|
+
attr_reader :password
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Constructs a new UserCredential.
|
|
58
|
+
#
|
|
59
|
+
# @param String username
|
|
60
|
+
# @param String password
|
|
61
|
+
# @optional String authority_host
|
|
62
|
+
# The host name of the authority to verify the user against.
|
|
63
|
+
def initialize(
|
|
64
|
+
username, password, authority_host = Authority::WORLD_WIDE_AUTHORITY)
|
|
65
|
+
@username = username
|
|
66
|
+
@password = password
|
|
67
|
+
@authority_host = authority_host
|
|
68
|
+
@discovery_path = "/common/userrealm/#{URI.escape @username}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# Determines the account type based on a Home Realm Discovery request.
|
|
73
|
+
#
|
|
74
|
+
# @return UserCredential::AccountType
|
|
75
|
+
def account_type
|
|
76
|
+
realm_discovery_response['account_type']
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# The OAuth parameters that respresent this UserCredential.
|
|
81
|
+
#
|
|
82
|
+
# @return Hash
|
|
83
|
+
def request_params
|
|
84
|
+
case account_type
|
|
85
|
+
when AccountType::MANAGED
|
|
86
|
+
managed_request_params
|
|
87
|
+
when AccountType::FEDERATED
|
|
88
|
+
federated_request_params
|
|
89
|
+
else
|
|
90
|
+
fail UnsupportedAccountTypeError, account_type
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# :nocov:
|
|
95
|
+
def to_s
|
|
96
|
+
"UserCredential[Username: #{@username}, AccountType: #{account_type}]"
|
|
97
|
+
end
|
|
98
|
+
# :nocov:
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
# Memoized response from the discovery endpoint. Since a UserCredential is
|
|
103
|
+
# read only, this should only ever need to be called once.
|
|
104
|
+
# @return Hash
|
|
105
|
+
def realm_discovery_response
|
|
106
|
+
@realm_discovery_response ||=
|
|
107
|
+
JSON.parse(Net::HTTP.get(realm_discovery_uri))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# @return URI
|
|
111
|
+
def realm_discovery_uri
|
|
112
|
+
URI::HTTPS.build(
|
|
113
|
+
host: @authority_host,
|
|
114
|
+
path: @discovery_path,
|
|
115
|
+
query: URI.encode_www_form('api-version' => '1.0'))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @return Hash
|
|
119
|
+
def federated_request_params
|
|
120
|
+
logger.verbose("Getting OAuth parameters for Federated #{@username}.")
|
|
121
|
+
wstrust_response = wstrust_request.execute(@username, @password)
|
|
122
|
+
{ assertion: Base64.encode64(wstrust_response.token).strip,
|
|
123
|
+
grant_type: wstrust_response.grant_type,
|
|
124
|
+
scope: :openid }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @return URI
|
|
128
|
+
def federation_metadata_url
|
|
129
|
+
URI.parse(realm_discovery_response['federation_metadata_url'])
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @return Hash
|
|
133
|
+
def managed_request_params
|
|
134
|
+
logger.verbose("Getting OAuth parameters for Managed #{@username}.")
|
|
135
|
+
{ username: @username,
|
|
136
|
+
password: @password,
|
|
137
|
+
grant_type: TokenRequest::GrantType::PASSWORD,
|
|
138
|
+
scope: :openid }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @return MexResponse
|
|
142
|
+
def mex_response
|
|
143
|
+
@mex_response ||= MexRequest.new(federation_metadata_url).execute
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @return WSTrustRequest
|
|
147
|
+
def wstrust_request
|
|
148
|
+
@wstrust_request ||=
|
|
149
|
+
WSTrustRequest.new(mex_response.wstrust_url, mex_response.action)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
# Identifier for users in the cache.
|
|
25
|
+
#
|
|
26
|
+
# Ideally, the application will first use a different OAuth flow, such as the
|
|
27
|
+
# Authorization Code flow, to acquire an ADAL::SuccessResponse. Then, it can
|
|
28
|
+
# create ADAL::UserIdentifier to query the cache which will refresh tokens as
|
|
29
|
+
# necessary.
|
|
30
|
+
class UserIdentifier
|
|
31
|
+
attr_reader :id
|
|
32
|
+
attr_reader :type
|
|
33
|
+
|
|
34
|
+
# Displayable IDs are human readable (eg email addresses) while Unique Ids
|
|
35
|
+
# are generally random UUIDs.
|
|
36
|
+
module Type
|
|
37
|
+
UNIQUE_ID = :UNIQUE_ID
|
|
38
|
+
DISPLAYABLE_ID = :DISPLAYABLE_ID
|
|
39
|
+
end
|
|
40
|
+
include Type
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Creates a UserIdentifier with a specific type. Used for cache lookups.
|
|
44
|
+
# Matches .NET ADAL implementation.
|
|
45
|
+
#
|
|
46
|
+
# @param String id
|
|
47
|
+
# @param UserIdentifier::Type
|
|
48
|
+
# @return ADAL::UserIdentifier
|
|
49
|
+
def initialize(id, type)
|
|
50
|
+
unless [UNIQUE_ID, DISPLAYABLE_ID].include? type
|
|
51
|
+
fail ArgumentError, 'type must be an ADAL::UserIdentifier::Type.'
|
|
52
|
+
end
|
|
53
|
+
@id = id
|
|
54
|
+
@type = type
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# These parameters should only be used for cache lookup. This is enforced
|
|
59
|
+
# by ADAL::TokenRequest.
|
|
60
|
+
#
|
|
61
|
+
# @return Hash
|
|
62
|
+
def request_params
|
|
63
|
+
{ user_info: self }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Overrides comparison operator for cache lookups
|
|
68
|
+
#
|
|
69
|
+
# @param UserIdentifier other
|
|
70
|
+
# @return Boolean
|
|
71
|
+
def ==(other)
|
|
72
|
+
case other
|
|
73
|
+
when UserIdentifier
|
|
74
|
+
self.equal? other
|
|
75
|
+
when UserInformation
|
|
76
|
+
(type == UNIQUE_ID && id == other.unique_id) ||
|
|
77
|
+
(type == DISPLAYABLE_ID && id == other.displayable_id)
|
|
78
|
+
when String
|
|
79
|
+
@id == other
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
# Basically just a holder for the id token.
|
|
25
|
+
class UserInformation
|
|
26
|
+
ID_TOKEN_FIELDS = [:aud, :iss, :iat, :nbf, :exp, :ver, :tid, :oid, :upn,
|
|
27
|
+
:sub, :given_name, :family_name, :name, :amr,
|
|
28
|
+
:unique_name, :nonce, :email]
|
|
29
|
+
ID_TOKEN_FIELDS.each { |field| attr_reader field }
|
|
30
|
+
attr_reader :unique_id
|
|
31
|
+
attr_reader :displayable_id
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# Constructs a new UserInformation.
|
|
35
|
+
#
|
|
36
|
+
# @param Hash claims
|
|
37
|
+
# Claims from an id token. The exact claims will vary, so whatever is not
|
|
38
|
+
# found in the claims will be nil.
|
|
39
|
+
def initialize(claims)
|
|
40
|
+
claims.each { |k, v| instance_variable_set("@#{k}", v) }
|
|
41
|
+
@unique_id = oid || sub || unique_id
|
|
42
|
+
@displayable_id = upn || email
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def ==(other)
|
|
46
|
+
unique_id == other.unique_id && displayable_id == other.displayable_id
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|