adal 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|