Exemple #1
0
def make_response_from_ticket(ticket, service_url):
    """
    Given a CAS ticket and service URL, attempt to validate the user and return a proper redirect response.

    :param str ticket: CAS service ticket
    :param str service_url: Service URL from which the authentication request originates
    :return: redirect response
    """

    service_furl = furl.furl(service_url)
    if 'ticket' in service_furl.args:
        service_furl.args.pop('ticket')
    client = get_client()
    cas_resp = client.service_validate(ticket, service_furl.url)
    if cas_resp.authenticated:
        user, external_credential, action = get_user_from_cas_resp(cas_resp)
        # user found and authenticated
        if user and action == 'authenticate':
            # if we successfully authenticate and a verification key is present, invalidate it
            if user.verification_key:
                user.verification_key = None
                user.save()

            # if user is authenticated by external IDP, ask CAS to authenticate user for a second time
            # this extra step will guarantee that 2FA are enforced
            # current CAS session created by external login must be cleared first before authentication
            if external_credential:
                user.verification_key = generate_verification_key()
                user.save()
                return redirect(get_logout_url(get_login_url(
                    service_url,
                    username=user.username,
                    verification_key=user.verification_key
                )))

            # if user is authenticated by CAS
            return authenticate(
                user,
                cas_resp.attributes['accessToken'],
                redirect(service_furl.url)
            )
        # first time login from external identity provider
        if not user and external_credential and action == 'external_first_login':
            from website.util import web_url_for
            # orcid attributes can be marked private and not shared, default to orcid otherwise
            fullname = '{} {}'.format(cas_resp.attributes.get('given-names', ''), cas_resp.attributes.get('family-name', '')).strip()
            if not fullname:
                fullname = external_credential['id']
            user = {
                'external_id_provider': external_credential['provider'],
                'external_id': external_credential['id'],
                'fullname': fullname,
                'access_token': cas_resp.attributes['accessToken'],
            }
            return external_first_login_authenticate(
                user,
                redirect(web_url_for('external_login_email_get'))
            )
    # Unauthorized: ticket could not be validated, or user does not exist.
    return redirect(service_furl.url)
Exemple #2
0
def make_response_from_ticket(ticket, service_url):
    """
    Given a CAS ticket and service URL, attempt to validate the user and return a proper redirect response.

    :param str ticket: CAS service ticket
    :param str service_url: Service URL from which the authentication request originates
    :return: redirect response
    """

    service_furl = furl.furl(service_url)
    # `service_url` is guaranteed to be removed of `ticket` parameter, which has been pulled off in
    # `framework.sessions.before_request()`.
    if 'ticket' in service_furl.args:
        service_furl.args.pop('ticket')
    client = get_client()
    cas_resp = client.service_validate(ticket, service_furl.url)
    if cas_resp.authenticated:
        user, external_credential, action = get_user_from_cas_resp(cas_resp)
        # user found and authenticated
        if user and action == 'authenticate':
            # if we successfully authenticate and a verification key is present, invalidate it
            if user.verification_key:
                user.verification_key = None
                user.save()

            # if user is authenticated by external IDP, ask CAS to authenticate user for a second time
            # this extra step will guarantee that 2FA are enforced
            # current CAS session created by external login must be cleared first before authentication
            if external_credential:
                user.verification_key = generate_verification_key()
                user.save()
                return redirect(
                    get_logout_url(
                        get_login_url(service_url,
                                      username=user.username,
                                      verification_key=user.verification_key)))

            # if user is authenticated by CAS
            return authenticate(user, cas_resp.attributes['accessToken'],
                                redirect(service_furl.url))
        # first time login from external identity provider
        if not user and external_credential and action == 'external_first_login':
            from website.util import web_url_for
            # orcid attributes can be marked private and not shared, default to orcid otherwise
            fullname = u'{} {}'.format(
                cas_resp.attributes.get('given-names', ''),
                cas_resp.attributes.get('family-name', '')).strip()
            if not fullname:
                fullname = external_credential['id']
            user = {
                'external_id_provider': external_credential['provider'],
                'external_id': external_credential['id'],
                'fullname': fullname,
                'access_token': cas_resp.attributes['accessToken'],
                'service_url': service_furl.url,
            }
            return external_first_login_authenticate(
                user, redirect(web_url_for('external_login_email_get')))
    # Unauthorized: ticket could not be validated, or user does not exist.
    return redirect(service_furl.url)
