Пример #1
0
def _check_excessive_login_attempts(user):
    """
    See if account has been locked out due to excessive login failures
    """
    if user and LoginFailures.is_feature_enabled():
        if LoginFailures.is_user_locked_out(user):
            _generate_locked_out_error_message()
Пример #2
0
def _handle_failed_authentication_viatris(user,site):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if 'viatris-via' not in site:
            if LoginFailures.is_feature_enabled():
                LoginFailures.increment_lockout_counter(user)

        if not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            loggable_id = user.id if user 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(user.email))
    if 'viatris-via' in site:
        raise AuthFailedError(_('Incorrect Password. You can reset your password using Forgot password link or Sign In using the OTP option.By entering you email address you will receive a OTP (One Time Password) by email that will be valid for 2 minutes and you can use it to sign-in into the VIA platform and join lectures.'))
    elif 'viatris-kreon' in site or 'viatris-farmaciaformacion' in site:
        raise AuthFailedError(_('Contrasena incorrecta. Puede restablecer su contrasena utilizando el enlace Olvide mi contrasena.'))
    elif 'viatris-pvp-i' in site or 'viatris.' in site or 'viatris-multimodal' in site:
        raise AuthFailedError(_('Incorrect Password. You can reset your password using Forgot password link.'))
    elif 'viatris-atpon' in site:
        raise AuthFailedError(_('Niepoprawne haslo. Mozesz zresetowac swoje haslo, korzystajac z linku Nie pamietam hasla.'))
    elif 'viatris-norge' in site:
        raise AuthFailedError(_('Du kan tilbakestille passordet ved a bruke Glemt passord - koblingen eller Logg pa med OTP-alternativet.Ved a skrive inn din e-postadresse vil du motta et OTP (engangspassord) pa e-post som vil vaere gyldig i 2 minutter, og som du kan bruke til a logge deg pa norge-plattformen og bli med pa forelesninger.'))
    else:
        raise AuthFailedError(_('Incorrect Password. You can reset your password using Forgot password link or Sign In using the OTP option.'))
Пример #3
0
def _handle_successful_authentication_and_login(user, request):
    """
    Handles clearing the failed login counter, login tracking, and setting session timeout.
    """
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    _track_user_login(user, request)

    try:
        django_login(request, user)
        request.session.set_expiry(604800 * 4)
        log.debug("Setting user session expiry to 4 weeks")

        # .. event_implemented_name: SESSION_LOGIN_COMPLETED
        SESSION_LOGIN_COMPLETED.send_event(user=UserData(
            pii=UserPersonalData(
                username=user.username,
                email=user.email,
                name=user.profile.name,
            ),
            id=user.id,
            is_active=user.is_active,
        ), )
    except Exception as exc:
        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
Пример #4
0
    def test_password_reset_with_login_failures_feature_disabled(self):
        """
        Tests that user's login failures lockout counter is not reset upon successful password reset.
        """

        # Adding an entry in LoginFailures to verify the password reset endpoint
        # does not reset the user's login failures lockout counter.
        LoginFailures.increment_lockout_counter(self.user)

        request_params = {
            'new_password1': 'password1',
            'new_password2': 'password1'
        }
        confirm_request = self.request_factory.post(
            self.password_reset_confirm_url, data=request_params)
        self.setup_request_session_with_token(confirm_request)
        confirm_request.user = self.user

        # Make a password reset request.
        resp = PasswordResetConfirmWrapper.as_view()(confirm_request,
                                                     uidb36=self.uidb36,
                                                     token=self.token)

        # Verify that the user's login failures lockout count is not reset.
        assert resp.status_code == 302
        assert not LoginFailures.is_feature_enabled()
        assert LoginFailures.is_user_locked_out(confirm_request.user)
Пример #5
0
def _enforce_password_policy_compliance(request, user):  # lint-amnesty, pylint: disable=missing-function-docstring
    try:
        password_policy_compliance.enforce_compliance_on_login(user, request.POST.get('password'))
    except password_policy_compliance.NonCompliantPasswordWarning as e:
        # Allow login, but warn the user that they will be required to reset their password soon.
        PageLevelMessages.register_warning_message(request, str(e))
    except password_policy_compliance.NonCompliantPasswordException as e:
        # Increment the lockout counter to safguard from further brute force requests
        # if user's password has been compromised.
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        AUDIT_LOG.info("Password reset initiated for email %s.", user.email)
        send_password_reset_email_for_user(user, request)

        # Prevent the login attempt.
        raise AuthFailedError(HTML(str(e)), error_code=e.__class__.__name__)  # lint-amnesty, pylint: disable=raise-missing-from
