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)