mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 04:17:32 +00:00
feat: Lockout policy (#2121)
* feat: lock users if lockout policy is set * feat: setup * feat: lock user on password failes * feat: render error * feat: lock user on command side * feat: auth_req tests * feat: lockout policy docs * feat: remove show lockout failures from proto * fix: console lockout * feat: tests * fix: tests * unlock function * add unlock button * fix migration version * lockout policy * lint * Update internal/auth/repository/eventsourcing/eventstore/auth_request.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * fix: err message * Update internal/command/setup_step4.go Co-authored-by: Silvan <silvan.reusser@gmail.com> Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
34
console/package-lock.json
generated
34
console/package-lock.json
generated
@@ -35,6 +35,7 @@
|
||||
"libphonenumber-js": "^1.9.16",
|
||||
"moment": "^2.29.1",
|
||||
"ngx-color": "^7.2.0",
|
||||
"ngx-image-cropper": "^3.3.5",
|
||||
"ngx-quicklink": "^0.2.6",
|
||||
"rxjs": "~6.6.7",
|
||||
"tinycolor2": "^1.4.2",
|
||||
@@ -10368,6 +10369,24 @@
|
||||
"@angular/core": ">=12.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-image-cropper": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ngx-image-cropper/-/ngx-image-cropper-3.3.5.tgz",
|
||||
"integrity": "sha512-0yRVKG5XAbVo3rOaj/iFDlekGsxEqXKU9iXFbjyvHvRT2DFs+AjwtyvINsHCWw+4ed9yA4Y+wLIUNqzA0bfxLw==",
|
||||
"dependencies": {
|
||||
"tslib": "^1.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=8.0.0",
|
||||
"@angular/core": ">=8.0.0",
|
||||
"@angular/platform-browser": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-image-cropper/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/ngx-quicklink": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz",
|
||||
@@ -27536,6 +27555,21 @@
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"ngx-image-cropper": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ngx-image-cropper/-/ngx-image-cropper-3.3.5.tgz",
|
||||
"integrity": "sha512-0yRVKG5XAbVo3rOaj/iFDlekGsxEqXKU9iXFbjyvHvRT2DFs+AjwtyvINsHCWw+4ed9yA4Y+wLIUNqzA0bfxLw==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ngx-quicklink": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz",
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.PWD_LOCKOUT.TITLE' | translate" [description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate">
|
||||
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
|
||||
<cnsl-info-section class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="resetPolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
@@ -14,22 +14,15 @@
|
||||
<span class="left-desc">{{'POLICY.DATA.MAXATTEMPTS' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<div class="length-wrapper">
|
||||
<button mat-icon-button (click)="incrementMaxAttempts()">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
<span>{{lockoutData?.maxAttempts}}</span>
|
||||
<button mat-icon-button (click)="decrementMaxAttempts()">
|
||||
<mat-icon>remove</mat-icon>
|
||||
</button>
|
||||
<span>{{lockoutData?.maxPasswordAttempts}}</span>
|
||||
<button mat-icon-button (click)="incrementMaxAttempts()">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="left-desc">{{'POLICY.DATA.SHOWLOCKOUTFAILURES' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="showLockoutFailure" ngDefaultControl
|
||||
[(ngModel)]="lockoutData.showLockoutFailure">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
|
@@ -1,6 +1,6 @@
|
||||
.default {
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@@ -3,13 +3,11 @@ import { FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { GetLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetPasswordLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetPasswordLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
|
||||
GetLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { PasswordLockoutPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { LockoutPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@@ -17,127 +15,124 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-lockout-policy',
|
||||
templateUrl: './password-lockout-policy.component.html',
|
||||
styleUrls: ['./password-lockout-policy.component.scss'],
|
||||
selector: 'app-password-lockout-policy',
|
||||
templateUrl: './password-lockout-policy.component.html',
|
||||
styleUrls: ['./password-lockout-policy.component.scss'],
|
||||
})
|
||||
export class PasswordLockoutPolicyComponent implements OnDestroy {
|
||||
@Input() public service!: ManagementService | AdminService;
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
@Input() public service!: ManagementService | AdminService;
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
|
||||
|
||||
public lockoutForm!: FormGroup;
|
||||
public lockoutData!: PasswordLockoutPolicy.AsObject;
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public lockoutForm!: FormGroup;
|
||||
public lockoutData!: LockoutPolicy.AsObject;
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
this.getData().then(resp => {
|
||||
if (resp.policy) {
|
||||
this.lockoutData = resp.policy;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getData():
|
||||
Promise<AdminGetPasswordLockoutPolicyResponse.AsObject | MgmtGetPasswordLockoutPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getLockoutPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getLockoutPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public resetPolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetLockoutPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
this.fetchData();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public incrementMaxAttempts(): void {
|
||||
if (this.lockoutData?.maxPasswordAttempts !== undefined) {
|
||||
this.lockoutData.maxPasswordAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
public decrementMaxAttempts(): void {
|
||||
if (this.lockoutData?.maxPasswordAttempts && this.lockoutData?.maxPasswordAttempts > 0) {
|
||||
this.lockoutData.maxPasswordAttempts--;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
let promise: Promise<any>;
|
||||
if (this.service instanceof AdminService) {
|
||||
promise = this.service.updateLockoutPolicy(
|
||||
this.lockoutData.maxPasswordAttempts,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
if ((this.lockoutData as LockoutPolicy.AsObject).isDefault) {
|
||||
promise = this.service.addCustomLockoutPolicy(
|
||||
this.lockoutData.maxPasswordAttempts,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private fetchData(): void {
|
||||
this.getData().then(resp => {
|
||||
if (resp.policy) {
|
||||
this.lockoutData = resp.policy;
|
||||
}
|
||||
} else {
|
||||
promise = this.service.updateCustomLockoutPolicy(
|
||||
this.lockoutData.maxPasswordAttempts,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getData():
|
||||
Promise<AdminGetPasswordLockoutPolicyResponse.AsObject | MgmtGetPasswordLockoutPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPasswordLockoutPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPasswordLockoutPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetPasswordLockoutPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
this.fetchData();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public incrementMaxAttempts(): void {
|
||||
if (this.lockoutData?.maxAttempts !== undefined) {
|
||||
this.lockoutData.maxAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
public decrementMaxAttempts(): void {
|
||||
if (this.lockoutData?.maxAttempts && this.lockoutData?.maxAttempts > 0) {
|
||||
this.lockoutData.maxAttempts--;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
let promise: Promise<any>;
|
||||
if (this.service instanceof AdminService) {
|
||||
promise = this.service.updatePasswordLockoutPolicy(
|
||||
this.lockoutData.maxAttempts,
|
||||
this.lockoutData.showLockoutFailure,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
if ((this.lockoutData as PasswordLockoutPolicy.AsObject).isDefault) {
|
||||
promise = this.service.addCustomPasswordLockoutPolicy(
|
||||
this.lockoutData.maxAttempts,
|
||||
this.lockoutData.showLockoutFailure,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
promise = this.service.updateCustomPasswordLockoutPolicy(
|
||||
this.lockoutData.maxAttempts,
|
||||
this.lockoutData.showLockoutFailure,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.lockoutData as PasswordLockoutPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
public get isDefault(): boolean {
|
||||
if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.lockoutData as LockoutPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -9,25 +9,26 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { LinksModule } from '../../links/links.module';
|
||||
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { PasswordLockoutPolicyRoutingModule } from './password-lockout-policy-routing.module';
|
||||
import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PasswordLockoutPolicyComponent],
|
||||
imports: [
|
||||
PasswordLockoutPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
],
|
||||
declarations: [PasswordLockoutPolicyComponent],
|
||||
imports: [
|
||||
PasswordLockoutPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
})
|
||||
export class PasswordLockoutPolicyModule { }
|
||||
|
@@ -25,6 +25,18 @@ export const COMPLEXITY_POLICY: GridPolicy = {
|
||||
color: 'yellow',
|
||||
};
|
||||
|
||||
export const LOCKOUT_POLICY: GridPolicy = {
|
||||
i18nTitle: 'POLICY.PWD_LOCKOUT.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_LOCKOUT.DESCRIPTION',
|
||||
iamRouterLink: ['/iam', 'policy', PolicyComponentType.LOCKOUT],
|
||||
orgRouterLink: ['/org', 'policy', PolicyComponentType.LOCKOUT],
|
||||
iamWithRole: ['iam.policy.read'],
|
||||
orgWithRole: ['policy.read'],
|
||||
tags: ['login', 'security'],
|
||||
icon: 'las la-lock',
|
||||
color: 'yellow',
|
||||
};
|
||||
|
||||
export const IAM_POLICY = {
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
@@ -99,6 +111,7 @@ export const LOGIN_TEXTS_POLICY = {
|
||||
|
||||
export const POLICIES: GridPolicy[] = [
|
||||
COMPLEXITY_POLICY,
|
||||
LOCKOUT_POLICY,
|
||||
IAM_POLICY,
|
||||
LOGIN_POLICY,
|
||||
PRIVATELABEL_POLICY,
|
||||
|
@@ -17,6 +17,7 @@ import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/memb
|
||||
import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { MachineKeysModule } from 'src/app/modules/machine-keys/machine-keys.module';
|
||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||
@@ -104,6 +105,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
LocalizedDatePipeModule,
|
||||
InputModule,
|
||||
MachineKeysModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
})
|
||||
export class UserDetailModule { }
|
||||
|
@@ -14,6 +14,11 @@
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
|
||||
<button class="unlock-button" mat-stroked-button color="warn"
|
||||
*ngIf="user?.state === UserState.USER_STATE_LOCKED"
|
||||
(click)="unlockUser()">{{'USER.PAGES.UNLOCK' |
|
||||
translate}}</button>
|
||||
|
||||
<button class="state-button" mat-stroked-button color="warn"
|
||||
*ngIf="user?.state !== UserState.USER_STATE_INACTIVE"
|
||||
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
|
||||
@@ -26,6 +31,7 @@
|
||||
|
||||
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
|
||||
<cnsl-info-section class="locked" *ngIf="user?.state === UserState.USER_STATE_LOCKED" type="WARN">{{'USER.PAGES.LOCKEDDESCRIPTION' | translate}}</cnsl-info-section>
|
||||
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
|
||||
|
||||
<app-card title="{{ 'USER.PAGES.LOGINNAMES' | translate }}"
|
||||
|
@@ -18,11 +18,20 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.unlock-button {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
.state-button {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.locked {
|
||||
display: block;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.img-phone-email {
|
||||
width: 300px;
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import { take } from 'rxjs/operators';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { SendHumanResetPasswordNotificationRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { SendHumanResetPasswordNotificationRequest, UnlockUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Email, Gender, Machine, Phone, Profile, User, UserState } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@@ -16,281 +16,292 @@ import { EditDialogComponent, EditDialogType } from '../auth-user-detail/edit-di
|
||||
import { ResendEmailDialogComponent } from '../auth-user-detail/resend-email-dialog/resend-email-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-detail',
|
||||
templateUrl: './user-detail.component.html',
|
||||
styleUrls: ['./user-detail.component.scss'],
|
||||
selector: 'app-user-detail',
|
||||
templateUrl: './user-detail.component.html',
|
||||
styleUrls: ['./user-detail.component.scss'],
|
||||
})
|
||||
export class UserDetailComponent implements OnInit {
|
||||
public user!: User.AsObject;
|
||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
public user!: User.AsObject;
|
||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
|
||||
public ChangeType: any = ChangeType;
|
||||
public loading: boolean = false;
|
||||
public ChangeType: any = ChangeType;
|
||||
public loading: boolean = false;
|
||||
|
||||
public UserState: any = UserState;
|
||||
public copied: string = '';
|
||||
public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER;
|
||||
public UserState: any = UserState;
|
||||
public copied: string = '';
|
||||
public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER;
|
||||
|
||||
public EditDialogType: any = EditDialogType;
|
||||
public refreshChanges$: EventEmitter<void> = new EventEmitter();
|
||||
public EditDialogType: any = EditDialogType;
|
||||
public refreshChanges$: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
public mgmtUserService: ManagementService,
|
||||
private _location: Location,
|
||||
private dialog: MatDialog,
|
||||
private router: Router,
|
||||
) { }
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
public mgmtUserService: ManagementService,
|
||||
private _location: Location,
|
||||
private dialog: MatDialog,
|
||||
private router: Router,
|
||||
) { }
|
||||
|
||||
refreshUser(): void {
|
||||
this.refreshChanges$.emit();
|
||||
this.route.params.pipe(take(1)).subscribe(params => {
|
||||
const { id } = params;
|
||||
this.mgmtUserService.getUserByID(id).then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
refreshUser(): void {
|
||||
this.refreshChanges$.emit();
|
||||
this.route.params.pipe(take(1)).subscribe(params => {
|
||||
const { id } = params;
|
||||
this.mgmtUserService.getUserByID(id).then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.refreshUser();
|
||||
}
|
||||
|
||||
public unlockUser(): void {
|
||||
const req = new UnlockUserRequest();
|
||||
req.setId(this.user.id);
|
||||
this.mgmtUserService.unlockUser(req).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.UNLOCKED', true);
|
||||
this.refreshUser();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public changeState(newState: UserState): void {
|
||||
if (newState === UserState.USER_STATE_ACTIVE) {
|
||||
this.mgmtUserService.reactivateUser(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.REACTIVATED', true);
|
||||
this.user.state = newState;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (newState === UserState.USER_STATE_INACTIVE) {
|
||||
this.mgmtUserService.deactivateUser(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.DEACTIVATED', true);
|
||||
this.user.state = newState;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public saveProfile(profileData: Profile.AsObject): void {
|
||||
if (this.user.human) {
|
||||
this.user.human.profile = profileData;
|
||||
this.mgmtUserService
|
||||
.updateHumanProfile(
|
||||
this.user.id,
|
||||
this.user.human.profile.firstName,
|
||||
this.user.human.profile.lastName,
|
||||
this.user.human.profile.nickName,
|
||||
this.user.human.profile.displayName,
|
||||
this.user.human.profile.preferredLanguage,
|
||||
this.user.human.profile.gender)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
public saveMachine(machineData: Machine.AsObject): void {
|
||||
if (this.user.machine) {
|
||||
this.user.machine.name = machineData.name;
|
||||
this.user.machine.description = machineData.description;
|
||||
|
||||
this.mgmtUserService
|
||||
.updateMachine(
|
||||
this.user.id,
|
||||
this.user.machine.name,
|
||||
this.user.machine.description)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public resendEmailVerification(): void {
|
||||
this.mgmtUserService.resendHumanEmailVerification(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public resendPhoneVerification(): void {
|
||||
this.mgmtUserService.resendHumanPhoneVerification(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public deletePhone(): void {
|
||||
this.mgmtUserService.removeHumanPhone(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = new Phone().setPhone('').toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public changeState(newState: UserState): void {
|
||||
if (newState === UserState.USER_STATE_ACTIVE) {
|
||||
this.mgmtUserService.reactivateUser(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.REACTIVATED', true);
|
||||
this.user.state = newState;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (newState === UserState.USER_STATE_INACTIVE) {
|
||||
this.mgmtUserService.deactivateUser(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.DEACTIVATED', true);
|
||||
this.user.state = newState;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
public saveEmail(email: string): void {
|
||||
if (this.user.id && email) {
|
||||
this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
||||
if (this.user.state === UserState.USER_STATE_INITIAL) {
|
||||
this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => {
|
||||
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public saveProfile(profileData: Profile.AsObject): void {
|
||||
if (this.user.human) {
|
||||
this.user.human.profile = profileData;
|
||||
this.mgmtUserService
|
||||
.updateHumanProfile(
|
||||
this.user.id,
|
||||
this.user.human.profile.firstName,
|
||||
this.user.human.profile.lastName,
|
||||
this.user.human.profile.nickName,
|
||||
this.user.human.profile.displayName,
|
||||
this.user.human.profile.preferredLanguage,
|
||||
this.user.human.profile.gender)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.user.human.email = new Email().setEmail(email).toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public saveMachine(machineData: Machine.AsObject): void {
|
||||
if (this.user.machine) {
|
||||
this.user.machine.name = machineData.name;
|
||||
this.user.machine.description = machineData.description;
|
||||
|
||||
this.mgmtUserService
|
||||
.updateMachine(
|
||||
this.user.id,
|
||||
this.user.machine.name,
|
||||
this.user.machine.description)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public resendEmailVerification(): void {
|
||||
this.mgmtUserService.resendHumanEmailVerification(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
public savePhone(phone: string): void {
|
||||
if (this.user.id && phone) {
|
||||
this.mgmtUserService
|
||||
.updateHumanPhone(this.user.id, phone).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = new Phone().setPhone(phone).toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public resendPhoneVerification(): void {
|
||||
this.mgmtUserService.resendHumanPhoneVerification(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
public navigateBack(): void {
|
||||
this._location.back();
|
||||
}
|
||||
|
||||
public sendSetPasswordNotification(): void {
|
||||
this.mgmtUserService.sendHumanResetPasswordNotification(
|
||||
this.user.id,
|
||||
SendHumanResetPasswordNotificationRequest.Type.TYPE_EMAIL,
|
||||
).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PASSWORDNOTIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public deleteUser(): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'USER.DIALOG.DELETE_TITLE',
|
||||
descriptionKey: 'USER.DIALOG.DELETE_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtUserService.removeUser(this.user.id).then(() => {
|
||||
const params: Params = {
|
||||
'deferredReload': true,
|
||||
};
|
||||
this.router.navigate(['/users/list', this.user.human ? 'humans' : 'machines'], { queryParams: params });
|
||||
this.toast.showInfo('USER.TOAST.DELETED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public deletePhone(): void {
|
||||
this.mgmtUserService.removeHumanPhone(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = new Phone().setPhone('').toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
public resendInitEmail(): void {
|
||||
const dialogRef = this.dialog.open(ResendEmailDialogComponent, {
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp.send && this.user.id) {
|
||||
this.mgmtUserService.resendHumanInitialization(this.user.id, resp.email ?? '').then(() => {
|
||||
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public saveEmail(email: string): void {
|
||||
if (this.user.id && email) {
|
||||
this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
||||
if (this.user.state === UserState.USER_STATE_INITIAL) {
|
||||
this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => {
|
||||
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
if (this.user.human) {
|
||||
this.user.human.email = new Email().setEmail(email).toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public savePhone(phone: string): void {
|
||||
if (this.user.id && phone) {
|
||||
this.mgmtUserService
|
||||
.updateHumanPhone(this.user.id, phone).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = new Phone().setPhone(phone).toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public navigateBack(): void {
|
||||
this._location.back();
|
||||
}
|
||||
|
||||
public sendSetPasswordNotification(): void {
|
||||
this.mgmtUserService.sendHumanResetPasswordNotification(
|
||||
this.user.id,
|
||||
SendHumanResetPasswordNotificationRequest.Type.TYPE_EMAIL,
|
||||
).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PASSWORDNOTIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public deleteUser(): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'USER.DIALOG.DELETE_TITLE',
|
||||
descriptionKey: 'USER.DIALOG.DELETE_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
public openEditDialog(type: EditDialogType): void {
|
||||
switch (type) {
|
||||
case EditDialogType.PHONE:
|
||||
const dialogRefPhone = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.PHONE.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC',
|
||||
value: this.user.human?.phone?.phone,
|
||||
type: EditDialogType.PHONE,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtUserService.removeUser(this.user.id).then(() => {
|
||||
const params: Params = {
|
||||
'deferredReload': true,
|
||||
};
|
||||
this.router.navigate(['/users/list', this.user.human ? 'humans' : 'machines'], { queryParams: params });
|
||||
this.toast.showInfo('USER.TOAST.DELETED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
dialogRefPhone.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.savePhone(resp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public resendInitEmail(): void {
|
||||
const dialogRef = this.dialog.open(ResendEmailDialogComponent, {
|
||||
width: '400px',
|
||||
break;
|
||||
case EditDialogType.EMAIL:
|
||||
const dialogRefEmail = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.EMAIL.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.EMAIL.EDITDESC',
|
||||
value: this.user.human?.email?.email,
|
||||
type: EditDialogType.EMAIL,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp.send && this.user.id) {
|
||||
this.mgmtUserService.resendHumanInitialization(this.user.id, resp.email ?? '').then(() => {
|
||||
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
dialogRefEmail.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.saveEmail(resp);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
public openEditDialog(type: EditDialogType): void {
|
||||
switch (type) {
|
||||
case EditDialogType.PHONE:
|
||||
const dialogRefPhone = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.PHONE.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC',
|
||||
value: this.user.human?.phone?.phone,
|
||||
type: EditDialogType.PHONE,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRefPhone.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.savePhone(resp);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case EditDialogType.EMAIL:
|
||||
const dialogRefEmail = this.dialog.open(EditDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.SAVE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
labelKey: 'ACTIONS.NEWVALUE',
|
||||
titleKey: 'USER.LOGINMETHODS.EMAIL.EDITTITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.EMAIL.EDITDESC',
|
||||
value: this.user.human?.email?.email,
|
||||
type: EditDialogType.EMAIL,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRefEmail.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.saveEmail(resp);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -51,6 +51,8 @@ import {
|
||||
GetIDPByIDResponse,
|
||||
GetLabelPolicyRequest,
|
||||
GetLabelPolicyResponse,
|
||||
GetLockoutPolicyRequest,
|
||||
GetLockoutPolicyResponse,
|
||||
GetLoginPolicyRequest,
|
||||
GetLoginPolicyResponse,
|
||||
GetOrgFeaturesRequest,
|
||||
@@ -61,8 +63,6 @@ import {
|
||||
GetPasswordAgePolicyResponse,
|
||||
GetPasswordComplexityPolicyRequest,
|
||||
GetPasswordComplexityPolicyResponse,
|
||||
GetPasswordLockoutPolicyRequest,
|
||||
GetPasswordLockoutPolicyResponse,
|
||||
GetPreviewLabelPolicyRequest,
|
||||
GetPreviewLabelPolicyResponse,
|
||||
GetPrivacyPolicyRequest,
|
||||
@@ -144,6 +144,8 @@ import {
|
||||
UpdateIDPResponse,
|
||||
UpdateLabelPolicyRequest,
|
||||
UpdateLabelPolicyResponse,
|
||||
UpdateLockoutPolicyRequest,
|
||||
UpdateLockoutPolicyResponse,
|
||||
UpdateLoginPolicyRequest,
|
||||
UpdateLoginPolicyResponse,
|
||||
UpdateOrgIAMPolicyRequest,
|
||||
@@ -152,8 +154,6 @@ import {
|
||||
UpdatePasswordAgePolicyResponse,
|
||||
UpdatePasswordComplexityPolicyRequest,
|
||||
UpdatePasswordComplexityPolicyResponse,
|
||||
UpdatePasswordLockoutPolicyRequest,
|
||||
UpdatePasswordLockoutPolicyResponse,
|
||||
UpdatePrivacyPolicyRequest,
|
||||
UpdatePrivacyPolicyResponse,
|
||||
} from '../proto/generated/zitadel/admin_pb';
|
||||
@@ -431,20 +431,18 @@ export class AdminService {
|
||||
|
||||
/* lockout */
|
||||
|
||||
public getPasswordLockoutPolicy(): Promise<GetPasswordLockoutPolicyResponse.AsObject> {
|
||||
const req = new GetPasswordLockoutPolicyRequest();
|
||||
return this.grpcService.admin.getPasswordLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
public getLockoutPolicy(): Promise<GetLockoutPolicyResponse.AsObject> {
|
||||
const req = new GetLockoutPolicyRequest();
|
||||
return this.grpcService.admin.getLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public updatePasswordLockoutPolicy(
|
||||
public updateLockoutPolicy(
|
||||
maxAttempts: number,
|
||||
showLockoutFailures: boolean,
|
||||
): Promise<UpdatePasswordLockoutPolicyResponse.AsObject> {
|
||||
const req = new UpdatePasswordLockoutPolicyRequest();
|
||||
req.setMaxAttempts(maxAttempts);
|
||||
req.setShowLockoutFailure(showLockoutFailures);
|
||||
): Promise<UpdateLockoutPolicyResponse.AsObject> {
|
||||
const req = new UpdateLockoutPolicyRequest();
|
||||
req.setMaxPasswordAttempts(maxAttempts);
|
||||
|
||||
return this.grpcService.admin.updatePasswordLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
return this.grpcService.admin.updateLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
/* label */
|
||||
|
@@ -17,14 +17,14 @@ import {
|
||||
AddAppKeyResponse,
|
||||
AddCustomLabelPolicyRequest,
|
||||
AddCustomLabelPolicyResponse,
|
||||
AddCustomLockoutPolicyRequest,
|
||||
AddCustomLockoutPolicyResponse,
|
||||
AddCustomLoginPolicyRequest,
|
||||
AddCustomLoginPolicyResponse,
|
||||
AddCustomPasswordAgePolicyRequest,
|
||||
AddCustomPasswordAgePolicyResponse,
|
||||
AddCustomPasswordComplexityPolicyRequest,
|
||||
AddCustomPasswordComplexityPolicyResponse,
|
||||
AddCustomPasswordLockoutPolicyRequest,
|
||||
AddCustomPasswordLockoutPolicyResponse,
|
||||
AddCustomPrivacyPolicyRequest,
|
||||
AddCustomPrivacyPolicyResponse,
|
||||
AddHumanUserRequest,
|
||||
@@ -122,6 +122,8 @@ import {
|
||||
GetIAMResponse,
|
||||
GetLabelPolicyRequest,
|
||||
GetLabelPolicyResponse,
|
||||
GetLockoutPolicyRequest,
|
||||
GetLockoutPolicyResponse,
|
||||
GetLoginPolicyRequest,
|
||||
GetLoginPolicyResponse,
|
||||
GetMyOrgRequest,
|
||||
@@ -138,8 +140,6 @@ import {
|
||||
GetPasswordAgePolicyResponse,
|
||||
GetPasswordComplexityPolicyRequest,
|
||||
GetPasswordComplexityPolicyResponse,
|
||||
GetPasswordLockoutPolicyRequest,
|
||||
GetPasswordLockoutPolicyResponse,
|
||||
GetPreviewLabelPolicyRequest,
|
||||
GetPreviewLabelPolicyResponse,
|
||||
GetPrivacyPolicyRequest,
|
||||
@@ -298,14 +298,14 @@ import {
|
||||
ResetCustomVerifyPhoneMessageTextToDefaultResponse,
|
||||
ResetLabelPolicyToDefaultRequest,
|
||||
ResetLabelPolicyToDefaultResponse,
|
||||
ResetLockoutPolicyToDefaultRequest,
|
||||
ResetLockoutPolicyToDefaultResponse,
|
||||
ResetLoginPolicyToDefaultRequest,
|
||||
ResetLoginPolicyToDefaultResponse,
|
||||
ResetPasswordAgePolicyToDefaultRequest,
|
||||
ResetPasswordAgePolicyToDefaultResponse,
|
||||
ResetPasswordComplexityPolicyToDefaultRequest,
|
||||
ResetPasswordComplexityPolicyToDefaultResponse,
|
||||
ResetPasswordLockoutPolicyToDefaultRequest,
|
||||
ResetPasswordLockoutPolicyToDefaultResponse,
|
||||
ResetPrivacyPolicyToDefaultRequest,
|
||||
ResetPrivacyPolicyToDefaultResponse,
|
||||
SendHumanResetPasswordNotificationRequest,
|
||||
@@ -324,20 +324,22 @@ import {
|
||||
SetHumanInitialPasswordRequest,
|
||||
SetPrimaryOrgDomainRequest,
|
||||
SetPrimaryOrgDomainResponse,
|
||||
UnlockUserRequest,
|
||||
UnlockUserResponse,
|
||||
UpdateAPIAppConfigRequest,
|
||||
UpdateAPIAppConfigResponse,
|
||||
UpdateAppRequest,
|
||||
UpdateAppResponse,
|
||||
UpdateCustomLabelPolicyRequest,
|
||||
UpdateCustomLabelPolicyResponse,
|
||||
UpdateCustomLockoutPolicyRequest,
|
||||
UpdateCustomLockoutPolicyResponse,
|
||||
UpdateCustomLoginPolicyRequest,
|
||||
UpdateCustomLoginPolicyResponse,
|
||||
UpdateCustomPasswordAgePolicyRequest,
|
||||
UpdateCustomPasswordAgePolicyResponse,
|
||||
UpdateCustomPasswordComplexityPolicyRequest,
|
||||
UpdateCustomPasswordComplexityPolicyResponse,
|
||||
UpdateCustomPasswordLockoutPolicyRequest,
|
||||
UpdateCustomPasswordLockoutPolicyResponse,
|
||||
UpdateCustomPrivacyPolicyRequest,
|
||||
UpdateCustomPrivacyPolicyResponse,
|
||||
UpdateHumanEmailRequest,
|
||||
@@ -556,6 +558,11 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.listOrgIDPs(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public unlockUser(req: UnlockUserRequest):
|
||||
Promise<UnlockUserResponse.AsObject> {
|
||||
return this.grpcService.mgmt.unlockUser(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public getPrivacyPolicy():
|
||||
Promise<GetPrivacyPolicyResponse.AsObject> {
|
||||
const req = new GetPrivacyPolicyRequest();
|
||||
@@ -1105,36 +1112,31 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.updateCustomPasswordComplexityPolicy(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public getPasswordLockoutPolicy(): Promise<GetPasswordLockoutPolicyResponse.AsObject> {
|
||||
const req = new GetPasswordLockoutPolicyRequest();
|
||||
|
||||
return this.grpcService.mgmt.getPasswordLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
public getLockoutPolicy(): Promise<GetLockoutPolicyResponse.AsObject> {
|
||||
const req = new GetLockoutPolicyRequest();
|
||||
return this.grpcService.mgmt.getLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public addCustomPasswordLockoutPolicy(
|
||||
public addCustomLockoutPolicy(
|
||||
maxAttempts: number,
|
||||
showLockoutFailures: boolean,
|
||||
): Promise<AddCustomPasswordLockoutPolicyResponse.AsObject> {
|
||||
const req = new AddCustomPasswordLockoutPolicyRequest();
|
||||
req.setMaxAttempts(maxAttempts);
|
||||
req.setShowLockoutFailure(showLockoutFailures);
|
||||
): Promise<AddCustomLockoutPolicyResponse.AsObject> {
|
||||
const req = new AddCustomLockoutPolicyRequest();
|
||||
req.setMaxPasswordAttempts(maxAttempts);
|
||||
|
||||
return this.grpcService.mgmt.addCustomPasswordLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
return this.grpcService.mgmt.addCustomLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public resetPasswordLockoutPolicyToDefault(): Promise<ResetPasswordLockoutPolicyToDefaultResponse.AsObject> {
|
||||
const req = new ResetPasswordLockoutPolicyToDefaultRequest();
|
||||
return this.grpcService.mgmt.resetPasswordLockoutPolicyToDefault(req, null).then(resp => resp.toObject());
|
||||
public resetLockoutPolicyToDefault(): Promise<ResetLockoutPolicyToDefaultResponse.AsObject> {
|
||||
const req = new ResetLockoutPolicyToDefaultRequest();
|
||||
return this.grpcService.mgmt.resetLockoutPolicyToDefault(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public updateCustomPasswordLockoutPolicy(
|
||||
public updateCustomLockoutPolicy(
|
||||
maxAttempts: number,
|
||||
showLockoutFailures: boolean,
|
||||
): Promise<UpdateCustomPasswordLockoutPolicyResponse.AsObject> {
|
||||
const req = new UpdateCustomPasswordLockoutPolicyRequest();
|
||||
req.setMaxAttempts(maxAttempts);
|
||||
req.setShowLockoutFailure(showLockoutFailures);
|
||||
return this.grpcService.mgmt.updateCustomPasswordLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
): Promise<UpdateCustomLockoutPolicyResponse.AsObject> {
|
||||
const req = new UpdateCustomLockoutPolicyRequest();
|
||||
req.setMaxPasswordAttempts(maxAttempts);
|
||||
return this.grpcService.mgmt.updateCustomLockoutPolicy(req, null).then(resp => resp.toObject());
|
||||
}
|
||||
|
||||
public getLocalizedComplexityPolicyPatternErrorString(policy: PasswordComplexityPolicy.AsObject): string {
|
||||
|
@@ -174,7 +174,9 @@
|
||||
"REACTIVATE": "Reaktivieren",
|
||||
"DEACTIVATE": "Deaktivieren",
|
||||
"FILTER": "Filter",
|
||||
"DELETE": "Benutzer löschen"
|
||||
"DELETE": "Benutzer löschen",
|
||||
"UNLOCK": "Benutzer entsperren",
|
||||
"LOCKEDDESCRIPTION":"Dieser Benutzer wurde aufgrund der Überschreitung der maximalen Anmeldeversuche gesperrt und muss zur erneuten Verwendung entsperrt werden."
|
||||
},
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE": "User löschen",
|
||||
@@ -421,7 +423,8 @@
|
||||
"STATE": {
|
||||
"0": "Unbekannt",
|
||||
"1": "Aktiv",
|
||||
"2": "Abgelaufen"
|
||||
"2": "Abgelaufen",
|
||||
"4": "Gesperrt"
|
||||
},
|
||||
"SEARCH": {
|
||||
"FOUND": "Gefunden"
|
||||
@@ -460,7 +463,8 @@
|
||||
"SELECTEDKEYSDELETED": "Selektierte Schlüssel gelöscht.",
|
||||
"KEYADDED": "Schlüssel hinzugefügt!",
|
||||
"MACHINEADDED": "Service User erstellt!",
|
||||
"DELETED": "Benutzer erfolgreich gelöscht!"
|
||||
"DELETED": "Benutzer erfolgreich gelöscht!",
|
||||
"UNLOCKED":"Benutzer erfolgreich freigeschaltet!"
|
||||
},
|
||||
"MEMBERSHIPS": {
|
||||
"TITLE": "ZITADEL Manager-Rollen",
|
||||
@@ -688,7 +692,7 @@
|
||||
},
|
||||
"PWD_LOCKOUT": {
|
||||
"TITLE": "Passwortsperre",
|
||||
"DESCRIPTION": "Standardmässig sind die Passwortwiederholungen bei Falscheingabe nicht begrenzt. Du musst diese Richtlinie installieren, wenn Du Wiederholungsversuche anzeigen, oder eine maximale Anzahl von wiederholten Passworteingaben festlegen möchtest."
|
||||
"DESCRIPTION": "Lege eine maximale Anzahl an Passwordwiederholungen fest, nachdem Accounts gesperrt werden sollen."
|
||||
},
|
||||
"IAM_POLICY": {
|
||||
"TITLE": "Zugangseinstellungen IAM",
|
||||
|
@@ -174,7 +174,9 @@
|
||||
"REACTIVATE": "Reactivate",
|
||||
"DEACTIVATE": "Deactivate",
|
||||
"FILTER": "Filter",
|
||||
"DELETE": "Delete User"
|
||||
"DELETE": "Delete User",
|
||||
"UNLOCK": "Unlock User",
|
||||
"LOCKEDDESCRIPTION":"This user has been locked out due to exceeding the maximum login attempts and must be unlocked to be used again."
|
||||
},
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE": "Delete User",
|
||||
@@ -421,7 +423,8 @@
|
||||
"STATE": {
|
||||
"0": "Unknown",
|
||||
"1": "Active",
|
||||
"2": "Expired"
|
||||
"2": "Expired",
|
||||
"4": "Locked"
|
||||
},
|
||||
"SEARCH": {
|
||||
"FOUND": "Found"
|
||||
@@ -460,7 +463,8 @@
|
||||
"SELECTEDKEYSDELETED": "Selected keys deleted.",
|
||||
"KEYADDED": "Key added!",
|
||||
"MACHINEADDED": "Service User created!",
|
||||
"DELETED": "User deleted successfully!"
|
||||
"DELETED": "User deleted successfully!",
|
||||
"UNLOCKED":"User unlocked successfully!"
|
||||
},
|
||||
"MEMBERSHIPS": {
|
||||
"TITLE": "ZITADEL Manager Roles",
|
||||
@@ -688,7 +692,7 @@
|
||||
},
|
||||
"PWD_LOCKOUT": {
|
||||
"TITLE": "Password Lockout",
|
||||
"DESCRIPTION": "Password retries are infinite in default mode. You have to apply this policy if you want to show the number of retries, or set a maximum number of retries after which the account will be blocked."
|
||||
"DESCRIPTION": "Set a maximum number of passwordretries, after which accounts will be blocked."
|
||||
},
|
||||
"IAM_POLICY": {
|
||||
"TITLE": "IAM Access Preferences",
|
||||
|
Reference in New Issue
Block a user