Beispiel #1
0
    def dispatch_to_register():
        """Redirects to the registration page."""

        request = strategy.request
        data = kwargs['response']
        data['terms_of_service'] = True
        data['honor_code'] = True
        data['password'] = '******'
        data['name'] = ' '.join([data['firstname'], data['lastname']])
        data['provider'] = backend.name

        if request.session.get('ExternalAuthMap'):
            del request.session['ExternalAuthMap']
        if User.objects.filter(email=data['email']).exists():
            return redirect(AUTH_DISPATCH_URLS[AUTH_ENTRY_LOGIN])

        create_account_with_params(request, data)
        user = request.user
        user.is_active = True
        user.save()
        set_logged_in_cookies(request, JsonResponse({"success": True}))

        # add roles for User
        add_user_roles(user, data['permissions'])

        return redirect(AUTH_DISPATCH_URLS[AUTH_ENTRY_LOGIN])
Beispiel #2
0
def login_user(request):
    """
    AJAX request to log in the user.
    """
    third_party_auth_requested = third_party_auth.is_enabled() and pipeline.running(request)
    trumped_by_first_party_auth = bool(request.POST.get('email')) or bool(request.POST.get('password'))
    was_authenticated_third_party = False

    try:
        if third_party_auth_requested and not trumped_by_first_party_auth:
            # The user has already authenticated via third-party auth and has not
            # asked to do first party auth by supplying a username or password. We
            # now want to put them through the same logging and cookie calculation
            # logic as with first-party auth.

            # This nested try is due to us only returning an HttpResponse in this
            # one case vs. JsonResponse everywhere else.
            try:
                email_user = _do_third_party_auth(request)
                was_authenticated_third_party = True
            except AuthFailedError as e:
                return HttpResponse(e.value, content_type="text/plain", status=403)
        else:
            email_user = _get_user_by_email(request)

        _check_shib_redirect(email_user)
        _check_excessive_login_attempts(email_user)
        _check_forced_password_reset(email_user)

        possibly_authenticated_user = email_user

        if not was_authenticated_third_party:
            possibly_authenticated_user = _authenticate_first_party(request, email_user)
            if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login():
                # Important: This call must be made AFTER the user was successfully authenticated.
                _enforce_password_policy_compliance(request, possibly_authenticated_user)

        if possibly_authenticated_user is None or not possibly_authenticated_user.is_active:
            _handle_failed_authentication(email_user)

        _handle_successful_authentication_and_login(possibly_authenticated_user, request)

        redirect_url = None  # The AJAX method calling should know the default destination upon success
        if was_authenticated_third_party:
            running_pipeline = pipeline.get(request)
            redirect_url = pipeline.get_complete_url(backend_name=running_pipeline['backend'])

        response = JsonResponse({
            'success': True,
            'redirect_url': redirect_url,
        })

        # Ensure that the external marketing site can
        # detect that the user is logged in.
        return set_logged_in_cookies(request, response, possibly_authenticated_user)
    except AuthFailedError as error:
        return JsonResponse(error.get_response())
Beispiel #3
0
    def dispatch_to_register():
        """Redirects to the registration page."""

        request = strategy.request
        data = kwargs['response']
        data['terms_of_service'] = True
        data['honor_code'] = True
        data['password'] = '******'
        data['name'] = ' '.join([data['firstname'], data['lastname']])
        data['provider'] = backend.name

        if request.session.get('ExternalAuthMap'):
            del request.session['ExternalAuthMap']

        create_account_with_params(request, data)
        user = request.user
        user.is_active = True
        user.save()
        set_logged_in_cookies(request, JsonResponse({"success": True}))
        return redirect(AUTH_DISPATCH_URLS[AUTH_ENTRY_LOGIN])
