ATH-01-008: fix web forms not submitting correctly when pressing enter

When submitting some forms with the Enter key instead of clicking "Confirm"/etc, the form would not get submitted correctly

This would in the worst case is when setting a user's password, where the new password can end up in the URL, but the password was not actually saved to the user.

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

# Conflicts:
#	web/src/admin/applications/ApplicationCheckAccessForm.ts
#	web/src/admin/crypto/CertificateGenerateForm.ts
#	web/src/admin/flows/FlowImportForm.ts
#	web/src/admin/groups/RelatedGroupList.ts
#	web/src/admin/policies/PolicyTestForm.ts
#	web/src/admin/property-mappings/PropertyMappingTestForm.ts
#	web/src/admin/providers/saml/SAMLProviderImportForm.ts
#	web/src/admin/users/RelatedUserList.ts
#	web/src/admin/users/ServiceAccountForm.ts
#	web/src/admin/users/UserPasswordForm.ts
#	web/src/admin/users/UserResetEmailForm.ts
This commit is contained in:
Jens Langhammer 2023-06-07 12:03:40 +02:00
parent 1aff300171
commit f05997740f
No known key found for this signature in database
13 changed files with 168 additions and 109 deletions

View File

@ -63,8 +63,8 @@ def ldap_sync_password(sender, user: User, password: str, **_):
if not sources.exists(): if not sources.exists():
return return
source = sources.first() source = sources.first()
changer = LDAPPasswordChanger(source)
try: try:
changer = LDAPPasswordChanger(source)
changer.change_password(user, password) changer.change_password(user, password)
except LDAPOperationResult as exc: except LDAPOperationResult as exc:
Event.new( Event.new(

View File

@ -115,9 +115,8 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
`; `;
} }
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal label=${t`User`} ?required=${true} name="forUser">
<ak-form-element-horizontal label=${t`User`} ?required=${true} name="forUser">
<ak-search-select <ak-search-select
.fetchObjects=${async (query?: string): Promise<User[]> => { .fetchObjects=${async (query?: string): Promise<User[]> => {
const args: CoreUsersListRequest = { const args: CoreUsersListRequest = {
@ -144,7 +143,6 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
> >
</ak-search-select> </ak-search-select>
</ak-form-element-horizontal> </ak-form-element-horizontal>
${this.result ? this.renderResult() : html``} ${this.result ? this.renderResult() : html``}`;
</form>`;
} }
} }

View File

@ -21,9 +21,12 @@ export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
}); });
}; };
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal
<ak-form-element-horizontal label=${t`Common Name`} name="commonName" ?required=${true}> label=${t`Common Name`}
name="commonName"
?required=${true}
>
<input type="text" class="pf-c-form-control" required /> <input type="text" class="pf-c-form-control" required />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal label=${t`Subject-alt name`} name="subjectAltName"> <ak-form-element-horizontal label=${t`Subject-alt name`} name="subjectAltName">
@ -38,7 +41,6 @@ export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
?required=${true} ?required=${true}
> >
<input class="pf-c-form-control" type="number" value="365" /> <input class="pf-c-form-control" type="number" value="365" />
</ak-form-element-horizontal> </ak-form-element-horizontal>`;
</form>`;
} }
} }

View File

@ -87,15 +87,13 @@ export class FlowImportForm extends Form<Flow> {
`; `;
} }
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal label=${t`Flow`} name="flow">
<ak-form-element-horizontal label=${t`Flow`} name="flow">
<input type="file" value="" class="pf-c-form-control" /> <input type="file" value="" class="pf-c-form-control" />
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`.yaml files, which can be found on goauthentik.io and can be exported by authentik.`} ${t`.yaml files, which can be found on goauthentik.io and can be exported by authentik.`}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
${this.result ? this.renderResult() : html``} ${this.result ? this.renderResult() : html``}`;
</form>`;
} }
} }

View File

@ -46,41 +46,39 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {
return data; return data;
} }
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal label=${t`Groups to add`} name="groups">
<ak-form-element-horizontal label=${t`Groups to add`} name="groups"> <div class="pf-c-input-group">
<div class="pf-c-input-group"> <ak-user-group-select-table
<ak-user-group-select-table .confirm=${(items: Group[]) => {
.confirm=${(items: Group[]) => { this.groupsToAdd = items;
this.groupsToAdd = items; this.requestUpdate();
this.requestUpdate(); return Promise.resolve();
return Promise.resolve(); }}
}} >
> <button slot="trigger" class="pf-c-button pf-m-control" type="button">
<button slot="trigger" class="pf-c-button pf-m-control" type="button"> <i class="fas fa-plus" aria-hidden="true"></i>
<i class="fas fa-plus" aria-hidden="true"></i> </button>
</button> </ak-user-group-select-table>
</ak-user-group-select-table> <div class="pf-c-form-control">
<div class="pf-c-form-control"> <ak-chip-group>
<ak-chip-group> ${this.groupsToAdd.map((group) => {
${this.groupsToAdd.map((group) => { return html`<ak-chip
return html`<ak-chip .removable=${true}
.removable=${true} value=${ifDefined(group.pk)}
value=${ifDefined(group.pk)} @remove=${() => {
@remove=${() => { const idx = this.groupsToAdd.indexOf(group);
const idx = this.groupsToAdd.indexOf(group); this.groupsToAdd.splice(idx, 1);
this.groupsToAdd.splice(idx, 1); this.requestUpdate();
this.requestUpdate(); }}
}} >
> ${group.name}
${group.name} </ak-chip>`;
</ak-chip>`; })}
})} </ak-chip-group>
</ak-chip-group>
</div>
</div> </div>
</ak-form-element-horizontal> </div>
</form> `; </ak-form-element-horizontal>`;
} }
} }

