stages/password: migrate to web
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -1,39 +0,0 @@ | |||||||
| """authentik administration forms""" |  | ||||||
| from django import forms |  | ||||||
| from django.utils.translation import gettext_lazy as _ |  | ||||||
|  |  | ||||||
| from authentik.flows.models import Flow, FlowDesignation |  | ||||||
| from authentik.stages.password.models import PasswordStage |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_authentication_backends(): |  | ||||||
|     """Return all available authentication backends as tuple set""" |  | ||||||
|     return [ |  | ||||||
|         ( |  | ||||||
|             "django.contrib.auth.backends.ModelBackend", |  | ||||||
|             _("authentik-internal Userdatabase"), |  | ||||||
|         ), |  | ||||||
|         ( |  | ||||||
|             "authentik.sources.ldap.auth.LDAPBackend", |  | ||||||
|             _("authentik LDAP"), |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class PasswordStageForm(forms.ModelForm): |  | ||||||
|     """Form to create/edit Password Stages""" |  | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |  | ||||||
|         super().__init__(*args, **kwargs) |  | ||||||
|         self.fields["configure_flow"].queryset = Flow.objects.filter( |  | ||||||
|             designation=FlowDesignation.STAGE_CONFIGURATION |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     class Meta: |  | ||||||
|  |  | ||||||
|         model = PasswordStage |  | ||||||
|         fields = ["name", "backends", "configure_flow", "failed_attempts_before_cancel"] |  | ||||||
|         widgets = { |  | ||||||
|             "name": forms.TextInput(), |  | ||||||
|             "backends": forms.SelectMultiple(choices=get_authentication_backends()), |  | ||||||
|         } |  | ||||||
| @ -0,0 +1,31 @@ | |||||||
|  | # Generated by Django 3.1.7 on 2021-04-02 22:21 | ||||||
|  |  | ||||||
|  | import django.contrib.postgres.fields | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("authentik_stages_password", "0004_auto_20200925_1057"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name="passwordstage", | ||||||
|  |             name="backends", | ||||||
|  |             field=django.contrib.postgres.fields.ArrayField( | ||||||
|  |                 base_field=models.TextField( | ||||||
|  |                     choices=[ | ||||||
|  |                         ( | ||||||
|  |                             "django.contrib.auth.backends.ModelBackend", | ||||||
|  |                             "authentik-internal Userdatabase", | ||||||
|  |                         ), | ||||||
|  |                         ("authentik.sources.ldap.auth.LDAPBackend", "authentik LDAP"), | ||||||
|  |                     ] | ||||||
|  |                 ), | ||||||
|  |                 help_text="Selection of backends to test the password against.", | ||||||
|  |                 size=None, | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -12,11 +12,25 @@ from authentik.core.types import UserSettingSerializer | |||||||
| from authentik.flows.models import ConfigurableStage, Stage | from authentik.flows.models import ConfigurableStage, Stage | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_authentication_backends(): | ||||||
|  |     """Return all available authentication backends as tuple set""" | ||||||
|  |     return [ | ||||||
|  |         ( | ||||||
|  |             "django.contrib.auth.backends.ModelBackend", | ||||||
|  |             _("authentik-internal Userdatabase"), | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |             "authentik.sources.ldap.auth.LDAPBackend", | ||||||
|  |             _("authentik LDAP"), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class PasswordStage(ConfigurableStage, Stage): | class PasswordStage(ConfigurableStage, Stage): | ||||||
|     """Prompts the user for their password, and validates it against the configured backends.""" |     """Prompts the user for their password, and validates it against the configured backends.""" | ||||||
|  |  | ||||||
|     backends = ArrayField( |     backends = ArrayField( | ||||||
|         models.TextField(), |         models.TextField(choices=get_authentication_backends()), | ||||||
|         help_text=_("Selection of backends to test the password against."), |         help_text=_("Selection of backends to test the password against."), | ||||||
|     ) |     ) | ||||||
|     failed_attempts_before_cancel = models.IntegerField( |     failed_attempts_before_cancel = models.IntegerField( | ||||||
| @ -42,10 +56,8 @@ class PasswordStage(ConfigurableStage, Stage): | |||||||
|         return PasswordStageView |         return PasswordStageView | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def form(self) -> Type[ModelForm]: |     def component(self) -> str: | ||||||
|         from authentik.stages.password.forms import PasswordStageForm |         return "ak-stage-password-form" | ||||||
|  |  | ||||||
|         return PasswordStageForm |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def ui_user_settings(self) -> Optional[UserSettingSerializer]: |     def ui_user_settings(self) -> Optional[UserSettingSerializer]: | ||||||
|  | |||||||
| @ -18049,7 +18049,9 @@ definitions: | |||||||
|         items: |         items: | ||||||
|           title: Backends |           title: Backends | ||||||
|           type: string |           type: string | ||||||
|           minLength: 1 |           enum: | ||||||
|  |             - django.contrib.auth.backends.ModelBackend | ||||||
|  |             - authentik.sources.ldap.auth.LDAPBackend | ||||||
|       configure_flow: |       configure_flow: | ||||||
|         title: Configure flow |         title: Configure flow | ||||||
|         description: Flow used by an authenticated user to configure this Stage. If |         description: Flow used by an authenticated user to configure this Stage. If | ||||||
|  | |||||||
| @ -112,7 +112,7 @@ export class IdentificationStageForm extends Form<IdentificationStage> { | |||||||
|                                 }); |                                 }); | ||||||
|                             }))} |                             }))} | ||||||
|                         </select> |                         </select> | ||||||
|                         <p class="pf-c-form__helper-text">${gettext("Optional enrollment flow, which is linked at the bottom of the page..")}</p> |                         <p class="pf-c-form__helper-text">${gettext("Optional enrollment flow, which is linked at the bottom of the page.")}</p> | ||||||
|                     </ak-form-element-horizontal> |                     </ak-form-element-horizontal> | ||||||
|                     <ak-form-element-horizontal |                     <ak-form-element-horizontal | ||||||
|                         label=${gettext("Recovery flow")} |                         label=${gettext("Recovery flow")} | ||||||
|  | |||||||
							
								
								
									
										114
									
								
								web/src/pages/stages/password/PasswordStageForm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								web/src/pages/stages/password/PasswordStageForm.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | |||||||
