policies/reputation: rework reputation to use a single entry, include geo_ip data
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -95,7 +95,7 @@ class TaskViewSet(ViewSet): | |||||||
|                 _("Successfully re-scheduled Task %(name)s!" % {"name": task.task_name}), |                 _("Successfully re-scheduled Task %(name)s!" % {"name": task.task_name}), | ||||||
|             ) |             ) | ||||||
|             return Response(status=204) |             return Response(status=204) | ||||||
|         except ImportError:  # pragma: no cover |         except (ImportError, AttributeError):  # pragma: no cover | ||||||
|             # if we get an import error, the module path has probably changed |             # if we get an import error, the module path has probably changed | ||||||
|             task.delete() |             task.delete() | ||||||
|             return Response(status=500) |             return Response(status=500) | ||||||
|  | |||||||
| @ -46,11 +46,7 @@ from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet | |||||||
| from authentik.policies.expression.api import ExpressionPolicyViewSet | from authentik.policies.expression.api import ExpressionPolicyViewSet | ||||||
| from authentik.policies.hibp.api import HaveIBeenPwendPolicyViewSet | from authentik.policies.hibp.api import HaveIBeenPwendPolicyViewSet | ||||||
| from authentik.policies.password.api import PasswordPolicyViewSet | from authentik.policies.password.api import PasswordPolicyViewSet | ||||||
| from authentik.policies.reputation.api import ( | from authentik.policies.reputation.api import ReputationPolicyViewSet, ReputationViewSet | ||||||
|     IPReputationViewSet, |  | ||||||
|     ReputationPolicyViewSet, |  | ||||||
|     UserReputationViewSet, |  | ||||||
| ) |  | ||||||
| from authentik.providers.ldap.api import LDAPOutpostConfigViewSet, LDAPProviderViewSet | from authentik.providers.ldap.api import LDAPOutpostConfigViewSet, LDAPProviderViewSet | ||||||
| from authentik.providers.oauth2.api.provider import OAuth2ProviderViewSet | from authentik.providers.oauth2.api.provider import OAuth2ProviderViewSet | ||||||
| from authentik.providers.oauth2.api.scope import ScopeMappingViewSet | from authentik.providers.oauth2.api.scope import ScopeMappingViewSet | ||||||
| @ -151,8 +147,7 @@ router.register("policies/event_matcher", EventMatcherPolicyViewSet) | |||||||
| router.register("policies/haveibeenpwned", HaveIBeenPwendPolicyViewSet) | router.register("policies/haveibeenpwned", HaveIBeenPwendPolicyViewSet) | ||||||
| router.register("policies/password_expiry", PasswordExpiryPolicyViewSet) | router.register("policies/password_expiry", PasswordExpiryPolicyViewSet) | ||||||
| router.register("policies/password", PasswordPolicyViewSet) | router.register("policies/password", PasswordPolicyViewSet) | ||||||
| router.register("policies/reputation/users", UserReputationViewSet) | router.register("policies/reputation/scores", ReputationViewSet) | ||||||
| router.register("policies/reputation/ips", IPReputationViewSet) |  | ||||||
| router.register("policies/reputation", ReputationPolicyViewSet) | router.register("policies/reputation", ReputationPolicyViewSet) | ||||||
|  |  | ||||||
| router.register("providers/all", ProviderViewSet) | router.register("providers/all", ProviderViewSet) | ||||||
|  | |||||||
| @ -35,12 +35,11 @@ class GeoIPReader: | |||||||
|  |  | ||||||
|     def __open(self): |     def __open(self): | ||||||
|         """Get GeoIP Reader, if configured, otherwise none""" |         """Get GeoIP Reader, if configured, otherwise none""" | ||||||
|         path = CONFIG.y("authentik.geoip") |         path = CONFIG.y("geoip") | ||||||
|         if path == "" or not path: |         if path == "" or not path: | ||||||
|             return |             return | ||||||
|         try: |         try: | ||||||
|             reader = Reader(path) |             self.__reader = Reader(path) | ||||||
|             self.__reader = reader |  | ||||||
|             self.__last_mtime = stat(path).st_mtime |             self.__last_mtime = stat(path).st_mtime | ||||||
|             LOGGER.info("Loaded GeoIP database", last_write=self.__last_mtime) |             LOGGER.info("Loaded GeoIP database", last_write=self.__last_mtime) | ||||||
|         except OSError as exc: |         except OSError as exc: | ||||||
|  | |||||||
| @ -1,11 +1,11 @@ | |||||||
| """Source API Views""" | """Reputation policy API Views""" | ||||||
| from rest_framework import mixins | from rest_framework import mixins | ||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer | ||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet | from rest_framework.viewsets import GenericViewSet, ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.used_by import UsedByMixin | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.policies.api.policies import PolicySerializer | from authentik.policies.api.policies import PolicySerializer | ||||||
| from authentik.policies.reputation.models import IPReputation, ReputationPolicy, UserReputation | from authentik.policies.reputation.models import Reputation, ReputationPolicy | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReputationPolicySerializer(PolicySerializer): | class ReputationPolicySerializer(PolicySerializer): | ||||||
| @ -29,59 +29,32 @@ class ReputationPolicyViewSet(UsedByMixin, ModelViewSet): | |||||||
|     ordering = ["name"] |     ordering = ["name"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class IPReputationSerializer(ModelSerializer): | class ReputationSerializer(ModelSerializer): | ||||||
|     """IPReputation Serializer""" |     """Reputation Serializer""" | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = IPReputation |         model = Reputation | ||||||
|         fields = [ |         fields = [ | ||||||
|             "pk", |             "pk", | ||||||
|  |             "identifier", | ||||||
|             "ip", |             "ip", | ||||||
|  |             "ip_geo_data", | ||||||
|             "score", |             "score", | ||||||
|             "updated", |             "updated", | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class IPReputationViewSet( | class ReputationViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|     UsedByMixin, |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|     """IPReputation Viewset""" |     """Reputation Viewset""" | ||||||
|  |  | ||||||
|     queryset = IPReputation.objects.all() |     queryset = Reputation.objects.all() | ||||||
|     serializer_class = IPReputationSerializer |     serializer_class = ReputationSerializer | ||||||
|     search_fields = ["ip", "score"] |     search_fields = ["identifier", "ip", "score"] | ||||||
|     filterset_fields = ["ip", "score"] |     filterset_fields = ["identifier", "ip", "score"] | ||||||
|     ordering = ["ip"] |     ordering = ["ip"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserReputationSerializer(ModelSerializer): |  | ||||||
|     """UserReputation Serializer""" |  | ||||||
|  |  | ||||||
|     class Meta: |  | ||||||
|         model = UserReputation |  | ||||||
|         fields = [ |  | ||||||
|             "pk", |  | ||||||
|             "username", |  | ||||||
|             "score", |  | ||||||
|             "updated", |  | ||||||
|         ] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserReputationViewSet( |  | ||||||
|     mixins.RetrieveModelMixin, |  | ||||||
|     mixins.DestroyModelMixin, |  | ||||||
|     UsedByMixin, |  | ||||||
|     mixins.ListModelMixin, |  | ||||||
|     GenericViewSet, |  | ||||||
| ): |  | ||||||
|     """UserReputation Viewset""" |  | ||||||
|  |  | ||||||
|     queryset = UserReputation.objects.all() |  | ||||||
|     serializer_class = UserReputationSerializer |  | ||||||
|     search_fields = ["username", "score"] |  | ||||||
|     filterset_fields = ["username", "score"] |  | ||||||
|     ordering = ["username"] |  | ||||||
|  | |||||||
| @ -13,3 +13,4 @@ class AuthentikPolicyReputationConfig(AppConfig): | |||||||
|  |  | ||||||
|     def ready(self): |     def ready(self): | ||||||
|         import_module("authentik.policies.reputation.signals") |         import_module("authentik.policies.reputation.signals") | ||||||
|  |         import_module("authentik.policies.reputation.tasks") | ||||||
|  | |||||||
| @ -0,0 +1,40 @@ | |||||||
|  | # Generated by Django 4.0.1 on 2022-01-05 18:56 | ||||||
|  |  | ||||||
|  | import uuid | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("authentik_policies_reputation", "0002_auto_20210529_2046"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="Reputation", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "reputation_uuid", | ||||||
|  |                     models.UUIDField( | ||||||
|  |                         default=uuid.uuid4, primary_key=True, serialize=False, unique=True | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("identifier", models.TextField()), | ||||||
|  |                 ("ip", models.GenericIPAddressField()), | ||||||
|  |                 ("ip_geo_data", models.JSONField(default=dict)), | ||||||
|  |                 ("score", models.BigIntegerField(default=0)), | ||||||
|  |                 ("updated", models.DateTimeField(auto_now_add=True)), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "unique_together": {("identifier", "ip")}, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name="IPReputation", | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name="UserReputation", | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -1,17 +1,20 @@ | |||||||
| """authentik reputation request policy""" | """authentik reputation request policy""" | ||||||
| from django.core.cache import cache | from uuid import uuid4 | ||||||
|  |  | ||||||
| from django.db import models | from django.db import models | ||||||
|  | from django.db.models import Sum | ||||||
|  | from django.db.models.query_utils import Q | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from rest_framework.serializers import BaseSerializer | from rest_framework.serializers import BaseSerializer | ||||||
| from structlog import get_logger | from structlog import get_logger | ||||||
|  |  | ||||||
|  | from authentik.lib.models import SerializerModel | ||||||
| from authentik.lib.utils.http import get_client_ip | from authentik.lib.utils.http import get_client_ip | ||||||
| from authentik.policies.models import Policy | from authentik.policies.models import Policy | ||||||
| from authentik.policies.types import PolicyRequest, PolicyResult | from authentik.policies.types import PolicyRequest, PolicyResult | ||||||
|  |  | ||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
| CACHE_KEY_IP_PREFIX = "authentik_reputation_ip_" | CACHE_KEY_PREFIX = "goauthentik.io/policies/reputation/scores/" | ||||||
| CACHE_KEY_USER_PREFIX = "authentik_reputation_user_" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReputationPolicy(Policy): | class ReputationPolicy(Policy): | ||||||
| @ -34,16 +37,17 @@ class ReputationPolicy(Policy): | |||||||
|     def passes(self, request: PolicyRequest) -> PolicyResult: |     def passes(self, request: PolicyRequest) -> PolicyResult: | ||||||
|         remote_ip = get_client_ip(request.http_request) |         remote_ip = get_client_ip(request.http_request) | ||||||
|         passing = False |         passing = False | ||||||
|  |         query = Q() | ||||||
|         if self.check_ip: |         if self.check_ip: | ||||||
|             score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0) |             query |= Q(ip=remote_ip) | ||||||
|             passing += passing or score <= self.threshold |  | ||||||
|             LOGGER.debug("Score for IP", ip=remote_ip, score=score, passing=passing) |  | ||||||
|         if self.check_username: |         if self.check_username: | ||||||
|             score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0) |             query |= Q(identifier=request.user.username) | ||||||
|  |         score = Reputation.objects.filter(query).annotate(total_score=Sum("score")).total_score | ||||||
|         passing += passing or score <= self.threshold |         passing += passing or score <= self.threshold | ||||||
|         LOGGER.debug( |         LOGGER.debug( | ||||||
|                 "Score for Username", |             "Score for user", | ||||||
|             username=request.user.username, |             username=request.user.username, | ||||||
|  |             remote_ip=remote_ip, | ||||||
|             score=score, |             score=score, | ||||||
|             passing=passing, |             passing=passing, | ||||||
|         ) |         ) | ||||||
| @ -55,23 +59,27 @@ class ReputationPolicy(Policy): | |||||||
|         verbose_name_plural = _("Reputation Policies") |         verbose_name_plural = _("Reputation Policies") | ||||||
|  |  | ||||||
|  |  | ||||||
| class IPReputation(models.Model): | class Reputation(SerializerModel): | ||||||
|     """Store score coming from the same IP""" |     """Reputation for user and or IP.""" | ||||||
|  |  | ||||||
|     ip = models.GenericIPAddressField(unique=True) |     reputation_uuid = models.UUIDField(primary_key=True, unique=True, default=uuid4) | ||||||
|     score = models.IntegerField(default=0) |  | ||||||
|     updated = models.DateTimeField(auto_now=True) |  | ||||||
|  |  | ||||||
|     def __str__(self): |     identifier = models.TextField() | ||||||
|         return f"IPReputation for {self.ip} @ {self.score}" |     ip = models.GenericIPAddressField() | ||||||
|  |     ip_geo_data = models.JSONField(default=dict) | ||||||
|  |     score = models.BigIntegerField(default=0) | ||||||
|  |  | ||||||
|  |     updated = models.DateTimeField(auto_now_add=True) | ||||||
|  |  | ||||||
| class UserReputation(models.Model): |     @property | ||||||
|     """Store score attempting to log in as the same username""" |     def serializer(self) -> BaseSerializer: | ||||||
|  |         from authentik.policies.reputation.api import ReputationSerializer | ||||||
|  |  | ||||||
|     username = models.TextField() |         return ReputationSerializer | ||||||
|     score = models.IntegerField(default=0) |  | ||||||
|     updated = models.DateTimeField(auto_now=True) |  | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self) -> str: | ||||||
|         return f"UserReputation for {self.username} @ {self.score}" |         return f"Reputation {self.identifier}/{self.ip} @ {self.score}" | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         unique_together = ("identifier", "ip") | ||||||
|  | |||||||
| @ -2,13 +2,8 @@ | |||||||
| from celery.schedules import crontab | from celery.schedules import crontab | ||||||
|  |  | ||||||
| CELERY_BEAT_SCHEDULE = { | CELERY_BEAT_SCHEDULE = { | ||||||
|     "policies_reputation_ip_save": { |     "policies_reputation_save": { | ||||||
|         "task": "authentik.policies.reputation.tasks.save_ip_reputation", |         "task": "authentik.policies.reputation.tasks.save_reputation", | ||||||
|         "schedule": crontab(minute="*/5"), |  | ||||||
|         "options": {"queue": "authentik_scheduled"}, |  | ||||||
|     }, |  | ||||||
|     "policies_reputation_user_save": { |  | ||||||
|         "task": "authentik.policies.reputation.tasks.save_user_reputation", |  | ||||||
|         "schedule": crontab(minute="*/5"), |         "schedule": crontab(minute="*/5"), | ||||||
|         "options": {"queue": "authentik_scheduled"}, |         "options": {"queue": "authentik_scheduled"}, | ||||||
|     }, |     }, | ||||||
|  | |||||||
| @ -7,28 +7,30 @@ from structlog.stdlib import get_logger | |||||||
|  |  | ||||||
| from authentik.lib.config import CONFIG | from authentik.lib.config import CONFIG | ||||||
| from authentik.lib.utils.http import get_client_ip | from authentik.lib.utils.http import get_client_ip | ||||||
| from authentik.policies.reputation.models import CACHE_KEY_IP_PREFIX, CACHE_KEY_USER_PREFIX | from authentik.policies.reputation.models import CACHE_KEY_PREFIX | ||||||
| from authentik.stages.identification.signals import identification_failed | from authentik.stages.identification.signals import identification_failed | ||||||
|  |  | ||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
| CACHE_TIMEOUT = int(CONFIG.y("redis.cache_timeout_reputation")) | CACHE_TIMEOUT = int(CONFIG.y("redis.cache_timeout_reputation")) | ||||||
|  |  | ||||||
|  |  | ||||||
| def update_score(request: HttpRequest, username: str, amount: int): | def update_score(request: HttpRequest, identifier: str, amount: int): | ||||||
|     """Update score for IP and User""" |     """Update score for IP and User""" | ||||||
|     remote_ip = get_client_ip(request) |     remote_ip = get_client_ip(request) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         # We only update the cache here, as its faster than writing to the DB |         # We only update the cache here, as its faster than writing to the DB | ||||||
|         cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0, CACHE_TIMEOUT) |         score = cache.get_or_set( | ||||||
|         cache.incr(CACHE_KEY_IP_PREFIX + remote_ip, amount) |             CACHE_KEY_PREFIX + remote_ip + identifier, | ||||||
|  |             {"ip": remote_ip, "identifier": identifier, "score": 0}, | ||||||
|         cache.get_or_set(CACHE_KEY_USER_PREFIX + username, 0, CACHE_TIMEOUT) |             CACHE_TIMEOUT, | ||||||
|         cache.incr(CACHE_KEY_USER_PREFIX + username, amount) |         ) | ||||||
|  |         score["score"] += amount | ||||||
|  |         cache.set(CACHE_KEY_PREFIX + remote_ip + identifier, score) | ||||||
|     except ValueError as exc: |     except ValueError as exc: | ||||||
|         LOGGER.warning("failed to set reputation", exc=exc) |         LOGGER.warning("failed to set reputation", exc=exc) | ||||||
|  |  | ||||||
|     LOGGER.debug("Updated score", amount=amount, for_user=username, for_ip=remote_ip) |     LOGGER.debug("Updated score", amount=amount, for_user=identifier, for_ip=remote_ip) | ||||||
|  |  | ||||||
|  |  | ||||||
| @receiver(user_login_failed) | @receiver(user_login_failed) | ||||||
|  | |||||||
| @ -2,14 +2,15 @@ | |||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
|  | from authentik.events.geo import GEOIP_READER | ||||||
| from authentik.events.monitored_tasks import ( | from authentik.events.monitored_tasks import ( | ||||||
|     MonitoredTask, |     MonitoredTask, | ||||||
|     TaskResult, |     TaskResult, | ||||||
|     TaskResultStatus, |     TaskResultStatus, | ||||||
|     prefill_task, |     prefill_task, | ||||||
| ) | ) | ||||||
| from authentik.policies.reputation.models import IPReputation, UserReputation | from authentik.policies.reputation.models import Reputation | ||||||
| from authentik.policies.reputation.signals import CACHE_KEY_IP_PREFIX, CACHE_KEY_USER_PREFIX | from authentik.policies.reputation.signals import CACHE_KEY_PREFIX | ||||||
| from authentik.root.celery import CELERY_APP | from authentik.root.celery import CELERY_APP | ||||||
|  |  | ||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
| @ -17,29 +18,16 @@ LOGGER = get_logger() | |||||||
|  |  | ||||||
| @CELERY_APP.task(bind=True, base=MonitoredTask) | @CELERY_APP.task(bind=True, base=MonitoredTask) | ||||||
| @prefill_task | @prefill_task | ||||||
| def save_ip_reputation(self: MonitoredTask): | def save_reputation(self: MonitoredTask): | ||||||
|     """Save currently cached reputation to database""" |     """Save currently cached reputation to database""" | ||||||
|     objects_to_update = [] |     objects_to_update = [] | ||||||
|     for key, score in cache.get_many(cache.keys(CACHE_KEY_IP_PREFIX + "*")).items(): |     for _, score in cache.get_many(cache.keys(CACHE_KEY_PREFIX + "*")).items(): | ||||||
|         remote_ip = key.replace(CACHE_KEY_IP_PREFIX, "") |         rep, _ = Reputation.objects.get_or_create( | ||||||
|         rep, _ = IPReputation.objects.get_or_create(ip=remote_ip) |             ip=score["ip"], | ||||||
|         rep.score = score |             identifier=score["identifier"], | ||||||
|         objects_to_update.append(rep) |  | ||||||
|     IPReputation.objects.bulk_update(objects_to_update, ["score"]) |  | ||||||
|     self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated IP Reputation"])) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @CELERY_APP.task(bind=True, base=MonitoredTask) |  | ||||||
| @prefill_task |  | ||||||
| def save_user_reputation(self: MonitoredTask): |  | ||||||
|     """Save currently cached reputation to database""" |  | ||||||
|     objects_to_update = [] |  | ||||||
|     for key, score in cache.get_many(cache.keys(CACHE_KEY_USER_PREFIX + "*")).items(): |  | ||||||
|         username = key.replace(CACHE_KEY_USER_PREFIX, "") |  | ||||||
|         rep, _ = UserReputation.objects.get_or_create(username=username) |  | ||||||
|         rep.score = score |  | ||||||
|         objects_to_update.append(rep) |  | ||||||
|     UserReputation.objects.bulk_update(objects_to_update, ["score"]) |  | ||||||
|     self.set_status( |  | ||||||
|         TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated User Reputation"]) |  | ||||||
|         ) |         ) | ||||||
|  |         rep.ip_geo_data = GEOIP_READER.city_dict(score["ip"]) or {} | ||||||
|  |         rep.score = score["score"] | ||||||
|  |         objects_to_update.append(rep) | ||||||
|  |     Reputation.objects.bulk_update(objects_to_update, ["score", "ip_geo_data"]) | ||||||
|  |     self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated Reputation"])) | ||||||
|  | |||||||
| @ -5,14 +5,8 @@ from django.test import RequestFactory, TestCase | |||||||
|  |  | ||||||
| from authentik.core.models import User | from authentik.core.models import User | ||||||
| from authentik.lib.utils.http import DEFAULT_IP | from authentik.lib.utils.http import DEFAULT_IP | ||||||
| from authentik.policies.reputation.models import ( | from authentik.policies.reputation.models import CACHE_KEY_PREFIX, Reputation, ReputationPolicy | ||||||
|     CACHE_KEY_IP_PREFIX, | from authentik.policies.reputation.tasks import save_reputation | ||||||
|     CACHE_KEY_USER_PREFIX, |  | ||||||
|     IPReputation, |  | ||||||
|     ReputationPolicy, |  | ||||||
|     UserReputation, |  | ||||||
| ) |  | ||||||
| from authentik.policies.reputation.tasks import save_ip_reputation, save_user_reputation |  | ||||||
| from authentik.policies.types import PolicyRequest | from authentik.policies.types import PolicyRequest | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -24,9 +18,8 @@ class TestReputationPolicy(TestCase): | |||||||
|         self.request = self.request_factory.get("/") |         self.request = self.request_factory.get("/") | ||||||
|         self.test_ip = "127.0.0.1" |         self.test_ip = "127.0.0.1" | ||||||
|         self.test_username = "test" |         self.test_username = "test" | ||||||
|         cache.delete(CACHE_KEY_IP_PREFIX + self.test_ip) |         keys = cache.keys(CACHE_KEY_PREFIX + "*") | ||||||
|         cache.delete(CACHE_KEY_IP_PREFIX + DEFAULT_IP) |         cache.delete_many(keys) | ||||||
|         cache.delete(CACHE_KEY_USER_PREFIX + self.test_username) |  | ||||||
|         # We need a user for the one-to-one in userreputation |         # We need a user for the one-to-one in userreputation | ||||||
|         self.user = User.objects.create(username=self.test_username) |         self.user = User.objects.create(username=self.test_username) | ||||||
|  |  | ||||||
| @ -35,20 +28,26 @@ class TestReputationPolicy(TestCase): | |||||||
|         # Trigger negative reputation |         # Trigger negative reputation | ||||||
|         authenticate(self.request, username=self.test_username, password=self.test_username) |         authenticate(self.request, username=self.test_username, password=self.test_username) | ||||||
|         # Test value in cache |         # Test value in cache | ||||||
|         self.assertEqual(cache.get(CACHE_KEY_IP_PREFIX + self.test_ip), -1) |         self.assertEqual( | ||||||
|  |             cache.get(CACHE_KEY_PREFIX + self.test_ip + self.test_username), | ||||||
|  |             {"ip": "127.0.0.1", "identifier": "test", "score": -1}, | ||||||
|  |         ) | ||||||
|         # Save cache and check db values |         # Save cache and check db values | ||||||
|         save_ip_reputation.delay().get() |         save_reputation.delay().get() | ||||||
|         self.assertEqual(IPReputation.objects.get(ip=self.test_ip).score, -1) |         self.assertEqual(Reputation.objects.get(ip=self.test_ip).score, -1) | ||||||
|  |  | ||||||
|     def test_user_reputation(self): |     def test_user_reputation(self): | ||||||
|         """test User reputation""" |         """test User reputation""" | ||||||
|         # Trigger negative reputation |         # Trigger negative reputation | ||||||
|         authenticate(self.request, username=self.test_username, password=self.test_username) |         authenticate(self.request, username=self.test_username, password=self.test_username) | ||||||
|         # Test value in cache |         # Test value in cache | ||||||
|         self.assertEqual(cache.get(CACHE_KEY_USER_PREFIX + self.test_username), -1) |         self.assertEqual( | ||||||
|  |             cache.get(CACHE_KEY_PREFIX + self.test_ip + self.test_username), | ||||||
|  |             {"ip": "127.0.0.1", "identifier": "test", "score": -1}, | ||||||
|  |         ) | ||||||
|         # Save cache and check db values |         # Save cache and check db values | ||||||
|         save_user_reputation.delay().get() |         save_reputation.delay().get() | ||||||
|         self.assertEqual(UserReputation.objects.get(username=self.test_username).score, -1) |         self.assertEqual(Reputation.objects.get(identifier=self.test_username).score, -1) | ||||||
|  |  | ||||||
|     def test_policy(self): |     def test_policy(self): | ||||||
|         """Test Policy""" |         """Test Policy""" | ||||||
|  | |||||||
| @ -26,8 +26,8 @@ class PytestTestRunner:  # pragma: no cover | |||||||
|  |  | ||||||
|         settings.TEST = True |         settings.TEST = True | ||||||
|         settings.CELERY_TASK_ALWAYS_EAGER = True |         settings.CELERY_TASK_ALWAYS_EAGER = True | ||||||
|         CONFIG.y_set("authentik.avatars", "none") |         CONFIG.y_set("avatars", "none") | ||||||
|         CONFIG.y_set("authentik.geoip", "tests/GeoLite2-City-Test.mmdb") |         CONFIG.y_set("geoip", "tests/GeoLite2-City-Test.mmdb") | ||||||
|         CONFIG.y_set( |         CONFIG.y_set( | ||||||
|             "outposts.container_image_base", |             "outposts.container_image_base", | ||||||
|             f"ghcr.io/goauthentik/dev-%(type)s:{get_docker_tag()}", |             f"ghcr.io/goauthentik/dev-%(type)s:{get_docker_tag()}", | ||||||
|  | |||||||
							
								
								
									
										353
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										353
									
								
								schema.yml
									
									
									
									
									
								
							| @ -9099,11 +9099,15 @@ paths: | |||||||
|           $ref: '#/components/schemas/ValidationError' |           $ref: '#/components/schemas/ValidationError' | ||||||
|         '403': |         '403': | ||||||
|           $ref: '#/components/schemas/GenericError' |           $ref: '#/components/schemas/GenericError' | ||||||
|   /policies/reputation/ips/: |   /policies/reputation/scores/: | ||||||
|     get: |     get: | ||||||
|       operationId: policies_reputation_ips_list |       operationId: policies_reputation_scores_list | ||||||
|       description: IPReputation Viewset |       description: Reputation Viewset | ||||||
|       parameters: |       parameters: | ||||||
|  |       - in: query | ||||||
|  |         name: identifier | ||||||
|  |         schema: | ||||||
|  |           type: string | ||||||
|       - in: query |       - in: query | ||||||
|         name: ip |         name: ip | ||||||
|         schema: |         schema: | ||||||
| @ -9145,22 +9149,23 @@ paths: | |||||||
|           content: |           content: | ||||||
|             application/json: |             application/json: | ||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/PaginatedIPReputationList' |                 $ref: '#/components/schemas/PaginatedReputationList' | ||||||
|           description: '' |           description: '' | ||||||
|         '400': |         '400': | ||||||
|           $ref: '#/components/schemas/ValidationError' |           $ref: '#/components/schemas/ValidationError' | ||||||
|         '403': |         '403': | ||||||
|           $ref: '#/components/schemas/GenericError' |           $ref: '#/components/schemas/GenericError' | ||||||
|   /policies/reputation/ips/{id}/: |   /policies/reputation/scores/{reputation_uuid}/: | ||||||
|     get: |     get: | ||||||
|       operationId: policies_reputation_ips_retrieve |       operationId: policies_reputation_scores_retrieve | ||||||
|       description: IPReputation Viewset |       description: Reputation Viewset | ||||||
|       parameters: |       parameters: | ||||||
|       - in: path |       - in: path | ||||||
|         name: id |         name: reputation_uuid | ||||||
|         schema: |         schema: | ||||||
|           type: integer |           type: string | ||||||
|         description: A unique integer value identifying this ip reputation. |           format: uuid | ||||||
|  |         description: A UUID string identifying this reputation. | ||||||
|         required: true |         required: true | ||||||
|       tags: |       tags: | ||||||
|       - policies |       - policies | ||||||
| @ -9171,21 +9176,22 @@ paths: | |||||||
|           content: |           content: | ||||||
|             application/json: |             application/json: | ||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/IPReputation' |                 $ref: '#/components/schemas/Reputation' | ||||||
|           description: '' |           description: '' | ||||||
|         '400': |         '400': | ||||||
|           $ref: '#/components/schemas/ValidationError' |           $ref: '#/components/schemas/ValidationError' | ||||||
|         '403': |         '403': | ||||||
|           $ref: '#/components/schemas/GenericError' |           $ref: '#/components/schemas/GenericError' | ||||||
|     delete: |     delete: | ||||||
|       operationId: policies_reputation_ips_destroy |       operationId: policies_reputation_scores_destroy | ||||||
|       description: IPReputation Viewset |       description: Reputation Viewset | ||||||
|       parameters: |       parameters: | ||||||
|       - in: path |       - in: path | ||||||
|         name: id |         name: reputation_uuid | ||||||
|         schema: |         schema: | ||||||
|           type: integer |           type: string | ||||||
|         description: A unique integer value identifying this ip reputation. |           format: uuid | ||||||
|  |         description: A UUID string identifying this reputation. | ||||||
|         required: true |         required: true | ||||||
|       tags: |       tags: | ||||||
|       - policies |       - policies | ||||||
| @ -9198,143 +9204,17 @@ paths: | |||||||
|           $ref: '#/components/schemas/ValidationError' |           $ref: '#/components/schemas/ValidationError' | ||||||
|         '403': |         '403': | ||||||
|           $ref: '#/components/schemas/GenericError' |           $ref: '#/components/schemas/GenericError' | ||||||
|   /policies/reputation/ips/{id}/used_by/: |   /policies/reputation/scores/{reputation_uuid}/used_by/: | ||||||
|     get: |     get: | ||||||
|       operationId: policies_reputation_ips_used_by_list |       operationId: policies_reputation_scores_used_by_list | ||||||
|       description: Get a list of all objects that use this object |       description: Get a list of all objects that use this object | ||||||
|       parameters: |       parameters: | ||||||
|       - in: path |       - in: path | ||||||
|         name: id |         name: reputation_uuid | ||||||
|         schema: |  | ||||||
|           type: integer |  | ||||||
|         description: A unique integer value identifying this ip reputation. |  | ||||||
|         required: true |  | ||||||
|       tags: |  | ||||||
|       - policies |  | ||||||
|       security: |  | ||||||
|       - authentik: [] |  | ||||||
|       responses: |  | ||||||
|         '200': |  | ||||||
|           content: |  | ||||||
|             application/json: |  | ||||||
|               schema: |  | ||||||
|                 type: array |  | ||||||
|                 items: |  | ||||||
|                   $ref: '#/components/schemas/UsedBy' |  | ||||||
|           description: '' |  | ||||||
|         '400': |  | ||||||
|           $ref: '#/components/schemas/ValidationError' |  | ||||||
|         '403': |  | ||||||
|           $ref: '#/components/schemas/GenericError' |  | ||||||
|   /policies/reputation/users/: |  | ||||||
|     get: |  | ||||||
|       operationId: policies_reputation_users_list |  | ||||||
|       description: UserReputation Viewset |  | ||||||
|       parameters: |  | ||||||
|       - name: ordering |  | ||||||
|         required: false |  | ||||||
|         in: query |  | ||||||
|         description: Which field to use when ordering the results. |  | ||||||
|         schema: |         schema: | ||||||
|           type: string |           type: string | ||||||
|       - name: page |           format: uuid | ||||||
|         required: false |         description: A UUID string identifying this reputation. | ||||||
|         in: query |  | ||||||
|         description: A page number within the paginated result set. |  | ||||||
|         schema: |  | ||||||
|           type: integer |  | ||||||
|       - name: page_size |  | ||||||
|         required: false |  | ||||||
|         in: query |  | ||||||
|         description: Number of results to return per page. |  | ||||||
|         schema: |  | ||||||
|           type: integer |  | ||||||
|       - in: query |  | ||||||
|         name: score |  | ||||||
|         schema: |  | ||||||
|           type: integer |  | ||||||
|       - name: search |  | ||||||
|         required: false |  | ||||||
|         in: query |  | ||||||
|         description: A search term. |  | ||||||
|         schema: |  | ||||||
|           type: string |  | ||||||
|       - in: query |  | ||||||
|         name: username |  | ||||||
|         schema: |  | ||||||
|           type: string |  | ||||||
|       tags: |  | ||||||
|       - policies |  | ||||||
|       security: |  | ||||||
|       - authentik: [] |  | ||||||
|       responses: |  | ||||||
|         '200': |  | ||||||
|           content: |  | ||||||
|             application/json: |  | ||||||
|               schema: |  | ||||||
|                 $ref: '#/components/schemas/PaginatedUserReputationList' |  | ||||||
|           description: '' |  | ||||||
|         '400': |  | ||||||
|           $ref: '#/components/schemas/ValidationError' |  | ||||||
|         '403': |  | ||||||
|           $ref: '#/components/schemas/GenericError' |  | ||||||
|   /policies/reputation/users/{id}/: |  | ||||||
|     get: |  | ||||||
|       operationId: policies_reputation_users_retrieve |  | ||||||
|       description: UserReputation Viewset |  | ||||||
|       parameters: |  | ||||||
|       - in: path |  | ||||||
|         name: id |  | ||||||
|         schema: |  | ||||||
|           type: integer |  | ||||||
|         description: A unique integer value identifying this user reputation. |  | ||||||
|         required: true |  | ||||||
|       tags: |  | ||||||
|       - policies |  | ||||||
|       security: |  | ||||||
|       - authentik: [] |  | ||||||
|       responses: |  | ||||||
|         '200': |  | ||||||
|           content: |  | ||||||
|             application/json: |  | ||||||
|               schema: |  | ||||||
|                 $ref: '#/components/schemas/UserReputation' |  | ||||||
|           description: '' |  | ||||||
|         '400': |  | ||||||
|           $ref: '#/components/schemas/ValidationError' |  | ||||||
|         '403': |  | ||||||
|           $ref: '#/components/schemas/GenericError' |  | ||||||
|     delete: |  | ||||||
|       operationId: policies_reputation_users_destroy |  | ||||||
|       description: UserReputation Viewset |  | ||||||
|       parameters: |  | ||||||
|       - in: path |  | ||||||
|         name: id |  | ||||||
|         schema: |  | ||||||
|           type: integer |  | ||||||
|         description: A unique integer value identifying this user reputation. |  | ||||||
|         required: true |  | ||||||
|       tags: |  | ||||||
|       - policies |  | ||||||
|       security: |  | ||||||
|       - authentik: [] |  | ||||||
|       responses: |  | ||||||
|         '204': |  | ||||||
|           description: No response body |  | ||||||
|         '400': |  | ||||||
|           $ref: '#/components/schemas/ValidationError' |  | ||||||
|         '403': |  | ||||||
|           $ref: '#/components/schemas/GenericError' |  | ||||||
|   /policies/reputation/users/{id}/used_by/: |  | ||||||
|     get: |  | ||||||
|       operationId: policies_reputation_users_used_by_list |  | ||||||
|       description: Get a list of all objects that use this object |  | ||||||
|       parameters: |  | ||||||
|       - in: path |  | ||||||
|         name: id |  | ||||||
|         schema: |  | ||||||
|           type: integer |  | ||||||
|         description: A unique integer value identifying this user reputation. |  | ||||||
|         required: true |         required: true | ||||||
|       tags: |       tags: | ||||||
|       - policies |       - policies | ||||||
| @ -21938,28 +21818,6 @@ components: | |||||||
|           type: integer |           type: integer | ||||||
|           maximum: 2147483647 |           maximum: 2147483647 | ||||||
|           minimum: -2147483648 |           minimum: -2147483648 | ||||||
|     IPReputation: |  | ||||||
|       type: object |  | ||||||
|       description: IPReputation Serializer |  | ||||||
|       properties: |  | ||||||
|         pk: |  | ||||||
|           type: integer |  | ||||||
|           readOnly: true |  | ||||||
|           title: ID |  | ||||||
|         ip: |  | ||||||
|           type: string |  | ||||||
|         score: |  | ||||||
|           type: integer |  | ||||||
|           maximum: 2147483647 |  | ||||||
|           minimum: -2147483648 |  | ||||||
|         updated: |  | ||||||
|           type: string |  | ||||||
|           format: date-time |  | ||||||
|           readOnly: true |  | ||||||
|       required: |  | ||||||
|       - ip |  | ||||||
|       - pk |  | ||||||
|       - updated |  | ||||||
|     IdentificationChallenge: |     IdentificationChallenge: | ||||||
|       type: object |       type: object | ||||||
|       description: Identification challenges with all UI elements |       description: Identification challenges with all UI elements | ||||||
| @ -23433,7 +23291,6 @@ components: | |||||||
|           minLength: 1 |           minLength: 1 | ||||||
|         additional_scopes: |         additional_scopes: | ||||||
|           type: string |           type: string | ||||||
|           minLength: 1 |  | ||||||
|       required: |       required: | ||||||
|       - consumer_key |       - consumer_key | ||||||
|       - consumer_secret |       - consumer_secret | ||||||
| @ -24497,41 +24354,6 @@ components: | |||||||
|       required: |       required: | ||||||
|       - pagination |       - pagination | ||||||
|       - results |       - results | ||||||
|     PaginatedIPReputationList: |  | ||||||
|       type: object |  | ||||||
|       properties: |  | ||||||
|         pagination: |  | ||||||
|           type: object |  | ||||||
|           properties: |  | ||||||
|             next: |  | ||||||
|               type: number |  | ||||||
|             previous: |  | ||||||
|               type: number |  | ||||||
|             count: |  | ||||||
|               type: number |  | ||||||
|             current: |  | ||||||
|               type: number |  | ||||||
|             total_pages: |  | ||||||
|               type: number |  | ||||||
|             start_index: |  | ||||||
|               type: number |  | ||||||
|             end_index: |  | ||||||
|               type: number |  | ||||||
|           required: |  | ||||||
|           - next |  | ||||||
|           - previous |  | ||||||
|           - count |  | ||||||
|           - current |  | ||||||
|           - total_pages |  | ||||||
|           - start_index |  | ||||||
|           - end_index |  | ||||||
|         results: |  | ||||||
|           type: array |  | ||||||
|           items: |  | ||||||
|             $ref: '#/components/schemas/IPReputation' |  | ||||||
|       required: |  | ||||||
|       - pagination |  | ||||||
|       - results |  | ||||||
|     PaginatedIdentificationStageList: |     PaginatedIdentificationStageList: | ||||||
|       type: object |       type: object | ||||||
|       properties: |       properties: | ||||||
| @ -25547,6 +25369,41 @@ components: | |||||||
|       required: |       required: | ||||||
|       - pagination |       - pagination | ||||||
|       - results |       - results | ||||||
|  |     PaginatedReputationList: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         pagination: | ||||||
|  |           type: object | ||||||
|  |           properties: | ||||||
|  |             next: | ||||||
|  |               type: number | ||||||
|  |             previous: | ||||||
|  |               type: number | ||||||
|  |             count: | ||||||
|  |               type: number | ||||||
|  |             current: | ||||||
|  |               type: number | ||||||
|  |             total_pages: | ||||||
|  |               type: number | ||||||
|  |             start_index: | ||||||
|  |               type: number | ||||||
|  |             end_index: | ||||||
|  |               type: number | ||||||
|  |           required: | ||||||
|  |           - next | ||||||
|  |           - previous | ||||||
|  |           - count | ||||||
|  |           - current | ||||||
|  |           - total_pages | ||||||
|  |           - start_index | ||||||
|  |           - end_index | ||||||
|  |         results: | ||||||
|  |           type: array | ||||||
|  |           items: | ||||||
|  |             $ref: '#/components/schemas/Reputation' | ||||||
|  |       required: | ||||||
|  |       - pagination | ||||||
|  |       - results | ||||||
|     PaginatedReputationPolicyList: |     PaginatedReputationPolicyList: | ||||||
|       type: object |       type: object | ||||||
|       properties: |       properties: | ||||||
| @ -26212,41 +26069,6 @@ components: | |||||||
|       required: |       required: | ||||||
|       - pagination |       - pagination | ||||||
|       - results |       - results | ||||||
|     PaginatedUserReputationList: |  | ||||||
|       type: object |  | ||||||
|       properties: |  | ||||||
|         pagination: |  | ||||||
|           type: object |  | ||||||
|           properties: |  | ||||||
|             next: |  | ||||||
|               type: number |  | ||||||
|             previous: |  | ||||||
|               type: number |  | ||||||
|             count: |  | ||||||
|               type: number |  | ||||||
|             current: |  | ||||||
|               type: number |  | ||||||
|             total_pages: |  | ||||||
|               type: number |  | ||||||
|             start_index: |  | ||||||
|               type: number |  | ||||||
|             end_index: |  | ||||||
|               type: number |  | ||||||
|           required: |  | ||||||
|           - next |  | ||||||
|           - previous |  | ||||||
|           - count |  | ||||||
|           - current |  | ||||||
|           - total_pages |  | ||||||
|           - start_index |  | ||||||
|           - end_index |  | ||||||
|         results: |  | ||||||
|           type: array |  | ||||||
|           items: |  | ||||||
|             $ref: '#/components/schemas/UserReputation' |  | ||||||
|       required: |  | ||||||
|       - pagination |  | ||||||
|       - results |  | ||||||
|     PaginatedUserSourceConnectionList: |     PaginatedUserSourceConnectionList: | ||||||
|       type: object |       type: object | ||||||
|       properties: |       properties: | ||||||
| @ -27656,7 +27478,6 @@ components: | |||||||
|           minLength: 1 |           minLength: 1 | ||||||
|         additional_scopes: |         additional_scopes: | ||||||
|           type: string |           type: string | ||||||
|           minLength: 1 |  | ||||||
|     PatchedOutpostRequest: |     PatchedOutpostRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: Outpost Serializer |       description: Outpost Serializer | ||||||
| @ -29462,6 +29283,34 @@ components: | |||||||
|       - provider |       - provider | ||||||
|       - scope |       - scope | ||||||
|       - user |       - user | ||||||
|  |     Reputation: | ||||||
|  |       type: object | ||||||
|  |       description: Reputation Serializer | ||||||
|  |       properties: | ||||||
|  |         pk: | ||||||
|  |           type: string | ||||||
|  |           format: uuid | ||||||
|  |           title: Reputation uuid | ||||||
|  |         identifier: | ||||||
|  |           type: string | ||||||
|  |         ip: | ||||||
|  |           type: string | ||||||
|  |         ip_geo_data: | ||||||
|  |           type: object | ||||||
|  |           additionalProperties: {} | ||||||
|  |         score: | ||||||
|  |           type: integer | ||||||
|  |           maximum: 9223372036854775807 | ||||||
|  |           minimum: -9223372036854775808 | ||||||
|  |           format: int64 | ||||||
|  |         updated: | ||||||
|  |           type: string | ||||||
|  |           format: date-time | ||||||
|  |           readOnly: true | ||||||
|  |       required: | ||||||
|  |       - identifier | ||||||
|  |       - ip | ||||||
|  |       - updated | ||||||
|     ReputationPolicy: |     ReputationPolicy: | ||||||
|       type: object |       type: object | ||||||
|       description: Reputation Policy Serializer |       description: Reputation Policy Serializer | ||||||
| @ -31139,28 +30988,6 @@ components: | |||||||
|           minLength: 1 |           minLength: 1 | ||||||
|       required: |       required: | ||||||
|       - password |       - password | ||||||
|     UserReputation: |  | ||||||
|       type: object |  | ||||||
|       description: UserReputation Serializer |  | ||||||
|       properties: |  | ||||||
|         pk: |  | ||||||
|           type: integer |  | ||||||
|           readOnly: true |  | ||||||
|           title: ID |  | ||||||
|         username: |  | ||||||
|           type: string |  | ||||||
|         score: |  | ||||||
|           type: integer |  | ||||||
|           maximum: 2147483647 |  | ||||||
|           minimum: -2147483648 |  | ||||||
|         updated: |  | ||||||
|           type: string |  | ||||||
|           format: date-time |  | ||||||
|           readOnly: true |  | ||||||
|       required: |  | ||||||
|       - pk |  | ||||||
|       - updated |  | ||||||
|       - username |  | ||||||
|     UserRequest: |     UserRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: User Serializer |       description: User Serializer | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -45,6 +45,7 @@ | |||||||
|                 "chartjs-adapter-moment": "^1.0.0", |                 "chartjs-adapter-moment": "^1.0.0", | ||||||
|                 "codemirror": "^5.65.0", |                 "codemirror": "^5.65.0", | ||||||
|                 "construct-style-sheets-polyfill": "^3.0.5", |                 "construct-style-sheets-polyfill": "^3.0.5", | ||||||
|  |                 "country-flag-icons": "^1.4.19", | ||||||
|                 "eslint": "^8.6.0", |                 "eslint": "^8.6.0", | ||||||
|                 "eslint-config-google": "^0.14.0", |                 "eslint-config-google": "^0.14.0", | ||||||
|                 "eslint-plugin-custom-elements": "0.0.4", |                 "eslint-plugin-custom-elements": "0.0.4", | ||||||
| @ -3869,6 +3870,11 @@ | |||||||
|                 "node": ">=10" |                 "node": ">=10" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "node_modules/country-flag-icons": { | ||||||
|  |             "version": "1.4.19", | ||||||
|  |             "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.4.19.tgz", | ||||||
|  |             "integrity": "sha512-1hmXFJ4UURQt0Ex0990B7oOL4n9KLpT9NOSEmZoYh+/5DQ7/pikyqaptqCLUFFv/bYHyvYFeo0fqV82XxU6VOA==" | ||||||
|  |         }, | ||||||
|         "node_modules/create-require": { |         "node_modules/create-require": { | ||||||
|             "version": "1.1.1", |             "version": "1.1.1", | ||||||
|             "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", |             "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", | ||||||
| @ -11581,6 +11587,11 @@ | |||||||
|                 "yaml": "^1.10.0" |                 "yaml": "^1.10.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "country-flag-icons": { | ||||||
|  |             "version": "1.4.19", | ||||||
|  |             "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.4.19.tgz", | ||||||
|  |             "integrity": "sha512-1hmXFJ4UURQt0Ex0990B7oOL4n9KLpT9NOSEmZoYh+/5DQ7/pikyqaptqCLUFFv/bYHyvYFeo0fqV82XxU6VOA==" | ||||||
|  |         }, | ||||||
|         "create-require": { |         "create-require": { | ||||||
|             "version": "1.1.1", |             "version": "1.1.1", | ||||||
|             "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", |             "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", | ||||||
|  | |||||||
| @ -82,6 +82,7 @@ | |||||||
|         "chartjs-adapter-moment": "^1.0.0", |         "chartjs-adapter-moment": "^1.0.0", | ||||||
|         "codemirror": "^5.65.0", |         "codemirror": "^5.65.0", | ||||||
|         "construct-style-sheets-polyfill": "^3.0.5", |         "construct-style-sheets-polyfill": "^3.0.5", | ||||||
|  |         "country-flag-icons": "^1.4.19", | ||||||
|         "eslint": "^8.6.0", |         "eslint": "^8.6.0", | ||||||
|         "eslint-config-google": "^0.14.0", |         "eslint-config-google": "^0.14.0", | ||||||
|         "eslint-plugin-custom-elements": "0.0.4", |         "eslint-plugin-custom-elements": "0.0.4", | ||||||
|  | |||||||
| @ -242,11 +242,8 @@ export class AdminInterface extends LitElement { | |||||||
|                 <ak-sidebar-item path="/policy/policies"> |                 <ak-sidebar-item path="/policy/policies"> | ||||||
|                     <span slot="label">${t`Policies`}</span> |                     <span slot="label">${t`Policies`}</span> | ||||||
|                 </ak-sidebar-item> |                 </ak-sidebar-item> | ||||||
|                 <ak-sidebar-item path="/policy/reputation/ip"> |                 <ak-sidebar-item path="/policy/reputation"> | ||||||
|                     <span slot="label">${t`Reputation policy - IPs`}</span> |                     <span slot="label">${t`Reputation scores`}</span> | ||||||
|                 </ak-sidebar-item> |  | ||||||
|                 <ak-sidebar-item path="/policy/reputation/user"> |  | ||||||
|                     <span slot="label">${t`Reputation policy - Users`}</span> |  | ||||||
|                 </ak-sidebar-item> |                 </ak-sidebar-item> | ||||||
|                 <ak-sidebar-item path="/core/property-mappings"> |                 <ak-sidebar-item path="/core/property-mappings"> | ||||||
|                     <span slot="label">${t`Property Mappings`}</span> |                     <span slot="label">${t`Property Mappings`}</span> | ||||||
|  | |||||||
| @ -1370,8 +1370,7 @@ msgstr "Define how notifications are sent to users, like Email or Webhook." | |||||||
| #: src/pages/outposts/ServiceConnectionListPage.ts | #: src/pages/outposts/ServiceConnectionListPage.ts | ||||||
| #: src/pages/policies/BoundPoliciesList.ts | #: src/pages/policies/BoundPoliciesList.ts | ||||||
| #: src/pages/policies/PolicyListPage.ts | #: src/pages/policies/PolicyListPage.ts | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| #: src/pages/property-mappings/PropertyMappingListPage.ts | #: src/pages/property-mappings/PropertyMappingListPage.ts | ||||||
| #: src/pages/providers/ProviderListPage.ts | #: src/pages/providers/ProviderListPage.ts | ||||||
| #: src/pages/sources/SourcesListPage.ts | #: src/pages/sources/SourcesListPage.ts | ||||||
| @ -2331,14 +2330,14 @@ msgstr "ID" | |||||||
| msgid "ID Token" | msgid "ID Token" | ||||||
| msgstr "ID Token" | msgstr "ID Token" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| msgid "IP" | msgid "IP" | ||||||
| msgstr "IP" | msgstr "IP" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| msgid "IP Reputation" | #~ msgid "IP Reputation" | ||||||
| msgstr "IP Reputation" | #~ msgstr "IP Reputation" | ||||||
|  |  | ||||||
| #: src/pages/applications/ApplicationForm.ts | #: src/pages/applications/ApplicationForm.ts | ||||||
| #: src/pages/applications/ApplicationForm.ts | #: src/pages/applications/ApplicationForm.ts | ||||||
| @ -2354,6 +2353,7 @@ msgid "Icon shown in the browser tab." | |||||||
| msgstr "Icon shown in the browser tab." | msgstr "Icon shown in the browser tab." | ||||||
|  |  | ||||||
| #: src/pages/flows/FlowListPage.ts | #: src/pages/flows/FlowListPage.ts | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/system-tasks/SystemTaskListPage.ts | #: src/pages/system-tasks/SystemTaskListPage.ts | ||||||
| #: src/pages/tokens/TokenForm.ts | #: src/pages/tokens/TokenForm.ts | ||||||
| #: src/pages/tokens/TokenListPage.ts | #: src/pages/tokens/TokenListPage.ts | ||||||
| @ -3869,21 +3869,34 @@ msgstr "Related objects" | |||||||
| msgid "Remove the user from the current session." | msgid "Remove the user from the current session." | ||||||
| msgstr "Remove the user from the current session." | msgstr "Remove the user from the current session." | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation" | ||||||
|  | msgstr "Reputation" | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login." | ||||||
|  | msgstr "Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login." | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | #~ msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | ||||||
| msgstr "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | #~ msgstr "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | #~ msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | ||||||
| msgstr "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | #~ msgstr "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | ||||||
|  |  | ||||||
| #: src/interfaces/AdminInterface.ts | #: src/interfaces/AdminInterface.ts | ||||||
| msgid "Reputation policy - IPs" | #~ msgid "Reputation policy - IPs" | ||||||
| msgstr "Reputation policy - IPs" | #~ msgstr "Reputation policy - IPs" | ||||||
|  |  | ||||||
| #: src/interfaces/AdminInterface.ts | #: src/interfaces/AdminInterface.ts | ||||||
| msgid "Reputation policy - Users" | #~ msgid "Reputation policy - Users" | ||||||
| msgstr "Reputation policy - Users" | #~ msgstr "Reputation policy - Users" | ||||||
|  |  | ||||||
|  | #: src/interfaces/AdminInterface.ts | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation scores" | ||||||
|  | msgstr "Reputation scores" | ||||||
|  |  | ||||||
| #: src/pages/events/EventInfo.ts | #: src/pages/events/EventInfo.ts | ||||||
| #: src/pages/events/EventInfo.ts | #: src/pages/events/EventInfo.ts | ||||||
| @ -4048,8 +4061,7 @@ msgstr "Scope which the client can specify to access these properties." | |||||||
| msgid "Scopes" | msgid "Scopes" | ||||||
| msgstr "Scopes" | msgstr "Scopes" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| msgid "Score" | msgid "Score" | ||||||
| msgstr "Score" | msgstr "Score" | ||||||
|  |  | ||||||
| @ -5559,6 +5571,10 @@ msgstr "Update password" | |||||||
| msgid "Update {0}" | msgid "Update {0}" | ||||||
| msgstr "Update {0}" | msgstr "Update {0}" | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Updated" | ||||||
|  | msgstr "Updated" | ||||||
|  |  | ||||||
| #: src/pages/providers/proxy/ProxyProviderForm.ts | #: src/pages/providers/proxy/ProxyProviderForm.ts | ||||||
| msgid "Upstream host that the requests are forwarded to." | msgid "Upstream host that the requests are forwarded to." | ||||||
| msgstr "Upstream host that the requests are forwarded to." | msgstr "Upstream host that the requests are forwarded to." | ||||||
| @ -5640,8 +5656,8 @@ msgstr "User Property Mappings" | |||||||
|  |  | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| msgid "User Reputation" | #~ msgid "User Reputation" | ||||||
| msgstr "User Reputation" | #~ msgstr "User Reputation" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| #:  | #:  | ||||||
| @ -5752,7 +5768,6 @@ msgid "Userinfo URL" | |||||||
| msgstr "Userinfo URL" | msgstr "Userinfo URL" | ||||||
|  |  | ||||||
| #: src/flows/stages/identification/IdentificationStage.ts | #: src/flows/stages/identification/IdentificationStage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| #: src/pages/stages/identification/IdentificationStageForm.ts | #: src/pages/stages/identification/IdentificationStageForm.ts | ||||||
| #: src/pages/users/ServiceAccountForm.ts | #: src/pages/users/ServiceAccountForm.ts | ||||||
| #: src/pages/users/ServiceAccountForm.ts | #: src/pages/users/ServiceAccountForm.ts | ||||||
|  | |||||||
| @ -1369,8 +1369,7 @@ msgstr "Définit les méthodes d'envoi des notifications aux utilisateurs, telle | |||||||
| #: src/pages/outposts/ServiceConnectionListPage.ts | #: src/pages/outposts/ServiceConnectionListPage.ts | ||||||
| #: src/pages/policies/BoundPoliciesList.ts | #: src/pages/policies/BoundPoliciesList.ts | ||||||
| #: src/pages/policies/PolicyListPage.ts | #: src/pages/policies/PolicyListPage.ts | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| #: src/pages/property-mappings/PropertyMappingListPage.ts | #: src/pages/property-mappings/PropertyMappingListPage.ts | ||||||
| #: src/pages/providers/ProviderListPage.ts | #: src/pages/providers/ProviderListPage.ts | ||||||
| #: src/pages/sources/SourcesListPage.ts | #: src/pages/sources/SourcesListPage.ts | ||||||
| @ -2315,14 +2314,14 @@ msgstr "ID" | |||||||
| msgid "ID Token" | msgid "ID Token" | ||||||
| msgstr "ID du jeton" | msgstr "ID du jeton" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| msgid "IP" | msgid "IP" | ||||||
| msgstr "IP" | msgstr "IP" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| msgid "IP Reputation" | #~ msgid "IP Reputation" | ||||||
| msgstr "Réputation IP" | #~ msgstr "Réputation IP" | ||||||
|  |  | ||||||
| #: src/pages/applications/ApplicationForm.ts | #: src/pages/applications/ApplicationForm.ts | ||||||
| #: src/pages/applications/ApplicationForm.ts | #: src/pages/applications/ApplicationForm.ts | ||||||
| @ -2338,6 +2337,7 @@ msgid "Icon shown in the browser tab." | |||||||
| msgstr "Icône affichée dans l'onglet du navigateur." | msgstr "Icône affichée dans l'onglet du navigateur." | ||||||
|  |  | ||||||
| #: src/pages/flows/FlowListPage.ts | #: src/pages/flows/FlowListPage.ts | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/system-tasks/SystemTaskListPage.ts | #: src/pages/system-tasks/SystemTaskListPage.ts | ||||||
| #: src/pages/tokens/TokenForm.ts | #: src/pages/tokens/TokenForm.ts | ||||||
| #: src/pages/tokens/TokenListPage.ts | #: src/pages/tokens/TokenListPage.ts | ||||||
| @ -3841,21 +3841,34 @@ msgstr "" | |||||||
| msgid "Remove the user from the current session." | msgid "Remove the user from the current session." | ||||||
| msgstr "Supprimer l'utilisateur de la session actuelle." | msgstr "Supprimer l'utilisateur de la session actuelle." | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | #~ msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | ||||||
| msgstr "Réputation pour les IPs. Les notes sont réduites à chaque échec de connexion, et augmentées à chaque connexion réussie." | #~ msgstr "Réputation pour les IPs. Les notes sont réduites à chaque échec de connexion, et augmentées à chaque connexion réussie." | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | #~ msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | ||||||
| msgstr "Réputation pour les noms d'utilisateur. Les notes sont réduites à chaque échec de connexion, et augmentées à chaque connexion réussie." | #~ msgstr "Réputation pour les noms d'utilisateur. Les notes sont réduites à chaque échec de connexion, et augmentées à chaque connexion réussie." | ||||||
|  |  | ||||||
| #: src/interfaces/AdminInterface.ts | #: src/interfaces/AdminInterface.ts | ||||||
| msgid "Reputation policy - IPs" | #~ msgid "Reputation policy - IPs" | ||||||
| msgstr "Politique de réputation - IPs" | #~ msgstr "Politique de réputation - IPs" | ||||||
|  |  | ||||||
| #: src/interfaces/AdminInterface.ts | #: src/interfaces/AdminInterface.ts | ||||||
| msgid "Reputation policy - Users" | #~ msgid "Reputation policy - Users" | ||||||
| msgstr "Politique de réputation - Utilisateurs" | #~ msgstr "Politique de réputation - Utilisateurs" | ||||||
|  |  | ||||||
|  | #: src/interfaces/AdminInterface.ts | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation scores" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/events/EventInfo.ts | #: src/pages/events/EventInfo.ts | ||||||
| #: src/pages/events/EventInfo.ts | #: src/pages/events/EventInfo.ts | ||||||
| @ -4019,8 +4032,7 @@ msgstr "Portée que le client peut spécifier pour accéder à ces propriétés. | |||||||
| msgid "Scopes" | msgid "Scopes" | ||||||
| msgstr "Portées" | msgstr "Portées" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| msgid "Score" | msgid "Score" | ||||||
| msgstr "Note" | msgstr "Note" | ||||||
|  |  | ||||||
| @ -5500,6 +5512,10 @@ msgstr "" | |||||||
| msgid "Update {0}" | msgid "Update {0}" | ||||||
| msgstr "Mettre à jour {0}" | msgstr "Mettre à jour {0}" | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Updated" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/providers/proxy/ProxyProviderForm.ts | #: src/pages/providers/proxy/ProxyProviderForm.ts | ||||||
| msgid "Upstream host that the requests are forwarded to." | msgid "Upstream host that the requests are forwarded to." | ||||||
| msgstr "Hôte amont où transférer les requêtes." | msgstr "Hôte amont où transférer les requêtes." | ||||||
| @ -5581,8 +5597,8 @@ msgstr "Mapping des propriétés d'utilisateur" | |||||||
|  |  | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| msgid "User Reputation" | #~ msgid "User Reputation" | ||||||
| msgstr "Réputation utilisateur" | #~ msgstr "Réputation utilisateur" | ||||||
|  |  | ||||||
| #~ msgid "User Settings" | #~ msgid "User Settings" | ||||||
| #~ msgstr "Paramètres utilisateur" | #~ msgstr "Paramètres utilisateur" | ||||||
| @ -5691,7 +5707,6 @@ msgid "Userinfo URL" | |||||||
| msgstr "URL Userinfo" | msgstr "URL Userinfo" | ||||||
|  |  | ||||||
| #: src/flows/stages/identification/IdentificationStage.ts | #: src/flows/stages/identification/IdentificationStage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| #: src/pages/stages/identification/IdentificationStageForm.ts | #: src/pages/stages/identification/IdentificationStageForm.ts | ||||||
| #: src/pages/users/ServiceAccountForm.ts | #: src/pages/users/ServiceAccountForm.ts | ||||||
| #: src/pages/users/ServiceAccountForm.ts | #: src/pages/users/ServiceAccountForm.ts | ||||||
|  | |||||||
| @ -1364,8 +1364,7 @@ msgstr "" | |||||||
| #: src/pages/outposts/ServiceConnectionListPage.ts | #: src/pages/outposts/ServiceConnectionListPage.ts | ||||||
| #: src/pages/policies/BoundPoliciesList.ts | #: src/pages/policies/BoundPoliciesList.ts | ||||||
| #: src/pages/policies/PolicyListPage.ts | #: src/pages/policies/PolicyListPage.ts | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| #: src/pages/property-mappings/PropertyMappingListPage.ts | #: src/pages/property-mappings/PropertyMappingListPage.ts | ||||||
| #: src/pages/providers/ProviderListPage.ts | #: src/pages/providers/ProviderListPage.ts | ||||||
| #: src/pages/sources/SourcesListPage.ts | #: src/pages/sources/SourcesListPage.ts | ||||||
| @ -2323,14 +2322,14 @@ msgstr "" | |||||||
| msgid "ID Token" | msgid "ID Token" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| msgid "IP" | msgid "IP" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
| msgid "IP Reputation" | #~ msgid "IP Reputation" | ||||||
| msgstr "" | #~ msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/applications/ApplicationForm.ts | #: src/pages/applications/ApplicationForm.ts | ||||||
| #: src/pages/applications/ApplicationForm.ts | #: src/pages/applications/ApplicationForm.ts | ||||||
| @ -2346,6 +2345,7 @@ msgid "Icon shown in the browser tab." | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/flows/FlowListPage.ts | #: src/pages/flows/FlowListPage.ts | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/system-tasks/SystemTaskListPage.ts | #: src/pages/system-tasks/SystemTaskListPage.ts | ||||||
| #: src/pages/tokens/TokenForm.ts | #: src/pages/tokens/TokenForm.ts | ||||||
| #: src/pages/tokens/TokenListPage.ts | #: src/pages/tokens/TokenListPage.ts | ||||||
| @ -3859,20 +3859,33 @@ msgstr "" | |||||||
| msgid "Remove the user from the current session." | msgid "Remove the user from the current session." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | msgid "Reputation" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/IPReputationListPage.ts | ||||||
|  | #~ msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | ||||||
|  | #~ msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | #~ msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | ||||||
| msgstr "" | #~ msgstr "" | ||||||
|  |  | ||||||
| #: src/interfaces/AdminInterface.ts | #: src/interfaces/AdminInterface.ts | ||||||
| msgid "Reputation policy - IPs" | #~ msgid "Reputation policy - IPs" | ||||||
| msgstr "" | #~ msgstr "" | ||||||
|  |  | ||||||
| #: src/interfaces/AdminInterface.ts | #: src/interfaces/AdminInterface.ts | ||||||
| msgid "Reputation policy - Users" | #~ msgid "Reputation policy - Users" | ||||||
|  | #~ msgstr "" | ||||||
|  |  | ||||||
|  | #: src/interfaces/AdminInterface.ts | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Reputation scores" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/events/EventInfo.ts | #: src/pages/events/EventInfo.ts | ||||||
| @ -4038,8 +4051,7 @@ msgstr "" | |||||||
| msgid "Scopes" | msgid "Scopes" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| msgid "Score" | msgid "Score" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @ -5539,6 +5551,10 @@ msgstr "" | |||||||
| msgid "Update {0}" | msgid "Update {0}" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: src/pages/policies/reputation/ReputationListPage.ts | ||||||
|  | msgid "Updated" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: src/pages/providers/proxy/ProxyProviderForm.ts | #: src/pages/providers/proxy/ProxyProviderForm.ts | ||||||
| msgid "Upstream host that the requests are forwarded to." | msgid "Upstream host that the requests are forwarded to." | ||||||
| msgstr "" | msgstr "" | ||||||
| @ -5620,8 +5636,8 @@ msgstr "" | |||||||
|  |  | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | #: src/pages/policies/reputation/UserReputationListPage.ts | ||||||
| msgid "User Reputation" | #~ msgid "User Reputation" | ||||||
| msgstr "" | #~ msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| #:  | #:  | ||||||
| @ -5732,7 +5748,6 @@ msgid "Userinfo URL" | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: src/flows/stages/identification/IdentificationStage.ts | #: src/flows/stages/identification/IdentificationStage.ts | ||||||
| #: src/pages/policies/reputation/UserReputationListPage.ts |  | ||||||
| #: src/pages/stages/identification/IdentificationStageForm.ts | #: src/pages/stages/identification/IdentificationStageForm.ts | ||||||
| #: src/pages/users/ServiceAccountForm.ts | #: src/pages/users/ServiceAccountForm.ts | ||||||
| #: src/pages/users/ServiceAccountForm.ts | #: src/pages/users/ServiceAccountForm.ts | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,76 +0,0 @@ | |||||||
| import { t } from "@lingui/macro"; |  | ||||||
|  |  | ||||||
| import { TemplateResult, html } from "lit"; |  | ||||||
| import { customElement, property } from "lit/decorators.js"; |  | ||||||
|  |  | ||||||
| import { IPReputation, PoliciesApi } from "@goauthentik/api"; |  | ||||||
|  |  | ||||||
| import { AKResponse } from "../../../api/Client"; |  | ||||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; |  | ||||||
| import { uiConfig } from "../../../common/config"; |  | ||||||
| import "../../../elements/buttons/ModalButton"; |  | ||||||
| import "../../../elements/buttons/SpinnerButton"; |  | ||||||
| import "../../../elements/forms/DeleteBulkForm"; |  | ||||||
| import "../../../elements/forms/ModalForm"; |  | ||||||
| import { TableColumn } from "../../../elements/table/Table"; |  | ||||||
| import { TablePage } from "../../../elements/table/TablePage"; |  | ||||||
|  |  | ||||||
| @customElement("ak-policy-reputation-ip-list") |  | ||||||
| export class IPReputationListPage extends TablePage<IPReputation> { |  | ||||||
|     searchEnabled(): boolean { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|     pageTitle(): string { |  | ||||||
|         return t`IP Reputation`; |  | ||||||
|     } |  | ||||||
|     pageDescription(): string { |  | ||||||
|         return t`Reputation for IPs. Scores are decreased for each failed login and increased for each successful login.`; |  | ||||||
|     } |  | ||||||
|     pageIcon(): string { |  | ||||||
|         return "fa fa-ban"; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @property() |  | ||||||
|     order = "ip"; |  | ||||||
|  |  | ||||||
|     checkbox = true; |  | ||||||
|  |  | ||||||
|     async apiEndpoint(page: number): Promise<AKResponse<IPReputation>> { |  | ||||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsList({ |  | ||||||
|             ordering: this.order, |  | ||||||
|             page: page, |  | ||||||
|             pageSize: (await uiConfig()).pagination.perPage, |  | ||||||
|             search: this.search || "", |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     columns(): TableColumn[] { |  | ||||||
|         return [new TableColumn(t`IP`, "ip"), new TableColumn(t`Score`, "score")]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     renderToolbarSelected(): TemplateResult { |  | ||||||
|         const disabled = this.selectedElements.length < 1; |  | ||||||
|         return html`<ak-forms-delete-bulk |  | ||||||
|             objectLabel=${t`IP Reputation`} |  | ||||||
|             .objects=${this.selectedElements} |  | ||||||
|             .usedBy=${(item: IPReputation) => { |  | ||||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsUsedByList({ |  | ||||||
|                     id: item.pk, |  | ||||||
|                 }); |  | ||||||
|             }} |  | ||||||
|             .delete=${(item: IPReputation) => { |  | ||||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsDestroy({ |  | ||||||
|                     id: item.pk, |  | ||||||
|                 }); |  | ||||||
|             }} |  | ||||||
|         > |  | ||||||
|             <button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger"> |  | ||||||
|                 ${t`Delete`} |  | ||||||
|             </button> |  | ||||||
|         </ak-forms-delete-bulk>`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     row(item: IPReputation): TemplateResult[] { |  | ||||||
|         return [html`${item.ip}`, html`${item.score}`]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,9 +1,11 @@ | |||||||
|  | import getUnicodeFlagIcon from "country-flag-icons/unicode/index.js"; | ||||||
|  | 
 | ||||||
| import { t } from "@lingui/macro"; | import { t } from "@lingui/macro"; | ||||||
| 
 | 
 | ||||||
| import { TemplateResult, html } from "lit"; | import { TemplateResult, html } from "lit"; | ||||||
| import { customElement, property } from "lit/decorators.js"; | import { customElement, property } from "lit/decorators.js"; | ||||||
| 
 | 
 | ||||||
| import { PoliciesApi, UserReputation } from "@goauthentik/api"; | import { PoliciesApi, Reputation } from "@goauthentik/api"; | ||||||
| 
 | 
 | ||||||
| import { AKResponse } from "../../../api/Client"; | import { AKResponse } from "../../../api/Client"; | ||||||
| import { DEFAULT_CONFIG } from "../../../api/Config"; | import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||||
| @ -15,28 +17,28 @@ import "../../../elements/forms/ModalForm"; | |||||||
| import { TableColumn } from "../../../elements/table/Table"; | import { TableColumn } from "../../../elements/table/Table"; | ||||||
| import { TablePage } from "../../../elements/table/TablePage"; | import { TablePage } from "../../../elements/table/TablePage"; | ||||||
| 
 | 
 | ||||||
| @customElement("ak-policy-reputation-user-list") | @customElement("ak-policy-reputation-list") | ||||||
| export class UserReputationListPage extends TablePage<UserReputation> { | export class ReputationListPage extends TablePage<Reputation> { | ||||||
|     searchEnabled(): boolean { |     searchEnabled(): boolean { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|     pageTitle(): string { |     pageTitle(): string { | ||||||
|         return t`User Reputation`; |         return t`Reputation scores`; | ||||||
|     } |     } | ||||||
|     pageDescription(): string { |     pageDescription(): string { | ||||||
|         return t`Reputation for usernames. Scores are decreased for each failed login and increased for each successful login.`; |         return t`Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login.`; | ||||||
|     } |     } | ||||||
|     pageIcon(): string { |     pageIcon(): string { | ||||||
|         return "fa fa-ban"; |         return "fa fa-ban"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @property() | ||||||
|  |     order = "identifier"; | ||||||
|  | 
 | ||||||
|     checkbox = true; |     checkbox = true; | ||||||
| 
 | 
 | ||||||
|     @property() |     async apiEndpoint(page: number): Promise<AKResponse<Reputation>> { | ||||||
|     order = "username"; |         return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresList({ | ||||||
| 
 |  | ||||||
|     async apiEndpoint(page: number): Promise<AKResponse<UserReputation>> { |  | ||||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersList({ |  | ||||||
|             ordering: this.order, |             ordering: this.order, | ||||||
|             page: page, |             page: page, | ||||||
|             pageSize: (await uiConfig()).pagination.perPage, |             pageSize: (await uiConfig()).pagination.perPage, | ||||||
| @ -45,22 +47,27 @@ export class UserReputationListPage extends TablePage<UserReputation> { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     columns(): TableColumn[] { |     columns(): TableColumn[] { | ||||||
|         return [new TableColumn(t`Username`, "username"), new TableColumn(t`Score`, "score")]; |         return [ | ||||||
|  |             new TableColumn(t`Identifier`, "identifier"), | ||||||
|  |             new TableColumn(t`IP`, "ip"), | ||||||
|  |             new TableColumn(t`Score`, "score"), | ||||||
|  |             new TableColumn(t`Updated`, "updated"), | ||||||
|  |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     renderToolbarSelected(): TemplateResult { |     renderToolbarSelected(): TemplateResult { | ||||||
|         const disabled = this.selectedElements.length < 1; |         const disabled = this.selectedElements.length < 1; | ||||||
|         return html`<ak-forms-delete-bulk
 |         return html`<ak-forms-delete-bulk
 | ||||||
|             objectLabel=${t`User Reputation`} |             objectLabel=${t`Reputation`} | ||||||
|             .objects=${this.selectedElements} |             .objects=${this.selectedElements} | ||||||
|             .usedBy=${(item: UserReputation) => { |             .usedBy=${(item: Reputation) => { | ||||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersUsedByList({ |                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresUsedByList({ | ||||||
|                     id: item.pk, |                     reputationUuid: item.pk || "", | ||||||
|                 }); |                 }); | ||||||
|             }} |             }} | ||||||
|             .delete=${(item: UserReputation) => { |             .delete=${(item: Reputation) => { | ||||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersDestroy({ |                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresDestroy({ | ||||||
|                     id: item.pk, |                     reputationUuid: item.pk || "", | ||||||
|                 }); |                 }); | ||||||
|             }} |             }} | ||||||
|         > |         > | ||||||
| @ -70,7 +77,15 @@ export class UserReputationListPage extends TablePage<UserReputation> { | |||||||
|         </ak-forms-delete-bulk>`; |         </ak-forms-delete-bulk>`; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     row(item: UserReputation): TemplateResult[] { |     row(item: Reputation): TemplateResult[] { | ||||||
|         return [html`${item.username}`, html`${item.score}`]; |         return [ | ||||||
|  |             html`${item.identifier}`, | ||||||
|  |             html`${item.ipGeoData?.country | ||||||
|  |                 ? html` ${getUnicodeFlagIcon(item.ipGeoData.country)} ` | ||||||
|  |                 : html``} | ||||||
|  |             ${item.ip}`,
 | ||||||
|  |             html`${item.score}`, | ||||||
|  |             html`${item.updated.toLocaleString()}`, | ||||||
|  |         ]; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -16,8 +16,7 @@ import "./pages/groups/GroupListPage"; | |||||||
| import "./pages/outposts/OutpostListPage"; | import "./pages/outposts/OutpostListPage"; | ||||||
| import "./pages/outposts/ServiceConnectionListPage"; | import "./pages/outposts/ServiceConnectionListPage"; | ||||||
| import "./pages/policies/PolicyListPage"; | import "./pages/policies/PolicyListPage"; | ||||||
| import "./pages/policies/reputation/IPReputationListPage"; | import "./pages/policies/reputation/ReputationListPage"; | ||||||
| import "./pages/policies/reputation/UserReputationListPage"; |  | ||||||
| import "./pages/property-mappings/PropertyMappingListPage"; | import "./pages/property-mappings/PropertyMappingListPage"; | ||||||
| import "./pages/providers/ProviderListPage"; | import "./pages/providers/ProviderListPage"; | ||||||
| import "./pages/providers/ProviderViewPage"; | import "./pages/providers/ProviderViewPage"; | ||||||
| @ -72,12 +71,8 @@ export const ROUTES: Route[] = [ | |||||||
|     new Route(new RegExp("^/core/tenants$"), html`<ak-tenant-list></ak-tenant-list>`), |     new Route(new RegExp("^/core/tenants$"), html`<ak-tenant-list></ak-tenant-list>`), | ||||||
|     new Route(new RegExp("^/policy/policies$"), html`<ak-policy-list></ak-policy-list>`), |     new Route(new RegExp("^/policy/policies$"), html`<ak-policy-list></ak-policy-list>`), | ||||||
|     new Route( |     new Route( | ||||||
|         new RegExp("^/policy/reputation/ip$"), |         new RegExp("^/policy/reputation$"), | ||||||
|         html`<ak-policy-reputation-ip-list></ak-policy-reputation-ip-list>`, |         html`<ak-policy-reputation-list></ak-policy-reputation-list>`, | ||||||
|     ), |  | ||||||
|     new Route( |  | ||||||
|         new RegExp("^/policy/reputation/user$"), |  | ||||||
|         html`<ak-policy-reputation-user-list></ak-policy-reputation-user-list>`, |  | ||||||
|     ), |     ), | ||||||
|     new Route(new RegExp("^/identity/groups$"), html`<ak-group-list></ak-group-list>`), |     new Route(new RegExp("^/identity/groups$"), html`<ak-group-list></ak-group-list>`), | ||||||
|     new Route(new RegExp("^/identity/users$"), html`<ak-user-list></ak-user-list>`), |     new Route(new RegExp("^/identity/users$"), html`<ak-user-list></ak-user-list>`), | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer