import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search"; import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/Radio"; import "@goauthentik/elements/forms/SearchSelect"; import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { CoreApi, CoreGroupsListRequest, FlowsInstancesListDesignationEnum, Group, LDAPAPIAccessMode, LDAPProvider, ProvidersApi, } from "@goauthentik/api"; @customElement("ak-provider-ldap-form") export class LDAPProviderFormPage extends WithBrandConfig(BaseProviderForm) { async loadInstance(pk: number): Promise { return new ProvidersApi(DEFAULT_CONFIG).providersLdapRetrieve({ id: pk, }); } async send(data: LDAPProvider): Promise { if (this.instance) { return new ProvidersApi(DEFAULT_CONFIG).providersLdapUpdate({ id: this.instance.pk || 0, lDAPProviderRequest: data, }); } else { return new ProvidersApi(DEFAULT_CONFIG).providersLdapCreate({ lDAPProviderRequest: data, }); } } // All Provider objects have an Authorization flow, but not all providers have an Authentication // flow. LDAP needs only one field, but it is not an Authorization field, it is an // Authentication field. So, yeah, we're using the authorization field to store the // authentication information, which is why the ak-branded-flow-search call down there looks so // weird-- we're looking up Authentication flows, but we're storing them in the Authorization // field of the target Provider. renderForm(): TemplateResult { return html`

${msg("Flow used for users to authenticate.")}

=> { 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; }} .selected=${(group: Group): boolean => { return group.pk === this.instance?.searchGroup; }} ?blankable=${true} >

${msg( "Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed.", )}

${msg("Configure how the outpost authenticates requests.")}

${msg("Configure how the outpost queries the core authentik server's users.")}

${msg( "When enabled, code-based multi-factor authentication can be used by appending a semicolon and the TOTP code to the password. This should only be enabled if all users that will bind to this provider have a TOTP device configured, as otherwise a password may incorrectly be rejected if it contains a semicolon.", )}

${msg("Protocol settings")}

${msg( "LDAP DN under which bind requests and search requests can be made.", )}

${msg( "The certificate for the above configured Base DN. As a fallback, the provider uses a self-signed certificate.", )}

${msg( "DNS name for which the above configured certificate should be used. The certificate cannot be detected based on the base DN, as the SSL/TLS negotiation happens before such data is exchanged.", )}

${msg( "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber", )}

${msg( "The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber", )}

`; } }