Пример #6
0
def _handle_successful_authentication_and_login(user, request):
    """
    Handles clearing the failed login counter, login tracking, and setting session timeout.
    """
    if LoginFailures.is_feature_enabled():
        LoginFailures.clear_lockout_counter(user)

    _track_user_login(user, request)

    try:
        django_login(request, user)
        request.session.set_expiry(604800 * 4)
        log.debug("Setting user session expiry to 4 weeks")
    except Exception as exc:
        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
Пример #7
0
    def test_password_reset_request_with_login_failures_feature_disabled(self):
        """
        Tests that user's login failures lockout counter is not reset upon successful password reset.
        """

        # Adding an entry in LoginFailures to verify the password reset endpoint
        # does not reset the user's login failures lockout counter.
        LoginFailures.increment_lockout_counter(self.user)

        post_request = self.create_reset_request(self.uidb36, self.token,
                                                 False)
        post_request.user = AnonymousUser()
        reset_view = LogistrationPasswordResetView.as_view()
        reset_view(post_request, uidb36=self.uidb36, token=self.token).render()

        # Verify that the user's login failures lockout count is not reset.
        assert not LoginFailures.is_feature_enabled()
        assert LoginFailures.is_user_locked_out(self.user)
Пример #8
0
    def post(self, request, *args, **kwargs):
        # We have to make a copy of request.POST because it is a QueryDict object which is immutable until copied.
        # We have to use request.POST because the password_reset_confirm method takes in the request and a user's
        # password is set to the request.POST['new_password1'] field. We have to also normalize the new_password2
        # field so it passes the equivalence check that new_password1 == new_password2
        # In order to switch out of having to do this copy, we would want to move the normalize_password code into
        # a custom User model's set_password method to ensure it is always happening upon calling set_password.
        request.POST = request.POST.copy()
        request.POST['new_password1'] = normalize_password(
            request.POST['new_password1'])
        request.POST['new_password2'] = normalize_password(
            request.POST['new_password2'])
        is_account_recovery = 'is_account_recovery' in request.GET

        password = request.POST['new_password1']
        response = self._validate_password(password, request)
        if response:
            return response

        response = self._process_password_reset_success(
            request, self.token, self.uidb64, extra_context=self.platform_name)

        # If password reset was unsuccessful a template response is returned (status_code 200).
        # Check if form is invalid then show an error to the user.
        # Note if password reset was successful we get response redirect (status_code 302).
        password_reset_successful = response.status_code == 302
        if not password_reset_successful:
            return self._handle_password_reset_failure(response)

        updated_user = User.objects.get(id=self.uid_int)
        if is_account_recovery:
            self._handle_primary_email_update(updated_user)

        updated_user.save()
        if password_reset_successful and is_account_recovery:
            self._handle_password_creation(request, updated_user)

        # Handles clearing the failed login counter upon password reset.
        if LoginFailures.is_feature_enabled():
            LoginFailures.clear_lockout_counter(updated_user)

        update_session_auth_hash(request, updated_user)
        send_password_reset_success_email(updated_user, request)
        return response
Пример #9
0
 def test_check_password_policy_compliance_exception(self):
     """
     Tests _enforce_password_policy_compliance fails with an exception thrown
     """
     assert not LoginFailures.objects.filter(user=self.user).exists()
     enforce_compliance_on_login = '******'
     with patch(enforce_compliance_on_login) as mock_enforce_compliance_on_login:
         mock_enforce_compliance_on_login.side_effect = NonCompliantPasswordException()
         response, _ = self._login_response(
             self.user_email,
             self.password
         )
         response_content = json.loads(response.content.decode('utf-8'))
     assert not response_content.get('success')
     assert len(mail.outbox) == 1
     assert 'Password reset' in mail.outbox[0].subject
     failure_record = LoginFailures.objects.get(user=self.user)
     assert failure_record.failure_count == 1
     LoginFailures.clear_lockout_counter(user=self.user)
Пример #10
0
def _handle_failed_authentication_microsite(user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        if not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            loggable_id = user.id if user 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(user.email))

    raise AuthFailedError(_('Incorrect Password. You can reset your password using Forgot password link or Sign In using the OTP option.'))
