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:
Jens L.
2024-08-22 16:38:55 +02:00
committed by GitHub
parent e428e4cf5e
commit 46acab3b2e
11 changed files with 424 additions and 14 deletions

View File

@ -6,7 +6,10 @@ from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.api import EnterpriseRequiredMixin from authentik.enterprise.api import EnterpriseRequiredMixin
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProvider 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 from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
@ -52,3 +55,4 @@ class GoogleWorkspaceProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixi
search_fields = ["name"] search_fields = ["name"]
ordering = ["name"] ordering = ["name"]
sync_single_task = google_workspace_sync sync_single_task = google_workspace_sync
sync_objects_task = google_workspace_sync_objects

View File

@ -6,7 +6,10 @@ from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.api import EnterpriseRequiredMixin from authentik.enterprise.api import EnterpriseRequiredMixin
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider 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 from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
@ -50,3 +53,4 @@ class MicrosoftEntraProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin
search_fields = ["name"] search_fields = ["name"]
ordering = ["name"] ordering = ["name"]
sync_single_task = microsoft_entra_sync sync_single_task = microsoft_entra_sync
sync_objects_task = microsoft_entra_sync_objects

View File

@ -1,16 +1,19 @@
from collections.abc import Callable from celery import Task
from django.utils.text import slugify from django.utils.text import slugify
from drf_spectacular.utils import OpenApiResponse, extend_schema from drf_spectacular.utils import OpenApiResponse, extend_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 BooleanField from rest_framework.fields import BooleanField, CharField, ChoiceField
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 authentik.core.api.utils import ModelSerializer, PassiveSerializer 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.api.tasks import SystemTaskSerializer
from authentik.events.logs import LogEvent, LogEventSerializer
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider 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): class SyncStatusSerializer(PassiveSerializer):
@ -20,10 +23,29 @@ class SyncStatusSerializer(PassiveSerializer):
tasks = SystemTaskSerializer(many=True, read_only=True) 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: class OutgoingSyncProviderStatusMixin:
"""Common API Endpoints for Outgoing sync providers""" """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( @extend_schema(
responses={ responses={
@ -36,7 +58,7 @@ class OutgoingSyncProviderStatusMixin:
detail=True, detail=True,
pagination_class=None, pagination_class=None,
url_path="sync/status", url_path="sync/status",
filter_backends=[], filter_backends=[ObjectFilter],
) )
def sync_status(self, request: Request, pk: int) -> Response: def sync_status(self, request: Request, pk: int) -> Response:
"""Get provider's sync status""" """Get provider's sync status"""
@ -55,6 +77,30 @@ class OutgoingSyncProviderStatusMixin:
} }
return Response(SyncStatusSerializer(status).data) 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: class OutgoingSyncConnectionCreateMixin:
"""Mixin for connection objects that fetches remote data upon creation""" """Mixin for connection objects that fetches remote data upon creation"""

View File

@ -105,7 +105,7 @@ class SyncTasks:
return return
task.set_status(TaskStatus.SUCCESSFUL, *messages) 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) _object_type = path_to_class(object_type)
self.logger = get_logger().bind( self.logger = get_logger().bind(
provider_type=class_to_path(self._provider_model), provider_type=class_to_path(self._provider_model),
@ -120,7 +120,7 @@ class SyncTasks:
client = provider.client_for_model(_object_type) client = provider.client_for_model(_object_type)
except TransientSyncException: except TransientSyncException:
return messages 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: if client.can_discover:
self.logger.debug("starting discover") self.logger.debug("starting discover")
client.discover() client.discover()

View File

@ -6,7 +6,7 @@ from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
from authentik.providers.scim.models import SCIMProvider 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): class SCIMProviderSerializer(ProviderSerializer):
@ -42,3 +42,4 @@ class SCIMProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin, ModelVie
search_fields = ["name", "url"] search_fields = ["name", "url"]
ordering = ["name", "url"] ordering = ["name", "url"]
sync_single_task = scim_sync sync_single_task = scim_sync
sync_objects_task = scim_sync_objects

View File

@ -17853,6 +17853,46 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' 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/: /providers/google_workspace/{id}/sync/status/:
get: get:
operationId: providers_google_workspace_sync_status_retrieve operationId: providers_google_workspace_sync_status_retrieve
@ -18856,6 +18896,46 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' 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/: /providers/microsoft_entra/{id}/sync/status/:
get: get:
operationId: providers_microsoft_entra_sync_status_retrieve operationId: providers_microsoft_entra_sync_status_retrieve
@ -21346,6 +21426,46 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' 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/: /providers/scim/{id}/sync/status/:
get: get:
operationId: providers_scim_sync_status_retrieve operationId: providers_scim_sync_status_retrieve
@ -51354,6 +51474,34 @@ components:
- user_email - user_email
- user_upn - user_upn
type: string 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: SyncStatus:
type: object type: object
description: Provider sync status description: Provider sync status

View File

@ -1,12 +1,14 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/DeleteBulkForm"; 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 { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { GoogleWorkspaceProviderGroup, ProvidersApi } from "@goauthentik/api"; import { GoogleWorkspaceProviderGroup, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
@customElement("ak-provider-google-workspace-groups-list") @customElement("ak-provider-google-workspace-groups-list")
export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProviderGroup> { export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProviderGroup> {
@ -22,6 +24,23 @@ export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProvi
checkbox = true; checkbox = true;
clearOnRefresh = 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 { renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1; const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk return html`<ak-forms-delete-bulk

View File

@ -1,12 +1,14 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/DeleteBulkForm"; 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 { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { GoogleWorkspaceProviderUser, ProvidersApi } from "@goauthentik/api"; import { GoogleWorkspaceProviderUser, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
@customElement("ak-provider-google-workspace-users-list") @customElement("ak-provider-google-workspace-users-list")
export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProviderUser> { export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProviderUser> {
@ -22,6 +24,23 @@ export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProvid
checkbox = true; checkbox = true;
clearOnRefresh = 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 { renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1; const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk return html`<ak-forms-delete-bulk

View File

@ -1,12 +1,14 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/DeleteBulkForm"; 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 { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { MicrosoftEntraProviderGroup, ProvidersApi } from "@goauthentik/api"; import { MicrosoftEntraProviderGroup, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
@customElement("ak-provider-microsoft-entra-groups-list") @customElement("ak-provider-microsoft-entra-groups-list")
export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProviderGroup> { export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProviderGroup> {
@ -19,6 +21,23 @@ export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProvide
return true; 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 { renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1; const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk return html`<ak-forms-delete-bulk

View File

@ -1,12 +1,14 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/DeleteBulkForm"; 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 { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { MicrosoftEntraProviderUser, ProvidersApi } from "@goauthentik/api"; import { MicrosoftEntraProviderUser, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
@customElement("ak-provider-microsoft-entra-users-list") @customElement("ak-provider-microsoft-entra-users-list")
export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProviderUser> { export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProviderUser> {
@ -22,6 +24,23 @@ export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProvider
checkbox = true; checkbox = true;
clearOnRefresh = 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 { renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1; const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk return html`<ak-forms-delete-bulk

View 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;
}
}