Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/adal/authentication_context.py: 30%

90 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:05 +0000

1#------------------------------------------------------------------------------ 

2# 

3# Copyright (c) Microsoft Corporation.  

4# All rights reserved. 

5#  

6# This code is licensed under the MIT License. 

7#  

8# Permission is hereby granted, free of charge, to any person obtaining a copy 

9# of this software and associated documentation files(the "Software"), to deal 

10# in the Software without restriction, including without limitation the rights 

11# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 

12# copies of the Software, and to permit persons to whom the Software is 

13# furnished to do so, subject to the following conditions : 

14#  

15# The above copyright notice and this permission notice shall be included in 

16# all copies or substantial portions of the Software. 

17#  

18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 

21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

23# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

24# THE SOFTWARE. 

25# 

26#------------------------------------------------------------------------------ 

27import os 

28import threading 

29import warnings 

30 

31from .authority import Authority 

32from . import argument 

33from .code_request import CodeRequest 

34from .token_request import TokenRequest 

35from .token_cache import TokenCache 

36from . import log 

37from .constants import OAuth2DeviceCodeResponseParameters 

38 

39 

40#warnings.simplefilter('default', DeprecationWarning) # Make them visible to end users 

41 

42GLOBAL_ADAL_OPTIONS = {} 

43 

44class AuthenticationContext(object): 

45 '''Retrieves authentication tokens from Azure Active Directory. 

46 

47 For usages, check out the "sample" folder at: 

48 https://github.com/AzureAD/azure-activedirectory-library-for-python 

49 ''' 

50 

51 def __init__( 

52 self, authority, validate_authority=None, cache=None, 

53 api_version=None, timeout=None, enable_pii=False, verify_ssl=None, proxies=None): 

54 '''Creates a new AuthenticationContext object. 

55 

56 By default the authority will be checked against a list of known Azure 

57 Active Directory authorities. If the authority is not recognized as  

58 one of these well known authorities then token acquisition will fail. 

59 This behavior can be turned off via the validate_authority parameter 

60 below. 

61 

62 :param str authority: A URL that identifies a token authority. It should be of the 

63 format https://login.microsoftonline.com/your_tenant 

64 :param bool validate_authority: (optional) Turns authority validation  

65 on or off. This parameter default to true. 

66 :param TokenCache cache: (optional) Sets the token cache used by this  

67 AuthenticationContext instance. If this parameter is not set, then 

68 a default is used. Cache instances is only used by that instance of 

69 the AuthenticationContext and are not shared unless it has been 

70 manually passed during the construction of other 

71 AuthenticationContexts. 

72 :param api_version: (optional) Specifies API version using on the wire. 

73 Historically it has a hardcoded default value as "1.0". 

74 Developers have been encouraged to set it as None explicitly, 

75 which means the underlying API version will be automatically chosen. 

76 Starting from ADAL Python 1.0, this default value becomes None. 

77 :param timeout: (optional) requests timeout. How long to wait for the server to send 

78 data before giving up, as a float, or a `(connect timeout, 

79 read timeout) <timeouts>` tuple. 

80 :param enable_pii: (optional) Unless this is set to True, 

81 there will be no Personally Identifiable Information (PII) written in log. 

82 :param verify_ssl: (optional) requests verify. Either a boolean, in which case it  

83 controls whether we verify the server's TLS certificate, or a string, in which  

84 case it must be a path to a CA bundle to use. If this value is not provided, and  

85 ADAL_PYTHON_SSL_NO_VERIFY env variable is set, behavior is equivalent to  

86 verify_ssl=False. 

87 :param proxies: (optional) requests proxies. Dictionary mapping protocol to the URL  

88 of the proxy. See http://docs.python-requests.org/en/master/user/advanced/#proxies 

89 for details. 

90 ''' 

91 warnings.warn( 

92 """ADAL Python library no longer receives any feature update or bugfix. 

93Please use the new library, MSAL Python, which is easier to use, and more secure. 

94 

95MSAL Python is available here: https://pypi.org/project/msal/ 

96 

97If you are building your new project, 

98start using MSAL Python by choosing one of the samples that suit your need. 

99https://msal-python.readthedocs.io/en/latest/#scenarios 

100 

101If you are migrating your existing ADAL-powered project into MSAL, please read 

102https://learn.microsoft.com/en-us/azure/active-directory/develop/migrate-python-adal-msal 

103""", DeprecationWarning) 

104 self.authority = Authority(authority, validate_authority is None or validate_authority) 

105 self._oauth2client = None 

106 self.correlation_id = None 

107 env_verify = 'ADAL_PYTHON_SSL_NO_VERIFY' not in os.environ 

108 verify = verify_ssl if verify_ssl is not None else env_verify 

109 if api_version is not None: 