Beispiel #4
0
    def post(self, request):
        """Create the user's account.

        You must send all required form fields with the request.

        You can optionally send a "course_id" param to indicate in analytics
        events that the user registered while enrolling in a particular course.

        Arguments:
            request (HTTPRequest)

        Returns:
            HttpResponse: 200 on success
            HttpResponse: 400 if the request is not valid.
            HttpResponse: 409 if an account with the given username or email
                address already exists
            HttpResponse: 403 operation not allowed
        """
        data = request.POST.copy()

        email = data.get('email')
        username = data.get('username')

        # Handle duplicate email/username
        conflicts = check_account_exists(email=email, username=username)
        if conflicts:
            conflict_messages = {
                "email":
                _(
                    # Translators: This message is shown to users who attempt to create a new
                    # account using an email address associated with an existing account.
                    u"It looks like {email_address} belongs to an existing account. "
                    u"Try again with a different email address.").format(
                        email_address=email),
                "username":
                _(
                    # Translators: This message is shown to users who attempt to create a new
                    # account using a username associated with an existing account.
                    u"It looks like {username} belongs to an existing account. "
                    u"Try again with a different username.").format(
                        username=username),
            }
            errors = {
                field: [{
                    "user_message": conflict_messages[field]
                }]
                for field in conflicts
            }
            return JsonResponse(errors, status=409)

        # Backwards compatibility: the student view expects both
        # terms of service and honor code values.  Since we're combining
        # these into a single checkbox, the only value we may get
        # from the new view is "honor_code".
        # Longer term, we will need to make this more flexible to support
        # open source installations that may have separate checkboxes
        # for TOS, privacy policy, etc.
        if data.get("honor_code") and "terms_of_service" not in data:
            data["terms_of_service"] = data["honor_code"]

        try:
            user = create_account_with_params(request, data)
        except ValidationError as err:
            # Should only get non-field errors from this function
            assert NON_FIELD_ERRORS not in err.message_dict
            # Only return first error for each field
            errors = {
                field: [{
                    "user_message": error
                } for error in error_list]
                for field, error_list in err.message_dict.items()
            }
            return JsonResponse(errors, status=400)
        except PermissionDenied:
            return HttpResponseForbidden(_("Account creation not allowed."))

        response = JsonResponse({"success": True})
        set_logged_in_cookies(request, response, user)
        return response
Beispiel #5
0
    def post(self, request):
        """Create the user's account.

        You must send all required form fields with the request.

        You can optionally send a "course_id" param to indicate in analytics
        events that the user registered while enrolling in a particular course.

        Arguments:
            request (HTTPRequest)

        Returns:
            HttpResponse: 200 on success
            HttpResponse: 400 if the request is not valid.
            HttpResponse: 409 if an account with the given username or email
                address already exists
        """
        data = request.POST.copy()

        email = data.get('email')
        username = data.get('username')

        # Handle duplicate email/username
        conflicts = check_account_exists(email=email, username=username)
        if conflicts:
            conflict_messages = {
                "email": _(
                    # Translators: This message is shown to users who attempt to create a new
                    # account using an email address associated with an existing account.
                    u"It looks like {email_address} belongs to an existing account. "
                    u"Try again with a different email address."
                ).format(email_address=email),
                "username": _(
                    # Translators: This message is shown to users who attempt to create a new
                    # account using a username associated with an existing account.
                    u"It looks like {username} belongs to an existing account. "
                    u"Try again with a different username."
                ).format(username=username),
            }
            errors = {
                field: [{"user_message": conflict_messages[field]}]
                for field in conflicts
            }
            return JsonResponse(errors, status=409)

        # Backwards compatibility: the student view expects both
        # terms of service and honor code values.  Since we're combining
        # these into a single checkbox, the only value we may get
        # from the new view is "honor_code".
        # Longer term, we will need to make this more flexible to support
        # open source installations that may have separate checkboxes
        # for TOS, privacy policy, etc.
        if data.get("honor_code") and "terms_of_service" not in data:
            data["terms_of_service"] = data["honor_code"]

        try:
            user = create_account_with_params(request, data)
        except ValidationError as err:
            # Should only get non-field errors from this function
            assert NON_FIELD_ERRORS not in err.message_dict
            # Only return first error for each field
            errors = {
                field: [{"user_message": error} for error in error_list]
                for field, error_list in err.message_dict.items()
            }
            return JsonResponse(errors, status=400)

        response = JsonResponse({"success": True})
        set_logged_in_cookies(request, response, user)
        return response
Beispiel #6
0
    def post(self, request):
        """Create the user's account.

        You must send all required form fields with the request.

        You can optionally send a "course_id" param to indicate in analytics
        events that the user registered while enrolling in a particular course.

        Arguments:
            request (HTTPRequest)

        Returns:
            HttpResponse: 200 on success
            HttpResponse: 400 if the request is not valid.
            HttpResponse: 409 if an account with the given username or email
                address already exists
            HttpResponse: 403 operation not allowed
        """
        import logging
        import datetime
        LOGGER = logging.getLogger(__name__)
        start_time = datetime.datetime.now()

        data = request.POST.copy()
        email = data.get('email')
        username = data.get('username')
        LOGGER.info(
            "*************** Request received for registration Username = {username}. Start time {start_time}: **************"
            .format(username=username, start_time=start_time))

        # Handle duplicate email/username
        conflicts = check_account_exists(email=email, username=username)
        if conflicts:
            conflict_messages = {
                "email":
                accounts.EMAIL_CONFLICT_MSG.format(email_address=email),
                "username":
                accounts.USERNAME_CONFLICT_MSG.format(username=username),
            }
            errors = {
                field: [{
                    "user_message": conflict_messages[field]
                }]
                for field in conflicts
            }
            LOGGER.info(
                "*************** Registration failed for Username. Error: Username and email conflicts **************"
                .format(username=username))
            return JsonResponse(errors, status=409)

        # Backwards compatibility: the student view expects both
        # terms of service and honor code values.  Since we're combining
        # these into a single checkbox, the only value we may get
        # from the new view is "honor_code".
        # Longer term, we will need to make this more flexible to support
        # open source installations that may have separate checkboxes
        # for TOS, privacy policy, etc.
        if data.get("honor_code") and "terms_of_service" not in data:
            data["terms_of_service"] = data["honor_code"]

        try:
            user = create_account_with_params(request, data)
        except AccountValidationError as err:
            errors = {err.field: [{"user_message": text_type(err)}]}
            LOGGER.info(
                "*************** Registration failed for Username = {username}. Error: {error} **************"
                .format(username=username, error=err))
            return JsonResponse(errors, status=409)
        except ValidationError as err:
            # Should only get non-field errors from this function
            assert NON_FIELD_ERRORS not in err.message_dict
            # Only return first error for each field
            errors = {
                field: [{
                    "user_message": error
                } for error in error_list]
                for field, error_list in err.message_dict.items()
            }
            LOGGER.info(
                "*************** Registration failed for Username = {username}. Error: {error} **************"
                .format(username=username, error=err))
            return JsonResponse(errors, status=400)
        except PermissionDenied:
            LOGGER.info(
                "*************** Registration failed for Username. Error: PermissionDenied **************"
                .format(username=username))
            return HttpResponseForbidden(_("Account creation not allowed."))

        except Exception as err:
            LOGGER.exception(
                "*************** Registration failed for Username = {username}. Error: {error}"
                .format(username=username, error=err))

        response = JsonResponse({"success": True})
        set_logged_in_cookies(request, response, user)
        end_time = datetime.datetime.now()
        LOGGER.info(
            "*************** Request completed for registration Username = {username}. End time: {end_time} **************"
            .format(username=username, end_time=end_time))
        time_diff = end_time - start_time
        LOGGER.info(
            "*************** Time Difference for User = {username}:  {time_diff} **************"
            .format(username=username, time_diff=time_diff))

        return response
