API: add endpoint to show by what objects an object is used (#995)
* core: add used_by API to show what objects are affected before deletion Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/elements: add support for used_by API Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: add authentik_used_by_shadows to shadow other models Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: implement used_by API Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * *: fix duplicate imports Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: add action field to used_by api Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: add UI for used_by action Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: add notice to tenant form Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: fix naming in used_by Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: check length for used_by Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: fix used_by for non-pk models Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * *: improve __str__ on models Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: add support for many to many in used_by Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -29,6 +29,7 @@ from structlog.stdlib import get_logger | |||||||
| from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.models import Application, User | from authentik.core.models import Application, User | ||||||
| from authentik.events.models import EventAction | from authentik.events.models import EventAction | ||||||
| from authentik.policies.api.exec import PolicyTestResultSerializer | from authentik.policies.api.exec import PolicyTestResultSerializer | ||||||
| @ -73,7 +74,7 @@ class ApplicationSerializer(ModelSerializer): | |||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
| class ApplicationViewSet(ModelViewSet): | class ApplicationViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Application Viewset""" |     """Application Viewset""" | ||||||
|  |  | ||||||
|     queryset = Application.objects.all() |     queryset = Application.objects.all() | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
| from ua_parser import user_agent_parser | from ua_parser import user_agent_parser | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.models import AuthenticatedSession | from authentik.core.models import AuthenticatedSession | ||||||
| from authentik.events.geo import GEOIP_READER, GeoIPDict | from authentik.events.geo import GEOIP_READER, GeoIPDict | ||||||
|  |  | ||||||
| @ -92,6 +93,7 @@ class AuthenticatedSessionSerializer(ModelSerializer): | |||||||
| class AuthenticatedSessionViewSet( | class AuthenticatedSessionViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
| from rest_framework_guardian.filters import ObjectPermissionsFilter | from rest_framework_guardian.filters import ObjectPermissionsFilter | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import is_dict | from authentik.core.api.utils import is_dict | ||||||
| from authentik.core.models import Group | from authentik.core.models import Group | ||||||
|  |  | ||||||
| @ -20,7 +21,7 @@ class GroupSerializer(ModelSerializer): | |||||||
|         fields = ["pk", "name", "is_superuser", "parent", "users", "attributes"] |         fields = ["pk", "name", "is_superuser", "parent", "users", "attributes"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupViewSet(ModelViewSet): | class GroupViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Group Viewset""" |     """Group Viewset""" | ||||||
|  |  | ||||||
|     queryset = Group.objects.all() |     queryset = Group.objects.all() | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ from rest_framework.serializers import ModelSerializer, SerializerMethodField | |||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
|  |  | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import ( | from authentik.core.api.utils import ( | ||||||
|     MetaNameSerializer, |     MetaNameSerializer, | ||||||
|     PassiveSerializer, |     PassiveSerializer, | ||||||
| @ -65,6 +66,7 @@ class PropertyMappingSerializer(ManagedSerializer, ModelSerializer, MetaNameSeri | |||||||
| class PropertyMappingViewSet( | class PropertyMappingViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ from rest_framework.response import Response | |||||||
| from rest_framework.serializers import ModelSerializer, SerializerMethodField | from rest_framework.serializers import ModelSerializer, SerializerMethodField | ||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | ||||||
| from authentik.core.models import Provider | from authentik.core.models import Provider | ||||||
| from authentik.lib.utils.reflection import all_subclasses | from authentik.lib.utils.reflection import all_subclasses | ||||||
| @ -48,6 +49,7 @@ class ProviderSerializer(ModelSerializer, MetaNameSerializer): | |||||||
| class ProviderViewSet( | class ProviderViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ from rest_framework.serializers import ModelSerializer, SerializerMethodField | |||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | ||||||
| from authentik.core.models import Source | from authentik.core.models import Source | ||||||
| from authentik.core.types import UserSettingSerializer | from authentik.core.types import UserSettingSerializer | ||||||
| @ -52,6 +53,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer): | |||||||
| class SourceViewSet( | class SourceViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.users import UserSerializer | from authentik.core.api.users import UserSerializer | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.core.models import Token, TokenIntents | from authentik.core.models import Token, TokenIntents | ||||||
| @ -43,7 +44,7 @@ class TokenViewSerializer(PassiveSerializer): | |||||||
|     key = CharField(read_only=True) |     key = CharField(read_only=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| class TokenViewSet(ModelViewSet): | class TokenViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Token Viewset""" |     """Token Viewset""" | ||||||
|  |  | ||||||
|     lookup_field = "identifier" |     lookup_field = "identifier" | ||||||
|  | |||||||
							
								
								
									
										102
									
								
								authentik/core/api/used_by.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								authentik/core/api/used_by.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | |||||||
|  | """used_by mixin""" | ||||||
|  | from enum import Enum | ||||||
|  | from inspect import getmembers | ||||||
|  |  | ||||||
|  | from django.db.models.base import Model | ||||||
|  | from django.db.models.deletion import SET_DEFAULT, SET_NULL | ||||||
|  | from django.db.models.manager import Manager | ||||||
|  | from drf_spectacular.utils import extend_schema | ||||||
|  | from guardian.shortcuts import get_objects_for_user | ||||||
|  | from rest_framework.decorators import action | ||||||
|  | from rest_framework.fields import CharField, ChoiceField | ||||||
|  | from rest_framework.request import Request | ||||||
|  | from rest_framework.response import Response | ||||||
|  |  | ||||||
|  | from authentik.core.api.utils import PassiveSerializer | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class DeleteAction(Enum): | ||||||
|  |     """Which action a delete will have on a used object""" | ||||||
|  |  | ||||||
|  |     CASCADE = "cascade" | ||||||
|  |     CASCADE_MANY = "cascade_many" | ||||||
|  |     SET_NULL = "set_null" | ||||||
|  |     SET_DEFAULT = "set_default" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class UsedBySerializer(PassiveSerializer): | ||||||
|  |     """A list of all objects referencing the queried object""" | ||||||
|  |  | ||||||
|  |     app = CharField() | ||||||
|  |     model_name = CharField() | ||||||
|  |     pk = CharField() | ||||||
|  |     name = CharField() | ||||||
|  |     action = ChoiceField(choices=[(x.name, x.name) for x in DeleteAction]) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_delete_action(manager: Manager) -> str: | ||||||
|  |     """Get the delete action from the Foreign key, falls back to cascade""" | ||||||
|  |     if hasattr(manager, "field"): | ||||||
|  |         if manager.field.remote_field.on_delete.__name__ == SET_NULL.__name__: | ||||||
|  |             return DeleteAction.SET_NULL.name | ||||||
|  |         if manager.field.remote_field.on_delete.__name__ == SET_DEFAULT.__name__: | ||||||
|  |             return DeleteAction.SET_DEFAULT.name | ||||||
|  |     if hasattr(manager, "source_field"): | ||||||
|  |         return DeleteAction.CASCADE_MANY.name | ||||||
|  |     return DeleteAction.CASCADE.name | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class UsedByMixin: | ||||||
|  |     """Mixin to add a used_by endpoint to return a list of all objects using this object""" | ||||||
|  |  | ||||||
|  |     @extend_schema( | ||||||
|  |         responses={200: UsedBySerializer(many=True)}, | ||||||
|  |     ) | ||||||
|  |     @action(detail=True, pagination_class=None, filter_backends=[]) | ||||||
|  |     # pylint: disable=invalid-name, unused-argument, too-many-locals | ||||||
|  |     def used_by(self, request: Request, *args, **kwargs) -> Response: | ||||||
|  |         """Get a list of all objects that use this object""" | ||||||
|  |         # pyright: reportGeneralTypeIssues=false | ||||||
|  |         model: Model = self.get_object() | ||||||
|  |         used_by = [] | ||||||
|  |         shadows = [] | ||||||
|  |         for attr_name, manager in getmembers(model, lambda x: isinstance(x, Manager)): | ||||||
|  |             if attr_name == "objects":  # pragma: no cover | ||||||
|  |                 continue | ||||||
|  |             manager: Manager | ||||||
|  |             if manager.model._meta.abstract: | ||||||
|  |                 continue | ||||||
|  |             app = manager.model._meta.app_label | ||||||
|  |             model_name = manager.model._meta.model_name | ||||||
|  |             delete_action = get_delete_action(manager) | ||||||
|  |  | ||||||
|  |             # To make sure we only apply shadows when there are any objects, | ||||||
|  |             # but so we only apply them once, have a simple flag for the first object | ||||||
|  |             first_object = True | ||||||
|  |  | ||||||
|  |             for obj in get_objects_for_user( | ||||||
|  |                 request.user, f"{app}.view_{model_name}", manager | ||||||
|  |             ).all(): | ||||||
|  |                 # Only merge shadows on first object | ||||||
|  |                 if first_object: | ||||||
|  |                     shadows += getattr( | ||||||
|  |                         manager.model._meta, "authentik_used_by_shadows", [] | ||||||
|  |                     ) | ||||||
|  |                 first_object = False | ||||||
|  |                 serializer = UsedBySerializer( | ||||||
|  |                     data={ | ||||||
|  |                         "app": app, | ||||||
|  |                         "model_name": model_name, | ||||||
|  |                         "pk": str(obj.pk), | ||||||
|  |                         "name": str(obj), | ||||||
|  |                         "action": delete_action, | ||||||
|  |                     } | ||||||
|  |                 ) | ||||||
|  |                 serializer.is_valid() | ||||||
|  |                 used_by.append(serializer.data) | ||||||
|  |         # Check the shadows map and remove anything that should be shadowed | ||||||
|  |         for idx, user in enumerate(used_by): | ||||||
|  |             full_model_name = f"{user['app']}.{user['model_name']}" | ||||||
|  |             if full_model_name in shadows: | ||||||
|  |                 del used_by[idx] | ||||||
|  |         return Response(used_by) | ||||||
| @ -25,6 +25,7 @@ from rest_framework_guardian.filters import ObjectPermissionsFilter | |||||||
| from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
| from authentik.core.api.groups import GroupSerializer | from authentik.core.api.groups import GroupSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict | from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict | ||||||
| from authentik.core.middleware import ( | from authentik.core.middleware import ( | ||||||
|     SESSION_IMPERSONATE_ORIGINAL_USER, |     SESSION_IMPERSONATE_ORIGINAL_USER, | ||||||
| @ -131,7 +132,7 @@ class UsersFilter(FilterSet): | |||||||
|         fields = ["username", "name", "is_active", "is_superuser", "attributes"] |         fields = ["username", "name", "is_active", "is_superuser", "attributes"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserViewSet(ModelViewSet): | class UserViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """User Viewset""" |     """User Viewset""" | ||||||
|  |  | ||||||
|     queryset = User.objects.none() |     queryset = User.objects.none() | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ from typing import Any, Optional, Type | |||||||
| from urllib.parse import urlencode | from urllib.parse import urlencode | ||||||
| from uuid import uuid4 | from uuid import uuid4 | ||||||
|  |  | ||||||
|  | import django.db.models.options as options | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.contrib.auth.models import AbstractUser | from django.contrib.auth.models import AbstractUser | ||||||
| from django.contrib.auth.models import UserManager as DjangoUserManager | from django.contrib.auth.models import UserManager as DjangoUserManager | ||||||
| @ -41,6 +42,9 @@ GRAVATAR_URL = "https://secure.gravatar.com" | |||||||
| DEFAULT_AVATAR = static("dist/assets/images/user_default.png") | DEFAULT_AVATAR = static("dist/assets/images/user_default.png") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | options.DEFAULT_NAMES = options.DEFAULT_NAMES + ("authentik_used_by_shadows",) | ||||||
|  |  | ||||||
|  |  | ||||||
| def default_token_duration(): | def default_token_duration(): | ||||||
|     """Default duration a Token is valid""" |     """Default duration a Token is valid""" | ||||||
|     return now() + timedelta(minutes=30) |     return now() + timedelta(minutes=30) | ||||||
|  | |||||||
| @ -1,10 +1,11 @@ | |||||||
| """Crypto API Views""" | """Crypto API Views""" | ||||||
| import django_filters |  | ||||||
| from cryptography.hazmat.backends import default_backend | from cryptography.hazmat.backends import default_backend | ||||||
| from cryptography.hazmat.primitives.serialization import load_pem_private_key | from cryptography.hazmat.primitives.serialization import load_pem_private_key | ||||||
| from cryptography.x509 import load_pem_x509_certificate | from cryptography.x509 import load_pem_x509_certificate | ||||||
| from django.http.response import HttpResponse | from django.http.response import HttpResponse | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import gettext_lazy as _ | ||||||
|  | from django_filters import FilterSet | ||||||
|  | from django_filters.filters import BooleanFilter | ||||||
| from drf_spectacular.types import OpenApiTypes | from drf_spectacular.types import OpenApiTypes | ||||||
| from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema | from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| @ -20,6 +21,7 @@ from rest_framework.serializers import ModelSerializer, ValidationError | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.crypto.builder import CertificateBuilder | from authentik.crypto.builder import CertificateBuilder | ||||||
| from authentik.crypto.models import CertificateKeyPair | from authentik.crypto.models import CertificateKeyPair | ||||||
| @ -100,10 +102,10 @@ class CertificateGenerationSerializer(PassiveSerializer): | |||||||
|     validity_days = IntegerField(initial=365) |     validity_days = IntegerField(initial=365) | ||||||
|  |  | ||||||
|  |  | ||||||
| class CertificateKeyPairFilter(django_filters.FilterSet): | class CertificateKeyPairFilter(FilterSet): | ||||||
|     """Filter for certificates""" |     """Filter for certificates""" | ||||||
|  |  | ||||||
|     has_key = django_filters.BooleanFilter( |     has_key = BooleanFilter( | ||||||
|         label="Only return certificate-key pairs with keys", method="filter_has_key" |         label="Only return certificate-key pairs with keys", method="filter_has_key" | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
| @ -117,7 +119,7 @@ class CertificateKeyPairFilter(django_filters.FilterSet): | |||||||
|         fields = ["name"] |         fields = ["name"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class CertificateKeyPairViewSet(ModelViewSet): | class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """CertificateKeyPair Viewset""" |     """CertificateKeyPair Viewset""" | ||||||
|  |  | ||||||
|     queryset = CertificateKeyPair.objects.all() |     queryset = CertificateKeyPair.objects.all() | ||||||
|  | |||||||
| @ -4,10 +4,14 @@ import datetime | |||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import DeleteAction | ||||||
| from authentik.core.models import User | from authentik.core.models import User | ||||||
| from authentik.crypto.api import CertificateKeyPairSerializer | from authentik.crypto.api import CertificateKeyPairSerializer | ||||||
| from authentik.crypto.builder import CertificateBuilder | from authentik.crypto.builder import CertificateBuilder | ||||||
| from authentik.crypto.models import CertificateKeyPair | from authentik.crypto.models import CertificateKeyPair | ||||||
|  | from authentik.flows.models import Flow | ||||||
|  | from authentik.providers.oauth2.generators import generate_client_secret | ||||||
|  | from authentik.providers.oauth2.models import OAuth2Provider | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestCrypto(TestCase): | class TestCrypto(TestCase): | ||||||
| @ -91,3 +95,35 @@ class TestCrypto(TestCase): | |||||||
|         ) |         ) | ||||||
|         self.assertEqual(200, response.status_code) |         self.assertEqual(200, response.status_code) | ||||||
|         self.assertIn("Content-Disposition", response) |         self.assertIn("Content-Disposition", response) | ||||||
|  |  | ||||||
|  |     def test_used_by(self): | ||||||
|  |         """Test used_by endpoint""" | ||||||
|  |         self.client.force_login(User.objects.get(username="akadmin")) | ||||||
|  |         keypair = CertificateKeyPair.objects.first() | ||||||
|  |         provider = OAuth2Provider.objects.create( | ||||||
|  |             name="test", | ||||||
|  |             client_id="test", | ||||||
|  |             client_secret=generate_client_secret(), | ||||||
|  |             authorization_flow=Flow.objects.first(), | ||||||
|  |             redirect_uris="http://localhost", | ||||||
|  |             rsa_key=CertificateKeyPair.objects.first(), | ||||||
|  |         ) | ||||||
|  |         response = self.client.get( | ||||||
|  |             reverse( | ||||||
|  |                 "authentik_api:certificatekeypair-used-by", | ||||||
|  |                 kwargs={"pk": keypair.pk}, | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(200, response.status_code) | ||||||
|  |         self.assertJSONEqual( | ||||||
|  |             response.content.decode(), | ||||||
|  |             [ | ||||||
|  |                 { | ||||||
|  |                     "app": "authentik_providers_oauth2", | ||||||
|  |                     "model_name": "oauth2provider", | ||||||
|  |                     "pk": str(provider.pk), | ||||||
|  |                     "name": str(provider), | ||||||
|  |                     "action": DeleteAction.SET_NULL.name, | ||||||
|  |                 } | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
|  |  | ||||||
| from authentik.api.authorization import OwnerFilter, OwnerPermissions | from authentik.api.authorization import OwnerFilter, OwnerPermissions | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.events.api.event import EventSerializer | from authentik.events.api.event import EventSerializer | ||||||
| from authentik.events.models import Notification | from authentik.events.models import Notification | ||||||
|  |  | ||||||
| @ -35,6 +36,7 @@ class NotificationViewSet( | |||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.UpdateModelMixin, |     mixins.UpdateModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.groups import GroupSerializer | from authentik.core.api.groups import GroupSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.events.models import NotificationRule | from authentik.events.models import NotificationRule | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -24,7 +25,7 @@ class NotificationRuleSerializer(ModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class NotificationRuleViewSet(ModelViewSet): | class NotificationRuleViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """NotificationRule Viewset""" |     """NotificationRule Viewset""" | ||||||
|  |  | ||||||
|     queryset = NotificationRule.objects.all() |     queryset = NotificationRule.objects.all() | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ from rest_framework.serializers import ModelSerializer, Serializer | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.events.models import ( | from authentik.events.models import ( | ||||||
|     Notification, |     Notification, | ||||||
|     NotificationSeverity, |     NotificationSeverity, | ||||||
| @ -52,7 +53,7 @@ class NotificationTransportTestSerializer(Serializer): | |||||||
|         raise NotImplementedError |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
| class NotificationTransportViewSet(ModelViewSet): | class NotificationTransportViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """NotificationTransport Viewset""" |     """NotificationTransport Viewset""" | ||||||
|  |  | ||||||
|     queryset = NotificationTransport.objects.all() |     queryset = NotificationTransport.objects.all() | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.flows.models import FlowStageBinding | from authentik.flows.models import FlowStageBinding | ||||||
|  |  | ||||||
| @ -27,7 +28,7 @@ class FlowStageBindingSerializer(ModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class FlowStageBindingViewSet(ModelViewSet): | class FlowStageBindingViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """FlowStageBinding Viewset""" |     """FlowStageBinding Viewset""" | ||||||
|  |  | ||||||
|     queryset = FlowStageBinding.objects.all() |     queryset = FlowStageBinding.objects.all() | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ from rest_framework.viewsets import ModelViewSet | |||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import CacheSerializer, LinkSerializer | from authentik.core.api.utils import CacheSerializer, LinkSerializer | ||||||
| from authentik.flows.exceptions import FlowNonApplicableException | from authentik.flows.exceptions import FlowNonApplicableException | ||||||
| from authentik.flows.models import Flow | from authentik.flows.models import Flow | ||||||
| @ -94,7 +95,7 @@ class DiagramElement: | |||||||
|         return f"{self.identifier}=>{self.type}: {self.rest}" |         return f"{self.identifier}=>{self.type}: {self.rest}" | ||||||
|  |  | ||||||
|  |  | ||||||
| class FlowViewSet(ModelViewSet): | class FlowViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Flow Viewset""" |     """Flow Viewset""" | ||||||
|  |  | ||||||
|     queryset = Flow.objects.all() |     queryset = Flow.objects.all() | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ from rest_framework.serializers import ModelSerializer, SerializerMethodField | |||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer | ||||||
| from authentik.core.types import UserSettingSerializer | from authentik.core.types import UserSettingSerializer | ||||||
| from authentik.flows.api.flows import FlowSerializer | from authentik.flows.api.flows import FlowSerializer | ||||||
| @ -49,6 +50,7 @@ class StageSerializer(ModelSerializer, MetaNameSerializer): | |||||||
| class StageViewSet( | class StageViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -72,7 +72,7 @@ class Stage(SerializerModel): | |||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         if hasattr(self, "__in_memory_type"): |         if hasattr(self, "__in_memory_type"): | ||||||
|             return f"In-memory Stage {getattr(self, '__in_memory_type')}" |             return f"In-memory Stage {getattr(self, '__in_memory_type')}" | ||||||
|         return self.name |         return f"Stage {self.name}" | ||||||
|  |  | ||||||
|  |  | ||||||
| def in_memory_stage(view: Type["StageView"]) -> Stage: | def in_memory_stage(view: Type["StageView"]) -> Stage: | ||||||
| @ -212,7 +212,7 @@ class FlowStageBinding(SerializerModel, PolicyBindingModel): | |||||||
|         return FlowStageBindingSerializer |         return FlowStageBindingSerializer | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
|         return f"{self.target} #{self.order}" |         return f"Flow-stage binding #{self.order} to {self.target}" | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ from rest_framework.serializers import JSONField, ModelSerializer, ValidationErr | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer, is_dict | from authentik.core.api.utils import PassiveSerializer, is_dict | ||||||
| from authentik.core.models import Provider | from authentik.core.models import Provider | ||||||
| from authentik.outposts.api.service_connections import ServiceConnectionSerializer | from authentik.outposts.api.service_connections import ServiceConnectionSerializer | ||||||
| @ -95,7 +96,7 @@ class OutpostHealthSerializer(PassiveSerializer): | |||||||
|     version_outdated = BooleanField(read_only=True) |     version_outdated = BooleanField(read_only=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| class OutpostViewSet(ModelViewSet): | class OutpostViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Outpost Viewset""" |     """Outpost Viewset""" | ||||||
|  |  | ||||||
|     queryset = Outpost.objects.all() |     queryset = Outpost.objects.all() | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ from rest_framework.response import Response | |||||||
| 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.utils import ( | from authentik.core.api.utils import ( | ||||||
|     MetaNameSerializer, |     MetaNameSerializer, | ||||||
|     PassiveSerializer, |     PassiveSerializer, | ||||||
| @ -55,6 +56,7 @@ class ServiceConnectionStateSerializer(PassiveSerializer): | |||||||
| class ServiceConnectionViewSet( | class ServiceConnectionViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
| @ -105,7 +107,7 @@ class DockerServiceConnectionSerializer(ServiceConnectionSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class DockerServiceConnectionViewSet(ModelViewSet): | class DockerServiceConnectionViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """DockerServiceConnection Viewset""" |     """DockerServiceConnection Viewset""" | ||||||
|  |  | ||||||
|     queryset = DockerServiceConnection.objects.all() |     queryset = DockerServiceConnection.objects.all() | ||||||
| @ -139,7 +141,7 @@ class KubernetesServiceConnectionSerializer(ServiceConnectionSerializer): | |||||||
|         fields = ServiceConnectionSerializer.Meta.fields + ["kubeconfig"] |         fields = ServiceConnectionSerializer.Meta.fields + ["kubeconfig"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class KubernetesServiceConnectionViewSet(ModelViewSet): | class KubernetesServiceConnectionViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """KubernetesServiceConnection Viewset""" |     """KubernetesServiceConnection Viewset""" | ||||||
|  |  | ||||||
|     queryset = KubernetesServiceConnection.objects.all() |     queryset = KubernetesServiceConnection.objects.all() | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ from rest_framework.viewsets import ModelViewSet | |||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.core.api.groups import GroupSerializer | from authentik.core.api.groups import GroupSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.users import UserSerializer | from authentik.core.api.users import UserSerializer | ||||||
| from authentik.policies.api.policies import PolicySerializer | from authentik.policies.api.policies import PolicySerializer | ||||||
| from authentik.policies.models import PolicyBinding, PolicyBindingModel | from authentik.policies.models import PolicyBinding, PolicyBindingModel | ||||||
| @ -99,7 +100,7 @@ class PolicyBindingSerializer(ModelSerializer): | |||||||
|         return data |         return data | ||||||
|  |  | ||||||
|  |  | ||||||
| class PolicyBindingViewSet(ModelViewSet): | class PolicyBindingViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """PolicyBinding Viewset""" |     """PolicyBinding Viewset""" | ||||||
|  |  | ||||||
|     queryset = ( |     queryset = ( | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ from structlog.stdlib import get_logger | |||||||
|  |  | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
| from authentik.core.api.applications import user_app_cache_key | from authentik.core.api.applications import user_app_cache_key | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import ( | from authentik.core.api.utils import ( | ||||||
|     CacheSerializer, |     CacheSerializer, | ||||||
|     MetaNameSerializer, |     MetaNameSerializer, | ||||||
| @ -79,6 +80,7 @@ class PolicySerializer(ModelSerializer, MetaNameSerializer): | |||||||
| class PolicyViewSet( | class PolicyViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Dummy Policy API Views""" | """Dummy Policy API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | 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.dummy.models import DummyPolicy | from authentik.policies.dummy.models import DummyPolicy | ||||||
|  |  | ||||||
| @ -13,7 +14,7 @@ class DummyPolicySerializer(PolicySerializer): | |||||||
|         fields = PolicySerializer.Meta.fields + ["result", "wait_min", "wait_max"] |         fields = PolicySerializer.Meta.fields + ["result", "wait_min", "wait_max"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class DummyPolicyViewSet(ModelViewSet): | class DummyPolicyViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Dummy Viewset""" |     """Dummy Viewset""" | ||||||
|  |  | ||||||
|     queryset = DummyPolicy.objects.all() |     queryset = DummyPolicy.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Event Matcher Policy API""" | """Event Matcher Policy API""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | 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.event_matcher.models import EventMatcherPolicy | from authentik.policies.event_matcher.models import EventMatcherPolicy | ||||||
|  |  | ||||||
| @ -17,7 +18,7 @@ class EventMatcherPolicySerializer(PolicySerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class EventMatcherPolicyViewSet(ModelViewSet): | class EventMatcherPolicyViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Event Matcher Policy Viewset""" |     """Event Matcher Policy Viewset""" | ||||||
|  |  | ||||||
|     queryset = EventMatcherPolicy.objects.all() |     queryset = EventMatcherPolicy.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Password Expiry Policy API Views""" | """Password Expiry Policy API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | 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.expiry.models import PasswordExpiryPolicy | from authentik.policies.expiry.models import PasswordExpiryPolicy | ||||||
|  |  | ||||||
| @ -13,7 +14,7 @@ class PasswordExpiryPolicySerializer(PolicySerializer): | |||||||
|         fields = PolicySerializer.Meta.fields + ["days", "deny_only"] |         fields = PolicySerializer.Meta.fields + ["days", "deny_only"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PasswordExpiryPolicyViewSet(ModelViewSet): | class PasswordExpiryPolicyViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Password Expiry Viewset""" |     """Password Expiry Viewset""" | ||||||
|  |  | ||||||
|     queryset = PasswordExpiryPolicy.objects.all() |     queryset = PasswordExpiryPolicy.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Expression Policy API""" | """Expression Policy API""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | 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.expression.evaluator import PolicyEvaluator | from authentik.policies.expression.evaluator import PolicyEvaluator | ||||||
| from authentik.policies.expression.models import ExpressionPolicy | from authentik.policies.expression.models import ExpressionPolicy | ||||||
| @ -20,7 +21,7 @@ class ExpressionPolicySerializer(PolicySerializer): | |||||||
|         fields = PolicySerializer.Meta.fields + ["expression"] |         fields = PolicySerializer.Meta.fields + ["expression"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class ExpressionPolicyViewSet(ModelViewSet): | class ExpressionPolicyViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Source Viewset""" |     """Source Viewset""" | ||||||
|  |  | ||||||
|     queryset = ExpressionPolicy.objects.all() |     queryset = ExpressionPolicy.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Source API Views""" | """Source API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | 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.hibp.models import HaveIBeenPwendPolicy | from authentik.policies.hibp.models import HaveIBeenPwendPolicy | ||||||
|  |  | ||||||
| @ -13,7 +14,7 @@ class HaveIBeenPwendPolicySerializer(PolicySerializer): | |||||||
|         fields = PolicySerializer.Meta.fields + ["password_field", "allowed_count"] |         fields = PolicySerializer.Meta.fields + ["password_field", "allowed_count"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class HaveIBeenPwendPolicyViewSet(ModelViewSet): | class HaveIBeenPwendPolicyViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Source Viewset""" |     """Source Viewset""" | ||||||
|  |  | ||||||
|     queryset = HaveIBeenPwendPolicy.objects.all() |     queryset = HaveIBeenPwendPolicy.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Password Policy API Views""" | """Password Policy API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | 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.password.models import PasswordPolicy | from authentik.policies.password.models import PasswordPolicy | ||||||
|  |  | ||||||
| @ -21,7 +22,7 @@ class PasswordPolicySerializer(PolicySerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PasswordPolicyViewSet(ModelViewSet): | class PasswordPolicyViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Password Policy Viewset""" |     """Password Policy Viewset""" | ||||||
|  |  | ||||||
|     queryset = PasswordPolicy.objects.all() |     queryset = PasswordPolicy.objects.all() | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ 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.policies.api.policies import PolicySerializer | from authentik.policies.api.policies import PolicySerializer | ||||||
| from authentik.policies.reputation.models import ( | from authentik.policies.reputation.models import ( | ||||||
|     IPReputation, |     IPReputation, | ||||||
| @ -23,7 +24,7 @@ class ReputationPolicySerializer(PolicySerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReputationPolicyViewSet(ModelViewSet): | class ReputationPolicyViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Reputation Policy Viewset""" |     """Reputation Policy Viewset""" | ||||||
|  |  | ||||||
|     queryset = ReputationPolicy.objects.all() |     queryset = ReputationPolicy.objects.all() | ||||||
| @ -46,6 +47,7 @@ class IPReputationSerializer(ModelSerializer): | |||||||
| class IPReputationViewSet( | class IPReputationViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
| @ -74,6 +76,7 @@ class UserReputationSerializer(ModelSerializer): | |||||||
| class UserReputationViewSet( | class UserReputationViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.providers.ldap.models import LDAPProvider | from authentik.providers.ldap.models import LDAPProvider | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -19,7 +20,7 @@ class LDAPProviderSerializer(ProviderSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class LDAPProviderViewSet(ModelViewSet): | class LDAPProviderViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """LDAPProvider Viewset""" |     """LDAPProvider Viewset""" | ||||||
|  |  | ||||||
|     queryset = LDAPProvider.objects.all() |     queryset = LDAPProvider.objects.all() | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ from rest_framework.serializers import ValidationError | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.core.models import Provider | from authentik.core.models import Provider | ||||||
| from authentik.providers.oauth2.models import JWTAlgorithms, OAuth2Provider | from authentik.providers.oauth2.models import JWTAlgorithms, OAuth2Provider | ||||||
| @ -61,7 +62,7 @@ class OAuth2ProviderSetupURLs(PassiveSerializer): | |||||||
|     logout = ReadOnlyField() |     logout = ReadOnlyField() | ||||||
|  |  | ||||||
|  |  | ||||||
| class OAuth2ProviderViewSet(ModelViewSet): | class OAuth2ProviderViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """OAuth2Provider Viewset""" |     """OAuth2Provider Viewset""" | ||||||
|  |  | ||||||
|     queryset = OAuth2Provider.objects.all() |     queryset = OAuth2Provider.objects.all() | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.propertymappings import PropertyMappingSerializer | from authentik.core.api.propertymappings import PropertyMappingSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.providers.oauth2.models import ScopeMapping | from authentik.providers.oauth2.models import ScopeMapping | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -17,7 +18,7 @@ class ScopeMappingSerializer(PropertyMappingSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class ScopeMappingViewSet(ModelViewSet): | class ScopeMappingViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """ScopeMapping Viewset""" |     """ScopeMapping Viewset""" | ||||||
|  |  | ||||||
|     queryset = ScopeMapping.objects.all() |     queryset = ScopeMapping.objects.all() | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ from rest_framework.filters import OrderingFilter, SearchFilter | |||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer | ||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.users import UserSerializer | from authentik.core.api.users import UserSerializer | ||||||
| from authentik.core.api.utils import MetaNameSerializer | from authentik.core.api.utils import MetaNameSerializer | ||||||
| from authentik.providers.oauth2.api.provider import OAuth2ProviderSerializer | from authentik.providers.oauth2.api.provider import OAuth2ProviderSerializer | ||||||
| @ -57,6 +58,7 @@ class RefreshTokenModelSerializer(ExpiringBaseGrantModelSerializer): | |||||||
| class AuthorizationCodeViewSet( | class AuthorizationCodeViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
| @ -82,6 +84,7 @@ class AuthorizationCodeViewSet( | |||||||
| class RefreshTokenViewSet( | class RefreshTokenViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -0,0 +1,26 @@ | |||||||
|  | # Generated by Django 3.2.3 on 2021-06-09 21:52 | ||||||
|  |  | ||||||
|  | import django.db.models.deletion | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("authentik_crypto", "0002_create_self_signed_kp"), | ||||||
|  |         ("authentik_providers_oauth2", "0013_alter_authorizationcode_nonce"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name="oauth2provider", | ||||||
|  |             name="rsa_key", | ||||||
|  |             field=models.ForeignKey( | ||||||
|  |                 help_text="Key used to sign the tokens. Only required when JWT Algorithm is set to RS256.", | ||||||
|  |                 null=True, | ||||||
|  |                 on_delete=django.db.models.deletion.SET_NULL, | ||||||
|  |                 to="authentik_crypto.certificatekeypair", | ||||||
|  |                 verbose_name="RSA Key", | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -215,8 +215,7 @@ class OAuth2Provider(Provider): | |||||||
|     rsa_key = models.ForeignKey( |     rsa_key = models.ForeignKey( | ||||||
|         CertificateKeyPair, |         CertificateKeyPair, | ||||||
|         verbose_name=_("RSA Key"), |         verbose_name=_("RSA Key"), | ||||||
|         on_delete=models.CASCADE, |         on_delete=models.SET_NULL, | ||||||
|         blank=True, |  | ||||||
|         null=True, |         null=True, | ||||||
|         help_text=_( |         help_text=_( | ||||||
|             "Key used to sign the tokens. Only required when JWT Algorithm is set to RS256." |             "Key used to sign the tokens. Only required when JWT Algorithm is set to RS256." | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.providers.oauth2.views.provider import ProviderInfoView | from authentik.providers.oauth2.views.provider import ProviderInfoView | ||||||
| from authentik.providers.proxy.models import ProxyMode, ProxyProvider | from authentik.providers.proxy.models import ProxyMode, ProxyProvider | ||||||
| @ -76,7 +77,7 @@ class ProxyProviderSerializer(ProviderSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class ProxyProviderViewSet(ModelViewSet): | class ProxyProviderViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """ProxyProvider Viewset""" |     """ProxyProvider Viewset""" | ||||||
|  |  | ||||||
|     queryset = ProxyProvider.objects.all() |     queryset = ProxyProvider.objects.all() | ||||||
|  | |||||||
| @ -167,3 +167,4 @@ class ProxyProvider(OutpostModel, OAuth2Provider): | |||||||
|  |  | ||||||
|         verbose_name = _("Proxy Provider") |         verbose_name = _("Proxy Provider") | ||||||
|         verbose_name_plural = _("Proxy Providers") |         verbose_name_plural = _("Proxy Providers") | ||||||
|  |         authentik_used_by_shadows = ["authentik_providers_oauth2.oauth2provider"] | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ from structlog.stdlib import get_logger | |||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
| from authentik.core.api.propertymappings import PropertyMappingSerializer | from authentik.core.api.propertymappings import PropertyMappingSerializer | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.core.models import Provider | from authentik.core.models import Provider | ||||||
| from authentik.flows.models import Flow, FlowDesignation | from authentik.flows.models import Flow, FlowDesignation | ||||||
| @ -75,7 +76,7 @@ class SAMLProviderImportSerializer(PassiveSerializer): | |||||||
|     file = FileField() |     file = FileField() | ||||||
|  |  | ||||||
|  |  | ||||||
| class SAMLProviderViewSet(ModelViewSet): | class SAMLProviderViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """SAMLProvider Viewset""" |     """SAMLProvider Viewset""" | ||||||
|  |  | ||||||
|     queryset = SAMLProvider.objects.all() |     queryset = SAMLProvider.objects.all() | ||||||
| @ -166,7 +167,7 @@ class SAMLPropertyMappingSerializer(PropertyMappingSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class SAMLPropertyMappingViewSet(ModelViewSet): | class SAMLPropertyMappingViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """SAMLPropertyMapping Viewset""" |     """SAMLPropertyMapping Viewset""" | ||||||
|  |  | ||||||
|     queryset = SAMLPropertyMapping.objects.all() |     queryset = SAMLPropertyMapping.objects.all() | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ from rest_framework.viewsets import ModelViewSet | |||||||
| from authentik.admin.api.tasks import TaskSerializer | from authentik.admin.api.tasks import TaskSerializer | ||||||
| from authentik.core.api.propertymappings import PropertyMappingSerializer | from authentik.core.api.propertymappings import PropertyMappingSerializer | ||||||
| from authentik.core.api.sources import SourceSerializer | from authentik.core.api.sources import SourceSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.events.monitored_tasks import TaskInfo | from authentik.events.monitored_tasks import TaskInfo | ||||||
| from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource | from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource | ||||||
|  |  | ||||||
| @ -41,7 +42,7 @@ class LDAPSourceSerializer(SourceSerializer): | |||||||
|         extra_kwargs = {"bind_password": {"write_only": True}} |         extra_kwargs = {"bind_password": {"write_only": True}} | ||||||
|  |  | ||||||
|  |  | ||||||
| class LDAPSourceViewSet(ModelViewSet): | class LDAPSourceViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """LDAP Source Viewset""" |     """LDAP Source Viewset""" | ||||||
|  |  | ||||||
|     queryset = LDAPSource.objects.all() |     queryset = LDAPSource.objects.all() | ||||||
| @ -75,7 +76,7 @@ class LDAPPropertyMappingSerializer(PropertyMappingSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class LDAPPropertyMappingViewSet(ModelViewSet): | class LDAPPropertyMappingViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """LDAP PropertyMapping Viewset""" |     """LDAP PropertyMapping Viewset""" | ||||||
|  |  | ||||||
|     queryset = LDAPPropertyMapping.objects.all() |     queryset = LDAPPropertyMapping.objects.all() | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ from rest_framework.serializers import ValidationError | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.sources import SourceSerializer | from authentik.core.api.sources import SourceSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.sources.oauth.models import OAuthSource | from authentik.sources.oauth.models import OAuthSource | ||||||
| from authentik.sources.oauth.types.manager import MANAGER | from authentik.sources.oauth.types.manager import MANAGER | ||||||
| @ -78,7 +79,7 @@ class OAuthSourceSerializer(SourceSerializer): | |||||||
|         extra_kwargs = {"consumer_secret": {"write_only": True}} |         extra_kwargs = {"consumer_secret": {"write_only": True}} | ||||||
|  |  | ||||||
|  |  | ||||||
| class OAuthSourceViewSet(ModelViewSet): | class OAuthSourceViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Source Viewset""" |     """Source Viewset""" | ||||||
|  |  | ||||||
|     queryset = OAuthSource.objects.all() |     queryset = OAuthSource.objects.all() | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ from rest_framework.viewsets import GenericViewSet | |||||||
|  |  | ||||||
| from authentik.api.authorization import OwnerFilter, OwnerPermissions | from authentik.api.authorization import OwnerFilter, OwnerPermissions | ||||||
| from authentik.core.api.sources import SourceSerializer | from authentik.core.api.sources import SourceSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.sources.oauth.models import UserOAuthSourceConnection | from authentik.sources.oauth.models import UserOAuthSourceConnection | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -26,6 +27,7 @@ class UserOAuthSourceConnectionViewSet( | |||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.UpdateModelMixin, |     mixins.UpdateModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ from structlog.stdlib import get_logger | |||||||
|  |  | ||||||
| from authentik.api.decorators import permission_required | from authentik.api.decorators import permission_required | ||||||
| from authentik.core.api.sources import SourceSerializer | from authentik.core.api.sources import SourceSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.flows.challenge import RedirectChallenge | from authentik.flows.challenge import RedirectChallenge | ||||||
| from authentik.flows.views import to_stage_response | from authentik.flows.views import to_stage_response | ||||||
| @ -42,7 +43,7 @@ class PlexTokenRedeemSerializer(PassiveSerializer): | |||||||
|     plex_token = CharField() |     plex_token = CharField() | ||||||
|  |  | ||||||
|  |  | ||||||
| class PlexSourceViewSet(ModelViewSet): | class PlexSourceViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Plex source Viewset""" |     """Plex source Viewset""" | ||||||
|  |  | ||||||
|     queryset = PlexSource.objects.all() |     queryset = PlexSource.objects.all() | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ from rest_framework.response import Response | |||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.sources import SourceSerializer | from authentik.core.api.sources import SourceSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.providers.saml.api import SAMLMetadataSerializer | from authentik.providers.saml.api import SAMLMetadataSerializer | ||||||
| from authentik.sources.saml.models import SAMLSource | from authentik.sources.saml.models import SAMLSource | ||||||
| from authentik.sources.saml.processors.metadata import MetadataProcessor | from authentik.sources.saml.processors.metadata import MetadataProcessor | ||||||
| @ -33,7 +34,7 @@ class SAMLSourceSerializer(SourceSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class SAMLSourceViewSet(ModelViewSet): | class SAMLSourceViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """SAMLSource Viewset""" |     """SAMLSource Viewset""" | ||||||
|  |  | ||||||
|     queryset = SAMLSource.objects.all() |     queryset = SAMLSource.objects.all() | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.api.authorization import OwnerFilter, OwnerPermissions | from authentik.api.authorization import OwnerFilter, OwnerPermissions | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice | from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice | ||||||
| from authentik.stages.authenticator_duo.stage import ( | from authentik.stages.authenticator_duo.stage import ( | ||||||
| @ -37,7 +38,7 @@ class AuthenticatorDuoStageSerializer(StageSerializer): | |||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthenticatorDuoStageViewSet(ModelViewSet): | class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """AuthenticatorDuoStage Viewset""" |     """AuthenticatorDuoStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = AuthenticatorDuoStage.objects.all() |     queryset = AuthenticatorDuoStage.objects.all() | ||||||
| @ -78,6 +79,7 @@ class DuoDeviceViewSet( | |||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.UpdateModelMixin, |     mixins.UpdateModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.api.authorization import OwnerFilter, OwnerPermissions | from authentik.api.authorization import OwnerFilter, OwnerPermissions | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_static.models import AuthenticatorStaticStage | from authentik.stages.authenticator_static.models import AuthenticatorStaticStage | ||||||
|  |  | ||||||
| @ -21,7 +22,7 @@ class AuthenticatorStaticStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields + ["configure_flow", "token_count"] |         fields = StageSerializer.Meta.fields + ["configure_flow", "token_count"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthenticatorStaticStageViewSet(ModelViewSet): | class AuthenticatorStaticStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """AuthenticatorStaticStage Viewset""" |     """AuthenticatorStaticStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = AuthenticatorStaticStage.objects.all() |     queryset = AuthenticatorStaticStage.objects.all() | ||||||
| @ -52,6 +53,7 @@ class StaticDeviceViewSet( | |||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.UpdateModelMixin, |     mixins.UpdateModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.api.authorization import OwnerFilter, OwnerPermissions | from authentik.api.authorization import OwnerFilter, OwnerPermissions | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage | from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage | ||||||
|  |  | ||||||
| @ -21,7 +22,7 @@ class AuthenticatorTOTPStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields + ["configure_flow", "digits"] |         fields = StageSerializer.Meta.fields + ["configure_flow", "digits"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthenticatorTOTPStageViewSet(ModelViewSet): | class AuthenticatorTOTPStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """AuthenticatorTOTPStage Viewset""" |     """AuthenticatorTOTPStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = AuthenticatorTOTPStage.objects.all() |     queryset = AuthenticatorTOTPStage.objects.all() | ||||||
| @ -45,6 +46,7 @@ class TOTPDeviceViewSet( | |||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.UpdateModelMixin, |     mixins.UpdateModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| from rest_framework.serializers import ValidationError | from rest_framework.serializers import ValidationError | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.flows.models import NotConfiguredAction | from authentik.flows.models import NotConfiguredAction | ||||||
| from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage | from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage | ||||||
| @ -32,7 +33,7 @@ class AuthenticatorValidateStageSerializer(StageSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthenticatorValidateStageViewSet(ModelViewSet): | class AuthenticatorValidateStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """AuthenticatorValidateStage Viewset""" |     """AuthenticatorValidateStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = AuthenticatorValidateStage.objects.all() |     queryset = AuthenticatorValidateStage.objects.all() | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ from rest_framework.serializers import ModelSerializer | |||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet | ||||||
|  |  | ||||||
| from authentik.api.authorization import OwnerFilter, OwnerPermissions | from authentik.api.authorization import OwnerFilter, OwnerPermissions | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.authenticator_webauthn.models import ( | from authentik.stages.authenticator_webauthn.models import ( | ||||||
|     AuthenticateWebAuthnStage, |     AuthenticateWebAuthnStage, | ||||||
| @ -23,7 +24,7 @@ class AuthenticateWebAuthnStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields + ["configure_flow"] |         fields = StageSerializer.Meta.fields + ["configure_flow"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class AuthenticateWebAuthnStageViewSet(ModelViewSet): | class AuthenticateWebAuthnStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """AuthenticateWebAuthnStage Viewset""" |     """AuthenticateWebAuthnStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = AuthenticateWebAuthnStage.objects.all() |     queryset = AuthenticateWebAuthnStage.objects.all() | ||||||
| @ -44,6 +45,7 @@ class WebAuthnDeviceViewSet( | |||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.UpdateModelMixin, |     mixins.UpdateModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """CaptchaStage API Views""" | """CaptchaStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.captcha.models import CaptchaStage | from authentik.stages.captcha.models import CaptchaStage | ||||||
|  |  | ||||||
| @ -15,7 +16,7 @@ class CaptchaStageSerializer(StageSerializer): | |||||||
|         extra_kwargs = {"private_key": {"write_only": True}} |         extra_kwargs = {"private_key": {"write_only": True}} | ||||||
|  |  | ||||||
|  |  | ||||||
| class CaptchaStageViewSet(ModelViewSet): | class CaptchaStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """CaptchaStage Viewset""" |     """CaptchaStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = CaptchaStage.objects.all() |     queryset = CaptchaStage.objects.all() | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ from rest_framework.filters import OrderingFilter, SearchFilter | |||||||
| from rest_framework.viewsets import GenericViewSet, ModelViewSet | from rest_framework.viewsets import GenericViewSet, ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.applications import ApplicationSerializer | from authentik.core.api.applications import ApplicationSerializer | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.users import UserSerializer | from authentik.core.api.users import UserSerializer | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.consent.models import ConsentStage, UserConsent | from authentik.stages.consent.models import ConsentStage, UserConsent | ||||||
| @ -20,7 +21,7 @@ class ConsentStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields + ["mode", "consent_expire_in"] |         fields = StageSerializer.Meta.fields + ["mode", "consent_expire_in"] | ||||||
|  |  | ||||||
|  |  | ||||||
| class ConsentStageViewSet(ModelViewSet): | class ConsentStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """ConsentStage Viewset""" |     """ConsentStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = ConsentStage.objects.all() |     queryset = ConsentStage.objects.all() | ||||||
| @ -42,6 +43,7 @@ class UserConsentSerializer(StageSerializer): | |||||||
| class UserConsentViewSet( | class UserConsentViewSet( | ||||||
|     mixins.RetrieveModelMixin, |     mixins.RetrieveModelMixin, | ||||||
|     mixins.DestroyModelMixin, |     mixins.DestroyModelMixin, | ||||||
|  |     UsedByMixin, | ||||||
|     mixins.ListModelMixin, |     mixins.ListModelMixin, | ||||||
|     GenericViewSet, |     GenericViewSet, | ||||||
| ): | ): | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """deny Stage API Views""" | """deny Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.deny.models import DenyStage | from authentik.stages.deny.models import DenyStage | ||||||
|  |  | ||||||
| @ -14,7 +15,7 @@ class DenyStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields |         fields = StageSerializer.Meta.fields | ||||||
|  |  | ||||||
|  |  | ||||||
| class DenyStageViewSet(ModelViewSet): | class DenyStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """DenyStage Viewset""" |     """DenyStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = DenyStage.objects.all() |     queryset = DenyStage.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """DummyStage API Views""" | """DummyStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.dummy.models import DummyStage | from authentik.stages.dummy.models import DummyStage | ||||||
|  |  | ||||||
| @ -14,7 +15,7 @@ class DummyStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields |         fields = StageSerializer.Meta.fields | ||||||
|  |  | ||||||
|  |  | ||||||
| class DummyStageViewSet(ModelViewSet): | class DummyStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """DummyStage Viewset""" |     """DummyStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = DummyStage.objects.all() |     queryset = DummyStage.objects.all() | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ from rest_framework.response import Response | |||||||
| from rest_framework.serializers import ValidationError | from rest_framework.serializers import ValidationError | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import TypeCreateSerializer | from authentik.core.api.utils import TypeCreateSerializer | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.email.models import EmailStage, get_template_choices | from authentik.stages.email.models import EmailStage, get_template_choices | ||||||
| @ -46,7 +47,7 @@ class EmailStageSerializer(StageSerializer): | |||||||
|         extra_kwargs = {"password": {"write_only": True}} |         extra_kwargs = {"password": {"write_only": True}} | ||||||
|  |  | ||||||
|  |  | ||||||
| class EmailStageViewSet(ModelViewSet): | class EmailStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """EmailStage Viewset""" |     """EmailStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = EmailStage.objects.all() |     queryset = EmailStage.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Identification Stage API Views""" | """Identification Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.identification.models import IdentificationStage | from authentik.stages.identification.models import IdentificationStage | ||||||
|  |  | ||||||
| @ -22,7 +23,7 @@ class IdentificationStageSerializer(StageSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class IdentificationStageViewSet(ModelViewSet): | class IdentificationStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """IdentificationStage Viewset""" |     """IdentificationStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = IdentificationStage.objects.all() |     queryset = IdentificationStage.objects.all() | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ from rest_framework.fields import JSONField | |||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.users import UserSerializer | from authentik.core.api.users import UserSerializer | ||||||
| from authentik.core.api.utils import is_dict | from authentik.core.api.utils import is_dict | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| @ -20,7 +21,7 @@ class InvitationStageSerializer(StageSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class InvitationStageViewSet(ModelViewSet): | class InvitationStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """InvitationStage Viewset""" |     """InvitationStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = InvitationStage.objects.all() |     queryset = InvitationStage.objects.all() | ||||||
| @ -45,7 +46,7 @@ class InvitationSerializer(ModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class InvitationViewSet(ModelViewSet): | class InvitationViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Invitation Viewset""" |     """Invitation Viewset""" | ||||||
|  |  | ||||||
|     queryset = Invitation.objects.all() |     queryset = Invitation.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """PasswordStage API Views""" | """PasswordStage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.password.models import PasswordStage | from authentik.stages.password.models import PasswordStage | ||||||
|  |  | ||||||
| @ -18,7 +19,7 @@ class PasswordStageSerializer(StageSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PasswordStageViewSet(ModelViewSet): | class PasswordStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """PasswordStage Viewset""" |     """PasswordStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = PasswordStage.objects.all() |     queryset = PasswordStage.objects.all() | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ from rest_framework.serializers import CharField, ModelSerializer | |||||||
| from rest_framework.validators import UniqueValidator | from rest_framework.validators import UniqueValidator | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.prompt.models import Prompt, PromptStage | from authentik.stages.prompt.models import Prompt, PromptStage | ||||||
|  |  | ||||||
| @ -21,7 +22,7 @@ class PromptStageSerializer(StageSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PromptStageViewSet(ModelViewSet): | class PromptStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """PromptStage Viewset""" |     """PromptStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = PromptStage.objects.all() |     queryset = PromptStage.objects.all() | ||||||
| @ -48,7 +49,7 @@ class PromptSerializer(ModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PromptViewSet(ModelViewSet): | class PromptViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Prompt Viewset""" |     """Prompt Viewset""" | ||||||
|  |  | ||||||
|     queryset = Prompt.objects.all().prefetch_related("promptstage_set") |     queryset = Prompt.objects.all().prefetch_related("promptstage_set") | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """User Delete Stage API Views""" | """User Delete Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_delete.models import UserDeleteStage | from authentik.stages.user_delete.models import UserDeleteStage | ||||||
|  |  | ||||||
| @ -14,7 +15,7 @@ class UserDeleteStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields |         fields = StageSerializer.Meta.fields | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserDeleteStageViewSet(ModelViewSet): | class UserDeleteStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """UserDeleteStage Viewset""" |     """UserDeleteStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = UserDeleteStage.objects.all() |     queryset = UserDeleteStage.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Login Stage API Views""" | """Login Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_login.models import UserLoginStage | from authentik.stages.user_login.models import UserLoginStage | ||||||
|  |  | ||||||
| @ -16,7 +17,7 @@ class UserLoginStageSerializer(StageSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserLoginStageViewSet(ModelViewSet): | class UserLoginStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """UserLoginStage Viewset""" |     """UserLoginStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = UserLoginStage.objects.all() |     queryset = UserLoginStage.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """Logout Stage API Views""" | """Logout Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_logout.models import UserLogoutStage | from authentik.stages.user_logout.models import UserLogoutStage | ||||||
|  |  | ||||||
| @ -14,7 +15,7 @@ class UserLogoutStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields |         fields = StageSerializer.Meta.fields | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserLogoutStageViewSet(ModelViewSet): | class UserLogoutStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """UserLogoutStage Viewset""" |     """UserLogoutStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = UserLogoutStage.objects.all() |     queryset = UserLogoutStage.objects.all() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """User Write Stage API Views""" | """User Write Stage API Views""" | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.flows.api.stages import StageSerializer | from authentik.flows.api.stages import StageSerializer | ||||||
| from authentik.stages.user_write.models import UserWriteStage | from authentik.stages.user_write.models import UserWriteStage | ||||||
|  |  | ||||||
| @ -14,7 +15,7 @@ class UserWriteStageSerializer(StageSerializer): | |||||||
|         fields = StageSerializer.Meta.fields |         fields = StageSerializer.Meta.fields | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserWriteStageViewSet(ModelViewSet): | class UserWriteStageViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """UserWriteStage Viewset""" |     """UserWriteStage Viewset""" | ||||||
|  |  | ||||||
|     queryset = UserWriteStage.objects.all() |     queryset = UserWriteStage.objects.all() | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ from rest_framework.response import Response | |||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
|  | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.lib.config import CONFIG | from authentik.lib.config import CONFIG | ||||||
| from authentik.tenants.models import Tenant | from authentik.tenants.models import Tenant | ||||||
| @ -56,7 +57,7 @@ class CurrentTenantSerializer(PassiveSerializer): | |||||||
|     flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False) |     flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False) | ||||||
|  |  | ||||||
|  |  | ||||||
| class TenantViewSet(ModelViewSet): | class TenantViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Tenant Viewset""" |     """Tenant Viewset""" | ||||||
|  |  | ||||||
|     queryset = Tenant.objects.all() |     queryset = Tenant.objects.all() | ||||||
|  | |||||||
| @ -41,7 +41,9 @@ class Tenant(models.Model): | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     def __str__(self) -> str: |     def __str__(self) -> str: | ||||||
|         return self.domain |         if self.default: | ||||||
|  |             return "Default tenant" | ||||||
|  |         return f"Tenant {self.domain}" | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ force_grid_wrap = 0 | |||||||
| use_parentheses = true | use_parentheses = true | ||||||
| line_length = 88 | line_length = 88 | ||||||
| src_paths = ["authentik", "tests", "lifecycle"] | src_paths = ["authentik", "tests", "lifecycle"] | ||||||
|  | force_to_top = "*" | ||||||
|  |  | ||||||
| [tool.coverage.run] | [tool.coverage.run] | ||||||
| source = ["authentik"] | source = ["authentik"] | ||||||
|  | |||||||
							
								
								
									
										2162
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										2162
									
								
								schema.yml
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,20 +1,30 @@ | |||||||
| import { t } from "@lingui/macro"; | import { t } from "@lingui/macro"; | ||||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { EVENT_REFRESH } from "../../constants"; | import { EVENT_REFRESH } from "../../constants"; | ||||||
| import { ModalButton } from "../buttons/ModalButton"; | import { ModalButton } from "../buttons/ModalButton"; | ||||||
| import { MessageLevel } from "../messages/Message"; | import { MessageLevel } from "../messages/Message"; | ||||||
| import { showMessage } from "../messages/MessageContainer"; | import { showMessage } from "../messages/MessageContainer"; | ||||||
| import "../buttons/SpinnerButton"; | import "../buttons/SpinnerButton"; | ||||||
|  | import { UsedBy, UsedByActionEnum } from "authentik-api"; | ||||||
|  | import PFList from "@patternfly/patternfly/components/List/list.css"; | ||||||
|  | import { until } from "lit-html/directives/until"; | ||||||
|  |  | ||||||
| @customElement("ak-forms-delete") | @customElement("ak-forms-delete") | ||||||
| export class DeleteForm extends ModalButton { | export class DeleteForm extends ModalButton { | ||||||
|  |  | ||||||
|  |     static get styles(): CSSResult[] { | ||||||
|  |         return super.styles.concat(PFList); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @property({attribute: false}) |     @property({attribute: false}) | ||||||
|     obj?: Record<string, unknown>; |     obj?: Record<string, unknown>; | ||||||
|  |  | ||||||
|     @property() |     @property() | ||||||
|     objectLabel?: string; |     objectLabel?: string; | ||||||
|  |  | ||||||
|  |     @property({attribute: false}) | ||||||
|  |     usedBy?: () => Promise<UsedBy[]>; | ||||||
|  |  | ||||||
|     @property({attribute: false}) |     @property({attribute: false}) | ||||||
|     delete!: () => Promise<unknown>; |     delete!: () => Promise<unknown>; | ||||||
|  |  | ||||||
| @ -69,6 +79,40 @@ export class DeleteForm extends ModalButton { | |||||||
|                 </p> |                 </p> | ||||||
|             </form> |             </form> | ||||||
|         </section> |         </section> | ||||||
|  |         ${this.usedBy ? until(this.usedBy().then(usedBy => { | ||||||
|  |             if (usedBy.length < 1) { | ||||||
|  |                 return html``; | ||||||
|  |             } | ||||||
|  |             return html` | ||||||
|  |                 <section class="pf-c-page__main-section"> | ||||||
|  |                     <form class="pf-c-form pf-m-horizontal"> | ||||||
|  |                         <p> | ||||||
|  |                             ${t`The following objects use ${objName} `} | ||||||
|  |                         </p> | ||||||
|  |                         <ul class="pf-c-list"> | ||||||
|  |                             ${usedBy.map(ub => { | ||||||
|  |                                 let consequence = ""; | ||||||
|  |                                 switch (ub.action) { | ||||||
|  |                                 case UsedByActionEnum.Cascade: | ||||||
|  |                                     consequence = t`object will be DELETED`; | ||||||
|  |                                     break; | ||||||
|  |                                 case UsedByActionEnum.CascadeMany: | ||||||
|  |                                     consequence = t`connecting object will be deleted`; | ||||||
|  |                                     break; | ||||||
|  |                                 case UsedByActionEnum.SetDefault: | ||||||
|  |                                     consequence = t`reference will be reset to default value`; | ||||||
|  |                                     break; | ||||||
|  |                                 case UsedByActionEnum.SetNull: | ||||||
|  |                                     consequence = t`reference will be set to an empty value`; | ||||||
|  |                                     break; | ||||||
|  |                                 } | ||||||
|  |                                 return html`<li>${t`${ub.name} (${consequence})`}</li>`; | ||||||
|  |                             })} | ||||||
|  |                         </ul> | ||||||
|  |                     </form> | ||||||
|  |                 </section> | ||||||
|  |             `; | ||||||
|  |         })) : html``} | ||||||
|         <footer class="pf-c-modal-box__footer"> |         <footer class="pf-c-modal-box__footer"> | ||||||
|             <ak-spinner-button |             <ak-spinner-button | ||||||
|                 .callAction=${() => { |                 .callAction=${() => { | ||||||
|  | |||||||
| @ -44,9 +44,14 @@ export class UserOAuthCodeList extends Table<ExpiringBaseGrantModel> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Authorization Code`} |                 objectLabel=${t`Authorization Code`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new Oauth2Api(DEFAULT_CONFIG).oauth2AuthorizationCodesUsedByList({ | ||||||
|  |                         id: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new Oauth2Api(DEFAULT_CONFIG).oauth2AuthorizationCodesDestroy({ |                     return new Oauth2Api(DEFAULT_CONFIG).oauth2AuthorizationCodesDestroy({ | ||||||
|                         id: item.pk || 0, |                         id: item.pk, | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -68,9 +68,14 @@ export class UserOAuthRefreshList extends Table<RefreshTokenModel> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Refresh Code`} |                 objectLabel=${t`Refresh Code`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new Oauth2Api(DEFAULT_CONFIG).oauth2RefreshTokensUsedByList({ | ||||||
|  |                         id: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new Oauth2Api(DEFAULT_CONFIG).oauth2RefreshTokensDestroy({ |                     return new Oauth2Api(DEFAULT_CONFIG).oauth2RefreshTokensDestroy({ | ||||||
|                         id: item.pk || 0, |                         id: item.pk, | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -45,6 +45,11 @@ export class AuthenticatedSessionList extends Table<AuthenticatedSession> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Session`} |                 objectLabel=${t`Session`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new CoreApi(DEFAULT_CONFIG).coreAuthenticatedSessionsUsedByList({ | ||||||
|  |                         uuid: item.uuid || "", | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new CoreApi(DEFAULT_CONFIG).coreAuthenticatedSessionsDestroy({ |                     return new CoreApi(DEFAULT_CONFIG).coreAuthenticatedSessionsDestroy({ | ||||||
|                         uuid: item.uuid || "", |                         uuid: item.uuid || "", | ||||||
|  | |||||||
| @ -40,9 +40,14 @@ export class UserConsentList extends Table<UserConsent> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Consent`} |                 objectLabel=${t`Consent`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new CoreApi(DEFAULT_CONFIG).coreUserConsentUsedByList({ | ||||||
|  |                         id: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new CoreApi(DEFAULT_CONFIG).coreUserConsentDestroy({ |                     return new CoreApi(DEFAULT_CONFIG).coreUserConsentDestroy({ | ||||||
|                         id: item.pk || 0, |                         id: item.pk, | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -1144,6 +1144,9 @@ msgid "Disable" | |||||||
| msgstr "Disable" | msgstr "Disable" | ||||||
|  |  | ||||||
| #: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts | #: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts | ||||||
|  | msgid "Disable Duo authenticator" | ||||||
|  | msgstr "Disable Duo authenticator" | ||||||
|  |  | ||||||
| #: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts | #: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts | ||||||
| msgid "Disable Static Tokens" | msgid "Disable Static Tokens" | ||||||
| msgstr "Disable Static Tokens" | msgstr "Disable Static Tokens" | ||||||
| @ -1293,11 +1296,14 @@ msgstr "Email: Text field with Email type." | |||||||
| msgid "Enable" | msgid "Enable" | ||||||
| msgstr "Enable" | msgstr "Enable" | ||||||
|  |  | ||||||
|  | #: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts | ||||||
|  | msgid "Enable Duo authenticator" | ||||||
|  | msgstr "Enable Duo authenticator" | ||||||
|  |  | ||||||
| #: src/pages/sources/ldap/LDAPSourceForm.ts | #: src/pages/sources/ldap/LDAPSourceForm.ts | ||||||
| msgid "Enable StartTLS" | msgid "Enable StartTLS" | ||||||
| msgstr "Enable StartTLS" | msgstr "Enable StartTLS" | ||||||
|  |  | ||||||
| #: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts |  | ||||||
| #: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts | #: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts | ||||||
| msgid "Enable Static Tokens" | msgid "Enable Static Tokens" | ||||||
| msgstr "Enable Static Tokens" | msgstr "Enable Static Tokens" | ||||||
| @ -2148,6 +2154,10 @@ msgstr "Matches Event's Client IP (strict matching, for network matching use an | |||||||
| msgid "Matches an event against a set of criteria. If any of the configured values match, the policy passes." | msgid "Matches an event against a set of criteria. If any of the configured values match, the policy passes." | ||||||
| msgstr "Matches an event against a set of criteria. If any of the configured values match, the policy passes." | msgstr "Matches an event against a set of criteria. If any of the configured values match, the policy passes." | ||||||
|  |  | ||||||
|  | #: src/pages/tenants/TenantForm.ts | ||||||
|  | msgid "Matching is done based on domain suffix, so if you enter domain.tld, foo.domain.tld will still match." | ||||||
|  | msgstr "Matching is done based on domain suffix, so if you enter domain.tld, foo.domain.tld will still match." | ||||||
|  |  | ||||||
| #: src/pages/policies/expiry/ExpiryPolicyForm.ts | #: src/pages/policies/expiry/ExpiryPolicyForm.ts | ||||||
| msgid "Maximum age (in days)" | msgid "Maximum age (in days)" | ||||||
| msgstr "Maximum age (in days)" | msgstr "Maximum age (in days)" | ||||||
| @ -3805,6 +3815,10 @@ msgstr "The external URL you'll access the application at. Include any non-stand | |||||||
| msgid "The external URL you'll authenticate at. Can be the same domain as authentik." | msgid "The external URL you'll authenticate at. Can be the same domain as authentik." | ||||||
| msgstr "The external URL you'll authenticate at. Can be the same domain as authentik." | msgstr "The external URL you'll authenticate at. Can be the same domain as authentik." | ||||||
|  |  | ||||||
|  | #: src/elements/forms/DeleteForm.ts | ||||||
|  | msgid "The following objects use {objName}" | ||||||
|  | msgstr "The following objects use {objName}" | ||||||
|  |  | ||||||
| #: src/pages/policies/dummy/DummyPolicyForm.ts | #: src/pages/policies/dummy/DummyPolicyForm.ts | ||||||
| msgid "The policy takes a random time to execute. This controls the minimum time it will take." | msgid "The policy takes a random time to execute. This controls the minimum time it will take." | ||||||
| msgstr "The policy takes a random time to execute. This controls the minimum time it will take." | msgstr "The policy takes a random time to execute. This controls the minimum time it will take." | ||||||
| @ -4519,6 +4533,18 @@ msgstr "authentik LDAP Backend" | |||||||
| msgid "no tabs defined" | msgid "no tabs defined" | ||||||
| msgstr "no tabs defined" | msgstr "no tabs defined" | ||||||
|  |  | ||||||
|  | #: src/elements/forms/DeleteForm.ts | ||||||
|  | msgid "object will be DELETED" | ||||||
|  | msgstr "object will be DELETED" | ||||||
|  |  | ||||||
|  | #: src/elements/forms/DeleteForm.ts | ||||||
|  | msgid "reference will be reset to default value" | ||||||
|  | msgstr "reference will be reset to default value" | ||||||
|  |  | ||||||
|  | #: src/elements/forms/DeleteForm.ts | ||||||
|  | msgid "reference will be set to an empty value" | ||||||
|  | msgstr "reference will be set to an empty value" | ||||||
|  |  | ||||||
| #: src/elements/Expand.ts | #: src/elements/Expand.ts | ||||||
| #: src/elements/Expand.ts | #: src/elements/Expand.ts | ||||||
| msgid "{0}" | msgid "{0}" | ||||||
| @ -4532,6 +4558,10 @@ msgstr "{0} (\"{1}\", of type {2})" | |||||||
| msgid "{0} ({1})" | msgid "{0} ({1})" | ||||||
| msgstr "{0} ({1})" | msgstr "{0} ({1})" | ||||||
|  |  | ||||||
|  | #: src/elements/forms/DeleteForm.ts | ||||||
|  | msgid "{0} ({consequence})" | ||||||
|  | msgstr "{0} ({consequence})" | ||||||
|  |  | ||||||
| #: src/elements/table/TablePagination.ts | #: src/elements/table/TablePagination.ts | ||||||
| msgid "{0} - {1} of {2}" | msgid "{0} - {1} of {2}" | ||||||
| msgstr "{0} - {1} of {2}" | msgstr "{0} - {1} of {2}" | ||||||
|  | |||||||
| @ -1136,6 +1136,9 @@ msgid "Disable" | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
|  | msgid "Disable Duo authenticator" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| msgid "Disable Static Tokens" | msgid "Disable Static Tokens" | ||||||
| msgstr "" | msgstr "" | ||||||
| @ -1286,10 +1289,13 @@ msgid "Enable" | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| msgid "Enable StartTLS" | msgid "Enable Duo authenticator" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
|  | msgid "Enable StartTLS" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| msgid "Enable Static Tokens" | msgid "Enable Static Tokens" | ||||||
| msgstr "" | msgstr "" | ||||||
| @ -2140,6 +2146,10 @@ msgstr "" | |||||||
| msgid "Matches an event against a set of criteria. If any of the configured values match, the policy passes." | msgid "Matches an event against a set of criteria. If any of the configured values match, the policy passes." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #:  | ||||||
|  | msgid "Matching is done based on domain suffix, so if you enter domain.tld, foo.domain.tld will still match." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| msgid "Maximum age (in days)" | msgid "Maximum age (in days)" | ||||||
| msgstr "" | msgstr "" | ||||||
| @ -3797,6 +3807,10 @@ msgstr "" | |||||||
| msgid "The external URL you'll authenticate at. Can be the same domain as authentik." | msgid "The external URL you'll authenticate at. Can be the same domain as authentik." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #:  | ||||||
|  | msgid "The following objects use {objName}" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| msgid "The policy takes a random time to execute. This controls the minimum time it will take." | msgid "The policy takes a random time to execute. This controls the minimum time it will take." | ||||||
| msgstr "" | msgstr "" | ||||||
| @ -4505,6 +4519,18 @@ msgstr "" | |||||||
| msgid "no tabs defined" | msgid "no tabs defined" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #:  | ||||||
|  | msgid "object will be DELETED" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #:  | ||||||
|  | msgid "reference will be reset to default value" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #:  | ||||||
|  | msgid "reference will be set to an empty value" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| #:  | #:  | ||||||
| msgid "{0}" | msgid "{0}" | ||||||
| @ -4518,6 +4544,10 @@ msgstr "" | |||||||
| msgid "{0} ({1})" | msgid "{0} ({1})" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #:  | ||||||
|  | msgid "{0} ({consequence})" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #:  | #:  | ||||||
| msgid "{0} - {1} of {2}" | msgid "{0} - {1} of {2}" | ||||||
| msgstr "" | msgstr "" | ||||||
|  | |||||||
| @ -103,9 +103,14 @@ export class ApplicationListPage extends TablePage<Application> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Application`} |                 objectLabel=${t`Application`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new CoreApi(DEFAULT_CONFIG).coreApplicationsUsedByList({ | ||||||
|  |                         slug: item.slug | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new CoreApi(DEFAULT_CONFIG).coreApplicationsDestroy({ |                     return new CoreApi(DEFAULT_CONFIG).coreApplicationsDestroy({ | ||||||
|                         slug: item.slug || "" |                         slug: item.slug | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -79,9 +79,14 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Certificate-Key Pair`} |                 objectLabel=${t`Certificate-Key Pair`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsUsedByList({ | ||||||
|  |                         kpUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsDestroy({ |                     return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsDestroy({ | ||||||
|                         kpUuid: item.pk || "" |                         kpUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import { t } from "@lingui/macro"; | import { t } from "@lingui/macro"; | ||||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||||
| import { until } from "lit-html/directives/until"; | import { until } from "lit-html/directives/until"; | ||||||
| import { ActionEnum, FlowsApi } from "authentik-api"; | import { EventMatcherPolicyActionEnum, FlowsApi } from "authentik-api"; | ||||||
| import "../../elements/Spinner"; | import "../../elements/Spinner"; | ||||||
| import "../../elements/Expand"; | import "../../elements/Expand"; | ||||||
| import { PFSize } from "../../elements/Spinner"; | import { PFSize } from "../../elements/Spinner"; | ||||||
| @ -142,14 +142,14 @@ export class EventInfo extends LitElement { | |||||||
|             return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`; |             return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`; | ||||||
|         } |         } | ||||||
|         switch (this.event?.action) { |         switch (this.event?.action) { | ||||||
|         case ActionEnum.ModelCreated: |         case EventMatcherPolicyActionEnum.ModelCreated: | ||||||
|         case ActionEnum.ModelUpdated: |         case EventMatcherPolicyActionEnum.ModelUpdated: | ||||||
|         case ActionEnum.ModelDeleted: |         case EventMatcherPolicyActionEnum.ModelDeleted: | ||||||
|             return html` |             return html` | ||||||
|                 <h3>${t`Affected model:`}</h3> |                 <h3>${t`Affected model:`}</h3> | ||||||
|                 ${this.getModelInfo(this.event.context?.model as EventContext)} |                 ${this.getModelInfo(this.event.context?.model as EventContext)} | ||||||
|                 `; |                 `; | ||||||
|         case ActionEnum.AuthorizeApplication: |         case EventMatcherPolicyActionEnum.AuthorizeApplication: | ||||||
|             return html`<div class="pf-l-flex"> |             return html`<div class="pf-l-flex"> | ||||||
|                     <div class="pf-l-flex__item"> |                     <div class="pf-l-flex__item"> | ||||||
|                         <h3>${t`Authorized application:`}</h3> |                         <h3>${t`Authorized application:`}</h3> | ||||||
| @ -166,17 +166,17 @@ export class EventInfo extends LitElement { | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|                 <ak-expand>${this.defaultResponse()}</ak-expand>`; |                 <ak-expand>${this.defaultResponse()}</ak-expand>`; | ||||||
|         case ActionEnum.EmailSent: |         case EventMatcherPolicyActionEnum.EmailSent: | ||||||
|             return html`<h3>${t`Email info:`}</h3> |             return html`<h3>${t`Email info:`}</h3> | ||||||
|                 ${this.getEmailInfo(this.event.context)} |                 ${this.getEmailInfo(this.event.context)} | ||||||
|                 <ak-expand> |                 <ak-expand> | ||||||
|                     <iframe srcdoc=${this.event.context.body}></iframe> |                     <iframe srcdoc=${this.event.context.body}></iframe> | ||||||
|                 </ak-expand>`; |                 </ak-expand>`; | ||||||
|         case ActionEnum.SecretView: |         case EventMatcherPolicyActionEnum.SecretView: | ||||||
|             return html` |             return html` | ||||||
|                 <h3>${t`Secret:`}</h3> |                 <h3>${t`Secret:`}</h3> | ||||||
|                 ${this.getModelInfo(this.event.context.secret as EventContext)}`; |                 ${this.getModelInfo(this.event.context.secret as EventContext)}`; | ||||||
|         case ActionEnum.PropertyMappingException: |         case EventMatcherPolicyActionEnum.PropertyMappingException: | ||||||
|             return html`<div class="pf-l-flex"> |             return html`<div class="pf-l-flex"> | ||||||
|                     <div class="pf-l-flex__item"> |                     <div class="pf-l-flex__item"> | ||||||
|                         <h3>${t`Exception`}</h3> |                         <h3>${t`Exception`}</h3> | ||||||
| @ -188,7 +188,7 @@ export class EventInfo extends LitElement { | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|                 <ak-expand>${this.defaultResponse()}</ak-expand>`; |                 <ak-expand>${this.defaultResponse()}</ak-expand>`; | ||||||
|         case ActionEnum.PolicyException: |         case EventMatcherPolicyActionEnum.PolicyException: | ||||||
|             return html`<div class="pf-l-flex"> |             return html`<div class="pf-l-flex"> | ||||||
|                     <div class="pf-l-flex__item"> |                     <div class="pf-l-flex__item"> | ||||||
|                         <h3>${t`Binding`}</h3> |                         <h3>${t`Binding`}</h3> | ||||||
| @ -207,7 +207,7 @@ export class EventInfo extends LitElement { | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|                 <ak-expand>${this.defaultResponse()}</ak-expand>`; |                 <ak-expand>${this.defaultResponse()}</ak-expand>`; | ||||||
|         case ActionEnum.PolicyExecution: |         case EventMatcherPolicyActionEnum.PolicyExecution: | ||||||
|             return html`<div class="pf-l-flex"> |             return html`<div class="pf-l-flex"> | ||||||
|                     <div class="pf-l-flex__item"> |                     <div class="pf-l-flex__item"> | ||||||
|                         <h3>${t`Binding`}</h3> |                         <h3>${t`Binding`}</h3> | ||||||
| @ -235,10 +235,10 @@ export class EventInfo extends LitElement { | |||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|                 <ak-expand>${this.defaultResponse()}</ak-expand>`; |                 <ak-expand>${this.defaultResponse()}</ak-expand>`; | ||||||
|         case ActionEnum.ConfigurationError: |         case EventMatcherPolicyActionEnum.ConfigurationError: | ||||||
|             return html`<h3>${this.event.context.message}</h3> |             return html`<h3>${this.event.context.message}</h3> | ||||||
|                 <ak-expand>${this.defaultResponse()}</ak-expand>`; |                 <ak-expand>${this.defaultResponse()}</ak-expand>`; | ||||||
|         case ActionEnum.UpdateAvailable: |         case EventMatcherPolicyActionEnum.UpdateAvailable: | ||||||
|             return html`<h3>${t`New version available!`}</h3> |             return html`<h3>${t`New version available!`}</h3> | ||||||
|                 <a |                 <a | ||||||
|                     target="_blank" |                     target="_blank" | ||||||
| @ -247,7 +247,7 @@ export class EventInfo extends LitElement { | |||||||
|                 </a>`; |                 </a>`; | ||||||
|         // Action types which typically don't record any extra context. |         // Action types which typically don't record any extra context. | ||||||
|         // If context is not empty, we fall to the default response. |         // If context is not empty, we fall to the default response. | ||||||
|         case ActionEnum.Login: |         case EventMatcherPolicyActionEnum.Login: | ||||||
|             if ("using_source" in this.event.context) { |             if ("using_source" in this.event.context) { | ||||||
|                 return html`<div class="pf-l-flex"> |                 return html`<div class="pf-l-flex"> | ||||||
|                     <div class="pf-l-flex__item"> |                     <div class="pf-l-flex__item"> | ||||||
| @ -257,11 +257,11 @@ export class EventInfo extends LitElement { | |||||||
|                 </div>`; |                 </div>`; | ||||||
|             } |             } | ||||||
|             return this.defaultResponse(); |             return this.defaultResponse(); | ||||||
|         case ActionEnum.LoginFailed: |         case EventMatcherPolicyActionEnum.LoginFailed: | ||||||
|             return html` |             return html` | ||||||
|                 <h3>${t`Attempted to log in as ${this.event.context.username}`}</h3> |                 <h3>${t`Attempted to log in as ${this.event.context.username}`}</h3> | ||||||
|                 <ak-expand>${this.defaultResponse()}</ak-expand>`; |                 <ak-expand>${this.defaultResponse()}</ak-expand>`; | ||||||
|         case ActionEnum.Logout: |         case EventMatcherPolicyActionEnum.Logout: | ||||||
|             if (this.event.context === {}) { |             if (this.event.context === {}) { | ||||||
|                 return html`<span>${t`No additional data available.`}</span>`; |                 return html`<span>${t`No additional data available.`}</span>`; | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -73,9 +73,14 @@ export class RuleListPage extends TablePage<NotificationRule> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Notification rule`} |                 objectLabel=${t`Notification rule`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new EventsApi(DEFAULT_CONFIG).eventsRulesUsedByList({ | ||||||
|  |                         pbmUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new EventsApi(DEFAULT_CONFIG).eventsRulesDestroy({ |                     return new EventsApi(DEFAULT_CONFIG).eventsRulesDestroy({ | ||||||
|                         pbmUuid: item.pk || "" |                         pbmUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -77,9 +77,14 @@ export class TransportListPage extends TablePage<NotificationTransport> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Notifications Transport`} |                 objectLabel=${t`Notifications Transport`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new EventsApi(DEFAULT_CONFIG).eventsTransportsUsedByList({ | ||||||
|  |                         uuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new EventsApi(DEFAULT_CONFIG).eventsTransportsDestroy({ |                     return new EventsApi(DEFAULT_CONFIG).eventsTransportsDestroy({ | ||||||
|                         uuid: item.pk || "" |                         uuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -82,9 +82,14 @@ export class BoundStagesList extends Table<FlowStageBinding> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Stage binding`} |                 objectLabel=${t`Stage binding`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUsedByList({ | ||||||
|  |                         fsbUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new FlowsApi(DEFAULT_CONFIG).flowsBindingsDestroy({ |                     return new FlowsApi(DEFAULT_CONFIG).flowsBindingsDestroy({ | ||||||
|                         fsbUuid: item.pk || "", |                         fsbUuid: item.pk, | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -78,6 +78,11 @@ export class FlowListPage extends TablePage<Flow> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Flow`} |                 objectLabel=${t`Flow`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new FlowsApi(DEFAULT_CONFIG).flowsInstancesUsedByList({ | ||||||
|  |                         slug: item.slug | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new FlowsApi(DEFAULT_CONFIG).flowsInstancesDestroy({ |                     return new FlowsApi(DEFAULT_CONFIG).flowsInstancesDestroy({ | ||||||
|                         slug: item.slug |                         slug: item.slug | ||||||
|  | |||||||
| @ -72,9 +72,14 @@ export class GroupListPage extends TablePage<Group> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Group`} |                 objectLabel=${t`Group`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new CoreApi(DEFAULT_CONFIG).coreGroupsUsedByList({ | ||||||
|  |                         groupUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new CoreApi(DEFAULT_CONFIG).coreGroupsDestroy({ |                     return new CoreApi(DEFAULT_CONFIG).coreGroupsDestroy({ | ||||||
|                         groupUuid: item.pk || "" |                         groupUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -77,9 +77,14 @@ export class OutpostListPage extends TablePage<Outpost> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Outpost`} |                 objectLabel=${t`Outpost`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUsedByList({ | ||||||
|  |                         uuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesDestroy({ |                     return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesDestroy({ | ||||||
|                         uuid: item.pk || "" |                         uuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -93,9 +93,14 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Outpost Service-connection`} |                 objectLabel=${t`Outpost Service-connection`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllUsedByList({ | ||||||
|  |                         uuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllDestroy({ |                     return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllDestroy({ | ||||||
|                         uuid: item.pk || "" |                         uuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -137,9 +137,14 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Policy binding`} |                 objectLabel=${t`Policy binding`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsUsedByList({ | ||||||
|  |                         policyBindingUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsDestroy({ |                     return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsDestroy({ | ||||||
|                         policyBindingUuid: item.pk || "", |                         policyBindingUuid: item.pk, | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -107,9 +107,14 @@ export class PolicyListPage extends TablePage<Policy> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Policy`} |                 objectLabel=${t`Policy`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new PoliciesApi(DEFAULT_CONFIG).policiesAllUsedByList({ | ||||||
|  |                         policyUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new PoliciesApi(DEFAULT_CONFIG).policiesAllDestroy({ |                     return new PoliciesApi(DEFAULT_CONFIG).policiesAllDestroy({ | ||||||
|                         policyUuid: item.pk || "" |                         policyUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -55,6 +55,11 @@ export class IPReputationListPage extends TablePage<IPReputation> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`IP Reputation`} |                 objectLabel=${t`IP Reputation`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsUsedByList({ | ||||||
|  |                         id: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsDestroy({ |                     return new PoliciesApi(DEFAULT_CONFIG).policiesReputationIpsDestroy({ | ||||||
|                         id: item.pk, |                         id: item.pk, | ||||||
|  | |||||||
| @ -55,6 +55,11 @@ export class UserReputationListPage extends TablePage<UserReputation> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`User Reputation`} |                 objectLabel=${t`User Reputation`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersUsedByList({ | ||||||
|  |                         id: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersDestroy({ |                     return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUsersDestroy({ | ||||||
|                         id: item.pk, |                         id: item.pk, | ||||||
|  | |||||||
| @ -97,9 +97,14 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Property Mapping`} |                 objectLabel=${t`Property Mapping`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllUsedByList({ | ||||||
|  |                         pmUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllDestroy({ |                     return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllDestroy({ | ||||||
|                         pmUuid: item.pk || "" |                         pmUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -90,9 +90,14 @@ export class ProviderListPage extends TablePage<Provider> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Provider`} |                 objectLabel=${t`Provider`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new ProvidersApi(DEFAULT_CONFIG).providersAllUsedByList({ | ||||||
|  |                         id: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new ProvidersApi(DEFAULT_CONFIG).providersAllDestroy({ |                     return new ProvidersApi(DEFAULT_CONFIG).providersAllDestroy({ | ||||||
|                         id: item.pk || 0 |                         id: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -86,9 +86,14 @@ export class SourceListPage extends TablePage<Source> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Source`} |                 objectLabel=${t`Source`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new SourcesApi(DEFAULT_CONFIG).sourcesAllUsedByList({ | ||||||
|  |                         slug: item.slug | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new SourcesApi(DEFAULT_CONFIG).sourcesAllDestroy({ |                     return new SourcesApi(DEFAULT_CONFIG).sourcesAllDestroy({ | ||||||
|                         slug: item.slug || "" |                         slug: item.slug | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -102,9 +102,14 @@ export class StageListPage extends TablePage<Stage> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${item.verboseName || ""} |                 objectLabel=${item.verboseName || ""} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new StagesApi(DEFAULT_CONFIG).stagesAllUsedByList({ | ||||||
|  |                         stageUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new StagesApi(DEFAULT_CONFIG).stagesAllDestroy({ |                     return new StagesApi(DEFAULT_CONFIG).stagesAllDestroy({ | ||||||
|                         stageUuid: item.pk || "" |                         stageUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -58,9 +58,14 @@ export class InvitationListPage extends TablePage<Invitation> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Invitation`} |                 objectLabel=${t`Invitation`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsUsedByList({ | ||||||
|  |                         inviteUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsDestroy({ |                     return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsDestroy({ | ||||||
|                         inviteUuid: item.pk || "" |                         inviteUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -77,9 +77,14 @@ export class PromptListPage extends TablePage<Prompt> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Prompt`} |                 objectLabel=${t`Prompt`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsUsedByList({ | ||||||
|  |                         promptUuid: item.pk | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsDestroy({ |                     return new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsDestroy({ | ||||||
|                         promptUuid: item.pk || "" |                         promptUuid: item.pk | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                 <button slot="trigger" class="pf-c-button pf-m-danger"> |                 <button slot="trigger" class="pf-c-button pf-m-danger"> | ||||||
|  | |||||||
| @ -47,6 +47,7 @@ export class TenantForm extends ModelForm<Tenant, string> { | |||||||
|                 ?required=${true} |                 ?required=${true} | ||||||
|                 name="domain"> |                 name="domain"> | ||||||
|                 <input type="text" value="${first(this.instance?.domain, window.location.host)}" class="pf-c-form-control" required> |                 <input type="text" value="${first(this.instance?.domain, window.location.host)}" class="pf-c-form-control" required> | ||||||
|  |                 <p class="pf-c-form__helper-text">${t`Matching is done based on domain suffix, so if you enter domain.tld, foo.domain.tld will still match.`}</p> | ||||||
|             </ak-form-element-horizontal> |             </ak-form-element-horizontal> | ||||||
|             <ak-form-element-horizontal name="default"> |             <ak-form-element-horizontal name="default"> | ||||||
|                 <div class="pf-c-check"> |                 <div class="pf-c-check"> | ||||||
|  | |||||||
| @ -68,6 +68,11 @@ export class TenantListPage extends TablePage<Tenant> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Tenant`} |                 objectLabel=${t`Tenant`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new CoreApi(DEFAULT_CONFIG).coreTenantsUsedByList({ | ||||||
|  |                         tenantUuid: item.tenantUuid | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new CoreApi(DEFAULT_CONFIG).coreTenantsDestroy({ |                     return new CoreApi(DEFAULT_CONFIG).coreTenantsDestroy({ | ||||||
|                         tenantUuid: item.tenantUuid |                         tenantUuid: item.tenantUuid | ||||||
|  | |||||||
| @ -58,6 +58,11 @@ export class TokenListPage extends TablePage<Token> { | |||||||
|             <ak-forms-delete |             <ak-forms-delete | ||||||
|                 .obj=${item} |                 .obj=${item} | ||||||
|                 objectLabel=${t`Token`} |                 objectLabel=${t`Token`} | ||||||
|  |                 .usedBy=${() => { | ||||||
|  |                     return new CoreApi(DEFAULT_CONFIG).coreTokensUsedByList({ | ||||||
|  |                         identifier: item.identifier | ||||||
|  |                     }); | ||||||
|  |                 }} | ||||||
|                 .delete=${() => { |                 .delete=${() => { | ||||||
|                     return new CoreApi(DEFAULT_CONFIG).coreTokensDestroy({ |                     return new CoreApi(DEFAULT_CONFIG).coreTokensDestroy({ | ||||||
|                         identifier: item.identifier |                         identifier: item.identifier | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ export class UserSettingsAuthenticatorDuo extends BaseUserSettings { | |||||||
|                             }); |                             }); | ||||||
|                         }); |                         }); | ||||||
|                     }}> |                     }}> | ||||||
|                     ${t`Disable Static Tokens`} |                     ${t`Disable Duo authenticator`} | ||||||
|                 </button> |                 </button> | ||||||
|             </div>`; |             </div>`; | ||||||
|     } |     } | ||||||
| @ -47,7 +47,7 @@ export class UserSettingsAuthenticatorDuo extends BaseUserSettings { | |||||||
|             <div class="pf-c-card__footer"> |             <div class="pf-c-card__footer"> | ||||||
|                 ${this.configureUrl ? |                 ${this.configureUrl ? | ||||||
|                     html`<a href="${this.configureUrl}?next=/%23%2Fuser" |                     html`<a href="${this.configureUrl}?next=/%23%2Fuser" | ||||||
|                             class="pf-c-button pf-m-primary">${t`Enable Static Tokens`} |                             class="pf-c-button pf-m-primary">${t`Enable Duo authenticator`} | ||||||
|                         </a>`: html``} |                         </a>`: html``} | ||||||
|             </div>`; |             </div>`; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -111,9 +111,14 @@ export class UserListPage extends TablePage<User> { | |||||||
|                         <ak-forms-delete |                         <ak-forms-delete | ||||||
|                             .obj=${item} |                             .obj=${item} | ||||||
|                             objectLabel=${t`User`} |                             objectLabel=${t`User`} | ||||||
|  |                             .usedBy=${() => { | ||||||
|  |                                 return new CoreApi(DEFAULT_CONFIG).coreUsersUsedByList({ | ||||||
|  |                                     id: item.pk | ||||||
|  |                                 }); | ||||||
|  |                             }} | ||||||
|                             .delete=${() => { |                             .delete=${() => { | ||||||
|                                 return new CoreApi(DEFAULT_CONFIG).coreUsersDestroy({ |                                 return new CoreApi(DEFAULT_CONFIG).coreUsersDestroy({ | ||||||
|                                     id: item.pk || 0 |                                     id: item.pk | ||||||
|                                 }); |                                 }); | ||||||
|                             }}> |                             }}> | ||||||
|                             <button slot="trigger" class="pf-c-dropdown__menu-item"> |                             <button slot="trigger" class="pf-c-dropdown__menu-item"> | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L