Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/msal/wstrust_request.py: 36%

28 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:20 +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#------------------------------------------------------------------------------ 

27 

28import uuid 

29from datetime import datetime, timedelta 

30import logging 

31 

32from .mex import Mex 

33from .wstrust_response import parse_response 

34 

35logger = logging.getLogger(__name__) 

36 

37def send_request( 

38 username, password, cloud_audience_urn, endpoint_address, soap_action, http_client, 

39 **kwargs): 

40 if not endpoint_address: 

41 raise ValueError("WsTrust endpoint address can not be empty") 

42 if soap_action is None: 

43 if '/trust/2005/usernamemixed' in endpoint_address: 

44 soap_action = Mex.ACTION_2005 

45 elif '/trust/13/usernamemixed' in endpoint_address: 

46 soap_action = Mex.ACTION_13 

47 if soap_action not in (Mex.ACTION_13, Mex.ACTION_2005): 

48 raise ValueError("Unsupported soap action: %s. " 

49 "Contact your administrator to check your ADFS's MEX settings." % soap_action) 

50 data = _build_rst( 

51 username, password, cloud_audience_urn, endpoint_address, soap_action) 

52 resp = http_client.post(endpoint_address, data=data, headers={ 

53 'Content-type':'application/soap+xml; charset=utf-8', 

54 'SOAPAction': soap_action, 

55 }, **kwargs) 

56 if resp.status_code >= 400: 

57 logger.debug("Unsuccessful WsTrust request receives: %s", resp.text) 

58 # It turns out ADFS uses 5xx status code even with client-side incorrect password error 

59 # resp.raise_for_status() 

60 return parse_response(resp.text) 

61 

62 

63def escape_password(password): 

64 return (password.replace('&', '&').replace('"', '"') 

65 .replace("'", ''') # the only one not provided by cgi.escape(s, True) 

66 .replace('<', '&lt;').replace('>', '&gt;')) 

67 

68 

69def wsu_time_format(datetime_obj): 

70 # WsTrust (http://docs.oasis-open.org/ws-sx/ws-trust/v1.4/ws-trust.html) 

71 # does not seem to define timestamp format, but we see YYYY-mm-ddTHH:MM:SSZ 

72 # here (https://www.ibm.com/developerworks/websphere/library/techarticles/1003_chades/1003_chades.html) 

73 # It avoids the uncertainty of the optional ".ssssss" in datetime.isoformat() 

74 # https://docs.python.org/2/library/datetime.html#datetime.datetime.isoformat 

75 return datetime_obj.strftime('%Y-%m-%dT%H:%M:%SZ') 

76 

77 

78def _build_rst(username, password, cloud_audience_urn, endpoint_address, soap_action): 

79 now = datetime.utcnow() 

80 return """<s:Envelope xmlns:s='{s}' xmlns:wsa='{wsa}' xmlns:wsu='{wsu}'> 

81 <s:Header> 

82 <wsa:Action s:mustUnderstand='1'>{soap_action}</wsa:Action> 

83 <wsa:MessageID>urn:uuid:{message_id}</wsa:MessageID> 

84 <wsa:ReplyTo> 

85 <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address> 

86 </wsa:ReplyTo> 

87 <wsa:To s:mustUnderstand='1'>{endpoint_address}</wsa:To> 

88 

89 <wsse:Security s:mustUnderstand='1' 

90 xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'> 

91 <wsu:Timestamp wsu:Id='_0'> 

92 <wsu:Created>{time_now}</wsu:Created> 

93 <wsu:Expires>{time_expire}</wsu:Expires> 

94 </wsu:Timestamp> 

95 <wsse:UsernameToken wsu:Id='ADALUsernameToken'> 

96 <wsse:Username>{username}</wsse:Username> 

97 <wsse:Password>{password}</wsse:Password> 

98 </wsse:UsernameToken> 

99 </wsse:Security> 

100 

101 </s:Header> 

102 <s:Body> 

103 <wst:RequestSecurityToken xmlns:wst='{wst}'> 

104 <wsp:AppliesTo xmlns:wsp='http://schemas.xmlsoap.org/ws/2004/09/policy'> 

105 <wsa:EndpointReference> 

106 <wsa:Address>{applies_to}</wsa:Address> 

107 </wsa:EndpointReference> 

108 </wsp:AppliesTo> 

109 <wst:KeyType>{key_type}</wst:KeyType> 

110 <wst:RequestType>{request_type}</wst:RequestType> 

111 </wst:RequestSecurityToken> 

112 </s:Body> 

113 </s:Envelope>""".format( 

114 s=Mex.NS["s"], wsu=Mex.NS["wsu"], wsa=Mex.NS["wsa10"], 

115 soap_action=soap_action, message_id=str(uuid.uuid4()), 

116 endpoint_address=endpoint_address, 

117 time_now=wsu_time_format(now), 

118 time_expire=wsu_time_format(now + timedelta(minutes=10)), 

119 username=username, password=escape_password(password), 

120 wst=Mex.NS["wst"] if soap_action == Mex.ACTION_13 else Mex.NS["wst2005"], 

121 applies_to=cloud_audience_urn, 

122 key_type='http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer' 

123 if soap_action == Mex.ACTION_13 else 

124 'http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey', 

125 request_type='http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue' 

126 if soap_action == Mex.ACTION_13 else 

127 'http://schemas.xmlsoap.org/ws/2005/02/trust/Issue', 

128 ) 

129