Пример #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)
Пример #2
0
    def user_logged_out(self, sender, request, user, **kwargs):  # pylint: disable=unused-argument
        """
        When user logs out, update the AccessLog related to the user.
        """

        attempt_time = now()

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

        username = user.get_username()
        ip_address = get_client_ip_address(request)
        user_agent = get_client_user_agent(request)
        path_info = get_client_path_info(request)
        client_str = get_client_str(username, ip_address, user_agent,
                                    path_info)

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

        if username and not settings.AXES_DISABLE_ACCESS_LOG:
            # 2. database query: Update existing attempt logs with logout time
            AccessLog.objects.filter(
                username=username,
                logout_time__isnull=True,
            ).update(logout_time=attempt_time, )
Пример #3
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)
Пример #4
0
    def __call__(self, request: HttpRequest):
        request.axes_attempt_time = now()
        request.axes_ip_address = get_client_ip_address(request)
        request.axes_user_agent = get_client_user_agent(request)
        request.axes_path_info = get_client_path_info(request)
        request.axes_http_accept = get_client_http_accept(request)

        return self.get_response(request)
Пример #5
0
    def user_logged_out(self, sender, request, user, **kwargs):
        username = user.get_username()
        ip_address = get_client_ip_address(request)
        user_agent = get_client_user_agent(request)
        path_info = get_client_path_info(request)
        client_str = get_client_str(username, ip_address, user_agent,
                                    path_info)

        log.info('AXES: Successful logout by %s.', client_str)
Пример #6
0
    def user_login_failed(self, sender, credentials, request=None, **kwargs):  # pylint: disable=too-many-locals
        """
        When user login fails, save attempt record in cache and lock user out if necessary.

        :raises AxesSignalPermissionDenied: if user should be locked out.
        """

        if request is None:
            log.error(
                'AXES: AxesCacheHandler.user_login_failed does not function without a request.'
            )
            return

        username = get_client_username(request, credentials)
        ip_address = get_client_ip_address(request)
        user_agent = get_client_user_agent(request)
        path_info = get_client_path_info(request)
        client_str = get_client_str(username, ip_address, user_agent,
                                    path_info)

        if self.is_whitelisted(request, credentials):
            log.info('AXES: Login failed from whitelisted client %s.',
                     client_str)
            return

        failures_since_start = 1 + self.get_failures(request, credentials)

        if failures_since_start > 1:
            log.warning(
                'AXES: Repeated login failure by %s. Count = %d of %d. Updating existing record in the cache.',
                client_str,
                failures_since_start,
                settings.AXES_FAILURE_LIMIT,
            )
        else:
            log.warning(
                'AXES: New login failure by %s. Creating new record in the cache.',
                client_str,
            )

        cache_key = get_client_cache_key(request, credentials)
        self.cache.set(cache_key, failures_since_start, self.cache_timeout)

        if failures_since_start >= settings.AXES_FAILURE_LIMIT:
            log.warning('AXES: Locking out %s after repeated login failures.',
                        client_str)

            user_locked_out.send(
                'axes',
                request=request,
                username=username,
                ip_address=ip_address,
            )

            raise AxesSignalPermissionDenied(
                'Locked out due to repeated login failures.')
Пример #7
0
    def update_request(self, request: HttpRequest):
        """
        Construct an ``AxesHttpRequest`` from the given ``HttpRequest``
        by updating the request with necessary attempt tracking attributes.

        This method is called by the middleware class ``__call__`` method
        when iterating over the middleware stack.
        """

        request.axes_attempt_time = now()
        request.axes_ip_address = get_client_ip_address(request)
        request.axes_user_agent = get_client_user_agent(request)
        request.axes_path_info = get_client_path_info(request)
        request.axes_http_accept = get_client_http_accept(request)
Пример #8
0
    def update_request(self, request: HttpRequest):
        """
        Construct an ``AxesHttpRequest`` from the given ``HttpRequest``
        by updating the request with necessary attempt tracking attributes.

        This method is called by the middleware class ``__call__`` method
        when iterating over the middleware stack.
        """

        request.axes_attempt_time = now()
        request.axes_ip_address = get_client_ip_address(request)
        request.axes_user_agent = get_client_user_agent(request)
        request.axes_path_info = get_client_path_info(request)
        request.axes_http_accept = get_client_http_accept(request)
