authentik.policies.reputation.signals

authentik reputation request signals

 1"""authentik reputation request signals"""
 2
 3from django.contrib.auth.signals import user_logged_in
 4from django.db.models import F
 5from django.db.models.functions import Greatest, Least
 6from django.dispatch import receiver
 7from django.http import HttpRequest
 8from psqlextra.query import ConflictAction
 9from psqlextra.util import postgres_manager
10from structlog.stdlib import get_logger
11
12from authentik.core.signals import login_failed
13from authentik.events.context_processors.asn import ASN_CONTEXT_PROCESSOR
14from authentik.events.context_processors.geoip import GEOIP_CONTEXT_PROCESSOR
15from authentik.policies.reputation.models import Reputation, reputation_expiry
16from authentik.root.middleware import ClientIPMiddleware
17from authentik.stages.identification.signals import identification_failed
18from authentik.tenants.utils import get_current_tenant
19
20LOGGER = get_logger()
21
22
23def update_score(request: HttpRequest, identifier: str, amount: int):
24    """Update score for IP and User"""
25    remote_ip = ClientIPMiddleware.get_client_ip(request)
26    tenant = getattr(request, "tenant", get_current_tenant())
27    amount = max(tenant.reputation_lower_limit, min(tenant.reputation_upper_limit, amount))
28
29    with postgres_manager(Reputation) as manager:
30        reputation = manager.on_conflict(
31            ["ip", "identifier"],
32            ConflictAction.UPDATE,
33            update_values=dict(
34                score=Greatest(
35                    tenant.reputation_lower_limit,
36                    Least(tenant.reputation_upper_limit, F("score") + amount),
37                ),
38            ),
39        ).insert_and_get(
40            ip=remote_ip,
41            identifier=identifier,
42            score=amount,
43            ip_geo_data=GEOIP_CONTEXT_PROCESSOR.city_dict(remote_ip) or {},
44            ip_asn_data=ASN_CONTEXT_PROCESSOR.asn_dict(remote_ip) or {},
45            expires=reputation_expiry(),
46        )
47
48    LOGGER.info("Updated score", amount=reputation.score, for_user=identifier, for_ip=remote_ip)
49
50
51@receiver(login_failed)
52def handle_failed_login(sender, request, credentials, **_):
53    """Lower Score for failed login attempts"""
54    if "username" in credentials:
55        update_score(request, credentials.get("username"), -1)
56
57
58@receiver(identification_failed)
59def handle_identification_failed(sender, request, uid_field: str, **_):
60    """Lower Score for failed identification attempts"""
61    update_score(request, uid_field, -1)
62
63
64@receiver(user_logged_in)
65def handle_successful_login(sender, request, user, **_):
66    """Raise score for successful attempts"""
67    update_score(request, user.username, 1)
LOGGER = <BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
def update_score( request: django.http.request.HttpRequest, identifier: str, amount: int):
24def update_score(request: HttpRequest, identifier: str, amount: int):
25    """Update score for IP and User"""
26    remote_ip = ClientIPMiddleware.get_client_ip(request)
27    tenant = getattr(request, "tenant", get_current_tenant())
28    amount = max(tenant.reputation_lower_limit, min(tenant.reputation_upper_limit, amount))
29
30    with postgres_manager(Reputation) as manager:
31        reputation = manager.on_conflict(
32            ["ip", "identifier"],
33            ConflictAction.UPDATE,
34            update_values=dict(
35                score=Greatest(
36                    tenant.reputation_lower_limit,
37                    Least(tenant.reputation_upper_limit, F("score") + amount),
38                ),
39            ),
40        ).insert_and_get(
41            ip=remote_ip,
42            identifier=identifier,
43            score=amount,
44            ip_geo_data=GEOIP_CONTEXT_PROCESSOR.city_dict(remote_ip) or {},
45            ip_asn_data=ASN_CONTEXT_PROCESSOR.asn_dict(remote_ip) or {},
46            expires=reputation_expiry(),
47        )
48
49    LOGGER.info("Updated score", amount=reputation.score, for_user=identifier, for_ip=remote_ip)

Update score for IP and User

@receiver(login_failed)
def handle_failed_login(sender, request, credentials, **_):
52@receiver(login_failed)
53def handle_failed_login(sender, request, credentials, **_):
54    """Lower Score for failed login attempts"""
55    if "username" in credentials:
56        update_score(request, credentials.get("username"), -1)

Lower Score for failed login attempts

@receiver(identification_failed)
def handle_identification_failed(sender, request, uid_field: str, **_):
59@receiver(identification_failed)
60def handle_identification_failed(sender, request, uid_field: str, **_):
61    """Lower Score for failed identification attempts"""
62    update_score(request, uid_field, -1)

Lower Score for failed identification attempts

@receiver(user_logged_in)
def handle_successful_login(sender, request, user, **_):
65@receiver(user_logged_in)
66def handle_successful_login(sender, request, user, **_):
67    """Raise score for successful attempts"""
68    update_score(request, user.username, 1)

Raise score for successful attempts