web: use generated API Client (#616)
* api: fix types for config API * api: remove broken swagger UI * admin: re-fix system task enum * events: make event optional * events: fix Schema for notification transport test * flows: use APIView for Flow Executor * core: fix schema for Metrics APIs * web: rewrite to use generated API client * web: generate API Client in CI * admin: use x_cord and y_cord to prevent yaml issues * events: fix linting errors * web: don't lint generated code * core: fix fields not being required in TypeSerializer * flows: fix missing permission_classes * web: cleanup * web: fix rendering of graph on Overview page * web: cleanup imports * core: fix missing background image filter * flows: fix flows not advancing properly * stages/*: fix warnings during get_challenge * web: send Flow response as JSON instead of FormData * web: fix styles for horizontal tabs * web: add base chart class and custom chart for application view * root: generate ts client for e2e tests * web: don't attempt to connect to websocket in selenium tests * web: fix UserTokenList not being included in the build * web: fix styling for static token list * web: fix CSRF Token missing * stages/authenticator_static: fix error when disable static tokens * core: fix display issue when updating user info * web: fix Flow executor not showing spinner when redirecting
This commit is contained in:
		
							
								
								
									
										3
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @ -59,6 +59,9 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v1 |       - uses: actions/checkout@v1 | ||||||
|  |       - name: prepare ts api client | ||||||
|  |         run: | | ||||||
|  |           docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true | ||||||
|       - name: Docker Login Registry |       - name: Docker Login Registry | ||||||
|         env: |         env: | ||||||
|           DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} |           DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | ||||||
|  | |||||||
| @ -45,4 +45,5 @@ COPY ./lifecycle/ /lifecycle | |||||||
| USER authentik | USER authentik | ||||||
| STOPSIGNAL SIGINT | STOPSIGNAL SIGINT | ||||||
| ENV TMPDIR /dev/shm/ | ENV TMPDIR /dev/shm/ | ||||||
|  | ENV PYTHONUBUFFERED 1 | ||||||
| ENTRYPOINT [ "/lifecycle/bootstrap.sh" ] | ENTRYPOINT [ "/lifecycle/bootstrap.sh" ] | ||||||
|  | |||||||
| @ -7,8 +7,8 @@ from django.db.models import Count, ExpressionWrapper, F, Model | |||||||
| from django.db.models.fields import DurationField | from django.db.models.fields import DurationField | ||||||
| from django.db.models.functions import ExtractHour | from django.db.models.functions import ExtractHour | ||||||
| from django.utils.timezone import now | from django.utils.timezone import now | ||||||
| from drf_yasg2.utils import swagger_auto_schema | from drf_yasg2.utils import swagger_auto_schema, swagger_serializer_method | ||||||
| from rest_framework.fields import SerializerMethodField | from rest_framework.fields import IntegerField, SerializerMethodField | ||||||
| from rest_framework.permissions import IsAdminUser | from rest_framework.permissions import IsAdminUser | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| @ -37,23 +37,39 @@ def get_events_per_1h(**filter_kwargs) -> list[dict[str, int]]: | |||||||
|     for hour in range(0, -24, -1): |     for hour in range(0, -24, -1): | ||||||
|         results.append( |         results.append( | ||||||
|             { |             { | ||||||
|                 "x": time.mktime((_now + timedelta(hours=hour)).timetuple()) * 1000, |                 "x_cord": time.mktime((_now + timedelta(hours=hour)).timetuple()) | ||||||
|                 "y": data[hour * -1], |                 * 1000, | ||||||
|  |                 "y_cord": data[hour * -1], | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|     return results |     return results | ||||||
|  |  | ||||||
|  |  | ||||||
| class AdministrationMetricsSerializer(Serializer): | class CoordinateSerializer(Serializer): | ||||||
|  |     """Coordinates for diagrams""" | ||||||
|  |  | ||||||
|  |     x_cord = IntegerField(read_only=True) | ||||||
|  |     y_cord = IntegerField(read_only=True) | ||||||
|  |  | ||||||
|  |     def create(self, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |     def update(self, instance: Model, validated_data: dict) -> Model: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class LoginMetricsSerializer(Serializer): | ||||||
|     """Login Metrics per 1h""" |     """Login Metrics per 1h""" | ||||||
|  |  | ||||||
|     logins_per_1h = SerializerMethodField() |     logins_per_1h = SerializerMethodField() | ||||||
|     logins_failed_per_1h = SerializerMethodField() |     logins_failed_per_1h = SerializerMethodField() | ||||||
|  |  | ||||||
|  |     @swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True)) | ||||||
|     def get_logins_per_1h(self, _): |     def get_logins_per_1h(self, _): | ||||||
|         """Get successful logins per hour for the last 24 hours""" |         """Get successful logins per hour for the last 24 hours""" | ||||||
|         return get_events_per_1h(action=EventAction.LOGIN) |         return get_events_per_1h(action=EventAction.LOGIN) | ||||||
|  |  | ||||||
|  |     @swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True)) | ||||||
|     def get_logins_failed_per_1h(self, _): |     def get_logins_failed_per_1h(self, _): | ||||||
|         """Get failed logins per hour for the last 24 hours""" |         """Get failed logins per hour for the last 24 hours""" | ||||||
|         return get_events_per_1h(action=EventAction.LOGIN_FAILED) |         return get_events_per_1h(action=EventAction.LOGIN_FAILED) | ||||||
| @ -70,8 +86,8 @@ class AdministrationMetricsViewSet(ViewSet): | |||||||
|  |  | ||||||
|     permission_classes = [IsAdminUser] |     permission_classes = [IsAdminUser] | ||||||
|  |  | ||||||
|     @swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)}) |     @swagger_auto_schema(responses={200: LoginMetricsSerializer(many=False)}) | ||||||
|     def list(self, request: Request) -> Response: |     def list(self, request: Request) -> Response: | ||||||
|         """Login Metrics per 1h""" |         """Login Metrics per 1h""" | ||||||
|         serializer = AdministrationMetricsSerializer(True) |         serializer = LoginMetricsSerializer(True) | ||||||
|         return Response(serializer.data) |         return Response(serializer.data) | ||||||
|  | |||||||
| @ -25,8 +25,8 @@ class TaskSerializer(Serializer): | |||||||
|     task_finish_timestamp = DateTimeField(source="finish_timestamp") |     task_finish_timestamp = DateTimeField(source="finish_timestamp") | ||||||
|  |  | ||||||
|     status = ChoiceField( |     status = ChoiceField( | ||||||
|         source="result.status.value", |         source="result.status.name", | ||||||
|         choices=[(x.value, x.name) for x in TaskResultStatus], |         choices=[(x.name, x.name) for x in TaskResultStatus], | ||||||
|     ) |     ) | ||||||
|     messages = ListField(source="result.messages") |     messages = ListField(source="result.messages") | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| """api v2 urls""" | """api v2 urls""" | ||||||
|  | from django.conf import settings | ||||||
| from django.urls import path, re_path | from django.urls import path, re_path | ||||||
| from drf_yasg2 import openapi | from drf_yasg2 import openapi | ||||||
| from drf_yasg2.views import get_schema_view | from drf_yasg2.views import get_schema_view | ||||||
| @ -156,6 +157,14 @@ router.register("stages/user_write", UserWriteStageViewSet) | |||||||
| router.register("stages/dummy", DummyStageViewSet) | router.register("stages/dummy", DummyStageViewSet) | ||||||
| router.register("policies/dummy", DummyPolicyViewSet) | router.register("policies/dummy", DummyPolicyViewSet) | ||||||
|  |  | ||||||
|  | api_urls = router.urls + [ | ||||||
|  |     path( | ||||||
|  |         "flows/executor/<slug:flow_slug>/", | ||||||
|  |         FlowExecutorView.as_view(), | ||||||
|  |         name="flow-executor", | ||||||
|  |     ), | ||||||
|  | ] | ||||||
|  |  | ||||||
| info = openapi.Info( | info = openapi.Info( | ||||||
|     title="authentik API", |     title="authentik API", | ||||||
|     default_version="v2", |     default_version="v2", | ||||||
| @ -165,26 +174,22 @@ info = openapi.Info( | |||||||
|     ), |     ), | ||||||
| ) | ) | ||||||
| SchemaView = get_schema_view( | SchemaView = get_schema_view( | ||||||
|     info, |     info, public=True, permission_classes=(AllowAny,), patterns=api_urls | ||||||
|     public=True, |  | ||||||
|     permission_classes=(AllowAny,), |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| urlpatterns = [ | urlpatterns = api_urls + [ | ||||||
|     re_path( |     re_path( | ||||||
|         r"^swagger(?P<format>\.json|\.yaml)$", |         r"^swagger(?P<format>\.json|\.yaml)$", | ||||||
|         SchemaView.without_ui(cache_timeout=0), |         SchemaView.without_ui(cache_timeout=0), | ||||||
|         name="schema-json", |         name="schema-json", | ||||||
|     ), |     ), | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | if settings.DEBUG: | ||||||
|  |     urlpatterns = urlpatterns + [ | ||||||
|         path( |         path( | ||||||
|             "swagger/", |             "swagger/", | ||||||
|             SchemaView.with_ui("swagger", cache_timeout=0), |             SchemaView.with_ui("swagger", cache_timeout=0), | ||||||
|             name="schema-swagger-ui", |             name="schema-swagger-ui", | ||||||
|         ), |         ), | ||||||
|     path("redoc/", SchemaView.with_ui("redoc", cache_timeout=0), name="schema-redoc"), |     ] | ||||||
|     path( |  | ||||||
|         "flows/executor/<slug:flow_slug>/", |  | ||||||
|         FlowExecutorView.as_view(), |  | ||||||
|         name="flow-executor", |  | ||||||
|     ), |  | ||||||
| ] + router.urls |  | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.db.models import QuerySet | from django.db.models import QuerySet | ||||||
| from django.http.response import Http404 | from django.http.response import Http404 | ||||||
|  | from drf_yasg2.utils import swagger_auto_schema | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.fields import SerializerMethodField | from rest_framework.fields import SerializerMethodField | ||||||
| @ -13,7 +14,7 @@ from rest_framework.viewsets import ModelViewSet | |||||||
| from rest_framework_guardian.filters import ObjectPermissionsFilter | from rest_framework_guardian.filters import ObjectPermissionsFilter | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.admin.api.metrics import get_events_per_1h | from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h | ||||||
| from authentik.core.api.providers import ProviderSerializer | from authentik.core.api.providers import ProviderSerializer | ||||||
| from authentik.core.models import Application | from authentik.core.models import Application | ||||||
| from authentik.events.models import EventAction | from authentik.events.models import EventAction | ||||||
| @ -109,6 +110,7 @@ class ApplicationViewSet(ModelViewSet): | |||||||
|         serializer = self.get_serializer(allowed_applications, many=True) |         serializer = self.get_serializer(allowed_applications, many=True) | ||||||
|         return self.get_paginated_response(serializer.data) |         return self.get_paginated_response(serializer.data) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema(responses={200: CoordinateSerializer(many=True)}) | ||||||
|     @action(detail=True) |     @action(detail=True) | ||||||
|     def metrics(self, request: Request, slug: str): |     def metrics(self, request: Request, slug: str): | ||||||
|         """Metrics for application logins""" |         """Metrics for application logins""" | ||||||
|  | |||||||
| @ -28,9 +28,9 @@ class MetaNameSerializer(Serializer): | |||||||
| class TypeCreateSerializer(Serializer): | class TypeCreateSerializer(Serializer): | ||||||
|     """Types of an object that can be created""" |     """Types of an object that can be created""" | ||||||
|  |  | ||||||
|     name = CharField(read_only=True) |     name = CharField(required=True) | ||||||
|     description = CharField(read_only=True) |     description = CharField(required=True) | ||||||
|     link = CharField(read_only=True) |     link = CharField(required=True) | ||||||
|  |  | ||||||
|     def create(self, validated_data: dict) -> Model: |     def create(self, validated_data: dict) -> Model: | ||||||
|         raise NotImplementedError |         raise NotImplementedError | ||||||
|  | |||||||
| @ -16,6 +16,16 @@ | |||||||
|  |  | ||||||
| {% block body %} | {% block body %} | ||||||
| <div class="pf-c-background-image"> | <div class="pf-c-background-image"> | ||||||
|  |     <svg xmlns="http://www.w3.org/2000/svg" class="pf-c-background-image__filter" width="0" height="0"> | ||||||
|  |         <filter id="image_overlay"> | ||||||
|  |             <feComponentTransfer color-interpolation-filters="sRGB" result="duotone"> | ||||||
|  |                 <feFuncR type="table" tableValues="0.0086274509803922 0.63921568627451"></feFuncR> | ||||||
|  |                 <feFuncG type="table" tableValues="0.0086274509803922 0.63921568627451"></feFuncG> | ||||||
|  |                 <feFuncB type="table" tableValues="0.0086274509803922 0.63921568627451"></feFuncB> | ||||||
|  |                 <feFuncA type="table" tableValues="0 1"></feFuncA> | ||||||
|  |             </feComponentTransfer> | ||||||
|  |         </filter> | ||||||
|  |     </svg> | ||||||
| </div> | </div> | ||||||
| <ak-message-container></ak-message-container> | <ak-message-container></ak-message-container> | ||||||
| <div class="pf-c-login"> | <div class="pf-c-login"> | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ from django.contrib.auth.mixins import ( | |||||||
| ) | ) | ||||||
| from django.contrib.messages.views import SuccessMessageMixin | from django.contrib.messages.views import SuccessMessageMixin | ||||||
| from django.http.response import HttpResponse | from django.http.response import HttpResponse | ||||||
|  | from django.urls import reverse_lazy | ||||||
| from django.utils.translation import gettext as _ | from django.utils.translation import gettext as _ | ||||||
| from django.views.generic import UpdateView | from django.views.generic import UpdateView | ||||||
| from django.views.generic.base import TemplateView | from django.views.generic.base import TemplateView | ||||||
| @ -34,7 +35,7 @@ class UserDetailsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView): | |||||||
|     form_class = UserDetailForm |     form_class = UserDetailForm | ||||||
|  |  | ||||||
|     success_message = _("Successfully updated user.") |     success_message = _("Successfully updated user.") | ||||||
|     success_url = "/" |     success_url = reverse_lazy("authentik_core:user-details") | ||||||
|  |  | ||||||
|     def get_object(self): |     def get_object(self): | ||||||
|         return self.request.user |         return self.request.user | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ class NotificationSerializer(ModelSerializer): | |||||||
|  |  | ||||||
|     body = ReadOnlyField() |     body = ReadOnlyField() | ||||||
|     severity = ReadOnlyField() |     severity = ReadOnlyField() | ||||||
|     event = EventSerializer() |     event = EventSerializer(required=False) | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,11 +1,12 @@ | |||||||
| """NotificationTransport API Views""" | """NotificationTransport API Views""" | ||||||
| from django.http.response import Http404 | from django.http.response import Http404 | ||||||
|  | from drf_yasg2.utils import no_body, swagger_auto_schema | ||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.fields import SerializerMethodField | from rest_framework.fields import CharField, ListField, SerializerMethodField | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework.serializers import ModelSerializer | from rest_framework.serializers import ModelSerializer, Serializer | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.events.models import ( | from authentik.events.models import ( | ||||||
| @ -38,12 +39,28 @@ class NotificationTransportSerializer(ModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class NotificationTransportTestSerializer(Serializer): | ||||||
|  |     """Notification test serializer""" | ||||||
|  |  | ||||||
|  |     messages = ListField(child=CharField()) | ||||||
|  |  | ||||||
|  |     def create(self, request: Request) -> Response: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |     def update(self, request: Request) -> Response: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
| class NotificationTransportViewSet(ModelViewSet): | class NotificationTransportViewSet(ModelViewSet): | ||||||
|     """NotificationTransport Viewset""" |     """NotificationTransport Viewset""" | ||||||
|  |  | ||||||
|     queryset = NotificationTransport.objects.all() |     queryset = NotificationTransport.objects.all() | ||||||
|     serializer_class = NotificationTransportSerializer |     serializer_class = NotificationTransportSerializer | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema( | ||||||
|  |         responses={200: NotificationTransportTestSerializer(many=False)}, | ||||||
|  |         request_body=no_body, | ||||||
|  |     ) | ||||||
|     @action(detail=True, methods=["post"]) |     @action(detail=True, methods=["post"]) | ||||||
|     # pylint: disable=invalid-name |     # pylint: disable=invalid-name | ||||||
|     def test(self, request: Request, pk=None) -> Response: |     def test(self, request: Request, pk=None) -> Response: | ||||||
| @ -61,6 +78,10 @@ class NotificationTransportViewSet(ModelViewSet): | |||||||
|             user=request.user, |             user=request.user, | ||||||
|         ) |         ) | ||||||
|         try: |         try: | ||||||
|             return Response(transport.send(notification)) |             response = NotificationTransportTestSerializer( | ||||||
|  |                 data={"messages": transport.send(notification)} | ||||||
|  |             ) | ||||||
|  |             response.is_valid() | ||||||
|  |             return Response(response.data) | ||||||
|         except NotificationTransportError as exc: |         except NotificationTransportError as exc: | ||||||
|             return Response(str(exc.__cause__ or None), status=503) |             return Response(str(exc.__cause__ or None), status=503) | ||||||
|  | |||||||
| @ -38,7 +38,9 @@ class Challenge(Serializer): | |||||||
|     """Challenge that gets sent to the client based on which stage |     """Challenge that gets sent to the client based on which stage | ||||||
|     is currently active""" |     is currently active""" | ||||||
|  |  | ||||||
|     type = ChoiceField(choices=list(ChallengeTypes)) |     type = ChoiceField( | ||||||
|  |         choices=[(x.name, x.name) for x in ChallengeTypes], | ||||||
|  |     ) | ||||||
|     component = CharField(required=False) |     component = CharField(required=False) | ||||||
|     title = CharField(required=False) |     title = CharField(required=False) | ||||||
|  |  | ||||||
| @ -90,7 +92,7 @@ class ChallengeResponse(Serializer): | |||||||
|  |  | ||||||
|     stage: Optional["StageView"] |     stage: Optional["StageView"] | ||||||
|  |  | ||||||
|     def __init__(self, instance, data, **kwargs): |     def __init__(self, instance=None, data=None, **kwargs): | ||||||
|         self.stage = kwargs.pop("stage", None) |         self.stage = kwargs.pop("stage", None) | ||||||
|         super().__init__(instance=instance, data=data, **kwargs) |         super().__init__(instance=instance, data=data, **kwargs) | ||||||
|  |  | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ from django.http import HttpRequest | |||||||
| from django.http.request import QueryDict | from django.http.request import QueryDict | ||||||
| from django.http.response import HttpResponse | from django.http.response import HttpResponse | ||||||
| from django.views.generic.base import View | from django.views.generic.base import View | ||||||
|  | from rest_framework.request import Request | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.core.models import DEFAULT_AVATAR, User | from authentik.core.models import DEFAULT_AVATAR, User | ||||||
| @ -67,9 +68,9 @@ class ChallengeStageView(StageView): | |||||||
|         return HttpChallengeResponse(challenge) |         return HttpChallengeResponse(challenge) | ||||||
|  |  | ||||||
|     # pylint: disable=unused-argument |     # pylint: disable=unused-argument | ||||||
|     def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: |     def post(self, request: Request, *args, **kwargs) -> HttpResponse: | ||||||
|         """Handle challenge response""" |         """Handle challenge response""" | ||||||
|         challenge: ChallengeResponse = self.get_response_instance(data=request.POST) |         challenge: ChallengeResponse = self.get_response_instance(data=request.data) | ||||||
|         if not challenge.is_valid(): |         if not challenge.is_valid(): | ||||||
|             return self.challenge_invalid(challenge) |             return self.challenge_invalid(challenge) | ||||||
|         return self.challenge_valid(challenge) |         return self.challenge_valid(challenge) | ||||||
|  | |||||||
| @ -9,11 +9,16 @@ from django.template.response import TemplateResponse | |||||||
| from django.utils.decorators import method_decorator | from django.utils.decorators import method_decorator | ||||||
| from django.views.decorators.clickjacking import xframe_options_sameorigin | from django.views.decorators.clickjacking import xframe_options_sameorigin | ||||||
| from django.views.generic import TemplateView, View | from django.views.generic import TemplateView, View | ||||||
|  | from drf_yasg2.utils import no_body, swagger_auto_schema | ||||||
|  | from rest_framework.permissions import AllowAny | ||||||
|  | from rest_framework.views import APIView | ||||||
| from structlog.stdlib import BoundLogger, get_logger | from structlog.stdlib import BoundLogger, get_logger | ||||||
|  |  | ||||||
| from authentik.core.models import USER_ATTRIBUTE_DEBUG | from authentik.core.models import USER_ATTRIBUTE_DEBUG | ||||||
| from authentik.events.models import cleanse_dict | from authentik.events.models import cleanse_dict | ||||||
| from authentik.flows.challenge import ( | from authentik.flows.challenge import ( | ||||||
|  |     Challenge, | ||||||
|  |     ChallengeResponse, | ||||||
|     ChallengeTypes, |     ChallengeTypes, | ||||||
|     HttpChallengeResponse, |     HttpChallengeResponse, | ||||||
|     RedirectChallenge, |     RedirectChallenge, | ||||||
| @ -40,9 +45,11 @@ SESSION_KEY_GET = "authentik_flows_get" | |||||||
|  |  | ||||||
|  |  | ||||||
| @method_decorator(xframe_options_sameorigin, name="dispatch") | @method_decorator(xframe_options_sameorigin, name="dispatch") | ||||||
| class FlowExecutorView(View): | class FlowExecutorView(APIView): | ||||||
|     """Stage 1 Flow executor, passing requests to Stage Views""" |     """Stage 1 Flow executor, passing requests to Stage Views""" | ||||||
|  |  | ||||||
|  |     permission_classes = [AllowAny] | ||||||
|  |  | ||||||
|     flow: Flow |     flow: Flow | ||||||
|  |  | ||||||
|     plan: Optional[FlowPlan] = None |     plan: Optional[FlowPlan] = None | ||||||
| @ -113,8 +120,13 @@ class FlowExecutorView(View): | |||||||
|         self.current_stage_view.request = request |         self.current_stage_view.request = request | ||||||
|         return super().dispatch(request) |         return super().dispatch(request) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema( | ||||||
|  |         responses={200: Challenge()}, | ||||||
|  |         request_body=no_body, | ||||||
|  |         operation_id="flows_executor_get", | ||||||
|  |     ) | ||||||
|     def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: |     def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: | ||||||
|         """pass get request to current stage""" |         """Get the next pending challenge from the currently active flow.""" | ||||||
|         self._logger.debug( |         self._logger.debug( | ||||||
|             "f(exec): Passing GET", |             "f(exec): Passing GET", | ||||||
|             view_class=class_to_path(self.current_stage_view.__class__), |             view_class=class_to_path(self.current_stage_view.__class__), | ||||||
| @ -127,8 +139,13 @@ class FlowExecutorView(View): | |||||||
|             self._logger.exception(exc) |             self._logger.exception(exc) | ||||||
|             return to_stage_response(request, FlowErrorResponse(request, exc)) |             return to_stage_response(request, FlowErrorResponse(request, exc)) | ||||||
|  |  | ||||||
|  |     @swagger_auto_schema( | ||||||
|  |         responses={200: Challenge()}, | ||||||
|  |         request_body=ChallengeResponse(), | ||||||
|  |         operation_id="flows_executor_solve", | ||||||
|  |     ) | ||||||
|     def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: |     def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: | ||||||
|         """pass post request to current stage""" |         """Solve the previously retrieved challenge and advanced to the next stage.""" | ||||||
|         self._logger.debug( |         self._logger.debug( | ||||||
|             "f(exec): Passing POST", |             "f(exec): Passing POST", | ||||||
|             view_class=class_to_path(self.current_stage_view.__class__), |             view_class=class_to_path(self.current_stage_view.__class__), | ||||||
| @ -175,8 +192,10 @@ class FlowExecutorView(View): | |||||||
|                 "f(exec): Continuing with next stage", |                 "f(exec): Continuing with next stage", | ||||||
|                 reamining=len(self.plan.stages), |                 reamining=len(self.plan.stages), | ||||||
|             ) |             ) | ||||||
|  |             kwargs = self.kwargs | ||||||
|  |             kwargs.update({"flow_slug": self.flow.slug}) | ||||||
|             return redirect_with_qs( |             return redirect_with_qs( | ||||||
|                 "authentik_api:flow-executor", self.request.GET, **self.kwargs |                 "authentik_api:flow-executor", self.request.GET, **kwargs | ||||||
|             ) |             ) | ||||||
|         # User passed all stages |         # User passed all stages | ||||||
|         self._logger.debug( |         self._logger.debug( | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ class SAMLFlowFinalView(ChallengeStageView): | |||||||
|             return super().get( |             return super().get( | ||||||
|                 self.request, |                 self.request, | ||||||
|                 **{ |                 **{ | ||||||
|                     "type": ChallengeTypes.native, |                     "type": ChallengeTypes.native.value, | ||||||
|                     "component": "ak-stage-autosubmit", |                     "component": "ak-stage-autosubmit", | ||||||
|                     "title": "Redirecting to %(app)s..." % {"app": application.name}, |                     "title": "Redirecting to %(app)s..." % {"app": application.name}, | ||||||
|                     "url": provider.acs_url, |                     "url": provider.acs_url, | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ class AuthenticatorStaticStageView(ChallengeStageView): | |||||||
|         tokens: list[StaticToken] = self.request.session[SESSION_STATIC_TOKENS] |         tokens: list[StaticToken] = self.request.session[SESSION_STATIC_TOKENS] | ||||||
|         return AuthenticatorStaticChallenge( |         return AuthenticatorStaticChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-static", |                 "component": "ak-stage-authenticator-static", | ||||||
|                 "codes": [token.token for token in tokens], |                 "codes": [token.token for token in tokens], | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ class AuthenticatorTOTPStageView(ChallengeStageView): | |||||||
|         device: TOTPDevice = self.request.session[SESSION_TOTP_DEVICE] |         device: TOTPDevice = self.request.session[SESSION_TOTP_DEVICE] | ||||||
|         return AuthenticatorTOTPChallenge( |         return AuthenticatorTOTPChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-totp", |                 "component": "ak-stage-authenticator-totp", | ||||||
|                 "config_url": device.config_url, |                 "config_url": device.config_url, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -145,7 +145,7 @@ class AuthenticatorValidateStageView(ChallengeStageView): | |||||||
|         challenges = self.request.session["device_challenges"] |         challenges = self.request.session["device_challenges"] | ||||||
|         return AuthenticatorChallenge( |         return AuthenticatorChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-validate", |                 "component": "ak-stage-authenticator-validate", | ||||||
|                 "device_challenges": challenges, |                 "device_challenges": challenges, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -122,7 +122,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView): | |||||||
|  |  | ||||||
|         return AuthenticatorWebAuthnChallenge( |         return AuthenticatorWebAuthnChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-authenticator-webauthn", |                 "component": "ak-stage-authenticator-webauthn", | ||||||
|                 "registration": make_credential_options.registration_dict, |                 "registration": make_credential_options.registration_dict, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -63,7 +63,7 @@ class CaptchaStageView(ChallengeStageView): | |||||||
|     def get_challenge(self, *args, **kwargs) -> Challenge: |     def get_challenge(self, *args, **kwargs) -> Challenge: | ||||||
|         return CaptchaChallenge( |         return CaptchaChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-captcha", |                 "component": "ak-stage-captcha", | ||||||
|                 "site_key": self.executor.current_stage.public_key, |                 "site_key": self.executor.current_stage.public_key, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ class ConsentStageView(ChallengeStageView): | |||||||
|     def get_challenge(self) -> Challenge: |     def get_challenge(self) -> Challenge: | ||||||
|         challenge = ConsentChallenge( |         challenge = ConsentChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-consent", |                 "component": "ak-stage-consent", | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ class DummyStageView(ChallengeStageView): | |||||||
|     def get_challenge(self, *args, **kwargs) -> Challenge: |     def get_challenge(self, *args, **kwargs) -> Challenge: | ||||||
|         return DummyChallenge( |         return DummyChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "", |                 "component": "", | ||||||
|                 "title": self.executor.current_stage.name, |                 "title": self.executor.current_stage.name, | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -94,7 +94,7 @@ class EmailStageView(ChallengeStageView): | |||||||
|  |  | ||||||
|     def get_challenge(self) -> Challenge: |     def get_challenge(self) -> Challenge: | ||||||
|         challenge = EmailChallenge( |         challenge = EmailChallenge( | ||||||
|             data={"type": ChallengeTypes.native, "component": "ak-stage-email"} |             data={"type": ChallengeTypes.native.value, "component": "ak-stage-email"} | ||||||
|         ) |         ) | ||||||
|         return challenge |         return challenge | ||||||
|  |  | ||||||
|  | |||||||
| @ -78,7 +78,7 @@ class IdentificationStageView(ChallengeStageView): | |||||||
|         current_stage: IdentificationStage = self.executor.current_stage |         current_stage: IdentificationStage = self.executor.current_stage | ||||||
|         challenge = IdentificationChallenge( |         challenge = IdentificationChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-identification", |                 "component": "ak-stage-identification", | ||||||
|                 "primary_action": _("Log in"), |                 "primary_action": _("Log in"), | ||||||
|                 "input_type": "text", |                 "input_type": "text", | ||||||
|  | |||||||
| @ -78,7 +78,7 @@ class PasswordStageView(ChallengeStageView): | |||||||
|     def get_challenge(self) -> Challenge: |     def get_challenge(self) -> Challenge: | ||||||
|         challenge = PasswordChallenge( |         challenge = PasswordChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-password", |                 "component": "ak-stage-password", | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -164,7 +164,7 @@ class PromptStageView(ChallengeStageView): | |||||||
|         fields = list(self.executor.current_stage.fields.all().order_by("order")) |         fields = list(self.executor.current_stage.fields.all().order_by("order")) | ||||||
|         challenge = PromptChallenge( |         challenge = PromptChallenge( | ||||||
|             data={ |             data={ | ||||||
|                 "type": ChallengeTypes.native, |                 "type": ChallengeTypes.native.value, | ||||||
|                 "component": "ak-stage-prompt", |                 "component": "ak-stage-prompt", | ||||||
|                 "fields": [PromptSerializer(field).data for field in fields], |                 "fields": [PromptSerializer(field).data for field in fields], | ||||||
|             }, |             }, | ||||||
|  | |||||||
| @ -279,6 +279,7 @@ stages: | |||||||
|             displayName: Build static files for e2e |             displayName: Build static files for e2e | ||||||
|             inputs: |             inputs: | ||||||
|               script: | |               script: | | ||||||
|  |                 docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true | ||||||
|                 cd web |                 cd web | ||||||
|                 npm i |                 npm i | ||||||
|                 npm run build |                 npm run build | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ stages: | |||||||
|           - task: PublishPipelineArtifact@1 |           - task: PublishPipelineArtifact@1 | ||||||
|             inputs: |             inputs: | ||||||
|               targetPath: 'outpost/pkg/' |               targetPath: 'outpost/pkg/' | ||||||
|               artifact: 'swagger_client' |               artifact: 'go_swagger_client' | ||||||
|               publishLocation: 'pipeline' |               publishLocation: 'pipeline' | ||||||
|   - stage: lint |   - stage: lint | ||||||
|     jobs: |     jobs: | ||||||
| @ -51,7 +51,7 @@ stages: | |||||||
|           - task: DownloadPipelineArtifact@2 |           - task: DownloadPipelineArtifact@2 | ||||||
|             inputs: |             inputs: | ||||||
|               buildType: 'current' |               buildType: 'current' | ||||||
|               artifactName: 'swagger_client' |               artifactName: 'go_swagger_client' | ||||||
|               path: "outpost/pkg/" |               path: "outpost/pkg/" | ||||||
|           - task: CmdLine@2 |           - task: CmdLine@2 | ||||||
|             inputs: |             inputs: | ||||||
| @ -70,7 +70,7 @@ stages: | |||||||
|           - task: DownloadPipelineArtifact@2 |           - task: DownloadPipelineArtifact@2 | ||||||
|             inputs: |             inputs: | ||||||
|               buildType: 'current' |               buildType: 'current' | ||||||
|               artifactName: 'swagger_client' |               artifactName: 'go_swagger_client' | ||||||
|               path: "outpost/pkg/" |               path: "outpost/pkg/" | ||||||
|           - task: Go@0 |           - task: Go@0 | ||||||
|             inputs: |             inputs: | ||||||
| @ -89,7 +89,7 @@ stages: | |||||||
|           - task: DownloadPipelineArtifact@2 |           - task: DownloadPipelineArtifact@2 | ||||||
|             inputs: |             inputs: | ||||||
|               buildType: 'current' |               buildType: 'current' | ||||||
|               artifactName: 'swagger_client' |               artifactName: 'go_swagger_client' | ||||||
|               path: "outpost/pkg/" |               path: "outpost/pkg/" | ||||||
|           - task: Bash@3 |           - task: Bash@3 | ||||||
|             inputs: |             inputs: | ||||||
|  | |||||||
							
								
								
									
										165
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								swagger.yaml
									
									
									
									
									
								
							| @ -29,10 +29,7 @@ paths: | |||||||
|         '200': |         '200': | ||||||
|           description: Login Metrics per 1h |           description: Login Metrics per 1h | ||||||
|           schema: |           schema: | ||||||
|             description: '' |             $ref: '#/definitions/LoginMetrics' | ||||||
|             type: array |  | ||||||
|             items: |  | ||||||
|               $ref: '#/definitions/AdministrationMetrics' |  | ||||||
|       tags: |       tags: | ||||||
|         - admin |         - admin | ||||||
|     parameters: [] |     parameters: [] | ||||||
| @ -318,9 +315,12 @@ paths: | |||||||
|       parameters: [] |       parameters: [] | ||||||
|       responses: |       responses: | ||||||
|         '200': |         '200': | ||||||
|           description: '' |           description: Coordinates for diagrams | ||||||
|           schema: |           schema: | ||||||
|             $ref: '#/definitions/Application' |             description: '' | ||||||
|  |             type: array | ||||||
|  |             items: | ||||||
|  |               $ref: '#/definitions/Coordinate' | ||||||
|       tags: |       tags: | ||||||
|         - core |         - core | ||||||
|     parameters: |     parameters: | ||||||
| @ -1620,17 +1620,12 @@ paths: | |||||||
|       description: |- |       description: |- | ||||||
|         Send example notification using selected transport. Requires |         Send example notification using selected transport. Requires | ||||||
|         Modify permissions. |         Modify permissions. | ||||||
|       parameters: |       parameters: [] | ||||||
|         - name: data |  | ||||||
|           in: body |  | ||||||
|           required: true |  | ||||||
|           schema: |  | ||||||
|             $ref: '#/definitions/NotificationTransport' |  | ||||||
|       responses: |       responses: | ||||||
|         '201': |         '200': | ||||||
|           description: '' |           description: Notification test serializer | ||||||
|           schema: |           schema: | ||||||
|             $ref: '#/definitions/NotificationTransport' |             $ref: '#/definitions/NotificationTransportTest' | ||||||
|       tags: |       tags: | ||||||
|         - events |         - events | ||||||
|     parameters: |     parameters: | ||||||
| @ -1822,6 +1817,42 @@ paths: | |||||||
|         required: true |         required: true | ||||||
|         type: string |         type: string | ||||||
|         format: uuid |         format: uuid | ||||||
|  |   /flows/executor/{flow_slug}/: | ||||||
|  |     get: | ||||||
|  |       operationId: flows_executor_get | ||||||
|  |       description: Get the next pending challenge from the currently active flow. | ||||||
|  |       parameters: [] | ||||||
|  |       responses: | ||||||
|  |         '200': | ||||||
|  |           description: Challenge that gets sent to the client based on which stage | ||||||
|  |             is currently active | ||||||
|  |           schema: | ||||||
|  |             $ref: '#/definitions/Challenge' | ||||||
|  |       tags: | ||||||
|  |         - flows | ||||||
|  |     post: | ||||||
|  |       operationId: flows_executor_solve | ||||||
|  |       description: Solve the previously retrieved challenge and advanced to the next | ||||||
|  |         stage. | ||||||
|  |       parameters: | ||||||
|  |         - name: data | ||||||
|  |           in: body | ||||||
|  |           required: true | ||||||
|  |           schema: | ||||||
|  |             $ref: '#/definitions/ChallengeResponse' | ||||||
|  |       responses: | ||||||
|  |         '200': | ||||||
|  |           description: Challenge that gets sent to the client based on which stage | ||||||
|  |             is currently active | ||||||
|  |           schema: | ||||||
|  |             $ref: '#/definitions/Challenge' | ||||||
|  |       tags: | ||||||
|  |         - flows | ||||||
|  |     parameters: | ||||||
|  |       - name: flow_slug | ||||||
|  |         in: path | ||||||
|  |         required: true | ||||||
|  |         type: string | ||||||
|   /flows/instances/: |   /flows/instances/: | ||||||
|     get: |     get: | ||||||
|       operationId: flows_instances_list |       operationId: flows_instances_list | ||||||
| @ -9296,17 +9327,33 @@ paths: | |||||||
|         type: string |         type: string | ||||||
|         format: uuid |         format: uuid | ||||||
| definitions: | definitions: | ||||||
|   AdministrationMetrics: |   Coordinate: | ||||||
|  |     description: Coordinates for diagrams | ||||||
|  |     type: object | ||||||
|  |     properties: | ||||||
|  |       x_cord: | ||||||
|  |         title: X cord | ||||||
|  |         type: integer | ||||||
|  |         readOnly: true | ||||||
|  |       y_cord: | ||||||
|  |         title: Y cord | ||||||
|  |         type: integer | ||||||
|  |         readOnly: true | ||||||
|  |   LoginMetrics: | ||||||
|     description: Login Metrics per 1h |     description: Login Metrics per 1h | ||||||
|     type: object |     type: object | ||||||
|     properties: |     properties: | ||||||
|       logins_per_1h: |       logins_per_1h: | ||||||
|         title: Logins per 1h |         description: '' | ||||||
|         type: string |         type: array | ||||||
|  |         items: | ||||||
|  |           $ref: '#/definitions/Coordinate' | ||||||
|         readOnly: true |         readOnly: true | ||||||
|       logins_failed_per_1h: |       logins_failed_per_1h: | ||||||
|         title: Logins failed per 1h |         description: '' | ||||||
|         type: string |         type: array | ||||||
|  |         items: | ||||||
|  |           $ref: '#/definitions/Coordinate' | ||||||
|         readOnly: true |         readOnly: true | ||||||
|   Task: |   Task: | ||||||
|     description: Serialize TaskInfo and TaskResult |     description: Serialize TaskInfo and TaskResult | ||||||
| @ -9332,11 +9379,11 @@ definitions: | |||||||
|         format: date-time |         format: date-time | ||||||
|       status: |       status: | ||||||
|         title: Status |         title: Status | ||||||
|         type: integer |         type: string | ||||||
|         enum: |         enum: | ||||||
|           - 1 |           - SUCCESSFUL | ||||||
|           - 2 |           - WARNING | ||||||
|           - 4 |           - ERROR | ||||||
|       messages: |       messages: | ||||||
|         description: '' |         description: '' | ||||||
|         type: array |         type: array | ||||||
| @ -9728,8 +9775,6 @@ definitions: | |||||||
|         type: integer |         type: integer | ||||||
|   Notification: |   Notification: | ||||||
|     description: Notification Serializer |     description: Notification Serializer | ||||||
|     required: |  | ||||||
|       - event |  | ||||||
|     type: object |     type: object | ||||||
|     properties: |     properties: | ||||||
|       pk: |       pk: | ||||||
| @ -9897,6 +9942,18 @@ definitions: | |||||||
|       webhook_url: |       webhook_url: | ||||||
|         title: Webhook url |         title: Webhook url | ||||||
|         type: string |         type: string | ||||||
|  |   NotificationTransportTest: | ||||||
|  |     description: Notification test serializer | ||||||
|  |     required: | ||||||
|  |       - messages | ||||||
|  |     type: object | ||||||
|  |     properties: | ||||||
|  |       messages: | ||||||
|  |         description: '' | ||||||
|  |         type: array | ||||||
|  |         items: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|   Flow: |   Flow: | ||||||
|     description: Flow Serializer |     description: Flow Serializer | ||||||
|     required: |     required: | ||||||
| @ -10050,6 +10107,55 @@ definitions: | |||||||
|           format: uuid |           format: uuid | ||||||
|         readOnly: true |         readOnly: true | ||||||
|         uniqueItems: true |         uniqueItems: true | ||||||
|  |   ErrorDetail: | ||||||
|  |     description: Serializer for rest_framework's error messages | ||||||
|  |     required: | ||||||
|  |       - string | ||||||
|  |       - code | ||||||
|  |     type: object | ||||||
|  |     properties: | ||||||
|  |       string: | ||||||
|  |         title: String | ||||||
|  |         type: string | ||||||
|  |         minLength: 1 | ||||||
|  |       code: | ||||||
|  |         title: Code | ||||||
|  |         type: string | ||||||
|  |         minLength: 1 | ||||||
|  |   Challenge: | ||||||
|  |     description: Challenge that gets sent to the client based on which stage is currently | ||||||
|  |       active | ||||||
|  |     required: | ||||||
|  |       - type | ||||||
|  |     type: object | ||||||
|  |     properties: | ||||||
|  |       type: | ||||||
|  |         title: Type | ||||||
|  |         type: string | ||||||
|  |         enum: | ||||||
|  |           - native | ||||||
|  |           - shell | ||||||
|  |           - redirect | ||||||
|  |       component: | ||||||
|  |         title: Component | ||||||
|  |         type: string | ||||||
|  |         minLength: 1 | ||||||
|  |       title: | ||||||
|  |         title: Title | ||||||
|  |         type: string | ||||||
|  |         minLength: 1 | ||||||
|  |       response_errors: | ||||||
|  |         title: Response errors | ||||||
|  |         type: object | ||||||
|  |         additionalProperties: | ||||||
|  |           description: '' | ||||||
|  |           type: array | ||||||
|  |           items: | ||||||
|  |             $ref: '#/definitions/ErrorDetail' | ||||||
|  |   ChallengeResponse: | ||||||
|  |     description: Base class for all challenge responses | ||||||
|  |     type: object | ||||||
|  |     properties: {} | ||||||
|   Cache: |   Cache: | ||||||
|     description: Generic cache stats for an object |     description: Generic cache stats for an object | ||||||
|     type: object |     type: object | ||||||
| @ -10303,22 +10409,23 @@ definitions: | |||||||
|         readOnly: true |         readOnly: true | ||||||
|   TypeCreate: |   TypeCreate: | ||||||
|     description: Types of an object that can be created |     description: Types of an object that can be created | ||||||
|  |     required: | ||||||
|  |       - name | ||||||
|  |       - description | ||||||
|  |       - link | ||||||
|     type: object |     type: object | ||||||
|     properties: |     properties: | ||||||
|       name: |       name: | ||||||
|         title: Name |         title: Name | ||||||
|         type: string |         type: string | ||||||
|         readOnly: true |  | ||||||
|         minLength: 1 |         minLength: 1 | ||||||
|       description: |       description: | ||||||
|         title: Description |         title: Description | ||||||
|         type: string |         type: string | ||||||
|         readOnly: true |  | ||||||
|         minLength: 1 |         minLength: 1 | ||||||
|       link: |       link: | ||||||
|         title: Link |         title: Link | ||||||
|         type: string |         type: string | ||||||
|         readOnly: true |  | ||||||
|         minLength: 1 |         minLength: 1 | ||||||
|   ServiceConnectionState: |   ServiceConnectionState: | ||||||
|     description: Serializer for Service connection state |     description: Serializer for Service connection state | ||||||
|  | |||||||
| @ -4,3 +4,8 @@ node_modules | |||||||
| dist | dist | ||||||
| # don't lint nyc coverage output | # don't lint nyc coverage output | ||||||
| coverage | coverage | ||||||
|  | # don't lint generated code | ||||||
|  | src/api/apis | ||||||
|  | src/api/models | ||||||
|  | src/api/index.ts | ||||||
|  | src/api/runtime.ts | ||||||
|  | |||||||
| @ -10,6 +10,25 @@ variables: | |||||||
|     branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }} |     branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }} | ||||||
|  |  | ||||||
| stages: | stages: | ||||||
|  |   - stage: generate | ||||||
|  |     jobs: | ||||||
|  |       - job: swagger_generate | ||||||
|  |         pool: | ||||||
|  |           vmImage: 'ubuntu-latest' | ||||||
|  |         steps: | ||||||
|  |           - task: NodeTool@0 | ||||||
|  |             inputs: | ||||||
|  |               versionSpec: '12.x' | ||||||
|  |             displayName: 'Install Node.js' | ||||||
|  |           - task: CmdLine@2 | ||||||
|  |             inputs: | ||||||
|  |               script: | | ||||||
|  |                 docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true | ||||||
|  |           - task: PublishPipelineArtifact@1 | ||||||
|  |             inputs: | ||||||
|  |               targetPath: 'web/src/api/' | ||||||
|  |               artifact: 'ts_swagger_client' | ||||||
|  |               publishLocation: 'pipeline' | ||||||
|   - stage: lint |   - stage: lint | ||||||
|     jobs: |     jobs: | ||||||
|       - job: eslint |       - job: eslint | ||||||
| @ -20,6 +39,11 @@ stages: | |||||||
|             inputs: |             inputs: | ||||||
|               versionSpec: '12.x' |               versionSpec: '12.x' | ||||||
|             displayName: 'Install Node.js' |             displayName: 'Install Node.js' | ||||||
|  |           - task: DownloadPipelineArtifact@2 | ||||||
|  |             inputs: | ||||||
|  |               buildType: 'current' | ||||||
|  |               artifactName: 'ts_swagger_client' | ||||||
|  |               path: "web/src/api/" | ||||||
|           - task: Npm@1 |           - task: Npm@1 | ||||||
|             inputs: |             inputs: | ||||||
|               command: 'install' |               command: 'install' | ||||||
| @ -37,6 +61,11 @@ stages: | |||||||
|             inputs: |             inputs: | ||||||
|               versionSpec: '12.x' |               versionSpec: '12.x' | ||||||
|             displayName: 'Install Node.js' |             displayName: 'Install Node.js' | ||||||
|  |           - task: DownloadPipelineArtifact@2 | ||||||
|  |             inputs: | ||||||
|  |               buildType: 'current' | ||||||
|  |               artifactName: 'ts_swagger_client' | ||||||
|  |               path: "web/src/api/" | ||||||
|           - task: Npm@1 |           - task: Npm@1 | ||||||
|             inputs: |             inputs: | ||||||
|               command: 'install' |               command: 'install' | ||||||
| @ -56,6 +85,11 @@ stages: | |||||||
|             inputs: |             inputs: | ||||||
|               versionSpec: '12.x' |               versionSpec: '12.x' | ||||||
|             displayName: 'Install Node.js' |             displayName: 'Install Node.js' | ||||||
|  |           - task: DownloadPipelineArtifact@2 | ||||||
|  |             inputs: | ||||||
|  |               buildType: 'current' | ||||||
|  |               artifactName: 'ts_swagger_client' | ||||||
|  |               path: "web/src/api/" | ||||||
|           - task: Npm@1 |           - task: Npm@1 | ||||||
|             inputs: |             inputs: | ||||||
|               command: 'install' |               command: 'install' | ||||||
| @ -71,6 +105,11 @@ stages: | |||||||
|         pool: |         pool: | ||||||
|           vmImage: 'ubuntu-latest' |           vmImage: 'ubuntu-latest' | ||||||
|         steps: |         steps: | ||||||
|  |           - task: DownloadPipelineArtifact@2 | ||||||
|  |             inputs: | ||||||
|  |               buildType: 'current' | ||||||
|  |               artifactName: 'ts_swagger_client' | ||||||
|  |               path: "web/src/api/" | ||||||
|           - task: Bash@3 |           - task: Bash@3 | ||||||
|             inputs: |             inputs: | ||||||
|               targetType: 'inline' |               targetType: 'inline' | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								web/src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/src/api/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | apis/** | ||||||
|  | models/** | ||||||
|  | index.ts | ||||||
|  | runtime.ts | ||||||
							
								
								
									
										23
									
								
								web/src/api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/api/.openapi-generator-ignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | # OpenAPI Generator Ignore | ||||||
|  | # Generated by openapi-generator https://github.com/openapitools/openapi-generator | ||||||
|  |  | ||||||
|  | # Use this file to prevent files from being overwritten by the generator. | ||||||
|  | # The patterns follow closely to .gitignore or .dockerignore. | ||||||
|  |  | ||||||
|  | # As an example, the C# client generator defines ApiClient.cs. | ||||||
|  | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: | ||||||
|  | #ApiClient.cs | ||||||
|  |  | ||||||
|  | # You can match any string of characters against a directory, file or extension with a single asterisk (*): | ||||||
|  | #foo/*/qux | ||||||
|  | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux | ||||||
|  |  | ||||||
|  | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): | ||||||
|  | #foo/**/qux | ||||||
|  | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux | ||||||
|  |  | ||||||
|  | # You can also negate patterns with an exclamation (!). | ||||||
|  | # For example, you can ignore all files in a docs folder with the file extension .md: | ||||||
|  | #docs/*.md | ||||||
|  | # Then explicitly reverse the ignore rule for a single file: | ||||||
|  | #!docs/README.md | ||||||
							
								
								
									
										167
									
								
								web/src/api/.openapi-generator/FILES
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								web/src/api/.openapi-generator/FILES
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,167 @@ | |||||||
|  | apis/AdminApi.ts | ||||||
|  | apis/CoreApi.ts | ||||||
|  | apis/CryptoApi.ts | ||||||
|  | apis/EventsApi.ts | ||||||
|  | apis/FlowsApi.ts | ||||||
|  | apis/OutpostsApi.ts | ||||||
|  | apis/PoliciesApi.ts | ||||||
|  | apis/PropertymappingsApi.ts | ||||||
|  | apis/ProvidersApi.ts | ||||||
|  | apis/RootApi.ts | ||||||
|  | apis/SourcesApi.ts | ||||||
|  | apis/StagesApi.ts | ||||||
|  | apis/index.ts | ||||||
|  | index.ts | ||||||
|  | models/Application.ts | ||||||
|  | models/AuthenticateWebAuthnStage.ts | ||||||
|  | models/AuthenticatorStaticStage.ts | ||||||
|  | models/AuthenticatorTOTPStage.ts | ||||||
|  | models/AuthenticatorValidateStage.ts | ||||||
|  | models/Cache.ts | ||||||
|  | models/CaptchaStage.ts | ||||||
|  | models/CertificateData.ts | ||||||
|  | models/CertificateKeyPair.ts | ||||||
|  | models/Challenge.ts | ||||||
|  | models/Config.ts | ||||||
|  | models/ConsentStage.ts | ||||||
|  | models/Coordinate.ts | ||||||
|  | models/DenyStage.ts | ||||||
|  | models/DockerServiceConnection.ts | ||||||
|  | models/DummyPolicy.ts | ||||||
|  | models/DummyStage.ts | ||||||
|  | models/EmailStage.ts | ||||||
|  | models/ErrorDetail.ts | ||||||
|  | models/Event.ts | ||||||
|  | models/EventMatcherPolicy.ts | ||||||
|  | models/EventTopPerUser.ts | ||||||
|  | models/ExpressionPolicy.ts | ||||||
|  | models/Flow.ts | ||||||
|  | models/FlowDiagram.ts | ||||||
|  | models/FlowStageBinding.ts | ||||||
|  | models/Group.ts | ||||||
|  | models/GroupMembershipPolicy.ts | ||||||
|  | models/HaveIBeenPwendPolicy.ts | ||||||
|  | models/IPReputation.ts | ||||||
|  | models/IdentificationStage.ts | ||||||
|  | models/InlineResponse200.ts | ||||||
|  | models/InlineResponse2001.ts | ||||||
|  | models/InlineResponse20010.ts | ||||||
|  | models/InlineResponse20011.ts | ||||||
|  | models/InlineResponse20012.ts | ||||||
|  | models/InlineResponse20013.ts | ||||||
|  | models/InlineResponse20014.ts | ||||||
|  | models/InlineResponse20015.ts | ||||||
|  | models/InlineResponse20016.ts | ||||||
|  | models/InlineResponse20017.ts | ||||||
|  | models/InlineResponse20018.ts | ||||||
|  | models/InlineResponse20019.ts | ||||||
|  | models/InlineResponse2002.ts | ||||||
|  | models/InlineResponse20020.ts | ||||||
|  | models/InlineResponse20021.ts | ||||||
|  | models/InlineResponse20022.ts | ||||||
|  | models/InlineResponse20023.ts | ||||||
|  | models/InlineResponse20024.ts | ||||||
|  | models/InlineResponse20025.ts | ||||||
|  | models/InlineResponse20026.ts | ||||||
|  | models/InlineResponse20027.ts | ||||||
|  | models/InlineResponse20028.ts | ||||||
|  | models/InlineResponse20029.ts | ||||||
|  | models/InlineResponse2003.ts | ||||||
|  | models/InlineResponse20030.ts | ||||||
|  | models/InlineResponse20031.ts | ||||||
|  | models/InlineResponse20032.ts | ||||||
|  | models/InlineResponse20033.ts | ||||||
|  | models/InlineResponse20034.ts | ||||||
|  | models/InlineResponse20035.ts | ||||||
|  | models/InlineResponse20036.ts | ||||||
|  | models/InlineResponse20037.ts | ||||||
|  | models/InlineResponse20038.ts | ||||||
|  | models/InlineResponse20039.ts | ||||||
|  | models/InlineResponse2004.ts | ||||||
|  | models/InlineResponse20040.ts | ||||||
|  | models/InlineResponse20041.ts | ||||||
|  | models/InlineResponse20042.ts | ||||||
|  | models/InlineResponse20043.ts | ||||||
|  | models/InlineResponse20044.ts | ||||||
|  | models/InlineResponse20045.ts | ||||||
|  | models/InlineResponse20046.ts | ||||||
|  | models/InlineResponse20047.ts | ||||||
|  | models/InlineResponse20048.ts | ||||||
|  | models/InlineResponse20049.ts | ||||||
|  | models/InlineResponse2005.ts | ||||||
|  | models/InlineResponse20050.ts | ||||||
|  | models/InlineResponse20051.ts | ||||||
|  | models/InlineResponse20052.ts | ||||||
|  | models/InlineResponse20053.ts | ||||||
|  | models/InlineResponse20054.ts | ||||||
|  | models/InlineResponse20055.ts | ||||||
|  | models/InlineResponse20056.ts | ||||||
|  | models/InlineResponse20057.ts | ||||||
|  | models/InlineResponse20058.ts | ||||||
|  | models/InlineResponse20059.ts | ||||||
|  | models/InlineResponse2006.ts | ||||||
|  | models/InlineResponse20060.ts | ||||||
|  | models/InlineResponse2007.ts | ||||||
|  | models/InlineResponse2008.ts | ||||||
|  | models/InlineResponse2009.ts | ||||||
|  | models/InlineResponse200Pagination.ts | ||||||
|  | models/Invitation.ts | ||||||
|  | models/InvitationStage.ts | ||||||
|  | models/KubernetesServiceConnection.ts | ||||||
|  | models/LDAPPropertyMapping.ts | ||||||
|  | models/LDAPSource.ts | ||||||
|  | models/LDAPSourceSyncStatus.ts | ||||||
|  | models/LoginMetrics.ts | ||||||
|  | models/Notification.ts | ||||||
|  | models/NotificationRule.ts | ||||||
|  | models/NotificationRuleGroup.ts | ||||||
|  | models/NotificationRuleGroupParent.ts | ||||||
|  | models/NotificationRuleTransports.ts | ||||||
|  | models/NotificationTransport.ts | ||||||
|  | models/NotificationTransportTest.ts | ||||||
|  | models/OAuth2Provider.ts | ||||||
|  | models/OAuth2ProviderSetupURLs.ts | ||||||
|  | models/OAuthSource.ts | ||||||
|  | models/OpenIDConnectConfiguration.ts | ||||||
|  | models/Outpost.ts | ||||||
|  | models/OutpostHealth.ts | ||||||
|  | models/PasswordExpiryPolicy.ts | ||||||
|  | models/PasswordPolicy.ts | ||||||
|  | models/PasswordStage.ts | ||||||
|  | models/Policy.ts | ||||||
|  | models/PolicyBinding.ts | ||||||
|  | models/PolicyBindingPolicy.ts | ||||||
|  | models/PolicyBindingUser.ts | ||||||
|  | models/PolicyBindingUserAkGroups.ts | ||||||
|  | models/PolicyBindingUserGroups.ts | ||||||
|  | models/PolicyBindingUserSources.ts | ||||||
|  | models/PolicyBindingUserUserPermissions.ts | ||||||
|  | models/Prompt.ts | ||||||
|  | models/PromptStage.ts | ||||||
|  | models/PropertyMapping.ts | ||||||
|  | models/Provider.ts | ||||||
|  | models/ProxyOutpostConfig.ts | ||||||
|  | models/ProxyProvider.ts | ||||||
|  | models/ReputationPolicy.ts | ||||||
|  | models/SAMLMetadata.ts | ||||||
|  | models/SAMLPropertyMapping.ts | ||||||
|  | models/SAMLProvider.ts | ||||||
|  | models/SAMLSource.ts | ||||||
|  | models/ScopeMapping.ts | ||||||
|  | models/ServiceConnection.ts | ||||||
|  | models/ServiceConnectionState.ts | ||||||
|  | models/Source.ts | ||||||
|  | models/Stage.ts | ||||||
|  | models/Task.ts | ||||||
|  | models/Token.ts | ||||||
|  | models/TokenView.ts | ||||||
|  | models/TypeCreate.ts | ||||||
|  | models/User.ts | ||||||
|  | models/UserDeleteStage.ts | ||||||
|  | models/UserLoginStage.ts | ||||||
|  | models/UserLogoutStage.ts | ||||||
|  | models/UserReputation.ts | ||||||
|  | models/UserWriteStage.ts | ||||||
|  | models/Version.ts | ||||||
|  | models/index.ts | ||||||
|  | runtime.ts | ||||||
							
								
								
									
										1
									
								
								web/src/api/.openapi-generator/VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/src/api/.openapi-generator/VERSION
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | 5.1.0-SNAPSHOT | ||||||
| @ -1,32 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { Provider } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class Application { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     slug: string; |  | ||||||
|     provider?: Provider; |  | ||||||
|  |  | ||||||
|     launch_url: string; |  | ||||||
|     meta_launch_url: string; |  | ||||||
|     meta_icon: string; |  | ||||||
|     meta_description: string; |  | ||||||
|     meta_publisher: string; |  | ||||||
|     policies: string[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Application> { |  | ||||||
|         return DefaultClient.fetch<Application>(["core", "applications", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Application>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Application>>(["core", "applications"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/applications/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
|  |  | ||||||
| export class CertificateKeyPair { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     fingerprint: string; |  | ||||||
|     cert_expiry: number; |  | ||||||
|     cert_subject: string; |  | ||||||
|     private_key_available: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<CertificateKeyPair> { |  | ||||||
|         return DefaultClient.fetch<CertificateKeyPair>(["crypto", "certificatekeypairs", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<CertificateKeyPair>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<CertificateKeyPair>>(["crypto", "certificatekeypairs"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/crypto/certificates/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,10 +1,3 @@ | |||||||
| import { gettext } from "django"; |  | ||||||
| import { showMessage } from "../elements/messages/MessageContainer"; |  | ||||||
| import { getCookie } from "../utils"; |  | ||||||
| import { NotFoundError, RequestError } from "./Error"; |  | ||||||
|  |  | ||||||
| export const VERSION = "v2beta"; |  | ||||||
|  |  | ||||||
| export interface QueryArguments { | export interface QueryArguments { | ||||||
|     page?: number; |     page?: number; | ||||||
|     page_size?: number; |     page_size?: number; | ||||||
| @ -20,97 +13,20 @@ export interface BaseInheritanceModel { | |||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export class Client { | export interface AKPagination { | ||||||
|     makeUrl(url: string[], query?: QueryArguments): string { |  | ||||||
|         let builtUrl = `/api/${VERSION}/${url.join("/")}/`; |  | ||||||
|         if (query) { |  | ||||||
|             const queryString = Object.keys(query) |  | ||||||
|                 .filter((k) => query[k] !== null) |  | ||||||
|                 // we default to a string in query[k] as we've filtered out the null above |  | ||||||
|                 // this is just for type-hinting |  | ||||||
|                 .map((k) => encodeURIComponent(k) + "=" + encodeURIComponent(query[k] || "")) |  | ||||||
|                 .join("&"); |  | ||||||
|             builtUrl += `?${queryString}`; |  | ||||||
|         } |  | ||||||
|         return builtUrl; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fetch<T>(url: string[], query?: QueryArguments): Promise<T> { |  | ||||||
|         const finalUrl = this.makeUrl(url, query); |  | ||||||
|         return fetch(finalUrl) |  | ||||||
|             .then((r) => { |  | ||||||
|                 if (r.status > 300) { |  | ||||||
|                     switch (r.status) { |  | ||||||
|                     case 404: |  | ||||||
|                         throw new NotFoundError(`URL ${finalUrl} not found`); |  | ||||||
|                     default: |  | ||||||
|                         throw new RequestError(r.statusText); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 return r; |  | ||||||
|             }) |  | ||||||
|             .catch((e) => { |  | ||||||
|                 showMessage({ |  | ||||||
|                     level_tag: "error", |  | ||||||
|                     message: gettext(`Unexpected error while fetching: ${e.toString()}`), |  | ||||||
|                 }); |  | ||||||
|                 return e; |  | ||||||
|             }) |  | ||||||
|             .then((r) => r.json()) |  | ||||||
|             .then((r) => <T>r); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private writeRequest<T>(url: string[], body: T, method: string, query?: QueryArguments): Promise<T> { |  | ||||||
|         const finalUrl = this.makeUrl(url, query); |  | ||||||
|         const csrftoken = getCookie("authentik_csrf"); |  | ||||||
|         const request = new Request(finalUrl, { |  | ||||||
|             headers: { |  | ||||||
|                 "Accept": "application/json", |  | ||||||
|                 "Content-Type": "application/json", |  | ||||||
|                 "X-CSRFToken": csrftoken, |  | ||||||
|             }, |  | ||||||
|         }); |  | ||||||
|         return fetch(request, { |  | ||||||
|             method: method, |  | ||||||
|             mode: "same-origin", |  | ||||||
|             body: JSON.stringify(body), |  | ||||||
|         }) |  | ||||||
|             .then((r) => { |  | ||||||
|                 if (r.status > 300) { |  | ||||||
|                     switch (r.status) { |  | ||||||
|                     case 404: |  | ||||||
|                         throw new NotFoundError(`URL ${finalUrl} not found`); |  | ||||||
|                     default: |  | ||||||
|                         throw new RequestError(r.statusText); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 return r; |  | ||||||
|             }) |  | ||||||
|             .then((r) => r.json()) |  | ||||||
|             .then((r) => <T>r); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     update<T>(url: string[], body: T, query?: QueryArguments): Promise<T> { |  | ||||||
|         return this.writeRequest(url, body, "PATCH", query); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const DefaultClient = new Client(); |  | ||||||
|  |  | ||||||
| export interface PBPagination { |  | ||||||
|     next?: number; |     next?: number; | ||||||
|     previous?: number; |     previous?: number; | ||||||
|  |  | ||||||
|     count: number; |     count: number; | ||||||
|     current: number; |     current: number; | ||||||
|     total_pages: number; |     totalPages: number; | ||||||
|  |  | ||||||
|     start_index: number; |     startIndex: number; | ||||||
|     end_index: number; |     endIndex: number; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface AKResponse<T> { | export interface AKResponse<T> { | ||||||
|     pagination: PBPagination; |     pagination: AKPagination; | ||||||
|  |  | ||||||
|     results: Array<T>; |     results: Array<T>; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,24 +1,22 @@ | |||||||
| import { DefaultClient } from "./Client"; |  | ||||||
| import * as Sentry from "@sentry/browser"; | import * as Sentry from "@sentry/browser"; | ||||||
| import { Integrations } from "@sentry/tracing"; | import { Integrations } from "@sentry/tracing"; | ||||||
| import { VERSION } from "../constants"; | import { VERSION } from "../constants"; | ||||||
| import { SentryIgnoredError } from "../common/errors"; | import { SentryIgnoredError } from "../common/errors"; | ||||||
|  | import { Configuration } from "./runtime"; | ||||||
|  | import { RootApi } from "./apis"; | ||||||
|  | import { Config } from "."; | ||||||
|  | import { getCookie } from "../utils"; | ||||||
|  |  | ||||||
| export class Config { | export const DEFAULT_CONFIG = new Configuration({ | ||||||
|     branding_logo: string; |     basePath: "/api/v2beta", | ||||||
|     branding_title: string; |     headers: { | ||||||
|  |         "X-CSRFToken": getCookie("authentik_csrf"), | ||||||
|     error_reporting_enabled: boolean; |  | ||||||
|     error_reporting_environment: string; |  | ||||||
|     error_reporting_send_pii: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |     } | ||||||
|  | }); | ||||||
|  |  | ||||||
|     static get(): Promise<Config> { | export function configureSentry(): Promise<Config> { | ||||||
|         return DefaultClient.fetch<Config>(["root", "config"]).then((config) => { |     return new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => { | ||||||
|             if (config.error_reporting_enabled) { |         if (config.errorReportingEnabled) { | ||||||
|             Sentry.init({ |             Sentry.init({ | ||||||
|                 dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", |                 dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", | ||||||
|                 release: `authentik@${VERSION}`, |                 release: `authentik@${VERSION}`, | ||||||
| @ -26,7 +24,7 @@ export class Config { | |||||||
|                     new Integrations.BrowserTracing(), |                     new Integrations.BrowserTracing(), | ||||||
|                 ], |                 ], | ||||||
|                 tracesSampleRate: 0.6, |                 tracesSampleRate: 0.6, | ||||||
|                     environment: config.error_reporting_environment, |                 environment: config.errorReportingEnvironment, | ||||||
|                 beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { |                 beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { | ||||||
|                     if (hint.originalException instanceof SentryIgnoredError) { |                     if (hint.originalException instanceof SentryIgnoredError) { | ||||||
|                         return null; |                         return null; | ||||||
| @ -39,4 +37,3 @@ export class Config { | |||||||
|         return config; |         return config; | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,30 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { Event } from "./Events"; |  | ||||||
|  |  | ||||||
| export class Notification { |  | ||||||
|     pk: string; |  | ||||||
|     severity: string; |  | ||||||
|     body: string; |  | ||||||
|     created: string; |  | ||||||
|     event?: Event; |  | ||||||
|     seen: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Notification> { |  | ||||||
|         return DefaultClient.fetch<Notification>(["events", "notifications", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Notification>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Notification>>(["events", "notifications"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static markSeen(pk: string): Promise<{seen: boolean}> { |  | ||||||
|         return DefaultClient.update(["events", "notifications", pk], { |  | ||||||
|             "seen": true |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { Group } from "./Groups"; |  | ||||||
|  |  | ||||||
| export class Rule { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     transports: string[]; |  | ||||||
|     severity: string; |  | ||||||
|     group?: Group; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Rule> { |  | ||||||
|         return DefaultClient.fetch<Rule>(["events", "rules", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Rule>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Rule>>(["events", "rules"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/events/rules/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,25 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
|  |  | ||||||
| export class Transport { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     mode: string; |  | ||||||
|     mode_verbose: string; |  | ||||||
|     webhook_url: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Transport> { |  | ||||||
|         return DefaultClient.fetch<Transport>(["events", "transports", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Transport>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Transport>>(["events", "transports"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/events/transports/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | import { Event } from "./models"; | ||||||
|  |  | ||||||
| export interface EventUser { | export interface EventUser { | ||||||
|     pk: number; |     pk: number; | ||||||
| @ -11,37 +11,7 @@ export interface EventContext { | |||||||
|     [key: string]: EventContext | string | number | string[]; |     [key: string]: EventContext | string | number | string[]; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class Event { | export interface EventWithContext extends Event { | ||||||
|     pk: string; |  | ||||||
|     user: EventUser; |     user: EventUser; | ||||||
|     action: string; |  | ||||||
|     app: string; |  | ||||||
|     context: EventContext; |     context: EventContext; | ||||||
|     client_ip: string; |  | ||||||
|     created: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Event> { |  | ||||||
|         return DefaultClient.fetch<Event>(["events", "events", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Event>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Event>>(["events", "events"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // events/events/top_per_user/?filter_action=authorize_application |  | ||||||
|     static topForUser(action: string): Promise<TopNEvent[]> { |  | ||||||
|         return DefaultClient.fetch<TopNEvent[]>(["events", "events", "top_per_user"], { |  | ||||||
|             "filter_action": action, |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface TopNEvent { |  | ||||||
|     application: { [key: string]: string}; |  | ||||||
|     counted_events: number; |  | ||||||
|     unique_users: number; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,12 +1,4 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments, BaseInheritanceModel } from "./Client"; | import { ChallengeTypeEnum } from "./models"; | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export enum ChallengeTypes { |  | ||||||
|     native = "native", |  | ||||||
|     response = "response", |  | ||||||
|     shell = "shell", |  | ||||||
|     redirect = "redirect", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface Error { | export interface Error { | ||||||
|     code: string; |     code: string; | ||||||
| @ -18,11 +10,12 @@ export interface ErrorDict { | |||||||
| } | } | ||||||
|  |  | ||||||
| export interface Challenge { | export interface Challenge { | ||||||
|     type: ChallengeTypes; |     type: ChallengeTypeEnum; | ||||||
|     component?: string; |     component?: string; | ||||||
|     title?: string; |     title?: string; | ||||||
|     response_errors?: ErrorDict; |     response_errors?: ErrorDict; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface WithUserInfoChallenge extends Challenge { | export interface WithUserInfoChallenge extends Challenge { | ||||||
|     pending_user: string; |     pending_user: string; | ||||||
|     pending_user_avatar: string; |     pending_user_avatar: string; | ||||||
| @ -31,6 +24,7 @@ export interface WithUserInfoChallenge extends Challenge { | |||||||
| export interface ShellChallenge extends Challenge { | export interface ShellChallenge extends Challenge { | ||||||
|     body: string; |     body: string; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface RedirectChallenge extends Challenge { | export interface RedirectChallenge extends Challenge { | ||||||
|     to: string; |     to: string; | ||||||
| } | } | ||||||
| @ -44,104 +38,3 @@ export enum FlowDesignation { | |||||||
|     Recovery = "recovery", |     Recovery = "recovery", | ||||||
|     StageConfiguration = "stage_configuration", |     StageConfiguration = "stage_configuration", | ||||||
| } | } | ||||||
|  |  | ||||||
| export class Flow { |  | ||||||
|     pk: string; |  | ||||||
|     policybindingmodel_ptr_id: string; |  | ||||||
|     name: string; |  | ||||||
|     slug: string; |  | ||||||
|     title: string; |  | ||||||
|     designation: FlowDesignation; |  | ||||||
|     background: string; |  | ||||||
|     stages: string[]; |  | ||||||
|     policies: string[]; |  | ||||||
|     cache_count: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Flow> { |  | ||||||
|         return DefaultClient.fetch<Flow>(["flows", "instances", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static diagram(slug: string): Promise<{ diagram: string }> { |  | ||||||
|         return DefaultClient.fetch<{ diagram: string }>(["flows", "instances", slug, "diagram"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Flow>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Flow>>(["flows", "instances"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static cached(): Promise<number> { |  | ||||||
|         return DefaultClient.fetch<{ count: number }>(["flows", "instances", "cached"]).then(r => { |  | ||||||
|             return r.count; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static executor(slug: string): Promise<Challenge> { |  | ||||||
|         return DefaultClient.fetch(["flows", "executor", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/flows/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Stage implements BaseInheritanceModel { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|     flow_set: Flow[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Stage> { |  | ||||||
|         return DefaultClient.fetch<Stage>(["stages", "all", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Stage>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Stage>>(["stages", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["stages", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class FlowStageBinding { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     policybindingmodel_ptr_id: string; |  | ||||||
|     target: string; |  | ||||||
|     stage: string; |  | ||||||
|     stage_obj: Stage; |  | ||||||
|     evaluate_on_plan: boolean; |  | ||||||
|     re_evaluate_policies: boolean; |  | ||||||
|     order: number; |  | ||||||
|     policies: string[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<FlowStageBinding> { |  | ||||||
|         return DefaultClient.fetch<FlowStageBinding>(["flows", "bindings", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<FlowStageBinding>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<FlowStageBinding>>(["flows", "bindings"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages/bindings/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,28 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { EventContext } from "./Events"; |  | ||||||
|  |  | ||||||
| export class Group { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     is_superuser: boolean; |  | ||||||
|     attributes: EventContext; |  | ||||||
|     parent?: Group; |  | ||||||
|     users: number[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Group> { |  | ||||||
|         return DefaultClient.fetch<Group>(["core", "groups", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Group>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Group>>(["core", "groups"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/groups/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,27 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { EventContext } from "./Events"; |  | ||||||
| import { User } from "./Users"; |  | ||||||
|  |  | ||||||
| export class Invitation { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     expires: number; |  | ||||||
|     fixed_date: EventContext; |  | ||||||
|     created_by: User; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Invitation> { |  | ||||||
|         return DefaultClient.fetch<Invitation>(["stages", "invitation", "invitations", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Invitation>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Invitation>>(["stages", "invitation", "invitations"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages/invitations/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,79 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { Provider, TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export interface OutpostHealth { |  | ||||||
|     last_seen: number; |  | ||||||
|     version: string; |  | ||||||
|     version_should: string; |  | ||||||
|     version_outdated: boolean; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Outpost { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     providers: number[]; |  | ||||||
|     providers_obj: Provider[]; |  | ||||||
|     service_connection?: string; |  | ||||||
|     _config: QueryArguments; |  | ||||||
|     token_identifier: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Outpost> { |  | ||||||
|         return DefaultClient.fetch<Outpost>(["outposts", "outposts", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Outpost>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Outpost>>(["outposts", "outposts"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static health(pk: string): Promise<OutpostHealth[]> { |  | ||||||
|         return DefaultClient.fetch<OutpostHealth[]>(["outposts", "outposts", pk, "health"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/outposts/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface OutpostServiceConnectionState { |  | ||||||
|     version: string; |  | ||||||
|     healthy: boolean; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class OutpostServiceConnection { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     local: boolean; |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<OutpostServiceConnection> { |  | ||||||
|         return DefaultClient.fetch<OutpostServiceConnection>(["outposts", "service_connections", "all", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<OutpostServiceConnection>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<OutpostServiceConnection>>(["outposts", "service_connections", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static state(pk: string): Promise<OutpostServiceConnectionState> { |  | ||||||
|         return DefaultClient.fetch<OutpostServiceConnectionState>(["outposts", "service_connections", "all", pk, "state"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["outposts", "service_connections", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/outpost_service_connections/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| import { DefaultClient, BaseInheritanceModel, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class Policy implements BaseInheritanceModel { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     execution_logging: boolean; |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|     bound_to: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Policy> { |  | ||||||
|         return DefaultClient.fetch<Policy>(["policies", "all", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Policy>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Policy>>(["policies", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static cached(): Promise<number> { |  | ||||||
|         return DefaultClient.fetch<{ count: number }>(["policies", "all", "cached"]).then(r => { |  | ||||||
|             return r.count; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["policies", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/policies/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,31 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { Group } from "./Groups"; |  | ||||||
| import { Policy } from "./Policies"; |  | ||||||
| import { User } from "./Users"; |  | ||||||
|  |  | ||||||
| export class PolicyBinding { |  | ||||||
|     pk: string; |  | ||||||
|     policy?: Policy; |  | ||||||
|     group?: Group; |  | ||||||
|     user?: User; |  | ||||||
|     target: string; |  | ||||||
|     enabled: boolean; |  | ||||||
|     order: number; |  | ||||||
|     timeout: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<PolicyBinding> { |  | ||||||
|         return DefaultClient.fetch<PolicyBinding>(["policies", "bindings", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<PolicyBinding>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<PolicyBinding>>(["policies", "bindings"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/policies/bindings/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,30 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments, AKResponse } from "./Client"; |  | ||||||
| import { Stage } from "./Flows"; |  | ||||||
|  |  | ||||||
| export class Prompt { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     field_key: string; |  | ||||||
|     label: string; |  | ||||||
|     type: string; |  | ||||||
|     required: boolean; |  | ||||||
|     placeholder: string; |  | ||||||
|     order: number; |  | ||||||
|     promptstage_set: Stage[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<Prompt> { |  | ||||||
|         return DefaultClient.fetch<Prompt>(["stages", "prompt", "prompts", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Prompt>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Prompt>>(["stages", "prompt", "prompts"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/stages_prompts/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,31 +0,0 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class PropertyMapping { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     expression: string; |  | ||||||
|  |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<PropertyMapping> { |  | ||||||
|         return DefaultClient.fetch<PropertyMapping>(["propertymappings", "all", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<PropertyMapping>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<PropertyMapping>>(["propertymappings", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["propertymappings", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/property-mappings/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,40 +0,0 @@ | |||||||
| import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
|  |  | ||||||
| export interface TypeCreate { |  | ||||||
|     name: string; |  | ||||||
|     description: string; |  | ||||||
|     link: string; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Provider implements BaseInheritanceModel { |  | ||||||
|     pk: number; |  | ||||||
|     name: string; |  | ||||||
|     authorization_flow: string; |  | ||||||
|     object_type: string; |  | ||||||
|  |  | ||||||
|     assigned_application_slug?: string; |  | ||||||
|     assigned_application_name?: string; |  | ||||||
|  |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(id: number): Promise<Provider> { |  | ||||||
|         return DefaultClient.fetch<Provider>(["providers", "all", id.toString()]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Provider>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Provider>>(["providers", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["providers", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/providers/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,34 +0,0 @@ | |||||||
| import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; |  | ||||||
| import { TypeCreate } from "./Providers"; |  | ||||||
|  |  | ||||||
| export class Source implements BaseInheritanceModel { |  | ||||||
|     pk: string; |  | ||||||
|     name: string; |  | ||||||
|     slug: string; |  | ||||||
|     enabled: boolean; |  | ||||||
|     authentication_flow: string; |  | ||||||
|     enrollment_flow: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|     object_type: string; |  | ||||||
|     verbose_name: string; |  | ||||||
|     verbose_name_plural: string; |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<Source> { |  | ||||||
|         return DefaultClient.fetch<Source>(["sources", "all", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Source>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Source>>(["sources", "all"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getTypes(): Promise<TypeCreate[]> { |  | ||||||
|         return DefaultClient.fetch<TypeCreate[]>(["sources", "all", "types"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/sources/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| import { DefaultClient, QueryArguments } from "./Client"; |  | ||||||
|  |  | ||||||
| export enum TaskStatus { |  | ||||||
|     SUCCESSFUL = 1, |  | ||||||
|     WARNING = 2, |  | ||||||
|     ERROR = 4, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class SystemTask { |  | ||||||
|  |  | ||||||
|     task_name: string; |  | ||||||
|     task_description: string; |  | ||||||
|     task_finish_timestamp: number; |  | ||||||
|     status: TaskStatus; |  | ||||||
|     messages: string[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(task_name: string): Promise<SystemTask> { |  | ||||||
|         return DefaultClient.fetch<SystemTask>(["admin", "system_tasks", task_name]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<SystemTask[]> { |  | ||||||
|         return DefaultClient.fetch<SystemTask[]>(["admin", "system_tasks"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static retry(task_name: string): string { |  | ||||||
|         return DefaultClient.makeUrl(["admin", "system_tasks", task_name, "retry"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,47 +0,0 @@ | |||||||
| import { AKResponse, DefaultClient, QueryArguments } from "./Client"; |  | ||||||
| import { User } from "./Users"; |  | ||||||
|  |  | ||||||
| export enum TokenIntent { |  | ||||||
|     INTENT_VERIFICATION = "verification", |  | ||||||
|     INTENT_API = "api", |  | ||||||
|     INTENT_RECOVERY = "recovery", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class Token { |  | ||||||
|  |  | ||||||
|     pk: string; |  | ||||||
|     identifier: string; |  | ||||||
|     intent: TokenIntent; |  | ||||||
|     user: User; |  | ||||||
|     description: string; |  | ||||||
|  |  | ||||||
|     expires: number; |  | ||||||
|     expiring: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<User> { |  | ||||||
|         return DefaultClient.fetch<User>(["core", "tokens", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<Token>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<Token>>(["core", "tokens"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/tokens/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static userUrl(rest: string): string { |  | ||||||
|         return `/-/user/tokens/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getKey(identifier: string): Promise<string> { |  | ||||||
|         return DefaultClient.fetch<{ key: string }>(["core", "tokens", identifier, "view_key"]).then( |  | ||||||
|             (r) => r.key |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,45 +1,11 @@ | |||||||
| import { DefaultClient, AKResponse, QueryArguments } from "./Client"; | import { CoreApi } from "./apis"; | ||||||
|  | import { DEFAULT_CONFIG } from "./Config"; | ||||||
|  | import { User } from "./models"; | ||||||
|  |  | ||||||
| let _globalMePromise: Promise<User>; | let _globalMePromise: Promise<User>; | ||||||
|  | export function me(): Promise<User> { | ||||||
| export class User { |  | ||||||
|     pk: number; |  | ||||||
|     username: string; |  | ||||||
|     name: string; |  | ||||||
|     is_superuser: boolean; |  | ||||||
|     email: boolean; |  | ||||||
|     avatar: string; |  | ||||||
|     is_active: boolean; |  | ||||||
|     last_login: number; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(pk: string): Promise<User> { |  | ||||||
|         return DefaultClient.fetch<User>(["core", "users", pk]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static list(filter?: QueryArguments): Promise<AKResponse<User>> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<User>>(["core", "users"], filter); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static adminUrl(rest: string): string { |  | ||||||
|         return `/administration/users/${rest}`; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static me(): Promise<User> { |  | ||||||
|     if (!_globalMePromise) { |     if (!_globalMePromise) { | ||||||
|             _globalMePromise = DefaultClient.fetch<User>(["core", "users", "me"]); |         _globalMePromise = new CoreApi(DEFAULT_CONFIG).coreUsersMe({}); | ||||||
|     } |     } | ||||||
|     return _globalMePromise; |     return _globalMePromise; | ||||||
| } | } | ||||||
|  |  | ||||||
|     static count(): Promise<number> { |  | ||||||
|         return DefaultClient.fetch<AKResponse<User>>(["core", "users"], { |  | ||||||
|             "page_size": 1 |  | ||||||
|         }).then(r => { |  | ||||||
|             return r.pagination.count; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,17 +0,0 @@ | |||||||
| import { DefaultClient } from "./Client"; |  | ||||||
|  |  | ||||||
| export class Version { |  | ||||||
|  |  | ||||||
|     version_current: string; |  | ||||||
|     version_latest: string; |  | ||||||
|     outdated: boolean; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(): Promise<Version> { |  | ||||||
|         return DefaultClient.fetch<Version>(["admin", "version"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										97
									
								
								web/src/api/legacy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								web/src/api/legacy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | |||||||
|  | export class AdminURLManager { | ||||||
|  |  | ||||||
|  |     static applications(rest: string): string { | ||||||
|  |         return `/administration/applications/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static cryptoCertificates(rest: string): string { | ||||||
|  |         return `/administration/crypto/certificates/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static policies(rest: string): string { | ||||||
|  |         return `/administration/policies/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static policyBindings(rest: string): string { | ||||||
|  |         return `/administration/policies/bindings/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static providers(rest: string): string { | ||||||
|  |         return `/administration/providers/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static propertyMappings(rest: string): string { | ||||||
|  |         return `/administration/property-mappings/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static outposts(rest: string): string { | ||||||
|  |         return `/administration/outposts/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static outpostServiceConnections(rest: string): string { | ||||||
|  |         return `/administration/outpost_service_connections/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static flows(rest: string): string { | ||||||
|  |         return `/administration/flows/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stages(rest: string): string { | ||||||
|  |         return `/administration/stages/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stagePrompts(rest: string): string { | ||||||
|  |         return `/administration/stages_prompts/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stageInvitations(rest: string): string { | ||||||
|  |         return `/administration/stages/invitations/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static stageBindings(rest: string): string { | ||||||
|  |         return `/administration/stages/bindings/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static sources(rest: string): string { | ||||||
|  |         return `/administration/sources/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static tokens(rest: string): string { | ||||||
|  |         return `/administration/tokens/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static eventRules(rest: string): string { | ||||||
|  |         return `/administration/events/rules/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static eventTransports(rest: string): string { | ||||||
|  |         return `/administration/events/transports/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static users(rest: string): string { | ||||||
|  |         return `/administration/users/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static groups(rest: string): string { | ||||||
|  |         return `/administration/groups/${rest}`; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class UserURLManager { | ||||||
|  |  | ||||||
|  |     static tokens(rest: string): string { | ||||||
|  |         return `/-/user/tokens/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class AppURLManager { | ||||||
|  |  | ||||||
|  |     static sourceSAML(slug: string, rest: string): string { | ||||||
|  |         return `/source/saml/${slug}/${rest}`; | ||||||
|  |     } | ||||||
|  |     static providerSAML(rest: string): string { | ||||||
|  |         return `/application/saml/${rest}`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -1,43 +0,0 @@ | |||||||
| import { DefaultClient } from "../Client"; |  | ||||||
| import { Provider } from "../Providers"; |  | ||||||
|  |  | ||||||
| export interface OAuth2SetupURLs { |  | ||||||
|  |  | ||||||
|     issuer?: string; |  | ||||||
|     authorize: string; |  | ||||||
|     token: string; |  | ||||||
|     user_info: string; |  | ||||||
|     provider_info?: string; |  | ||||||
|     logout?: string; |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class OAuth2Provider extends Provider { |  | ||||||
|     client_type: string |  | ||||||
|     client_id: string; |  | ||||||
|     client_secret: string; |  | ||||||
|     token_validity: string; |  | ||||||
|     include_claims_in_id_token: boolean; |  | ||||||
|     jwt_alg: string; |  | ||||||
|     rsa_key: string; |  | ||||||
|     redirect_uris: string; |  | ||||||
|     sub_mode: string; |  | ||||||
|     issuer_mode: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(id: number): Promise<OAuth2Provider> { |  | ||||||
|         return DefaultClient.fetch<OAuth2Provider>(["providers", "oauth2", id.toString()]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getLaunchURls(id: number): Promise<OAuth2SetupURLs> { |  | ||||||
|         return DefaultClient.fetch(["providers", "oauth2", id.toString(), "setup_urls"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static appUrl(rest: string): string { |  | ||||||
|         return `/application/oauth2/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,30 +0,0 @@ | |||||||
| import { DefaultClient } from "../Client"; |  | ||||||
| import { Provider } from "../Providers"; |  | ||||||
|  |  | ||||||
| export class ProxyProvider extends Provider { |  | ||||||
|     internal_host: string; |  | ||||||
|     external_host: string; |  | ||||||
|     internal_host_ssl_validation: boolean |  | ||||||
|     certificate?: string; |  | ||||||
|     skip_path_regex: string; |  | ||||||
|     basic_auth_enabled: boolean; |  | ||||||
|     basic_auth_password_attribute: string; |  | ||||||
|     basic_auth_user_attribute: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(id: number): Promise<ProxyProvider> { |  | ||||||
|         return DefaultClient.fetch<ProxyProvider>(["providers", "proxy", id.toString()]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getMetadata(id: number): Promise<{ metadata: string }> { |  | ||||||
|         return DefaultClient.fetch(["providers", "proxy", id.toString(), "metadata"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static appUrl(rest: string): string { |  | ||||||
|         return `/application/proxy/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| import { DefaultClient } from "../Client"; |  | ||||||
| import { Provider } from "../Providers"; |  | ||||||
|  |  | ||||||
| export class SAMLProvider extends Provider { |  | ||||||
|     acs_url: string; |  | ||||||
|     audience: string; |  | ||||||
|     issuer: string; |  | ||||||
|     assertion_valid_not_before: string; |  | ||||||
|     assertion_valid_not_on_or_after: string; |  | ||||||
|     session_valid_not_on_or_after: string; |  | ||||||
|     name_id_mapping?: string; |  | ||||||
|     digest_algorithm: string; |  | ||||||
|     signature_algorithm: string; |  | ||||||
|     signing_kp?: string; |  | ||||||
|     verification_kp?: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(id: number): Promise<SAMLProvider> { |  | ||||||
|         return DefaultClient.fetch<SAMLProvider>(["providers", "saml", id.toString()]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getMetadata(id: number): Promise<{ metadata: string }> { |  | ||||||
|         return DefaultClient.fetch(["providers", "saml", id.toString(), "metadata"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static appUrl(rest: string): string { |  | ||||||
|         return `/application/saml/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,35 +0,0 @@ | |||||||
| import { DefaultClient } from "../Client"; |  | ||||||
| import { Source } from "../Sources"; |  | ||||||
|  |  | ||||||
| export class LDAPSource extends Source { |  | ||||||
|     server_uri: string; |  | ||||||
|     bind_cn: string; |  | ||||||
|     start_tls: boolean |  | ||||||
|     base_dn: string; |  | ||||||
|     additional_user_dn: string; |  | ||||||
|     additional_group_dn: string; |  | ||||||
|     user_object_filter: string; |  | ||||||
|     group_object_filter: string; |  | ||||||
|     group_membership_field: string; |  | ||||||
|     object_uniqueness_field: string; |  | ||||||
|     sync_users: boolean; |  | ||||||
|     sync_users_password: boolean; |  | ||||||
|     sync_groups: boolean; |  | ||||||
|     sync_parent_group?: string; |  | ||||||
|     property_mappings: string[]; |  | ||||||
|     property_mappings_group: string[]; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<LDAPSource> { |  | ||||||
|         return DefaultClient.fetch<LDAPSource>(["sources", "ldap", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static syncStatus(slug: string): Promise<{ last_sync?: number }> { |  | ||||||
|         return DefaultClient.fetch(["sources", "ldap", slug, "sync_status"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,22 +0,0 @@ | |||||||
| import { DefaultClient } from "../Client"; |  | ||||||
| import { Source } from "../Sources"; |  | ||||||
|  |  | ||||||
| export class OAuthSource extends Source { |  | ||||||
|     provider_type: string; |  | ||||||
|     request_token_url: string; |  | ||||||
|     authorization_url: string; |  | ||||||
|     access_token_url: string; |  | ||||||
|     profile_url: string; |  | ||||||
|     consumer_key: string; |  | ||||||
|     callback_url: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<OAuthSource> { |  | ||||||
|         return DefaultClient.fetch<OAuthSource>(["sources", "oauth", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,32 +0,0 @@ | |||||||
| import { DefaultClient } from "../Client"; |  | ||||||
| import { Source } from "../Sources"; |  | ||||||
|  |  | ||||||
| export class SAMLSource extends Source { |  | ||||||
|     issuer: string; |  | ||||||
|     sso_url: string; |  | ||||||
|     slo_url: string; |  | ||||||
|     allow_idp_initiated: boolean; |  | ||||||
|     name_id_policy: string; |  | ||||||
|     binding_type: string |  | ||||||
|     signing_kp?: string; |  | ||||||
|     digest_algorithm: string; |  | ||||||
|     signature_algorithm: string; |  | ||||||
|     temporary_user_delete_after: string; |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         throw Error(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get(slug: string): Promise<SAMLSource> { |  | ||||||
|         return DefaultClient.fetch<SAMLSource>(["sources", "saml", slug]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static getMetadata(slug: string): Promise<{ metadata: string }> { |  | ||||||
|         return DefaultClient.fetch(["sources", "saml", slug, "metadata"]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static appUrl(slug: string, rest: string): string { |  | ||||||
|         return `/source/saml/${slug}/${rest}`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -157,7 +157,7 @@ ak-message { | |||||||
|         color: var(--ak-dark-foreground) !important; |         color: var(--ak-dark-foreground) !important; | ||||||
|     } |     } | ||||||
|     /* tabs, vertical */ |     /* tabs, vertical */ | ||||||
|     .pf-c-tabs__link { |     .pf-c-tabs.pf-m-vertical .pf-c-tabs__link { | ||||||
|         background-color: var(--ak-dark-background-light); |         background-color: var(--ak-dark-background-light); | ||||||
|     } |     } | ||||||
|     /* table, on mobile */ |     /* table, on mobile */ | ||||||
|  | |||||||
| @ -1,119 +0,0 @@ | |||||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; |  | ||||||
| import Chart from "chart.js"; |  | ||||||
| import { DefaultClient } from "../api/Client"; |  | ||||||
|  |  | ||||||
| interface TickValue { |  | ||||||
|     value: number; |  | ||||||
|     major: boolean; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface LoginMetrics { |  | ||||||
|     logins_failed_per_1h: { x: number, y: number }[]; |  | ||||||
|     logins_per_1h: { x: number, y: number }[]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @customElement("ak-admin-logins-chart") |  | ||||||
| export class AdminLoginsChart extends LitElement { |  | ||||||
|     @property({type: Array}) |  | ||||||
|     url: string[] = []; |  | ||||||
|  |  | ||||||
|     chart?: Chart; |  | ||||||
|  |  | ||||||
|     static get styles(): CSSResult[] { |  | ||||||
|         return [css` |  | ||||||
|             :host { |  | ||||||
|                 position: relative; |  | ||||||
|                 height: 100%; |  | ||||||
|                 width: 100%; |  | ||||||
|                 display: block; |  | ||||||
|                 min-height: 25rem; |  | ||||||
|             } |  | ||||||
|             canvas { |  | ||||||
|                 width: 100px; |  | ||||||
|                 height: 100px; |  | ||||||
|             } |  | ||||||
|         `]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     constructor() { |  | ||||||
|         super(); |  | ||||||
|         window.addEventListener("resize", () => { |  | ||||||
|             if (this.chart) { |  | ||||||
|                 this.chart.resize(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     firstUpdated(): void { |  | ||||||
|         DefaultClient.fetch<LoginMetrics>(this.url) |  | ||||||
|             .then((r) => { |  | ||||||
|                 const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas"); |  | ||||||
|                 if (!canvas) { |  | ||||||
|                     console.warn("Failed to get canvas element"); |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|                 const ctx = canvas.getContext("2d"); |  | ||||||
|                 if (!ctx) { |  | ||||||
|                     console.warn("failed to get 2d context"); |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|                 this.chart = new Chart(ctx, { |  | ||||||
|                     type: "bar", |  | ||||||
|                     data: { |  | ||||||
|                         datasets: [ |  | ||||||
|                             { |  | ||||||
|                                 label: "Failed Logins", |  | ||||||
|                                 backgroundColor: "rgba(201, 25, 11, .5)", |  | ||||||
|                                 spanGaps: true, |  | ||||||
|                                 data: r.logins_failed_per_1h, |  | ||||||
|                             }, |  | ||||||
|                             { |  | ||||||
|                                 label: "Successful Logins", |  | ||||||
|                                 backgroundColor: "rgba(189, 229, 184, .5)", |  | ||||||
|                                 spanGaps: true, |  | ||||||
|                                 data: r.logins_per_1h, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                     }, |  | ||||||
|                     options: { |  | ||||||
|                         maintainAspectRatio: false, |  | ||||||
|                         spanGaps: true, |  | ||||||
|                         scales: { |  | ||||||
|                             xAxes: [ |  | ||||||
|                                 { |  | ||||||
|                                     stacked: true, |  | ||||||
|                                     gridLines: { |  | ||||||
|                                         color: "rgba(0, 0, 0, 0)", |  | ||||||
|                                     }, |  | ||||||
|                                     type: "time", |  | ||||||
|                                     offset: true, |  | ||||||
|                                     ticks: { |  | ||||||
|                                         callback: function (value, index: number, values) { |  | ||||||
|                                             const valueStamp = <TickValue>(<unknown>values[index]); |  | ||||||
|                                             const delta = Date.now() - valueStamp.value; |  | ||||||
|                                             const ago = Math.round(delta / 1000 / 3600); |  | ||||||
|                                             return `${ago} Hours ago`; |  | ||||||
|                                         }, |  | ||||||
|                                         autoSkip: true, |  | ||||||
|                                         maxTicksLimit: 8, |  | ||||||
|                                     }, |  | ||||||
|                                 }, |  | ||||||
|                             ], |  | ||||||
|                             yAxes: [ |  | ||||||
|                                 { |  | ||||||
|                                     stacked: true, |  | ||||||
|                                     gridLines: { |  | ||||||
|                                         color: "rgba(0, 0, 0, 0)", |  | ||||||
|                                     }, |  | ||||||
|                                 }, |  | ||||||
|                             ], |  | ||||||
|                         }, |  | ||||||
|                     }, |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     render(): TemplateResult { |  | ||||||
|         return html`<canvas></canvas>`; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,4 +1,3 @@ | |||||||
| import { getCookie } from "../../utils"; |  | ||||||
| import { customElement, property } from "lit-element"; | import { customElement, property } from "lit-element"; | ||||||
| import { ERROR_CLASS, SUCCESS_CLASS } from "../../constants"; | import { ERROR_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||||
| import { SpinnerButton } from "./SpinnerButton"; | import { SpinnerButton } from "./SpinnerButton"; | ||||||
| @ -12,26 +11,16 @@ export class ActionButton extends SpinnerButton { | |||||||
|     @property() |     @property() | ||||||
|     method = "POST"; |     method = "POST"; | ||||||
|  |  | ||||||
|  |     @property({attribute: false}) | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||||
|  |     apiRequest: () => Promise<any> = () => { throw new Error(); }; | ||||||
|  |  | ||||||
|     callAction(): void { |     callAction(): void { | ||||||
|         if (this.isRunning === true) { |         if (this.isRunning === true) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         this.setLoading(); |         this.setLoading(); | ||||||
|         const csrftoken = getCookie("authentik_csrf"); |         this.apiRequest().then(() => { | ||||||
|         const request = new Request(this.url, { |  | ||||||
|             headers: { "X-CSRFToken": csrftoken }, |  | ||||||
|         }); |  | ||||||
|         fetch(request, { |  | ||||||
|             method: this.method, |  | ||||||
|             mode: "same-origin", |  | ||||||
|         }) |  | ||||||
|             .then((r) => { |  | ||||||
|                 if (!r.ok) { |  | ||||||
|                     throw r; |  | ||||||
|                 } |  | ||||||
|                 return r; |  | ||||||
|             }) |  | ||||||
|             .then(() => { |  | ||||||
|             this.setDone(SUCCESS_CLASS); |             this.setDone(SUCCESS_CLASS); | ||||||
|         }) |         }) | ||||||
|         .catch((e: Error | Response) => { |         .catch((e: Error | Response) => { | ||||||
|  | |||||||
| @ -3,9 +3,10 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu | |||||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||||
| import { Token } from "../../api/Tokens"; | import { CoreApi } from "../../api"; | ||||||
| import { ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | import { ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||||
| import { ColorStyles } from "../../common/styles"; | import { ColorStyles } from "../../common/styles"; | ||||||
|  | import { DEFAULT_CONFIG } from "../../api/Config"; | ||||||
|  |  | ||||||
| @customElement("ak-token-copy-button") | @customElement("ak-token-copy-button") | ||||||
| export class TokenCopyButton extends LitElement { | export class TokenCopyButton extends LitElement { | ||||||
| @ -36,8 +37,14 @@ export class TokenCopyButton extends LitElement { | |||||||
|             }, 1500); |             }, 1500); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         Token.getKey(this.identifier).then((token) => { |         new CoreApi(DEFAULT_CONFIG).coreTokensViewKey({ | ||||||
|             navigator.clipboard.writeText(token).then(() => { |             identifier: this.identifier | ||||||
|  |         }).then((token) => { | ||||||
|  |             if (!token.key) { | ||||||
|  |                 this.buttonClass = ERROR_CLASS; | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             navigator.clipboard.writeText(token.key).then(() => { | ||||||
|                 this.buttonClass = SUCCESS_CLASS; |                 this.buttonClass = SUCCESS_CLASS; | ||||||
|                 setTimeout(() => { |                 setTimeout(() => { | ||||||
|                     this.buttonClass = PRIMARY_CLASS; |                     this.buttonClass = PRIMARY_CLASS; | ||||||
|  | |||||||
							
								
								
									
										41
									
								
								web/src/elements/charts/AdminLoginsChart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								web/src/elements/charts/AdminLoginsChart.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | import { customElement } from "lit-element"; | ||||||
|  | import Chart from "chart.js"; | ||||||
|  | import { AdminApi, LoginMetrics } from "../../api"; | ||||||
|  | import { AKChart } from "./Chart"; | ||||||
|  | import { DEFAULT_CONFIG } from "../../api/Config"; | ||||||
|  |  | ||||||
|  | @customElement("ak-charts-admin-login") | ||||||
|  | export class AdminLoginsChart extends AKChart<LoginMetrics> { | ||||||
|  |  | ||||||
|  |     apiRequest(): Promise<LoginMetrics> { | ||||||
|  |         return new AdminApi(DEFAULT_CONFIG).adminMetricsList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     getDatasets(data: LoginMetrics): Chart.ChartDataSets[] { | ||||||
|  |         return [ | ||||||
|  |             { | ||||||
|  |                 label: "Failed Logins", | ||||||
|  |                 backgroundColor: "rgba(201, 25, 11, .5)", | ||||||
|  |                 spanGaps: true, | ||||||
|  |                 data: data.loginsFailedPer1h?.map((cord) => { | ||||||
|  |                     return { | ||||||
|  |                         x: cord.xCord, | ||||||
|  |                         y: cord.yCord, | ||||||
|  |                     }; | ||||||
|  |                 }), | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |                 label: "Successful Logins", | ||||||
|  |                 backgroundColor: "rgba(189, 229, 184, .5)", | ||||||
|  |                 spanGaps: true, | ||||||
|  |                 data: data.loginsPer1h?.map((cord) => { | ||||||
|  |                     return { | ||||||
|  |                         x: cord.xCord, | ||||||
|  |                         y: cord.yCord, | ||||||
|  |                     }; | ||||||
|  |                 }), | ||||||
|  |             }, | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								web/src/elements/charts/ApplicationAuthorizeChart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/src/elements/charts/ApplicationAuthorizeChart.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | import { customElement, property } from "lit-element"; | ||||||
|  | import { Coordinate, CoreApi } from "../../api"; | ||||||
|  | import { DEFAULT_CONFIG } from "../../api/Config"; | ||||||
|  | import { AKChart } from "./Chart"; | ||||||
|  |  | ||||||
|  | @customElement("ak-charts-application-authorize") | ||||||
|  | export class ApplicationAuthorizeChart extends AKChart<Coordinate[]> { | ||||||
|  |  | ||||||
|  |     @property() | ||||||
|  |     applicationSlug!: string; | ||||||
|  |  | ||||||
|  |     apiRequest(): Promise<Coordinate[]> { | ||||||
|  |         return new CoreApi(DEFAULT_CONFIG).coreApplicationsMetrics({ slug: this.applicationSlug }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     getDatasets(data: Coordinate[]): Chart.ChartDataSets[] { | ||||||
|  |         return [ | ||||||
|  |             { | ||||||
|  |                 label: "Authorizations", | ||||||
|  |                 backgroundColor: "rgba(189, 229, 184, .5)", | ||||||
|  |                 spanGaps: true, | ||||||
|  |                 data: data.map((cord) => { | ||||||
|  |                     return { | ||||||
|  |                         x: cord.xCord, | ||||||
|  |                         y: cord.yCord, | ||||||
|  |                     }; | ||||||
|  |                 }), | ||||||
|  |             }, | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										103
									
								
								web/src/elements/charts/Chart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								web/src/elements/charts/Chart.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | |||||||
|  | import { css, CSSResult, html, LitElement, TemplateResult } from "lit-element"; | ||||||
|  | import Chart from "chart.js"; | ||||||
|  |  | ||||||
|  | interface TickValue { | ||||||
|  |     value: number; | ||||||
|  |     major: boolean; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export abstract class AKChart<T> extends LitElement { | ||||||
|  |  | ||||||
|  |     abstract apiRequest(): Promise<T>; | ||||||
|  |     abstract getDatasets(data: T): Chart.ChartDataSets[]; | ||||||
|  |  | ||||||
|  |     chart?: Chart; | ||||||
|  |  | ||||||
|  |     static get styles(): CSSResult[] { | ||||||
|  |         return [css` | ||||||
|  |             :host { | ||||||
|  |                 position: relative; | ||||||
|  |                 height: 100%; | ||||||
|  |                 width: 100%; | ||||||
|  |                 display: block; | ||||||
|  |                 min-height: 25rem; | ||||||
|  |             } | ||||||
|  |             canvas { | ||||||
|  |                 width: 100px; | ||||||
|  |                 height: 100px; | ||||||
|  |             } | ||||||
|  |         `]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         super(); | ||||||
|  |         window.addEventListener("resize", () => { | ||||||
|  |             if (this.chart) { | ||||||
|  |                 this.chart.resize(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     configureChart(data: T, ctx: CanvasRenderingContext2D): Chart { | ||||||
|  |         return new Chart(ctx, { | ||||||
|  |             type: "bar", | ||||||
|  |             data: { | ||||||
|  |                 datasets: this.getDatasets(data), | ||||||
|  |             }, | ||||||
|  |             options: { | ||||||
|  |                 maintainAspectRatio: false, | ||||||
|  |                 spanGaps: true, | ||||||
|  |                 scales: { | ||||||
|  |                     xAxes: [ | ||||||
|  |                         { | ||||||
|  |                             stacked: true, | ||||||
|  |                             gridLines: { | ||||||
|  |                                 color: "rgba(0, 0, 0, 0)", | ||||||
|  |                             }, | ||||||
|  |                             type: "time", | ||||||
|  |                             offset: true, | ||||||
|  |                             ticks: { | ||||||
|  |                                 callback: function (value, index: number, values) { | ||||||
|  |                                     const valueStamp = <TickValue>(<unknown>values[index]); | ||||||
|  |                                     const delta = Date.now() - valueStamp.value; | ||||||
|  |                                     const ago = Math.round(delta / 1000 / 3600); | ||||||
|  |                                     return `${ago} Hours ago`; | ||||||
|  |                                 }, | ||||||
|  |                                 autoSkip: true, | ||||||
|  |                                 maxTicksLimit: 8, | ||||||
|  |                             }, | ||||||
|  |                         }, | ||||||
|  |                     ], | ||||||
|  |                     yAxes: [ | ||||||
|  |                         { | ||||||
|  |                             stacked: true, | ||||||
|  |                             gridLines: { | ||||||
|  |                                 color: "rgba(0, 0, 0, 0)", | ||||||
|  |                             }, | ||||||
|  |                         }, | ||||||
|  |                     ], | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     firstUpdated(): void { | ||||||
|  |         this.apiRequest().then((r) => { | ||||||
|  |             const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas"); | ||||||
|  |             if (!canvas) { | ||||||
|  |                 console.warn("Failed to get canvas element"); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             const ctx = canvas.getContext("2d"); | ||||||
|  |             if (!ctx) { | ||||||
|  |                 console.warn("failed to get 2d context"); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             this.chart = this.configureChart(r, ctx); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     render(): TemplateResult { | ||||||
|  |         return html`<canvas></canvas>`; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -35,6 +35,7 @@ export class MessageContainer extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     connect(): void { |     connect(): void { | ||||||
|  |         if (navigator.webdriver) return; | ||||||
|         const wsUrl = `${window.location.protocol.replace("http", "ws")}//${ |         const wsUrl = `${window.location.protocol.replace("http", "ws")}//${ | ||||||
|             window.location.host |             window.location.host | ||||||
|         }/ws/client/`; |         }/ws/client/`; | ||||||
|  | |||||||
| @ -1,7 +1,8 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||||
|  | import { EventsApi, Notification } from "../../api"; | ||||||
| import { AKResponse } from "../../api/Client"; | import { AKResponse } from "../../api/Client"; | ||||||
| import { Notification } from "../../api/EventNotification"; | import { DEFAULT_CONFIG } from "../../api/Config"; | ||||||
| import { COMMON_STYLES } from "../../common/styles"; | import { COMMON_STYLES } from "../../common/styles"; | ||||||
|  |  | ||||||
| @customElement("ak-notification-drawer") | @customElement("ak-notification-drawer") | ||||||
| @ -30,9 +31,9 @@ export class NotificationDrawer extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     firstUpdated(): void { |     firstUpdated(): void { | ||||||
|         Notification.list({ |         new EventsApi(DEFAULT_CONFIG).eventsNotificationsList({ | ||||||
|             seen: false, |             seen: "false", | ||||||
|             ordering: "-created" |             ordering: "-created", | ||||||
|         }).then(r => { |         }).then(r => { | ||||||
|             this.notifications = r; |             this.notifications = r; | ||||||
|             this.unread = r.results.length; |             this.unread = r.results.length; | ||||||
| @ -40,7 +41,6 @@ export class NotificationDrawer extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderItem(item: Notification): TemplateResult { |     renderItem(item: Notification): TemplateResult { | ||||||
|         const created = new Date(parseInt(item.created, 10) * 1000); |  | ||||||
|         let level = ""; |         let level = ""; | ||||||
|         switch (item.severity) { |         switch (item.severity) { | ||||||
|         case "notice": |         case "notice": | ||||||
| @ -66,15 +66,18 @@ export class NotificationDrawer extends LitElement { | |||||||
|             </div> |             </div> | ||||||
|             <div class="pf-c-notification-drawer__list-item-action"> |             <div class="pf-c-notification-drawer__list-item-action"> | ||||||
|                 <button class="pf-c-dropdown__toggle pf-m-plain" type="button" @click=${() => { |                 <button class="pf-c-dropdown__toggle pf-m-plain" type="button" @click=${() => { | ||||||
|                     Notification.markSeen(item.pk).then(() => { |                     new EventsApi(DEFAULT_CONFIG).eventsNotificationsPartialUpdate({ | ||||||
|                         this.firstUpdated(); |                         uuid: item.pk || "", | ||||||
|  |                         data: { | ||||||
|  |                             seen: true, | ||||||
|  |                         } | ||||||
|                     }); |                     }); | ||||||
|                 }}> |                 }}> | ||||||
|                     <i class="fas fa-times"></i> |                     <i class="fas fa-times"></i> | ||||||
|                 </button> |                 </button> | ||||||
|             </div> |             </div> | ||||||
|             <p class="pf-c-notification-drawer__list-item-description">${item.body}</p> |             <p class="pf-c-notification-drawer__list-item-description">${item.body}</p> | ||||||
|             <small class="pf-c-notification-drawer__list-item-timestamp">${created.toLocaleString()}</small> |             <small class="pf-c-notification-drawer__list-item-timestamp">${item.created?.toLocaleString()}</small> | ||||||
|         </li>`; |         </li>`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
| @ -2,16 +2,16 @@ import { gettext } from "django"; | |||||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | import { customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { AKResponse } from "../../api/Client"; | import { AKResponse } from "../../api/Client"; | ||||||
| import { Table, TableColumn } from "../../elements/table/Table"; | import { Table, TableColumn } from "../../elements/table/Table"; | ||||||
| import { PolicyBinding } from "../../api/PolicyBindings"; | import { PoliciesApi, PolicyBinding } from "../../api"; | ||||||
|  |  | ||||||
| import "../../elements/Tabs"; | import "../../elements/Tabs"; | ||||||
| import "../../elements/AdminLoginsChart"; |  | ||||||
| import "../../elements/buttons/ModalButton"; | import "../../elements/buttons/ModalButton"; | ||||||
| import "../../elements/buttons/SpinnerButton"; | import "../../elements/buttons/SpinnerButton"; | ||||||
| import "../../elements/buttons/Dropdown"; | import "../../elements/buttons/Dropdown"; | ||||||
| import { Policy } from "../../api/Policies"; |  | ||||||
| import { until } from "lit-html/directives/until"; | import { until } from "lit-html/directives/until"; | ||||||
| import { PAGE_SIZE } from "../../constants"; | import { PAGE_SIZE } from "../../constants"; | ||||||
|  | import { DEFAULT_CONFIG } from "../../api/Config"; | ||||||
|  | import { AdminURLManager } from "../../api/legacy"; | ||||||
|  |  | ||||||
| @customElement("ak-bound-policies-list") | @customElement("ak-bound-policies-list") | ||||||
| export class BoundPoliciesList extends Table<PolicyBinding> { | export class BoundPoliciesList extends Table<PolicyBinding> { | ||||||
| @ -19,11 +19,11 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | |||||||
|     target?: string; |     target?: string; | ||||||
|  |  | ||||||
|     apiEndpoint(page: number): Promise<AKResponse<PolicyBinding>> { |     apiEndpoint(page: number): Promise<AKResponse<PolicyBinding>> { | ||||||
|         return PolicyBinding.list({ |         return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({ | ||||||
|             target: this.target || "", |             target: this.target || "", | ||||||
|             ordering: "order", |             ordering: "order", | ||||||
|             page: page, |             page: page, | ||||||
|             page_size: PAGE_SIZE, |             pageSize: PAGE_SIZE, | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -56,13 +56,13 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | |||||||
|             html`${item.order}`, |             html`${item.order}`, | ||||||
|             html`${item.timeout}`, |             html`${item.timeout}`, | ||||||
|             html` |             html` | ||||||
|             <ak-modal-button href="${PolicyBinding.adminUrl(`${item.pk}/update/`)}"> |             <ak-modal-button href="${AdminURLManager.policyBindings(`${item.pk}/update/`)}"> | ||||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> |                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||||
|                     ${gettext("Edit")} |                     ${gettext("Edit")} | ||||||
|                 </ak-spinner-button> |                 </ak-spinner-button> | ||||||
|                 <div slot="modal"></div> |                 <div slot="modal"></div> | ||||||
|             </ak-modal-button> |             </ak-modal-button> | ||||||
|             <ak-modal-button href="${PolicyBinding.adminUrl(`${item.pk}/delete/`)}"> |             <ak-modal-button href="${AdminURLManager.policyBindings(`${item.pk}/delete/`)}"> | ||||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> |                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||||
|                     ${gettext("Delete")} |                     ${gettext("Delete")} | ||||||
|                 </ak-spinner-button> |                 </ak-spinner-button> | ||||||
| @ -78,7 +78,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | |||||||
|                 ${gettext("No policies are currently bound to this object.")} |                 ${gettext("No policies are currently bound to this object.")} | ||||||
|             </div> |             </div> | ||||||
|             <div slot="primary"> |             <div slot="primary"> | ||||||
|                 <ak-modal-button href=${PolicyBinding.adminUrl(`create/?target=${this.target}`)}> |                 <ak-modal-button href=${AdminURLManager.policyBindings(`create/?target=${this.target}`)}> | ||||||
|                     <ak-spinner-button slot="trigger" class="pf-m-primary"> |                     <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||||
|                         ${gettext("Bind Policy")} |                         ${gettext("Bind Policy")} | ||||||
|                     </ak-spinner-button> |                     </ak-spinner-button> | ||||||
| @ -96,7 +96,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | |||||||
|                 <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> |                 <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> | ||||||
|             </button> |             </button> | ||||||
|             <ul class="pf-c-dropdown__menu" hidden> |             <ul class="pf-c-dropdown__menu" hidden> | ||||||
|                 ${until(Policy.getTypes().then((types) => { |                 ${until(new PoliciesApi(DEFAULT_CONFIG).policiesAllTypes({}).then((types) => { | ||||||
|                     return types.map((type) => { |                     return types.map((type) => { | ||||||
|                         return html`<li> |                         return html`<li> | ||||||
|                             <ak-modal-button href="${type.link}"> |                             <ak-modal-button href="${type.link}"> | ||||||
| @ -110,7 +110,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> { | |||||||
|                 }), html`<ak-spinner></ak-spinner>`)} |                 }), html`<ak-spinner></ak-spinner>`)} | ||||||
|             </ul> |             </ul> | ||||||
|         </ak-dropdown> |         </ak-dropdown> | ||||||
|         <ak-modal-button href=${PolicyBinding.adminUrl(`create/?target=${this.target}`)}> |         <ak-modal-button href=${AdminURLManager.policyBindings(`create/?target=${this.target}`)}> | ||||||
|             <ak-spinner-button slot="trigger" class="pf-m-primary"> |             <ak-spinner-button slot="trigger" class="pf-m-primary"> | ||||||
|                 ${gettext("Bind Policy")} |                 ${gettext("Bind Policy")} | ||||||
|             </ak-spinner-button> |             </ak-spinner-button> | ||||||
|  | |||||||
| @ -3,15 +3,17 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu | |||||||
| import PageStyle from "@patternfly/patternfly/components/Page/page.css"; | import PageStyle from "@patternfly/patternfly/components/Page/page.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||||
| import { Config } from "../../api/Config"; | import { configureSentry } from "../../api/Config"; | ||||||
|  | import { Config } from "../../api"; | ||||||
|  | import { ifDefined } from "lit-html/directives/if-defined"; | ||||||
|  |  | ||||||
| export const DefaultConfig: Config = { | export const DefaultConfig: Config = { | ||||||
|     branding_logo: " /static/dist/assets/icons/icon_left_brand.svg", |     brandingLogo: " /static/dist/assets/icons/icon_left_brand.svg", | ||||||
|     branding_title: "authentik", |     brandingTitle: "authentik", | ||||||
|  |  | ||||||
|     error_reporting_enabled: false, |     errorReportingEnabled: false, | ||||||
|     error_reporting_environment: "", |     errorReportingEnvironment: "", | ||||||
|     error_reporting_send_pii: false, |     errorReportingSendPii: false, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @customElement("ak-sidebar-brand") | @customElement("ak-sidebar-brand") | ||||||
| @ -40,13 +42,13 @@ export class SidebarBrand extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     firstUpdated(): void { |     firstUpdated(): void { | ||||||
|         Config.get().then((c) => (this.config = c)); |         configureSentry().then((c) => {this.config = c;}); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     render(): TemplateResult { |     render(): TemplateResult { | ||||||
|         return html` <a href="#/" class="pf-c-page__header-brand-link"> |         return html` <a href="#/" class="pf-c-page__header-brand-link"> | ||||||
|             <div class="pf-c-brand ak-brand"> |             <div class="pf-c-brand ak-brand"> | ||||||
|                 <img src="${this.config.branding_logo}" alt="authentik icon" loading="lazy" /> |                 <img src="${ifDefined(this.config.brandingLogo)}" alt="authentik icon" loading="lazy" /> | ||||||
|             </div> |             </div> | ||||||
|         </a>`; |         </a>`; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -5,10 +5,11 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css"; | |||||||
| import fa from "@fortawesome/fontawesome-free/css/all.css"; | import fa from "@fortawesome/fontawesome-free/css/all.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css"; | import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css"; | ||||||
| import { User } from "../../api/Users"; | import { me } from "../../api/Users"; | ||||||
| import { until } from "lit-html/directives/until"; | import { until } from "lit-html/directives/until"; | ||||||
|  |  | ||||||
| import "../notifications/NotificationTrigger"; | import "../notifications/NotificationTrigger"; | ||||||
|  | import { ifDefined } from "lit-html/directives/if-defined"; | ||||||
|  |  | ||||||
| @customElement("ak-sidebar-user") | @customElement("ak-sidebar-user") | ||||||
| export class SidebarUser extends LitElement { | export class SidebarUser extends LitElement { | ||||||
| @ -37,8 +38,8 @@ export class SidebarUser extends LitElement { | |||||||
|     render(): TemplateResult { |     render(): TemplateResult { | ||||||
|         return html` |         return html` | ||||||
|             <a href="#/-/user/" class="pf-c-nav__link user-avatar" id="user-settings"> |             <a href="#/-/user/" class="pf-c-nav__link user-avatar" id="user-settings"> | ||||||
|                 ${until(User.me().then((u) => { |                 ${until(me().then((u) => { | ||||||
|                     return html`<img class="pf-c-avatar" src="${u.avatar}" alt="" />`; |                     return html`<img class="pf-c-avatar" src="${ifDefined(u.avatar)}" alt="" />`; | ||||||
|                 }), html``)} |                 }), html``)} | ||||||
|             </a> |             </a> | ||||||
|             <ak-notification-trigger class="pf-c-nav__link user-notifications"> |             <ak-notification-trigger class="pf-c-nav__link user-notifications"> | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ import { COMMON_STYLES } from "../../common/styles"; | |||||||
|  |  | ||||||
| import "./TablePagination"; | import "./TablePagination"; | ||||||
| import "../EmptyState"; | import "../EmptyState"; | ||||||
|  | import "../Spinner"; | ||||||
|  |  | ||||||
| export class TableColumn { | export class TableColumn { | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,12 +1,12 @@ | |||||||
| import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||||
| import { COMMON_STYLES } from "../../common/styles"; | import { COMMON_STYLES } from "../../common/styles"; | ||||||
| import { PBPagination } from "../../api/Client"; | import { AKPagination } from "../../api/Client"; | ||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
|  |  | ||||||
| @customElement("ak-table-pagination") | @customElement("ak-table-pagination") | ||||||
| export class TablePagination extends LitElement { | export class TablePagination extends LitElement { | ||||||
|     @property({attribute: false}) |     @property({attribute: false}) | ||||||
|     pages?: PBPagination; |     pages?: AKPagination; | ||||||
|  |  | ||||||
|     @property({attribute: false}) |     @property({attribute: false}) | ||||||
|     // eslint-disable-next-line |     // eslint-disable-next-line | ||||||
| @ -22,8 +22,8 @@ export class TablePagination extends LitElement { | |||||||
|                 <div class="pf-c-options-menu"> |                 <div class="pf-c-options-menu"> | ||||||
|                     <div class="pf-c-options-menu__toggle pf-m-text pf-m-plain"> |                     <div class="pf-c-options-menu__toggle pf-m-text pf-m-plain"> | ||||||
|                         <span class="pf-c-options-menu__toggle-text"> |                         <span class="pf-c-options-menu__toggle-text"> | ||||||
|                             ${this.pages?.start_index} - |                             ${this.pages?.startIndex} - | ||||||
|                             ${this.pages?.end_index} of |                             ${this.pages?.endIndex} of | ||||||
|                             ${this.pages?.count} |                             ${this.pages?.count} | ||||||
|                         </span> |                         </span> | ||||||
|                     </div> |                     </div> | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| import "construct-style-sheets-polyfill"; | import "construct-style-sheets-polyfill"; | ||||||
|  |  | ||||||
| import "./pages/generic/FlowExecutor"; | import "./flows/FlowExecutor"; | ||||||
|  | |||||||
| @ -1,34 +1,34 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { LitElement, html, customElement, property, TemplateResult, CSSResult, css } from "lit-element"; | import { LitElement, html, customElement, property, TemplateResult, CSSResult, css } from "lit-element"; | ||||||
| import { unsafeHTML } from "lit-html/directives/unsafe-html"; | import { unsafeHTML } from "lit-html/directives/unsafe-html"; | ||||||
| import { getCookie } from "../../utils"; | import "./stages/authenticator_static/AuthenticatorStaticStage"; | ||||||
| import "../../elements/stages/authenticator_static/AuthenticatorStaticStage"; | import "./stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||||
| import "../../elements/stages/authenticator_totp/AuthenticatorTOTPStage"; | import "./stages/authenticator_validate/AuthenticatorValidateStage"; | ||||||
| import "../../elements/stages/authenticator_validate/AuthenticatorValidateStage"; | import "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||||
| import "../../elements/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | import "./stages/autosubmit/AutosubmitStage"; | ||||||
| import "../../elements/stages/autosubmit/AutosubmitStage"; | import "./stages/captcha/CaptchaStage"; | ||||||
| import "../../elements/stages/captcha/CaptchaStage"; | import "./stages/consent/ConsentStage"; | ||||||
| import "../../elements/stages/consent/ConsentStage"; | import "./stages/email/EmailStage"; | ||||||
| import "../../elements/stages/email/EmailStage"; | import "./stages/identification/IdentificationStage"; | ||||||
| import "../../elements/stages/identification/IdentificationStage"; | import "./stages/password/PasswordStage"; | ||||||
| import "../../elements/stages/password/PasswordStage"; | import "./stages/prompt/PromptStage"; | ||||||
| import "../../elements/stages/prompt/PromptStage"; | import { ShellChallenge, RedirectChallenge } from "../api/Flows"; | ||||||
| import { ShellChallenge, Challenge, ChallengeTypes, Flow, RedirectChallenge } from "../../api/Flows"; | import { IdentificationChallenge } from "./stages/identification/IdentificationStage"; | ||||||
| import { DefaultClient } from "../../api/Client"; | import { PasswordChallenge } from "./stages/password/PasswordStage"; | ||||||
| import { IdentificationChallenge } from "../../elements/stages/identification/IdentificationStage"; | import { ConsentChallenge } from "./stages/consent/ConsentStage"; | ||||||
| import { PasswordChallenge } from "../../elements/stages/password/PasswordStage"; | import { EmailChallenge } from "./stages/email/EmailStage"; | ||||||
| import { ConsentChallenge } from "../../elements/stages/consent/ConsentStage"; | import { AutosubmitChallenge } from "./stages/autosubmit/AutosubmitStage"; | ||||||
| import { EmailChallenge } from "../../elements/stages/email/EmailStage"; | import { PromptChallenge } from "./stages/prompt/PromptStage"; | ||||||
| import { AutosubmitChallenge } from "../../elements/stages/autosubmit/AutosubmitStage"; | import { AuthenticatorTOTPChallenge } from "./stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||||
| import { PromptChallenge } from "../../elements/stages/prompt/PromptStage"; | import { AuthenticatorStaticChallenge } from "./stages/authenticator_static/AuthenticatorStaticStage"; | ||||||
| import { AuthenticatorTOTPChallenge } from "../../elements/stages/authenticator_totp/AuthenticatorTOTPStage"; | import { AuthenticatorValidateStageChallenge } from "./stages/authenticator_validate/AuthenticatorValidateStage"; | ||||||
| import { AuthenticatorStaticChallenge } from "../../elements/stages/authenticator_static/AuthenticatorStaticStage"; | import { WebAuthnAuthenticatorRegisterChallenge } from "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||||
| import { AuthenticatorValidateStageChallenge } from "../../elements/stages/authenticator_validate/AuthenticatorValidateStage"; | import { CaptchaChallenge } from "./stages/captcha/CaptchaStage"; | ||||||
| import { WebAuthnAuthenticatorRegisterChallenge } from "../../elements/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | import { COMMON_STYLES } from "../common/styles"; | ||||||
| import { CaptchaChallenge } from "../../elements/stages/captcha/CaptchaStage"; | import { SpinnerSize } from "../elements/Spinner"; | ||||||
| import { COMMON_STYLES } from "../../common/styles"; | import { StageHost } from "./stages/base"; | ||||||
| import { SpinnerSize } from "../../elements/Spinner"; | import { Challenge, ChallengeTypeEnum, FlowsApi } from "../api"; | ||||||
| import { StageHost } from "../../elements/stages/base"; | import { DEFAULT_CONFIG } from "../api/Config"; | ||||||
| 
 | 
 | ||||||
| @customElement("ak-flow-executor") | @customElement("ak-flow-executor") | ||||||
| export class FlowExecutor extends LitElement implements StageHost { | export class FlowExecutor extends LitElement implements StageHost { | ||||||
| @ -68,37 +68,30 @@ export class FlowExecutor extends LitElement implements StageHost { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     submit(formData?: FormData): Promise<void> { |     submit<T>(formData?: T): Promise<void> { | ||||||
|         const csrftoken = getCookie("authentik_csrf"); |  | ||||||
|         const request = new Request(DefaultClient.makeUrl(["flows", "executor", this.flowSlug]), { |  | ||||||
|             headers: { |  | ||||||
|                 "X-CSRFToken": csrftoken, |  | ||||||
|             }, |  | ||||||
|         }); |  | ||||||
|         this.loading = true; |         this.loading = true; | ||||||
|         return fetch(request, { |         return new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolveRaw({ | ||||||
|             method: "POST", |             flowSlug: this.flowSlug, | ||||||
|             mode: "same-origin", |             data: formData || {}, | ||||||
|             body: formData, |         }).then((challengeRaw) => { | ||||||
|         }) |             return challengeRaw.raw.json(); | ||||||
|             .then((response) => { |         }).then((data) => { | ||||||
|                 return response.json(); |  | ||||||
|             }) |  | ||||||
|             .then((data) => { |  | ||||||
|             this.challenge = data; |             this.challenge = data; | ||||||
|             }) |         }).catch((e) => { | ||||||
|             .catch((e) => { |  | ||||||
|             this.errorMessage(e); |             this.errorMessage(e); | ||||||
|             }) |         }).finally(() => { | ||||||
|             .finally(() => { |  | ||||||
|             this.loading = false; |             this.loading = false; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     firstUpdated(): void { |     firstUpdated(): void { | ||||||
|         this.loading = true; |         this.loading = true; | ||||||
|         Flow.executor(this.flowSlug).then((challenge) => { |         new FlowsApi(DEFAULT_CONFIG).flowsExecutorGetRaw({ | ||||||
|             this.challenge = challenge; |             flowSlug: this.flowSlug | ||||||
|  |         }).then((challengeRaw) => { | ||||||
|  |             return challengeRaw.raw.json(); | ||||||
|  |         }).then((challenge) => { | ||||||
|  |             this.challenge = challenge as Challenge; | ||||||
|         }).catch((e) => { |         }).catch((e) => { | ||||||
|             // Catch JSON or Update errors
 |             // Catch JSON or Update errors
 | ||||||
|             this.errorMessage(e); |             this.errorMessage(e); | ||||||
| @ -109,7 +102,7 @@ export class FlowExecutor extends LitElement implements StageHost { | |||||||
| 
 | 
 | ||||||
|     errorMessage(error: string): void { |     errorMessage(error: string): void { | ||||||
|         this.challenge = <ShellChallenge>{ |         this.challenge = <ShellChallenge>{ | ||||||
|             type: ChallengeTypes.shell, |             type: ChallengeTypeEnum.Shell, | ||||||
|             body: `<style>
 |             body: `<style>
 | ||||||
|                     .ak-exception { |                     .ak-exception { | ||||||
|                         font-family: monospace; |                         font-family: monospace; | ||||||
| @ -139,13 +132,13 @@ export class FlowExecutor extends LitElement implements StageHost { | |||||||
|             return this.renderLoading(); |             return this.renderLoading(); | ||||||
|         } |         } | ||||||
|         switch (this.challenge.type) { |         switch (this.challenge.type) { | ||||||
|             case ChallengeTypes.redirect: |             case ChallengeTypeEnum.Redirect: | ||||||
|                 console.debug(`authentik/flows: redirecting to ${(this.challenge as RedirectChallenge).to}`); |                 console.debug(`authentik/flows: redirecting to ${(this.challenge as RedirectChallenge).to}`); | ||||||
|                 window.location.assign((this.challenge as RedirectChallenge).to); |                 window.location.assign((this.challenge as RedirectChallenge).to); | ||||||
|                 return this.renderLoading(); |                 return this.renderLoading(); | ||||||
|             case ChallengeTypes.shell: |             case ChallengeTypeEnum.Shell: | ||||||
|                 return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`; |                 return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`; | ||||||
|             case ChallengeTypes.native: |             case ChallengeTypeEnum.Native: | ||||||
|                 switch (this.challenge.component) { |                 switch (this.challenge.component) { | ||||||
|                     case "ak-stage-identification": |                     case "ak-stage-identification": | ||||||
|                         return html`<ak-stage-identification .host=${this} .challenge=${this.challenge as IdentificationChallenge}></ak-stage-identification>`; |                         return html`<ak-stage-identification .host=${this} .challenge=${this.challenge as IdentificationChallenge}></ak-stage-identification>`; | ||||||
| @ -4,6 +4,7 @@ import { WithUserInfoChallenge } from "../../../api/Flows"; | |||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import "../form"; | import "../form"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| export interface AuthenticatorStaticChallenge extends WithUserInfoChallenge { | export interface AuthenticatorStaticChallenge extends WithUserInfoChallenge { | ||||||
|     codes: number[]; |     codes: number[]; | ||||||
| @ -5,7 +5,8 @@ import { COMMON_STYLES } from "../../../common/styles"; | |||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import "webcomponent-qr-code"; | import "webcomponent-qr-code"; | ||||||
| import "../form"; | import "../form"; | ||||||
| import { showMessage } from "../../messages/MessageContainer"; | import { showMessage } from "../../../elements/messages/MessageContainer"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| export interface AuthenticatorTOTPChallenge extends WithUserInfoChallenge { | export interface AuthenticatorTOTPChallenge extends WithUserInfoChallenge { | ||||||
|     config_url: string; |     config_url: string; | ||||||
| @ -36,8 +36,8 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost { | |||||||
|     @property({attribute: false}) |     @property({attribute: false}) | ||||||
|     selectedDeviceChallenge?: DeviceChallenge; |     selectedDeviceChallenge?: DeviceChallenge; | ||||||
| 
 | 
 | ||||||
|     submit(formData?: FormData): Promise<void> { |     submit<T>(formData?: T): Promise<void> { | ||||||
|         return this.host?.submit(formData) || Promise.resolve(); |         return this.host?.submit<T>(formData) || Promise.resolve(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static get styles(): CSSResult[] { |     static get styles(): CSSResult[] { | ||||||
| @ -4,6 +4,7 @@ import { COMMON_STYLES } from "../../../common/styles"; | |||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage"; | import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage"; | ||||||
| import "../form"; | import "../form"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| @customElement("ak-stage-authenticator-validate-code") | @customElement("ak-stage-authenticator-validate-code") | ||||||
| export class AuthenticatorValidateStageWebCode extends BaseStage { | export class AuthenticatorValidateStageWebCode extends BaseStage { | ||||||
| @ -1,7 +1,7 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { SpinnerSize } from "../../Spinner"; | import { SpinnerSize } from "../../../elements/Spinner"; | ||||||
| import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils"; | import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage"; | import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage"; | ||||||
| @ -1,7 +1,7 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { customElement, html, property, TemplateResult } from "lit-element"; | import { customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||||
| import { SpinnerSize } from "../../Spinner"; | import { SpinnerSize } from "../../../elements/Spinner"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import { Assertion, transformCredentialCreateOptions, transformNewAssertionForServer } from "./utils"; | import { Assertion, transformCredentialCreateOptions, transformNewAssertionForServer } from "./utils"; | ||||||
| 
 | 
 | ||||||
| @ -3,7 +3,8 @@ import { CSSResult, customElement, html, property, TemplateResult } from "lit-el | |||||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import "../../Spinner"; | import "../../../elements/Spinner"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| export interface AutosubmitChallenge extends WithUserInfoChallenge { | export interface AutosubmitChallenge extends WithUserInfoChallenge { | ||||||
|     url: string; |     url: string; | ||||||
| @ -1,7 +1,7 @@ | |||||||
| import { LitElement } from "lit-element"; | import { LitElement } from "lit-element"; | ||||||
| 
 | 
 | ||||||
| export interface StageHost { | export interface StageHost { | ||||||
|     submit(formData?: FormData): Promise<void>; |     submit<T>(formData?: T): Promise<void>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class BaseStage extends LitElement { | export class BaseStage extends LitElement { | ||||||
| @ -10,8 +10,12 @@ export class BaseStage extends LitElement { | |||||||
| 
 | 
 | ||||||
|     submitForm(e: Event): void { |     submitForm(e: Event): void { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|  |         const object: { | ||||||
|  |             [key: string]: unknown; | ||||||
|  |         } = {}; | ||||||
|         const form = new FormData(this.shadowRoot?.querySelector("form") || undefined); |         const form = new FormData(this.shadowRoot?.querySelector("form") || undefined); | ||||||
|         this.host?.submit(form); |         form.forEach((value, key) => object[key] = value); | ||||||
|  |         this.host?.submit(object); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @ -2,9 +2,10 @@ import { gettext } from "django"; | |||||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { SpinnerSize } from "../../Spinner"; | import { SpinnerSize } from "../../../elements/Spinner"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import "../form"; | import "../form"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| export interface CaptchaChallenge extends WithUserInfoChallenge { | export interface CaptchaChallenge extends WithUserInfoChallenge { | ||||||
|     site_key: string; |     site_key: string; | ||||||
| @ -3,6 +3,7 @@ import { CSSResult, customElement, html, property, TemplateResult } from "lit-el | |||||||
| import { WithUserInfoChallenge } from "../../../api/Flows"; | import { WithUserInfoChallenge } from "../../../api/Flows"; | ||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| export interface Permission { | export interface Permission { | ||||||
|     name: string; |     name: string; | ||||||
| @ -1,10 +1,11 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { Challenge } from "../../../api/Flows"; | import { Challenge } from "../../../api"; | ||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| export type EmailChallenge = Challenge | export type EmailChallenge = Challenge; | ||||||
| 
 | 
 | ||||||
| @customElement("ak-stage-email") | @customElement("ak-stage-email") | ||||||
| export class EmailStage extends BaseStage { | export class EmailStage extends BaseStage { | ||||||
| @ -1,9 +1,10 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { Challenge } from "../../../api/Flows"; |  | ||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import "../form"; | import "../form"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
|  | import { Challenge } from "../../../api/Flows"; | ||||||
| 
 | 
 | ||||||
| export interface IdentificationChallenge extends Challenge { | export interface IdentificationChallenge extends Challenge { | ||||||
| 
 | 
 | ||||||
| @ -4,6 +4,7 @@ import { WithUserInfoChallenge } from "../../../api/Flows"; | |||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import "../form"; | import "../form"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
| 
 | 
 | ||||||
| export interface PasswordChallenge extends WithUserInfoChallenge { | export interface PasswordChallenge extends WithUserInfoChallenge { | ||||||
|     recovery_url?: string; |     recovery_url?: string; | ||||||
| @ -1,10 +1,11 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; | ||||||
| import { unsafeHTML } from "lit-html/directives/unsafe-html"; | import { unsafeHTML } from "lit-html/directives/unsafe-html"; | ||||||
| import { Challenge } from "../../../api/Flows"; |  | ||||||
| import { COMMON_STYLES } from "../../../common/styles"; | import { COMMON_STYLES } from "../../../common/styles"; | ||||||
| import { BaseStage } from "../base"; | import { BaseStage } from "../base"; | ||||||
| import "../form"; | import "../form"; | ||||||
|  | import "../../../elements/utils/LoadingState"; | ||||||
|  | import { Challenge } from "../../../api/Flows"; | ||||||
| 
 | 
 | ||||||
| export interface Prompt { | export interface Prompt { | ||||||
|     field_key: string; |     field_key: string; | ||||||
| @ -1,5 +1,5 @@ | |||||||
| import { customElement } from "lit-element"; | import { customElement } from "lit-element"; | ||||||
| import { User } from "../api/Users"; | import { me } from "../api/Users"; | ||||||
| import { SidebarItem } from "../elements/sidebar/Sidebar"; | import { SidebarItem } from "../elements/sidebar/Sidebar"; | ||||||
| import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "../elements/router/Route"; | import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "../elements/router/Route"; | ||||||
| import { Interface } from "./Interface"; | import { Interface } from "./Interface"; | ||||||
| @ -10,7 +10,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | |||||||
|         new SidebarItem("Overview", "/administration/overview"), |         new SidebarItem("Overview", "/administration/overview"), | ||||||
|         new SidebarItem("System Tasks", "/administration/system-tasks"), |         new SidebarItem("System Tasks", "/administration/system-tasks"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return me().then(u => u.isSuperuser||false); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Events").children( |     new SidebarItem("Events").children( | ||||||
|         new SidebarItem("Log", "/events/log").activeWhen( |         new SidebarItem("Log", "/events/log").activeWhen( | ||||||
| @ -19,7 +19,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | |||||||
|         new SidebarItem("Notification Rules", "/events/rules"), |         new SidebarItem("Notification Rules", "/events/rules"), | ||||||
|         new SidebarItem("Notification Transports", "/events/transports"), |         new SidebarItem("Notification Transports", "/events/transports"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return me().then(u => u.isSuperuser||false); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Resources").children( |     new SidebarItem("Resources").children( | ||||||
|         new SidebarItem("Applications", "/core/applications").activeWhen( |         new SidebarItem("Applications", "/core/applications").activeWhen( | ||||||
| @ -34,13 +34,13 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | |||||||
|         new SidebarItem("Outposts", "/outpost/outposts"), |         new SidebarItem("Outposts", "/outpost/outposts"), | ||||||
|         new SidebarItem("Outpost Service Connections", "/outpost/service-connections"), |         new SidebarItem("Outpost Service Connections", "/outpost/service-connections"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return me().then(u => u.isSuperuser||false); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Customisation").children( |     new SidebarItem("Customisation").children( | ||||||
|         new SidebarItem("Policies", "/policy/policies"), |         new SidebarItem("Policies", "/policy/policies"), | ||||||
|         new SidebarItem("Property Mappings", "/core/property-mappings"), |         new SidebarItem("Property Mappings", "/core/property-mappings"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return me().then(u => u.isSuperuser||false); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Flows").children( |     new SidebarItem("Flows").children( | ||||||
|         new SidebarItem("Flows", "/flow/flows").activeWhen(`^/flow/flows/(?<slug>${SLUG_REGEX})$`), |         new SidebarItem("Flows", "/flow/flows").activeWhen(`^/flow/flows/(?<slug>${SLUG_REGEX})$`), | ||||||
| @ -48,7 +48,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | |||||||
|         new SidebarItem("Prompts", "/flow/stages/prompts"), |         new SidebarItem("Prompts", "/flow/stages/prompts"), | ||||||
|         new SidebarItem("Invitations", "/flow/stages/invitations"), |         new SidebarItem("Invitations", "/flow/stages/invitations"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return me().then(u => u.isSuperuser||false); | ||||||
|     }), |     }), | ||||||
|     new SidebarItem("Identity & Cryptography").children( |     new SidebarItem("Identity & Cryptography").children( | ||||||
|         new SidebarItem("User", "/identity/users"), |         new SidebarItem("User", "/identity/users"), | ||||||
| @ -56,7 +56,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [ | |||||||
|         new SidebarItem("Certificates", "/crypto/certificates"), |         new SidebarItem("Certificates", "/crypto/certificates"), | ||||||
|         new SidebarItem("Tokens", "/core/tokens"), |         new SidebarItem("Tokens", "/core/tokens"), | ||||||
|     ).when((): Promise<boolean> => { |     ).when((): Promise<boolean> => { | ||||||
|         return User.me().then(u => u.is_superuser); |         return me().then(u => u.isSuperuser||false); | ||||||
|     }), |     }), | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,34 +1,13 @@ | |||||||
| import "construct-style-sheets-polyfill"; | import "construct-style-sheets-polyfill"; | ||||||
|  |  | ||||||
|  | // Elements that are used by SiteShell pages | ||||||
|  | // And can't dynamically be imported | ||||||
| import "./elements/buttons/ActionButton"; | import "./elements/buttons/ActionButton"; | ||||||
| import "./elements/buttons/Dropdown"; | import "./elements/buttons/Dropdown"; | ||||||
| import "./elements/buttons/ModalButton"; | import "./elements/buttons/ModalButton"; | ||||||
| import "./elements/buttons/SpinnerButton"; | import "./elements/buttons/SpinnerButton"; | ||||||
| import "./elements/buttons/TokenCopyButton"; |  | ||||||
|  |  | ||||||
| import "./elements/sidebar/Sidebar"; |  | ||||||
| import "./elements/sidebar/SidebarBrand"; |  | ||||||
| import "./elements/sidebar/SidebarUser"; |  | ||||||
|  |  | ||||||
| import "./elements/table/TablePagination"; |  | ||||||
|  |  | ||||||
| import "./elements/AdminLoginsChart"; |  | ||||||
| import "./elements/EmptyState"; |  | ||||||
| import "./elements/cards/AggregateCard"; |  | ||||||
| import "./elements/cards/AggregatePromiseCard"; |  | ||||||
| import "./elements/CodeMirror"; | import "./elements/CodeMirror"; | ||||||
| import "./elements/messages/MessageContainer"; |  | ||||||
| import "./elements/Spinner"; |  | ||||||
| import "./elements/Tabs"; |  | ||||||
| import "./elements/router/RouterOutlet"; |  | ||||||
|  |  | ||||||
| import "./pages/generic/SiteShell"; |  | ||||||
|  |  | ||||||
| import "./pages/admin-overview/AdminOverviewPage"; |  | ||||||
| import "./pages/admin-overview/TopApplicationsTable"; |  | ||||||
| import "./pages/applications/ApplicationListPage"; |  | ||||||
| import "./pages/applications/ApplicationViewPage"; |  | ||||||
| import "./pages/tokens/UserTokenList"; | import "./pages/tokens/UserTokenList"; | ||||||
| import "./pages/LibraryPage"; | import "./pages/generic/SiteShell"; | ||||||
|  |  | ||||||
| import "./interfaces/AdminInterface"; | import "./interfaces/AdminInterface"; | ||||||
|  | |||||||
| @ -1,8 +1,9 @@ | |||||||
| import { gettext } from "django"; | import { gettext } from "django"; | ||||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||||
| import { ifDefined } from "lit-html/directives/if-defined"; | import { ifDefined } from "lit-html/directives/if-defined"; | ||||||
| import { Application } from "../api/Applications"; | import { Application, CoreApi } from "../api"; | ||||||
| import { AKResponse } from "../api/Client"; | import { AKResponse } from "../api/Client"; | ||||||
|  | import { DEFAULT_CONFIG } from "../api/Config"; | ||||||
| import { COMMON_STYLES } from "../common/styles"; | import { COMMON_STYLES } from "../common/styles"; | ||||||
| import { loading, truncate } from "../utils"; | import { loading, truncate } from "../utils"; | ||||||
|  |  | ||||||
| @ -31,19 +32,19 @@ export class LibraryApplication extends LitElement { | |||||||
|         if (!this.application) { |         if (!this.application) { | ||||||
|             return html`<ak-spinner></ak-spinner>`; |             return html`<ak-spinner></ak-spinner>`; | ||||||
|         } |         } | ||||||
|         return html` <a href="${this.application.launch_url}" class="pf-c-card pf-m-hoverable pf-m-compact"> |         return html` <a href="${ifDefined(this.application.launchUrl)}" class="pf-c-card pf-m-hoverable pf-m-compact"> | ||||||
|             <div class="pf-c-card__header"> |             <div class="pf-c-card__header"> | ||||||
|                 ${this.application.meta_icon |                 ${this.application.metaIcon | ||||||
|                     ? html`<img class="app-icon pf-c-avatar" src="${ifDefined(this.application.meta_icon)}" alt="Application Icon"/>` |                     ? html`<img class="app-icon pf-c-avatar" src="${ifDefined(this.application.metaIcon)}" alt="Application Icon"/>` | ||||||
|                     : html`<i class="pf-icon pf-icon-arrow"></i>`} |                     : html`<i class="pf-icon pf-icon-arrow"></i>`} | ||||||
|             </div> |             </div> | ||||||
|             <div class="pf-c-card__title"> |             <div class="pf-c-card__title"> | ||||||
|                 <p id="card-1-check-label">${this.application.name}</p> |                 <p id="card-1-check-label">${this.application.name}</p> | ||||||
|                 <div class="pf-c-content"> |                 <div class="pf-c-content"> | ||||||
|                     <small>${this.application.meta_publisher}</small> |                     <small>${this.application.metaPublisher}</small> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|             <div class="pf-c-card__body">${truncate(this.application.meta_description, 35)}</div> |             <div class="pf-c-card__body">${truncate(this.application.metaDescription, 35)}</div> | ||||||
|         </a>`; |         </a>`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -64,7 +65,9 @@ export class LibraryPage extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     firstUpdated(): void { |     firstUpdated(): void { | ||||||
|         Application.list().then((r) => (this.apps = r)); |         new CoreApi(DEFAULT_CONFIG).coreApplicationsList({}).then((apps) => { | ||||||
|  |             this.apps = apps; | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderEmptyState(): TemplateResult { |     renderEmptyState(): TemplateResult { | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import { gettext } from "django"; | |||||||
| import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element"; | import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element"; | ||||||
| import { COMMON_STYLES } from "../../common/styles"; | import { COMMON_STYLES } from "../../common/styles"; | ||||||
|  |  | ||||||
| import "../../elements/AdminLoginsChart"; | import "../../elements/charts/AdminLoginsChart"; | ||||||
| import "../../elements/cards/AggregatePromiseCard"; | import "../../elements/cards/AggregatePromiseCard"; | ||||||
| import "./TopApplicationsTable"; | import "./TopApplicationsTable"; | ||||||
| import "./cards/AdminStatusCard"; | import "./cards/AdminStatusCard"; | ||||||
| @ -30,7 +30,7 @@ export class AdminOverviewPage extends LitElement { | |||||||
|         <section class="pf-c-page__main-section"> |         <section class="pf-c-page__main-section"> | ||||||
|             <div class="pf-l-gallery pf-m-gutter"> |             <div class="pf-l-gallery pf-m-gutter"> | ||||||
|                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Logins over the last 24 hours" style="grid-column-end: span 3;grid-row-end: span 2;"> |                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Logins over the last 24 hours" style="grid-column-end: span 3;grid-row-end: span 2;"> | ||||||
|                     <ak-admin-logins-chart .url="${["admin", "metrics"]}"></ak-admin-logins-chart> |                     <ak-charts-admin-login></ak-charts-admin-login> | ||||||
|                 </ak-aggregate-card> |                 </ak-aggregate-card> | ||||||
|                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;"> |                 <ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;"> | ||||||
|                     <ak-top-applications-table></ak-top-applications-table> |                     <ak-top-applications-table></ak-top-applications-table> | ||||||
|  | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L