Beispiel #7
0
    def post(self, request):
        """Create the user's account.

        You must send all required form fields with the request.

        You can optionally send a "course_id" param to indicate in analytics
        events that the user registered while enrolling in a particular course.

        Arguments:
            request (HTTPRequest)

        Returns:
            HttpResponse: 200 on success
            HttpResponse: 400 if the request is not valid.
            HttpResponse: 409 if an account with the given username or email
                address already exists
            HttpResponse: 403 operation not allowed
        """
        data = request.POST.copy()

        email = data.get('email')
        username = data.get('username')

        # Handle duplicate email/username
        conflicts = check_account_exists(email=email, username=username)
        if conflicts:
            conflict_messages = {
                "email": accounts.EMAIL_CONFLICT_MSG.format(email_address=email),
                "username": accounts.USERNAME_CONFLICT_MSG.format(username=username),
            }
            errors = {
                field: [{"user_message": conflict_messages[field]}]
                for field in conflicts
            }
            return JsonResponse(errors, status=409)

        # Backwards compatibility: the student view expects both
        # terms of service and honor code values.  Since we're combining
        # these into a single checkbox, the only value we may get
        # from the new view is "honor_code".
        # Longer term, we will need to make this more flexible to support
        # open source installations that may have separate checkboxes
        # for TOS, privacy policy, etc.
        if data.get("honor_code") and "terms_of_service" not in data:
            data["terms_of_service"] = data["honor_code"]

        try:
            user = create_account_with_params(request, data)
        except AccountValidationError as err:
            errors = {
                err.field: [{"user_message": text_type(err)}]
            }
            return JsonResponse(errors, status=409)
        except ValidationError as err:
            # Should only get non-field errors from this function
            assert NON_FIELD_ERRORS not in err.message_dict
            # Only return first error for each field
            errors = {
                field: [{"user_message": error} for error in error_list]
                for field, error_list in err.message_dict.items()
            }
            return JsonResponse(errors, status=400)
        except PermissionDenied:
            return HttpResponseForbidden(_("Account creation not allowed."))

        response = JsonResponse({"success": True})
        set_logged_in_cookies(request, response, user)
        return response
Beispiel #8
0
    def post(self, request):
        """Create the user's account.

        You must send all required form fields with the request.

        You can optionally send a "course_id" param to indicate in analytics
        events that the user registered while enrolling in a particular course.

        Arguments:
            request (HTTPRequest)

        Returns:
            HttpResponse: 200 on success
            HttpResponse: 400 if the request is not valid.
            HttpResponse: 409 if an account with the given username or email
                address already exists
            HttpResponse: 403 operation not allowed
        """
        data = request.POST.copy()

        # Decrypt form data if it is encrypted
        if 'data_token' in request.POST:
            data_token = request.POST.get('data_token')

            try:
                decoded_data = jwt.decode(
                    data_token,
                    settings.EDRAAK_LOGISTRATION_SECRET_KEY,
                    verify=False,
                    algorithms=[
                        settings.EDRAAK_LOGISTRATION_SIGNING_ALGORITHM
                    ])
                data.update(decoded_data)

            except jwt.ExpiredSignatureError:
                err_msg = u"The provided data_token has been expired"
                log.warning(err_msg)

                return JsonResponse({
                    "success": False,
                    "value": err_msg,
                },
                                    status=400)

            except jwt.DecodeError:
                err_msg = u"Signature verification failed"
                log.warning(err_msg)

                return JsonResponse({
                    "success": False,
                    "value": err_msg,
                },
                                    status=400)

            except (jwt.InvalidTokenError, ValueError):
                err_msg = u"Invalid token"
                log.warning(err_msg)

                return JsonResponse({
                    "success": False,
                    "value": err_msg,
                },
                                    status=400)

        email = data.get('email')
        username = data.get('username')

        # Handle duplicate email/username
        conflicts = check_account_exists(email=email, username=username)
        if conflicts:
            conflict_messages = {
                "email":
                accounts.EMAIL_CONFLICT_MSG.format(email_address=email),
                "username":
                accounts.USERNAME_CONFLICT_MSG.format(username=username),
            }
            errors = {
                field: [{
                    "user_message": conflict_messages[field]
                }]
                for field in conflicts
            }
            return JsonResponse(errors, status=409)

        # Backwards compatibility: the student view expects both
        # terms of service and honor code values.  Since we're combining
        # these into a single checkbox, the only value we may get
        # from the new view is "honor_code".
        # Longer term, we will need to make this more flexible to support
        # open source installations that may have separate checkboxes
        # for TOS, privacy policy, etc.
        if data.get("honor_code") and "terms_of_service" not in data:
            data["terms_of_service"] = data["honor_code"]

        try:
            user = create_account_with_params(request, data)
        except AccountValidationError as err:
            errors = {err.field: [{"user_message": text_type(err)}]}
            return JsonResponse(errors, status=409)
        except ValidationError as err:
            # Should only get non-field errors from this function
            assert NON_FIELD_ERRORS not in err.message_dict
            # Only return first error for each field
            errors = {
                field: [{
                    "user_message": error
                } for error in error_list]
                for field, error_list in err.message_dict.items()
            }
            return JsonResponse(errors, status=400)
        except PermissionDenied:
            return HttpResponseForbidden(_("Account creation not allowed."))

        response = JsonResponse({"success": True})
        set_logged_in_cookies(request, response, user)
        return response
