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:
Jens Langhammer 2022-01-05 21:02:33 +01:00
parent e3be0f2550
commit 6e53f1689d
23 changed files with 1111 additions and 1783 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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"]

View File

@ -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")

View File

@ -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",
),
]

View File

@ -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,19 +37,20 @@ 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)
passing += passing or score <= self.threshold score = Reputation.objects.filter(query).annotate(total_score=Sum("score")).total_score
LOGGER.debug( passing += passing or score <= self.threshold
"Score for Username", LOGGER.debug(
username=request.user.username, "Score for user",
score=score, username=request.user.username,
passing=passing, remote_ip=remote_ip,
) score=score,
passing=passing,
)
return PolicyResult(bool(passing)) return PolicyResult(bool(passing))
class Meta: class Meta:
@ -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")

View File

@ -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"},
}, },

View File

@ -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)

View File

@ -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"],
)
rep.ip_geo_data = GEOIP_READER.city_dict(score["ip"]) or {}
rep.score = score["score"]
objects_to_update.append(rep) objects_to_update.append(rep)
IPReputation.objects.bulk_update(objects_to_update, ["score"]) Reputation.objects.bulk_update(objects_to_update, ["score", "ip_geo_data"])
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated IP Reputation"])) self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated 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"])
)

View File

@ -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"""

View File

@ -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()}",

View File

@ -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
View File

@ -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",

View File

@ -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",

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}`];
}
}

View File

@ -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()}`,
];
} }
} }

View File

@ -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>`),