providers/scim: add API endpoint to sync single user (#8486)
* add api Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add UI Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		| @ -6,7 +6,10 @@ from authentik.core.api.providers import ProviderSerializer | ||||
| from authentik.core.api.used_by import UsedByMixin | ||||
| from authentik.enterprise.api import EnterpriseRequiredMixin | ||||
| from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProvider | ||||
| from authentik.enterprise.providers.google_workspace.tasks import google_workspace_sync | ||||
| from authentik.enterprise.providers.google_workspace.tasks import ( | ||||
|     google_workspace_sync, | ||||
|     google_workspace_sync_objects, | ||||
| ) | ||||
| from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin | ||||
|  | ||||
|  | ||||
| @ -52,3 +55,4 @@ class GoogleWorkspaceProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixi | ||||
|     search_fields = ["name"] | ||||
|     ordering = ["name"] | ||||
|     sync_single_task = google_workspace_sync | ||||
|     sync_objects_task = google_workspace_sync_objects | ||||
|  | ||||
| @ -6,7 +6,10 @@ from authentik.core.api.providers import ProviderSerializer | ||||
| from authentik.core.api.used_by import UsedByMixin | ||||
| from authentik.enterprise.api import EnterpriseRequiredMixin | ||||
| from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider | ||||
| from authentik.enterprise.providers.microsoft_entra.tasks import microsoft_entra_sync | ||||
| from authentik.enterprise.providers.microsoft_entra.tasks import ( | ||||
|     microsoft_entra_sync, | ||||
|     microsoft_entra_sync_objects, | ||||
| ) | ||||
| from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin | ||||
|  | ||||
|  | ||||
| @ -50,3 +53,4 @@ class MicrosoftEntraProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin | ||||
|     search_fields = ["name"] | ||||
|     ordering = ["name"] | ||||
|     sync_single_task = microsoft_entra_sync | ||||
|     sync_objects_task = microsoft_entra_sync_objects | ||||
|  | ||||
| @ -1,16 +1,19 @@ | ||||
| from collections.abc import Callable | ||||
|  | ||||
| from celery import Task | ||||
| from django.utils.text import slugify | ||||
| from drf_spectacular.utils import OpenApiResponse, extend_schema | ||||
| from guardian.shortcuts import get_objects_for_user | ||||
| from rest_framework.decorators import action | ||||
| from rest_framework.fields import BooleanField | ||||
| from rest_framework.fields import BooleanField, CharField, ChoiceField | ||||
| from rest_framework.request import Request | ||||
| from rest_framework.response import Response | ||||
|  | ||||
| from authentik.core.api.utils import ModelSerializer, PassiveSerializer | ||||
| from authentik.core.models import Group, User | ||||
| from authentik.events.api.tasks import SystemTaskSerializer | ||||
| from authentik.events.logs import LogEvent, LogEventSerializer | ||||
| from authentik.lib.sync.outgoing.models import OutgoingSyncProvider | ||||
| from authentik.lib.utils.reflection import class_to_path | ||||
| from authentik.rbac.filters import ObjectFilter | ||||
|  | ||||
|  | ||||
| class SyncStatusSerializer(PassiveSerializer): | ||||
| @ -20,10 +23,29 @@ class SyncStatusSerializer(PassiveSerializer): | ||||
|     tasks = SystemTaskSerializer(many=True, read_only=True) | ||||
|  | ||||
|  | ||||
| class SyncObjectSerializer(PassiveSerializer): | ||||
|     """Sync object serializer""" | ||||
|  | ||||
|     sync_object_model = ChoiceField( | ||||
|         choices=( | ||||
|             (class_to_path(User), "user"), | ||||
|             (class_to_path(Group), "group"), | ||||
|         ) | ||||
|     ) | ||||
|     sync_object_id = CharField() | ||||
|  | ||||
|  | ||||
| class SyncObjectResultSerializer(PassiveSerializer): | ||||
|     """Result of a single object sync""" | ||||
|  | ||||
|     messages = LogEventSerializer(many=True, read_only=True) | ||||
|  | ||||
|  | ||||
| class OutgoingSyncProviderStatusMixin: | ||||
|     """Common API Endpoints for Outgoing sync providers""" | ||||
|  | ||||
|     sync_single_task: Callable = None | ||||
|     sync_single_task: type[Task] = None | ||||
|     sync_objects_task: type[Task] = None | ||||
|  | ||||
|     @extend_schema( | ||||
|         responses={ | ||||
| @ -36,7 +58,7 @@ class OutgoingSyncProviderStatusMixin: | ||||
|         detail=True, | ||||
|         pagination_class=None, | ||||
|         url_path="sync/status", | ||||
|         filter_backends=[], | ||||
|         filter_backends=[ObjectFilter], | ||||
|     ) | ||||
|     def sync_status(self, request: Request, pk: int) -> Response: | ||||
|         """Get provider's sync status""" | ||||
| @ -55,6 +77,30 @@ class OutgoingSyncProviderStatusMixin: | ||||
|             } | ||||
|         return Response(SyncStatusSerializer(status).data) | ||||
|  | ||||
|     @extend_schema( | ||||
|         request=SyncObjectSerializer, | ||||
|         responses={200: SyncObjectResultSerializer()}, | ||||
|     ) | ||||
|     @action( | ||||
|         methods=["POST"], | ||||
|         detail=True, | ||||
|         pagination_class=None, | ||||
|         url_path="sync/object", | ||||
|         filter_backends=[ObjectFilter], | ||||
|     ) | ||||
|     def sync_object(self, request: Request, pk: int) -> Response: | ||||
|         """Sync/Re-sync a single user/group object""" | ||||
|         provider: OutgoingSyncProvider = self.get_object() | ||||
|         params = SyncObjectSerializer(data=request.data) | ||||
|         params.is_valid(raise_exception=True) | ||||
|         res: list[LogEvent] = self.sync_objects_task.delay( | ||||
|             params.validated_data["sync_object_model"], | ||||
|             page=1, | ||||
|             provider_pk=provider.pk, | ||||
|             pk=params.validated_data["sync_object_id"], | ||||
|         ).get() | ||||
|         return Response(SyncObjectResultSerializer(instance={"messages": res}).data) | ||||
|  | ||||
|  | ||||
| class OutgoingSyncConnectionCreateMixin: | ||||
|     """Mixin for connection objects that fetches remote data upon creation""" | ||||
|  | ||||
| @ -105,7 +105,7 @@ class SyncTasks: | ||||
|                 return | ||||
|         task.set_status(TaskStatus.SUCCESSFUL, *messages) | ||||
|  | ||||
|     def sync_objects(self, object_type: str, page: int, provider_pk: int): | ||||
|     def sync_objects(self, object_type: str, page: int, provider_pk: int, **filter): | ||||
|         _object_type = path_to_class(object_type) | ||||
|         self.logger = get_logger().bind( | ||||
|             provider_type=class_to_path(self._provider_model), | ||||
| @ -120,7 +120,7 @@ class SyncTasks: | ||||
|             client = provider.client_for_model(_object_type) | ||||
|         except TransientSyncException: | ||||
|             return messages | ||||
|         paginator = Paginator(provider.get_object_qs(_object_type), PAGE_SIZE) | ||||
|         paginator = Paginator(provider.get_object_qs(_object_type).filter(**filter), PAGE_SIZE) | ||||
|         if client.can_discover: | ||||
|             self.logger.debug("starting discover") | ||||
|             client.discover() | ||||
|  | ||||
| @ -6,7 +6,7 @@ from authentik.core.api.providers import ProviderSerializer | ||||
| from authentik.core.api.used_by import UsedByMixin | ||||
| from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin | ||||
| from authentik.providers.scim.models import SCIMProvider | ||||
| from authentik.providers.scim.tasks import scim_sync | ||||
| from authentik.providers.scim.tasks import scim_sync, scim_sync_objects | ||||
|  | ||||
|  | ||||
| class SCIMProviderSerializer(ProviderSerializer): | ||||
| @ -42,3 +42,4 @@ class SCIMProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin, ModelVie | ||||
|     search_fields = ["name", "url"] | ||||
|     ordering = ["name", "url"] | ||||
|     sync_single_task = scim_sync | ||||
|     sync_objects_task = scim_sync_objects | ||||
|  | ||||
							
								
								
									
										148
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								schema.yml
									
									
									
									
									
								
							| @ -17853,6 +17853,46 @@ paths: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|   /providers/google_workspace/{id}/sync/object/: | ||||
|     post: | ||||
|       operationId: providers_google_workspace_sync_object_create | ||||
|       description: Sync/Re-sync a single user/group object | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: id | ||||
|         schema: | ||||
|           type: integer | ||||
|         description: A unique integer value identifying this Google Workspace Provider. | ||||
|         required: true | ||||
|       tags: | ||||
|       - providers | ||||
|       requestBody: | ||||
|         content: | ||||
|           application/json: | ||||
|             schema: | ||||
|               $ref: '#/components/schemas/SyncObjectRequest' | ||||
|         required: true | ||||
|       security: | ||||
|       - authentik: [] | ||||
|       responses: | ||||
|         '200': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/SyncObjectResult' | ||||
|           description: '' | ||||
|         '400': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/ValidationError' | ||||
|           description: '' | ||||
|         '403': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|   /providers/google_workspace/{id}/sync/status/: | ||||
|     get: | ||||
|       operationId: providers_google_workspace_sync_status_retrieve | ||||
| @ -18856,6 +18896,46 @@ paths: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|   /providers/microsoft_entra/{id}/sync/object/: | ||||
|     post: | ||||
|       operationId: providers_microsoft_entra_sync_object_create | ||||
|       description: Sync/Re-sync a single user/group object | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: id | ||||
|         schema: | ||||
|           type: integer | ||||
|         description: A unique integer value identifying this Microsoft Entra Provider. | ||||
|         required: true | ||||
|       tags: | ||||
|       - providers | ||||
|       requestBody: | ||||
|         content: | ||||
|           application/json: | ||||
|             schema: | ||||
|               $ref: '#/components/schemas/SyncObjectRequest' | ||||
|         required: true | ||||
|       security: | ||||
|       - authentik: [] | ||||
|       responses: | ||||
|         '200': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/SyncObjectResult' | ||||
|           description: '' | ||||
|         '400': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/ValidationError' | ||||
|           description: '' | ||||
|         '403': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|   /providers/microsoft_entra/{id}/sync/status/: | ||||
|     get: | ||||
|       operationId: providers_microsoft_entra_sync_status_retrieve | ||||
| @ -21346,6 +21426,46 @@ paths: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|   /providers/scim/{id}/sync/object/: | ||||
|     post: | ||||
|       operationId: providers_scim_sync_object_create | ||||
|       description: Sync/Re-sync a single user/group object | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: id | ||||
|         schema: | ||||
|           type: integer | ||||
|         description: A unique integer value identifying this SCIM Provider. | ||||
|         required: true | ||||
|       tags: | ||||
|       - providers | ||||
|       requestBody: | ||||
|         content: | ||||
|           application/json: | ||||
|             schema: | ||||
|               $ref: '#/components/schemas/SyncObjectRequest' | ||||
|         required: true | ||||
|       security: | ||||
|       - authentik: [] | ||||
|       responses: | ||||
|         '200': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/SyncObjectResult' | ||||
|           description: '' | ||||
|         '400': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/ValidationError' | ||||
|           description: '' | ||||
|         '403': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|   /providers/scim/{id}/sync/status/: | ||||
|     get: | ||||
|       operationId: providers_scim_sync_status_retrieve | ||||
| @ -51354,6 +51474,34 @@ components: | ||||
|       - user_email | ||||
|       - user_upn | ||||
|       type: string | ||||
|     SyncObjectModelEnum: | ||||
|       enum: | ||||
|       - authentik.core.models.User | ||||
|       - authentik.core.models.Group | ||||
|       type: string | ||||
|     SyncObjectRequest: | ||||
|       type: object | ||||
|       description: Sync object serializer | ||||
|       properties: | ||||
|         sync_object_model: | ||||
|           $ref: '#/components/schemas/SyncObjectModelEnum' | ||||
|         sync_object_id: | ||||
|           type: string | ||||
|           minLength: 1 | ||||
|       required: | ||||
|       - sync_object_id | ||||
|       - sync_object_model | ||||
|     SyncObjectResult: | ||||
|       type: object | ||||
|       description: Result of a single object sync | ||||
|       properties: | ||||
|         messages: | ||||
|           type: array | ||||
|           items: | ||||
|             $ref: '#/components/schemas/LogEvent' | ||||
|           readOnly: true | ||||
|       required: | ||||
|       - messages | ||||
|     SyncStatus: | ||||
|       type: object | ||||
|       description: Provider sync status | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||
| import "@goauthentik/elements/forms/ModalForm"; | ||||
| import "@goauthentik/elements/sync/SyncObjectForm"; | ||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, html } from "lit"; | ||||
| import { customElement, property } from "lit/decorators.js"; | ||||
|  | ||||
| import { GoogleWorkspaceProviderGroup, ProvidersApi } from "@goauthentik/api"; | ||||
| import { GoogleWorkspaceProviderGroup, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api"; | ||||
|  | ||||
| @customElement("ak-provider-google-workspace-groups-list") | ||||
| export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProviderGroup> { | ||||
| @ -22,6 +24,23 @@ export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProvi | ||||
|     checkbox = true; | ||||
|     clearOnRefresh = true; | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}> | ||||
|                 <span slot="submit">${msg("Sync")}</span> | ||||
|                 <span slot="header">${msg("Sync User")}</span> | ||||
|                 <ak-sync-object-form | ||||
|                     .provider=${this.providerId} | ||||
|                     model=${SyncObjectModelEnum.Group} | ||||
|                     .sync=${new ProvidersApi(DEFAULT_CONFIG) | ||||
|                         .providersGoogleWorkspaceSyncObjectCreate} | ||||
|                     slot="form" | ||||
|                 > | ||||
|                 </ak-sync-object-form> | ||||
|                 <button slot="trigger" class="pf-c-button pf-m-primary">${msg("Sync")}</button> | ||||
|             </ak-forms-modal> | ||||
|             ${super.renderToolbar()}`; | ||||
|     } | ||||
|  | ||||
|     renderToolbarSelected(): TemplateResult { | ||||
|         const disabled = this.selectedElements.length < 1; | ||||
|         return html`<ak-forms-delete-bulk | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||
| import "@goauthentik/elements/forms/ModalForm"; | ||||
| import "@goauthentik/elements/sync/SyncObjectForm"; | ||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, html } from "lit"; | ||||
| import { customElement, property } from "lit/decorators.js"; | ||||
|  | ||||
| import { GoogleWorkspaceProviderUser, ProvidersApi } from "@goauthentik/api"; | ||||
| import { GoogleWorkspaceProviderUser, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api"; | ||||
|  | ||||
| @customElement("ak-provider-google-workspace-users-list") | ||||
| export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProviderUser> { | ||||
| @ -22,6 +24,23 @@ export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProvid | ||||
|     checkbox = true; | ||||
|     clearOnRefresh = true; | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}> | ||||
|                 <span slot="submit">${msg("Sync")}</span> | ||||
|                 <span slot="header">${msg("Sync User")}</span> | ||||
|                 <ak-sync-object-form | ||||
|                     .provider=${this.providerId} | ||||
|                     model=${SyncObjectModelEnum.User} | ||||
|                     .sync=${new ProvidersApi(DEFAULT_CONFIG) | ||||
|                         .providersGoogleWorkspaceSyncObjectCreate} | ||||
|                     slot="form" | ||||
|                 > | ||||
|                 </ak-sync-object-form> | ||||
|                 <button slot="trigger" class="pf-c-button pf-m-primary">${msg("Sync")}</button> | ||||
|             </ak-forms-modal> | ||||
|             ${super.renderToolbar()}`; | ||||
|     } | ||||
|  | ||||
|     renderToolbarSelected(): TemplateResult { | ||||
|         const disabled = this.selectedElements.length < 1; | ||||
|         return html`<ak-forms-delete-bulk | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||
| import "@goauthentik/elements/forms/ModalForm"; | ||||
| import "@goauthentik/elements/sync/SyncObjectForm"; | ||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, html } from "lit"; | ||||
| import { customElement, property } from "lit/decorators.js"; | ||||
|  | ||||
| import { MicrosoftEntraProviderGroup, ProvidersApi } from "@goauthentik/api"; | ||||
| import { MicrosoftEntraProviderGroup, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api"; | ||||
|  | ||||
| @customElement("ak-provider-microsoft-entra-groups-list") | ||||
| export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProviderGroup> { | ||||
| @ -19,6 +21,23 @@ export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProvide | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}> | ||||
|                 <span slot="submit">${msg("Sync")}</span> | ||||
|                 <span slot="header">${msg("Sync User")}</span> | ||||
|                 <ak-sync-object-form | ||||
|                     .provider=${this.providerId} | ||||
|                     model=${SyncObjectModelEnum.Group} | ||||
|                     .sync=${new ProvidersApi(DEFAULT_CONFIG) | ||||
|                         .providersMicrosoftEntraSyncObjectCreate} | ||||
|                     slot="form" | ||||
|                 > | ||||
|                 </ak-sync-object-form> | ||||
|                 <button slot="trigger" class="pf-c-button pf-m-primary">${msg("Sync")}</button> | ||||
|             </ak-forms-modal> | ||||
|             ${super.renderToolbar()}`; | ||||
|     } | ||||
|  | ||||
|     renderToolbarSelected(): TemplateResult { | ||||
|         const disabled = this.selectedElements.length < 1; | ||||
|         return html`<ak-forms-delete-bulk | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||
| import "@goauthentik/elements/forms/ModalForm"; | ||||
| import "@goauthentik/elements/sync/SyncObjectForm"; | ||||
| import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, html } from "lit"; | ||||
| import { customElement, property } from "lit/decorators.js"; | ||||
|  | ||||
| import { MicrosoftEntraProviderUser, ProvidersApi } from "@goauthentik/api"; | ||||
| import { MicrosoftEntraProviderUser, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api"; | ||||
|  | ||||
| @customElement("ak-provider-microsoft-entra-users-list") | ||||
| export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProviderUser> { | ||||
| @ -22,6 +24,23 @@ export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProvider | ||||
|     checkbox = true; | ||||
|     clearOnRefresh = true; | ||||
|  | ||||
|     renderToolbar(): TemplateResult { | ||||
|         return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}> | ||||
|                 <span slot="submit">${msg("Sync")}</span> | ||||
|                 <span slot="header">${msg("Sync User")}</span> | ||||
|                 <ak-sync-object-form | ||||
|                     .provider=${this.providerId} | ||||
|                     model=${SyncObjectModelEnum.User} | ||||
|                     .sync=${new ProvidersApi(DEFAULT_CONFIG) | ||||
|                         .providersMicrosoftEntraSyncObjectCreate} | ||||
|                     slot="form" | ||||
|                 > | ||||
|                 </ak-sync-object-form> | ||||
|                 <button slot="trigger" class="pf-c-button pf-m-primary">${msg("Sync")}</button> | ||||
|             </ak-forms-modal> | ||||
|             ${super.renderToolbar()}`; | ||||
|     } | ||||
|  | ||||
|     renderToolbarSelected(): TemplateResult { | ||||
|         const disabled = this.selectedElements.length < 1; | ||||
|         return html`<ak-forms-delete-bulk | ||||
|  | ||||
							
								
								
									
										131
									
								
								web/src/elements/sync/SyncObjectForm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								web/src/elements/sync/SyncObjectForm.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | ||||
| import "@goauthentik/admin/common/ak-flow-search/ak-flow-search-no-default"; | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import { Form } from "@goauthentik/elements/forms/Form"; | ||||
| import "@goauthentik/elements/forms/HorizontalFormElement"; | ||||
| import "@goauthentik/elements/forms/SearchSelect"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, html, nothing } from "lit"; | ||||
| import { customElement, property } from "lit/decorators.js"; | ||||
|  | ||||
| import { | ||||
|     CoreApi, | ||||
|     CoreGroupsListRequest, | ||||
|     CoreUsersListRequest, | ||||
|     Group, | ||||
|     InitOverrideFunction, | ||||
|     SyncObjectModelEnum, | ||||
|     SyncObjectRequest, | ||||
|     SyncObjectResult, | ||||
|     User, | ||||
| } from "@goauthentik/api"; | ||||
|  | ||||
| @customElement("ak-sync-object-form") | ||||
| export class SyncObjectForm extends Form<SyncObjectRequest> { | ||||
|     @property({ type: Number }) | ||||
|     provider?: number; | ||||
|  | ||||
|     @property() | ||||
|     model: SyncObjectModelEnum = SyncObjectModelEnum.UnknownDefaultOpenApi; | ||||
|  | ||||
|     @property({ attribute: false }) | ||||
|     result?: SyncObjectResult; | ||||
|  | ||||
|     @property({ attribute: false }) | ||||
|     sync: ( | ||||
|         requestParameters: { | ||||
|             id: number; | ||||
|             syncObjectRequest: SyncObjectRequest; | ||||
|         }, | ||||
|         initOverrides?: RequestInit | InitOverrideFunction, | ||||
|     ) => Promise<SyncObjectResult> = (_, __) => { | ||||
|         return Promise.reject(); | ||||
|     }; | ||||
|  | ||||
|     getSuccessMessage(): string { | ||||
|         return msg("Successfully triggered sync."); | ||||
|     } | ||||
|  | ||||
|     async send(data: SyncObjectRequest): Promise<void> { | ||||
|         data.syncObjectModel = this.model; | ||||
|         this.result = await this.sync({ | ||||
|             id: this.provider || 0, | ||||
|             syncObjectRequest: data, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     renderSelectUser() { | ||||
|         return html`<ak-form-element-horizontal label=${msg("User")} name="syncObjectId"> | ||||
|             <ak-search-select | ||||
|                 .fetchObjects=${async (query?: string): Promise<User[]> => { | ||||
|                     const args: CoreUsersListRequest = { | ||||
|                         ordering: "username", | ||||
|                     }; | ||||
|                     if (query !== undefined) { | ||||
|                         args.search = query; | ||||
|                     } | ||||
|                     const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args); | ||||
|                     return users.results; | ||||
|                 }} | ||||
|                 .renderElement=${(user: User): string => { | ||||
|                     return user.username; | ||||
|                 }} | ||||
|                 .renderDescription=${(user: User): TemplateResult => { | ||||
|                     return html`${user.name}`; | ||||
|                 }} | ||||
|                 .value=${(user: User | undefined): number | undefined => { | ||||
|                     return user?.pk; | ||||
|                 }} | ||||
|             > | ||||
|             </ak-search-select> | ||||
|         </ak-form-element-horizontal>`; | ||||
|     } | ||||
|  | ||||
|     renderSelectGroup() { | ||||
|         return html` <ak-form-element-horizontal label=${msg("Group")} name="syncObjectId"> | ||||
|             <ak-search-select | ||||
|                 .fetchObjects=${async (query?: string): Promise<Group[]> => { | ||||
|                     const args: CoreGroupsListRequest = { | ||||
|                         ordering: "name", | ||||
|                     }; | ||||
|                     if (query !== undefined) { | ||||
|                         args.search = query; | ||||
|                     } | ||||
|                     const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList(args); | ||||
|                     return groups.results; | ||||
|                 }} | ||||
|                 .renderElement=${(group: Group): string => { | ||||
|                     return group.name; | ||||
|                 }} | ||||
|                 .value=${(group: Group | undefined): string | undefined => { | ||||
|                     return group?.pk; | ||||
|                 }} | ||||
|             > | ||||
|             </ak-search-select> | ||||
|         </ak-form-element-horizontal>`; | ||||
|     } | ||||
|  | ||||
|     renderResult(): TemplateResult { | ||||
|         return html`<ak-form-element-horizontal label=${msg("Log messages")}> | ||||
|             <div class="pf-c-form__group-label"> | ||||
|                 <div class="c-form__horizontal-group"> | ||||
|                     <dl class="pf-c-description-list pf-m-horizontal"> | ||||
|                         <ak-log-viewer .logs=${this.result?.messages}></ak-log-viewer> | ||||
|                     </dl> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </ak-form-element-horizontal> `; | ||||
|     } | ||||
|  | ||||
|     renderForm() { | ||||
|         return html` ${this.model === SyncObjectModelEnum.User ? this.renderSelectUser() : nothing} | ||||
|         ${this.model === SyncObjectModelEnum.Group ? this.renderSelectGroup() : nothing} | ||||
|         ${this.result ? this.renderResult() : html``}`; | ||||
|     } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|     interface HTMLElementTagNameMap { | ||||
|         "ak-sync-object-form": SyncObjectForm; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Jens L.
					Jens L.