Example #1
0
    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)
Example #2
0
    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())
Example #3
0
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())
Example #5
0
    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)
Example #6
0
    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())
Example #7
0
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
Example #8
0
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)
Example #10
0
    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