View File

@ -116,9 +116,8 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
`; `;
} }
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal label=${t`User`} ?required=${true} name="user">
<ak-form-element-horizontal label=${t`User`} ?required=${true} name="user">
<ak-search-select <ak-search-select
.fetchObjects=${async (query?: string): Promise<User[]> => { .fetchObjects=${async (query?: string): Promise<User[]> => {
const args: CoreUsersListRequest = { const args: CoreUsersListRequest = {
@ -155,7 +154,6 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
${t`Set custom attributes using YAML or JSON.`} ${t`Set custom attributes using YAML or JSON.`}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
${this.result ? this.renderResult() : html``} ${this.result ? this.renderResult() : html``}`;
</form>`;
} }
} }

View File

@ -64,9 +64,63 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
</ak-form-element-horizontal>`; </ak-form-element-horizontal>`;
} }
renderForm(): TemplateResult { renderExampleButtons(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> const header = html`<p>${t`Example context data`}</p>`;
<ak-form-element-horizontal label=${t`User`} ?required=${true} name="user"> switch (this.mapping?.metaModelName) {
case "authentik_sources_ldap.ldappropertymapping":
return html`${header}${this.renderExampleLDAP()}`;
default:
return html``;
}
}
renderExampleLDAP(): TemplateResult {
return html`
<button
class="pf-c-button pf-m-secondary"
role="button"
@click=${() => {
this.request = {
user: this.request?.user || 0,
context: {
ldap: {
name: "test-user",
objectSid: "S-1-5-21-2611707862-2219215769-354220275-1137",
objectClass: "person",
displayName: "authentik test user",
sAMAccountName: "sAMAccountName",
distinguishedName: "cn=user,ou=users,dc=goauthentik,dc=io",
},
},
};
}}
>
${t`Active Directory User`}
</button>
<button
class="pf-c-button pf-m-secondary"
role="button"
@click=${() => {
this.request = {
user: this.request?.user || 0,
context: {
ldap: {
name: "test-group",
objectSid: "S-1-5-21-2611707862-2219215769-354220275-1137",
objectClass: "group",
distinguishedName: "cn=group,ou=groups,dc=goauthentik,dc=io",
},
},
};
}}
>
${t`Active Directory Group`}
</button>
`;
}
renderInlineForm(): TemplateResult {
return html`<ak-form-element-horizontal label=${t`User`} ?required=${true} name="user">
<ak-search-select <ak-search-select
.fetchObjects=${async (query?: string): Promise<User[]> => { .fetchObjects=${async (query?: string): Promise<User[]> => {
const args: CoreUsersListRequest = { const args: CoreUsersListRequest = {
@ -98,7 +152,6 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
>> >>
</ak-codemirror> </ak-codemirror>
</ak-form-element-horizontal> </ak-form-element-horizontal>
${this.result ? this.renderResult() : html``} ${this.result ? this.renderResult() : html``}`;
</form>`;
} }
} }

View File

@ -37,9 +37,8 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
}); });
}; };
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input type="text" class="pf-c-form-control" required /> <input type="text" class="pf-c-form-control" required />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
@ -77,7 +76,6 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
<ak-form-element-horizontal label=${t`Metadata`} name="metadata"> <ak-form-element-horizontal label=${t`Metadata`} name="metadata">
<input type="file" value="" class="pf-c-form-control" /> <input type="file" value="" class="pf-c-form-control" />
</ak-form-element-horizontal> </ak-form-element-horizontal>`;
</form>`;
} }
} }

View File

@ -59,9 +59,8 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
return data; return data;
} }
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`${this.group?.isSuperuser ? html`` : html``}
${this.group?.isSuperuser ? html`` : html``}
<ak-form-element-horizontal label=${t`Users to add`} name="users"> <ak-form-element-horizontal label=${t`Users to add`} name="users">
<div class="pf-c-input-group"> <div class="pf-c-input-group">
<ak-group-member-select-table <ak-group-member-select-table
@ -93,8 +92,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
</ak-chip-group> </ak-chip-group>
</div> </div>
</div> </div>
</ak-form-element-horizontal> </ak-form-element-horizontal>`;
</form> `;
} }
} }