|  | import { FlowDesignationEnum, FlowsApi, PasswordStage, PasswordStageBackendsEnum, StagesApi } from "authentik-api"; | ||||||
|  | import { gettext } from "django"; | ||||||
|  | import { customElement, property } from "lit-element"; | ||||||
|  | import { html, TemplateResult } from "lit-html"; | ||||||
|  | import { DEFAULT_CONFIG } from "../../../api/Config"; | ||||||
|  | import { Form } from "../../../elements/forms/Form"; | ||||||
|  | import { ifDefined } from "lit-html/directives/if-defined"; | ||||||
|  | import "../../../elements/forms/HorizontalFormElement"; | ||||||
|  | import "../../../elements/forms/FormGroup"; | ||||||
|  | import { until } from "lit-html/directives/until"; | ||||||
|  |  | ||||||
|  | @customElement("ak-stage-password-form") | ||||||
|  | export class PasswordStageForm extends Form<PasswordStage> { | ||||||
|  |  | ||||||
|  |     set stageUUID(value: string) { | ||||||
|  |         new StagesApi(DEFAULT_CONFIG).stagesPasswordRead({ | ||||||
|  |             stageUuid: value, | ||||||
|  |         }).then(stage => { | ||||||
|  |             this.stage = stage; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @property({attribute: false}) | ||||||
|  |     stage?: PasswordStage; | ||||||
|  |  | ||||||
|  |     getSuccessMessage(): string { | ||||||
|  |         if (this.stage) { | ||||||
|  |             return gettext("Successfully updated stage."); | ||||||
|  |         } else { | ||||||
|  |             return gettext("Successfully created stage."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     send = (data: PasswordStage): Promise<PasswordStage> => { | ||||||
|  |         if (this.stage) { | ||||||
|  |             return new StagesApi(DEFAULT_CONFIG).stagesPasswordUpdate({ | ||||||
|  |                 stageUuid: this.stage.pk || "", | ||||||
|  |                 data: data | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             return new StagesApi(DEFAULT_CONFIG).stagesPasswordCreate({ | ||||||
|  |                 data: data | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     isBackendSelected(field: PasswordStageBackendsEnum): boolean { | ||||||
|  |         return (this.stage?.backends || []).filter(isField => { | ||||||
|  |             return field === isField; | ||||||
|  |         }).length > 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     renderForm(): TemplateResult { | ||||||
|  |         return html`<form class="pf-c-form pf-m-horizontal"> | ||||||
|  |             <ak-form-element-horizontal | ||||||
|  |                 label=${gettext("Name")} | ||||||
|  |                 ?required=${true} | ||||||
|  |                 name="name"> | ||||||
|  |                 <input type="text" value="${ifDefined(this.stage?.name || "")}" class="pf-c-form-control" required> | ||||||
|  |             </ak-form-element-horizontal> | ||||||
|  |             <ak-form-group .expanded=${true}> | ||||||
|  |                 <span slot="header"> | ||||||
|  |                     ${gettext("Stage-specific settings")} | ||||||
|  |                 </span> | ||||||
|  |                 <div slot="body" class="pf-c-form"> | ||||||
|  |                     <ak-form-element-horizontal | ||||||
|  |                         label=${gettext("User fields")} | ||||||
|  |                         ?required=${true} | ||||||
|  |                         name="transports"> | ||||||
|  |                         <select name="users" class="pf-c-form-control" multiple> | ||||||
|  |                             <option value=${PasswordStageBackendsEnum.DjangoContribAuthBackendsModelBackend} ?selected=${this.isBackendSelected(PasswordStageBackendsEnum.DjangoContribAuthBackendsModelBackend)}> | ||||||
|  |                                 ${gettext("authentik Builtin Database")} | ||||||
|  |                                 </option> | ||||||
|  |                             <option value=${PasswordStageBackendsEnum.AuthentikSourcesLdapAuthLdapBackend} ?selected=${this.isBackendSelected(PasswordStageBackendsEnum.AuthentikSourcesLdapAuthLdapBackend)}> | ||||||
|  |                                 ${gettext("authentik LDAP Backend")} | ||||||
|  |                             </option> | ||||||
|  |                         </select> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("Selection of backends to test the password against.")}</p> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("Hold control/command to select multiple items.")}</p> | ||||||
|  |                     </ak-form-element-horizontal> | ||||||
|  |                     <ak-form-element-horizontal | ||||||
|  |                         label=${gettext("Configuration flow")} | ||||||
|  |                         ?required=${true} | ||||||
|  |                         name="configureFlow"> | ||||||
|  |                         <select class="pf-c-form-control"> | ||||||
|  |                             <option value="" ?selected=${this.stage?.configureFlow === undefined}>---------</option> | ||||||
|  |                             ${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ | ||||||
|  |                                 ordering: "pk", | ||||||
|  |                                 designation: FlowDesignationEnum.StageConfiguration, | ||||||
|  |                             }).then(flows => { | ||||||
|  |                                 return flows.results.map(flow => { | ||||||
|  |                                     let selected = this.stage?.configureFlow === flow.pk; | ||||||
|  |                                     if (!this.stage?.configureFlow && flow.slug === "default-password-change") { | ||||||
|  |                                         selected = true; | ||||||
|  |                                     } | ||||||
|  |                                     return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`; | ||||||
|  |                                 }); | ||||||
|  |                             }))} | ||||||
|  |                         </select> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password.")}</p> | ||||||
|  |                     </ak-form-element-horizontal> | ||||||
|  |                     <ak-form-element-horizontal | ||||||
|  |                         label=${gettext("Failed attempts before cancel")} | ||||||
|  |                         ?required=${true} | ||||||
|  |                         name="failedAttemptsBeforeCancel"> | ||||||
|  |                         <input type="number" value="${ifDefined(this.stage?.failedAttemptsBeforeCancel || 5)}" class="pf-c-form-control" required> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("How many attempts a user has before the flow is canceled. To lock the user out, use a reputation policy and a user_write stage.")}</p> | ||||||
|  |                     </ak-form-element-horizontal> | ||||||
|  |                 </div> | ||||||
|  |             </ak-form-group> | ||||||
|  |         </form>`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer