${msg(
@@ -101,7 +99,7 @@ export class ServiceConnectionDockerForm extends ModelForm
${msg(
diff --git a/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts b/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts
index f087998cc..414bb8922 100644
--- a/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts
+++ b/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts
@@ -25,11 +25,9 @@ export class ServiceConnectionKubernetesForm extends ModelForm<
}
getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated integration.");
- } else {
- return msg("Successfully created integration.");
- }
+ return this.instance
+ ? msg("Successfully updated integration.")
+ : msg("Successfully created integration.");
}
async send(data: KubernetesServiceConnection): Promise {
diff --git a/web/src/admin/policies/BasePolicyForm.ts b/web/src/admin/policies/BasePolicyForm.ts
new file mode 100644
index 000000000..450a653a6
--- /dev/null
+++ b/web/src/admin/policies/BasePolicyForm.ts
@@ -0,0 +1,11 @@
+import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
+
+import { msg } from "@lit/localize";
+
+export abstract class BasePolicyForm extends ModelForm {
+ getSuccessMessage(): string {
+ return this.instance
+ ? msg("Successfully updated policy.")
+ : msg("Successfully created policy.");
+ }
+}
diff --git a/web/src/admin/policies/dummy/DummyPolicyForm.ts b/web/src/admin/policies/dummy/DummyPolicyForm.ts
index 7dcb82ec2..57fe5812d 100644
--- a/web/src/admin/policies/dummy/DummyPolicyForm.ts
+++ b/web/src/admin/policies/dummy/DummyPolicyForm.ts
@@ -1,8 +1,8 @@
+import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -12,21 +12,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { DummyPolicy, PoliciesApi } from "@goauthentik/api";
@customElement("ak-policy-dummy-form")
-export class DummyPolicyForm extends ModelForm {
+export class DummyPolicyForm extends BasePolicyForm {
loadInstance(pk: string): Promise {
return new PoliciesApi(DEFAULT_CONFIG).policiesDummyRetrieve({
policyUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated policy.");
- } else {
- return msg("Successfully created policy.");
- }
- }
-
async send(data: DummyPolicy): Promise {
if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesDummyUpdate({
diff --git a/web/src/admin/policies/event_matcher/EventMatcherPolicyForm.ts b/web/src/admin/policies/event_matcher/EventMatcherPolicyForm.ts
index a8198d8f6..b6d3e00d1 100644
--- a/web/src/admin/policies/event_matcher/EventMatcherPolicyForm.ts
+++ b/web/src/admin/policies/event_matcher/EventMatcherPolicyForm.ts
@@ -1,8 +1,8 @@
+import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -20,21 +20,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-policy-event-matcher-form")
-export class EventMatcherPolicyForm extends ModelForm {
+export class EventMatcherPolicyForm extends BasePolicyForm {
loadInstance(pk: string): Promise {
return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherRetrieve({
policyUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated policy.");
- } else {
- return msg("Successfully created policy.");
- }
- }
-
async send(data: EventMatcherPolicy): Promise {
if (data.action?.toString() === "") data.action = null;
if (data.clientIp?.toString() === "") data.clientIp = null;
diff --git a/web/src/admin/policies/expiry/ExpiryPolicyForm.ts b/web/src/admin/policies/expiry/ExpiryPolicyForm.ts
index 0ec7caae3..c22311571 100644
--- a/web/src/admin/policies/expiry/ExpiryPolicyForm.ts
+++ b/web/src/admin/policies/expiry/ExpiryPolicyForm.ts
@@ -1,8 +1,8 @@
+import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -12,21 +12,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { PasswordExpiryPolicy, PoliciesApi } from "@goauthentik/api";
@customElement("ak-policy-password-expiry-form")
-export class PasswordExpiryPolicyForm extends ModelForm {
+export class PasswordExpiryPolicyForm extends BasePolicyForm {
loadInstance(pk: string): Promise {
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryRetrieve({
policyUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated policy.");
- } else {
- return msg("Successfully created policy.");
- }
- }
-
async send(data: PasswordExpiryPolicy): Promise {
if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryUpdate({
diff --git a/web/src/admin/policies/expression/ExpressionPolicyForm.ts b/web/src/admin/policies/expression/ExpressionPolicyForm.ts
index a00c6584d..01c603914 100644
--- a/web/src/admin/policies/expression/ExpressionPolicyForm.ts
+++ b/web/src/admin/policies/expression/ExpressionPolicyForm.ts
@@ -1,3 +1,4 @@
+import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import { first } from "@goauthentik/common/utils";
@@ -5,7 +6,6 @@ import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -15,21 +15,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { ExpressionPolicy, PoliciesApi } from "@goauthentik/api";
@customElement("ak-policy-expression-form")
-export class ExpressionPolicyForm extends ModelForm {
+export class ExpressionPolicyForm extends BasePolicyForm {
loadInstance(pk: string): Promise {
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionRetrieve({
policyUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated policy.");
- } else {
- return msg("Successfully created policy.");
- }
- }
-
async send(data: ExpressionPolicy): Promise {
if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionUpdate({
diff --git a/web/src/admin/policies/password/PasswordPolicyForm.ts b/web/src/admin/policies/password/PasswordPolicyForm.ts
index 96183b077..08674effa 100644
--- a/web/src/admin/policies/password/PasswordPolicyForm.ts
+++ b/web/src/admin/policies/password/PasswordPolicyForm.ts
@@ -1,8 +1,8 @@
+import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -12,7 +12,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { PasswordPolicy, PoliciesApi } from "@goauthentik/api";
@customElement("ak-policy-password-form")
-export class PasswordPolicyForm extends ModelForm {
+export class PasswordPolicyForm extends BasePolicyForm {
@state()
showStatic = true;
@@ -32,14 +32,6 @@ export class PasswordPolicyForm extends ModelForm {
return policy;
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated policy.");
- } else {
- return msg("Successfully created policy.");
- }
- }
-
async send(data: PasswordPolicy): Promise {
if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordUpdate({
diff --git a/web/src/admin/policies/reputation/ReputationPolicyForm.ts b/web/src/admin/policies/reputation/ReputationPolicyForm.ts
index a32d6febf..d5f12ba1c 100644
--- a/web/src/admin/policies/reputation/ReputationPolicyForm.ts
+++ b/web/src/admin/policies/reputation/ReputationPolicyForm.ts
@@ -1,8 +1,8 @@
+import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -12,21 +12,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { PoliciesApi, ReputationPolicy } from "@goauthentik/api";
@customElement("ak-policy-reputation-form")
-export class ReputationPolicyForm extends ModelForm {
+export class ReputationPolicyForm extends BasePolicyForm {
loadInstance(pk: string): Promise {
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationRetrieve({
policyUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated policy.");
- } else {
- return msg("Successfully created policy.");
- }
- }
-
async send(data: ReputationPolicy): Promise {
if (this.instance) {
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationUpdate({
diff --git a/web/src/admin/property-mappings/BasePropertyMappingForm.ts b/web/src/admin/property-mappings/BasePropertyMappingForm.ts
new file mode 100644
index 000000000..82981b9cf
--- /dev/null
+++ b/web/src/admin/property-mappings/BasePropertyMappingForm.ts
@@ -0,0 +1,11 @@
+import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
+
+import { msg } from "@lit/localize";
+
+export abstract class BasePropertyMappingForm extends ModelForm {
+ getSuccessMessage(): string {
+ return this.instance
+ ? msg("Successfully updated mapping.")
+ : msg("Successfully created mapping.");
+ }
+}
diff --git a/web/src/admin/property-mappings/PropertyMappingLDAPForm.ts b/web/src/admin/property-mappings/PropertyMappingLDAPForm.ts
index 6919ab079..0b5afe81b 100644
--- a/web/src/admin/property-mappings/PropertyMappingLDAPForm.ts
+++ b/web/src/admin/property-mappings/PropertyMappingLDAPForm.ts
@@ -1,9 +1,9 @@
+import { BasePropertyMappingForm } from "@goauthentik/admin/property-mappings/BasePropertyMappingForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -13,21 +13,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { LDAPPropertyMapping, PropertymappingsApi } from "@goauthentik/api";
@customElement("ak-property-mapping-ldap-form")
-export class PropertyMappingLDAPForm extends ModelForm {
+export class PropertyMappingLDAPForm extends BasePropertyMappingForm {
loadInstance(pk: string): Promise {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapRetrieve({
pmUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated mapping.");
- } else {
- return msg("Successfully created mapping.");
- }
- }
-
async send(data: LDAPPropertyMapping): Promise {
if (this.instance) {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapUpdate({
diff --git a/web/src/admin/property-mappings/PropertyMappingNotification.ts b/web/src/admin/property-mappings/PropertyMappingNotification.ts
index 8ab561dec..9991d2a32 100644
--- a/web/src/admin/property-mappings/PropertyMappingNotification.ts
+++ b/web/src/admin/property-mappings/PropertyMappingNotification.ts
@@ -21,11 +21,9 @@ export class PropertyMappingNotification extends ModelForm {
diff --git a/web/src/admin/property-mappings/PropertyMappingSAMLForm.ts b/web/src/admin/property-mappings/PropertyMappingSAMLForm.ts
index 77836c505..101455c84 100644
--- a/web/src/admin/property-mappings/PropertyMappingSAMLForm.ts
+++ b/web/src/admin/property-mappings/PropertyMappingSAMLForm.ts
@@ -1,9 +1,9 @@
+import { BasePropertyMappingForm } from "@goauthentik/admin/property-mappings/BasePropertyMappingForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -13,21 +13,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { PropertymappingsApi, SAMLPropertyMapping } from "@goauthentik/api";
@customElement("ak-property-mapping-saml-form")
-export class PropertyMappingSAMLForm extends ModelForm {
+export class PropertyMappingSAMLForm extends BasePropertyMappingForm {
loadInstance(pk: string): Promise {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlRetrieve({
pmUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated mapping.");
- } else {
- return msg("Successfully created mapping.");
- }
- }
-
async send(data: SAMLPropertyMapping): Promise {
if (this.instance) {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlUpdate({
diff --git a/web/src/admin/property-mappings/PropertyMappingSCIMForm.ts b/web/src/admin/property-mappings/PropertyMappingSCIMForm.ts
index 19bd13aab..30993cbbd 100644
--- a/web/src/admin/property-mappings/PropertyMappingSCIMForm.ts
+++ b/web/src/admin/property-mappings/PropertyMappingSCIMForm.ts
@@ -1,9 +1,9 @@
+import { BasePropertyMappingForm } from "@goauthentik/admin/property-mappings/BasePropertyMappingForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -13,21 +13,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { PropertymappingsApi, SCIMMapping } from "@goauthentik/api";
@customElement("ak-property-mapping-scim-form")
-export class PropertyMappingSCIMForm extends ModelForm {
+export class PropertyMappingSCIMForm extends BasePropertyMappingForm {
loadInstance(pk: string): Promise {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScimRetrieve({
pmUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated mapping.");
- } else {
- return msg("Successfully created mapping.");
- }
- }
-
async send(data: SCIMMapping): Promise {
if (this.instance) {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScimUpdate({
diff --git a/web/src/admin/property-mappings/PropertyMappingScopeForm.ts b/web/src/admin/property-mappings/PropertyMappingScopeForm.ts
index caf2dc11d..3df60889f 100644
--- a/web/src/admin/property-mappings/PropertyMappingScopeForm.ts
+++ b/web/src/admin/property-mappings/PropertyMappingScopeForm.ts
@@ -1,9 +1,9 @@
+import { BasePropertyMappingForm } from "@goauthentik/admin/property-mappings/BasePropertyMappingForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -13,21 +13,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { PropertymappingsApi, ScopeMapping } from "@goauthentik/api";
@customElement("ak-property-mapping-scope-form")
-export class PropertyMappingScopeForm extends ModelForm {
+export class PropertyMappingScopeForm extends BasePropertyMappingForm {
loadInstance(pk: string): Promise {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeRetrieve({
pmUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated mapping.");
- } else {
- return msg("Successfully created mapping.");
- }
- }
-
async send(data: ScopeMapping): Promise {
if (this.instance) {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScopeUpdate({
diff --git a/web/src/admin/providers/BaseProviderForm.ts b/web/src/admin/providers/BaseProviderForm.ts
new file mode 100644
index 000000000..3f6134046
--- /dev/null
+++ b/web/src/admin/providers/BaseProviderForm.ts
@@ -0,0 +1,11 @@
+import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
+
+import { msg } from "@lit/localize";
+
+export abstract class BaseProviderForm extends ModelForm {
+ getSuccessMessage(): string {
+ return this.instance
+ ? msg("Successfully updated provider.")
+ : msg("Successfully created provider.");
+ }
+}
diff --git a/web/src/admin/providers/ldap/LDAPProviderForm.ts b/web/src/admin/providers/ldap/LDAPProviderForm.ts
index f7853e7e1..449a3ad5b 100644
--- a/web/src/admin/providers/ldap/LDAPProviderForm.ts
+++ b/web/src/admin/providers/ldap/LDAPProviderForm.ts
@@ -1,11 +1,11 @@
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
+import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import { rootInterface } from "@goauthentik/elements/Base";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
@@ -25,21 +25,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-provider-ldap-form")
-export class LDAPProviderFormPage extends ModelForm {
+export class LDAPProviderFormPage extends BaseProviderForm {
async loadInstance(pk: number): Promise {
return new ProvidersApi(DEFAULT_CONFIG).providersLdapRetrieve({
id: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated provider.");
- } else {
- return msg("Successfully created provider.");
- }
- }
-
async send(data: LDAPProvider): Promise {
if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersLdapUpdate({
@@ -206,7 +198,7 @@ export class LDAPProviderFormPage extends ModelForm {
${msg(
diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts
index f5b59efe5..000df8cad 100644
--- a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts
+++ b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts
@@ -1,5 +1,6 @@
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
+import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-radio-input";
@@ -7,7 +8,6 @@ import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
import "@goauthentik/elements/utils/TimeDeltaHelp";
@@ -116,7 +116,7 @@ export const redirectUriHelp = html`${redirectUriHelpMessages.map(
*/
@customElement("ak-provider-oauth2-form")
-export class OAuth2ProviderFormPage extends ModelForm {
+export class OAuth2ProviderFormPage extends BaseProviderForm {
propertyMappings?: PaginatedScopeMappingList;
oauthSources?: PaginatedOAuthSourceList;
@@ -143,14 +143,6 @@ export class OAuth2ProviderFormPage extends ModelForm {
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated provider.");
- } else {
- return msg("Successfully created provider.");
- }
- }
-
async send(data: OAuth2Provider): Promise {
if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Update({
diff --git a/web/src/admin/providers/proxy/ProxyProviderForm.ts b/web/src/admin/providers/proxy/ProxyProviderForm.ts
index 9886c9be3..ddd554572 100644
--- a/web/src/admin/providers/proxy/ProxyProviderForm.ts
+++ b/web/src/admin/providers/proxy/ProxyProviderForm.ts
@@ -1,11 +1,11 @@
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
+import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-toggle-group";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import "@goauthentik/elements/utils/TimeDeltaHelp";
@@ -30,7 +30,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-provider-proxy-form")
-export class ProxyProviderFormPage extends ModelForm {
+export class ProxyProviderFormPage extends BaseProviderForm {
static get styles(): CSSResult[] {
return [...super.styles, PFContent, PFList, PFSpacing];
}
@@ -65,14 +65,6 @@ export class ProxyProviderFormPage extends ModelForm {
@state()
mode: ProxyMode = ProxyMode.Proxy;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated provider.");
- } else {
- return msg("Successfully created provider.");
- }
- }
-
async send(data: ProxyProvider): Promise {
data.mode = this.mode;
if (this.mode !== ProxyMode.ForwardDomain) {
@@ -324,7 +316,7 @@ export class ProxyProviderFormPage extends ModelForm {
{
+export class RadiusProviderFormPage extends BaseProviderForm {
loadInstance(pk: number): Promise {
return new ProvidersApi(DEFAULT_CONFIG).providersRadiusRetrieve({
id: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated provider.");
- } else {
- return msg("Successfully created provider.");
- }
- }
-
async send(data: RadiusProvider): Promise {
if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersRadiusUpdate({
diff --git a/web/src/admin/providers/saml/SAMLProviderForm.ts b/web/src/admin/providers/saml/SAMLProviderForm.ts
index c48993e84..006a32545 100644
--- a/web/src/admin/providers/saml/SAMLProviderForm.ts
+++ b/web/src/admin/providers/saml/SAMLProviderForm.ts
@@ -1,9 +1,9 @@
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
+import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
import "@goauthentik/elements/utils/TimeDeltaHelp";
@@ -27,7 +27,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-provider-saml-form")
-export class SAMLProviderFormPage extends ModelForm {
+export class SAMLProviderFormPage extends BaseProviderForm {
loadInstance(pk: number): Promise {
return new ProvidersApi(DEFAULT_CONFIG).providersSamlRetrieve({
id: pk,
@@ -44,14 +44,6 @@ export class SAMLProviderFormPage extends ModelForm {
propertyMappings?: PaginatedSAMLPropertyMappingList;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated provider.");
- } else {
- return msg("Successfully created provider.");
- }
- }
-
async send(data: SAMLProvider): Promise {
if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersSamlUpdate({
@@ -175,7 +167,7 @@ export class SAMLProviderFormPage extends ModelForm {
name="signingKp"
>
${msg(
@@ -188,7 +180,7 @@ export class SAMLProviderFormPage extends ModelForm {
name="verificationKp"
>
diff --git a/web/src/admin/providers/scim/SCIMProviderForm.ts b/web/src/admin/providers/scim/SCIMProviderForm.ts
index cdc935973..e505c4b23 100644
--- a/web/src/admin/providers/scim/SCIMProviderForm.ts
+++ b/web/src/admin/providers/scim/SCIMProviderForm.ts
@@ -1,8 +1,8 @@
+import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
@@ -22,7 +22,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-provider-scim-form")
-export class SCIMProviderFormPage extends ModelForm {
+export class SCIMProviderFormPage extends BaseProviderForm {
loadInstance(pk: number): Promise {
return new ProvidersApi(DEFAULT_CONFIG).providersScimRetrieve({
id: pk,
@@ -39,14 +39,6 @@ export class SCIMProviderFormPage extends ModelForm {
propertyMappings?: PaginatedSCIMMappingList;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated provider.");
- } else {
- return msg("Successfully created provider.");
- }
- }
-
async send(data: SCIMProvider): Promise {
if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersScimUpdate({
diff --git a/web/src/admin/roles/RoleForm.ts b/web/src/admin/roles/RoleForm.ts
index 7d4778469..e5cc0e8f3 100644
--- a/web/src/admin/roles/RoleForm.ts
+++ b/web/src/admin/roles/RoleForm.ts
@@ -21,11 +21,9 @@ export class RoleForm extends ModelForm {
}
getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated role.");
- } else {
- return msg("Successfully created role.");
- }
+ return this.instance
+ ? msg("Successfully updated role.")
+ : msg("Successfully created role.");
}
async send(data: Role): Promise {
diff --git a/web/src/admin/sources/BaseSourceForm.ts b/web/src/admin/sources/BaseSourceForm.ts
new file mode 100644
index 000000000..f8c68c4bd
--- /dev/null
+++ b/web/src/admin/sources/BaseSourceForm.ts
@@ -0,0 +1,11 @@
+import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
+
+import { msg } from "@lit/localize";
+
+export abstract class BaseSourceForm extends ModelForm {
+ getSuccessMessage(): string {
+ return this.instance
+ ? msg("Successfully updated source.")
+ : msg("Successfully created source.");
+ }
+}
diff --git a/web/src/admin/sources/ldap/LDAPSourceForm.ts b/web/src/admin/sources/ldap/LDAPSourceForm.ts
index a927eae29..39a121cb9 100644
--- a/web/src/admin/sources/ldap/LDAPSourceForm.ts
+++ b/web/src/admin/sources/ldap/LDAPSourceForm.ts
@@ -1,10 +1,10 @@
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import { placeholderHelperText } from "@goauthentik/admin/helperText";
+import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -24,7 +24,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-source-ldap-form")
-export class LDAPSourceForm extends ModelForm {
+export class LDAPSourceForm extends BaseSourceForm {
loadInstance(pk: string): Promise {
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapRetrieve({
slug: pk,
@@ -41,14 +41,6 @@ export class LDAPSourceForm extends ModelForm {
propertyMappings?: PaginatedLDAPPropertyMappingList;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated source.");
- } else {
- return msg("Successfully created source.");
- }
- }
-
async send(data: LDAPSource): Promise {
if (this.instance) {
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapPartialUpdate({
@@ -206,7 +198,7 @@ export class LDAPSourceForm extends ModelForm {
name="peerCertificate"
>
@@ -220,7 +212,7 @@ export class LDAPSourceForm extends ModelForm {
name="clientCertificate"
>
${msg(
diff --git a/web/src/admin/sources/oauth/OAuthSourceForm.ts b/web/src/admin/sources/oauth/OAuthSourceForm.ts
index 51b1c6e42..cf0209fcb 100644
--- a/web/src/admin/sources/oauth/OAuthSourceForm.ts
+++ b/web/src/admin/sources/oauth/OAuthSourceForm.ts
@@ -1,5 +1,6 @@
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
+import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
@@ -8,7 +9,6 @@ import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -28,7 +28,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-source-oauth-form")
-export class OAuthSourceForm extends ModelForm {
+export class OAuthSourceForm extends BaseSourceForm {
async loadInstance(pk: string): Promise {
const source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthRetrieve({
slug: pk,
@@ -61,14 +61,6 @@ export class OAuthSourceForm extends ModelForm {
@state()
clearIcon = false;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated source.");
- } else {
- return msg("Successfully created source.");
- }
- }
-
async send(data: OAuthSource): Promise {
data.providerType = (this.providerType?.slug || "") as ProviderTypeEnum;
let source: OAuthSource;
diff --git a/web/src/admin/sources/plex/PlexSourceForm.ts b/web/src/admin/sources/plex/PlexSourceForm.ts
index 1318a1ae9..8091067ff 100644
--- a/web/src/admin/sources/plex/PlexSourceForm.ts
+++ b/web/src/admin/sources/plex/PlexSourceForm.ts
@@ -1,5 +1,6 @@
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
+import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
@@ -7,7 +8,6 @@ import { ascii_letters, digits, first, randomString } from "@goauthentik/common/
import { rootInterface } from "@goauthentik/elements/Base";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -24,7 +24,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-source-plex-form")
-export class PlexSourceForm extends ModelForm {
+export class PlexSourceForm extends BaseSourceForm {
async loadInstance(pk: string): Promise {
const source = await new SourcesApi(DEFAULT_CONFIG).sourcesPlexRetrieve({
slug: pk,
@@ -50,14 +50,6 @@ export class PlexSourceForm extends ModelForm {
} as PlexSource;
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated source.");
- } else {
- return msg("Successfully created source.");
- }
- }
-
async send(data: PlexSource): Promise {
data.plexToken = this.plexToken || "";
let source: PlexSource;
diff --git a/web/src/admin/sources/saml/SAMLSourceForm.ts b/web/src/admin/sources/saml/SAMLSourceForm.ts
index 9e9fb8392..76e996322 100644
--- a/web/src/admin/sources/saml/SAMLSourceForm.ts
+++ b/web/src/admin/sources/saml/SAMLSourceForm.ts
@@ -1,13 +1,13 @@
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 { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import { rootInterface } from "@goauthentik/elements/Base";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/utils/TimeDeltaHelp";
@@ -29,7 +29,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-source-saml-form")
-export class SAMLSourceForm extends ModelForm {
+export class SAMLSourceForm extends BaseSourceForm {
@state()
clearIcon = false;
@@ -41,14 +41,6 @@ export class SAMLSourceForm extends ModelForm {
return source;
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated source.");
- } else {
- return msg("Successfully created source.");
- }
- }
-
async send(data: SAMLSource): Promise {
let source: SAMLSource;
if (this.instance) {
@@ -272,7 +264,7 @@ export class SAMLSourceForm extends ModelForm {
${msg(
@@ -285,7 +277,7 @@ export class SAMLSourceForm extends ModelForm {
name="verificationKp"
>
diff --git a/web/src/admin/stages/BaseStageForm.ts b/web/src/admin/stages/BaseStageForm.ts
new file mode 100644
index 000000000..67c5ffc35
--- /dev/null
+++ b/web/src/admin/stages/BaseStageForm.ts
@@ -0,0 +1,11 @@
+import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
+
+import { msg } from "@lit/localize";
+
+export abstract class BaseStageForm extends ModelForm {
+ getSuccessMessage(): string {
+ return this.instance
+ ? msg("Successfully updated stage.")
+ : msg("Successfully created stage.");
+ }
+}
diff --git a/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts b/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
index 4d74059be..db9322a84 100644
--- a/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
+++ b/web/src/admin/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
@@ -1,9 +1,9 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -21,21 +21,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-duo-form")
-export class AuthenticatorDuoStageForm extends ModelForm {
+export class AuthenticatorDuoStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: AuthenticatorDuoStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoPartialUpdate({
diff --git a/web/src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts b/web/src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts
index 4522d050b..2938b156a 100644
--- a/web/src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts
+++ b/web/src/admin/stages/authenticator_sms/AuthenticatorSMSStageForm.ts
@@ -1,9 +1,9 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
@@ -26,7 +26,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-sms-form")
-export class AuthenticatorSMSStageForm extends ModelForm {
+export class AuthenticatorSMSStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG)
.stagesAuthenticatorSmsRetrieve({
@@ -45,14 +45,6 @@ export class AuthenticatorSMSStageForm extends ModelForm {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorSmsUpdate({
diff --git a/web/src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts b/web/src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts
index f9f2684d0..7a5dcab47 100644
--- a/web/src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts
+++ b/web/src/admin/stages/authenticator_static/AuthenticatorStaticStageForm.ts
@@ -1,8 +1,8 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -19,21 +19,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-static-form")
-export class AuthenticatorStaticStageForm extends ModelForm {
+export class AuthenticatorStaticStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorStaticRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: AuthenticatorStaticStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorStaticUpdate({
diff --git a/web/src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts b/web/src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
index aebca9872..91ddcab19 100644
--- a/web/src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
+++ b/web/src/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
@@ -1,9 +1,9 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -21,21 +21,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-totp-form")
-export class AuthenticatorTOTPStageForm extends ModelForm {
+export class AuthenticatorTOTPStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorTotpRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: AuthenticatorTOTPStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorTotpUpdate({
diff --git a/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts b/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
index 146422988..2884f948c 100644
--- a/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
+++ b/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
@@ -1,7 +1,7 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/utils/TimeDeltaHelp";
@@ -20,7 +20,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-validate-form")
-export class AuthenticatorValidateStageForm extends ModelForm {
+export class AuthenticatorValidateStageForm extends BaseStageForm {
async loadInstance(pk: string): Promise {
const stage = await new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorValidateRetrieve({
stageUuid: pk,
@@ -41,14 +41,6 @@ export class AuthenticatorValidateStageForm extends ModelForm {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorValidateUpdate({
diff --git a/web/src/admin/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts b/web/src/admin/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
index 9bd8bebe1..d8464b6f3 100644
--- a/web/src/admin/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
+++ b/web/src/admin/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts
@@ -1,8 +1,8 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
@@ -23,21 +23,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-webauthn-form")
-export class AuthenticateWebAuthnStageForm extends ModelForm {
+export class AuthenticateWebAuthnStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorWebauthnRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: AuthenticateWebAuthnStage): Promise {
if (data.authenticatorAttachment?.toString() === "") {
data.authenticatorAttachment = null;
diff --git a/web/src/admin/stages/captcha/CaptchaStageForm.ts b/web/src/admin/stages/captcha/CaptchaStageForm.ts
index cc8299f30..cacdee5de 100644
--- a/web/src/admin/stages/captcha/CaptchaStageForm.ts
+++ b/web/src/admin/stages/captcha/CaptchaStageForm.ts
@@ -1,7 +1,7 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -11,21 +11,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { CaptchaStage, CaptchaStageRequest, StagesApi } from "@goauthentik/api";
@customElement("ak-stage-captcha-form")
-export class CaptchaStageForm extends ModelForm {
+export class CaptchaStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesCaptchaRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: CaptchaStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesCaptchaPartialUpdate({
diff --git a/web/src/admin/stages/consent/ConsentStageForm.ts b/web/src/admin/stages/consent/ConsentStageForm.ts
index 84866b10d..7bc5ad0ec 100644
--- a/web/src/admin/stages/consent/ConsentStageForm.ts
+++ b/web/src/admin/stages/consent/ConsentStageForm.ts
@@ -1,7 +1,7 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/utils/TimeDeltaHelp";
import { msg } from "@lit/localize";
@@ -12,7 +12,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { ConsentStage, ConsentStageModeEnum, StagesApi } from "@goauthentik/api";
@customElement("ak-stage-consent-form")
-export class ConsentStageForm extends ModelForm {
+export class ConsentStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG)
.stagesConsentRetrieve({
@@ -27,14 +27,6 @@ export class ConsentStageForm extends ModelForm {
@property({ type: Boolean })
showExpiresIn = false;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: ConsentStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesConsentUpdate({
diff --git a/web/src/admin/stages/deny/DenyStageForm.ts b/web/src/admin/stages/deny/DenyStageForm.ts
index 340e911d2..ed3dffa5f 100644
--- a/web/src/admin/stages/deny/DenyStageForm.ts
+++ b/web/src/admin/stages/deny/DenyStageForm.ts
@@ -1,6 +1,6 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -10,21 +10,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { DenyStage, StagesApi } from "@goauthentik/api";
@customElement("ak-stage-deny-form")
-export class DenyStageForm extends ModelForm {
+export class DenyStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesDenyRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: DenyStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesDenyUpdate({
diff --git a/web/src/admin/stages/dummy/DummyStageForm.ts b/web/src/admin/stages/dummy/DummyStageForm.ts
index 03fbbb4c3..39d36b99d 100644
--- a/web/src/admin/stages/dummy/DummyStageForm.ts
+++ b/web/src/admin/stages/dummy/DummyStageForm.ts
@@ -1,7 +1,7 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -11,21 +11,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { DummyStage, StagesApi } from "@goauthentik/api";
@customElement("ak-stage-dummy-form")
-export class DummyStageForm extends ModelForm {
+export class DummyStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesDummyRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: DummyStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesDummyUpdate({
diff --git a/web/src/admin/stages/email/EmailStageForm.ts b/web/src/admin/stages/email/EmailStageForm.ts
index a08b5c115..c2595674d 100644
--- a/web/src/admin/stages/email/EmailStageForm.ts
+++ b/web/src/admin/stages/email/EmailStageForm.ts
@@ -1,8 +1,8 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -12,7 +12,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { EmailStage, StagesApi, TypeCreate } from "@goauthentik/api";
@customElement("ak-stage-email-form")
-export class EmailStageForm extends ModelForm {
+export class EmailStageForm extends BaseStageForm {
async loadInstance(pk: string): Promise {
const stage = await new StagesApi(DEFAULT_CONFIG).stagesEmailRetrieve({
stageUuid: pk,
@@ -30,14 +30,6 @@ export class EmailStageForm extends ModelForm {
@property({ type: Boolean })
showConnectionSettings = false;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: EmailStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesEmailPartialUpdate({
diff --git a/web/src/admin/stages/identification/IdentificationStageForm.ts b/web/src/admin/stages/identification/IdentificationStageForm.ts
index 6fad5fbb2..cc064adf1 100644
--- a/web/src/admin/stages/identification/IdentificationStageForm.ts
+++ b/web/src/admin/stages/identification/IdentificationStageForm.ts
@@ -1,9 +1,9 @@
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first, groupBy } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -23,7 +23,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-identification-form")
-export class IdentificationStageForm extends ModelForm {
+export class IdentificationStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesIdentificationRetrieve({
stageUuid: pk,
@@ -38,14 +38,6 @@ export class IdentificationStageForm extends ModelForm {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesIdentificationUpdate({
diff --git a/web/src/admin/stages/invitation/InvitationForm.ts b/web/src/admin/stages/invitation/InvitationForm.ts
index 4555a547e..24b2c34cf 100644
--- a/web/src/admin/stages/invitation/InvitationForm.ts
+++ b/web/src/admin/stages/invitation/InvitationForm.ts
@@ -23,11 +23,9 @@ export class InvitationForm extends ModelForm {
}
getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated invitation.");
- } else {
- return msg("Successfully created invitation.");
- }
+ return this.instance
+ ? msg("Successfully updated invitation.")
+ : msg("Successfully created invitation.");
}
async send(data: Invitation): Promise {
diff --git a/web/src/admin/stages/invitation/InvitationStageForm.ts b/web/src/admin/stages/invitation/InvitationStageForm.ts
index 549c58ae6..822d8a2da 100644
--- a/web/src/admin/stages/invitation/InvitationStageForm.ts
+++ b/web/src/admin/stages/invitation/InvitationStageForm.ts
@@ -1,8 +1,8 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -11,21 +11,13 @@ import { customElement } from "lit/decorators.js";
import { InvitationStage, StagesApi } from "@goauthentik/api";
@customElement("ak-stage-invitation-form")
-export class InvitationStageForm extends ModelForm {
+export class InvitationStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesInvitationStagesRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: InvitationStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesInvitationStagesUpdate({
diff --git a/web/src/admin/stages/password/PasswordStageForm.ts b/web/src/admin/stages/password/PasswordStageForm.ts
index ce503ef00..939f92931 100644
--- a/web/src/admin/stages/password/PasswordStageForm.ts
+++ b/web/src/admin/stages/password/PasswordStageForm.ts
@@ -1,9 +1,9 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/SearchSelect";
import { msg } from "@lit/localize";
@@ -22,21 +22,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-password-form")
-export class PasswordStageForm extends ModelForm {
+export class PasswordStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesPasswordRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: PasswordStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesPasswordUpdate({
diff --git a/web/src/admin/stages/prompt/PromptForm.ts b/web/src/admin/stages/prompt/PromptForm.ts
index f46a6be5a..bddf1c368 100644
--- a/web/src/admin/stages/prompt/PromptForm.ts
+++ b/web/src/admin/stages/prompt/PromptForm.ts
@@ -90,11 +90,9 @@ export class PromptForm extends ModelForm {
}
getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated prompt.");
- } else {
- return msg("Successfully created prompt.");
- }
+ return this.instance
+ ? msg("Successfully updated prompt.")
+ : msg("Successfully created prompt.");
}
static get styles(): CSSResult[] {
diff --git a/web/src/admin/stages/prompt/PromptStageForm.ts b/web/src/admin/stages/prompt/PromptStageForm.ts
index f3841e10f..43e35f475 100644
--- a/web/src/admin/stages/prompt/PromptStageForm.ts
+++ b/web/src/admin/stages/prompt/PromptStageForm.ts
@@ -1,9 +1,9 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import "@goauthentik/admin/stages/prompt/PromptForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/ModalForm";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -19,7 +19,7 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-prompt-form")
-export class PromptStageForm extends ModelForm {
+export class PromptStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesPromptStagesRetrieve({
stageUuid: pk,
@@ -38,14 +38,6 @@ export class PromptStageForm extends ModelForm {
prompts?: PaginatedPromptList;
policies?: PaginatedPolicyList;
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: PromptStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesPromptStagesUpdate({
diff --git a/web/src/admin/stages/user_delete/UserDeleteStageForm.ts b/web/src/admin/stages/user_delete/UserDeleteStageForm.ts
index 3d15b52ff..617cd182a 100644
--- a/web/src/admin/stages/user_delete/UserDeleteStageForm.ts
+++ b/web/src/admin/stages/user_delete/UserDeleteStageForm.ts
@@ -1,6 +1,6 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -10,21 +10,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { StagesApi, UserDeleteStage } from "@goauthentik/api";
@customElement("ak-stage-user-delete-form")
-export class UserDeleteStageForm extends ModelForm {
+export class UserDeleteStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesUserDeleteRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: UserDeleteStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesUserDeleteUpdate({
diff --git a/web/src/admin/stages/user_login/UserLoginStageForm.ts b/web/src/admin/stages/user_login/UserLoginStageForm.ts
index 6ba879799..c386a9af2 100644
--- a/web/src/admin/stages/user_login/UserLoginStageForm.ts
+++ b/web/src/admin/stages/user_login/UserLoginStageForm.ts
@@ -1,9 +1,9 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/Alert";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/utils/TimeDeltaHelp";
import { msg } from "@lit/localize";
@@ -13,21 +13,13 @@ import { customElement } from "lit/decorators.js";
import { StagesApi, UserLoginStage } from "@goauthentik/api";
@customElement("ak-stage-user-login-form")
-export class UserLoginStageForm extends ModelForm {
+export class UserLoginStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesUserLoginRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: UserLoginStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesUserLoginUpdate({
diff --git a/web/src/admin/stages/user_logout/UserLogoutStageForm.ts b/web/src/admin/stages/user_logout/UserLogoutStageForm.ts
index 7e90aade6..40a99500f 100644
--- a/web/src/admin/stages/user_logout/UserLogoutStageForm.ts
+++ b/web/src/admin/stages/user_logout/UserLogoutStageForm.ts
@@ -1,6 +1,6 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@@ -10,21 +10,13 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { StagesApi, UserLogoutStage } from "@goauthentik/api";
@customElement("ak-stage-user-logout-form")
-export class UserLogoutStageForm extends ModelForm {
+export class UserLogoutStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesUserLogoutRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: UserLogoutStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesUserLogoutUpdate({
diff --git a/web/src/admin/stages/user_write/UserWriteStageForm.ts b/web/src/admin/stages/user_write/UserWriteStageForm.ts
index 3ef5185cd..08b436f9b 100644
--- a/web/src/admin/stages/user_write/UserWriteStageForm.ts
+++ b/web/src/admin/stages/user_write/UserWriteStageForm.ts
@@ -1,9 +1,9 @@
+import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { UserCreationModeEnum } from "@goauthentik/api/dist/models/UserCreationModeEnum";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
-import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
@@ -22,21 +22,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-stage-user-write-form")
-export class UserWriteStageForm extends ModelForm {
+export class UserWriteStageForm extends BaseStageForm {
loadInstance(pk: string): Promise {
return new StagesApi(DEFAULT_CONFIG).stagesUserWriteRetrieve({
stageUuid: pk,
});
}
- getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated stage.");
- } else {
- return msg("Successfully created stage.");
- }
- }
-
async send(data: UserWriteStage): Promise {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesUserWriteUpdate({
diff --git a/web/src/admin/tenants/TenantForm.ts b/web/src/admin/tenants/TenantForm.ts
index 59048d987..51e81b24e 100644
--- a/web/src/admin/tenants/TenantForm.ts
+++ b/web/src/admin/tenants/TenantForm.ts
@@ -26,11 +26,9 @@ export class TenantForm extends ModelForm {
}
getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated tenant.");
- } else {
- return msg("Successfully created tenant.");
- }
+ return this.instance
+ ? msg("Successfully updated tenant.")
+ : msg("Successfully created tenant.");
}
async send(data: Tenant): Promise {
@@ -235,7 +233,7 @@ export class TenantForm extends ModelForm {
name="webCertificate"
>
{
}
getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated token.");
- } else {
- return msg("Successfully created token.");
- }
+ return this.instance
+ ? msg("Successfully updated token.")
+ : msg("Successfully created token.");
}
async send(data: Token): Promise {
diff --git a/web/src/admin/users/UserViewPage.ts b/web/src/admin/users/UserViewPage.ts
index 25915b64a..6ef77aa51 100644
--- a/web/src/admin/users/UserViewPage.ts
+++ b/web/src/admin/users/UserViewPage.ts
@@ -14,6 +14,11 @@ import "@goauthentik/app/elements/rbac/ObjectPermissionsPage";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { userTypeToLabel } from "@goauthentik/common/labels";
+import "@goauthentik/components/DescriptionList";
+import {
+ type DescriptionPair,
+ renderDescriptionList,
+} from "@goauthentik/components/DescriptionList";
import "@goauthentik/components/ak-status-label";
import "@goauthentik/components/events/ObjectChangelog";
import "@goauthentik/components/events/UserEvents";
@@ -137,165 +142,91 @@ export class UserViewPage extends AKElement {
const user = this.user;
- const canImpersonate =
- rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
- this.user.pk !== this.me?.user.pk;
+ // prettier-ignore
+ const userInfo: DescriptionPair[] = [
+ [msg("Username"), user.username],
+ [msg("Name"), user.name],
+ [msg("Email"), user.email || "-"],
+ [msg("Last login"), user.lastLogin?.toLocaleString()],
+ [msg("Active"), html``],
+ [msg("Type"), userTypeToLabel(user.type)],
+ [msg("Superuser"), html``],
+ [msg("Actions"), this.renderActionButtons(user)],
+ [msg("Recovery"), this.renderRecoveryButtons(user)],
+ ];
return html`
${msg("User Info")}
-
-
-
-
-
- ${msg("Username")}
-
-
-
-
${user.username}
-
-
-
-
-
- ${msg("Name")}
-
-
-
-
${user.name}
-
-
-
-
-
- ${msg("Email")}
-
-
-
-
${user.email || "-"}
-
-
-
-
-
- ${msg("Last login")}
-
-
-
-
- ${user.lastLogin?.toLocaleString()}
-
-
-
-
-
-
- ${msg("Active")}
-
-
-
-
-
-
-
-
-
- ${msg("Type")}
-
-
-
-
- ${userTypeToLabel(user.type)}
-
-
-
-
-
-
- ${msg("Superuser")}
-
-
-
-
-
-
-
-
-
- ${msg("Actions")}
-
-
-
-
-
- ${msg("Update")}
- ${msg("Update User")}
-
-
-
-
-
{
- return new CoreApi(DEFAULT_CONFIG).coreUsersPartialUpdate({
- id: user.pk,
- patchedUserRequest: {
- isActive: !user.isActive,
- },
- });
- }}
- >
-
-
- ${canImpersonate
- ? html`
-
{
- return new CoreApi(DEFAULT_CONFIG)
- .coreUsersImpersonateCreate({
- id: user.pk,
- })
- .then(() => {
- window.location.href = "/";
- });
- }}
- >
-
- ${msg("Impersonate")}
-
-
- `
- : nothing}
-
-
-
-
-
-
- ${msg("Recovery")}
-
-
-
-
+
${renderDescriptionList(userInfo)}
+ `;
+ }
+
+ renderActionButtons(user: User) {
+ const canImpersonate =
+ rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
+ user.pk !== this.me?.user.pk;
+
+ return html`
`;
+ }
+
+ renderRecoveryButtons(user: User) {
+ return html`
`;
}
@@ -469,57 +430,87 @@ export abstract class Table extends AKElement {
this.fetch();
}
+ /* The checkbox on the table header row that allows the user to "activate all on this page,"
+ * "deactivate all on this page" with a single click.
+ */
+ renderAllOnThisPageCheckbox(): TemplateResult {
+ const checked =
+ this.selectedElements.length === this.data?.results.length &&
+ this.selectedElements.length > 0;
+
+ const onInput = (ev: InputEvent) => {
+ this.selectedElements = (ev.target as HTMLInputElement).checked
+ ? this.data?.results.slice(0) || []
+ : [];
+ };
+
+ return html`
+
+ | `;
+ }
+
+ /* For very large tables where the user is selecting a limited number of entries, we provide a
+ * chip-based subtable at the top that shows the list of selected entries. Long text result in
+ * ellipsized chips, which is sub-optimal.
+ */
+ renderSelectedChip(_item: T): TemplateResult {
+ // Override this for chip-based displays
+ return html``;
+ }
+
+ get needChipGroup() {
+ return this.checkbox && this.checkboxChip;
+ }
+
+ renderChipGroup(): TemplateResult {
+ return html`
+ ${this.selectedElements.map((el) => {
+ return html`${this.renderSelectedChip(el)}`;
+ })}
+ `;
+ }
+
+ /* A simple pagination display, shown at both the top and bottom of the page. */
+ renderTablePagination(): TemplateResult {
+ const handler = (page: number) => {
+ updateURLParams({ tablePage: page });
+ this.page = page;
+ this.fetch();
+ };
+
+ return html`
+
+ `;
+ }
+
renderTable(): TemplateResult {
- return html` ${this.checkbox && this.checkboxChip
- ? html`
- ${this.selectedElements.map((el) => {
- return html`${this.renderSelectedChip(el)}`;
- })}
- `
- : html``}
+ const renderBottomPagination = () =>
+ html``;
+
+ return html` ${this.needChipGroup ? this.renderChipGroup() : html``}
${this.renderToolbarContainer()}
- ${this.paginated
- ? html` `
- : html``}`;
+ ${this.paginated ? renderBottomPagination() : html``}`;
}
render(): TemplateResult {
diff --git a/web/src/flow/stages/captcha/CaptchaStage.ts b/web/src/flow/stages/captcha/CaptchaStage.ts
index 7690fc626..753bda99c 100644
--- a/web/src/flow/stages/captcha/CaptchaStage.ts
+++ b/web/src/flow/stages/captcha/CaptchaStage.ts
@@ -1,11 +1,11 @@
///
-///
import "@goauthentik/elements/EmptyState";
import { PFSize } from "@goauthentik/elements/Spinner";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
import { BaseStage } from "@goauthentik/flow/stages/base";
+import type { TurnstileObject } from "turnstile-types";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
@@ -21,6 +21,10 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { CaptchaChallenge, CaptchaChallengeResponseRequest } from "@goauthentik/api";
+interface TurnstileWindow extends Window {
+ turnstile: TurnstileObject;
+}
+
@customElement("ak-stage-captcha")
export class CaptchaStage extends BaseStage {
static get styles(): CSSResult[] {
@@ -110,9 +114,8 @@ export class CaptchaStage extends BaseStage {
this.host?.submit({
token: token,
diff --git a/web/src/global.d.ts b/web/src/global.d.ts
index b7b3dfdf8..059e86815 100644
--- a/web/src/global.d.ts
+++ b/web/src/global.d.ts
@@ -1,4 +1,5 @@
declare module "*.css";
+
declare module "*.md" {
const html: string;
const metadata: { [key: string]: string };
diff --git a/web/src/user/UserInterface.ts b/web/src/user/UserInterface.ts
index 9fb29ff98..2035c570a 100644
--- a/web/src/user/UserInterface.ts
+++ b/web/src/user/UserInterface.ts
@@ -80,9 +80,9 @@ export class UserInterface extends Interface {
:host([theme="dark"]) .pf-c-page__header {
color: var(--ak-dark-foreground) !important;
}
- .pf-c-page__header-tools-item .fas,
- .pf-c-notification-badge__count,
- .pf-c-page__header-tools-group .pf-c-button {
+ :host([theme="light"]) .pf-c-page__header-tools-item .fas,
+ :host([theme="light"]) .pf-c-notification-badge__count,
+ :host([theme="light"]) .pf-c-page__header-tools-group .pf-c-button {
color: var(--ak-global--Color--100) !important;
}
.pf-c-page {
@@ -181,7 +181,7 @@ export class UserInterface extends Interface {
- ${this.uiConfig.theme.background === ""
+ ${(this.uiConfig.theme.background || "") === ""
? html`
`
: html``}
diff --git a/web/src/user/user-settings/tokens/UserTokenForm.ts b/web/src/user/user-settings/tokens/UserTokenForm.ts
index 05bb3b111..78ce3413d 100644
--- a/web/src/user/user-settings/tokens/UserTokenForm.ts
+++ b/web/src/user/user-settings/tokens/UserTokenForm.ts
@@ -21,11 +21,9 @@ export class UserTokenForm extends ModelForm
{
}
getSuccessMessage(): string {
- if (this.instance) {
- return msg("Successfully updated token.");
- } else {
- return msg("Successfully created token.");
- }
+ return this.instance
+ ? msg("Successfully updated token.")
+ : msg("Successfully created token.");
}
async send(data: Token): Promise {
diff --git a/web/xliff/fr.xlf b/web/xliff/fr.xlf
index 2c647c773..e0976e5e8 100644
--- a/web/xliff/fr.xlf
+++ b/web/xliff/fr.xlf
@@ -1,4 +1,4 @@
-
+
@@ -613,9 +613,9 @@ Il y a jour(s)
-
- L'URL "
- " n'a pas été trouvée.
+
+ L'URL "
+ " n'a pas été trouvée.
@@ -1057,8 +1057,8 @@ Il y a jour(s)
-
- Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur ".*". Soyez conscient des possibles implications de sécurité que cela peut avoir.
+
+ Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur ".*". Soyez conscient des possibles implications de sécurité que cela peut avoir.
@@ -1630,7 +1630,7 @@ Il y a jour(s)
- Jeton d'authentification à utiliser. Actuellement, seule l'authentification "bearer authentication" est prise en charge.
+ Jeton d'authentification à utiliser. Actuellement, seule l'authentification "bearer authentication" est prise en charge.
@@ -1798,8 +1798,8 @@ Il y a jour(s)
-
- Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome "fa-test".
+
+ Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome "fa-test".
@@ -2897,7 +2897,7 @@ doesn't pass when either or both of the selected options are equal or above the
- Pour utiliser SSL à la base, utilisez "ldaps://" et désactviez cette option.
+ Pour utiliser SSL à la base, utilisez "ldaps://" et désactviez cette option.
@@ -2986,8 +2986,8 @@ doesn't pass when either or both of the selected options are equal or above the
-
- Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...'
+
+ Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...'
@@ -3282,7 +3282,7 @@ doesn't pass when either or both of the selected options are equal or above the
- Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID "transient" et que l'utilisateur ne se déconnecte pas manuellement.
+ Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID "transient" et que l'utilisateur ne se déconnecte pas manuellement.
@@ -3450,7 +3450,7 @@ doesn't pass when either or both of the selected options are equal or above the
- Indiquer la valeur "FriendlyName" de l'attribut d'assertion (optionnel)
+ Indiquer la valeur "FriendlyName" de l'attribut d'assertion (optionnel)
@@ -3779,8 +3779,8 @@ doesn't pass when either or both of the selected options are equal or above the
-
- En cas d'utilisation d'une solution de journalisation externe pour l'archivage, cette valeur peut être fixée à "minutes=5".
+
+ En cas d'utilisation d'une solution de journalisation externe pour l'archivage, cette valeur peut être fixée à "minutes=5".
@@ -3789,8 +3789,8 @@ doesn't pass when either or both of the selected options are equal or above the
-
- Format : "weeks=3;days=2;hours=3,seconds=2".
+
+ Format : "weeks=3;days=2;hours=3,seconds=2".
@@ -3986,10 +3986,10 @@ doesn't pass when either or both of the selected options are equal or above the
-
+
Êtes-vous sûr de vouloir mettre à jour
- "
- " ?
+ "
+ " ?
@@ -5075,8 +5075,8 @@ doesn't pass when either or both of the selected options are equal or above the
-
- Un authentificateur "itinérant", comme une YubiKey
+
+ Un authentificateur "itinérant", comme une YubiKey
@@ -5401,7 +5401,7 @@ doesn't pass when either or both of the selected options are equal or above the
- Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable "prompt_data".
+ Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable "prompt_data".
@@ -5410,10 +5410,10 @@ doesn't pass when either or both of the selected options are equal or above the
-
+
- ("
- ", de type
+ ("
+ ", de type
)
@@ -5462,8 +5462,8 @@ doesn't pass when either or both of the selected options are equal or above the
-
- Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de "rester connecté", ce qui prolongera sa session jusqu'à la durée spécifiée ici.
+
+ Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de "rester connecté", ce qui prolongera sa session jusqu'à la durée spécifiée ici.
@@ -6247,7 +6247,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
- Peut être au format "unix://" pour une connexion à un service docker local, "ssh://" pour une connexion via SSH, ou "https://:2376" pour une connexion à un système distant.
+ Peut être au format "unix://" pour une connexion à un service docker local, "ssh://" pour une connexion via SSH, ou "https://:2376" pour une connexion à un système distant.
@@ -7554,7 +7554,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
- Utilisez ce fournisseur avec l'option "auth_request" de Nginx ou "forwardAuth" de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, "/outpost.goauthentik.io" doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous).
+ Utilisez ce fournisseur avec l'option "auth_request" de Nginx ou "forwardAuth" de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, "/outpost.goauthentik.io" doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous).
@@ -7968,7 +7968,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
Utilisateur créé et ajouté au groupe avec succès
-
+
Cet utilisateur sera ajouté au groupe "".
@@ -8045,4 +8045,4 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
-
\ No newline at end of file
+
diff --git a/website/blog/2023-11-1-happy-birthday-to-us/item.md b/website/blog/2023-11-1-happy-birthday-to-us/item.md
index 684975a1b..a49487d74 100644
--- a/website/blog/2023-11-1-happy-birthday-to-us/item.md
+++ b/website/blog/2023-11-1-happy-birthday-to-us/item.md
@@ -1,6 +1,6 @@
---
-title: “Happy Birthday to Us!”
-description: “We are celebrating our one-year anniversary since the founding of Authentik Security..”
+title: "Happy Birthday to Us!"
+description: "We are celebrating our one-year anniversary since the founding of Authentik Security.."
slug: 2023-11-1-happy-birthday-to-us
authors:
- name: Jens Langhammer and the authentik team
@@ -56,7 +56,7 @@ Once you get to know Jens, you won’t be surprised to his answer about what he
That task alone will scare most of us. In software, team work is most definitely what makes the dream work, so finding the right talents and skills sets and experiences to compliment Jens’ deep technical skills and full-stack experience was of paramount importance. We now have developers with expertise in frontend and backend development, infrastructure, and security, a well as a content editor.
-Of course, it is not just the technical skills that a potential new hire needs; as important are less-measurable skills like collaboration, communication, and perhaps most importantly, what we call “technical curiosity”.
+Of course, it is not just the technical skills that a potential new hire needs; as important are less-measurable skills like collaboration, communication, and perhaps most importantly, what we call "technical curiosity".
> How does this thing work, from whom can I learn more, and with whom can I share my knowledge?
@@ -90,10 +90,10 @@ An interesting offset to our shared love of building is the shared sense of humi
The tone and espirit of the company is one reason it’s so meaningful to celebrate our 1-year birthday; we can happily celebrate a hard year of doing things with full, enthusiastic engagement. At authentik, nerdiness is embraced, technical curiosity flourishes, and transparency is a big part of our nature. Speaking of how we communicate with our community, our Discord forum is (in addition to GitHub) an important place where transparency matters. For example, we recently asked our community what they preferred for a release cycle. Based on the answers, we lengthened the release time from from monthly to every two or three months.
-Moving from a role of solo creator of an open source project, to being primary maintainer of a popular, growing project, to suddenly being CTO of a company based on that project is a quite a transition. A natural question we wanted to ask Jens is “What’s been the hardest thing about building a company?” His answers:
+Moving from a role of solo creator of an open source project, to being primary maintainer of a popular, growing project, to suddenly being CTO of a company based on that project is a quite a transition. A natural question we wanted to ask Jens is "What’s been the hardest thing about building a company?" His answers:
-- “Recognizing and accepting that you don’t get to work on only what you want to, 100% of time… “
-- “Learning to delegate, learning to let go a bit, trusting others to do it in their way, in the right spirit. Especially letting others get into the code… I’ve learned that instead of saying ‘I would not have done it this way’, I instead measure the success of the change itself.”
+- "Recognizing and accepting that you don’t get to work on only what you want to, 100% of time… "
+- "Learning to delegate, learning to let go a bit, trusting others to do it in their way, in the right spirit. Especially letting others get into the code… I’ve learned that instead of saying ‘I would not have done it this way’, I instead measure the success of the change itself."
### What’s up next?
diff --git a/website/blog/2023-11-30-automated-security-versus-the-security-mindset/item.md b/website/blog/2023-11-30-automated-security-versus-the-security-mindset/item.md
index ff1e0bf24..6814a8857 100644
--- a/website/blog/2023-11-30-automated-security-versus-the-security-mindset/item.md
+++ b/website/blog/2023-11-30-automated-security-versus-the-security-mindset/item.md
@@ -1,6 +1,6 @@
---
title: Automated security versus the security mindset
-description: “Automated security plays a key part in many cybersecurity tasks. But what are its failings and will a security mindset always require the human factor?”
+description: "Automated security plays a key part in many cybersecurity tasks. But what are its failings and will a security mindset always require the human factor?"
slug: 2023-11-30-automated-security-versus-the-security-mindset
authors:
- name: Jens Langhammer
@@ -142,7 +142,7 @@ Once new and significant threats are detected by the automated security, it is h
### Human-centered cybersecurity
-Despite the growing technology around automated security, and the temptation to relax when it is deployed, there are human factors that are irreplaceable in the practice of cybersecurity. We recently wrote about the importance of the “Blue Team” and how [organizational and product hardening](https://goauthentik.io/blog/2023-11-22-how-we-saved-over-100k#hardening) are an integral part of our human-centered security mindset.
+Despite the growing technology around automated security, and the temptation to relax when it is deployed, there are human factors that are irreplaceable in the practice of cybersecurity. We recently wrote about the importance of the "Blue Team" and how [organizational and product hardening](https://goauthentik.io/blog/2023-11-22-how-we-saved-over-100k#hardening) are an integral part of our human-centered security mindset.
- The human ability to think creatively and rapidly adapt to changing situations is invaluable to good security processes.
- The higher the security risk, the more you need skilled security professionals to supervise the security process.
diff --git a/website/blog/2023-12-12-oktas-october-breach-part-two/item.md b/website/blog/2023-12-12-oktas-october-breach-part-two/item.md
new file mode 100644
index 000000000..24284f79a
--- /dev/null
+++ b/website/blog/2023-12-12-oktas-october-breach-part-two/item.md
@@ -0,0 +1,93 @@
+---
+title: "Okta's October breach part two: a delayed but slightly better response"
+description: "Okta continues to revel more information about the HAR files breach first revealed in October; now we know that a service account was involved, and 100% of their customer support users were impacted."
+slug: 2023-12-12-oktas-october-breach-part-two
+authors:
+ - name: Jens Langhammer
+ title: CTO at Authentik Security Inc
+ url: https://github.com/BeryJu
+ image_url: https://github.com/BeryJu.png
+tags:
+ - authentik
+ - security mindset
+ - incident response
+ - service account
+ - Okta
+ - SSO
+ - HAR files
+ - identity provider
+ - authentication
+ - Authentik Security
+hide_table_of_contents: false
+image: ./okta-timeline.png
+---
+
+> **_authentik is an open source Identity Provider that unifies your identity needs into a single platform, replacing Okta, Active Directory, and auth0. Authentik Security is a [public benefit company](https://github.com/OpenCoreVentures/ocv-public-benefit-company/blob/main/ocv-public-benefit-company-charter.md) building on top of the open source project._**
+
+---
+
+On November 29th, 2023, Okta [revealed](https://sec.okta.com/harfiles) that a breach they announced in October was much worse than originally conveyed. The number of impacted users went from less than 1% of customers to every single customer who had every opened a Support ticket in the Okta Help Center.
+
+> So the impact leapt from [134 users](https://sec.okta.com/articles/2023/11/unauthorized-access-oktas-support-case-management-system-root-cause) to [18,400 users](https://www.beyondtrust.com/blog/entry/okta-support-unit-breach-update).
+
+We wrote in October about Okta’s poor response to breaches (see [Okta got breached again](https://goauthentik.io/blog/2023-10-23-another-okta-breach)), but since our blog doesn’t seem to be changing Okta’s behaviour, let’s take a closer look at the new revelations from Okta about what happened back in October, how it is impacting users now, and why Okta is still dealing with it in December.
+
+> Now all of Okta’s customers are paying the price… with increased phishing and spam.
+
+Our take is that any company can be hacked, but it is the response that matters. How quick is the response, how transparent are the details, how forthright are the acknowledgments? Okta’s initial announcement about the October breach (remember the [HAR file](https://goauthentik.io/blog/2023-10-23-another-okta-breach) that contained a session token?) was less-than-timely, devoid of details, and titled with one of the worst titles ever given such a serious announcement.
+
+![screenshot of the timeline that Okta published](./okta-timeline.png)
+
+
+
+## Looking back at October’s breach
+
+With the original incident, probably what most people now recall is not only the technical details of the session tokens that were exposed in HAR files, but also the very slow response time. Turns out 1Password reported the breach to Okta on September 29, and [BeyondTrust](https://www.beyondtrust.com/blog/entry/okta-support-unit-breach) reported the breach to Okta on October 2. But Okta waited three weeks before announcing the breach on October 20th.
+
+In this October 20th announcement, Okta CISO David Bradbury stated that the malicious actor had gained access to Okta’s Support dashboard and retrieved only names and emails addresses for a very small number of customers. He explained that the hacker used session tokens that were not scrubbed from a HAR file (which Okta support routinely asks their customers to submit, for troubleshooting purposes) to gain access to specific customer’s accounts. But what wasn’t revealed at the time (because Okta themselves did not yet know) was _how_ the hacker obtained access to the Customer Support dashboard to access customer accounts and then download associated HAR files.
+
+### The second **Okta shoe fell in early November**
+
+As mentioned above, the new information revealed by Okta came from their security team retracing the steps of the original malicious actor. Okta’s research and analysis was greatly aided by the fact that [BeyondTrust shared a suspicious IP address with Okta](https://www.beyondtrust.com/blog/entry/okta-support-unit-breach-update); the IP address that BeyondTrust believed to be the hacker’s.
+
+**Initial access gained via a service account**
+
+This new finding, based on retracing the steps of that IP address, show that the initial breach occurred when the hacker obtained the credentials for a service account "stored in the system", which provided access to the Customer Support dashboard and had permissions to view and update customer support cases. The hacker then reviewed customer support cases, downloaded the associated HAR files, and retrieved the session tokens.
+
+From the [November 3rd announcement](https://sec.okta.com/articles/2023/11/unauthorized-access-oktas-support-case-management-system-root-cause), we now know that the service account credentials were exposed through an Okta employee’s personal Google account, which the employee had accessed on the Okta-issued work laptop.
+
+In announcing the service account’s role in the breach, Okta’s CISO stated:
+
+> "During our investigation into suspicious use of this account, Okta Security identified that an employee had signed-in to their personal Google profile on the Chrome browser of their Okta-managed laptop. The username and password of the service account had been saved into the employee’s personal Google account."
+
+The use of the service account was discovered on October 16, by examining the activities of the IP address of the hacker that BeyondTrust had supplied Okta. This begs the question of why Okta did not revel the malicious use of the service account in their October 20th announcement. Perhaps they did not yet want to show details of their internal investigation?
+
+### And a third shoe in late November
+
+Now fast-forward to Okta’s [November 29th announcement](https://sec.okta.com/harfiles). Back in October, it was known that after the hacker accessed Okta’s Support dashboard they ran queries on the support database to create reports containing customer data. In the November 3rd announcement Okta shared that the report was thought to be quite small is scope; this is the infamous "less than 1% of Okta customers" statement.
+
+But after more internal investigation and recreating the reports that the malicious actor ran, Okta announced on November 29th that their original statement that less than 1% of their users were impacted by the October breach was incorrect. Instead, Okta revealed that the scope of the report was much larger, and indeed concluded that "[the report contained a list of all customer support system users](https://sec.okta.com/harfiles)". A total of 18,400 users. The only customers that were not impacted are those in FedRAMP and DoD IL4 environments, who are on a separate support platform.
+
+> An aside, perhaps, but the timing of Okta’s update is interesting; the announcement was released on the same date as the quarterly earnings report. This could be seen as transparency, or it could be seen as damage control. (Also, why in the heck is Nov 29th within Okta’s 3rd quarter of **fiscal year 2024**? But we aren’t writing here about Okta’s financial schedules; I digress.)
+
+**Filters removed from report**
+
+Apparently when the hacker ran queries to gather customer data, they used a standard template available from the dashboard. However, they removed all filters on the templated report, thus grabbing much more data then the original template would have returned.
+
+In addition to removing all filters on the report, it seems that Okta’s original analysis of the logs pertaining to the breach failed to take in to account exactly HOW the hacker accessed and downloaded data:
+
+> "For a period of 14 days, while actively investigating, Okta did not identify suspicious downloads in our logs. When a user opens and views files attached to a support case, a specific log event type and ID is generated tied to that file. If a user instead navigates directly to the Files tab in the customer support system, as the threat actor did in this attack, they will instead generate an **entirely different log event** with a different record ID."
+
+## Now what?
+
+The third shoe has dropped, but it feels like there might still be a fourth. Maybe even more data was stolen, beyond just email addresses. Perhaps the malicious actor gained more sensitive customer data when they were able to log into specific customer’s accounts, but are sitting on it waiting to use it. Perhaps the explorations by the hacker form within the customer support system reveled other weaknesses that have yet to be exploited.
+
+So while we are all waiting with baited breath to see if Okta can squeeze even more drama into 2023, here are a few tips to consider:
+
+- If you are an Okta customer, do indeed follow each and every one of their recommendations, listed under the "**Implementing recommended best practices**" section of their [November 29th announcement](https://sec.okta.com/harfiles).
+- Be aware of Okta’s plan for a 90-day pause on new features. During the [earning report call](https://seekingalpha.com/article/4655057-okta-inc-okta-q3-2024-earnings-call-transcript) on November 29th CEO Todd McKinnon stated "During this hyper-focused phase, no other project or even product development area is more important. In fact, the launch dates for the new products and features that we highlighted at Oktane last month will be pushed out approximately 90 days."
+- As Okta advises, be on the lookout for more phishing attempts and stay hyper-vigilant.
+- In general, across the board, be vigilant and adopt a "[security mindset](https://goauthentik.io/blog/2023-11-30-automated-security-versus-the-security-mindset)" (as valuable and maybe more than any technology).
+- Consider breaking out of vendor lock-in and using an on-premise, open core solution such as [authentik](https://goauthentik.io/). We realize change is hard, but continual breaches and uncertainty around when a breach has been fully contained is also painful.
+
+We’d be happy to talk with you more about your security and identity management needs; reach out to us with an email to [hello@goauthentik.io](mailto:hello@goauthentik.io) or on [Discord](https://discord.com/channels/809154715984199690/809154716507963434).
diff --git a/website/blog/2023-12-12-oktas-october-breach-part-two/okta-timeline.png b/website/blog/2023-12-12-oktas-october-breach-part-two/okta-timeline.png
new file mode 100644
index 000000000..69e0a8aa2
Binary files /dev/null and b/website/blog/2023-12-12-oktas-october-breach-part-two/okta-timeline.png differ
diff --git a/website/docs/outposts/_config.md b/website/docs/outposts/_config.md
index f1b3e8792..ce013b625 100644
--- a/website/docs/outposts/_config.md
+++ b/website/docs/outposts/_config.md
@@ -64,9 +64,17 @@ kubernetes_image_pull_secrets: []
# (Available with 2022.11.0+)
# Applies to: proxy outposts
kubernetes_ingress_class_name: null
-# Optionally apply an RFC 6902 compliant patch to the Kubernetes objects. This value expects
-# a mapping of a key which can be any of the values from `kubernetes_disabled_components`,
-# which configures which component the patches are applied to. For example:
+# Optionally apply an RFC 6902 compliant patch to the Kubernetes objects.
+# For an understanding of how this works, refer to the link below:
+# https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md
+#
+# This value expects a mapping where the key represents
+# the Kubernetes component that shall be patched.
+# It can be any of the same values supported by `kubernetes_disabled_components`.
+#
+# For example use this patch to add custom resource requests and limits
+# to the outpost deployment:
+#
# deployment:
# - op: add
# path: "/spec/template/spec/containers/0/resources"
diff --git a/website/docs/security/2023-06-cure53.md b/website/docs/security/2023-06-cure53.md
index 3df339e81..55d65d12d 100644
--- a/website/docs/security/2023-06-cure53.md
+++ b/website/docs/security/2023-06-cure53.md
@@ -1,8 +1,8 @@
# 2023-06 Cure53 Code audit
-In May/June of 2023, we've had a Pen-test conducted by [Cure53](https://cure53.de). The following security updates, 2023.4.2 and 2023.5.3 were released as a response to the found issues.
+In May/June of 2023, we've had a Pentest conducted by [Cure53](https://cure53.de). The following security updates, 2023.4.2 and 2023.5.3 were released as a response to the found issues.
-From the complete report, these are the points we're addressing with this update:
+From the [complete report](https://cure53.de/pentest-report_authentik.pdf), these are the points we're addressing with this update:
### ATH-01-001: Path traversal on blueprints allows arbitrary file-read (Medium)
diff --git a/website/package-lock.json b/website/package-lock.json
index 5b1300f37..009fe65dd 100644
--- a/website/package-lock.json
+++ b/website/package-lock.json
@@ -33,7 +33,7 @@
"@docusaurus/module-type-aliases": "3.0.1",
"@docusaurus/tsconfig": "3.0.1",
"@docusaurus/types": "3.0.1",
- "@types/react": "^18.2.43",
+ "@types/react": "^18.2.45",
"prettier": "3.1.1",
"typescript": "~5.3.3"
},
@@ -4373,9 +4373,9 @@
"integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA=="
},
"node_modules/@types/react": {
- "version": "18.2.43",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.43.tgz",
- "integrity": "sha512-nvOV01ZdBdd/KW6FahSbcNplt2jCJfyWdTos61RYHV+FVv5L/g9AOX1bmbVcWcLFL8+KHQfh1zVIQrud6ihyQA==",
+ "version": "18.2.45",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz",
+ "integrity": "sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
diff --git a/website/package.json b/website/package.json
index 154632203..c8c48c25d 100644
--- a/website/package.json
+++ b/website/package.json
@@ -52,7 +52,7 @@
"@docusaurus/module-type-aliases": "3.0.1",
"@docusaurus/tsconfig": "3.0.1",
"@docusaurus/types": "3.0.1",
- "@types/react": "^18.2.43",
+ "@types/react": "^18.2.45",
"prettier": "3.1.1",
"typescript": "~5.3.3"
},