Exemple #3
0
def make_response_from_ticket(ticket, service_url):
    """
    Given a CAS ticket and service URL, attempt to validate the user and return a proper redirect response.

    :param str ticket: CAS service ticket
    :param str service_url: Service URL from which the authentication request originates
    :return: redirect response
    """

    service_furl = furl.furl(service_url)
    if 'ticket' in service_furl.args:
        service_furl.args.pop('ticket')
    client = get_client()
    cas_resp = client.service_validate(ticket, service_furl.url)
    if cas_resp.authenticated:
        user, external_credential, action = get_user_from_cas_resp(cas_resp)
        # user found and authenticated
        if user and action == 'authenticate':
            # if we successfully authenticate and a verification key is present, invalidate it
            if user.verification_key:
                user.verification_key = None
                user.save()

            if external_credential:
                user.verification_key = generate_verification_key()
                user.save()
                return redirect(
                    get_logout_url(
                        get_login_url(service_url,
                                      username=user.username,
                                      verification_key=user.verification_key)))

            return authenticate(user, cas_resp.attributes['accessToken'],
                                redirect(service_furl.url))
        # first time login from external identity provider
        if not user and external_credential and action == 'external_first_login':
            from website.util import web_url_for
            # orcid attributes can be marked private and not shared, default to orcid otherwise
            fullname = '{} {}'.format(
                cas_resp.attributes.get('given-names', ''),
                cas_resp.attributes.get('family-name', '')).strip()
            if not fullname:
                fullname = external_credential['id']
            user = {
                'external_id_provider': external_credential['provider'],
                'external_id': external_credential['id'],
                'fullname': fullname,
                'access_token': cas_resp.attributes['accessToken'],
            }
            return external_first_login_authenticate(
                user, redirect(web_url_for('external_login_email_get')))
    # Unauthorized: ticket could not be validated, or user does not exist.
    return redirect(service_furl.url)
Exemple #4
0
def make_response_from_ticket(ticket, service_url):
    """
    Given a CAS ticket and service URL, attempt to validate the user and return a proper redirect response.

    :param str ticket: CAS service ticket
    :param str service_url: Service URL from which the authentication request originates
    :return: redirect response
    """

    service_furl = furl.furl(service_url)
    if 'ticket' in service_furl.args:
        service_furl.args.pop('ticket')
    client = get_client()
    cas_resp = client.service_validate(ticket, service_furl.url)
    if cas_resp.authenticated:
        user, external_credential, action = get_user_from_cas_resp(cas_resp)
        # user found and authenticated
        if user and action == 'authenticate':
            # if we successfully authenticate and a verification key is present, invalidate it
            if user.verification_key:
                user.verification_key = None
                user.save()
            return authenticate(
                user,
                cas_resp.attributes['accessToken'],
                redirect(service_furl.url)
            )
        # first time login from external identity provider
        if not user and external_credential and action == 'external_first_login':
            from website.util import web_url_for
            # TODO: [#OSF-6935] verify both names are in attributes, which should be handled in CAS
            user = {
                'external_id_provider': external_credential['provider'],
                'external_id': external_credential['id'],
                'fullname': '{} {}'.format(cas_resp.attributes.get('given-names', ''), cas_resp.attributes.get('family-name', '')),
                'access_token': cas_resp.attributes['accessToken'],
            }
            return external_first_login_authenticate(
                user,
                redirect(web_url_for('external_login_email_get'))
            )
    # Unauthorized: ticket could not be validated, or user does not exist.
    return redirect(service_furl.url)