Beispiel #9
0
def login_user_custom(request, error=""):  # pylint: disable=too-many-statements,unused-argument
    """AJAX request to log in the user."""

    backend_name = None
    email = None
    password = None
    redirect_url = None
    response = None
    running_pipeline = None
    third_party_auth_requested = third_party_auth.is_enabled(
    ) and pipeline.running(request)
    third_party_auth_successful = False
    trumped_by_first_party_auth = bool(request.POST.get('email')) or bool(
        request.POST.get('password'))
    user = None
    platform_name = configuration_helpers.get_value("platform_name",
                                                    settings.PLATFORM_NAME)

    if third_party_auth_requested and not trumped_by_first_party_auth:
        # The user has already authenticated via third-party auth and has not
        # asked to do first party auth by supplying a username or password. We
        # now want to put them through the same logging and cookie calculation
        # logic as with first-party auth.
        running_pipeline = pipeline.get(request)
        username = running_pipeline['kwargs'].get('username')
        backend_name = running_pipeline['backend']
        third_party_uid = running_pipeline['kwargs']['uid']
        requested_provider = provider.Registry.get_from_pipeline(
            running_pipeline)

        try:
            user = pipeline.get_authenticated_user(requested_provider,
                                                   username, third_party_uid)
            third_party_auth_successful = True
        except User.DoesNotExist:
            AUDIT_LOG.warning(
                u"Login failed - user with username {username} has no social auth "
                "with backend_name {backend_name}".format(
                    username=username, backend_name=backend_name))
            message = _(
                "You've successfully logged into your {provider_name} account, "
                "but this account isn't linked with an {platform_name} account yet."
            ).format(
                platform_name=platform_name,
                provider_name=requested_provider.name,
            )
            message += "<br/><br/>"
            message += _(
                "Use your {platform_name} username and password to log into {platform_name} below, "
                "and then link your {platform_name} account with {provider_name} from your dashboard."
            ).format(
                platform_name=platform_name,
                provider_name=requested_provider.name,
            )
            message += "<br/><br/>"
            message += _(
                "If you don't have an {platform_name} account yet, "
                "click <strong>Register</strong> at the top of the page."
            ).format(platform_name=platform_name)

            return HttpResponse(message, content_type="text/plain", status=403)

    else:

        if 'email' not in request.POST or 'password' not in request.POST:
            return JsonResponse({
                "success":
                False,
                # TODO: User error message
                "value":
                _('There was an error receiving your login information. Please email us.'
                  ),
            })  # TODO: this should be status code 400

        email = request.POST['email']
        password = request.POST['password']
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                AUDIT_LOG.warning(u"Login failed - Unknown user email")
            else:
                AUDIT_LOG.warning(
                    u"Login failed - Unknown user email: {0}".format(email))

    # check if the user has a linked shibboleth account, if so, redirect the user to shib-login
    # This behavior is pretty much like what gmail does for shibboleth.  Try entering some @stanford.edu
    # address into the Gmail login.
    if settings.FEATURES.get('AUTH_USE_SHIB') and user:
        try:
            eamap = ExternalAuthMap.objects.get(user=user)
            if eamap.external_domain.startswith(
                    openedx.core.djangoapps.external_auth.views.
                    SHIBBOLETH_DOMAIN_PREFIX):
                return JsonResponse({
                    "success": False,
                    "redirect": reverse('shib-login'),
                })  # TODO: this should be status code 301  # pylint: disable=fixme
        except ExternalAuthMap.DoesNotExist:
            # This is actually the common case, logging in user without external linked login
            AUDIT_LOG.info(u"User %s w/o external auth attempting login", user)

    # see if account has been locked out due to excessive login failures
    user_found_by_email_lookup = user
    if user_found_by_email_lookup and LoginFailures.is_feature_enabled():
        if LoginFailures.is_user_locked_out(user_found_by_email_lookup):
            lockout_message = _(
                'This account has been temporarily locked due '
                'to excessive login failures. Try again later.')
            return JsonResponse({
                "success": False,
                "value": lockout_message,
            })  # TODO: this should be status code 429  # pylint: disable=fixme

    # see if the user must reset his/her password due to any policy settings
    if user_found_by_email_lookup and PasswordHistory.should_user_reset_password_now(
            user_found_by_email_lookup):
        return JsonResponse({
            "success":
            False,
            "value":
            _('Your password has expired due to password policy on this account. You must '
              'reset your password before you can log in again. Please click the '
              '"Forgot Password" link on this page to reset your password before logging in again.'
              ),
        })  # TODO: this should be status code 403  # pylint: disable=fixme

    # if the user doesn't exist, we want to set the username to an invalid
    # username so that authentication is guaranteed to fail and we can take
    # advantage of the ratelimited backend
    username = user.username if user else ""

    if not third_party_auth_successful:
        try:
            user = authenticate(username=username,
                                password=password,
                                request=request)
        # this occurs when there are too many attempts from the same IP address
        except RateLimitException:
            return JsonResponse({
                "success":
                False,
                "value":
                _('Too many failed login attempts. Try again later.'),
            })  # TODO: this should be status code 429  # pylint: disable=fixme

    if user is None:
        # tick the failed login counters if the user exists in the database
        if user_found_by_email_lookup and LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user_found_by_email_lookup)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if username != "":
            if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                loggable_id = user_found_by_email_lookup.id if user_found_by_email_lookup else "<unknown>"
                AUDIT_LOG.warning(
                    u"Login failed - password for user.id: {0} is invalid".
                    format(loggable_id))
            else:
                AUDIT_LOG.warning(
                    u"Login failed - password for {0} is invalid".format(
                        email))
        return JsonResponse({
            "success": False,
            "value": _('Email or password is incorrect.'),
        })  # TODO: this should be status code 400  # pylint: disable=fixme

    # successful login, clear failed login attempts counters, if applicable
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    # Track the user's sign in
    if hasattr(settings, 'LMS_SEGMENT_KEY') and settings.LMS_SEGMENT_KEY:
        tracking_context = tracker.get_tracker().resolve_context()
        analytics.identify(
            user.id,
            {
                'email': email,
                'username': username
            },
            {
                # Disable MailChimp because we don't want to update the user's email
                # and username in MailChimp on every page load. We only need to capture
                # this data on registration/activation.
                'MailChimp': False
            })

        analytics.track(user.id,
                        "edx.bi.user.account.authenticated", {
                            'category': "conversion",
                            'label': request.POST.get('course_id'),
                            'provider': None
                        },
                        context={
                            'ip': tracking_context.get('ip'),
                            'Google Analytics': {
                                'clientId': tracking_context.get('client_id')
                            }
                        })

    if user is not None and user.is_active:
        try:
            # We do not log here, because we have a handler registered
            # to perform logging on successful logins.
            login(request, user)
            if request.POST.get('remember') == 'true':
                request.session.set_expiry(604800)
                log.debug("Setting user session to never expire")
            else:
                request.session.set_expiry(0)
        except Exception as exc:  # pylint: disable=broad-except
            AUDIT_LOG.critical(
                "Login failed - Could not create session. Is memcached running?"
            )
            log.critical(
                "Login failed - Could not create session. Is memcached running?"
            )
            log.exception(exc)
            raise

        redirect_url = None  # The AJAX method calling should know the default destination upon success
        if third_party_auth_successful:
            redirect_url = pipeline.get_complete_url(backend_name)

        response = JsonResponse({
            "success": True,
            "redirect_url": redirect_url,
        })

        # Ensure that the external marketing site can
        # detect that the user is logged in.
        return set_logged_in_cookies(request, response, user)

    if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
        AUDIT_LOG.warning(
            u"Login failed - Account not active for user.id: {0}, resending activation"
            .format(user.id))
    else:
        AUDIT_LOG.warning(
            u"Login failed - Account not active for user {0}, resending activation"
            .format(username))

    reactivation_email_for_user_custom(request, user)
    not_activated_msg = _(
        "Before you sign in, you need to activate your account. We have sent you an "
        "email message with instructions for activating your account.")
    return JsonResponse({
        "success": False,
        "value": not_activated_msg,
    })  # TODO: this should be status code 400  # pylint: disable=fixme