Пример #9
0
    def update_request(request):
        """
        Update request attributes before passing them into the selected handler class.
        """

        if request is None:
            log.error('AXES: AxesProxyHandler.update_request can not set request attributes to a None request')
            return

        request.axes_locked_out = False
        request.axes_attempt_time = now()
        request.axes_ip_address = get_client_ip_address(request)
        request.axes_user_agent = get_client_user_agent(request)
        request.axes_path_info = get_client_path_info(request)
        request.axes_http_accept = get_client_http_accept(request)
Пример #10
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)
        ip_address = get_client_ip_address(request)
        user_agent = get_client_user_agent(request)
        path_info = get_client_path_info(request)
        client_str = get_client_str(username, ip_address, user_agent,
                                    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)
Пример #11
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.
        """

        attempt_time = now()

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

        username = user.get_username()
        credentials = get_credentials(username)
        ip_address = get_client_ip_address(request)
        user_agent = get_client_user_agent(request)
        path_info = get_client_path_info(request)
        http_accept = get_client_http_accept(request)
        client_str = get_client_str(username, ip_address, user_agent,
                                    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=ip_address,
                user_agent=user_agent,
                http_accept=http_accept,
                path_info=path_info,
                attempt_time=attempt_time,
                trusted=True,
            )

        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)
Пример #12
0
    def user_login_failed(self, sender, credentials, request=None, **kwargs):  # pylint: disable=too-many-locals
        """
        When user login fails, save AccessAttempt record in database and lock user out if necessary.

        :raises AxesSignalPermissionDenied: if user should be locked out.
        """

        attempt_time = now()

        # 1. database query: Clean up expired user attempts from the database before logging new attempts
        clean_expired_user_attempts(attempt_time)

        if request is None:
            log.error(
                'AXES: AxesDatabaseHandler.user_login_failed does not function without a request.'
            )
            return

        username = get_client_username(request, credentials)
        ip_address = get_client_ip_address(request)
        user_agent = get_client_user_agent(request)
        path_info = get_client_path_info(request)
        http_accept = get_client_http_accept(request)
        client_str = get_client_str(username, ip_address, user_agent,
                                    path_info)

        get_data = get_query_str(request.GET)
        post_data = get_query_str(request.POST)

        if self.is_whitelisted(request, credentials):
            log.info('AXES: Login failed from whitelisted client %s.',
                     client_str)
            return

        # 2. database query: Calculate the current maximum failure number from the existing attempts
        failures_since_start = 1 + self.get_failures(request, credentials,
                                                     attempt_time)

        # 3. database query: Insert or update access records with the new failure data
        if failures_since_start > 1:
            # Update failed attempt information but do not touch the username, IP address, or user agent fields,
            # because attackers can request the site with multiple different configurations
            # in order to bypass the defense mechanisms that are used by the site.

            log.warning(
                'AXES: Repeated login failure by %s. Count = %d of %d. Updating existing record in the database.',
                client_str,
                failures_since_start,
                settings.AXES_FAILURE_LIMIT,
            )

            separator = '\n---------\n'

            attempts = get_user_attempts(request, credentials, attempt_time)
            attempts.update(
                get_data=Concat('get_data', Value(separator + get_data)),
                post_data=Concat('post_data', Value(separator + post_data)),
                http_accept=http_accept,
                path_info=path_info,
                failures_since_start=failures_since_start,
                attempt_time=attempt_time,
            )
        else:
            # Record failed attempt with all the relevant information.
            # Filtering based on username, IP address and user agent handled elsewhere,
            # and this handler just records the available information for further use.

            log.warning(
                'AXES: New login failure by %s. Creating new record in the database.',
                client_str,
            )

            AccessAttempt.objects.create(
                username=username,
                ip_address=ip_address,
                user_agent=user_agent,
                get_data=get_data,
                post_data=post_data,
                http_accept=http_accept,
                path_info=path_info,
                failures_since_start=failures_since_start,
                attempt_time=attempt_time,
            )

        if failures_since_start >= settings.AXES_FAILURE_LIMIT:
            log.warning('AXES: Locking out %s after repeated login failures.',
                        client_str)

            user_locked_out.send(
                'axes',
                request=request,
                username=username,
                ip_address=ip_address,
            )

            raise AxesSignalPermissionDenied(
                'Locked out due to repeated login failures.')