110 warnings.warn( 

111 """The default behavior of including api-version=1.0 on the wire 

112 is now deprecated. 

113 Future version of ADAL will change the default value to None. 

114 

115 To ensure a smooth transition, you are recommended to explicitly 

116 set it to None in your code now, and test out the new behavior. 

117 

118 context = AuthenticationContext(..., api_version=None) 

119 """, DeprecationWarning) 

120 self._call_context = { 

121 'options': GLOBAL_ADAL_OPTIONS, 

122 'api_version': api_version, 

123 'verify_ssl': verify, 

124 'proxies':proxies, 

125 'timeout':timeout, 

126 "enable_pii": enable_pii, 

127 } 

128 self._token_requests_with_user_code = {} 

129 self.cache = cache or TokenCache() 

130 self._lock = threading.RLock() 

131 

132 @property 

133 def options(self): 

134 return self._call_context['options'] 

135 

136 @options.setter 

137 def options(self, val): 

138 self._call_context['options'] = val 

139 

140 def _acquire_token(self, token_func, correlation_id=None): 

141 self._call_context['log_context'] = log.create_log_context( 

142 correlation_id or self.correlation_id, self._call_context.get('enable_pii', False)) 

143 self.authority.validate(self._call_context) 

144 return token_func(self) 

145 

146 def acquire_token(self, resource, user_id, client_id): 

147 '''Gets a token for a given resource via cached tokens. 

148 

149 :param str resource: A URI that identifies the resource for which the 

150 token is valid. 

151 :param str user_id: The username of the user on behalf this application 

152 is authenticating. 

153 :param str client_id: The OAuth client id of the calling application. 

154 :returns: dic with several keys, include "accessToken" and 

155 "refreshToken". 

156 ''' 

157 def token_func(self): 

158 token_request = TokenRequest(self._call_context, self, client_id, resource) 

159 return token_request.get_token_from_cache_with_refresh(user_id) 

160 

161 return self._acquire_token(token_func) 

162 

163 def acquire_token_with_username_password(self, resource, username, password, client_id): 

164 '''Gets a token for a given resource via user credentails. 

165  

166 :param str resource: A URI that identifies the resource for which the  

167 token is valid. 

168 :param str username: The username of the user on behalf this 

169 application is authenticating. 

170 :param str password: The password of the user named in the username 

171 parameter. 

172 :param str client_id: The OAuth client id of the calling application. 

173 :returns: dict with several keys, include "accessToken" and 

174 "refreshToken". 

175 ''' 

176 def token_func(self): 

177 token_request = TokenRequest(self._call_context, self, client_id, resource) 

178 return token_request.get_token_with_username_password(username, password) 

179 

180 return self._acquire_token(token_func) 

181 

182 def acquire_token_with_client_credentials(self, resource, client_id, client_secret): 

183 '''Gets a token for a given resource via client credentials. 

184 

185 :param str resource: A URI that identifies the resource for which the  

186 token is valid. 

187 :param str client_id: The OAuth client id of the calling application. 

188 :param str client_secret: The OAuth client secret of the calling application. 

189 :returns: dict with several keys, include "accessToken". 

190 ''' 

191 def token_func(self): 

192 token_request = TokenRequest(self._call_context, self, client_id, resource) 

193 return token_request.get_token_with_client_credentials(client_secret) 

194 

195 return self._acquire_token(token_func) 

196 

197 def acquire_token_with_authorization_code(self, authorization_code, 

198 redirect_uri, resource, 

199 client_id, client_secret=None, code_verifier=None): 

200 '''Gets a token for a given resource via authorization code for a 

201 server app. 

202  

203 :param str authorization_code: An authorization code returned from a 

204 client. 

205 :param str redirect_uri: the redirect uri that was used in the 

206 authorize call. 

207 :param str resource: A URI that identifies the resource for which the 

208 token is valid. 

209 :param str client_id: The OAuth client id of the calling application. 

210 :param str client_secret: (only for confidential clients)The OAuth 

211 client secret of the calling application. This parameter if not set, 

212 defaults to None 

213 :param str code_verifier: (optional)The code verifier that was used to 

214 obtain authorization code if PKCE was used in the authorization 

215 code grant request.(usually used by public clients) This parameter if not set, 

216 defaults to None 

217 :returns: dict with several keys, include "accessToken" and 

218 "refreshToken". 

219 ''' 

220 def token_func(self): 

221 token_request = TokenRequest( 

222 self._call_context, 

223 self, 

224 client_id, 

225 resource, 

226 redirect_uri) 

227 return token_request.get_token_with_authorization_code( 

228 authorization_code, 

229 client_secret, code_verifier) 

230 

231 return self._acquire_token(token_func) 

232 

233 def acquire_token_with_refresh_token(self, refresh_token, client_id, 

234 resource, client_secret=None): 