Beispiel #10
0
def login_user(request):
    """
    AJAX request to log in the user.
    """
    post_data = request.POST.copy()

    # Decrypt form data if it is encrypted
    if 'data_token' in request.POST:
        data_token = request.POST.get('data_token')

        try:
            decoded_data = jwt.decode(
                data_token,
                settings.EDRAAK_LOGISTRATION_SECRET_KEY,
                verify=False,
                algorithms=[settings.EDRAAK_LOGISTRATION_SIGNING_ALGORITHM])
            post_data.update(decoded_data)

        except jwt.ExpiredSignatureError:
            err_msg = u"The provided data_token has been expired"
            AUDIT_LOG.warning(err_msg)

            return JsonResponse({
                "success": False,
                "value": err_msg,
            },
                                status=400)

        except jwt.DecodeError:
            err_msg = u"Signature verification failed"
            AUDIT_LOG.warning(err_msg)

            return JsonResponse({
                "success": False,
                "value": err_msg,
            },
                                status=400)

        except (jwt.InvalidTokenError, ValueError):
            err_msg = u"Invalid token"
            AUDIT_LOG.warning(err_msg)

            return JsonResponse({
                "success": False,
                "value": err_msg,
            },
                                status=400)

    third_party_auth_requested = third_party_auth.is_enabled(
    ) and pipeline.running(request)
    trumped_by_first_party_auth = bool(post_data.get('email')) or bool(
        post_data.get('password'))
    was_authenticated_third_party = False
    parent_user = None
    child_user = None

    try:
        if third_party_auth_requested and not trumped_by_first_party_auth:
            # The user has already authenticated via third-party auth and has not
            # asked to do first party auth by supplying a username or password. We
            # now want to put them through the same logging and cookie calculation
            # logic as with first-party auth.

            # This nested try is due to us only returning an HttpResponse in this
            # one case vs. JsonResponse everywhere else.
            try:
                email_user = _do_third_party_auth(request)
                was_authenticated_third_party = True
            except AuthFailedError as e:
                return HttpResponse(e.value,
                                    content_type="text/plain",
                                    status=403)

        elif 'child_user_id' in post_data:
            child_user_id = post_data['child_user_id']
            try:
                child_user = User.objects.get(id=child_user_id)
            except User.DoesNotExist:
                if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
                    AUDIT_LOG.warning(
                        u"Child login failed - Unknown child user id")
                else:
                    AUDIT_LOG.warning(
                        u"Child login failed - Unknown child user id: {0}".
                        format(child_user_id))

        else:
            email_user = _get_user_by_email(request, post_data=post_data)

        if child_user:
            parent_user = request.user
            email_user = child_user

        _check_shib_redirect(email_user)
        _check_excessive_login_attempts(email_user)
        _check_forced_password_reset(email_user)

        # set the user object to child_user object if a child is being logged in
        possibly_authenticated_user = email_user

        if not was_authenticated_third_party:
            possibly_authenticated_user = _authenticate_first_party(
                request, post_data, email_user)
            if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login(
            ):
                # Important: This call must be made AFTER the user was successfully authenticated.
                _enforce_password_policy_compliance(
                    request, post_data, possibly_authenticated_user)

        if possibly_authenticated_user is None or not possibly_authenticated_user.is_active:
            _handle_failed_authentication(email_user)

        _handle_successful_authentication_and_login(
            possibly_authenticated_user, request, post_data)
        if parent_user:
            request.session['parent_user'] = json.dumps({
                'user_id':
                parent_user.id,
                'username':
                parent_user.username,
                'email':
                parent_user.email,
                'name':
                parent_user.profile.name
            })

        redirect_url = None  # The AJAX method calling should know the default destination upon success
        if was_authenticated_third_party:
            running_pipeline = pipeline.get(request)
            redirect_url = pipeline.get_complete_url(
                backend_name=running_pipeline['backend'])

        response = JsonResponse({
            'success': True,
            'redirect_url': redirect_url,
        })

        # Ensure that the external marketing site can
        # detect that the user is logged in.
        return set_logged_in_cookies(request, response,
                                     possibly_authenticated_user)
    except AuthFailedError as error:
        return JsonResponse(error.get_response())