From 05d875c9926a1d274e93fd3108e8954a1cede0ab Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 24 Oct 2022 16:33:06 +0200 Subject: [PATCH] fix(login, console): correctly fill username on initialization, password and change password view (#4546) * fix(login): add loginname as query param, send with inituserlink * set loginname as username autofill on password site * add loginname input on change password * fix console password change autocomplete * fix(console): apply labelpolicy if icon is provided, signout page (#4499) * label policy as observable * signedout policy via state * add caching * disable loading spinner on signedout * cleanup * catch error * update deps * move policy to localstorage * handle labelpolicy for users without org Co-authored-by: Livio Spring Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com> * fix(email): set sender address as return-path header (#4569) * feat(login): additionally use email/phone for authentication (#4563) * feat: add ability to disable login by email and phone * feat: check login by email and phone * fix: set verified email / phone correctly on notify users * update projection version * fix merge * fix email/phone verified reduce tests * fix user tests * loginname check * cleanup * fix: update user projection version to handle fixed statement * ci(e2e): give console init time (#4567) * fix: idp usage (#4571) * fix: send email verification instead of init code for idp users * fix: select single idp of external only users * fix: use single idp on login * fix(import): add import for app and machine keys (#4536) * fix(import): add import for app and machine keys * fix(export): add review changes * fix(import): Apply suggestions from code review Co-authored-by: Livio Spring * fix(import): add review changes Co-authored-by: Livio Spring * fix(console): hide metadata on auth side if no `user.read` role present (#4512) * check for role * require user.read for showing metadata section in auth-user * remove aggregate id from role check Co-authored-by: Livio Spring * update stable release to 2.8.2 (#4574) * fix: import of trigger actions and export of idp links (#4576) Co-authored-by: Livio Spring * fix(console): split password from contact information, initialization mail on top (#4380) * chore(console): split password from contact information * change user detail, initialization mail * fix translation * Update console/src/assets/i18n/de.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/de.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/en.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/fr.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/fr.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/it.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/en.json Co-authored-by: Elio Bischof * i18n Co-authored-by: Elio Bischof * fix(import): import json marshal to jsonpb (#4580) * fix(import): import json marshal to jsonpb * fix: add unmarshaloptions discard unknown Co-authored-by: Livio Spring * fix(import): import json marshal to jsonpb Co-authored-by: Livio Spring * feat(console): rename org (#4542) * rename org * add data-e2e * e2e test * restore state after * use ngIf instead of hasrole directive and initialized regex * rm h2 check * Update e2e/cypress/e2e/organization/organizations.cy.ts Co-authored-by: Elio Bischof * Update console/src/assets/i18n/de.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/de.json Co-authored-by: Elio Bischof * Update console/src/assets/i18n/en.json Co-authored-by: Elio Bischof * change e2e test * org param * reintroduct org param * use org query param * org rename test * no initial focus on button * contain name Co-authored-by: Elio Bischof * feat: instance remove (#4345) * feat(instance): add remove instance event with projections cleanup * fix(instance): corrected used id to clean up projections * fix merge * fix: correct unit test projection names * fix: current sequence of lists and query for ensuring keypair based projections Co-authored-by: Livio Spring Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com> * docs: change nextjs quickstart (#4566) * docs: change nextjs repo update readme * Update docs/docs/examples/login/nextjs.md Co-authored-by: Florian Forster * Update docs/docs/examples/login/nextjs.md Co-authored-by: Florian Forster Co-authored-by: Florian Forster * fix(console): preserve logo and icon aspect ratios, remove border radius in header (#4585) * chore(e2e): Skip asking for new password on Admin in dev environment (#4599) * feat(e2e): Skip asking for new password on Admin * remove password changing Co-authored-by: Elio Bischof * docs(contributing): remove guides folder (#4603) * preferredLoginName as queryParam Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com> Co-authored-by: Livio Spring Co-authored-by: Elio Bischof Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: Florian Forster Co-authored-by: p_0g_8mm3_ <37022952+pr0gr8mm3r@users.noreply.github.com> --- .../auth-user-detail.component.html | 8 +++- .../user-detail/contact/contact.component.ts | 1 + .../password/password.component.html | 27 +++++++++--- .../password/password.component.scss | 43 +++++++++++-------- .../password/password.component.ts | 14 ++++-- .../user-detail/user-detail.component.html | 2 + internal/api/ui/login/init_user_handler.go | 27 +++++++----- internal/api/ui/login/renderer.go | 2 +- .../static/templates/change_password.html | 1 + .../ui/login/static/templates/init_user.html | 3 +- .../ui/login/static/templates/password.html | 2 +- internal/notification/types/init_code.go | 2 +- 12 files changed, 88 insertions(+), 44 deletions(-) diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html index e8b07421f6..bc4c1da4ba 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html @@ -57,6 +57,7 @@ diff --git a/console/src/app/pages/users/user-detail/contact/contact.component.ts b/console/src/app/pages/users/user-detail/contact/contact.component.ts index 68e59f5221..f4fd937cc7 100644 --- a/console/src/app/pages/users/user-detail/contact/contact.component.ts +++ b/console/src/app/pages/users/user-detail/contact/contact.component.ts @@ -15,6 +15,7 @@ export class ContactComponent { @Input() disablePhoneCode: boolean = false; @Input() canWrite: boolean | null = false; @Input() human?: Human.AsObject; + @Input() username: string = ''; @Input() state!: UserState; @Output() editType: EventEmitter = new EventEmitter(); @Output() resendEmailVerification: EventEmitter = new EventEmitter(); diff --git a/console/src/app/pages/users/user-detail/password/password.component.html b/console/src/app/pages/users/user-detail/password/password.component.html index e9cc67ab6f..90d694bfb9 100644 --- a/console/src/app/pages/users/user-detail/password/password.component.html +++ b/console/src/app/pages/users/user-detail/password/password.component.html @@ -1,12 +1,17 @@

{{ 'USER.PASSWORD.DESCRIPTION' | translate }}

-
+ + +
@@ -39,6 +44,16 @@ + +
{{ 'USER.PASSWORD.OLD' | translate }} diff --git a/console/src/app/pages/users/user-detail/password/password.component.scss b/console/src/app/pages/users/user-detail/password/password.component.scss index 340702035b..d7d02d8bb1 100644 --- a/console/src/app/pages/users/user-detail/password/password.component.scss +++ b/console/src/app/pages/users/user-detail/password/password.component.scss @@ -3,35 +3,40 @@ font-size: 14px; } -.password-content { - display: flex; - flex-direction: row; - flex-wrap: wrap; - max-width: 40rem; +form { + .hiddeninput { + display: none; + } - .side-by-side { + .password-content { display: flex; - flex-wrap: wrap; flex-direction: row; - margin: 0 -0.5rem; - width: 100%; + flex-wrap: wrap; + max-width: 40rem; - @media only screen and (max-width: 500px) { - flex-direction: column; + .side-by-side { + display: flex; + flex-wrap: wrap; + flex-direction: row; + margin: 0 -0.5rem; + width: 100%; + + @media only screen and (max-width: 500px) { + flex-direction: column; + } + + .formfield { + flex: 1; + margin: 0 0.5rem; + } } .formfield { - flex: 1; - margin: 0 0.5rem; + max-width: 400px; + width: 100%; } } - - .formfield { - max-width: 400px; - width: 100%; - } } - .validation { &.between { margin: 0 0.5rem; diff --git a/console/src/app/pages/users/user-detail/password/password.component.ts b/console/src/app/pages/users/user-detail/password/password.component.ts index b6ee1c497a..ef32a1478d 100644 --- a/console/src/app/pages/users/user-detail/password/password.component.ts +++ b/console/src/app/pages/users/user-detail/password/password.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy } from '@angular/core'; import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; -import { Subscription, take } from 'rxjs'; +import { Subject, Subscription, take, takeUntil } from 'rxjs'; import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators'; import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; @@ -31,11 +31,13 @@ function passwordConfirmValidator(c: AbstractControl): any { }) export class PasswordComponent implements OnDestroy { userId: string = ''; + public username: string = ''; public policy!: PasswordComplexityPolicy.AsObject; public passwordForm!: UntypedFormGroup; private formSub: Subscription = new Subscription(); + private destroy$: Subject = new Subject(); constructor( activatedRoute: ActivatedRoute, @@ -45,11 +47,14 @@ export class PasswordComponent implements OnDestroy { private toast: ToastService, private breadcrumbService: BreadcrumbService, ) { - activatedRoute.params.subscribe((data) => { + activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe((data) => { + const { username } = data; + this.username = username; + }); + activatedRoute.params.pipe(takeUntil(this.destroy$)).subscribe((data) => { const { id } = data; if (id) { this.userId = id; - breadcrumbService.setBreadcrumb([ new Breadcrumb({ type: BreadcrumbType.ORG, @@ -59,6 +64,7 @@ export class PasswordComponent implements OnDestroy { } else { this.authService.user.pipe(take(1)).subscribe((user) => { if (user) { + this.username = user.preferredLoginName; this.breadcrumbService.setBreadcrumb([ new Breadcrumb({ type: BreadcrumbType.AUTHUSER, @@ -102,6 +108,8 @@ export class PasswordComponent implements OnDestroy { } ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); this.formSub.unsubscribe(); } diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html index b249030ec8..c97c0f5e31 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html @@ -103,6 +103,7 @@ diff --git a/internal/api/ui/login/init_user_handler.go b/internal/api/ui/login/init_user_handler.go index fb1240c48e..707cbc8416 100644 --- a/internal/api/ui/login/init_user_handler.go +++ b/internal/api/ui/login/init_user_handler.go @@ -12,6 +12,7 @@ import ( const ( queryInitUserCode = "code" queryInitUserUserID = "userID" + queryInitUserLoginName = "loginname" queryInitUserPassword = "passwordset" tmplInitUser = "inituser" @@ -20,6 +21,7 @@ const ( type initUserFormData struct { Code string `schema:"code"` + LoginName string `schema:"loginname"` Password string `schema:"password"` PasswordConfirm string `schema:"passwordconfirm"` UserID string `schema:"userID"` @@ -31,6 +33,7 @@ type initUserData struct { baseData profileData Code string + LoginName string UserID string PasswordSet bool MinLength uint64 @@ -40,15 +43,16 @@ type initUserData struct { HasSymbol string } -func InitUserLink(origin, userID, code, orgID string, passwordSet bool) string { - return fmt.Sprintf("%s%s?userID=%s&code=%s&passwordset=%t&orgID=%s", externalLink(origin), EndpointInitUser, userID, code, passwordSet, orgID) +func InitUserLink(origin, userID, loginName, code, orgID string, passwordSet bool) string { + return fmt.Sprintf("%s%s?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", externalLink(origin), EndpointInitUser, userID, loginName, code, orgID, passwordSet) } func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) { userID := r.FormValue(queryInitUserUserID) code := r.FormValue(queryInitUserCode) + loginName := r.FormValue(queryInitUserLoginName) passwordSet, _ := strconv.ParseBool(r.FormValue(queryInitUserPassword)) - l.renderInitUser(w, r, nil, userID, code, passwordSet, nil) + l.renderInitUser(w, r, nil, userID, loginName, code, passwordSet, nil) } func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) { @@ -60,7 +64,7 @@ func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) { } if data.Resend { - l.resendUserInit(w, r, authReq, data.UserID, data.PasswordSet) + l.resendUserInit(w, r, authReq, data.UserID, data.LoginName, data.PasswordSet) return } l.checkUserInitCode(w, r, authReq, data, nil) @@ -69,7 +73,7 @@ func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) { func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, data *initUserFormData, err error) { if data.Password != data.PasswordConfirm { err := caos_errs.ThrowInvalidArgument(nil, "VIEW-fsdfd", "Errors.User.Password.ConfirmationWrong") - l.renderInitUser(w, r, authReq, data.UserID, data.Code, data.PasswordSet, err) + l.renderInitUser(w, r, authReq, data.UserID,data.LoginName, data.Code, data.PasswordSet, err) return } userOrgID := "" @@ -78,32 +82,32 @@ func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authRe } initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg) if err != nil { - l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err) + l.renderInitUser(w, r, authReq, data.UserID,data.LoginName, "", data.PasswordSet, err) return } err = l.command.HumanVerifyInitCode(setContext(r.Context(), userOrgID), data.UserID, userOrgID, data.Code, data.Password, initCodeGenerator) if err != nil { - l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err) + l.renderInitUser(w, r, authReq, data.UserID,data.LoginName, "", data.PasswordSet, err) return } l.renderInitUserDone(w, r, authReq, userOrgID) } -func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string, showPassword bool) { +func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID string,loginName string, showPassword bool) { userOrgID := "" if authReq != nil { userOrgID = authReq.UserOrgID } initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg) if err != nil { - l.renderInitUser(w, r, authReq, userID, "", showPassword, err) + l.renderInitUser(w, r, authReq, userID,loginName, "", showPassword, err) return } _, err = l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID, initCodeGenerator) - l.renderInitUser(w, r, authReq, userID, "", showPassword, err) + l.renderInitUser(w, r, authReq, userID, loginName, "", showPassword, err) } -func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID, code string, passwordSet bool, err error) { +func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, userID,loginName string, code string, passwordSet bool, err error) { var errID, errMessage string if err != nil { errID, errMessage = l.getErrorMessage(r, err) @@ -115,6 +119,7 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq * baseData: l.getBaseData(r, authReq, "Init User", errID, errMessage), profileData: l.getProfileData(authReq), UserID: userID, + LoginName: loginName, Code: code, PasswordSet: passwordSet, } diff --git a/internal/api/ui/login/renderer.go b/internal/api/ui/login/renderer.go index d3c4ef3d4e..292ec59249 100644 --- a/internal/api/ui/login/renderer.go +++ b/internal/api/ui/login/renderer.go @@ -292,7 +292,7 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq * case *domain.MFAPromptStep: l.renderMFAPrompt(w, r, authReq, step, err) case *domain.InitUserStep: - l.renderInitUser(w, r, authReq, "", "", step.PasswordSet, nil) + l.renderInitUser(w, r, authReq, "", "", "", step.PasswordSet, nil) case *domain.ChangeUsernameStep: l.renderChangeUsername(w, r, authReq, nil) case *domain.LinkUsersStep: diff --git a/internal/api/ui/login/static/templates/change_password.html b/internal/api/ui/login/static/templates/change_password.html index 5627b3f91a..456b9d4449 100644 --- a/internal/api/ui/login/static/templates/change_password.html +++ b/internal/api/ui/login/static/templates/change_password.html @@ -12,6 +12,7 @@ {{ .CSRF }} +
diff --git a/internal/api/ui/login/static/templates/init_user.html b/internal/api/ui/login/static/templates/init_user.html index c6b6b6bbe5..1cc1073c85 100644 --- a/internal/api/ui/login/static/templates/init_user.html +++ b/internal/api/ui/login/static/templates/init_user.html @@ -16,11 +16,12 @@ +
-
diff --git a/internal/api/ui/login/static/templates/password.html b/internal/api/ui/login/static/templates/password.html index bb9011ef59..a8149a8269 100644 --- a/internal/api/ui/login/static/templates/password.html +++ b/internal/api/ui/login/static/templates/password.html @@ -11,7 +11,7 @@ {{ .CSRF }} - +
diff --git a/internal/notification/types/init_code.go b/internal/notification/types/init_code.go index 01399ef29d..10d68cf346 100644 --- a/internal/notification/types/init_code.go +++ b/internal/notification/types/init_code.go @@ -7,7 +7,7 @@ import ( ) func (notify Notify) SendUserInitCode(user *query.NotifyUser, origin, code string) error { - url := login.InitUserLink(origin, user.ID, code, user.ResourceOwner, user.PasswordSet) + url := login.InitUserLink(origin, user.ID, user.PreferredLoginName, code, user.ResourceOwner, user.PasswordSet) args := make(map[string]interface{}) args["Code"] = code return notify(url, args, domain.InitCodeMessageType, true)