diff --git a/web/src/admin/common/ak-crypto-certificate-search.ts b/web/src/admin/common/ak-crypto-certificate-search.ts index 3612b722b..15fe347c2 100644 --- a/web/src/admin/common/ak-crypto-certificate-search.ts +++ b/web/src/admin/common/ak-crypto-certificate-search.ts @@ -1,6 +1,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { AKElement } from "@goauthentik/elements/Base"; import { SearchSelect } from "@goauthentik/elements/forms/SearchSelect"; +import "@goauthentik/elements/forms/SearchSelect"; import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter"; import { html } from "lit"; @@ -29,7 +30,7 @@ const renderValue = (item: CertificateKeyPair | undefined): string | undefined = */ @customElement("ak-crypto-certificate-search") -export class CryptoCertificateSearch extends CustomListenerElement(AKElement) { +export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement) { @property({ type: String, reflect: true }) certificate?: string; @@ -126,4 +127,4 @@ export class CryptoCertificateSearch extends CustomListenerElement(AKElement) { } } -export default CryptoCertificateSearch; +export default AkCryptoCertificateSearch; diff --git a/web/src/admin/common/ak-flow-search/FlowSearch.ts b/web/src/admin/common/ak-flow-search/FlowSearch.ts index 6b4c04427..c1627edcf 100644 --- a/web/src/admin/common/ak-flow-search/FlowSearch.ts +++ b/web/src/admin/common/ak-flow-search/FlowSearch.ts @@ -2,6 +2,7 @@ import { RenderFlowOption } from "@goauthentik/admin/flows/utils"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { AKElement } from "@goauthentik/elements/Base"; import { SearchSelect } from "@goauthentik/elements/forms/SearchSelect"; +import "@goauthentik/elements/forms/SearchSelect"; import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter"; import { html } from "lit"; diff --git a/web/src/admin/common/stories/ak-crypto-certificate-search.stories.ts b/web/src/admin/common/stories/ak-crypto-certificate-search.stories.ts new file mode 100644 index 000000000..ac31ec2b9 --- /dev/null +++ b/web/src/admin/common/stories/ak-crypto-certificate-search.stories.ts @@ -0,0 +1,91 @@ +import "@goauthentik/elements/forms/HorizontalFormElement"; +import "@goauthentik/elements/messages/MessageContainer"; +import { Meta } from "@storybook/web-components"; + +import { TemplateResult, html } from "lit"; + +import "../ak-crypto-certificate-search"; +import AkCryptoCertificateSearch from "../ak-crypto-certificate-search"; +import { dummyCryptoCertsSearch } from "./samples"; + +const metadata: Meta = { + title: "Components / Searches / CryptoCertificateKeyPair", + component: "ak-crypto-certificate-search", + parameters: { + docs: { + description: { + component: "A search function for cryptographic certificates in Authentik", + }, + }, + mockData: [ + { + url: "/api/v3/crypto/certificatekeypairs/?has_key=true&include_details=false&ordering=name", + method: "GET", + status: 200, + response: dummyCryptoCertsSearch, + }, + ], + }, + argTypes: { + // Typescript is unaware that arguments for components are treated as properties, and + // properties are typically renamed to lower case, even if the variable is not. + // @ts-expect-error + nokey: { + control: "boolean", + description: + "When true, certificates without valid keys will be included in the search", + }, + singleton: { + control: "boolean", + description: + "Supports the SAML Source search: when true, if there is no certificate in the current form and there is one and only one certificate in the Authentik database, use that certificate by default.", + }, + }, +}; + +export default metadata; + +const LIGHT = "pf-t-light"; +function injectTheme() { + setTimeout(() => { + if (!document.body.classList.contains(LIGHT)) { + document.body.classList.add(LIGHT); + } + }); +} + +const container = (testItem: TemplateResult) => { + injectTheme(); + return html`
+ + + ${testItem} +

+    
`; +}; + +export const CryptoCertificateSearch = () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const showMessage = (ev: CustomEvent) => { + const detail = ev.detail; + delete detail["target"]; + document.getElementById("message-pad")!.innerText = `Event: ${JSON.stringify( + detail, + null, + 2, + )}`; + }; + + return container( + html` + `, + ); +}; diff --git a/web/src/admin/common/stories/samples.ts b/web/src/admin/common/stories/samples.ts new file mode 100644 index 000000000..10e683d6e --- /dev/null +++ b/web/src/admin/common/stories/samples.ts @@ -0,0 +1,29 @@ +export const dummyCryptoCertsSearch = { + pagination: { + next: 0, + previous: 0, + count: 1, + current: 1, + total_pages: 1, + start_index: 1, + end_index: 1, + }, + + results: [ + { + pk: "63efd1b8-6c39-4f65-8157-9a406cb37447", + name: "authentik Self-signed Certificate", + fingerprint_sha256: null, + fingerprint_sha1: null, + cert_expiry: null, + cert_subject: null, + private_key_available: true, + private_key_type: null, + certificate_download_url: + "/api/v3/crypto/certificatekeypairs/63efd1b8-6c39-4f65-8157-9a406cb37447/view_certificate/?download", + private_key_download_url: + "/api/v3/crypto/certificatekeypairs/63efd1b8-6c39-4f65-8157-9a406cb37447/view_private_key/?download", + managed: null, + }, + ], +}; diff --git a/web/src/admin/outposts/ServiceConnectionDockerForm.ts b/web/src/admin/outposts/ServiceConnectionDockerForm.ts index c0b1c499d..c6840d50d 100644 --- a/web/src/admin/outposts/ServiceConnectionDockerForm.ts +++ b/web/src/admin/outposts/ServiceConnectionDockerForm.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/common/ak-crypto-certificate-search"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; import "@goauthentik/elements/forms/HorizontalFormElement"; @@ -9,13 +10,7 @@ import { TemplateResult, html } from "lit"; import { customElement } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; -import { - CertificateKeyPair, - CryptoApi, - CryptoCertificatekeypairsListRequest, - DockerServiceConnection, - OutpostsApi, -} from "@goauthentik/api"; +import { DockerServiceConnection, OutpostsApi } from "@goauthentik/api"; @customElement("ak-service-connection-docker-form") export class ServiceConnectionDockerForm extends ModelForm { @@ -93,33 +88,9 @@ export class ServiceConnectionDockerForm extends ModelForm - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return this.instance?.tlsVerification === item.pk; - }} - ?blankable=${true} - > - +

${msg( "CA which the endpoint's Certificate is verified against. Can be left empty for no validation.", @@ -130,33 +101,9 @@ export class ServiceConnectionDockerForm extends ModelForm - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return this.instance?.tlsAuthentication === item.pk; - }} - ?blankable=${true} - > - +

${msg( "Certificate/Key used for authentication. Can be left empty for no authentication.", diff --git a/web/src/admin/providers/ldap/LDAPProviderForm.ts b/web/src/admin/providers/ldap/LDAPProviderForm.ts index c7adc1002..d89902f6b 100644 --- a/web/src/admin/providers/ldap/LDAPProviderForm.ts +++ b/web/src/admin/providers/ldap/LDAPProviderForm.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; @@ -14,11 +15,8 @@ import { customElement } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { - CertificateKeyPair, CoreApi, CoreGroupsListRequest, - CryptoApi, - CryptoCertificatekeypairsListRequest, FlowsInstancesListDesignationEnum, Group, LDAPAPIAccessMode, @@ -208,35 +206,9 @@ export class LDAPProviderFormPage extends ModelForm {

- => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.certificate; - }} - ?blankable=${true} - > - +

${msg( "The certificate for the above configured Base DN. As a fallback, the provider uses a self-signed certificate.", diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts index d77884d5c..0aac39deb 100644 --- a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts +++ b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts @@ -247,11 +247,10 @@ export class OAuth2ProviderFormPage extends ModelForm { - + >

${msg("Key used to sign the tokens.")}

diff --git a/web/src/admin/providers/proxy/ProxyProviderForm.ts b/web/src/admin/providers/proxy/ProxyProviderForm.ts index 0bd5ac252..8898ebc4a 100644 --- a/web/src/admin/providers/proxy/ProxyProviderForm.ts +++ b/web/src/admin/providers/proxy/ProxyProviderForm.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-flow-search"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; @@ -18,9 +19,6 @@ import PFList from "@patternfly/patternfly/components/List/list.css"; import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css"; import { - CertificateKeyPair, - CryptoApi, - CryptoCertificatekeypairsListRequest, FlowsInstancesListDesignationEnum, PaginatedOAuthSourceList, PaginatedScopeMappingList, @@ -103,7 +101,6 @@ export class ProxyProviderFormPage extends ModelForm { > -<<<<<<< HEAD { "User/Group Attribute used for the password part of the HTTP-Basic Header.", )} > -`; + `; } - + renderModeSelector(): TemplateResult { const setMode = (ev: CustomEvent<{ value: ProxyMode }>) => { this.mode = ev.detail.value; @@ -308,26 +305,7 @@ export class ProxyProviderFormPage extends ModelForm {
-<<<<<<< HEAD -
- ) => { - this.mode = ev.detail.value; - }} - > - - - - -
-=======
${this.renderModeSelector()}
->>>>>>> ak-toggle-group
@@ -346,35 +324,9 @@ export class ProxyProviderFormPage extends ModelForm { ${msg("Advanced protocol settings")}
- => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.certificate; - }} - ?blankable=${true} - > - + { label=${msg("Signing Certificate")} name="signingKp" > - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.signingKp; - }} - ?blankable=${true} - > - +

${msg( "Certificate used to sign outgoing Responses going to the Service Provider.", @@ -216,41 +188,16 @@ export class SAMLProviderFormPage extends ModelForm { label=${msg("Verification Certificate")} name="verificationKp" > - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.verificationKp; - }} - ?blankable=${true} - > - +

${msg( "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.", )}

- { label=${msg("TLS Verification Certificate")} name="peerCertificate" > - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.peerCertificate; - }} - ?blankable=${true} - > - +

${msg( "When connecting to an LDAP Server with TLS, certificates are not checked by default. Specify a keypair to validate the remote certificate.", @@ -246,35 +220,9 @@ export class LDAPSourceForm extends ModelForm { label=${msg("TLS Client authentication certificate")} name="clientCertificate" > - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.clientCertificate; - }} - ?blankable=${true} - > - +

${msg( "Client certificate keypair to authenticate against the LDAP Server's Certificate.", diff --git a/web/src/admin/sources/saml/SAMLSourceForm.ts b/web/src/admin/sources/saml/SAMLSourceForm.ts index 86e7e3bb8..ac168ae5b 100644 --- a/web/src/admin/sources/saml/SAMLSourceForm.ts +++ b/web/src/admin/sources/saml/SAMLSourceForm.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search"; import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText"; import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils"; @@ -18,9 +19,6 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { BindingTypeEnum, CapabilitiesEnum, - CertificateKeyPair, - CryptoApi, - CryptoCertificatekeypairsListRequest, DigestAlgorithmEnum, FlowsInstancesListDesignationEnum, NameIdPolicyEnum, @@ -274,35 +272,9 @@ export class SAMLSourceForm extends ModelForm { - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.signingKp; - }} - ?blankable=${true} - > - +

${msg( "Keypair which is used to sign outgoing requests. Leave empty to disable signing.", @@ -313,34 +285,10 @@ export class SAMLSourceForm extends ModelForm { label=${msg("Verification Certificate")} name="verificationKp" > - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.verificationKp; - }} - ?blankable=${true} - > - +

${msg( "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.", diff --git a/web/src/admin/tenants/TenantForm.ts b/web/src/admin/tenants/TenantForm.ts index 34420b367..9a3b88d61 100644 --- a/web/src/admin/tenants/TenantForm.ts +++ b/web/src/admin/tenants/TenantForm.ts @@ -1,3 +1,4 @@ +import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-flow-search"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; @@ -13,14 +14,7 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement } from "lit/decorators.js"; -import { - CertificateKeyPair, - CoreApi, - CryptoApi, - CryptoCertificatekeypairsListRequest, - FlowsInstancesListDesignationEnum, - Tenant, -} from "@goauthentik/api"; +import { CoreApi, FlowsInstancesListDesignationEnum, Tenant } from "@goauthentik/api"; @customElement("ak-tenant-form") export class TenantForm extends ModelForm { @@ -236,35 +230,9 @@ export class TenantForm extends ModelForm { label=${msg("Web Certificate")} name="webCertificate" > - => { - const args: CryptoCertificatekeypairsListRequest = { - ordering: "name", - hasKey: true, - includeDetails: false, - }; - if (query !== undefined) { - args.search = query; - } - const certificates = await new CryptoApi( - DEFAULT_CONFIG, - ).cryptoCertificatekeypairsList(args); - return certificates.results; - }} - .renderElement=${(item: CertificateKeyPair): string => { - return item.name; - }} - .value=${(item: CertificateKeyPair | undefined): string | undefined => { - return item?.pk; - }} - .selected=${(item: CertificateKeyPair): boolean => { - return item.pk === this.instance?.webCertificate; - }} - ?blankable=${true} - > - +