View File

@ -35,9 +35,8 @@ export class ServiceAccountForm extends Form<UserServiceAccountRequest> {
this.result = undefined; this.result = undefined;
} }
renderRequestForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal label=${t`Username`} ?required=${true} name="name">
<ak-form-element-horizontal label=${t`Username`} ?required=${true} name="name">
<input type="text" value="" class="pf-c-form-control" required /> <input type="text" value="" class="pf-c-form-control" required />
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${t`User's primary identifier. 150 characters or fewer.`} ${t`User's primary identifier. 150 characters or fewer.`}
@ -78,8 +77,7 @@ export class ServiceAccountForm extends Form<UserServiceAccountRequest> {
value="${dateTimeLocal(new Date(Date.now() + 1000 * 60 ** 2 * 24 * 360))}" value="${dateTimeLocal(new Date(Date.now() + 1000 * 60 ** 2 * 24 * 360))}"
class="pf-c-form-control" class="pf-c-form-control"
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>`;
</form>`;
} }
renderResponseForm(): TemplateResult { renderResponseForm(): TemplateResult {
@ -113,6 +111,6 @@ export class ServiceAccountForm extends Form<UserServiceAccountRequest> {
if (this.result) { if (this.result) {
return this.renderResponseForm(); return this.renderResponseForm();
} }
return this.renderRequestForm(); return super.renderForm();
} }
} }

View File

@ -26,11 +26,13 @@ export class UserPasswordForm extends Form<UserPasswordSetRequest> {
}); });
}; };
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal
<ak-form-element-horizontal label=${t`Password`} ?required=${true} name="password"> label=${t`Password`}
<input type="password" value="" class="pf-c-form-control" required /> ?required=${true}
</ak-form-element-horizontal> name="password"
</form>`; >
<input type="password" value="" class="pf-c-form-control" required />
</ak-form-element-horizontal>`;
} }
} }

View File

@ -32,32 +32,34 @@ export class UserResetEmailForm extends Form<CoreUsersRecoveryEmailRetrieveReque
return new CoreApi(DEFAULT_CONFIG).coreUsersRecoveryEmailRetrieve(data); return new CoreApi(DEFAULT_CONFIG).coreUsersRecoveryEmailRetrieve(data);
}; };
renderForm(): TemplateResult { renderInlineForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<ak-form-element-horizontal
<ak-form-element-horizontal label=${t`Email stage`} ?required=${true} name="emailStage"> label=${t`Email stage`}
<ak-search-select ?required=${true}
.fetchObjects=${async (query?: string): Promise<Stage[]> => { name="emailStage"
const args: StagesAllListRequest = { >
ordering: "name", <ak-search-select
}; .fetchObjects=${async (query?: string): Promise<Stage[]> => {
if (query !== undefined) { const args: StagesAllListRequest = {
args.search = query; ordering: "name",
} };
const stages = await new StagesApi(DEFAULT_CONFIG).stagesEmailList(args); if (query !== undefined) {
return stages.results; args.search = query;
}} }
.groupBy=${(items: Stage[]) => { const stages = await new StagesApi(DEFAULT_CONFIG).stagesEmailList(args);
return groupBy(items, (stage) => stage.verboseNamePlural); return stages.results;
}} }}
.renderElement=${(stage: Stage): string => { .groupBy=${(items: Stage[]) => {
return stage.name; return groupBy(items, (stage) => stage.verboseNamePlural);
}} }}
.value=${(stage: Stage | undefined): string | undefined => { .renderElement=${(stage: Stage): string => {
return stage?.pk; return stage.name;
}} }}
> .value=${(stage: Stage | undefined): string | undefined => {
</ak-search-select> return stage?.pk;
</ak-form-element-horizontal> }}
</form>`; >
</ak-search-select>
</ak-form-element-horizontal>`;
} }
} }

View File

@ -279,9 +279,23 @@ export abstract class Form<T> extends AKElement {
} }
renderForm(): TemplateResult { renderForm(): TemplateResult {
const inline = this.renderInlineForm();
if (inline) {
return html`<form class="pf-c-form pf-m-horizontal" @submit=${this.submit}>
${inline}
</form>`;
}
return html`<slot></slot>`; return html`<slot></slot>`;
} }
/**
* Inline form render callback when inheriting this class, should be overwritten
* instead of `this.renderForm`
*/
renderInlineForm(): TemplateResult | undefined {
return undefined;
}
renderNonFieldErrors(): TemplateResult { renderNonFieldErrors(): TemplateResult {
if (!this.nonFieldErrors) { if (!this.nonFieldErrors) {
return html``; return html``;