Пример #11
0
def _check_excessive_login_attempts_viatris(user,site):
    """
    See if account has been locked out due to excessive login failures
    """
    #log.info('site--> %s', site)
    if 'viatris-via' not in site:
        if user and LoginFailures.is_feature_enabled():
            if LoginFailures.is_user_locked_out(user):
                if 'viatris-via' in site:
                    raise AuthFailedError(_('The account has been temporarily locked due to excessive login failures. Try again after 5 mins. or Sign In using the One Time Password(OTP) option.By entering you email address you will receive a OTP (One Time Password) by email that will be valid for 2 minutes and you can use it to sign-in into the VIA platform and join lectures.'))
                elif 'viatris-kreon' in site or 'viatris-farmaciaformacion' in site:
                    #log.info('site3--> %s', site)
                    raise AuthFailedError(_('La cuenta se ha bloqueado temporalmente debido a un numero excesivo de errores de inicio de sesion. Intentalo de nuevo despus de 5 minutos.'))
                elif 'viatris-pvp-i' in site or 'viatris-multimodal' in site:
                    raise AuthFailedError(_('The account has been temporarily locked due to excessive login failures. Try again after 5 mins.'))
                elif 'viatris-atpon' in site:
                    raise AuthFailedError(_('Konto zostalo tymczasowo zablokowane z powodu nadmiernych niepowodzen logowania. Sprobuj ponownie za 5 minut.'))
                elif 'viatris-norge' in site:
                    raise AuthFailedError(_('Kontoen er midlertidig last pa grunn av store paloggingsfeil. Prov pa nytt etter 5 minutter.'))
                else:
                    raise AuthFailedError(_('The account has been temporarily locked due to excessive login failures. Try again after 5 mins. or Sign In using the One Time Password(OTP) option.'))
Пример #12
0
    def test_password_reset_request_with_login_failures_feature_enabled(self):
        """
        Tests that user's login failures lockout counter is reset upon successful password reset.
        """

        # Adding an entry in LoginFailures to verify the password reset endpoint
        # reset the user's login failures lockout counter.
        LoginFailures.increment_lockout_counter(self.user)

        post_request = self.create_reset_request(self.uidb36, self.token,
                                                 False)
        post_request.user = AnonymousUser()
        reset_view = LogistrationPasswordResetView.as_view()
        json_response = reset_view(post_request,
                                   uidb36=self.uidb36,
                                   token=self.token).render()
        json_response = json.loads(json_response.content.decode('utf-8'))

        # Verify that the user's login failures lockout count is reset.
        assert json_response.get('reset_status')
        assert not LoginFailures.is_user_locked_out(self.user)

        # Verify that the user's login failures lockout counter is not reset upon
        # password reset failure.
        LoginFailures.increment_lockout_counter(self.user)

        post_request = self.create_reset_request(self.uidb36, self.token,
                                                 False, 'new_password2')
        post_request.user = AnonymousUser()
        reset_view = LogistrationPasswordResetView.as_view()
        reset_view(post_request, uidb36=self.uidb36, token=self.token).render()

        assert LoginFailures.is_user_locked_out(self.user)
Пример #13
0
def _handle_inactive_user_failed_authentication(user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if not user.is_active:
            record, _ = LoginFailures.objects.get_or_create(user=user)
            record.failure_count = record.failure_count + 1
            log.info('count--> %s',record.failure_count)
            if record.failure_count <= 5:
                if record.failure_count < 5:
                    log.info('count1--> %s',record.failure_count)
                    if LoginFailures.is_feature_enabled():
                        LoginFailures.increment_lockout_counter(user)
                    return user
                elif record.failure_count == 5:
                    log.info('count2--> %s',record.failure_count)
                    record.failure_count = record.failure_count
                    #record.lockout_until = datetime.now(UTC)
                    record.save()
                    return user
            else:
                _log_and_raise_inactive_user_auth_error(user)
Пример #14
0
def _handle_failed_authentication(user, authenticated_user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    failure_count = 0
    if user:
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        if authenticated_user and not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        loggable_id = user.id if user else "<unknown>"
        AUDIT_LOG.warning(
            f"Login failed - password for user.id: {loggable_id} is invalid")

    if user and LoginFailures.is_feature_enabled():
        blocked_threshold, failure_count = LoginFailures.check_user_reset_password_threshold(
            user)
        if blocked_threshold:
            if not LoginFailures.is_user_locked_out(user):
                max_failures_allowed = settings.MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED
                remaining_attempts = max_failures_allowed - failure_count
                error_message = Text(
                    _('Email or password is incorrect.'
                      '{li_start}You have {remaining_attempts} more sign-in '
                      'attempts before your account is temporarily locked.{li_end}'
                      '{li_start}If you\'ve forgotten your password, click '
                      '{link_start}here{link_end} to reset.{li_end}')
                ).format(link_start=HTML(
                    '<a http="#login" class="form-toggle" data-type="password-reset">'
                ),
                         link_end=HTML('</a>'),
                         li_start=HTML('<li>'),
                         li_end=HTML('</li>'),
                         remaining_attempts=remaining_attempts)
                raise AuthFailedError(error_message,
                                      error_code='failed-login-attempt',
                                      context={
                                          'remaining_attempts':
                                          remaining_attempts,
                                          'allowed_failure_attempts':
                                          max_failures_allowed,
                                          'failure_count': failure_count,
                                      })

            _generate_locked_out_error_message()

    raise AuthFailedError(
        _('Email or password is incorrect.'),
        error_code='incorrect-email-or-password',
        context={'failure_count': failure_count},
    )
Пример #15
0
def _handle_failed_authentication(user, authenticated_user):
    """
    Handles updating the failed login count, inactive user notifications, and logging failed authentications.
    """
    if user:
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        if authenticated_user and not user.is_active:
            _log_and_raise_inactive_user_auth_error(user)

        # if we didn't find this username earlier, the account for this email
        # doesn't exist, and doesn't have a corresponding password
        if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
            loggable_id = user.id if user 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(user.email))

    if user and LoginFailures.is_feature_enabled():
        blocked_threshold, failure_count = LoginFailures.check_user_reset_password_threshold(user)
        if blocked_threshold:
            if not LoginFailures.is_user_locked_out(user):
                max_failures_allowed = settings.MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED
                remaining_attempts = max_failures_allowed - failure_count
                if not should_redirect_to_logistration_mircrofrontend:  # pylint: disable=no-else-raise
                    raise AuthFailedError(Text(_('Email or password is incorrect.'
                                                 '{li_start}You have {remaining_attempts} more sign-in '
                                                 'attempts before your account is temporarily locked.{li_end}'
                                                 '{li_start}If you\'ve forgotten your password, click '
                                                 '{link_start}here{link_end} to reset.{li_end}'
                                                 ))
                                          .format(
                        link_start=HTML('<a http="#login" class="form-toggle" data-type="password-reset">'),
                        link_end=HTML('</a>'),
                        li_start=HTML('<li>'),
                        li_end=HTML('</li>'),
                        remaining_attempts=remaining_attempts))
                else:
                    raise AuthFailedError(Text(_('Email or password is incorrect.\n'
                                                 'You have {remaining_attempts} more sign-in '
                                                 'attempts before your account is temporarily locked.\n'
                                                 'If you{quote}ve forgotten your password, click '
                                                 '{link_start}here{link_end} to reset.\n'
                                                 ))
                                          .format(
                        quote=HTML("'"),
                        link_start=HTML('<a href="/reset" >'),
                        link_end=HTML('</a>'),
                        remaining_attempts=remaining_attempts))
            else:
                _generate_locked_out_error_message()

    raise AuthFailedError(_('Email or password is incorrect.'))
Пример #16
0
    def post(self, request, **kwargs):
        """ Reset learner password using passed token and new credentials """

        reset_status = False
        uidb36 = kwargs.get('uidb36')
        token = kwargs.get('token')

        has_required_values, uid_int = self._check_token_has_required_values(
            uidb36, token)
        if not has_required_values:
            AUDIT_LOG.exception("Invalid password reset confirm token")
            return Response({'reset_status': reset_status})

        request.data._mutable = True  # lint-amnesty, pylint: disable=protected-access
        request.data['new_password1'] = normalize_password(
            request.data['new_password1'])
        request.data['new_password2'] = normalize_password(
            request.data['new_password2'])

        password = request.data['new_password1']
        try:
            user = User.objects.get(id=uid_int)
            if not default_token_generator.check_token(user, token):
                AUDIT_LOG.exception("Token validation failed")
                return Response({'reset_status': reset_status})

            validate_password(password, user=user)

            if settings.ENABLE_AUTHN_RESET_PASSWORD_HIBP_POLICY:
                # Checks the Pwned Databases for password vulnerability.
                pwned_response = check_pwned_password(password)
                if pwned_response.get('vulnerability', 'no') == 'yes':
                    error_status = {
                        'reset_status': reset_status,
                        'err_msg': accounts.AUTHN_PASSWORD_COMPROMISED_MSG
                    }
                    return Response(error_status)

            form = SetPasswordForm(user, request.data)
            if form.is_valid():
                form.save()
                reset_status = True

                if 'is_account_recovery' in request.GET:
                    try:
                        old_primary_email = user.email
                        user.email = user.account_recovery.secondary_email
                        user.account_recovery.delete()
                        # emit an event that the user changed their secondary email to the primary email
                        tracker.emit(
                            SETTING_CHANGE_INITIATED, {
                                "setting": "email",
                                "old": old_primary_email,
                                "new": user.email,
                                "user_id": user.id,
                            })
                        user.save()
                    except ObjectDoesNotExist:
                        err = 'Account recovery process initiated without AccountRecovery instance for user {username}'
                        log.error(err.format(username=user.username))

                # Handles clearing the failed login counter upon password reset.
                if LoginFailures.is_feature_enabled():
                    LoginFailures.clear_lockout_counter(user)

                send_password_reset_success_email(user, request)
                update_session_auth_hash(request, user)
        except ValidationError as err:
            AUDIT_LOG.exception("Password validation failed")
            error_status = {
                'reset_status': reset_status,
                'err_msg': ' '.join(err.messages)
            }
            return Response(error_status)
        except Exception:  # pylint: disable=broad-except
            AUDIT_LOG.exception("Setting new password failed")

        return Response({'reset_status': reset_status})