235 '''Gets a token for a given resource via refresh tokens 

236  

237 :param str refresh_token: A refresh token returned in a tokne response 

238 from a previous invocation of acquireToken. 

239 :param str client_id: The OAuth client id of the calling application. 

240 :param str resource: A URI that identifies the resource for which the 

241 token is valid. 

242 :param str client_secret: (optional)The OAuth client secret of the 

243 calling application.  

244 :returns: dict with several keys, include "accessToken" and 

245 "refreshToken". 

246 ''' 

247 def token_func(self): 

248 token_request = TokenRequest(self._call_context, self, client_id, resource) 

249 return token_request.get_token_with_refresh_token(refresh_token, client_secret) 

250 

251 return self._acquire_token(token_func) 

252 

253 def acquire_token_with_client_certificate(self, resource, client_id, 

254 certificate, thumbprint, public_certificate=None): 

255 '''Gets a token for a given resource via certificate credentials 

256 

257 :param str resource: A URI that identifies the resource for which the 

258 token is valid. 

259 :param str client_id: The OAuth client id of the calling application. 

260 :param str certificate: A PEM encoded certificate private key. 

261 :param str thumbprint: hex encoded thumbprint of the certificate. 

262 :param str public_certificate(optional): if not None, it will be sent to the service for subject name 

263 and issuer based authentication, which is to support cert auto rolls. The value must match the 

264 certificate private key parameter. 

265 

266 Per `specs <https://tools.ietf.org/html/rfc7515#section-4.1.6>`_, 

267 "the certificate containing 

268 the public key corresponding to the key used to digitally sign the 

269 JWS MUST be the first certificate. This MAY be followed by 

270 additional certificates, with each subsequent certificate being the 

271 one used to certify the previous one." 

272 However, your certificate's issuer may use a different order. 

273 So, if your attempt ends up with an error AADSTS700027 - 

274 "The provided signature value did not match the expected signature value", 

275 you may try use only the leaf cert (in PEM/str format) instead. 

276 

277 :returns: dict with several keys, include "accessToken". 

278 ''' 

279 def token_func(self): 

280 token_request = TokenRequest(self._call_context, self, client_id, resource) 

281 return token_request.get_token_with_certificate(certificate, thumbprint, public_certificate) 

282 

283 return self._acquire_token(token_func) 

284 

285 def acquire_user_code(self, resource, client_id, language=None): 

286 '''Gets the user code info which contains user_code, device_code for 

287 authenticating user on device. 

288  

289 :param str resource: A URI that identifies the resource for which the  

290 device_code and user_code is valid for. 

291 :param str client_id: The OAuth client id of the calling application. 

292 :param str language: The language code specifying how the message 

293 should be localized to. 

294 :returns: dict contains code and uri for users to login through browser. 

295 ''' 

296 self._call_context['log_context'] = log.create_log_context( 

297 self.correlation_id, self._call_context.get('enable_pii', False)) 

298 self.authority.validate(self._call_context) 

299 code_request = CodeRequest(self._call_context, self, client_id, resource) 

300 return code_request.get_user_code_info(language) 

301 

302 def acquire_token_with_device_code(self, resource, user_code_info, client_id): 

303 '''Gets a new access token using via a device code.  

304  

305 :param str resource: A URI that identifies the resource for which the 

306 token is valid. 

307 :param dict user_code_info: The code info from the invocation of 

308 "acquire_user_code" 

309 :param str client_id: The OAuth client id of the calling application. 

310 :returns: dict with several keys, include "accessToken" and 

311 "refreshToken". 

312 ''' 

313 def token_func(self): 

314 token_request = TokenRequest(self._call_context, self, client_id, resource) 

315 

316 key = user_code_info[OAuth2DeviceCodeResponseParameters.DEVICE_CODE] 

317 with self._lock: 

318 self._token_requests_with_user_code[key] = token_request 

319 

320 token = token_request.get_token_with_device_code(user_code_info) 

321 

322 with self._lock: 

323 self._token_requests_with_user_code.pop(key, None) 

324 

325 return token 

326 

327 return self._acquire_token(token_func, user_code_info.get('correlation_id', None)) 

328 

329 def cancel_request_to_get_token_with_device_code(self, user_code_info): 

330 '''Cancels the polling request to get token with device code.  

331 

332 :param dict user_code_info: The code info from the invocation of 

333 "acquire_user_code" 

334 :returns: None 

335 ''' 

336 argument.validate_user_code_info(user_code_info) 

337 

338 key = user_code_info[OAuth2DeviceCodeResponseParameters.DEVICE_CODE] 

339 with self._lock: 

340 request = self._token_requests_with_user_code.get(key) 

341 

342 if not request: 

343 raise ValueError('No acquire_token_with_device_code existed to be cancelled') 

344 

345 request.cancel_token_request_with_device_code() 

346 self._token_requests_with_user_code.pop(key, None)