sources/ldap: add check command to verify ldap connectivity (#7263)
* sources/ldap: add check command to verify ldap connectivity Signed-off-by: Jens Langhammer <jens@goauthentik.io> * default to checking all sources Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start adding an API for ldap connectivity Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add webui for ldap source connection status Signed-off-by: Jens Langhammer <jens@goauthentik.io> * better show sync status, clear previous tasks Signed-off-by: Jens Langhammer <jens@goauthentik.io> * set timeout on redis lock for ldap sync Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix py lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix web lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		@ -44,11 +44,11 @@ export class LDAPSyncStatusChart extends AKChart<SyncStatus[]> {
 | 
			
		||||
        await Promise.all(
 | 
			
		||||
            sources.results.map(async (element) => {
 | 
			
		||||
                try {
 | 
			
		||||
                    const health = await api.sourcesLdapSyncStatusList({
 | 
			
		||||
                    const health = await api.sourcesLdapSyncStatusRetrieve({
 | 
			
		||||
                        slug: element.slug,
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    health.forEach((task) => {
 | 
			
		||||
                    health.tasks.forEach((task) => {
 | 
			
		||||
                        if (task.status !== TaskStatusEnum.Successful) {
 | 
			
		||||
                            metrics.failed += 1;
 | 
			
		||||
                        }
 | 
			
		||||
@ -60,7 +60,7 @@ export class LDAPSyncStatusChart extends AKChart<SyncStatus[]> {
 | 
			
		||||
                            metrics.healthy += 1;
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    if (health.length < 1) {
 | 
			
		||||
                    if (health.tasks.length < 1) {
 | 
			
		||||
                        metrics.unsynced += 1;
 | 
			
		||||
                    }
 | 
			
		||||
                } catch {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								web/src/admin/sources/ldap/LDAPSourceConnectivity.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								web/src/admin/sources/ldap/LDAPSourceConnectivity.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
import { AKElement } from "@goauthentik/app/elements/Base";
 | 
			
		||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFList from "@patternfly/patternfly/components/List/list.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-source-ldap-connectivity")
 | 
			
		||||
export class LDAPSourceConnectivity extends AKElement {
 | 
			
		||||
    @property()
 | 
			
		||||
    connectivity?: {
 | 
			
		||||
        [key: string]: {
 | 
			
		||||
            [key: string]: string;
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [PFBase, PFList];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): TemplateResult {
 | 
			
		||||
        if (!this.connectivity) {
 | 
			
		||||
            return html``;
 | 
			
		||||
        }
 | 
			
		||||
        return html`<ul class="pf-c-list">
 | 
			
		||||
            ${Object.keys(this.connectivity).map((serverKey) => {
 | 
			
		||||
                let serverLabel = html`${serverKey}`;
 | 
			
		||||
                if (serverKey === "__all__") {
 | 
			
		||||
                    serverLabel = html`<b>${msg("Global status")}</b>`;
 | 
			
		||||
                }
 | 
			
		||||
                const server = this.connectivity![serverKey];
 | 
			
		||||
                const content = html`${serverLabel}: ${server.status}`;
 | 
			
		||||
                let tooltip = html`${content}`;
 | 
			
		||||
                if (server.status === "ok") {
 | 
			
		||||
                    tooltip = html`<pf-tooltip position="top">
 | 
			
		||||
                        <ul slot="content" class="pf-c-list">
 | 
			
		||||
                            <li>${msg("Vendor")}: ${server.vendor}</li>
 | 
			
		||||
                            <li>${msg("Version")}: ${server.version}</li>
 | 
			
		||||
                        </ul>
 | 
			
		||||
                        ${content}
 | 
			
		||||
                    </pf-tooltip>`;
 | 
			
		||||
                }
 | 
			
		||||
                return html`<li>${tooltip}</li>`;
 | 
			
		||||
            })}
 | 
			
		||||
        </ul>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
import "@goauthentik/admin/sources/ldap/LDAPSourceConnectivity";
 | 
			
		||||
import "@goauthentik/admin/sources/ldap/LDAPSourceForm";
 | 
			
		||||
import "@goauthentik/app/elements/rbac/ObjectPermissionsPage";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
@ -25,9 +26,9 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    LDAPSource,
 | 
			
		||||
    LDAPSyncStatus,
 | 
			
		||||
    RbacPermissionsAssignedByUsersListModelEnum,
 | 
			
		||||
    SourcesApi,
 | 
			
		||||
    Task,
 | 
			
		||||
    TaskStatusEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@ -48,7 +49,7 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
    source!: LDAPSource;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    syncState: Task[] = [];
 | 
			
		||||
    syncState?: LDAPSyncStatus;
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [PFBase, PFPage, PFButton, PFGrid, PFContent, PFCard, PFDescriptionList, PFList];
 | 
			
		||||
@ -62,6 +63,51 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderSyncStatus(): TemplateResult {
 | 
			
		||||
        if (!this.syncState) {
 | 
			
		||||
            return html`${msg("No sync status.")}`;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.syncState.isRunning) {
 | 
			
		||||
            return html`${msg("Sync currently running.")}`;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.syncState.tasks.length < 1) {
 | 
			
		||||
            return html`${msg("Not synced yet.")}`;
 | 
			
		||||
        }
 | 
			
		||||
        return html`
 | 
			
		||||
            <ul class="pf-c-list">
 | 
			
		||||
                ${this.syncState.tasks.map((task) => {
 | 
			
		||||
                    let header = "";
 | 
			
		||||
                    if (task.status === TaskStatusEnum.Warning) {
 | 
			
		||||
                        header = msg("Task finished with warnings");
 | 
			
		||||
                    } else if (task.status === TaskStatusEnum.Error) {
 | 
			
		||||
                        header = msg("Task finished with errors");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        header = msg(str`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`);
 | 
			
		||||
                    }
 | 
			
		||||
                    return html`<li>
 | 
			
		||||
                        <p>${task.taskName}</p>
 | 
			
		||||
                        <ul class="pf-c-list">
 | 
			
		||||
                            <li>${header}</li>
 | 
			
		||||
                            ${task.messages.map((m) => {
 | 
			
		||||
                                return html`<li>${m}</li>`;
 | 
			
		||||
                            })}
 | 
			
		||||
                        </ul>
 | 
			
		||||
                    </li> `;
 | 
			
		||||
                })}
 | 
			
		||||
            </ul>
 | 
			
		||||
        `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    load(): void {
 | 
			
		||||
        new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
            .sourcesLdapSyncStatusRetrieve({
 | 
			
		||||
                slug: this.source.slug,
 | 
			
		||||
            })
 | 
			
		||||
            .then((state) => {
 | 
			
		||||
                this.syncState = state;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): TemplateResult {
 | 
			
		||||
        if (!this.source) {
 | 
			
		||||
            return html``;
 | 
			
		||||
@ -72,13 +118,7 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
                data-tab-title="${msg("Overview")}"
 | 
			
		||||
                class="pf-c-page__main-section pf-m-no-padding-mobile"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .sourcesLdapSyncStatusList({
 | 
			
		||||
                            slug: this.source.slug,
 | 
			
		||||
                        })
 | 
			
		||||
                        .then((state) => {
 | 
			
		||||
                            this.syncState = state;
 | 
			
		||||
                        });
 | 
			
		||||
                    this.load();
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                <div class="pf-l-grid pf-m-gutter">
 | 
			
		||||
@ -137,42 +177,25 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
                            </ak-forms-modal>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-12-col">
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-2-col">
 | 
			
		||||
                        <div class="pf-c-card__title">
 | 
			
		||||
                            <p>${msg("Connectivity")}</p>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            <ak-source-ldap-connectivity
 | 
			
		||||
                                .connectivity=${this.source.connectivity}
 | 
			
		||||
                            ></ak-source-ldap-connectivity>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-10-col">
 | 
			
		||||
                        <div class="pf-c-card__title">
 | 
			
		||||
                            <p>${msg("Sync status")}</p>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            ${this.syncState.length < 1
 | 
			
		||||
                                ? html`<p>${msg("Not synced yet.")}</p>`
 | 
			
		||||
                                : html`
 | 
			
		||||
                                      <ul class="pf-c-list">
 | 
			
		||||
                                          ${this.syncState.map((task) => {
 | 
			
		||||
                                              let header = "";
 | 
			
		||||
                                              if (task.status === TaskStatusEnum.Warning) {
 | 
			
		||||
                                                  header = msg("Task finished with warnings");
 | 
			
		||||
                                              } else if (task.status === TaskStatusEnum.Error) {
 | 
			
		||||
                                                  header = msg("Task finished with errors");
 | 
			
		||||
                                              } else {
 | 
			
		||||
                                                  header = msg(
 | 
			
		||||
                                                      str`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`,
 | 
			
		||||
                                                  );
 | 
			
		||||
                                              }
 | 
			
		||||
                                              return html`<li>
 | 
			
		||||
                                                  <p>${task.taskName}</p>
 | 
			
		||||
                                                  <ul class="pf-c-list">
 | 
			
		||||
                                                      <li>${header}</li>
 | 
			
		||||
                                                      ${task.messages.map((m) => {
 | 
			
		||||
                                                          return html`<li>${m}</li>`;
 | 
			
		||||
                                                      })}
 | 
			
		||||
                                                  </ul>
 | 
			
		||||
                                              </li> `;
 | 
			
		||||
                                          })}
 | 
			
		||||
                                      </ul>
 | 
			
		||||
                                  `}
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__body">${this.renderSyncStatus()}</div>
 | 
			
		||||
                        <div class="pf-c-card__footer">
 | 
			
		||||
                            <ak-action-button
 | 
			
		||||
                                class="pf-m-secondary"
 | 
			
		||||
                                ?disabled=${this.syncState?.isRunning}
 | 
			
		||||
                                .apiRequest=${() => {
 | 
			
		||||
                                    return new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
                                        .sourcesLdapPartialUpdate({
 | 
			
		||||
@ -186,6 +209,7 @@ export class LDAPSourceViewPage extends AKElement {
 | 
			
		||||
                                                    composed: true,
 | 
			
		||||
                                                }),
 | 
			
		||||
                                            );
 | 
			
		||||
                                            this.load();
 | 
			
		||||
                                        });
 | 
			
		||||
                                }}
 | 
			
		||||
                            >
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user