def process_response(self, request_id=None): """ Process the SAML Response sent by the IdP. :param request_id: Is an optional argumen. Is the ID of the AuthNRequest sent by this SP to the IdP. :type request_id: string :raises: OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND, when a POST with a SAMLResponse is not found """ self.__errors = [] if 'post_data' in self.__request_data and 'SAMLResponse' in self.__request_data[ 'post_data']: # AuthnResponse -- HTTP_POST Binding response = OneLogin_Saml2_Response( self.__settings, self.__request_data['post_data']['SAMLResponse']) if response.is_valid(self.__request_data, request_id): self.__attributes = response.get_attributes() self.__nameid = response.get_nameid() self.__session_index = response.get_session_index() self.__session_expiration = response.get_session_not_on_or_after( ) self.__authenticated = True else: self.__errors.append('invalid_response') self.__error_reason = response.get_error() else: self.__errors.append('invalid_binding') raise OneLogin_Saml2_Error( 'SAML Response not found, Only supported HTTP_POST Binding', OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND)
def testResponseSignedAssertionNot(self): """ Tests the getNameId method of the OneLogin_Saml2_Response Case valid signed response, unsigned assertion """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'open_saml_response.xml')) response = OneLogin_Saml2_Response(settings, b64encode(message)) self.assertEquals('*****@*****.**', response.get_nameid())
def assertion_consumer_service(request): if request.method != 'POST': return http.HttpResponseBadRequest() target_url = request.POST.get('RelayState', '/') response = http.HttpResponseRedirect(target_url) auth = init_saml2_auth(request) saml2_response = OneLogin_Saml2_Response( get_saml2_settings(), request.POST['SAMLResponse'], ) if saml2_response.encrypted: response_document = saml2_response.decrypted_document else: response_document = saml2_response.document log.debug( 'decoded and decrypted SAMLResponse = %s', OneLogin_Saml2_XML.to_string(response_document).decode('utf-8') ) status = get_status(response_document) # status example: {code: FOO, msg: BAR} code = status.get('code') if code != OneLogin_Saml2_Constants.STATUS_SUCCESS: log.error('saml response status: {}'.format(status)) subcode = status.get('subcode', '') realme_inner_code = subcode.split(':')[-1] assert realme_inner_code response.set_cookie( app_settings.EXCHANGE_COOKIE_NAME, realme_inner_code, secure=settings.SESSION_COOKIE_SECURE ) return response auth.process_response() if auth.is_authenticated(): user = authenticate(saml2_auth=auth) if user and user.is_active: auth_login(request, user) auth_strength = get_authentication_strength(response_document) request.session['realme_strength'] = auth_strength.name return response response.set_cookie( app_settings.EXCHANGE_COOKIE_NAME, 'RealMeError', secure=settings.SESSION_COOKIE_SECURE ) return response
def testResponseAndAssertionSigned(self): """ Tests the getNameId method of the OneLogin_Saml2_Response Case valid signed response, signed assertion """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents( join(self.data_path, 'responses', 'simple_saml_php.xml')) response = OneLogin_Saml2_Response( settings, OneLogin_Saml2_Utils.b64encode(message)) self.assertEqual('*****@*****.**', response.get_nameid())
def process_response(self, request_id=None, request_issue_instant=None): """ Process the SAML Response sent by the IdP. :param request_issue_instant: Is an optional argument. Is Issue instant Date of the AuthNRequest sent by this SP to the IdP. :type: request_issue_instant: string :param request_id: Is an optional argument. Is the ID of the AuthNRequest sent by this SP to the IdP. :type request_id: string :raises: OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND, when a POST with a SAMLResponse is not found """ self.__errors = [] self.__error_reason = None if 'post_data' in self.__request_data and 'SAMLResponse' in self.__request_data[ 'post_data']: # AuthnResponse -- HTTP_POST Binding response = OneLogin_Saml2_Response( self.__settings, self.__request_data['post_data']['SAMLResponse']) self.__last_response = response.get_xml_document() if response.is_valid(self.__request_data, request_id=request_id, request_issue_instant=request_issue_instant): self.__attributes = response.get_attributes() self.__nameid = response.get_nameid() self.__nameid_format = response.get_nameid_format() self.__nameid_nq = response.get_nameid_nq() self.__nameid_spnq = response.get_nameid_spnq() self.__session_index = response.get_session_index() self.__session_expiration = response.get_session_not_on_or_after( ) self.__last_message_id = response.get_id() self.__last_assertion_id = response.get_assertion_id() self.__last_authn_contexts = response.get_authn_contexts() self.__authenticated = True self.__last_assertion_not_on_or_after = response.get_assertion_not_on_or_after( ) else: self.__errors.append('invalid_response') self.__error_reason = response.get_error() else: self.__errors.append('invalid_binding') raise OneLogin_Saml2_Error( 'SAML Response not found, Only supported HTTP_POST Binding', OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND)
def testResponseAndAssertionSigned(self): """ Tests the getNameId method of the OneLogin_Saml2_Response Case valid signed response, signed assertion """ settings_info = self.loadSettingsJSON() settings_info['idp']['entityId'] = "https://federate.example.net/saml/saml2/idp/metadata.php" settings_info['sp']['entityId'] = "hello.com" settings = OneLogin_Saml2_Settings(settings_info) message = self.file_contents(join(self.data_path, 'responses', 'simple_saml_php.xml')) response = OneLogin_Saml2_Response(settings, b64encode(message)) self.assertEquals('*****@*****.**', response.get_nameid())
def assertion_consumer_service(request): if request.method != 'POST': return http.HttpResponseBadRequest() target_url = request.POST.get('RelayState', '/') response = http.HttpResponseRedirect(target_url) auth = init_saml2_auth(request) saml2_response = OneLogin_Saml2_Response( get_saml2_settings(), request.POST['SAMLResponse'], ) status = get_status(saml2_response.document) # status example: {code: FOO, msg: BAR} code = status.get('code') if code != OneLogin_Saml2_Constants.STATUS_SUCCESS: log.error('saml response status: {}'.format(status)) subcode = status.get('subcode', '') realme_inner_code = subcode.split(':')[-1] assert realme_inner_code response.set_cookie(settings.EXCHANGE_COOKIE_NAME, realme_inner_code, secure=settings.SESSION_COOKIE_SECURE) return response auth.process_response() if auth.is_authenticated(): user = authenticate(saml2_auth=auth) if user and user.is_active: auth_login(request, user) return response else: # auth.process_response() populates the errors property # whenever it fails the authentication log.error("Error processing SAML response: '{}'. Reason: '{}'".format( auth.get_errors(), auth.get_last_error_reason())) response.set_cookie(settings.EXCHANGE_COOKIE_NAME, 'RealMeError', secure=settings.SESSION_COOKIE_SECURE) return response
def validateAssertion(xml, fingerprint=None, fingerprintalg=None): result = { 'schemaValidate': False, 'signCheck': False, 'certValidity': False, 'certAllowed': True, 'error': 0, 'msg': '', 'assertionName': None, 'chkTime': None, 'chkStatus': None, 'serviceAttributes': None } assert isinstance(xml, compat.text_types) if len(xml) == 0: result['error'] = 1 result['msg'] = 'Empty string supplied as input' return result OneLoginResponse = OneLogin_Saml2_Response({}, Saml2_Utils.b64encode(xml)) xml = xmlRemoveDeclaration(xml) parsedassertion = etree.fromstring(xml) # assertion name path assertionNameXpath = "local-name(/*)" assertionName = parsedassertion.xpath(assertionNameXpath) assertionName = str(assertionName) # find assertion schema if assertionName == 'EntityDescriptor': asscertionShema = 'saml-schema-metadata-2.0.xsd' elif assertionName == 'Response': asscertionShema = 'saml-schema-protocol-2.0.xsd' elif assertionName == 'AuthnRequest': asscertionShema = 'saml-schema-protocol-2.0.xsd' else: result['error'] = 2 result['msg'] = 'Assertion unknown' return result # siganture node path signatureNodeXpath = ".//*[local-name()='Signature']" if assertionName == 'Response': signatureNodeXpath = "*[local-name(/*)='Response']//*[local-name()='Signature']" result['assertionName'] = assertionName # get certificate signing try: signingcert = easyspid.lib.easyspid.get_signature_cert(xml) except Exception as error: signingcert = False # validate xml against its schema schemaCheck = OneLogin_Saml2_XML.validate_xml(xml, asscertionShema, False) if isinstance(schemaCheck, str): result['msg'] = schemaCheck result['schemaValidate'] = False result['error'] = 3 else: result['schemaValidate'] = True # check signature if signingcert: signingfingerprintalg = 'sha1' if fingerprintalg is not None: signingfingerprintalg = fingerprintalg signingfingerprint = (easyspid.lib.easyspid.calcCertFingerprint( signingcert, signingfingerprintalg))['result'] if assertionName == 'EntityDescriptor' and fingerprint is None: allowedCert = easyspid.lib.easyspid.get_metadata_allowed_cert(xml) allowedfingerprint = (easyspid.lib.easyspid.calcCertFingerprint( allowedCert, signingfingerprintalg))['result'] elif assertionName != 'EntityDescriptor' and fingerprint is None: allowedfingerprint = signingfingerprint if fingerprint is not None: allowedfingerprint = fingerprint signCheck = Saml2_Utils.validate_sign( xml, cert=signingcert, fingerprint=signingfingerprint, fingerprintalg=signingfingerprintalg, validatecert=False, debug=False, xpath=signatureNodeXpath, multicerts=None) if signCheck: result['signCheck'] = True else: result['error'] = 3 # check expired certificate certTimeValdity = easyspid.lib.easyspid.timeValidateCert(signingcert) if certTimeValdity: result['certValidity'] = True # checktime certificate allow if allowedfingerprint != signingfingerprint: result['certAllowed'] = False result['error'] = 3 elif not signingcert and assertionName == 'AuthnRequest': result['signCheck'] = None result['certValidity'] = None result['certAllowed'] = None if assertionName == 'Response': try: OneLoginResponse.validate_timestamps(raise_exceptions=True) result['chkTime'] = True except OneLogin_Saml2_ValidationError as error: result['chkTime'] = False result['error'] = 3 try: OneLoginResponse.check_status() result['chkStatus'] = True except OneLogin_Saml2_ValidationError as error: result['chkStatus'] = False result['error'] = 3 try: result['serviceAttributes'] = OneLoginResponse.get_attributes() except: pass return result
def saml_consume(event, cookies): body = event.get('body', "") body_data = urllib.parse.parse_qs(body) relay_state = body_data.get('RelayState', "") if relay_state: relay_state = relay_state[0] raw_saml_response = body_data.get('SAMLResponse', None) assert raw_saml_response, "SAMLResponse not present in POST data!" raw_saml_response = raw_saml_response[0] raw_saml_response = urllib.parse.unquote(raw_saml_response) # filename = "./onelogin-saml.settings.json" # The custom_settings.json contains a # json_data_file = open(filename, 'r') # settings_data dict. # settings_data = json.load(json_data_file) # json_data_file.close() # idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://' + SAML_IDP_HOSTNAME + '/idp/shibboleth') # settings_data['idp'] = idp_data['idp'] settings = OneLogin_Saml2_Settings(get_onelogin_saml_settings()) processed_saml_response = OneLogin_Saml2_Response(settings, raw_saml_response) try: processed_saml_response.check_status() processed_saml_response.is_valid(get_saml_request_info(event), raise_exceptions=True) except OneLogin_Saml2_ValidationError as e: print(e) return response_get_not_authorized_html( "<html><body><h1>Invalid SAML Reponse</h1><pre>{}</pre></body></html>" .format(str(e))) attributes = processed_saml_response.get_attributes() roles = attributes.get('https://aws.amazon.com/SAML/Attributes/Role', []) roles_arns = [] roles_str = "" for r in roles: r1 = r.split(',')[1] roles_arns.append(r1) roles_str += r1 + "\n" intersection = set(roles_arns) & set(TARGET_ROLE_ARNS) authorized_via_role = bool(intersection) netid = attributes.get(SAML_ATTRIBUTE_NETID, ['error-net-id-is-missing-from-saml-response'])[0] authorized_via_netid = netid in TARGET_NETIDS authorized = authorized_via_netid or authorized_via_role html = f'''<html> <body><h1>Consume Saml</h1> <pre> Requested url: { relay_state } Authorized: { authorized } Authorized via role: { authorized_via_role } Authorized via netid: { authorized_via_netid } audiences: { processed_saml_response.get_audiences() } get_issuers: { processed_saml_response.get_issuers() } get_nameid: { processed_saml_response.get_nameid() } netid: { netid } email_address: { attributes.get("urn:oid:0.9.2342.19200300.100.1.3", ['missing'])[0] } Full name: { attributes.get("urn:oid:2.5.4.3", ['missing'])[0] } Display name: { attributes.get("urn:oid:2.16.840.1.113730.3.1.241", ['missing'])[0] } Given name: { attributes.get("urn:oid:2.5.4.42", ['missing'])[0] } Surname: { attributes.get("urn:oid:2.5.4.4", ['missing'])[0] } roles: { roles_str } </pre> </body></html> ''' if not authorized: return response_get_not_authorized_html(html) cookies = get_signed_cookies(get_real_client_ip(event)) if relay_state: return response_redirect(relay_state, cookies) return respond_html(html, cookies)
def process_saml_response(self): """Extract the SAML response from the request and authenticate the user. """ if 'SAMLResponse' not in self.request.form: raise SAMLResponseError('Missing SAMLResponse') saml_repsonse_data = self.request.form['SAMLResponse'] try: saml_response = OneLogin_Saml2_Response(saml2_settings(), saml_repsonse_data) except TypeError as exc: logger.warning(exc.__str__()) raise SAMLResponseError(str(exc)) purl = urlparse(self.request.getURL()) req_data = { 'https': 'on' if purl.scheme == 'https' else 'off', 'http_host': purl.hostname, 'server_port': purl.port, 'script_name': purl.path, 'get_data': self.request.form, # Needed if using ADFS as IdP # https://github.com/onelogin/python-saml/pull/144 'lowercase_urlencoding': True, 'post_data': {}, } try: saml_response.is_valid(req_data, raise_exceptions=True) except OneLogin_Saml2_ValidationError as exc: logger.warning(exc.__str__()) raise SAMLResponseError(exc.message) except OneLogin_Saml2_Error as exc: logger.warning(exc.__str__()) raise SAMLResponseError('SAML2 Error') settings = self.sp_settings() portal_state = getMultiAdapter((self.context, self.request), name=u'plone_portal_state') portal_url = portal_state.portal_url() if settings.store_requests: # Verify InResponseTo attribute and get asscoiated url to redirect # to issued_requests = IAuthNRequestStorage(portal_state.portal()) in_response_to = saml_response.document.get('InResponseTo', None) url = issued_requests.pop(in_response_to) if not url: raise SAMLResponseError('Unknown SAMLResponse') else: # Get destination url from RelayState url = portal_url + urllib.unquote( self.request.form.get('RelayState', '')) userid = saml_response.get_nameid() attributes = { k: v[0] for k, v in saml_response.get_attributes().items() } self.login_user(userid, attributes) self.request.response.redirect('%s' % url) return