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}),
)
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)

View File

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

View File

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

View File

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

View File

@ -13,3 +13,4 @@ class AuthentikPolicyReputationConfig(AppConfig):
def ready(self):
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"""
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,19 +37,20 @@ 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)
passing += passing or score <= self.threshold
LOGGER.debug(
"Score for Username",
username=request.user.username,
score=score,
passing=passing,
)
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 user",
username=request.user.username,
remote_ip=remote_ip,
score=score,
passing=passing,
)
return PolicyResult(bool(passing))
class Meta:
@ -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")

View File

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

View File

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

View File

@ -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
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)
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"])
)
Reputation.objects.bulk_update(objects_to_update, ["score", "ip_geo_data"])
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated Reputation"]))

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

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

View File

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

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

View File

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