mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-06 08:57:38 +00:00
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 <livio.a@gmail.com> 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 <livio.a@gmail.com> * fix(import): add review changes Co-authored-by: Livio Spring <livio.a@gmail.com> * 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 <livio.a@gmail.com> * update stable release to 2.8.2 (#4574) * fix: import of trigger actions and export of idp links (#4576) Co-authored-by: Livio Spring <livio.a@gmail.com> * 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 <eliobischof@gmail.com> * Update console/src/assets/i18n/de.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * Update console/src/assets/i18n/en.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * Update console/src/assets/i18n/fr.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * Update console/src/assets/i18n/fr.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * Update console/src/assets/i18n/it.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * Update console/src/assets/i18n/en.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * i18n Co-authored-by: Elio Bischof <eliobischof@gmail.com> * 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 <livio.a@gmail.com> * fix(import): import json marshal to jsonpb Co-authored-by: Livio Spring <livio.a@gmail.com> * 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 <eliobischof@gmail.com> * Update console/src/assets/i18n/de.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * Update console/src/assets/i18n/de.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * Update console/src/assets/i18n/en.json Co-authored-by: Elio Bischof <eliobischof@gmail.com> * 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 <eliobischof@gmail.com> * 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 <livio.a@gmail.com> 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 <florian@zitadel.com> * Update docs/docs/examples/login/nextjs.md Co-authored-by: Florian Forster <florian@zitadel.com> Co-authored-by: Florian Forster <florian@zitadel.com> * 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 <eliobischof@gmail.com> * docs(contributing): remove guides folder (#4603) * preferredLoginName as queryParam Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com> Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Elio Bischof <eliobischof@gmail.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: Florian Forster <florian@zitadel.com> Co-authored-by: p_0g_8mm3_ <37022952+pr0gr8mm3r@users.noreply.github.com>
This commit is contained in:
parent
9693ed4455
commit
05d875c992
@ -57,6 +57,7 @@
|
||||
<cnsl-contact
|
||||
*ngIf="user.human"
|
||||
[human]="user.human"
|
||||
[username]="user.preferredLoginName"
|
||||
[state]="user.state"
|
||||
[canWrite]="true"
|
||||
(editType)="openEditDialog($event)"
|
||||
@ -97,7 +98,12 @@
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<a matTooltip="{{ 'USER.PASSWORD.SET' | translate }}" [routerLink]="['password']" mat-icon-button>
|
||||
<a
|
||||
matTooltip="{{ 'USER.PASSWORD.SET' | translate }}"
|
||||
[routerLink]="['password']"
|
||||
[queryParams]="{ username: user.preferredLoginName }"
|
||||
mat-icon-button
|
||||
>
|
||||
<i class="las la-pen"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -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<EditDialogType> = new EventEmitter<EditDialogType>();
|
||||
@Output() resendEmailVerification: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
@ -1,12 +1,17 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" title="{{ 'USER.PASSWORD.TITLE' | translate }}">
|
||||
<p class="password-info cnsl-secondary-text" sub>{{ 'USER.PASSWORD.DESCRIPTION' | translate }}</p>
|
||||
<ng-container *ngIf="userId; else authUser">
|
||||
<form
|
||||
*ngIf="passwordForm"
|
||||
autocomplete="new-password"
|
||||
[formGroup]="passwordForm"
|
||||
(ngSubmit)="setInitialPassword(userId)"
|
||||
>
|
||||
<form *ngIf="passwordForm" [formGroup]="passwordForm" (ngSubmit)="setInitialPassword(userId)">
|
||||
<input
|
||||
*ngIf="username"
|
||||
class="hiddeninput"
|
||||
type="hidden"
|
||||
autocomplete="username"
|
||||
name="username"
|
||||
type="text"
|
||||
[value]="username"
|
||||
/>
|
||||
|
||||
<div class="password-content">
|
||||
<div class="side-by-side">
|
||||
<cnsl-form-field class="formfield">
|
||||
@ -39,6 +44,16 @@
|
||||
|
||||
<ng-template #authUser>
|
||||
<form *ngIf="passwordForm" [formGroup]="passwordForm" (ngSubmit)="setPassword()">
|
||||
<input
|
||||
*ngIf="username"
|
||||
class="hiddeninput"
|
||||
type="hidden"
|
||||
autocomplete="username"
|
||||
name="username"
|
||||
type="text"
|
||||
[value]="username"
|
||||
/>
|
||||
|
||||
<div class="password-content">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.OLD' | translate }}</cnsl-label>
|
||||
|
@ -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;
|
||||
|
@ -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<void> = 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();
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,7 @@
|
||||
<cnsl-contact
|
||||
[disablePhoneCode]="true"
|
||||
[state]="user.state"
|
||||
[username]="user.preferredLoginName"
|
||||
[canWrite]="['user.write:' + user.id, 'user.write$'] | hasRole | async"
|
||||
*ngIf="user?.human"
|
||||
[human]="user.human"
|
||||
@ -186,6 +187,7 @@
|
||||
matTooltip="{{ 'USER.PASSWORD.SET' | translate }}"
|
||||
[disabled]="(['user.write:' + user.id, 'user.write$'] | hasRole | async) === false"
|
||||
[routerLink]="['password']"
|
||||
[queryParams]="{ username: user.preferredLoginName }"
|
||||
mat-icon-button
|
||||
>
|
||||
<i class="las la-pen"></i>
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -12,6 +12,7 @@
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="loginname" value="{{ .LoginName }}" autocomplete="username" />
|
||||
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
|
@ -16,11 +16,12 @@
|
||||
<input type="hidden" name="userID" value="{{ .UserID }}" />
|
||||
<input type="hidden" name="passwordSet" value="{{ .PasswordSet }}" />
|
||||
<input type="hidden" name="orgID" value="{{ .OrgID }}" />
|
||||
<input type="hidden" name="loginname" value="{{ .LoginName }}" autocomplete="username" />
|
||||
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<label class="lgn-label" for="code">{{t "InitUser.CodeLabel"}}</label>
|
||||
<input class="lgn-input" {{if .ErrMessage}}shake {{end}} type="text" id="code" name="code" value="{{.Code}}" autocomplete="off" autofocus
|
||||
<input class="lgn-input" {{if .ErrMessage}}shake {{end}} type="text" id="code" name="code" value="{{.Code}}" autocomplete="one-time-code" autofocus
|
||||
required>
|
||||
</div>
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="loginName" value="{{ .LoginName }}" />
|
||||
<input type="hidden" name="loginName" value="{{ .LoginName }}" autocomplete="username" />
|
||||
|
||||
<div class="fields">
|
||||
<label class="lgn-label" for="password">{{t "Password.PasswordLabel"}}</label>
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user