stages/identification: migrate to web
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -1,28 +0,0 @@ | |||||||
| """Additional fields""" |  | ||||||
| from django import forms |  | ||||||
| from django.utils.datastructures import MultiValueDict |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ArrayFieldSelectMultiple(forms.SelectMultiple): |  | ||||||
|     """This is a Form Widget for use with a Postgres ArrayField. It implements |  | ||||||
|     a multi-select interface that can be given a set of `choices`. |  | ||||||
|     You can provide a `delimiter` keyword argument to specify the delimeter used. |  | ||||||
|  |  | ||||||
|     https://gist.github.com/stephane/00e73c0002de52b1c601""" |  | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |  | ||||||
|         # Accept a `delimiter` argument, and grab it (defaulting to a comma) |  | ||||||
|         self.delimiter = kwargs.pop("delimiter", ",") |  | ||||||
|         super().__init__(*args, **kwargs) |  | ||||||
|  |  | ||||||
|     def value_from_datadict(self, data, files, name): |  | ||||||
|         if isinstance(data, MultiValueDict): |  | ||||||
|             # Normally, we'd want a list here, which is what we get from the |  | ||||||
|             # SelectMultiple superclass, but the SimpleArrayField expects to |  | ||||||
|             # get a delimited string, so we're doing a little extra work. |  | ||||||
|             return self.delimiter.join(data.getlist(name)) |  | ||||||
|  |  | ||||||
|         return data.get(name) |  | ||||||
|  |  | ||||||
|     def get_context(self, name, value, attrs): |  | ||||||
|         return super().get_context(name, value.split(self.delimiter), attrs) |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| """authentik flows identification forms""" |  | ||||||
| from django import forms |  | ||||||
| from structlog.stdlib import get_logger |  | ||||||
|  |  | ||||||
| from authentik.admin.fields import ArrayFieldSelectMultiple |  | ||||||
| from authentik.flows.models import Flow, FlowDesignation |  | ||||||
| from authentik.stages.identification.models import IdentificationStage, UserFields |  | ||||||
|  |  | ||||||
| LOGGER = get_logger() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class IdentificationStageForm(forms.ModelForm): |  | ||||||
|     """Form to create/edit IdentificationStage instances""" |  | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |  | ||||||
|         super().__init__(*args, **kwargs) |  | ||||||
|         self.fields["enrollment_flow"].queryset = Flow.objects.filter( |  | ||||||
|             designation=FlowDesignation.ENROLLMENT |  | ||||||
|         ) |  | ||||||
|         self.fields["recovery_flow"].queryset = Flow.objects.filter( |  | ||||||
|             designation=FlowDesignation.RECOVERY |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     class Meta: |  | ||||||
|  |  | ||||||
|         model = IdentificationStage |  | ||||||
|         fields = [ |  | ||||||
|             "name", |  | ||||||
|             "user_fields", |  | ||||||
|             "case_insensitive_matching", |  | ||||||
|             "show_matched_user", |  | ||||||
|             "enrollment_flow", |  | ||||||
|             "recovery_flow", |  | ||||||
|         ] |  | ||||||
|         widgets = { |  | ||||||
|             "name": forms.TextInput(), |  | ||||||
|             "user_fields": ArrayFieldSelectMultiple(choices=UserFields.choices), |  | ||||||
|         } |  | ||||||
| @ -3,7 +3,6 @@ from typing import Type | |||||||
|  |  | ||||||
| from django.contrib.postgres.fields import ArrayField | from django.contrib.postgres.fields import ArrayField | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.forms import ModelForm |  | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import gettext_lazy as _ | ||||||
| from django.views import View | from django.views import View | ||||||
| from rest_framework.serializers import BaseSerializer | from rest_framework.serializers import BaseSerializer | ||||||
| @ -84,10 +83,8 @@ class IdentificationStage(Stage): | |||||||
|         return IdentificationStageView |         return IdentificationStageView | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def form(self) -> Type[ModelForm]: |     def component(self) -> str: | ||||||
|         from authentik.stages.identification.forms import IdentificationStageForm |         return "ak-stage-identification-form" | ||||||
|  |  | ||||||
|         return IdentificationStageForm |  | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										139
									
								
								web/src/pages/stages/identification/IdentificationStageForm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								web/src/pages/stages/identification/IdentificationStageForm.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | |||||||
|  | import { FlowDesignationEnum, FlowsApi, IdentificationStage, IdentificationStageUserFieldsEnum, 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-identification-form") | ||||||
|  | export class IdentificationStageForm extends Form<IdentificationStage> { | ||||||
|  |  | ||||||
|  |     set stageUUID(value: string) { | ||||||
|  |         new StagesApi(DEFAULT_CONFIG).stagesIdentificationRead({ | ||||||
|  |             stageUuid: value, | ||||||
|  |         }).then(stage => { | ||||||
|  |             this.stage = stage; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @property({attribute: false}) | ||||||
|  |     stage?: IdentificationStage; | ||||||
|  |  | ||||||
|  |     getSuccessMessage(): string { | ||||||
|  |         if (this.stage) { | ||||||
|  |             return gettext("Successfully updated stage."); | ||||||
|  |         } else { | ||||||
|  |             return gettext("Successfully created stage."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     send = (data: IdentificationStage): Promise<IdentificationStage> => { | ||||||
|  |         if (this.stage) { | ||||||
|  |             return new StagesApi(DEFAULT_CONFIG).stagesIdentificationUpdate({ | ||||||
|  |                 stageUuid: this.stage.pk || "", | ||||||
|  |                 data: data | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             return new StagesApi(DEFAULT_CONFIG).stagesIdentificationCreate({ | ||||||
|  |                 data: data | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     isUserFieldSelected(field: IdentificationStageUserFieldsEnum): boolean { | ||||||
|  |         return (this.stage?.userFields || []).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=${IdentificationStageUserFieldsEnum.Username} ?selected=${this.isUserFieldSelected(IdentificationStageUserFieldsEnum.Username)}> | ||||||
|  |                                 ${IdentificationStageUserFieldsEnum.Username} | ||||||
|  |                             </option> | ||||||
|  |                             <option value=${IdentificationStageUserFieldsEnum.Email} ?selected=${this.isUserFieldSelected(IdentificationStageUserFieldsEnum.Email)}> | ||||||
|  |                                 ${IdentificationStageUserFieldsEnum.Email} | ||||||
|  |                             </option> | ||||||
|  |                         </select> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("Fields a user can identify themselves with.")}</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 name="caseInsensitiveMatching"> | ||||||
|  |                         <div class="pf-c-check"> | ||||||
|  |                             <input type="checkbox" class="pf-c-check__input" ?checked=${this.stage?.caseInsensitiveMatching || true}> | ||||||
|  |                             <label class="pf-c-check__label"> | ||||||
|  |                                 ${gettext("Case insensitive matching")} | ||||||
|  |                             </label> | ||||||
|  |                         </div> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("When enabled, user fields are matched regardless of their casing.")}</p> | ||||||
|  |                     </ak-form-element-horizontal> | ||||||
|  |                     <ak-form-element-horizontal name="showMatchedUser"> | ||||||
|  |                         <div class="pf-c-check"> | ||||||
|  |                             <input type="checkbox" class="pf-c-check__input" ?checked=${this.stage?.showMatchedUser || true}> | ||||||
|  |                             <label class="pf-c-check__label"> | ||||||
|  |                                 ${gettext("Show matched user")} | ||||||
|  |                             </label> | ||||||
|  |                         </div> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("When a valid username/email has been entered, and this option is enabled, the user's username and avatar will be shown. Otherwise, the text that the user entered will be shown.")}</p> | ||||||
|  |                     </ak-form-element-horizontal> | ||||||
|  |                     <ak-form-element-horizontal | ||||||
|  |                         label=${gettext("Enrollment flow")} | ||||||
|  |                         ?required=${true} | ||||||
|  |                         name="enrollmentFlow"> | ||||||
|  |                         <select class="pf-c-form-control"> | ||||||
|  |                             <option value="" ?selected=${this.stage?.enrollmentFlow === undefined}>---------</option> | ||||||
|  |                             ${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ | ||||||
|  |                                 ordering: "pk", | ||||||
|  |                                 designation: FlowDesignationEnum.Enrollment, | ||||||
|  |                             }).then(flows => { | ||||||
|  |                                 return flows.results.map(flow => { | ||||||
|  |                                     let selected = this.stage?.enrollmentFlow === flow.pk; | ||||||
|  |                                     return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`; | ||||||
|  |                                 }); | ||||||
|  |                             }))} | ||||||
|  |                         </select> | ||||||
|  |                         <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 | ||||||
|  |                         label=${gettext("Recovery flow")} | ||||||
|  |                         ?required=${true} | ||||||
|  |                         name="recoveryFlow"> | ||||||
|  |                         <select class="pf-c-form-control"> | ||||||
|  |                             ${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ | ||||||
|  |                                 ordering: "pk", | ||||||
|  |                                 designation: FlowDesignationEnum.Recovery, | ||||||
|  |                             }).then(flows => { | ||||||
|  |                                 return flows.results.map(flow => { | ||||||
|  |                                     let selected = this.stage?.recoveryFlow === flow.pk; | ||||||
|  |                                     return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`; | ||||||
|  |                                 }); | ||||||
|  |                             }))} | ||||||
|  |                         </select> | ||||||
|  |                         <p class="pf-c-form__helper-text">${gettext("Optional recovery flow, which is linked at the bottom of the page.")}</p> | ||||||
|  |                     </ak-form-element-horizontal> | ||||||
|  |                 </div> | ||||||
|  |             </ak-form-group> | ||||||
|  |         </form>`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer