Beispiel #1
0
    def setUp(self):
        """
        Create a valid user for login.
        """

        self.username = self.VALID_USERNAME
        self.password = self.VALID_PASSWORD
        self.email = self.VALID_EMAIL

        self.ip_address = self.VALID_IP_ADDRESS
        self.user_agent = self.VALID_USER_AGENT
        self.path_info = reverse("admin:login")

        self.user = get_user_model().objects.create_superuser(
            username=self.username, password=self.password, email=self.email)

        self.request = HttpRequest()
        self.request.method = "POST"
        self.request.META["REMOTE_ADDR"] = self.ip_address
        self.request.META["HTTP_USER_AGENT"] = self.user_agent
        self.request.META["PATH_INFO"] = self.path_info

        self.request.axes_attempt_time = now()
        self.request.axes_ip_address = get_client_ip_address(self.request)
        self.request.axes_user_agent = get_client_user_agent(self.request)
        self.request.axes_path_info = get_client_path_info(self.request)
        self.request.axes_http_accept = get_client_http_accept(self.request)

        self.credentials = get_credentials(self.username)
Beispiel #2
0
    def user_logged_in(self, sender, request, user, **kwargs):  # pylint: disable=unused-argument
        """
        When user logs in, update the AccessLog related to the user.
        """

        username = user.get_username()
        credentials = get_credentials(username)
        client_str = get_client_str(
            username,
            request.axes_ip_address,
            request.axes_user_agent,
            request.axes_path_info,
            request,
        )

        log.info("AXES: Successful login by %s.", client_str)

        if settings.AXES_RESET_ON_SUCCESS:
            cache_keys = get_client_cache_key(request, credentials)
            for cache_key in cache_keys:
                failures_since_start = self.cache.get(cache_key, default=0)
                self.cache.delete(cache_key)
                log.info(
                    "AXES: Deleted %d failed login attempts by %s from cache.",
                    failures_since_start,
                    client_str,
                )
Beispiel #3
0
    def user_logged_in(self, sender, request: AxesHttpRequest, user, **kwargs):  # pylint: disable=unused-argument
        """
        When user logs in, update the AccessLog related to the user.
        """

        # 1. database query: Clean up expired user attempts from the database
        clean_expired_user_attempts(request.axes_attempt_time)

        username = user.get_username()
        credentials = get_credentials(username)
        client_str = get_client_str(username, request.axes_ip_address,
                                    request.axes_user_agent,
                                    request.axes_path_info)

        log.info('AXES: Successful login by %s.', client_str)

        if not settings.AXES_DISABLE_SUCCESS_ACCESS_LOG:
            # 2. database query: Insert new access logs with login time
            AccessLog.objects.create(
                username=username,
                ip_address=request.axes_ip_address,
                user_agent=request.axes_user_agent,
                http_accept=request.axes_http_accept,
                path_info=request.axes_path_info,
                attempt_time=request.axes_attempt_time,
            )

        if settings.AXES_RESET_ON_SUCCESS:
            # 3. database query: Reset failed attempts for the logging in user
            count = reset_user_attempts(request, credentials)
            log.info(
                'AXES: Deleted %d failed login attempts by %s from database.',
                count, client_str)
Beispiel #4
0
    def authenticate(self, request: AxesHttpRequest, username: str = None, password: str = None, **kwargs: dict):
        """
        Checks user lockout status and raises an exception if user is not allowed to log in.

        This method interrupts the login flow and inserts  error message directly to the
        ``response_context`` attribute that is supplied as a keyword argument.

        :keyword response_context: kwarg that will be have its ``error`` attribute updated with context.
        :raises AxesBackendRequestParameterRequired: if request parameter is not passed.
        :raises AxesBackendPermissionDenied: if user is already locked out.
        """

        if request is None:
            raise AxesBackendRequestParameterRequired('AxesBackend requires a request as an argument to authenticate')

        credentials = get_credentials(username=username, password=password, **kwargs)

        if AxesProxyHandler.is_allowed(request, credentials):
            return

        # Locked out, don't try to authenticate, just update response_context and return.
        # Its a bit weird to pass a context and expect a response value but its nice to get a "why" back.

        error_msg = get_lockout_message()
        response_context = kwargs.get('response_context', {})
        response_context['error'] = error_msg

        # Raise an error that stops the authentication flows at django.contrib.auth.authenticate.
        # This error stops bubbling up at the authenticate call which catches backend PermissionDenied errors.
        # After this error is caught by authenticate it emits a signal indicating user login failed,
        # which is processed by axes.signals.log_user_login_failed which logs the attempt and raises
        # a second exception which bubbles up the middleware stack and produces a HTTP 403 Forbidden reply
        # in the axes.middleware.AxesMiddleware.process_exception middleware exception handler.

        raise AxesBackendPermissionDenied('AxesBackend detected that the given user is locked out')
Beispiel #5
0
    def setUp(self):
        """
        Create a valid user for login.
        """

        self.username = self.VALID_USERNAME
        self.password = self.VALID_PASSWORD
        self.email = self.VALID_EMAIL

        self.ip_address = self.VALID_IP_ADDRESS
        self.user_agent = self.VALID_USER_AGENT
        self.path_info = reverse('admin:login')

        self.user = get_user_model().objects.create_superuser(
            username=self.username,
            password=self.password,
            email=self.email,
        )

        self.request = HttpRequest()
        self.request.method = 'POST'
        self.request.META['REMOTE_ADDR'] = self.ip_address
        self.request.META['HTTP_USER_AGENT'] = self.user_agent
        self.request.META['PATH_INFO'] = self.path_info

        self.credentials = get_credentials(self.username)
Beispiel #6
0
    def setUp(self):
        """
        Create a valid user for login.
        """

        self.username = self.VALID_USERNAME
        self.password = self.VALID_PASSWORD
        self.email = self.VALID_EMAIL

        self.ip_address = self.VALID_IP_ADDRESS
        self.user_agent = self.VALID_USER_AGENT
        self.path_info = reverse('admin:login')

        self.user = get_user_model().objects.create_superuser(
            username=self.username,
            password=self.password,
            email=self.email,
        )

        self.request = HttpRequest()
        self.request.method = 'POST'
        self.request.META['REMOTE_ADDR'] = self.ip_address
        self.request.META['HTTP_USER_AGENT'] = self.user_agent
        self.request.META['PATH_INFO'] = self.path_info

        self.request.axes_attempt_time = now()
        self.request.axes_ip_address = get_client_ip_address(self.request)
        self.request.axes_user_agent = get_client_user_agent(self.request)
        self.request.axes_path_info = get_client_path_info(self.request)
        self.request.axes_http_accept = get_client_http_accept(self.request)

        self.credentials = get_credentials(self.username)
Beispiel #7
0
    def user_logged_in(self, sender, request: AxesHttpRequest, user, **kwargs):  # pylint: disable=unused-argument
        """
        When user logs in, update the AccessLog related to the user.
        """

        if not hasattr(request, 'axes_attempt_time'):
            log.error('AXES: AxesDatabaseHandler.user_logged_in needs a valid AxesHttpRequest object.')
            return

        # 1. database query: Clean up expired user attempts from the database
        clean_expired_user_attempts(request.axes_attempt_time)

        username = user.get_username()
        credentials = get_credentials(username)
        client_str = get_client_str(username, request.axes_ip_address, request.axes_user_agent, request.axes_path_info)

        log.info('AXES: Successful login by %s.', client_str)

        if not settings.AXES_DISABLE_SUCCESS_ACCESS_LOG:
            # 2. database query: Insert new access logs with login time
            AccessLog.objects.create(
                username=username,
                ip_address=request.axes_ip_address,
                user_agent=request.axes_user_agent,
                http_accept=request.axes_http_accept,
                path_info=request.axes_path_info,
                attempt_time=request.axes_attempt_time,
            )

        if settings.AXES_RESET_ON_SUCCESS:
            # 3. database query: Reset failed attempts for the logging in user
            count = reset_user_attempts(request, credentials)
            log.info('AXES: Deleted %d failed login attempts by %s from database.', count, client_str)
Beispiel #8
0
    def user_logged_in(self, sender, request: AxesHttpRequest, user, **kwargs):  # pylint: disable=unused-argument
        """
        When user logs in, update the AccessLog related to the user.
        """

        if not hasattr(request, 'axes_attempt_time'):
            log.error(
                'AXES: AxesCacheHandler.user_logged_in needs a valid AxesHttpRequest object.'
            )
            return

        username = user.get_username()
        credentials = get_credentials(username)
        client_str = get_client_str(username, request.axes_ip_address,
                                    request.axes_user_agent,
                                    request.axes_path_info)

        log.info('AXES: Successful login by %s.', client_str)

        if settings.AXES_RESET_ON_SUCCESS:
            cache_key = get_client_cache_key(request, credentials)
            failures_since_start = self.cache.get(cache_key, default=0)
            self.cache.delete(cache_key)
            log.info(
                'AXES: Deleted %d failed login attempts by %s from cache.',
                failures_since_start, client_str)
Beispiel #9
0
    def authenticate(self,
                     request: AxesHttpRequest,
                     username: str = None,
                     password: str = None,
                     **kwargs):
        """
        Check user lock out status and raises PermissionDenied if user is not allowed to log in.

        Inserts errors directly to `return_context` that is supplied as a keyword argument.

        Use this on top of your AUTHENTICATION_BACKENDS list to prevent locked out users
        from being authenticated in the standard Django authentication flow.

        Note that this method does not log your user in and delegates login to other backends.

        :param request: see django.contrib.auth.backends.ModelBackend.authenticate
        :param username: see django.contrib.auth.backends.ModelBackend.authenticate
        :param password: see django.contrib.auth.backends.ModelBackend.authenticate
        :param kwargs: see django.contrib.auth.backends.ModelBackend.authenticate
        :keyword response_context: context dict that will be updated with error information
        :raises AxesBackendRequestParameterRequired: if request parameter is not given correctly
        :raises AxesBackendPermissionDenied: if user is already locked out
        :return: None
        """

        if request is None:
            raise AxesBackendRequestParameterRequired(
                'AxesBackend requires a request as an argument to authenticate'
            )

        credentials = get_credentials(username=username,
                                      password=password,
                                      **kwargs)

        if AxesProxyHandler.is_allowed(request, credentials):
            return

        # Locked out, don't try to authenticate, just update response_context and return.
        # Its a bit weird to pass a context and expect a response value but its nice to get a "why" back.

        error_msg = get_lockout_message()
        response_context = kwargs.get('response_context', {})
        response_context['error'] = error_msg

        # Raise an error that stops the authentication flows at django.contrib.auth.authenticate.
        # This error stops bubbling up at the authenticate call which catches backend PermissionDenied errors.
        # After this error is caught by authenticate it emits a signal indicating user login failed,
        # which is processed by axes.signals.log_user_login_failed which logs the attempt and raises
        # a second exception which bubbles up the middleware stack and produces a HTTP 403 Forbidden reply
        # in the axes.middleware.AxesMiddleware.process_exception middleware exception handler.

        raise AxesBackendPermissionDenied(
            'AxesBackend detected that the given user is locked out')
Beispiel #10
0
    def authenticate(self,
                     request: AxesHttpRequest,
                     username: str = None,
                     password: str = None,
                     **kwargs: dict):
        """
        Checks user lockout status and raises an exception if user is not allowed to log in.

        This method interrupts the login flow and inserts  error message directly to the
        ``response_context`` attribute that is supplied as a keyword argument.

        :keyword response_context: kwarg that will be have its ``error`` attribute updated with context.
        :raises AxesBackendRequestParameterRequired: if request parameter is not passed.
        :raises AxesBackendPermissionDenied: if user is already locked out.
        """

        if request is None:
            raise AxesBackendRequestParameterRequired(
                'AxesBackend requires a request as an argument to authenticate'
            )

        credentials = get_credentials(username=username,
                                      password=password,
                                      **kwargs)

        if AxesProxyHandler.is_allowed(request, credentials):
            return

        # Locked out, don't try to authenticate, just update response_context and return.
        # Its a bit weird to pass a context and expect a response value but its nice to get a "why" back.

        error_msg = get_lockout_message()
        response_context = kwargs.get('response_context', {})
        response_context['error'] = error_msg

        # Raise an error that stops the authentication flows at django.contrib.auth.authenticate.
        # This error stops bubbling up at the authenticate call which catches backend PermissionDenied errors.
        # After this error is caught by authenticate it emits a signal indicating user login failed,
        # which is processed by axes.signals.log_user_login_failed which logs the attempt and raises
        # a second exception which bubbles up the middleware stack and produces a HTTP 403 Forbidden reply
        # in the axes.middleware.AxesMiddleware.process_exception middleware exception handler.

        raise AxesBackendPermissionDenied(
            'AxesBackend detected that the given user is locked out')
Beispiel #11
0
    def __call__(self, request):
        response = self.get_response(request)

        if "rest_framework" in settings.INSTALLED_APPS:
            AxesProxyHandler.update_request(request)
            username = get_client_username(request)
            credentials = get_credentials(username)
            failures_since_start = AxesProxyHandler.get_failures(
                request, credentials)
            if (settings.AXES_LOCK_OUT_AT_FAILURE
                    and failures_since_start >= get_failure_limit(
                        request, credentials)):

                request.axes_locked_out = True

        if getattr(request, "axes_locked_out", None):
            response = get_lockout_response(request)  # type: ignore

        return response
Beispiel #12
0
    def user_logged_in(self, sender, request: AxesHttpRequest, user, **kwargs):  # pylint: disable=unused-argument
        """
        When user logs in, update the AccessLog related to the user.
        """

        if not hasattr(request, 'axes_attempt_time'):
            log.error('AXES: AxesCacheHandler.user_logged_in needs a valid AxesHttpRequest object.')
            return

        username = user.get_username()
        credentials = get_credentials(username)
        client_str = get_client_str(username, request.axes_ip_address, request.axes_user_agent, request.axes_path_info)

        log.info('AXES: Successful login by %s.', client_str)

        if settings.AXES_RESET_ON_SUCCESS:
            cache_key = get_client_cache_key(request, credentials)
            failures_since_start = self.cache.get(cache_key, default=0)
            self.cache.delete(cache_key)
            log.info('AXES: Deleted %d failed login attempts by %s from cache.', failures_since_start, client_str)
Beispiel #13
0
def email_admins_on_user_locked_out(request, username, ip_address, **kwargs):
    """Email admins on user locked out."""
    tasks.email_admins_on_user_locked_out.apply_async(
        [get_client_cache_key(request, get_credentials(username)), ip_address]
    )