Exemple #5
0
def make_response_from_ticket(ticket, service_url):
    """
    Given a CAS ticket and service URL, attempt to validate the user and return a proper redirect response.

    :param str ticket: CAS service ticket
    :param str service_url: Service URL from which the authentication request originates
    :return: redirect response
    """

    service_furl = furl.furl(service_url)
    # `service_url` is guaranteed to be removed of `ticket` parameter, which has been pulled off in
    # `framework.sessions.before_request()`.
    if 'ticket' in service_furl.args:
        service_furl.args.pop('ticket')
    client = get_client()
    cas_resp = client.service_validate(ticket, service_furl.url)
    if cas_resp.authenticated:
        user, external_credential, action = get_user_from_cas_resp(cas_resp)
        user_updates = {}  # serialize updates to user to be applied async
        # user found and authenticated
        if user and action == 'authenticate':
            print_cas_log(
                f'CAS response - authenticating user: user=[{user._id}], '
                f'external=[{external_credential}], action=[{action}]',
                LogLevel.INFO,
            )
            # If users check the TOS consent checkbox via CAS, CAS sets the attribute `termsOfServiceChecked` to `true`
            # and then release it to OSF among other authentication attributes. When OSF receives it, it trusts CAS and
            # updates the user object if this is THE FINAL STEP of the login flow. DON'T update TOS consent status when
            # `external_credential == true` (i.e. w/ `action == 'authenticate'` or `action == 'external_first_login'`)
            # since neither is the final step of a login flow.
            tos_checked_via_cas = cas_resp.attributes.get(
                'termsOfServiceChecked', 'false') == 'true'
            if tos_checked_via_cas:
                user_updates['accepted_terms_of_service'] = timezone.now()
                print_cas_log(
                    f'CAS TOS consent checked: {user.guids.first()._id}, {user.username}',
                    LogLevel.INFO)
            # if we successfully authenticate and a verification key is present, invalidate it
            if user.verification_key:
                user_updates['verification_key'] = None

            # if user is authenticated by external IDP, ask CAS to authenticate user for a second time
            # this extra step will guarantee that 2FA are enforced
            # current CAS session created by external login must be cleared first before authentication
            if external_credential:
                user.verification_key = generate_verification_key()
                user.save()
                print_cas_log(
                    f'CAS response - redirect existing external IdP login to verification key login: user=[{user._id}]',
                    LogLevel.INFO)
                return redirect(
                    get_logout_url(
                        get_login_url(service_url,
                                      username=user.username,
                                      verification_key=user.verification_key)))

            # if user is authenticated by CAS
            # TODO [CAS-27]: Remove Access Token From Service Validation
            print_cas_log(
                f'CAS response - finalizing authentication: user=[{user._id}]',
                LogLevel.INFO)
            return authenticate(user,
                                cas_resp.attributes.get('accessToken', ''),
                                redirect(service_furl.url), user_updates)
        # first time login from external identity provider
        if not user and external_credential and action == 'external_first_login':
            print_cas_log(
                f'CAS response - first login from external IdP: '
                f'external=[{external_credential}], action=[{action}]',
                LogLevel.INFO,
            )
            from website.util import web_url_for
            # orcid attributes can be marked private and not shared, default to orcid otherwise
            fullname = u'{} {}'.format(
                cas_resp.attributes.get('given-names', ''),
                cas_resp.attributes.get('family-name', '')).strip()
            # TODO [CAS-27]: Remove Access Token From Service Validation
            user = {
                'external_id_provider': external_credential['provider'],
                'external_id': external_credential['id'],
                'fullname': fullname,
                'access_token': cas_resp.attributes.get('accessToken', ''),
                'service_url': service_furl.url,
            }
            print_cas_log(
                f'CAS response - creating anonymous session: external=[{external_credential}]',
                LogLevel.INFO)
            return external_first_login_authenticate(
                user, redirect(web_url_for('external_login_email_get')))
    # Unauthorized: ticket could not be validated, or user does not exist.
    print_cas_log(
        'Ticket validation failed or user does not exist. Redirect back to service URL (logged out).',
        LogLevel.ERROR)
    return redirect(service_furl.url)