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}), | ||||
|             ) | ||||
|             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 | ||||
|             task.delete() | ||||
|             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.hibp.api import HaveIBeenPwendPolicyViewSet | ||||
| from authentik.policies.password.api import PasswordPolicyViewSet | ||||
| from authentik.policies.reputation.api import ( | ||||
|     IPReputationViewSet, | ||||
|     ReputationPolicyViewSet, | ||||
|     UserReputationViewSet, | ||||
| ) | ||||
| from authentik.policies.reputation.api import ReputationPolicyViewSet, ReputationViewSet | ||||
| from authentik.providers.ldap.api import LDAPOutpostConfigViewSet, LDAPProviderViewSet | ||||
| from authentik.providers.oauth2.api.provider import OAuth2ProviderViewSet | ||||
| 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/password_expiry", PasswordExpiryPolicyViewSet) | ||||
| router.register("policies/password", PasswordPolicyViewSet) | ||||
| router.register("policies/reputation/users", UserReputationViewSet) | ||||
| router.register("policies/reputation/ips", IPReputationViewSet) | ||||
| router.register("policies/reputation/scores", ReputationViewSet) | ||||
| router.register("policies/reputation", ReputationPolicyViewSet) | ||||
|  | ||||
| router.register("providers/all", ProviderViewSet) | ||||
|  | ||||
| @ -35,12 +35,11 @@ class GeoIPReader: | ||||
|  | ||||
|     def __open(self): | ||||
|         """Get GeoIP Reader, if configured, otherwise none""" | ||||
|         path = CONFIG.y("authentik.geoip") | ||||
|         path = CONFIG.y("geoip") | ||||
|         if path == "" or not path: | ||||
|             return | ||||
|         try: | ||||
|             reader = Reader(path) | ||||
|             self.__reader = reader | ||||
|             self.__reader = Reader(path) | ||||
|             self.__last_mtime = stat(path).st_mtime | ||||
|             LOGGER.info("Loaded GeoIP database", last_write=self.__last_mtime) | ||||
|         except OSError as exc: | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| """Source API Views""" | ||||
| """Reputation policy API Views""" | ||||
| from rest_framework import mixins | ||||
| from rest_framework.serializers import ModelSerializer | ||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet | ||||
|  | ||||
| from authentik.core.api.used_by import UsedByMixin | ||||
| 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): | ||||
| @ -29,59 +29,32 @@ class ReputationPolicyViewSet(UsedByMixin, ModelViewSet): | ||||
|     ordering = ["name"] | ||||
|  | ||||
|  | ||||
| class IPReputationSerializer(ModelSerializer): | ||||
|     """IPReputation Serializer""" | ||||
| class ReputationSerializer(ModelSerializer): | ||||
|     """Reputation Serializer""" | ||||
|  | ||||
|     class Meta: | ||||
|         model = IPReputation | ||||
|         model = Reputation | ||||
|         fields = [ | ||||
|             "pk", | ||||
|             "identifier", | ||||
|             "ip", | ||||
|             "ip_geo_data", | ||||
|             "score", | ||||
|             "updated", | ||||
|         ] | ||||
|  | ||||
|  | ||||
| class IPReputationViewSet( | ||||
| class ReputationViewSet( | ||||
|     mixins.RetrieveModelMixin, | ||||
|     mixins.DestroyModelMixin, | ||||
|     UsedByMixin, | ||||
|     mixins.ListModelMixin, | ||||
|     GenericViewSet, | ||||
| ): | ||||
|     """IPReputation Viewset""" | ||||
|     """Reputation Viewset""" | ||||
|  | ||||
|     queryset = IPReputation.objects.all() | ||||
|     serializer_class = IPReputationSerializer | ||||
|     search_fields = ["ip", "score"] | ||||
|     filterset_fields = ["ip", "score"] | ||||
|     queryset = Reputation.objects.all() | ||||
|     serializer_class = ReputationSerializer | ||||
|     search_fields = ["identifier", "ip", "score"] | ||||
|     filterset_fields = ["identifier", "ip", "score"] | ||||
|     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): | ||||
|         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""" | ||||
| from django.core.cache import cache | ||||
| from uuid import uuid4 | ||||
|  | ||||
| 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 rest_framework.serializers import BaseSerializer | ||||
| from structlog import get_logger | ||||
|  | ||||
| from authentik.lib.models import SerializerModel | ||||
| from authentik.lib.utils.http import get_client_ip | ||||
| from authentik.policies.models import Policy | ||||
| from authentik.policies.types import PolicyRequest, PolicyResult | ||||
|  | ||||
| LOGGER = get_logger() | ||||
| CACHE_KEY_IP_PREFIX = "authentik_reputation_ip_" | ||||
| CACHE_KEY_USER_PREFIX = "authentik_reputation_user_" | ||||
| CACHE_KEY_PREFIX = "goauthentik.io/policies/reputation/scores/" | ||||
|  | ||||
|  | ||||
| class ReputationPolicy(Policy): | ||||
| @ -34,16 +37,17 @@ class ReputationPolicy(Policy): | ||||
|     def passes(self, request: PolicyRequest) -> PolicyResult: | ||||
|         remote_ip = get_client_ip(request.http_request) | ||||
|         passing = False | ||||
|         query = Q() | ||||
|         if self.check_ip: | ||||
|             score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0) | ||||
|             passing += passing or score <= self.threshold | ||||
|             LOGGER.debug("Score for IP", ip=remote_ip, score=score, passing=passing) | ||||
|             query |= Q(ip=remote_ip) | ||||
|         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 | ||||
|         LOGGER.debug( | ||||
|                 "Score for Username", | ||||
|             "Score for user", | ||||
|             username=request.user.username, | ||||
|             remote_ip=remote_ip, | ||||
|             score=score, | ||||
|             passing=passing, | ||||
|         ) | ||||
| @ -55,23 +59,27 @@ class ReputationPolicy(Policy): | ||||
|         verbose_name_plural = _("Reputation Policies") | ||||
|  | ||||
|  | ||||
| class IPReputation(models.Model): | ||||
|     """Store score coming from the same IP""" | ||||
| class Reputation(SerializerModel): | ||||
|     """Reputation for user and or IP.""" | ||||
|  | ||||
|     ip = models.GenericIPAddressField(unique=True) | ||||
|     score = models.IntegerField(default=0) | ||||
|     updated = models.DateTimeField(auto_now=True) | ||||
|     reputation_uuid = models.UUIDField(primary_key=True, unique=True, default=uuid4) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return f"IPReputation for {self.ip} @ {self.score}" | ||||
|     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) | ||||
|  | ||||
| class UserReputation(models.Model): | ||||
|     """Store score attempting to log in as the same username""" | ||||
|     @property | ||||
|     def serializer(self) -> BaseSerializer: | ||||
|         from authentik.policies.reputation.api import ReputationSerializer | ||||
|  | ||||
|     username = models.TextField() | ||||
|     score = models.IntegerField(default=0) | ||||
|     updated = models.DateTimeField(auto_now=True) | ||||
|         return ReputationSerializer | ||||
|  | ||||
|     def __str__(self): | ||||
|         return f"UserReputation for {self.username} @ {self.score}" | ||||
|     def __str__(self) -> str: | ||||
|         return f"Reputation {self.identifier}/{self.ip} @ {self.score}" | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
|         unique_together = ("identifier", "ip") | ||||
|  | ||||
| @ -2,13 +2,8 @@ | ||||
| from celery.schedules import crontab | ||||
|  | ||||
| CELERY_BEAT_SCHEDULE = { | ||||
|     "policies_reputation_ip_save": { | ||||
|         "task": "authentik.policies.reputation.tasks.save_ip_reputation", | ||||
|         "schedule": crontab(minute="*/5"), | ||||
|         "options": {"queue": "authentik_scheduled"}, | ||||
|     }, | ||||
|     "policies_reputation_user_save": { | ||||
|         "task": "authentik.policies.reputation.tasks.save_user_reputation", | ||||
|     "policies_reputation_save": { | ||||
|         "task": "authentik.policies.reputation.tasks.save_reputation", | ||||
|         "schedule": crontab(minute="*/5"), | ||||
|         "options": {"queue": "authentik_scheduled"}, | ||||
|     }, | ||||
|  | ||||
| @ -7,28 +7,30 @@ from structlog.stdlib import get_logger | ||||
|  | ||||
| from authentik.lib.config import CONFIG | ||||
| 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 | ||||
|  | ||||
| LOGGER = get_logger() | ||||
| 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""" | ||||
|     remote_ip = get_client_ip(request) | ||||
|  | ||||
|     try: | ||||
|         # 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) | ||||
|         cache.incr(CACHE_KEY_IP_PREFIX + remote_ip, amount) | ||||
|  | ||||
|         cache.get_or_set(CACHE_KEY_USER_PREFIX + username, 0, CACHE_TIMEOUT) | ||||
|         cache.incr(CACHE_KEY_USER_PREFIX + username, amount) | ||||
|         score = cache.get_or_set( | ||||
|             CACHE_KEY_PREFIX + remote_ip + identifier, | ||||
|             {"ip": remote_ip, "identifier": identifier, "score": 0}, | ||||
|             CACHE_TIMEOUT, | ||||
|         ) | ||||
|         score["score"] += amount | ||||
|         cache.set(CACHE_KEY_PREFIX + remote_ip + identifier, score) | ||||
|     except ValueError as 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) | ||||
|  | ||||
| @ -2,14 +2,15 @@ | ||||
| from django.core.cache import cache | ||||
| from structlog.stdlib import get_logger | ||||
|  | ||||
| from authentik.events.geo import GEOIP_READER | ||||
| from authentik.events.monitored_tasks import ( | ||||
|     MonitoredTask, | ||||
|     TaskResult, | ||||
|     TaskResultStatus, | ||||
|     prefill_task, | ||||
| ) | ||||
| from authentik.policies.reputation.models import IPReputation, UserReputation | ||||
| from authentik.policies.reputation.signals import CACHE_KEY_IP_PREFIX, CACHE_KEY_USER_PREFIX | ||||
| from authentik.policies.reputation.models import Reputation | ||||
| from authentik.policies.reputation.signals import CACHE_KEY_PREFIX | ||||
| from authentik.root.celery import CELERY_APP | ||||
|  | ||||
| LOGGER = get_logger() | ||||
| @ -17,29 +18,16 @@ LOGGER = get_logger() | ||||
|  | ||||
| @CELERY_APP.task(bind=True, base=MonitoredTask) | ||||
| @prefill_task | ||||
| def save_ip_reputation(self: MonitoredTask): | ||||
| def save_reputation(self: MonitoredTask): | ||||
|     """Save currently cached reputation to database""" | ||||
|     objects_to_update = [] | ||||
|     for key, score in cache.get_many(cache.keys(CACHE_KEY_IP_PREFIX + "*")).items(): | ||||
|         remote_ip = key.replace(CACHE_KEY_IP_PREFIX, "") | ||||
|         rep, _ = IPReputation.objects.get_or_create(ip=remote_ip) | ||||
|         rep.score = score | ||||
|         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"]) | ||||
|     for _, score in cache.get_many(cache.keys(CACHE_KEY_PREFIX + "*")).items(): | ||||
|         rep, _ = Reputation.objects.get_or_create( | ||||
|             ip=score["ip"], | ||||
|             identifier=score["identifier"], | ||||
|         ) | ||||
|         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.lib.utils.http import DEFAULT_IP | ||||
| from authentik.policies.reputation.models import ( | ||||
|     CACHE_KEY_IP_PREFIX, | ||||
|     CACHE_KEY_USER_PREFIX, | ||||
|     IPReputation, | ||||
|     ReputationPolicy, | ||||
|     UserReputation, | ||||
| ) | ||||
| from authentik.policies.reputation.tasks import save_ip_reputation, save_user_reputation | ||||
| from authentik.policies.reputation.models import CACHE_KEY_PREFIX, Reputation, ReputationPolicy | ||||
| from authentik.policies.reputation.tasks import save_reputation | ||||
| from authentik.policies.types import PolicyRequest | ||||
|  | ||||
|  | ||||
| @ -24,9 +18,8 @@ class TestReputationPolicy(TestCase): | ||||
|         self.request = self.request_factory.get("/") | ||||
|         self.test_ip = "127.0.0.1" | ||||
|         self.test_username = "test" | ||||
|         cache.delete(CACHE_KEY_IP_PREFIX + self.test_ip) | ||||
|         cache.delete(CACHE_KEY_IP_PREFIX + DEFAULT_IP) | ||||
|         cache.delete(CACHE_KEY_USER_PREFIX + self.test_username) | ||||
|         keys = cache.keys(CACHE_KEY_PREFIX + "*") | ||||
|         cache.delete_many(keys) | ||||
|         # We need a user for the one-to-one in userreputation | ||||
|         self.user = User.objects.create(username=self.test_username) | ||||
|  | ||||
| @ -35,20 +28,26 @@ class TestReputationPolicy(TestCase): | ||||
|         # Trigger negative reputation | ||||
|         authenticate(self.request, username=self.test_username, password=self.test_username) | ||||
|         # 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_ip_reputation.delay().get() | ||||
|         self.assertEqual(IPReputation.objects.get(ip=self.test_ip).score, -1) | ||||
|         save_reputation.delay().get() | ||||
|         self.assertEqual(Reputation.objects.get(ip=self.test_ip).score, -1) | ||||
|  | ||||
|     def test_user_reputation(self): | ||||
|         """test User reputation""" | ||||
|         # Trigger negative reputation | ||||
|         authenticate(self.request, username=self.test_username, password=self.test_username) | ||||
|         # 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_user_reputation.delay().get() | ||||
|         self.assertEqual(UserReputation.objects.get(username=self.test_username).score, -1) | ||||
|         save_reputation.delay().get() | ||||
|         self.assertEqual(Reputation.objects.get(identifier=self.test_username).score, -1) | ||||
|  | ||||
|     def test_policy(self): | ||||
|         """Test Policy""" | ||||
|  | ||||
| @ -26,8 +26,8 @@ class PytestTestRunner:  # pragma: no cover | ||||
|  | ||||
|         settings.TEST = True | ||||
|         settings.CELERY_TASK_ALWAYS_EAGER = True | ||||
|         CONFIG.y_set("authentik.avatars", "none") | ||||
|         CONFIG.y_set("authentik.geoip", "tests/GeoLite2-City-Test.mmdb") | ||||
|         CONFIG.y_set("avatars", "none") | ||||
|         CONFIG.y_set("geoip", "tests/GeoLite2-City-Test.mmdb") | ||||
|         CONFIG.y_set( | ||||
|             "outposts.container_image_base", | ||||
|             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' | ||||
|         '403': | ||||
|           $ref: '#/components/schemas/GenericError' | ||||
|   /policies/reputation/ips/: | ||||
|   /policies/reputation/scores/: | ||||
|     get: | ||||
|       operationId: policies_reputation_ips_list | ||||
|       description: IPReputation Viewset | ||||
|       operationId: policies_reputation_scores_list | ||||
|       description: Reputation Viewset | ||||
|       parameters: | ||||
|       - in: query | ||||
|         name: identifier | ||||
|         schema: | ||||
|           type: string | ||||
|       - in: query | ||||
|         name: ip | ||||
|         schema: | ||||
| @ -9145,22 +9149,23 @@ paths: | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/PaginatedIPReputationList' | ||||
|                 $ref: '#/components/schemas/PaginatedReputationList' | ||||
|           description: '' | ||||
|         '400': | ||||
|           $ref: '#/components/schemas/ValidationError' | ||||
|         '403': | ||||
|           $ref: '#/components/schemas/GenericError' | ||||
|   /policies/reputation/ips/{id}/: | ||||
|   /policies/reputation/scores/{reputation_uuid}/: | ||||
|     get: | ||||
|       operationId: policies_reputation_ips_retrieve | ||||
|       description: IPReputation Viewset | ||||
|       operationId: policies_reputation_scores_retrieve | ||||
|       description: Reputation Viewset | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: id | ||||
|         name: reputation_uuid | ||||
|         schema: | ||||
|           type: integer | ||||
|         description: A unique integer value identifying this ip reputation. | ||||
|           type: string | ||||
|           format: uuid | ||||
|         description: A UUID string identifying this reputation. | ||||
|         required: true | ||||
|       tags: | ||||
|       - policies | ||||
| @ -9171,21 +9176,22 @@ paths: | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/IPReputation' | ||||
|                 $ref: '#/components/schemas/Reputation' | ||||
|           description: '' | ||||
|         '400': | ||||
|           $ref: '#/components/schemas/ValidationError' | ||||
|         '403': | ||||
|           $ref: '#/components/schemas/GenericError' | ||||
|     delete: | ||||
|       operationId: policies_reputation_ips_destroy | ||||
|       description: IPReputation Viewset | ||||
|       operationId: policies_reputation_scores_destroy | ||||
|       description: Reputation Viewset | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: id | ||||
|         name: reputation_uuid | ||||
|         schema: | ||||
|           type: integer | ||||
|         description: A unique integer value identifying this ip reputation. | ||||
|           type: string | ||||
|           format: uuid | ||||
|         description: A UUID string identifying this reputation. | ||||
|         required: true | ||||
|       tags: | ||||
|       - policies | ||||
| @ -9198,143 +9204,17 @@ paths: | ||||
|           $ref: '#/components/schemas/ValidationError' | ||||
|         '403': | ||||
|           $ref: '#/components/schemas/GenericError' | ||||
|   /policies/reputation/ips/{id}/used_by/: | ||||
|   /policies/reputation/scores/{reputation_uuid}/used_by/: | ||||
|     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 | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: id | ||||
|         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. | ||||
|         name: reputation_uuid | ||||
|         schema: | ||||
|           type: string | ||||
|       - name: page | ||||
|         required: false | ||||
|         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. | ||||
|           format: uuid | ||||
|         description: A UUID string identifying this reputation. | ||||
|         required: true | ||||
|       tags: | ||||
|       - policies | ||||
| @ -21938,28 +21818,6 @@ components: | ||||
|           type: integer | ||||
|           maximum: 2147483647 | ||||
|           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: | ||||
|       type: object | ||||
|       description: Identification challenges with all UI elements | ||||
| @ -23433,7 +23291,6 @@ components: | ||||
|           minLength: 1 | ||||
|         additional_scopes: | ||||
|           type: string | ||||
|           minLength: 1 | ||||
|       required: | ||||
|       - consumer_key | ||||
|       - consumer_secret | ||||
| @ -24497,41 +24354,6 @@ components: | ||||
|       required: | ||||
|       - pagination | ||||
|       - 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: | ||||
|       type: object | ||||
|       properties: | ||||
| @ -25547,6 +25369,41 @@ components: | ||||
|       required: | ||||
|       - pagination | ||||
|       - 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: | ||||
|       type: object | ||||
|       properties: | ||||
| @ -26212,41 +26069,6 @@ components: | ||||
|       required: | ||||
|       - pagination | ||||
|       - 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: | ||||
|       type: object | ||||
|       properties: | ||||
| @ -27656,7 +27478,6 @@ components: | ||||
|           minLength: 1 | ||||
|         additional_scopes: | ||||
|           type: string | ||||
|           minLength: 1 | ||||
|     PatchedOutpostRequest: | ||||
|       type: object | ||||
|       description: Outpost Serializer | ||||
| @ -29462,6 +29283,34 @@ components: | ||||
|       - provider | ||||
|       - scope | ||||
|       - 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: | ||||
|       type: object | ||||
|       description: Reputation Policy Serializer | ||||
| @ -31139,28 +30988,6 @@ components: | ||||
|           minLength: 1 | ||||
|       required: | ||||
|       - 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: | ||||
|       type: object | ||||
|       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", | ||||
|                 "codemirror": "^5.65.0", | ||||
|                 "construct-style-sheets-polyfill": "^3.0.5", | ||||
|                 "country-flag-icons": "^1.4.19", | ||||
|                 "eslint": "^8.6.0", | ||||
|                 "eslint-config-google": "^0.14.0", | ||||
|                 "eslint-plugin-custom-elements": "0.0.4", | ||||
| @ -3869,6 +3870,11 @@ | ||||
|                 "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": { | ||||
|             "version": "1.1.1", | ||||
|             "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", | ||||
| @ -11581,6 +11587,11 @@ | ||||
|                 "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": { | ||||
|             "version": "1.1.1", | ||||
|             "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", | ||||
|  | ||||
| @ -82,6 +82,7 @@ | ||||
|         "chartjs-adapter-moment": "^1.0.0", | ||||
|         "codemirror": "^5.65.0", | ||||
|         "construct-style-sheets-polyfill": "^3.0.5", | ||||
|         "country-flag-icons": "^1.4.19", | ||||
|         "eslint": "^8.6.0", | ||||
|         "eslint-config-google": "^0.14.0", | ||||
|         "eslint-plugin-custom-elements": "0.0.4", | ||||
|  | ||||
| @ -242,11 +242,8 @@ export class AdminInterface extends LitElement { | ||||
|                 <ak-sidebar-item path="/policy/policies"> | ||||
|                     <span slot="label">${t`Policies`}</span> | ||||
|                 </ak-sidebar-item> | ||||
|                 <ak-sidebar-item path="/policy/reputation/ip"> | ||||
|                     <span slot="label">${t`Reputation policy - IPs`}</span> | ||||
|                 </ak-sidebar-item> | ||||
|                 <ak-sidebar-item path="/policy/reputation/user"> | ||||
|                     <span slot="label">${t`Reputation policy - Users`}</span> | ||||
|                 <ak-sidebar-item path="/policy/reputation"> | ||||
|                     <span slot="label">${t`Reputation scores`}</span> | ||||
|                 </ak-sidebar-item> | ||||
|                 <ak-sidebar-item path="/core/property-mappings"> | ||||
|                     <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/policies/BoundPoliciesList.ts | ||||
| #: src/pages/policies/PolicyListPage.ts | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| #: src/pages/property-mappings/PropertyMappingListPage.ts | ||||
| #: src/pages/providers/ProviderListPage.ts | ||||
| #: src/pages/sources/SourcesListPage.ts | ||||
| @ -2331,14 +2330,14 @@ msgstr "ID" | ||||
| msgid "ID Token" | ||||
| msgstr "ID Token" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "IP" | ||||
| msgstr "IP" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| msgid "IP Reputation" | ||||
| msgstr "IP Reputation" | ||||
| #~ msgid "IP Reputation" | ||||
| #~ msgstr "IP Reputation" | ||||
|  | ||||
| #: 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." | ||||
|  | ||||
| #: src/pages/flows/FlowListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| #: src/pages/system-tasks/SystemTaskListPage.ts | ||||
| #: src/pages/tokens/TokenForm.ts | ||||
| #: src/pages/tokens/TokenListPage.ts | ||||
| @ -3869,21 +3869,34 @@ msgstr "Related objects" | ||||
| msgid "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 | ||||
| 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." | ||||
| #~ 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." | ||||
|  | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| 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." | ||||
| #~ 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." | ||||
|  | ||||
| #: src/interfaces/AdminInterface.ts | ||||
| msgid "Reputation policy - IPs" | ||||
| msgstr "Reputation policy - IPs" | ||||
| #~ msgid "Reputation policy - IPs" | ||||
| #~ msgstr "Reputation policy - IPs" | ||||
|  | ||||
| #: src/interfaces/AdminInterface.ts | ||||
| msgid "Reputation policy - Users" | ||||
| msgstr "Reputation policy - Users" | ||||
| #~ msgid "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 | ||||
| @ -4048,8 +4061,7 @@ msgstr "Scope which the client can specify to access these properties." | ||||
| msgid "Scopes" | ||||
| msgstr "Scopes" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "Score" | ||||
| msgstr "Score" | ||||
|  | ||||
| @ -5559,6 +5571,10 @@ msgstr "Update password" | ||||
| msgid "Update {0}" | ||||
| msgstr "Update {0}" | ||||
|  | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "Updated" | ||||
| msgstr "Updated" | ||||
|  | ||||
| #: src/pages/providers/proxy/ProxyProviderForm.ts | ||||
| msgid "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 | ||||
| msgid "User Reputation" | ||||
| msgstr "User Reputation" | ||||
| #~ msgid "User Reputation" | ||||
| #~ msgstr "User Reputation" | ||||
|  | ||||
| #:  | ||||
| #:  | ||||
| @ -5752,7 +5768,6 @@ msgid "Userinfo URL" | ||||
| msgstr "Userinfo URL" | ||||
|  | ||||
| #: src/flows/stages/identification/IdentificationStage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/stages/identification/IdentificationStageForm.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/policies/BoundPoliciesList.ts | ||||
| #: src/pages/policies/PolicyListPage.ts | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| #: src/pages/property-mappings/PropertyMappingListPage.ts | ||||
| #: src/pages/providers/ProviderListPage.ts | ||||
| #: src/pages/sources/SourcesListPage.ts | ||||
| @ -2315,14 +2314,14 @@ msgstr "ID" | ||||
| msgid "ID Token" | ||||
| msgstr "ID du jeton" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "IP" | ||||
| msgstr "IP" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| msgid "IP Reputation" | ||||
| msgstr "Réputation IP" | ||||
| #~ msgid "IP Reputation" | ||||
| #~ msgstr "Réputation IP" | ||||
|  | ||||
| #: 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." | ||||
|  | ||||
| #: src/pages/flows/FlowListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| #: src/pages/system-tasks/SystemTaskListPage.ts | ||||
| #: src/pages/tokens/TokenForm.ts | ||||
| #: src/pages/tokens/TokenListPage.ts | ||||
| @ -3841,21 +3841,34 @@ msgstr "" | ||||
| msgid "Remove the user from the current session." | ||||
| 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 | ||||
| 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." | ||||
| #~ 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." | ||||
|  | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| 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." | ||||
| #~ 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." | ||||
|  | ||||
| #: src/interfaces/AdminInterface.ts | ||||
| msgid "Reputation policy - IPs" | ||||
| msgstr "Politique de réputation - IPs" | ||||
| #~ msgid "Reputation policy - IPs" | ||||
| #~ msgstr "Politique de réputation - IPs" | ||||
|  | ||||
| #: src/interfaces/AdminInterface.ts | ||||
| msgid "Reputation policy - Users" | ||||
| msgstr "Politique de réputation - Utilisateurs" | ||||
| #~ msgid "Reputation policy - Users" | ||||
| #~ 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 | ||||
| @ -4019,8 +4032,7 @@ msgstr "Portée que le client peut spécifier pour accéder à ces propriétés. | ||||
| msgid "Scopes" | ||||
| msgstr "Portées" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "Score" | ||||
| msgstr "Note" | ||||
|  | ||||
| @ -5500,6 +5512,10 @@ msgstr "" | ||||
| msgid "Update {0}" | ||||
| msgstr "Mettre à jour {0}" | ||||
|  | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "Updated" | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/providers/proxy/ProxyProviderForm.ts | ||||
| msgid "Upstream host that the requests are forwarded to." | ||||
| 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 | ||||
| msgid "User Reputation" | ||||
| msgstr "Réputation utilisateur" | ||||
| #~ msgid "User Reputation" | ||||
| #~ msgstr "Réputation utilisateur" | ||||
|  | ||||
| #~ msgid "User Settings" | ||||
| #~ msgstr "Paramètres utilisateur" | ||||
| @ -5691,7 +5707,6 @@ msgid "Userinfo URL" | ||||
| msgstr "URL Userinfo" | ||||
|  | ||||
| #: src/flows/stages/identification/IdentificationStage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/stages/identification/IdentificationStageForm.ts | ||||
| #: src/pages/users/ServiceAccountForm.ts | ||||
| #: src/pages/users/ServiceAccountForm.ts | ||||
|  | ||||
| @ -1364,8 +1364,7 @@ msgstr "" | ||||
| #: src/pages/outposts/ServiceConnectionListPage.ts | ||||
| #: src/pages/policies/BoundPoliciesList.ts | ||||
| #: src/pages/policies/PolicyListPage.ts | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| #: src/pages/property-mappings/PropertyMappingListPage.ts | ||||
| #: src/pages/providers/ProviderListPage.ts | ||||
| #: src/pages/sources/SourcesListPage.ts | ||||
| @ -2323,14 +2322,14 @@ msgstr "" | ||||
| msgid "ID Token" | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "IP" | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| msgid "IP Reputation" | ||||
| msgstr "" | ||||
| #~ msgid "IP Reputation" | ||||
| #~ msgstr "" | ||||
|  | ||||
| #: src/pages/applications/ApplicationForm.ts | ||||
| #: src/pages/applications/ApplicationForm.ts | ||||
| @ -2346,6 +2345,7 @@ msgid "Icon shown in the browser tab." | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/flows/FlowListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| #: src/pages/system-tasks/SystemTaskListPage.ts | ||||
| #: src/pages/tokens/TokenForm.ts | ||||
| #: src/pages/tokens/TokenListPage.ts | ||||
| @ -3859,20 +3859,33 @@ msgstr "" | ||||
| msgid "Remove the user from the current session." | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | ||||
| #: 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 | ||||
| #~ msgid "Reputation for IPs. Scores are decreased for each failed login and increased for each successful login." | ||||
| #~ msgstr "" | ||||
|  | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | ||||
| msgstr "" | ||||
| #~ msgid "Reputation for usernames. Scores are decreased for each failed login and increased for each successful login." | ||||
| #~ msgstr "" | ||||
|  | ||||
| #: src/interfaces/AdminInterface.ts | ||||
| msgid "Reputation policy - IPs" | ||||
| msgstr "" | ||||
| #~ msgid "Reputation policy - IPs" | ||||
| #~ msgstr "" | ||||
|  | ||||
| #: 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 "" | ||||
|  | ||||
| #: src/pages/events/EventInfo.ts | ||||
| @ -4038,8 +4051,7 @@ msgstr "" | ||||
| msgid "Scopes" | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/policies/reputation/IPReputationListPage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "Score" | ||||
| msgstr "" | ||||
|  | ||||
| @ -5539,6 +5551,10 @@ msgstr "" | ||||
| msgid "Update {0}" | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/policies/reputation/ReputationListPage.ts | ||||
| msgid "Updated" | ||||
| msgstr "" | ||||
|  | ||||
| #: src/pages/providers/proxy/ProxyProviderForm.ts | ||||
| msgid "Upstream host that the requests are forwarded to." | ||||
| msgstr "" | ||||
| @ -5620,8 +5636,8 @@ msgstr "" | ||||
|  | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| msgid "User Reputation" | ||||
| msgstr "" | ||||
| #~ msgid "User Reputation" | ||||
| #~ msgstr "" | ||||
|  | ||||
| #:  | ||||
| #:  | ||||
| @ -5732,7 +5748,6 @@ msgid "Userinfo URL" | ||||
| msgstr "" | ||||
|  | ||||
| #: src/flows/stages/identification/IdentificationStage.ts | ||||
| #: src/pages/policies/reputation/UserReputationListPage.ts | ||||
| #: src/pages/stages/identification/IdentificationStageForm.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 { TemplateResult, html } from "lit"; | ||||
| 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 { DEFAULT_CONFIG } from "../../../api/Config"; | ||||
| @ -15,28 +17,28 @@ import "../../../elements/forms/ModalForm"; | ||||
| import { TableColumn } from "../../../elements/table/Table"; | ||||
| import { TablePage } from "../../../elements/table/TablePage"; | ||||
| 
 | ||||
| @customElement("ak-policy-reputation-user-list") | ||||
| export class UserReputationListPage extends TablePage<UserReputation> { | ||||
| @customElement("ak-policy-reputation-list") | ||||
| export class ReputationListPage extends TablePage<Reputation> { | ||||
|     searchEnabled(): boolean { | ||||
|         return true; | ||||
|     } | ||||
|     pageTitle(): string { | ||||
|         return t`User Reputation`; | ||||
|         return t`Reputation scores`; | ||||
|     } | ||||
|     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 { | ||||
|         return "fa fa-ban"; | ||||
|     } | ||||
| 
 | ||||
|     @property() | ||||
|     order = "identifier"; | ||||
| 
 | ||||
|     checkbox = true; | ||||
| 
 | ||||
|     @property() | ||||
|     order = "username"; | ||||
| 
 | ||||
|     async apiEndpoint(page: number): Promise<AKResponse<UserReputation>> { | ||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersList({ | ||||
|     async apiEndpoint(page: number): Promise<AKResponse<Reputation>> { | ||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresList({ | ||||
|             ordering: this.order, | ||||
|             page: page, | ||||
|             pageSize: (await uiConfig()).pagination.perPage, | ||||
| @ -45,22 +47,27 @@ export class UserReputationListPage extends TablePage<UserReputation> { | ||||
|     } | ||||
| 
 | ||||
|     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 { | ||||
|         const disabled = this.selectedElements.length < 1; | ||||
|         return html`<ak-forms-delete-bulk
 | ||||
|             objectLabel=${t`User Reputation`} | ||||
|             objectLabel=${t`Reputation`} | ||||
|             .objects=${this.selectedElements} | ||||
|             .usedBy=${(item: UserReputation) => { | ||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersUsedByList({ | ||||
|                     id: item.pk, | ||||
|             .usedBy=${(item: Reputation) => { | ||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresUsedByList({ | ||||
|                     reputationUuid: item.pk || "", | ||||
|                 }); | ||||
|             }} | ||||
|             .delete=${(item: UserReputation) => { | ||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersDestroy({ | ||||
|                     id: item.pk, | ||||
|             .delete=${(item: Reputation) => { | ||||
|                 return new PoliciesApi(DEFAULT_CONFIG).policiesReputationScoresDestroy({ | ||||
|                     reputationUuid: item.pk || "", | ||||
|                 }); | ||||
|             }} | ||||
|         > | ||||
| @ -70,7 +77,15 @@ export class UserReputationListPage extends TablePage<UserReputation> { | ||||
|         </ak-forms-delete-bulk>`; | ||||
|     } | ||||
| 
 | ||||
|     row(item: UserReputation): TemplateResult[] { | ||||
|         return [html`${item.username}`, html`${item.score}`]; | ||||
|     row(item: Reputation): TemplateResult[] { | ||||
|         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/ServiceConnectionListPage"; | ||||
| import "./pages/policies/PolicyListPage"; | ||||
| import "./pages/policies/reputation/IPReputationListPage"; | ||||
| import "./pages/policies/reputation/UserReputationListPage"; | ||||
| import "./pages/policies/reputation/ReputationListPage"; | ||||
| import "./pages/property-mappings/PropertyMappingListPage"; | ||||
| import "./pages/providers/ProviderListPage"; | ||||
| 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("^/policy/policies$"), html`<ak-policy-list></ak-policy-list>`), | ||||
|     new Route( | ||||
|         new RegExp("^/policy/reputation/ip$"), | ||||
|         html`<ak-policy-reputation-ip-list></ak-policy-reputation-ip-list>`, | ||||
|     ), | ||||
|     new Route( | ||||
|         new RegExp("^/policy/reputation/user$"), | ||||
|         html`<ak-policy-reputation-user-list></ak-policy-reputation-user-list>`, | ||||
|         new RegExp("^/policy/reputation$"), | ||||
|         html`<ak-policy-reputation-list></ak-policy-reputation-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>`), | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer