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:
Fabi 2021-08-11 08:36:32 +02:00 committed by GitHub
parent 272e411e27
commit bc951985ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
101 changed files with 2170 additions and 1574 deletions

View File

@ -202,7 +202,7 @@ func startAPI(ctx context.Context, conf *Config, verifier *internal_authz.TokenV
for i, role := range conf.InternalAuthZ.RolePermissionMappings { for i, role := range conf.InternalAuthZ.RolePermissionMappings {
roles[i] = role.Role roles[i] = role.Role
} }
repo, err := admin_es.Start(ctx, conf.Admin, conf.SystemDefaults, static, roles, *localDevMode) repo, err := admin_es.Start(ctx, conf.Admin, conf.SystemDefaults, command, static, roles, *localDevMode)
logging.Log("API-D42tq").OnError(err).Fatal("error starting auth repo") logging.Log("API-D42tq").OnError(err).Fatal("error starting auth repo")
apis := api.Create(conf.API, conf.InternalAuthZ, authZRepo, authRepo, repo, conf.SystemDefaults) apis := api.Create(conf.API, conf.InternalAuthZ, authZRepo, authRepo, repo, conf.SystemDefaults)

View File

@ -74,7 +74,7 @@ SetUp:
ExpireWarnDays: 0 ExpireWarnDays: 0
Step4: Step4:
DefaultPasswordLockoutPolicy: DefaultPasswordLockoutPolicy:
MaxAttempts: 5 MaxPasswordAttempts: 5
ShowLockOutFailures: false ShowLockOutFailures: false
Step5: Step5:
DefaultOrgIAMPolicy: DefaultOrgIAMPolicy:
@ -192,4 +192,8 @@ SetUp:
Step17: Step17:
PrivacyPolicy: PrivacyPolicy:
TOSLink: https://docs.zitadel.ch/docs/legal/terms-of-service TOSLink: https://docs.zitadel.ch/docs/legal/terms-of-service
PrivacyLink: https://docs.zitadel.ch/docs/legal/privacy-policy PrivacyLink: https://docs.zitadel.ch/docs/legal/privacy-policy
Step18:
LockoutPolicy:
MaxPasswordAttempts: 0
ShowLockOutFailures: true

View File

@ -35,6 +35,7 @@
"libphonenumber-js": "^1.9.16", "libphonenumber-js": "^1.9.16",
"moment": "^2.29.1", "moment": "^2.29.1",
"ngx-color": "^7.2.0", "ngx-color": "^7.2.0",
"ngx-image-cropper": "^3.3.5",
"ngx-quicklink": "^0.2.6", "ngx-quicklink": "^0.2.6",
"rxjs": "~6.6.7", "rxjs": "~6.6.7",
"tinycolor2": "^1.4.2", "tinycolor2": "^1.4.2",
@ -10368,6 +10369,24 @@
"@angular/core": ">=12.0.0-0" "@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": { "node_modules/ngx-quicklink": {
"version": "0.2.7", "version": "0.2.7",
"resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz", "resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz",
@ -27536,6 +27555,21 @@
"tslib": "^2.1.0" "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": { "ngx-quicklink": {
"version": "0.2.7", "version": "0.2.7",
"resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz", "resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz",

View File

@ -1,10 +1,10 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']" <app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.PWD_LOCKOUT.TITLE' | translate" [description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate"> [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']"> <ng-template appHasRole [appHasRole]="['policy.delete']">
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault" <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}} {{'POLICY.RESET' | translate}}
</button> </button>
</ng-template> </ng-template>
@ -14,22 +14,15 @@
<span class="left-desc">{{'POLICY.DATA.MAXATTEMPTS' | translate}}</span> <span class="left-desc">{{'POLICY.DATA.MAXATTEMPTS' | translate}}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="length-wrapper"> <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()"> <button mat-icon-button (click)="decrementMaxAttempts()">
<mat-icon>remove</mat-icon> <mat-icon>remove</mat-icon>
</button> </button>
<span>{{lockoutData?.maxPasswordAttempts}}</span>
<button mat-icon-button (click)="incrementMaxAttempts()">
<mat-icon>add</mat-icon>
</button>
</div> </div>
</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>
<div class="btn-container"> <div class="btn-container">

View File

@ -1,6 +1,6 @@
.default { .default {
color: var(--color-main); display: block;
margin-top: 0; margin-bottom: 1rem;
} }
.content { .content {

View File

@ -3,13 +3,11 @@ import { FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { GetLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { import {
GetPasswordLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse, GetLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetPasswordLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
} from 'src/app/proto/generated/zitadel/management_pb'; } 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 { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.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'; import { PolicyComponentServiceType } from '../policy-component-types.enum';
@Component({ @Component({
selector: 'app-password-lockout-policy', selector: 'app-password-lockout-policy',
templateUrl: './password-lockout-policy.component.html', templateUrl: './password-lockout-policy.component.html',
styleUrls: ['./password-lockout-policy.component.scss'], styleUrls: ['./password-lockout-policy.component.scss'],
}) })
export class PasswordLockoutPolicyComponent implements OnDestroy { export class PasswordLockoutPolicyComponent implements OnDestroy {
@Input() public service!: ManagementService | AdminService; @Input() public service!: ManagementService | AdminService;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public lockoutForm!: FormGroup; public lockoutForm!: FormGroup;
public lockoutData!: PasswordLockoutPolicy.AsObject; public lockoutData!: LockoutPolicy.AsObject;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private toast: ToastService, private toast: ToastService,
private injector: Injector, private injector: Injector,
) { ) {
this.sub = this.route.data.pipe(switchMap(data => { this.sub = this.route.data.pipe(switchMap(data => {
this.serviceType = data.serviceType; this.serviceType = data.serviceType;
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>); this.service = this.injector.get(ManagementService as Type<ManagementService>);
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>); this.service = this.injector.get(AdminService as Type<AdminService>);
break; break;
} }
return this.route.params; return this.route.params;
})).subscribe(() => { })).subscribe(() => {
this.fetchData(); 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);
}); });
} } else {
promise = this.service.updateCustomLockoutPolicy(
public ngOnDestroy(): void { this.lockoutData.maxPasswordAttempts,
this.sub.unsubscribe(); ).then(() => {
} this.toast.showInfo('POLICY.TOAST.SET', true);
}).catch(error => {
private fetchData(): void { this.toast.showError(error);
this.getData().then(resp => {
if (resp.policy) {
this.lockoutData = resp.policy;
}
}); });
}
} }
}
private getData(): public get isDefault(): boolean {
Promise<AdminGetPasswordLockoutPolicyResponse.AsObject | MgmtGetPasswordLockoutPolicyResponse.AsObject> { if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) {
switch (this.serviceType) { return (this.lockoutData as LockoutPolicy.AsObject).isDefault;
case PolicyComponentServiceType.MGMT: } else {
return (this.service as ManagementService).getPasswordLockoutPolicy(); return false;
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;
}
} }
}
} }

View File

@ -9,25 +9,26 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module'; import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.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 { PasswordLockoutPolicyRoutingModule } from './password-lockout-policy-routing.module';
import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component'; import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component';
@NgModule({ @NgModule({
declarations: [PasswordLockoutPolicyComponent], declarations: [PasswordLockoutPolicyComponent],
imports: [ imports: [
PasswordLockoutPolicyRoutingModule, PasswordLockoutPolicyRoutingModule,
CommonModule, CommonModule,
FormsModule, FormsModule,
InputModule, InputModule,
MatButtonModule, MatButtonModule,
MatSlideToggleModule, MatSlideToggleModule,
MatIconModule, MatIconModule,
HasRoleModule, HasRoleModule,
MatTooltipModule, MatTooltipModule,
TranslateModule, TranslateModule,
DetailLayoutModule, DetailLayoutModule,
], InfoSectionModule,
],
}) })
export class PasswordLockoutPolicyModule { } export class PasswordLockoutPolicyModule { }

View File

@ -25,6 +25,18 @@ export const COMPLEXITY_POLICY: GridPolicy = {
color: 'yellow', 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 = { export const IAM_POLICY = {
i18nTitle: 'POLICY.IAM_POLICY.TITLE', i18nTitle: 'POLICY.IAM_POLICY.TITLE',
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION', i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
@ -99,6 +111,7 @@ export const LOGIN_TEXTS_POLICY = {
export const POLICIES: GridPolicy[] = [ export const POLICIES: GridPolicy[] = [
COMPLEXITY_POLICY, COMPLEXITY_POLICY,
LOCKOUT_POLICY,
IAM_POLICY, IAM_POLICY,
LOGIN_POLICY, LOGIN_POLICY,
PRIVATELABEL_POLICY, PRIVATELABEL_POLICY,

View File

@ -17,6 +17,7 @@ import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/memb
import { CardModule } from 'src/app/modules/card/card.module'; import { CardModule } from 'src/app/modules/card/card.module';
import { ChangesModule } from 'src/app/modules/changes/changes.module'; import { ChangesModule } from 'src/app/modules/changes/changes.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.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 { InputModule } from 'src/app/modules/input/input.module';
import { MachineKeysModule } from 'src/app/modules/machine-keys/machine-keys.module'; import { MachineKeysModule } from 'src/app/modules/machine-keys/machine-keys.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.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, LocalizedDatePipeModule,
InputModule, InputModule,
MachineKeysModule, MachineKeysModule,
InfoSectionModule,
], ],
}) })
export class UserDetailModule { } export class UserDetailModule { }

View File

@ -14,6 +14,11 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]"> <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" <button class="state-button" mat-stroked-button color="warn"
*ngIf="user?.state !== UserState.USER_STATE_INACTIVE" *ngIf="user?.state !== UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' | (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> <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> <span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
<app-card title="{{ 'USER.PAGES.LOGINNAMES' | translate }}" <app-card title="{{ 'USER.PAGES.LOGINNAMES' | translate }}"

View File

@ -18,11 +18,20 @@
flex: 1; flex: 1;
} }
.unlock-button {
margin-left: .5rem;
}
.state-button { .state-button {
margin-left: .5rem; margin-left: .5rem;
} }
} }
.locked {
display: block;
margin: 1rem 0;
}
.img-phone-email { .img-phone-email {
width: 300px; width: 300px;
} }

View File

@ -7,7 +7,7 @@ import { take } from 'rxjs/operators';
import { ChangeType } from 'src/app/modules/changes/changes.component'; import { ChangeType } from 'src/app/modules/changes/changes.component';
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; 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 { Email, Gender, Machine, Phone, Profile, User, UserState } from 'src/app/proto/generated/zitadel/user_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.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'; import { ResendEmailDialogComponent } from '../auth-user-detail/resend-email-dialog/resend-email-dialog.component';
@Component({ @Component({
selector: 'app-user-detail', selector: 'app-user-detail',
templateUrl: './user-detail.component.html', templateUrl: './user-detail.component.html',
styleUrls: ['./user-detail.component.scss'], styleUrls: ['./user-detail.component.scss'],
}) })
export class UserDetailComponent implements OnInit { export class UserDetailComponent implements OnInit {
public user!: User.AsObject; public user!: User.AsObject;
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE]; public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
public languages: string[] = ['de', 'en']; public languages: string[] = ['de', 'en'];
public ChangeType: any = ChangeType; public ChangeType: any = ChangeType;
public loading: boolean = false; public loading: boolean = false;
public UserState: any = UserState; public UserState: any = UserState;
public copied: string = ''; public copied: string = '';
public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER; public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER;
public EditDialogType: any = EditDialogType; public EditDialogType: any = EditDialogType;
public refreshChanges$: EventEmitter<void> = new EventEmitter(); public refreshChanges$: EventEmitter<void> = new EventEmitter();
constructor( constructor(
public translate: TranslateService, public translate: TranslateService,
private route: ActivatedRoute, private route: ActivatedRoute,
private toast: ToastService, private toast: ToastService,
public mgmtUserService: ManagementService, public mgmtUserService: ManagementService,
private _location: Location, private _location: Location,
private dialog: MatDialog, private dialog: MatDialog,
private router: Router, private router: Router,
) { } ) { }
refreshUser(): void { refreshUser(): void {
this.refreshChanges$.emit(); this.refreshChanges$.emit();
this.route.params.pipe(take(1)).subscribe(params => { this.route.params.pipe(take(1)).subscribe(params => {
const { id } = params; const { id } = params;
this.mgmtUserService.getUserByID(id).then(resp => { this.mgmtUserService.getUserByID(id).then(resp => {
if (resp.user) { if (resp.user) {
this.user = resp.user; this.user = resp.user;
} }
}).catch(err => { }).catch(err => {
console.error(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(); this.refreshUser();
} }
}).catch(error => {
this.toast.showError(error);
});
}
public changeState(newState: UserState): void { public saveEmail(email: string): void {
if (newState === UserState.USER_STATE_ACTIVE) { if (this.user.id && email) {
this.mgmtUserService.reactivateUser(this.user.id).then(() => { this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => {
this.toast.showInfo('USER.TOAST.REACTIVATED', true); this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
this.user.state = newState; if (this.user.state === UserState.USER_STATE_INITIAL) {
}).catch(error => { this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => {
this.toast.showError(error); this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
}); this.refreshChanges$.emit();
} else if (newState === UserState.USER_STATE_INACTIVE) { }).catch(error => {
this.mgmtUserService.deactivateUser(this.user.id).then(() => { this.toast.showError(error);
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) { if (this.user.human) {
this.user.human.profile = profileData; this.user.human.email = new Email().setEmail(email).toObject();
this.mgmtUserService this.refreshUser();
.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);
});
} }
}).catch(error => {
this.toast.showError(error);
});
} }
}
public saveMachine(machineData: Machine.AsObject): void { public savePhone(phone: string): void {
if (this.user.machine) { if (this.user.id && phone) {
this.user.machine.name = machineData.name; this.mgmtUserService
this.user.machine.description = machineData.description; .updateHumanPhone(this.user.id, phone).then(() => {
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
this.mgmtUserService if (this.user.human) {
.updateMachine( this.user.human.phone = new Phone().setPhone(phone).toObject();
this.user.id, this.refreshUser();
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 => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}
public resendPhoneVerification(): void { public navigateBack(): void {
this.mgmtUserService.resendHumanPhoneVerification(this.user.id).then(() => { this._location.back();
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true); }
this.refreshChanges$.emit();
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 => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
});
}
public deletePhone(): void { public resendInitEmail(): void {
this.mgmtUserService.removeHumanPhone(this.user.id).then(() => { const dialogRef = this.dialog.open(ResendEmailDialogComponent, {
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true); width: '400px',
if (this.user.human) { });
this.user.human.phone = new Phone().setPhone('').toObject();
this.refreshUser(); 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 => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
});
}
public saveEmail(email: string): void { public openEditDialog(type: EditDialogType): void {
if (this.user.id && email) { switch (type) {
this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => { case EditDialogType.PHONE:
this.toast.showInfo('USER.TOAST.EMAILSAVED', true); const dialogRefPhone = this.dialog.open(EditDialogComponent, {
if (this.user.state === UserState.USER_STATE_INITIAL) { data: {
this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => { confirmKey: 'ACTIONS.SAVE',
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true); cancelKey: 'ACTIONS.CANCEL',
this.refreshChanges$.emit(); labelKey: 'ACTIONS.NEWVALUE',
}).catch(error => { titleKey: 'USER.LOGINMETHODS.PHONE.EDITTITLE',
this.toast.showError(error); descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC',
}); value: this.user.human?.phone?.phone,
} type: EditDialogType.PHONE,
if (this.user.human) { },
this.user.human.email = new Email().setEmail(email).toObject(); width: '400px',
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',
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRefPhone.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.mgmtUserService.removeUser(this.user.id).then(() => { this.savePhone(resp);
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);
});
}
}); });
} break;
case EditDialogType.EMAIL:
public resendInitEmail(): void { const dialogRefEmail = this.dialog.open(EditDialogComponent, {
const dialogRef = this.dialog.open(ResendEmailDialogComponent, { data: {
width: '400px', 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 => { dialogRefEmail.afterClosed().subscribe(resp => {
if (resp.send && this.user.id) { if (resp) {
this.mgmtUserService.resendHumanInitialization(this.user.id, resp.email ?? '').then(() => { this.saveEmail(resp);
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true); }
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
}
}); });
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;
}
}
} }

View File

@ -51,6 +51,8 @@ import {
GetIDPByIDResponse, GetIDPByIDResponse,
GetLabelPolicyRequest, GetLabelPolicyRequest,
GetLabelPolicyResponse, GetLabelPolicyResponse,
GetLockoutPolicyRequest,
GetLockoutPolicyResponse,
GetLoginPolicyRequest, GetLoginPolicyRequest,
GetLoginPolicyResponse, GetLoginPolicyResponse,
GetOrgFeaturesRequest, GetOrgFeaturesRequest,
@ -61,8 +63,6 @@ import {
GetPasswordAgePolicyResponse, GetPasswordAgePolicyResponse,
GetPasswordComplexityPolicyRequest, GetPasswordComplexityPolicyRequest,
GetPasswordComplexityPolicyResponse, GetPasswordComplexityPolicyResponse,
GetPasswordLockoutPolicyRequest,
GetPasswordLockoutPolicyResponse,
GetPreviewLabelPolicyRequest, GetPreviewLabelPolicyRequest,
GetPreviewLabelPolicyResponse, GetPreviewLabelPolicyResponse,
GetPrivacyPolicyRequest, GetPrivacyPolicyRequest,
@ -144,6 +144,8 @@ import {
UpdateIDPResponse, UpdateIDPResponse,
UpdateLabelPolicyRequest, UpdateLabelPolicyRequest,
UpdateLabelPolicyResponse, UpdateLabelPolicyResponse,
UpdateLockoutPolicyRequest,
UpdateLockoutPolicyResponse,
UpdateLoginPolicyRequest, UpdateLoginPolicyRequest,
UpdateLoginPolicyResponse, UpdateLoginPolicyResponse,
UpdateOrgIAMPolicyRequest, UpdateOrgIAMPolicyRequest,
@ -152,8 +154,6 @@ import {
UpdatePasswordAgePolicyResponse, UpdatePasswordAgePolicyResponse,
UpdatePasswordComplexityPolicyRequest, UpdatePasswordComplexityPolicyRequest,
UpdatePasswordComplexityPolicyResponse, UpdatePasswordComplexityPolicyResponse,
UpdatePasswordLockoutPolicyRequest,
UpdatePasswordLockoutPolicyResponse,
UpdatePrivacyPolicyRequest, UpdatePrivacyPolicyRequest,
UpdatePrivacyPolicyResponse, UpdatePrivacyPolicyResponse,
} from '../proto/generated/zitadel/admin_pb'; } from '../proto/generated/zitadel/admin_pb';
@ -431,20 +431,18 @@ export class AdminService {
/* lockout */ /* lockout */
public getPasswordLockoutPolicy(): Promise<GetPasswordLockoutPolicyResponse.AsObject> { public getLockoutPolicy(): Promise<GetLockoutPolicyResponse.AsObject> {
const req = new GetPasswordLockoutPolicyRequest(); const req = new GetLockoutPolicyRequest();
return this.grpcService.admin.getPasswordLockoutPolicy(req, null).then(resp => resp.toObject()); return this.grpcService.admin.getLockoutPolicy(req, null).then(resp => resp.toObject());
} }
public updatePasswordLockoutPolicy( public updateLockoutPolicy(
maxAttempts: number, maxAttempts: number,
showLockoutFailures: boolean, ): Promise<UpdateLockoutPolicyResponse.AsObject> {
): Promise<UpdatePasswordLockoutPolicyResponse.AsObject> { const req = new UpdateLockoutPolicyRequest();
const req = new UpdatePasswordLockoutPolicyRequest(); req.setMaxPasswordAttempts(maxAttempts);
req.setMaxAttempts(maxAttempts);
req.setShowLockoutFailure(showLockoutFailures);
return this.grpcService.admin.updatePasswordLockoutPolicy(req, null).then(resp => resp.toObject()); return this.grpcService.admin.updateLockoutPolicy(req, null).then(resp => resp.toObject());
} }
/* label */ /* label */

View File

@ -17,14 +17,14 @@ import {
AddAppKeyResponse, AddAppKeyResponse,
AddCustomLabelPolicyRequest, AddCustomLabelPolicyRequest,
AddCustomLabelPolicyResponse, AddCustomLabelPolicyResponse,
AddCustomLockoutPolicyRequest,
AddCustomLockoutPolicyResponse,
AddCustomLoginPolicyRequest, AddCustomLoginPolicyRequest,
AddCustomLoginPolicyResponse, AddCustomLoginPolicyResponse,
AddCustomPasswordAgePolicyRequest, AddCustomPasswordAgePolicyRequest,
AddCustomPasswordAgePolicyResponse, AddCustomPasswordAgePolicyResponse,
AddCustomPasswordComplexityPolicyRequest, AddCustomPasswordComplexityPolicyRequest,
AddCustomPasswordComplexityPolicyResponse, AddCustomPasswordComplexityPolicyResponse,
AddCustomPasswordLockoutPolicyRequest,
AddCustomPasswordLockoutPolicyResponse,
AddCustomPrivacyPolicyRequest, AddCustomPrivacyPolicyRequest,
AddCustomPrivacyPolicyResponse, AddCustomPrivacyPolicyResponse,
AddHumanUserRequest, AddHumanUserRequest,
@ -122,6 +122,8 @@ import {
GetIAMResponse, GetIAMResponse,
GetLabelPolicyRequest, GetLabelPolicyRequest,
GetLabelPolicyResponse, GetLabelPolicyResponse,
GetLockoutPolicyRequest,
GetLockoutPolicyResponse,
GetLoginPolicyRequest, GetLoginPolicyRequest,
GetLoginPolicyResponse, GetLoginPolicyResponse,
GetMyOrgRequest, GetMyOrgRequest,
@ -138,8 +140,6 @@ import {
GetPasswordAgePolicyResponse, GetPasswordAgePolicyResponse,
GetPasswordComplexityPolicyRequest, GetPasswordComplexityPolicyRequest,
GetPasswordComplexityPolicyResponse, GetPasswordComplexityPolicyResponse,
GetPasswordLockoutPolicyRequest,
GetPasswordLockoutPolicyResponse,
GetPreviewLabelPolicyRequest, GetPreviewLabelPolicyRequest,
GetPreviewLabelPolicyResponse, GetPreviewLabelPolicyResponse,
GetPrivacyPolicyRequest, GetPrivacyPolicyRequest,
@ -298,14 +298,14 @@ import {
ResetCustomVerifyPhoneMessageTextToDefaultResponse, ResetCustomVerifyPhoneMessageTextToDefaultResponse,
ResetLabelPolicyToDefaultRequest, ResetLabelPolicyToDefaultRequest,
ResetLabelPolicyToDefaultResponse, ResetLabelPolicyToDefaultResponse,
ResetLockoutPolicyToDefaultRequest,
ResetLockoutPolicyToDefaultResponse,
ResetLoginPolicyToDefaultRequest, ResetLoginPolicyToDefaultRequest,
ResetLoginPolicyToDefaultResponse, ResetLoginPolicyToDefaultResponse,
ResetPasswordAgePolicyToDefaultRequest, ResetPasswordAgePolicyToDefaultRequest,
ResetPasswordAgePolicyToDefaultResponse, ResetPasswordAgePolicyToDefaultResponse,
ResetPasswordComplexityPolicyToDefaultRequest, ResetPasswordComplexityPolicyToDefaultRequest,
ResetPasswordComplexityPolicyToDefaultResponse, ResetPasswordComplexityPolicyToDefaultResponse,
ResetPasswordLockoutPolicyToDefaultRequest,
ResetPasswordLockoutPolicyToDefaultResponse,
ResetPrivacyPolicyToDefaultRequest, ResetPrivacyPolicyToDefaultRequest,
ResetPrivacyPolicyToDefaultResponse, ResetPrivacyPolicyToDefaultResponse,
SendHumanResetPasswordNotificationRequest, SendHumanResetPasswordNotificationRequest,
@ -324,20 +324,22 @@ import {
SetHumanInitialPasswordRequest, SetHumanInitialPasswordRequest,
SetPrimaryOrgDomainRequest, SetPrimaryOrgDomainRequest,
SetPrimaryOrgDomainResponse, SetPrimaryOrgDomainResponse,
UnlockUserRequest,
UnlockUserResponse,
UpdateAPIAppConfigRequest, UpdateAPIAppConfigRequest,
UpdateAPIAppConfigResponse, UpdateAPIAppConfigResponse,
UpdateAppRequest, UpdateAppRequest,
UpdateAppResponse, UpdateAppResponse,
UpdateCustomLabelPolicyRequest, UpdateCustomLabelPolicyRequest,
UpdateCustomLabelPolicyResponse, UpdateCustomLabelPolicyResponse,
UpdateCustomLockoutPolicyRequest,
UpdateCustomLockoutPolicyResponse,
UpdateCustomLoginPolicyRequest, UpdateCustomLoginPolicyRequest,
UpdateCustomLoginPolicyResponse, UpdateCustomLoginPolicyResponse,
UpdateCustomPasswordAgePolicyRequest, UpdateCustomPasswordAgePolicyRequest,
UpdateCustomPasswordAgePolicyResponse, UpdateCustomPasswordAgePolicyResponse,
UpdateCustomPasswordComplexityPolicyRequest, UpdateCustomPasswordComplexityPolicyRequest,
UpdateCustomPasswordComplexityPolicyResponse, UpdateCustomPasswordComplexityPolicyResponse,
UpdateCustomPasswordLockoutPolicyRequest,
UpdateCustomPasswordLockoutPolicyResponse,
UpdateCustomPrivacyPolicyRequest, UpdateCustomPrivacyPolicyRequest,
UpdateCustomPrivacyPolicyResponse, UpdateCustomPrivacyPolicyResponse,
UpdateHumanEmailRequest, UpdateHumanEmailRequest,
@ -556,6 +558,11 @@ export class ManagementService {
return this.grpcService.mgmt.listOrgIDPs(req, null).then(resp => resp.toObject()); 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(): public getPrivacyPolicy():
Promise<GetPrivacyPolicyResponse.AsObject> { Promise<GetPrivacyPolicyResponse.AsObject> {
const req = new GetPrivacyPolicyRequest(); const req = new GetPrivacyPolicyRequest();
@ -1105,36 +1112,31 @@ export class ManagementService {
return this.grpcService.mgmt.updateCustomPasswordComplexityPolicy(req, null).then(resp => resp.toObject()); return this.grpcService.mgmt.updateCustomPasswordComplexityPolicy(req, null).then(resp => resp.toObject());
} }
public getPasswordLockoutPolicy(): Promise<GetPasswordLockoutPolicyResponse.AsObject> { public getLockoutPolicy(): Promise<GetLockoutPolicyResponse.AsObject> {
const req = new GetPasswordLockoutPolicyRequest(); const req = new GetLockoutPolicyRequest();
return this.grpcService.mgmt.getLockoutPolicy(req, null).then(resp => resp.toObject());
return this.grpcService.mgmt.getPasswordLockoutPolicy(req, null).then(resp => resp.toObject());
} }
public addCustomPasswordLockoutPolicy( public addCustomLockoutPolicy(
maxAttempts: number, maxAttempts: number,
showLockoutFailures: boolean, ): Promise<AddCustomLockoutPolicyResponse.AsObject> {
): Promise<AddCustomPasswordLockoutPolicyResponse.AsObject> { const req = new AddCustomLockoutPolicyRequest();
const req = new AddCustomPasswordLockoutPolicyRequest(); req.setMaxPasswordAttempts(maxAttempts);
req.setMaxAttempts(maxAttempts);
req.setShowLockoutFailure(showLockoutFailures);
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> { public resetLockoutPolicyToDefault(): Promise<ResetLockoutPolicyToDefaultResponse.AsObject> {
const req = new ResetPasswordLockoutPolicyToDefaultRequest(); const req = new ResetLockoutPolicyToDefaultRequest();
return this.grpcService.mgmt.resetPasswordLockoutPolicyToDefault(req, null).then(resp => resp.toObject()); return this.grpcService.mgmt.resetLockoutPolicyToDefault(req, null).then(resp => resp.toObject());
} }
public updateCustomPasswordLockoutPolicy( public updateCustomLockoutPolicy(
maxAttempts: number, maxAttempts: number,
showLockoutFailures: boolean, ): Promise<UpdateCustomLockoutPolicyResponse.AsObject> {
): Promise<UpdateCustomPasswordLockoutPolicyResponse.AsObject> { const req = new UpdateCustomLockoutPolicyRequest();
const req = new UpdateCustomPasswordLockoutPolicyRequest(); req.setMaxPasswordAttempts(maxAttempts);
req.setMaxAttempts(maxAttempts); return this.grpcService.mgmt.updateCustomLockoutPolicy(req, null).then(resp => resp.toObject());
req.setShowLockoutFailure(showLockoutFailures);
return this.grpcService.mgmt.updateCustomPasswordLockoutPolicy(req, null).then(resp => resp.toObject());
} }
public getLocalizedComplexityPolicyPatternErrorString(policy: PasswordComplexityPolicy.AsObject): string { public getLocalizedComplexityPolicyPatternErrorString(policy: PasswordComplexityPolicy.AsObject): string {

View File

@ -174,7 +174,9 @@
"REACTIVATE": "Reaktivieren", "REACTIVATE": "Reaktivieren",
"DEACTIVATE": "Deaktivieren", "DEACTIVATE": "Deaktivieren",
"FILTER": "Filter", "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": { "DIALOG": {
"DELETE_TITLE": "User löschen", "DELETE_TITLE": "User löschen",
@ -421,7 +423,8 @@
"STATE": { "STATE": {
"0": "Unbekannt", "0": "Unbekannt",
"1": "Aktiv", "1": "Aktiv",
"2": "Abgelaufen" "2": "Abgelaufen",
"4": "Gesperrt"
}, },
"SEARCH": { "SEARCH": {
"FOUND": "Gefunden" "FOUND": "Gefunden"
@ -460,7 +463,8 @@
"SELECTEDKEYSDELETED": "Selektierte Schlüssel gelöscht.", "SELECTEDKEYSDELETED": "Selektierte Schlüssel gelöscht.",
"KEYADDED": "Schlüssel hinzugefügt!", "KEYADDED": "Schlüssel hinzugefügt!",
"MACHINEADDED": "Service User erstellt!", "MACHINEADDED": "Service User erstellt!",
"DELETED": "Benutzer erfolgreich gelöscht!" "DELETED": "Benutzer erfolgreich gelöscht!",
"UNLOCKED":"Benutzer erfolgreich freigeschaltet!"
}, },
"MEMBERSHIPS": { "MEMBERSHIPS": {
"TITLE": "ZITADEL Manager-Rollen", "TITLE": "ZITADEL Manager-Rollen",
@ -688,7 +692,7 @@
}, },
"PWD_LOCKOUT": { "PWD_LOCKOUT": {
"TITLE": "Passwortsperre", "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": { "IAM_POLICY": {
"TITLE": "Zugangseinstellungen IAM", "TITLE": "Zugangseinstellungen IAM",

View File

@ -174,7 +174,9 @@
"REACTIVATE": "Reactivate", "REACTIVATE": "Reactivate",
"DEACTIVATE": "Deactivate", "DEACTIVATE": "Deactivate",
"FILTER": "Filter", "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": { "DIALOG": {
"DELETE_TITLE": "Delete User", "DELETE_TITLE": "Delete User",
@ -421,7 +423,8 @@
"STATE": { "STATE": {
"0": "Unknown", "0": "Unknown",
"1": "Active", "1": "Active",
"2": "Expired" "2": "Expired",
"4": "Locked"
}, },
"SEARCH": { "SEARCH": {
"FOUND": "Found" "FOUND": "Found"
@ -460,7 +463,8 @@
"SELECTEDKEYSDELETED": "Selected keys deleted.", "SELECTEDKEYSDELETED": "Selected keys deleted.",
"KEYADDED": "Key added!", "KEYADDED": "Key added!",
"MACHINEADDED": "Service User created!", "MACHINEADDED": "Service User created!",
"DELETED": "User deleted successfully!" "DELETED": "User deleted successfully!",
"UNLOCKED":"User unlocked successfully!"
}, },
"MEMBERSHIPS": { "MEMBERSHIPS": {
"TITLE": "ZITADEL Manager Roles", "TITLE": "ZITADEL Manager Roles",
@ -688,7 +692,7 @@
}, },
"PWD_LOCKOUT": { "PWD_LOCKOUT": {
"TITLE": "Password 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": { "IAM_POLICY": {
"TITLE": "IAM Access Preferences", "TITLE": "IAM Access Preferences",

View File

@ -615,24 +615,24 @@ it impacts all organisations without a customised policy
PUT: /policies/password/age PUT: /policies/password/age
### GetPasswordLockoutPolicy ### GetLockoutPolicy
> **rpc** GetPasswordLockoutPolicy([GetPasswordLockoutPolicyRequest](#getpasswordlockoutpolicyrequest)) > **rpc** GetLockoutPolicy([GetLockoutPolicyRequest](#getlockoutpolicyrequest))
[GetPasswordLockoutPolicyResponse](#getpasswordlockoutpolicyresponse) [GetLockoutPolicyResponse](#getlockoutpolicyresponse)
Returns the password lockout policy defined by the administrators of ZITADEL Returns the lockout policy defined by the administrators of ZITADEL
GET: /policies/password/lockout GET: /policies/lockout
### UpdatePasswordLockoutPolicy ### UpdateLockoutPolicy
> **rpc** UpdatePasswordLockoutPolicy([UpdatePasswordLockoutPolicyRequest](#updatepasswordlockoutpolicyrequest)) > **rpc** UpdateLockoutPolicy([UpdateLockoutPolicyRequest](#updatelockoutpolicyrequest))
[UpdatePasswordLockoutPolicyResponse](#updatepasswordlockoutpolicyresponse) [UpdateLockoutPolicyResponse](#updatelockoutpolicyresponse)
Updates the default password lockout policy of ZITADEL Updates the default lockout policy of ZITADEL
it impacts all organisations without a customised policy it impacts all organisations without a customised policy
@ -1681,6 +1681,23 @@ This is an empty request
### GetLockoutPolicyRequest
This is an empty request
### GetLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| policy | zitadel.policy.v1.LockoutPolicy | - | |
### GetLoginPolicyRequest ### GetLoginPolicyRequest
This is an empty request This is an empty request
@ -1793,23 +1810,6 @@ This is an empty request
### GetPasswordLockoutPolicyRequest
This is an empty request
### GetPasswordLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| policy | zitadel.policy.v1.PasswordLockoutPolicy | - | |
### GetPreviewLabelPolicyRequest ### GetPreviewLabelPolicyRequest
This is an empty request This is an empty request
@ -2924,6 +2924,28 @@ This is an empty request
### UpdateLockoutPolicyRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| max_password_attempts | uint32 | failed attempts until a user gets locked | |
### UpdateLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### UpdateLoginPolicyRequest ### UpdateLoginPolicyRequest
@ -3022,29 +3044,6 @@ This is an empty request
### UpdatePasswordLockoutPolicyRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| max_attempts | uint32 | failed attempts until a user gets locked | |
| show_lockout_failure | bool | If an error should be displayed during a lockout or not | |
### UpdatePasswordLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### UpdatePrivacyPolicyRequest ### UpdatePrivacyPolicyRequest

View File

@ -1926,64 +1926,64 @@ The password age policy is not used at the moment
DELETE: /policies/password/age DELETE: /policies/password/age
### GetPasswordLockoutPolicy ### GetLockoutPolicy
> **rpc** GetPasswordLockoutPolicy([GetPasswordLockoutPolicyRequest](#getpasswordlockoutpolicyrequest)) > **rpc** GetLockoutPolicy([GetLockoutPolicyRequest](#getlockoutpolicyrequest))
[GetPasswordLockoutPolicyResponse](#getpasswordlockoutpolicyresponse) [GetLockoutPolicyResponse](#getlockoutpolicyresponse)
The password lockout policy is not used at the moment
GET: /policies/password/lockout
### GetDefaultPasswordLockoutPolicy GET: /policies/lockout
> **rpc** GetDefaultPasswordLockoutPolicy([GetDefaultPasswordLockoutPolicyRequest](#getdefaultpasswordlockoutpolicyrequest))
[GetDefaultPasswordLockoutPolicyResponse](#getdefaultpasswordlockoutpolicyresponse)
The password lockout policy is not used at the moment ### GetDefaultLockoutPolicy
> **rpc** GetDefaultLockoutPolicy([GetDefaultLockoutPolicyRequest](#getdefaultlockoutpolicyrequest))
[GetDefaultLockoutPolicyResponse](#getdefaultlockoutpolicyresponse)
GET: /policies/default/password/lockout
### AddCustomPasswordLockoutPolicy GET: /policies/default/lockout
> **rpc** AddCustomPasswordLockoutPolicy([AddCustomPasswordLockoutPolicyRequest](#addcustompasswordlockoutpolicyrequest))
[AddCustomPasswordLockoutPolicyResponse](#addcustompasswordlockoutpolicyresponse)
The password lockout policy is not used at the moment ### AddCustomLockoutPolicy
> **rpc** AddCustomLockoutPolicy([AddCustomLockoutPolicyRequest](#addcustomlockoutpolicyrequest))
[AddCustomLockoutPolicyResponse](#addcustomlockoutpolicyresponse)
POST: /policies/password/lockout
### UpdateCustomPasswordLockoutPolicy POST: /policies/lockout
> **rpc** UpdateCustomPasswordLockoutPolicy([UpdateCustomPasswordLockoutPolicyRequest](#updatecustompasswordlockoutpolicyrequest))
[UpdateCustomPasswordLockoutPolicyResponse](#updatecustompasswordlockoutpolicyresponse)
The password lockout policy is not used at the moment ### UpdateCustomLockoutPolicy
> **rpc** UpdateCustomLockoutPolicy([UpdateCustomLockoutPolicyRequest](#updatecustomlockoutpolicyrequest))
[UpdateCustomLockoutPolicyResponse](#updatecustomlockoutpolicyresponse)
PUT: /policies/password/lockout
### ResetPasswordLockoutPolicyToDefault PUT: /policies/lockout
> **rpc** ResetPasswordLockoutPolicyToDefault([ResetPasswordLockoutPolicyToDefaultRequest](#resetpasswordlockoutpolicytodefaultrequest))
[ResetPasswordLockoutPolicyToDefaultResponse](#resetpasswordlockoutpolicytodefaultresponse)
The password lockout policy is not used at the moment ### ResetLockoutPolicyToDefault
> **rpc** ResetLockoutPolicyToDefault([ResetLockoutPolicyToDefaultRequest](#resetlockoutpolicytodefaultrequest))
[ResetLockoutPolicyToDefaultResponse](#resetlockoutpolicytodefaultresponse)
DELETE: /policies/password/lockout
DELETE: /policies/lockout
### GetPrivacyPolicy ### GetPrivacyPolicy
@ -2769,6 +2769,28 @@ This is an empty request
### AddCustomLockoutPolicyRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| max_password_attempts | uint32 | - | |
### AddCustomLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### AddCustomLoginPolicyRequest ### AddCustomLoginPolicyRequest
@ -2845,29 +2867,6 @@ This is an empty request
### AddCustomPasswordLockoutPolicyRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| max_attempts | uint32 | - | |
| show_lockout_failure | bool | - | |
### AddCustomPasswordLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### AddCustomPrivacyPolicyRequest ### AddCustomPrivacyPolicyRequest
@ -3915,6 +3914,23 @@ This is an empty request
### GetDefaultLockoutPolicyRequest
This is an empty request
### GetDefaultLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| policy | zitadel.policy.v1.LockoutPolicy | - | |
### GetDefaultLoginPolicyRequest ### GetDefaultLoginPolicyRequest
@ -3988,23 +4004,6 @@ This is an empty request
### GetDefaultPasswordLockoutPolicyRequest
This is an empty request
### GetDefaultPasswordLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| policy | zitadel.policy.v1.PasswordLockoutPolicy | - | |
### GetDefaultPasswordResetMessageTextRequest ### GetDefaultPasswordResetMessageTextRequest
@ -4255,6 +4254,24 @@ This is an empty request
### GetLockoutPolicyRequest
This is an empty request
### GetLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| policy | zitadel.policy.v1.LockoutPolicy | - | |
| is_default | bool | - | |
### GetLoginPolicyRequest ### GetLoginPolicyRequest
@ -4428,24 +4445,6 @@ This is an empty request
### GetPasswordLockoutPolicyRequest
This is an empty request
### GetPasswordLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| policy | zitadel.policy.v1.PasswordLockoutPolicy | - | |
| is_default | bool | - | |
### GetPreviewLabelPolicyRequest ### GetPreviewLabelPolicyRequest
This is an empty request This is an empty request
@ -6529,6 +6528,23 @@ This is an empty request
### ResetLockoutPolicyToDefaultRequest
This is an empty request
### ResetLockoutPolicyToDefaultResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### ResetLoginPolicyToDefaultRequest ### ResetLoginPolicyToDefaultRequest
@ -6580,23 +6596,6 @@ This is an empty request
### ResetPasswordLockoutPolicyToDefaultRequest
This is an empty request
### ResetPasswordLockoutPolicyToDefaultResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### ResetPrivacyPolicyToDefaultRequest ### ResetPrivacyPolicyToDefaultRequest
This is an empty request This is an empty request
@ -7083,6 +7082,28 @@ This is an empty request
### UpdateCustomLockoutPolicyRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| max_password_attempts | uint32 | - | |
### UpdateCustomLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### UpdateCustomLoginPolicyRequest ### UpdateCustomLoginPolicyRequest
@ -7159,29 +7180,6 @@ This is an empty request
### UpdateCustomPasswordLockoutPolicyRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| max_attempts | uint32 | - | |
| show_lockout_failure | bool | - | |
### UpdateCustomPasswordLockoutPolicyResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### UpdateCustomPrivacyPolicyRequest ### UpdateCustomPrivacyPolicyRequest

View File

@ -36,6 +36,19 @@ title: zitadel/policy.proto
### LockoutPolicy
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
| max_password_attempts | uint64 | - | |
| is_default | bool | - | |
### LoginPolicy ### LoginPolicy
@ -98,20 +111,6 @@ title: zitadel/policy.proto
### PasswordLockoutPolicy
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
| max_attempts | uint64 | - | |
| show_lockout_failure | bool | - | |
| is_default | bool | - | |
### PrivacyPolicy ### PrivacyPolicy

View File

@ -37,6 +37,16 @@ The Login Policy defines how the login process should look like and which authen
![Login Policy](/img/manuals/policies/console_org_login.png) ![Login Policy](/img/manuals/policies/console_org_login.png)
## Lockout Policy
Define when a user should be blocked.
The following properties are possible:
- Maximum Password Attempts: When the user has reached the maximum password attempts the user will be locked
If a user is locked, an administrator has to unlock it in the ZITADEL console
### Multifactors / Second Factors ### Multifactors / Second Factors
In the multifactors section you can configure what kind of multifactors should be allowed. For passwordless to work, it's required to enable U2F (Universial Second Factor) with PIN. There is no other option at the moment. In the multifactors section you can configure what kind of multifactors should be allowed. For passwordless to work, it's required to enable U2F (Universial Second Factor) with PIN. There is no other option at the moment.

View File

@ -282,30 +282,30 @@ func (repo *IAMRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*ia
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_es_model.PasswordAgeViewToModel(policy), nil
} }
func (repo *IAMRepository) GetDefaultPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) { func (repo *IAMRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
policy, viewErr := repo.View.PasswordLockoutPolicyByAggregateID(repo.SystemDefaults.IamID) policy, viewErr := repo.View.LockoutPolicyByAggregateID(repo.SystemDefaults.IamID)
if viewErr != nil && !caos_errs.IsNotFound(viewErr) { if viewErr != nil && !caos_errs.IsNotFound(viewErr) {
return nil, viewErr return nil, viewErr
} }
if caos_errs.IsNotFound(viewErr) { if caos_errs.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordLockoutPolicyView) policy = new(iam_es_model.LockoutPolicyView)
} }
events, esErr := repo.getIAMEvents(ctx, policy.Sequence) events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if caos_errs.IsNotFound(viewErr) && len(events) == 0 { if caos_errs.IsNotFound(viewErr) && len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-2M9oP", "Errors.IAM.PasswordLockoutPolicy.NotFound") return nil, caos_errs.ThrowNotFound(nil, "EVENT-2M9oP", "Errors.IAM.LockoutPolicy.NotFound")
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-3M0xs").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-3M0xs").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
} }
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
func (repo *IAMRepository) GetOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) { func (repo *IAMRepository) GetOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) {

View File

@ -3,6 +3,7 @@ package handler
import ( import (
"time" "time"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/static" "github.com/caos/zitadel/internal/static"
@ -31,7 +32,7 @@ func (h *handler) Eventstore() v1.Eventstore {
return h.es return h.es
} }
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, defaults systemdefaults.SystemDefaults, static static.Storage, localDevMode bool) []query.Handler { func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, defaults systemdefaults.SystemDefaults, command *command.Commands, static static.Storage, localDevMode bool) []query.Handler {
handlers := []query.Handler{ handlers := []query.Handler{
newOrg( newOrg(
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
@ -53,8 +54,8 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}),
newPasswordAgePolicy( newPasswordAgePolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}),
newPasswordLockoutPolicy( newLockoutPolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("LockoutPolicy"), errorCount, es}),
newOrgIAMPolicy( newOrgIAMPolicy(
handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}),
newExternalIDP( newExternalIDP(

View File

@ -13,16 +13,16 @@ import (
) )
const ( const (
passwordLockoutPolicyTable = "adminapi.password_lockout_policies" lockoutPolicyTable = "adminapi.lockout_policies"
) )
type PasswordLockoutPolicy struct { type LockoutPolicy struct {
handler handler
subscription *v1.Subscription subscription *v1.Subscription
} }
func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy { func newLockoutPolicy(handler handler) *LockoutPolicy {
h := &PasswordLockoutPolicy{ h := &LockoutPolicy{
handler: handler, handler: handler,
} }
@ -31,7 +31,7 @@ func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy {
return h return h
} }
func (p *PasswordLockoutPolicy) subscribe() { func (p *LockoutPolicy) subscribe() {
p.subscription = p.es.Subscribe(p.AggregateTypes()...) p.subscription = p.es.Subscribe(p.AggregateTypes()...)
go func() { go func() {
for event := range p.subscription.Events { for event := range p.subscription.Events {
@ -40,28 +40,28 @@ func (p *PasswordLockoutPolicy) subscribe() {
}() }()
} }
func (p *PasswordLockoutPolicy) ViewModel() string { func (p *LockoutPolicy) ViewModel() string {
return passwordLockoutPolicyTable return lockoutPolicyTable
} }
func (m *PasswordLockoutPolicy) Subscription() *v1.Subscription { func (m *LockoutPolicy) Subscription() *v1.Subscription {
return m.subscription return m.subscription
} }
func (p *PasswordLockoutPolicy) AggregateTypes() []es_models.AggregateType { func (p *LockoutPolicy) AggregateTypes() []es_models.AggregateType {
return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate}
} }
func (p *PasswordLockoutPolicy) CurrentSequence() (uint64, error) { func (p *LockoutPolicy) CurrentSequence() (uint64, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil { if err != nil {
return 0, err return 0, err
} }
return sequence.CurrentSequence, nil return sequence.CurrentSequence, nil
} }
func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) { func (p *LockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -70,41 +70,41 @@ func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
LatestSequenceFilter(sequence.CurrentSequence), nil LatestSequenceFilter(sequence.CurrentSequence), nil
} }
func (p *PasswordLockoutPolicy) Reduce(event *es_models.Event) (err error) { func (p *LockoutPolicy) Reduce(event *es_models.Event) (err error) {
switch event.AggregateType { switch event.AggregateType {
case model.OrgAggregate, iam_es_model.IAMAggregate: case model.OrgAggregate, iam_es_model.IAMAggregate:
err = p.processPasswordLockoutPolicy(event) err = p.processLockoutPolicy(event)
} }
return err return err
} }
func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *es_models.Event) (err error) { func (p *LockoutPolicy) processLockoutPolicy(event *es_models.Event) (err error) {
policy := new(iam_model.PasswordLockoutPolicyView) policy := new(iam_model.LockoutPolicyView)
switch event.Type { switch event.Type {
case iam_es_model.PasswordLockoutPolicyAdded, model.PasswordLockoutPolicyAdded: case iam_es_model.LockoutPolicyAdded, model.LockoutPolicyAdded:
err = policy.AppendEvent(event) err = policy.AppendEvent(event)
case iam_es_model.PasswordLockoutPolicyChanged, model.PasswordLockoutPolicyChanged: case iam_es_model.LockoutPolicyChanged, model.LockoutPolicyChanged:
policy, err = p.view.PasswordLockoutPolicyByAggregateID(event.AggregateID) policy, err = p.view.LockoutPolicyByAggregateID(event.AggregateID)
if err != nil { if err != nil {
return err return err
} }
err = policy.AppendEvent(event) err = policy.AppendEvent(event)
case model.PasswordLockoutPolicyRemoved: case model.LockoutPolicyRemoved:
return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event) return p.view.DeleteLockoutPolicy(event.AggregateID, event)
default: default:
return p.view.ProcessedPasswordLockoutPolicySequence(event) return p.view.ProcessedLockoutPolicySequence(event)
} }
if err != nil { if err != nil {
return err return err
} }
return p.view.PutPasswordLockoutPolicy(policy, event) return p.view.PutLockoutPolicy(policy, event)
} }
func (p *PasswordLockoutPolicy) OnError(event *es_models.Event, err error) error { func (p *LockoutPolicy) OnError(event *es_models.Event, err error) error {
logging.LogWithFields("SPOOL-nD8sie", "id", event.AggregateID).WithError(err).Warn("something went wrong in passwordLockout policy handler") logging.LogWithFields("SPOOL-nD8sie", "id", event.AggregateID).WithError(err).Warn("something went wrong in Lockout policy handler")
return spooler.HandleError(event, err, p.view.GetLatestPasswordLockoutPolicyFailedEvent, p.view.ProcessedPasswordLockoutPolicyFailedEvent, p.view.ProcessedPasswordLockoutPolicySequence, p.errorCountUntilSkip) return spooler.HandleError(event, err, p.view.GetLatestLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicySequence, p.errorCountUntilSkip)
} }
func (p *PasswordLockoutPolicy) OnSuccess() error { func (p *LockoutPolicy) OnSuccess() error {
return spooler.HandleSuccess(p.view.UpdatePasswordLockoutPolicySpoolerRunTimestamp) return spooler.HandleSuccess(p.view.UpdateLockoutPolicySpoolerRunTimestamp)
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/spooler" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/spooler"
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults" sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/config/types"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
@ -34,7 +35,7 @@ type EsRepository struct {
eventstore.UserRepo eventstore.UserRepo
} }
func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, static static.Storage, roles []string, localDevMode bool) (*EsRepository, error) { func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, static static.Storage, roles []string, localDevMode bool) (*EsRepository, error) {
es, err := v1.Start(conf.Eventstore) es, err := v1.Start(conf.Eventstore)
if err != nil { if err != nil {
return nil, err return nil, err
@ -48,7 +49,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, s
return nil, err return nil, err
} }
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults, static, localDevMode) spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults, command, static, localDevMode)
assetsAPI := conf.APIDomain + "/assets/v1/" assetsAPI := conf.APIDomain + "/assets/v1/"
statikLoginFS, err := fs.NewWithNamespace("login") statikLoginFS, err := fs.NewWithNamespace("login")

View File

@ -2,6 +2,7 @@ package spooler
import ( import (
"database/sql" "database/sql"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/static" "github.com/caos/zitadel/internal/static"
@ -18,12 +19,12 @@ type SpoolerConfig struct {
Handlers handler.Configs Handlers handler.Configs
} }
func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, defaults systemdefaults.SystemDefaults, static static.Storage, localDevMode bool) *spooler.Spooler { func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, defaults systemdefaults.SystemDefaults, command *command.Commands, static static.Storage, localDevMode bool) *spooler.Spooler {
spoolerConfig := spooler.Config{ spoolerConfig := spooler.Config{
Eventstore: es, Eventstore: es,
Locker: &locker{dbClient: sql}, Locker: &locker{dbClient: sql},
ConcurrentWorkers: c.ConcurrentWorkers, ConcurrentWorkers: c.ConcurrentWorkers,
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, defaults, static, localDevMode), ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, defaults, command, static, localDevMode),
} }
spool := spoolerConfig.New() spool := spoolerConfig.New()
spool.Start() spool.Start()

View File

@ -0,0 +1,53 @@
package view
import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/repository/view"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_view "github.com/caos/zitadel/internal/view/repository"
)
const (
lockoutPolicyTable = "adminapi.lockout_policies"
)
func (v *View) LockoutPolicyByAggregateID(aggregateID string) (*model.LockoutPolicyView, error) {
return view.GetLockoutPolicyByAggregateID(v.Db, lockoutPolicyTable, aggregateID)
}
func (v *View) PutLockoutPolicy(policy *model.LockoutPolicyView, event *models.Event) error {
err := view.PutLockoutPolicy(v.Db, lockoutPolicyTable, policy)
if err != nil {
return err
}
return v.ProcessedLockoutPolicySequence(event)
}
func (v *View) DeleteLockoutPolicy(aggregateID string, event *models.Event) error {
err := view.DeleteLockoutPolicy(v.Db, lockoutPolicyTable, aggregateID)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedLockoutPolicySequence(event)
}
func (v *View) GetLatestLockoutPolicySequence() (*global_view.CurrentSequence, error) {
return v.latestSequence(lockoutPolicyTable)
}
func (v *View) ProcessedLockoutPolicySequence(event *models.Event) error {
return v.saveCurrentSequence(lockoutPolicyTable, event)
}
func (v *View) UpdateLockoutPolicySpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(lockoutPolicyTable)
}
func (v *View) GetLatestLockoutPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
return v.latestFailedEvent(lockoutPolicyTable, sequence)
}
func (v *View) ProcessedLockoutPolicyFailedEvent(failedEvent *global_view.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -1,53 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/repository/view"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_view "github.com/caos/zitadel/internal/view/repository"
)
const (
passwordLockoutPolicyTable = "adminapi.password_lockout_policies"
)
func (v *View) PasswordLockoutPolicyByAggregateID(aggregateID string) (*model.PasswordLockoutPolicyView, error) {
return view.GetPasswordLockoutPolicyByAggregateID(v.Db, passwordLockoutPolicyTable, aggregateID)
}
func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, event *models.Event) error {
err := view.PutPasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, policy)
if err != nil {
return err
}
return v.ProcessedPasswordLockoutPolicySequence(event)
}
func (v *View) DeletePasswordLockoutPolicy(aggregateID string, event *models.Event) error {
err := view.DeletePasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, aggregateID)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedPasswordLockoutPolicySequence(event)
}
func (v *View) GetLatestPasswordLockoutPolicySequence() (*global_view.CurrentSequence, error) {
return v.latestSequence(passwordLockoutPolicyTable)
}
func (v *View) ProcessedPasswordLockoutPolicySequence(event *models.Event) error {
return v.saveCurrentSequence(passwordLockoutPolicyTable, event)
}
func (v *View) UpdatePasswordLockoutPolicySpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(passwordLockoutPolicyTable)
}
func (v *View) GetLatestPasswordLockoutPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
return v.latestFailedEvent(passwordLockoutPolicyTable, sequence)
}
func (v *View) ProcessedPasswordLockoutPolicyFailedEvent(failedEvent *global_view.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -43,7 +43,7 @@ type IAMRepository interface {
GetDefaultPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error) GetDefaultPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error)
GetDefaultPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error)
GetDefaultPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) GetDefaultPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error)

View File

@ -0,0 +1,31 @@
package admin
import (
"context"
"github.com/caos/zitadel/internal/api/grpc/object"
policy_grpc "github.com/caos/zitadel/internal/api/grpc/policy"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
)
func (s *Server) GetLockoutPolicy(ctx context.Context, req *admin_pb.GetLockoutPolicyRequest) (*admin_pb.GetLockoutPolicyResponse, error) {
policy, err := s.iam.GetDefaultLockoutPolicy(ctx)
if err != nil {
return nil, err
}
return &admin_pb.GetLockoutPolicyResponse{Policy: policy_grpc.ModelLockoutPolicyToPb(policy)}, nil
}
func (s *Server) UpdateLockoutPolicy(ctx context.Context, req *admin_pb.UpdateLockoutPolicyRequest) (*admin_pb.UpdateLockoutPolicyResponse, error) {
policy, err := s.command.ChangeDefaultLockoutPolicy(ctx, UpdateLockoutPolicyToDomain(req))
if err != nil {
return nil, err
}
return &admin_pb.UpdateLockoutPolicyResponse{
Details: object.ChangeToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}

View File

@ -0,0 +1,12 @@
package admin
import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/pkg/grpc/admin"
)
func UpdateLockoutPolicyToDomain(p *admin.UpdateLockoutPolicyRequest) *domain.LockoutPolicy {
return &domain.LockoutPolicy{
MaxPasswordAttempts: uint64(p.MaxPasswordAttempts),
}
}

View File

@ -1,31 +0,0 @@
package admin
import (
"context"
"github.com/caos/zitadel/internal/api/grpc/object"
policy_grpc "github.com/caos/zitadel/internal/api/grpc/policy"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
)
func (s *Server) GetPasswordLockoutPolicy(ctx context.Context, req *admin_pb.GetPasswordLockoutPolicyRequest) (*admin_pb.GetPasswordLockoutPolicyResponse, error) {
policy, err := s.iam.GetDefaultPasswordLockoutPolicy(ctx)
if err != nil {
return nil, err
}
return &admin_pb.GetPasswordLockoutPolicyResponse{Policy: policy_grpc.ModelPasswordLockoutPolicyToPb(policy)}, nil
}
func (s *Server) UpdatePasswordLockoutPolicy(ctx context.Context, req *admin_pb.UpdatePasswordLockoutPolicyRequest) (*admin_pb.UpdatePasswordLockoutPolicyResponse, error) {
policy, err := s.command.ChangeDefaultPasswordLockoutPolicy(ctx, UpdatePasswordLockoutPolicyToDomain(req))
if err != nil {
return nil, err
}
return &admin_pb.UpdatePasswordLockoutPolicyResponse{
Details: object.ChangeToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}

View File

@ -1,13 +0,0 @@
package admin
import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/pkg/grpc/admin"
)
func UpdatePasswordLockoutPolicyToDomain(p *admin.UpdatePasswordLockoutPolicyRequest) *domain.PasswordLockoutPolicy {
return &domain.PasswordLockoutPolicy{
MaxAttempts: uint64(p.MaxAttempts),
ShowLockOutFailures: p.ShowLockoutFailure,
}
}

View File

@ -0,0 +1,63 @@
package management
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
policy_grpc "github.com/caos/zitadel/internal/api/grpc/policy"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
func (s *Server) GetLockoutPolicy(ctx context.Context, req *mgmt_pb.GetLockoutPolicyRequest) (*mgmt_pb.GetLockoutPolicyResponse, error) {
policy, err := s.org.GetLockoutPolicy(ctx)
if err != nil {
return nil, err
}
return &mgmt_pb.GetLockoutPolicyResponse{Policy: policy_grpc.ModelLockoutPolicyToPb(policy), IsDefault: policy.Default}, nil
}
func (s *Server) GetDefaultLockoutPolicy(ctx context.Context, req *mgmt_pb.GetDefaultLockoutPolicyRequest) (*mgmt_pb.GetDefaultLockoutPolicyResponse, error) {
policy, err := s.org.GetDefaultLockoutPolicy(ctx)
if err != nil {
return nil, err
}
return &mgmt_pb.GetDefaultLockoutPolicyResponse{Policy: policy_grpc.ModelLockoutPolicyToPb(policy)}, nil
}
func (s *Server) AddCustomLockoutPolicy(ctx context.Context, req *mgmt_pb.AddCustomLockoutPolicyRequest) (*mgmt_pb.AddCustomLockoutPolicyResponse, error) {
policy, err := s.command.AddLockoutPolicy(ctx, authz.GetCtxData(ctx).OrgID, AddLockoutPolicyToDomain(req))
if err != nil {
return nil, err
}
return &mgmt_pb.AddCustomLockoutPolicyResponse{
Details: object.AddToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}
func (s *Server) UpdateCustomLockoutPolicy(ctx context.Context, req *mgmt_pb.UpdateCustomLockoutPolicyRequest) (*mgmt_pb.UpdateCustomLockoutPolicyResponse, error) {
policy, err := s.command.ChangeLockoutPolicy(ctx, authz.GetCtxData(ctx).OrgID, UpdateLockoutPolicyToDomain(req))
if err != nil {
return nil, err
}
return &mgmt_pb.UpdateCustomLockoutPolicyResponse{
Details: object.ChangeToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}
func (s *Server) ResetLockoutPolicyToDefault(ctx context.Context, req *mgmt_pb.ResetLockoutPolicyToDefaultRequest) (*mgmt_pb.ResetLockoutPolicyToDefaultResponse, error) {
objectDetails, err := s.command.RemovePasswordComplexityPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return &mgmt_pb.ResetLockoutPolicyToDefaultResponse{
Details: object.DomainToChangeDetailsPb(objectDetails),
}, nil
}

View File

@ -0,0 +1,18 @@
package management
import (
"github.com/caos/zitadel/internal/domain"
mgmt "github.com/caos/zitadel/pkg/grpc/management"
)
func AddLockoutPolicyToDomain(p *mgmt.AddCustomLockoutPolicyRequest) *domain.LockoutPolicy {
return &domain.LockoutPolicy{
MaxPasswordAttempts: uint64(p.MaxPasswordAttempts),
}
}
func UpdateLockoutPolicyToDomain(p *mgmt.UpdateCustomLockoutPolicyRequest) *domain.LockoutPolicy {
return &domain.LockoutPolicy{
MaxPasswordAttempts: uint64(p.MaxPasswordAttempts),
}
}

View File

@ -1,63 +0,0 @@
package management
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
policy_grpc "github.com/caos/zitadel/internal/api/grpc/policy"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
func (s *Server) GetPasswordLockoutPolicy(ctx context.Context, req *mgmt_pb.GetPasswordLockoutPolicyRequest) (*mgmt_pb.GetPasswordLockoutPolicyResponse, error) {
policy, err := s.org.GetPasswordLockoutPolicy(ctx)
if err != nil {
return nil, err
}
return &mgmt_pb.GetPasswordLockoutPolicyResponse{Policy: policy_grpc.ModelPasswordLockoutPolicyToPb(policy), IsDefault: policy.Default}, nil
}
func (s *Server) GetDefaultPasswordLockoutPolicy(ctx context.Context, req *mgmt_pb.GetDefaultPasswordLockoutPolicyRequest) (*mgmt_pb.GetDefaultPasswordLockoutPolicyResponse, error) {
policy, err := s.org.GetDefaultPasswordLockoutPolicy(ctx)
if err != nil {
return nil, err
}
return &mgmt_pb.GetDefaultPasswordLockoutPolicyResponse{Policy: policy_grpc.ModelPasswordLockoutPolicyToPb(policy)}, nil
}
func (s *Server) AddCustomPasswordLockoutPolicy(ctx context.Context, req *mgmt_pb.AddCustomPasswordLockoutPolicyRequest) (*mgmt_pb.AddCustomPasswordLockoutPolicyResponse, error) {
policy, err := s.command.AddPasswordLockoutPolicy(ctx, authz.GetCtxData(ctx).OrgID, AddPasswordLockoutPolicyToDomain(req))
if err != nil {
return nil, err
}
return &mgmt_pb.AddCustomPasswordLockoutPolicyResponse{
Details: object.AddToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}
func (s *Server) UpdateCustomPasswordLockoutPolicy(ctx context.Context, req *mgmt_pb.UpdateCustomPasswordLockoutPolicyRequest) (*mgmt_pb.UpdateCustomPasswordLockoutPolicyResponse, error) {
policy, err := s.command.ChangePasswordLockoutPolicy(ctx, authz.GetCtxData(ctx).OrgID, UpdatePasswordLockoutPolicyToDomain(req))
if err != nil {
return nil, err
}
return &mgmt_pb.UpdateCustomPasswordLockoutPolicyResponse{
Details: object.ChangeToDetailsPb(
policy.Sequence,
policy.ChangeDate,
policy.ResourceOwner,
),
}, nil
}
func (s *Server) ResetPasswordLockoutPolicyToDefault(ctx context.Context, req *mgmt_pb.ResetPasswordLockoutPolicyToDefaultRequest) (*mgmt_pb.ResetPasswordLockoutPolicyToDefaultResponse, error) {
objectDetails, err := s.command.RemovePasswordComplexityPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return &mgmt_pb.ResetPasswordLockoutPolicyToDefaultResponse{
Details: object.DomainToChangeDetailsPb(objectDetails),
}, nil
}

View File

@ -1,20 +0,0 @@
package management
import (
"github.com/caos/zitadel/internal/domain"
mgmt "github.com/caos/zitadel/pkg/grpc/management"
)
func AddPasswordLockoutPolicyToDomain(p *mgmt.AddCustomPasswordLockoutPolicyRequest) *domain.PasswordLockoutPolicy {
return &domain.PasswordLockoutPolicy{
MaxAttempts: uint64(p.MaxAttempts),
ShowLockOutFailures: p.ShowLockoutFailure,
}
}
func UpdatePasswordLockoutPolicyToDomain(p *mgmt.UpdateCustomPasswordLockoutPolicyRequest) *domain.PasswordLockoutPolicy {
return &domain.PasswordLockoutPolicy{
MaxAttempts: uint64(p.MaxAttempts),
ShowLockOutFailures: p.ShowLockoutFailure,
}
}

View File

@ -6,11 +6,10 @@ import (
policy_pb "github.com/caos/zitadel/pkg/grpc/policy" policy_pb "github.com/caos/zitadel/pkg/grpc/policy"
) )
func ModelPasswordLockoutPolicyToPb(policy *model.PasswordLockoutPolicyView) *policy_pb.PasswordLockoutPolicy { func ModelLockoutPolicyToPb(policy *model.LockoutPolicyView) *policy_pb.LockoutPolicy {
return &policy_pb.PasswordLockoutPolicy{ return &policy_pb.LockoutPolicy{
IsDefault: policy.Default, IsDefault: policy.Default,
MaxAttempts: policy.MaxAttempts, MaxPasswordAttempts: policy.MaxPasswordAttempts,
ShowLockoutFailure: policy.ShowLockOutFailures,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
policy.Sequence, policy.Sequence,
policy.CreationDate, policy.CreationDate,

View File

@ -35,14 +35,15 @@ type AuthRequestRepo struct {
View *view.View View *view.View
Eventstore v1.Eventstore Eventstore v1.Eventstore
UserSessionViewProvider userSessionViewProvider UserSessionViewProvider userSessionViewProvider
UserViewProvider userViewProvider UserViewProvider userViewProvider
UserCommandProvider userCommandProvider UserCommandProvider userCommandProvider
UserEventProvider userEventProvider UserEventProvider userEventProvider
OrgViewProvider orgViewProvider OrgViewProvider orgViewProvider
LoginPolicyViewProvider loginPolicyViewProvider LoginPolicyViewProvider loginPolicyViewProvider
IDPProviderViewProvider idpProviderViewProvider LockoutPolicyViewProvider lockoutPolicyViewProvider
UserGrantProvider userGrantProvider IDPProviderViewProvider idpProviderViewProvider
UserGrantProvider userGrantProvider
IdGenerator id.Generator IdGenerator id.Generator
@ -69,6 +70,10 @@ type loginPolicyViewProvider interface {
LoginPolicyByAggregateID(string) (*iam_view_model.LoginPolicyView, error) LoginPolicyByAggregateID(string) (*iam_view_model.LoginPolicyView, error)
} }
type lockoutPolicyViewProvider interface {
LockoutPolicyByAggregateID(string) (*iam_view_model.LockoutPolicyView, error)
}
type idpProviderViewProvider interface { type idpProviderViewProvider interface {
IDPProvidersByAggregateIDAndState(string, iam_model.IDPConfigState) ([]*iam_view_model.IDPProviderView, error) IDPProvidersByAggregateIDAndState(string, iam_model.IDPConfigState) ([]*iam_view_model.IDPProviderView, error)
} }
@ -240,7 +245,7 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge
if err != nil { if err != nil {
return err return err
} }
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, userID) user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, userID)
if err != nil { if err != nil {
return err return err
} }
@ -262,7 +267,11 @@ func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, res
if err != nil { if err != nil {
return err return err
} }
return repo.Command.HumanCheckPassword(ctx, resourceOwner, userID, password, request.WithCurrentInfo(info)) policy, err := repo.getLockoutPolicy(ctx, resourceOwner)
if err != nil {
return err
}
return repo.Command.HumanCheckPassword(ctx, resourceOwner, userID, password, request.WithCurrentInfo(info), policy)
} }
func (repo *AuthRequestRepo) VerifyMFAOTP(ctx context.Context, authRequestID, userID, resourceOwner, code, userAgentID string, info *domain.BrowserInfo) (err error) { func (repo *AuthRequestRepo) VerifyMFAOTP(ctx context.Context, authRequestID, userID, resourceOwner, code, userAgentID string, info *domain.BrowserInfo) (err error) {
@ -414,6 +423,10 @@ func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authR
if request.UserID != userID { if request.UserID != userID {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-GBH32", "Errors.User.NotMatchingUserID") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-GBH32", "Errors.User.NotMatchingUserID")
} }
_, err = activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, request.UserID)
if err != nil {
return nil, err
}
return request, nil return request, nil
} }
@ -466,6 +479,11 @@ func (repo *AuthRequestRepo) fillPolicies(ctx context.Context, request *domain.A
if idpProviders != nil { if idpProviders != nil {
request.AllowedExternalIDPs = idpProviders request.AllowedExternalIDPs = idpProviders
} }
lockoutPolicy, err := repo.getLockoutPolicy(ctx, orgID)
if err != nil {
return err
}
request.LockoutPolicy = lockoutPolicy
privacyPolicy, err := repo.getPrivacyPolicy(ctx, orgID) privacyPolicy, err := repo.getPrivacyPolicy(ctx, orgID)
if err != nil { if err != nil {
return err return err
@ -587,7 +605,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
} }
return steps, nil return steps, nil
} }
user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, request.UserID) user, err := activeUserByID(ctx, repo.UserViewProvider, repo.UserEventProvider, repo.OrgViewProvider, repo.LockoutPolicyViewProvider, request.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -795,6 +813,21 @@ func (repo *AuthRequestRepo) getPrivacyPolicy(ctx context.Context, orgID string)
return policy.ToDomain(), err return policy.ToDomain(), err
} }
func (repo *AuthRequestRepo) getLockoutPolicy(ctx context.Context, orgID string) (*domain.LockoutPolicy, error) {
policy, err := repo.View.LockoutPolicyByAggregateID(orgID)
if errors.IsNotFound(err) {
policy, err = repo.View.LockoutPolicyByAggregateID(repo.IAMID)
if err != nil {
return nil, err
}
policy.Default = true
}
if err != nil {
return nil, err
}
return policy.ToDomain(), err
}
func (repo *AuthRequestRepo) getLabelPolicy(ctx context.Context, orgID string) (*domain.LabelPolicy, error) { func (repo *AuthRequestRepo) getLabelPolicy(ctx context.Context, orgID string) (*domain.LabelPolicy, error) {
policy, err := repo.View.LabelPolicyByAggregateIDAndState(orgID, int32(domain.LabelPolicyStateActive)) policy, err := repo.View.LabelPolicyByAggregateIDAndState(orgID, int32(domain.LabelPolicyStateActive))
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
@ -921,7 +954,8 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil
} }
func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, orgViewProvider orgViewProvider, userID string) (*user_model.UserView, error) { func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, orgViewProvider orgViewProvider, lockoutPolicyProvider lockoutPolicyViewProvider, userID string) (*user_model.UserView, error) {
// PLANNED: Check LockoutPolicy
user, err := userByID(ctx, userViewProvider, userEventProvider, userID) user, err := userByID(ctx, userViewProvider, userEventProvider, userID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -930,7 +964,6 @@ func activeUserByID(ctx context.Context, userViewProvider userViewProvider, user
if user.HumanView == nil { if user.HumanView == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-Lm69x", "Errors.User.NotHuman") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-Lm69x", "Errors.User.NotHuman")
} }
if user.State == user_model.UserStateLocked || user.State == user_model.UserStateSuspend { if user.State == user_model.UserStateLocked || user.State == user_model.UserStateSuspend {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-FJ262", "Errors.User.Locked") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-FJ262", "Errors.User.Locked")
} }

View File

@ -152,6 +152,14 @@ func (m *mockLoginPolicy) LoginPolicyByAggregateID(id string) (*iam_view_model.L
return m.policy, nil return m.policy, nil
} }
type mockLockoutPolicy struct {
policy *iam_view_model.LockoutPolicyView
}
func (m *mockLockoutPolicy) LockoutPolicyByAggregateID(id string) (*iam_view_model.LockoutPolicyView, error) {
return m.policy, nil
}
func (m *mockViewUser) UserByID(string) (*user_view_model.UserView, error) { func (m *mockViewUser) UserByID(string) (*user_view_model.UserView, error) {
return &user_view_model.UserView{ return &user_view_model.UserView{
State: int32(user_model.UserStateActive), State: int32(user_model.UserStateActive),
@ -229,6 +237,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
orgViewProvider orgViewProvider orgViewProvider orgViewProvider
userGrantProvider userGrantProvider userGrantProvider userGrantProvider
loginPolicyProvider loginPolicyViewProvider loginPolicyProvider loginPolicyViewProvider
lockoutPolicyProvider lockoutPolicyViewProvider
PasswordCheckLifeTime time.Duration PasswordCheckLifeTime time.Duration
ExternalLoginCheckLifeTime time.Duration ExternalLoginCheckLifeTime time.Duration
MFAInitSkippedLifeTime time.Duration MFAInitSkippedLifeTime time.Duration
@ -404,6 +413,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
}, },
}, },
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID"}, false}, args{&domain.AuthRequest{UserID: "UserID"}, false},
nil, nil,
@ -420,6 +434,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
}, },
}, },
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID"}, false}, args{&domain.AuthRequest{UserID: "UserID"}, false},
nil, nil,
@ -431,6 +450,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{}, userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewErrOrg{}, orgViewProvider: &mockViewErrOrg{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID"}, false}, args{&domain.AuthRequest{UserID: "UserID"}, false},
nil, nil,
@ -442,6 +466,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{}, userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateInactive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateInactive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID"}, false}, args{&domain.AuthRequest{UserID: "UserID"}, false},
nil, nil,
@ -456,6 +485,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.PasswordStep{}}, []domain.NextStep{&domain.PasswordStep{}},
@ -468,6 +502,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{}, userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID"}, false}, args{&domain.AuthRequest{UserID: "UserID"}, false},
nil, nil,
@ -483,6 +522,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID"}, false}, args{&domain.AuthRequest{UserID: "UserID"}, false},
[]domain.NextStep{&domain.InitUserStep{ []domain.NextStep{&domain.InitUserStep{
@ -500,6 +544,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour, MultiFactorCheckLifeTime: 10 * time.Hour,
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
[]domain.NextStep{&domain.PasswordlessRegistrationPromptStep{}}, []domain.NextStep{&domain.PasswordlessRegistrationPromptStep{}},
@ -512,8 +561,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{ userViewProvider: &mockViewUser{
PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}}, PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}},
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
MultiFactorCheckLifeTime: 10 * time.Hour, MultiFactorCheckLifeTime: 10 * time.Hour,
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
@ -528,8 +582,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordSet: true, PasswordSet: true,
PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}}, PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}},
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
MultiFactorCheckLifeTime: 10 * time.Hour, MultiFactorCheckLifeTime: 10 * time.Hour,
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
@ -550,7 +609,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: false, IsEmailVerified: false,
MFAMaxSetUp: int32(model.MFALevelMultiFactor), MFAMaxSetUp: int32(model.MFALevelMultiFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour, MultiFactorCheckLifeTime: 10 * time.Hour,
}, },
@ -572,7 +636,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordInitRequired: true, PasswordInitRequired: true,
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.InitPasswordStep{}}, []domain.NextStep{&domain.InitPasswordStep{}},
@ -588,7 +657,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -613,6 +687,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
loginPolicyProvider: &mockLoginPolicy{ loginPolicyProvider: &mockLoginPolicy{
policy: &iam_view_model.LoginPolicyView{}, policy: &iam_view_model.LoginPolicyView{},
}, },
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour, ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -634,8 +713,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{ userViewProvider: &mockViewUser{
PasswordSet: true, PasswordSet: true,
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
@ -654,9 +738,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour, ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
}, },
@ -682,8 +771,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
OTPState: int32(user_model.MFAStateReady), OTPState: int32(user_model.MFAStateReady),
MFAMaxSetUp: int32(model.MFALevelMultiFactor), MFAMaxSetUp: int32(model.MFALevelMultiFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -710,8 +804,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
OTPState: int32(user_model.MFAStateReady), OTPState: int32(user_model.MFAStateReady),
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -739,8 +838,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
OTPState: int32(user_model.MFAStateReady), OTPState: int32(user_model.MFAStateReady),
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour, ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
@ -771,8 +875,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -797,8 +906,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordSet: true, PasswordSet: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -823,8 +937,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordChangeRequired: true, PasswordChangeRequired: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -849,9 +968,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -877,9 +1001,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -912,6 +1041,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
roleCheck: true, roleCheck: true,
userGrants: 0, userGrants: 0,
}, },
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -944,6 +1078,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
roleCheck: true, roleCheck: true,
userGrants: 2, userGrants: 2,
}, },
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
@ -969,6 +1108,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
@ -995,8 +1139,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
PasswordCheckLifeTime: 10 * 24 * time.Hour, PasswordCheckLifeTime: 10 * 24 * time.Hour,
}, },
@ -1024,6 +1173,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
OrgViewProvider: tt.fields.orgViewProvider, OrgViewProvider: tt.fields.orgViewProvider,
UserGrantProvider: tt.fields.userGrantProvider, UserGrantProvider: tt.fields.userGrantProvider,
LoginPolicyViewProvider: tt.fields.loginPolicyProvider, LoginPolicyViewProvider: tt.fields.loginPolicyProvider,
LockoutPolicyViewProvider: tt.fields.lockoutPolicyProvider,
PasswordCheckLifeTime: tt.fields.PasswordCheckLifeTime, PasswordCheckLifeTime: tt.fields.PasswordCheckLifeTime,
ExternalLoginCheckLifeTime: tt.fields.ExternalLoginCheckLifeTime, ExternalLoginCheckLifeTime: tt.fields.ExternalLoginCheckLifeTime,
MFAInitSkippedLifeTime: tt.fields.MFAInitSkippedLifeTime, MFAInitSkippedLifeTime: tt.fields.MFAInitSkippedLifeTime,

View File

@ -73,6 +73,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
newPrivacyPolicy(handler{view, bulkLimit, configs.cycleDuration("PrivacyPolicy"), errorCount, es}), newPrivacyPolicy(handler{view, bulkLimit, configs.cycleDuration("PrivacyPolicy"), errorCount, es}),
newCustomText(handler{view, bulkLimit, configs.cycleDuration("CustomTexts"), errorCount, es}), newCustomText(handler{view, bulkLimit, configs.cycleDuration("CustomTexts"), errorCount, es}),
newMetadata(handler{view, bulkLimit, configs.cycleDuration("Metadata"), errorCount, es}), newMetadata(handler{view, bulkLimit, configs.cycleDuration("Metadata"), errorCount, es}),
newLockoutPolicy(handler{view, bulkLimit, configs.cycleDuration("LockoutPolicy"), errorCount, es}),
} }
} }

View File

@ -0,0 +1,110 @@
package handler
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/v1"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
)
const (
lockoutPolicyTable = "auth.lockout_policies"
)
type LockoutPolicy struct {
handler
subscription *v1.Subscription
}
func newLockoutPolicy(handler handler) *LockoutPolicy {
h := &LockoutPolicy{
handler: handler,
}
h.subscribe()
return h
}
func (p *LockoutPolicy) subscribe() {
p.subscription = p.es.Subscribe(p.AggregateTypes()...)
go func() {
for event := range p.subscription.Events {
query.ReduceEvent(p, event)
}
}()
}
func (p *LockoutPolicy) ViewModel() string {
return lockoutPolicyTable
}
func (p *LockoutPolicy) Subscription() *v1.Subscription {
return p.subscription
}
func (_ *LockoutPolicy) AggregateTypes() []es_models.AggregateType {
return []es_models.AggregateType{org_es_model.OrgAggregate, iam_es_model.IAMAggregate}
}
func (p *LockoutPolicy) CurrentSequence() (uint64, error) {
sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (p *LockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil {
return nil, err
}
return es_models.NewSearchQuery().
AggregateTypeFilter(p.AggregateTypes()...).
LatestSequenceFilter(sequence.CurrentSequence), nil
}
func (p *LockoutPolicy) Reduce(event *es_models.Event) (err error) {
switch event.AggregateType {
case org_es_model.OrgAggregate, iam_es_model.IAMAggregate:
err = p.processLockoutPolicy(event)
}
return err
}
func (p *LockoutPolicy) processLockoutPolicy(event *es_models.Event) (err error) {
policy := new(iam_model.LockoutPolicyView)
switch event.Type {
case iam_es_model.LockoutPolicyAdded, org_es_model.LockoutPolicyAdded:
err = policy.AppendEvent(event)
case iam_es_model.LockoutPolicyChanged, org_es_model.LockoutPolicyChanged:
policy, err = p.view.LockoutPolicyByAggregateID(event.AggregateID)
if err != nil {
return err
}
err = policy.AppendEvent(event)
case org_es_model.LockoutPolicyRemoved:
return p.view.DeleteLockoutPolicy(event.AggregateID, event)
default:
return p.view.ProcessedLockoutPolicySequence(event)
}
if err != nil {
return err
}
return p.view.PutLockoutPolicy(policy, event)
}
func (p *LockoutPolicy) OnError(event *es_models.Event, err error) error {
logging.LogWithFields("SPOOL-0pos2", "id", event.AggregateID).WithError(err).Warn("something went wrong in passwordLockout policy handler")
return spooler.HandleError(event, err, p.view.GetLatestLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicySequence, p.errorCountUntilSkip)
}
func (p *LockoutPolicy) OnSuccess() error {
return spooler.HandleSuccess(p.view.UpdateLockoutPolicySpoolerRunTimestamp)
}

View File

@ -109,6 +109,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
OrgViewProvider: view, OrgViewProvider: view,
IDPProviderViewProvider: view, IDPProviderViewProvider: view,
LoginPolicyViewProvider: view, LoginPolicyViewProvider: view,
LockoutPolicyViewProvider: view,
UserGrantProvider: view, UserGrantProvider: view,
IdGenerator: idGenerator, IdGenerator: idGenerator,
PasswordCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration, PasswordCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,

View File

@ -0,0 +1,53 @@
package view
import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/repository/view"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_view "github.com/caos/zitadel/internal/view/repository"
)
const (
passwordLockoutPolicyTable = "auth.lockout_policies"
)
func (v *View) LockoutPolicyByAggregateID(aggregateID string) (*model.LockoutPolicyView, error) {
return view.GetLockoutPolicyByAggregateID(v.Db, passwordLockoutPolicyTable, aggregateID)
}
func (v *View) PutLockoutPolicy(policy *model.LockoutPolicyView, event *models.Event) error {
err := view.PutLockoutPolicy(v.Db, passwordLockoutPolicyTable, policy)
if err != nil {
return err
}
return v.ProcessedLockoutPolicySequence(event)
}
func (v *View) DeleteLockoutPolicy(aggregateID string, event *models.Event) error {
err := view.DeleteLockoutPolicy(v.Db, passwordLockoutPolicyTable, aggregateID)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedLockoutPolicySequence(event)
}
func (v *View) GetLatestLockoutPolicySequence() (*global_view.CurrentSequence, error) {
return v.latestSequence(passwordLockoutPolicyTable)
}
func (v *View) ProcessedLockoutPolicySequence(event *models.Event) error {
return v.saveCurrentSequence(passwordLockoutPolicyTable, event)
}
func (v *View) UpdateLockoutPolicySpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(passwordLockoutPolicyTable)
}
func (v *View) GetLatestLockoutPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
return v.latestFailedEvent(passwordLockoutPolicyTable, sequence)
}
func (v *View) ProcessedLockoutPolicyFailedEvent(failedEvent *global_view.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -112,10 +112,10 @@ func writeModelToPasswordComplexityPolicy(wm *PasswordComplexityPolicyWriteModel
} }
} }
func writeModelToPasswordLockoutPolicy(wm *PasswordLockoutPolicyWriteModel) *domain.PasswordLockoutPolicy { func writeModelToLockoutPolicy(wm *LockoutPolicyWriteModel) *domain.LockoutPolicy {
return &domain.PasswordLockoutPolicy{ return &domain.LockoutPolicy{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel), ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
MaxAttempts: wm.MaxAttempts, MaxPasswordAttempts: wm.MaxPasswordAttempts,
ShowLockOutFailures: wm.ShowLockOutFailures, ShowLockOutFailures: wm.ShowLockOutFailures,
} }
} }

View File

@ -9,10 +9,10 @@ import (
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
) )
func (c *Commands) AddDefaultPasswordLockoutPolicy(ctx context.Context, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) { func (c *Commands) AddDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
addedPolicy := NewIAMPasswordLockoutPolicyWriteModel() addedPolicy := NewIAMLockoutPolicyWriteModel()
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.WriteModel) iamAgg := IAMAggregateFromWriteModel(&addedPolicy.WriteModel)
event, err := c.addDefaultPasswordLockoutPolicy(ctx, iamAgg, addedPolicy, policy) event, err := c.addDefaultLockoutPolicy(ctx, iamAgg, addedPolicy, policy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -25,34 +25,34 @@ func (c *Commands) AddDefaultPasswordLockoutPolicy(ctx context.Context, policy *
return nil, err return nil, err
} }
return writeModelToPasswordLockoutPolicy(&addedPolicy.PasswordLockoutPolicyWriteModel), nil return writeModelToLockoutPolicy(&addedPolicy.LockoutPolicyWriteModel), nil
} }
func (c *Commands) addDefaultPasswordLockoutPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMPasswordLockoutPolicyWriteModel, policy *domain.PasswordLockoutPolicy) (eventstore.EventPusher, error) { func (c *Commands) addDefaultLockoutPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMLockoutPolicyWriteModel, policy *domain.LockoutPolicy) (eventstore.EventPusher, error) {
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if addedPolicy.State == domain.PolicyStateActive { if addedPolicy.State == domain.PolicyStateActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0olDf", "Errors.IAM.PasswordLockoutPolicy.AlreadyExists") return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0olDf", "Errors.IAM.LockoutPolicy.AlreadyExists")
} }
return iam_repo.NewPasswordLockoutPolicyAddedEvent(ctx, iamAgg, policy.MaxAttempts, policy.ShowLockOutFailures), nil return iam_repo.NewLockoutPolicyAddedEvent(ctx, iamAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures), nil
} }
func (c *Commands) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) { func (c *Commands) ChangeDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
existingPolicy, err := c.defaultPasswordLockoutPolicyWriteModelByID(ctx) existingPolicy, err := c.defaultLockoutPolicyWriteModelByID(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved { if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordLockoutPolicy.NotFound") return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.LockoutPolicy.NotFound")
} }
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordLockoutPolicyWriteModel.WriteModel) iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.MaxAttempts, policy.ShowLockOutFailures) changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
if !hasChanged { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.PasswordLockoutPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LockoutPolicy.NotChanged")
} }
pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent) pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent)
@ -63,14 +63,14 @@ func (c *Commands) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, polic
if err != nil { if err != nil {
return nil, err return nil, err
} }
return writeModelToPasswordLockoutPolicy(&existingPolicy.PasswordLockoutPolicyWriteModel), nil return writeModelToLockoutPolicy(&existingPolicy.LockoutPolicyWriteModel), nil
} }
func (c *Commands) defaultPasswordLockoutPolicyWriteModelByID(ctx context.Context) (policy *IAMPasswordLockoutPolicyWriteModel, err error) { func (c *Commands) defaultLockoutPolicyWriteModelByID(ctx context.Context) (policy *IAMLockoutPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
writeModel := NewIAMPasswordLockoutPolicyWriteModel() writeModel := NewIAMLockoutPolicyWriteModel()
err = c.eventstore.FilterToQueryReducer(ctx, writeModel) err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -10,13 +10,13 @@ import (
"github.com/caos/zitadel/internal/repository/policy" "github.com/caos/zitadel/internal/repository/policy"
) )
type IAMPasswordLockoutPolicyWriteModel struct { type IAMLockoutPolicyWriteModel struct {
PasswordLockoutPolicyWriteModel LockoutPolicyWriteModel
} }
func NewIAMPasswordLockoutPolicyWriteModel() *IAMPasswordLockoutPolicyWriteModel { func NewIAMLockoutPolicyWriteModel() *IAMLockoutPolicyWriteModel {
return &IAMPasswordLockoutPolicyWriteModel{ return &IAMLockoutPolicyWriteModel{
PasswordLockoutPolicyWriteModel{ LockoutPolicyWriteModel{
WriteModel: eventstore.WriteModel{ WriteModel: eventstore.WriteModel{
AggregateID: domain.IAMID, AggregateID: domain.IAMID,
ResourceOwner: domain.IAMID, ResourceOwner: domain.IAMID,
@ -25,40 +25,40 @@ func NewIAMPasswordLockoutPolicyWriteModel() *IAMPasswordLockoutPolicyWriteModel
} }
} }
func (wm *IAMPasswordLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) { func (wm *IAMLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events { for _, event := range events {
switch e := event.(type) { switch e := event.(type) {
case *iam.PasswordLockoutPolicyAddedEvent: case *iam.LockoutPolicyAddedEvent:
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyAddedEvent) wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyAddedEvent)
case *iam.PasswordLockoutPolicyChangedEvent: case *iam.LockoutPolicyChangedEvent:
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyChangedEvent) wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyChangedEvent)
} }
} }
} }
func (wm *IAMPasswordLockoutPolicyWriteModel) Reduce() error { func (wm *IAMLockoutPolicyWriteModel) Reduce() error {
return wm.PasswordLockoutPolicyWriteModel.Reduce() return wm.LockoutPolicyWriteModel.Reduce()
} }
func (wm *IAMPasswordLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder { func (wm *IAMLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner). ResourceOwner(wm.ResourceOwner).
AddQuery(). AddQuery().
AggregateTypes(iam.AggregateType). AggregateTypes(iam.AggregateType).
AggregateIDs(wm.PasswordLockoutPolicyWriteModel.AggregateID). AggregateIDs(wm.LockoutPolicyWriteModel.AggregateID).
EventTypes( EventTypes(
iam.PasswordLockoutPolicyAddedEventType, iam.LockoutPolicyAddedEventType,
iam.PasswordLockoutPolicyChangedEventType). iam.LockoutPolicyChangedEventType).
Builder() Builder()
} }
func (wm *IAMPasswordLockoutPolicyWriteModel) NewChangedEvent( func (wm *IAMLockoutPolicyWriteModel) NewChangedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
maxAttempts uint64, maxAttempts uint64,
showLockoutFailure bool) (*iam.PasswordLockoutPolicyChangedEvent, bool) { showLockoutFailure bool) (*iam.LockoutPolicyChangedEvent, bool) {
changes := make([]policy.PasswordLockoutPolicyChanges, 0) changes := make([]policy.LockoutPolicyChanges, 0)
if wm.MaxAttempts != maxAttempts { if wm.MaxPasswordAttempts != maxAttempts {
changes = append(changes, policy.ChangeMaxAttempts(maxAttempts)) changes = append(changes, policy.ChangeMaxAttempts(maxAttempts))
} }
if wm.ShowLockOutFailures != showLockoutFailure { if wm.ShowLockOutFailures != showLockoutFailure {
@ -67,7 +67,7 @@ func (wm *IAMPasswordLockoutPolicyWriteModel) NewChangedEvent(
if len(changes) == 0 { if len(changes) == 0 {
return nil, false return nil, false
} }
changedEvent, err := iam.NewPasswordLockoutPolicyChangedEvent(ctx, aggregate, changes) changedEvent, err := iam.NewLockoutPolicyChangedEvent(ctx, aggregate, changes)
if err != nil { if err != nil {
return nil, false return nil, false
} }

View File

@ -13,16 +13,16 @@ import (
"testing" "testing"
) )
func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) { func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
policy *domain.PasswordLockoutPolicy policy *domain.LockoutPolicy
} }
type res struct { type res struct {
want *domain.PasswordLockoutPolicy want *domain.LockoutPolicy
err func(error) bool err func(error) bool
} }
tests := []struct { tests := []struct {
@ -32,13 +32,13 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
res res res res
}{ }{
{ {
name: "password lockout policy already existing, already exists error", name: "lockout policy already existing, already exists error",
fields: fields{ fields: fields{
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(), iam.NewLockoutPolicyAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
10, 10,
true, true,
@ -49,8 +49,8 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -67,7 +67,7 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(), iam.NewLockoutPolicyAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
10, 10,
true, true,
@ -79,18 +79,18 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
res: res{ res: res{
want: &domain.PasswordLockoutPolicy{ want: &domain.LockoutPolicy{
ObjectRoot: models.ObjectRoot{ ObjectRoot: models.ObjectRoot{
AggregateID: "IAM", AggregateID: "IAM",
ResourceOwner: "IAM", ResourceOwner: "IAM",
}, },
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -101,7 +101,7 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
} }
got, err := r.AddDefaultPasswordLockoutPolicy(tt.args.ctx, tt.args.policy) got, err := r.AddDefaultLockoutPolicy(tt.args.ctx, tt.args.policy)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -115,16 +115,16 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
} }
} }
func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) { func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
policy *domain.PasswordLockoutPolicy policy *domain.LockoutPolicy
} }
type res struct { type res struct {
want *domain.PasswordLockoutPolicy want *domain.LockoutPolicy
err func(error) bool err func(error) bool
} }
tests := []struct { tests := []struct {
@ -134,7 +134,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
res res res res
}{ }{
{ {
name: "password lockout policy not existing, not found error", name: "lockout policy not existing, not found error",
fields: fields{ fields: fields{
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
@ -143,8 +143,8 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -159,7 +159,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
t, t,
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(), iam.NewLockoutPolicyAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
10, 10,
true, true,
@ -170,8 +170,8 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -186,7 +186,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
t, t,
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(), iam.NewLockoutPolicyAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
10, 10,
true, true,
@ -196,7 +196,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
newDefaultPasswordLockoutPolicyChangedEvent(context.Background(), 20, false), newDefaultLockoutPolicyChangedEvent(context.Background(), 20, false),
), ),
}, },
), ),
@ -204,18 +204,18 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 20, MaxPasswordAttempts: 20,
ShowLockOutFailures: false, ShowLockOutFailures: false,
}, },
}, },
res: res{ res: res{
want: &domain.PasswordLockoutPolicy{ want: &domain.LockoutPolicy{
ObjectRoot: models.ObjectRoot{ ObjectRoot: models.ObjectRoot{
AggregateID: "IAM", AggregateID: "IAM",
ResourceOwner: "IAM", ResourceOwner: "IAM",
}, },
MaxAttempts: 20, MaxPasswordAttempts: 20,
ShowLockOutFailures: false, ShowLockOutFailures: false,
}, },
}, },
@ -226,7 +226,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
} }
got, err := r.ChangeDefaultPasswordLockoutPolicy(tt.args.ctx, tt.args.policy) got, err := r.ChangeDefaultLockoutPolicy(tt.args.ctx, tt.args.policy)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -240,10 +240,10 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
} }
} }
func newDefaultPasswordLockoutPolicyChangedEvent(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) *iam.PasswordLockoutPolicyChangedEvent { func newDefaultLockoutPolicyChangedEvent(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) *iam.LockoutPolicyChangedEvent {
event, _ := iam.NewPasswordLockoutPolicyChangedEvent(ctx, event, _ := iam.NewLockoutPolicyChangedEvent(ctx,
&iam.NewAggregate().Aggregate, &iam.NewAggregate().Aggregate,
[]policy.PasswordLockoutPolicyChanges{ []policy.LockoutPolicyChanges{
policy.ChangeMaxAttempts(maxAttempts), policy.ChangeMaxAttempts(maxAttempts),
policy.ChangeShowLockOutFailures(showLockoutFailure), policy.ChangeShowLockOutFailures(showLockoutFailure),
}, },

View File

@ -7,21 +7,21 @@ import (
"github.com/caos/zitadel/internal/repository/org" "github.com/caos/zitadel/internal/repository/org"
) )
func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) { func (c *Commands) AddLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
if resourceOwner == "" { if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-8fJif", "Errors.ResourceOwnerMissing") return nil, caos_errs.ThrowInvalidArgument(nil, "Org-8fJif", "Errors.ResourceOwnerMissing")
} }
addedPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner) addedPolicy := NewOrgLockoutPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if addedPolicy.State == domain.PolicyStateActive { if addedPolicy.State == domain.PolicyStateActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-0olDf", "Errors.ORG.PasswordLockoutPolicy.AlreadyExists") return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-0olDf", "Errors.ORG.LockoutPolicy.AlreadyExists")
} }
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel) orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewPasswordLockoutPolicyAddedEvent(ctx, orgAgg, policy.MaxAttempts, policy.ShowLockOutFailures)) pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLockoutPolicyAddedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -29,26 +29,26 @@ func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner s
if err != nil { if err != nil {
return nil, err return nil, err
} }
return writeModelToPasswordLockoutPolicy(&addedPolicy.PasswordLockoutPolicyWriteModel), nil return writeModelToLockoutPolicy(&addedPolicy.LockoutPolicyWriteModel), nil
} }
func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) { func (c *Commands) ChangeLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
if resourceOwner == "" { if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3J9fs", "Errors.ResourceOwnerMissing") return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3J9fs", "Errors.ResourceOwnerMissing")
} }
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner) existingPolicy := NewOrgLockoutPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy) err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved { if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-ADfs1", "Errors.Org.PasswordLockoutPolicy.NotFound") return nil, caos_errs.ThrowNotFound(nil, "ORG-ADfs1", "Errors.Org.LockoutPolicy.NotFound")
} }
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PasswordLockoutPolicyWriteModel.WriteModel) orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxAttempts, policy.ShowLockOutFailures) changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
if !hasChanged { if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-4M9vs", "Errors.Org.PasswordLockoutPolicy.NotChanged") return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-4M9vs", "Errors.Org.LockoutPolicy.NotChanged")
} }
pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent) pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent)
@ -59,23 +59,23 @@ func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwne
if err != nil { if err != nil {
return nil, err return nil, err
} }
return writeModelToPasswordLockoutPolicy(&existingPolicy.PasswordLockoutPolicyWriteModel), nil return writeModelToLockoutPolicy(&existingPolicy.LockoutPolicyWriteModel), nil
} }
func (c *Commands) RemovePasswordLockoutPolicy(ctx context.Context, orgID string) error { func (c *Commands) RemoveLockoutPolicy(ctx context.Context, orgID string) error {
if orgID == "" { if orgID == "" {
return caos_errs.ThrowInvalidArgument(nil, "Org-4J9fs", "Errors.ResourceOwnerMissing") return caos_errs.ThrowInvalidArgument(nil, "Org-4J9fs", "Errors.ResourceOwnerMissing")
} }
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(orgID) existingPolicy := NewOrgLockoutPolicyWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy) err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
if err != nil { if err != nil {
return err return err
} }
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved { if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return caos_errs.ThrowNotFound(nil, "ORG-D4zuz", "Errors.Org.PasswordLockoutPolicy.NotFound") return caos_errs.ThrowNotFound(nil, "ORG-D4zuz", "Errors.Org.LockoutPolicy.NotFound")
} }
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel) orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel)
_, err = c.eventstore.PushEvents(ctx, org.NewPasswordLockoutPolicyRemovedEvent(ctx, orgAgg)) _, err = c.eventstore.PushEvents(ctx, org.NewLockoutPolicyRemovedEvent(ctx, orgAgg))
return err return err
} }

View File

@ -9,13 +9,13 @@ import (
"github.com/caos/zitadel/internal/repository/policy" "github.com/caos/zitadel/internal/repository/policy"
) )
type OrgPasswordLockoutPolicyWriteModel struct { type OrgLockoutPolicyWriteModel struct {
PasswordLockoutPolicyWriteModel LockoutPolicyWriteModel
} }
func NewOrgPasswordLockoutPolicyWriteModel(orgID string) *OrgPasswordLockoutPolicyWriteModel { func NewOrgLockoutPolicyWriteModel(orgID string) *OrgLockoutPolicyWriteModel {
return &OrgPasswordLockoutPolicyWriteModel{ return &OrgLockoutPolicyWriteModel{
PasswordLockoutPolicyWriteModel{ LockoutPolicyWriteModel{
WriteModel: eventstore.WriteModel{ WriteModel: eventstore.WriteModel{
AggregateID: orgID, AggregateID: orgID,
ResourceOwner: orgID, ResourceOwner: orgID,
@ -24,42 +24,42 @@ func NewOrgPasswordLockoutPolicyWriteModel(orgID string) *OrgPasswordLockoutPoli
} }
} }
func (wm *OrgPasswordLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) { func (wm *OrgLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events { for _, event := range events {
switch e := event.(type) { switch e := event.(type) {
case *org.PasswordLockoutPolicyAddedEvent: case *org.LockoutPolicyAddedEvent:
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyAddedEvent) wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyAddedEvent)
case *org.PasswordLockoutPolicyChangedEvent: case *org.LockoutPolicyChangedEvent:
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyChangedEvent) wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyChangedEvent)
case *org.PasswordLockoutPolicyRemovedEvent: case *org.LockoutPolicyRemovedEvent:
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyRemovedEvent) wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyRemovedEvent)
} }
} }
} }
func (wm *OrgPasswordLockoutPolicyWriteModel) Reduce() error { func (wm *OrgLockoutPolicyWriteModel) Reduce() error {
return wm.PasswordLockoutPolicyWriteModel.Reduce() return wm.LockoutPolicyWriteModel.Reduce()
} }
func (wm *OrgPasswordLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder { func (wm *OrgLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner). ResourceOwner(wm.ResourceOwner).
AddQuery(). AddQuery().
AggregateTypes(org.AggregateType). AggregateTypes(org.AggregateType).
AggregateIDs(wm.PasswordLockoutPolicyWriteModel.AggregateID). AggregateIDs(wm.LockoutPolicyWriteModel.AggregateID).
EventTypes(org.PasswordLockoutPolicyAddedEventType, EventTypes(org.LockoutPolicyAddedEventType,
org.PasswordLockoutPolicyChangedEventType, org.LockoutPolicyChangedEventType,
org.PasswordLockoutPolicyRemovedEventType). org.LockoutPolicyRemovedEventType).
Builder() Builder()
} }
func (wm *OrgPasswordLockoutPolicyWriteModel) NewChangedEvent( func (wm *OrgLockoutPolicyWriteModel) NewChangedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
maxAttempts uint64, maxAttempts uint64,
showLockoutFailure bool) (*org.PasswordLockoutPolicyChangedEvent, bool) { showLockoutFailure bool) (*org.LockoutPolicyChangedEvent, bool) {
changes := make([]policy.PasswordLockoutPolicyChanges, 0) changes := make([]policy.LockoutPolicyChanges, 0)
if wm.MaxAttempts != maxAttempts { if wm.MaxPasswordAttempts != maxAttempts {
changes = append(changes, policy.ChangeMaxAttempts(maxAttempts)) changes = append(changes, policy.ChangeMaxAttempts(maxAttempts))
} }
if wm.ShowLockOutFailures != showLockoutFailure { if wm.ShowLockOutFailures != showLockoutFailure {
@ -68,7 +68,7 @@ func (wm *OrgPasswordLockoutPolicyWriteModel) NewChangedEvent(
if len(changes) == 0 { if len(changes) == 0 {
return nil, false return nil, false
} }
changedEvent, err := org.NewPasswordLockoutPolicyChangedEvent(ctx, aggregate, changes) changedEvent, err := org.NewLockoutPolicyChangedEvent(ctx, aggregate, changes)
if err != nil { if err != nil {
return nil, false return nil, false
} }

View File

@ -22,10 +22,10 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context
orgID string orgID string
policy *domain.PasswordLockoutPolicy policy *domain.LockoutPolicy
} }
type res struct { type res struct {
want *domain.PasswordLockoutPolicy want *domain.LockoutPolicy
err func(error) bool err func(error) bool
} }
tests := []struct { tests := []struct {
@ -43,8 +43,8 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -59,7 +59,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
t, t,
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
org.NewPasswordLockoutPolicyAddedEvent(context.Background(), org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
10, 10,
true, true,
@ -71,8 +71,8 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -89,7 +89,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
org.NewPasswordLockoutPolicyAddedEvent(context.Background(), org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
10, 10,
true, true,
@ -102,18 +102,18 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
res: res{ res: res{
want: &domain.PasswordLockoutPolicy{ want: &domain.LockoutPolicy{
ObjectRoot: models.ObjectRoot{ ObjectRoot: models.ObjectRoot{
AggregateID: "org1", AggregateID: "org1",
ResourceOwner: "org1", ResourceOwner: "org1",
}, },
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -124,7 +124,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
} }
got, err := r.AddPasswordLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy) got, err := r.AddLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -145,10 +145,10 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context
orgID string orgID string
policy *domain.PasswordLockoutPolicy policy *domain.LockoutPolicy
} }
type res struct { type res struct {
want *domain.PasswordLockoutPolicy want *domain.LockoutPolicy
err func(error) bool err func(error) bool
} }
tests := []struct { tests := []struct {
@ -166,8 +166,8 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -186,8 +186,8 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -202,7 +202,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
t, t,
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
org.NewPasswordLockoutPolicyAddedEvent(context.Background(), org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
10, 10,
true, true,
@ -214,8 +214,8 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
@ -230,7 +230,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
t, t,
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
org.NewPasswordLockoutPolicyAddedEvent(context.Background(), org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
10, 10,
true, true,
@ -249,18 +249,18 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
orgID: "org1", orgID: "org1",
policy: &domain.PasswordLockoutPolicy{ policy: &domain.LockoutPolicy{
MaxAttempts: 5, MaxPasswordAttempts: 5,
ShowLockOutFailures: false, ShowLockOutFailures: false,
}, },
}, },
res: res{ res: res{
want: &domain.PasswordLockoutPolicy{ want: &domain.LockoutPolicy{
ObjectRoot: models.ObjectRoot{ ObjectRoot: models.ObjectRoot{
AggregateID: "org1", AggregateID: "org1",
ResourceOwner: "org1", ResourceOwner: "org1",
}, },
MaxAttempts: 5, MaxPasswordAttempts: 5,
ShowLockOutFailures: false, ShowLockOutFailures: false,
}, },
}, },
@ -271,7 +271,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
} }
got, err := r.ChangePasswordLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy) got, err := r.ChangeLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -340,7 +340,7 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
t, t,
expectFilter( expectFilter(
eventFromEventPusher( eventFromEventPusher(
org.NewPasswordLockoutPolicyAddedEvent(context.Background(), org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, &org.NewAggregate("org1", "org1").Aggregate,
10, 10,
true, true,
@ -350,7 +350,7 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
expectPush( expectPush(
[]*repository.Event{ []*repository.Event{
eventFromEventPusher( eventFromEventPusher(
org.NewPasswordLockoutPolicyRemovedEvent(context.Background(), org.NewLockoutPolicyRemovedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate), &org.NewAggregate("org1", "org1").Aggregate),
), ),
}, },
@ -373,7 +373,7 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
} }
err := r.RemovePasswordLockoutPolicy(tt.args.ctx, tt.args.orgID) err := r.RemoveLockoutPolicy(tt.args.ctx, tt.args.orgID)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -384,10 +384,10 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
} }
} }
func newPasswordLockoutPolicyChangedEvent(ctx context.Context, orgID string, maxAttempts uint64, showLockoutFailure bool) *org.PasswordLockoutPolicyChangedEvent { func newPasswordLockoutPolicyChangedEvent(ctx context.Context, orgID string, maxAttempts uint64, showLockoutFailure bool) *org.LockoutPolicyChangedEvent {
event, _ := org.NewPasswordLockoutPolicyChangedEvent(ctx, event, _ := org.NewLockoutPolicyChangedEvent(ctx,
&org.NewAggregate(orgID, orgID).Aggregate, &org.NewAggregate(orgID, orgID).Aggregate,
[]policy.PasswordLockoutPolicyChanges{ []policy.LockoutPolicyChanges{
policy.ChangeMaxAttempts(maxAttempts), policy.ChangeMaxAttempts(maxAttempts),
policy.ChangeShowLockOutFailures(showLockoutFailure), policy.ChangeShowLockOutFailures(showLockoutFailure),
}, },

View File

@ -6,29 +6,29 @@ import (
"github.com/caos/zitadel/internal/repository/policy" "github.com/caos/zitadel/internal/repository/policy"
) )
type PasswordLockoutPolicyWriteModel struct { type LockoutPolicyWriteModel struct {
eventstore.WriteModel eventstore.WriteModel
MaxAttempts uint64 MaxPasswordAttempts uint64
ShowLockOutFailures bool ShowLockOutFailures bool
State domain.PolicyState State domain.PolicyState
} }
func (wm *PasswordLockoutPolicyWriteModel) Reduce() error { func (wm *LockoutPolicyWriteModel) Reduce() error {
for _, event := range wm.Events { for _, event := range wm.Events {
switch e := event.(type) { switch e := event.(type) {
case *policy.PasswordLockoutPolicyAddedEvent: case *policy.LockoutPolicyAddedEvent:
wm.MaxAttempts = e.MaxAttempts wm.MaxPasswordAttempts = e.MaxPasswordAttempts
wm.ShowLockOutFailures = e.ShowLockOutFailures wm.ShowLockOutFailures = e.ShowLockOutFailures
wm.State = domain.PolicyStateActive wm.State = domain.PolicyStateActive
case *policy.PasswordLockoutPolicyChangedEvent: case *policy.LockoutPolicyChangedEvent:
if e.MaxAttempts != nil { if e.MaxPasswordAttempts != nil {
wm.MaxAttempts = *e.MaxAttempts wm.MaxPasswordAttempts = *e.MaxPasswordAttempts
} }
if e.ShowLockOutFailures != nil { if e.ShowLockOutFailures != nil {
wm.ShowLockOutFailures = *e.ShowLockOutFailures wm.ShowLockOutFailures = *e.ShowLockOutFailures
} }
case *policy.PasswordLockoutPolicyRemovedEvent: case *policy.LockoutPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved wm.State = domain.PolicyStateRemoved
} }
} }

View File

@ -0,0 +1,35 @@
package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
)
type Step18 struct {
LockoutPolicy domain.LockoutPolicy
}
func (s *Step18) Step() domain.Step {
return domain.Step18
}
func (s *Step18) execute(ctx context.Context, commandSide *Commands) error {
return commandSide.SetupStep18(ctx, s)
}
func (c *Commands) SetupStep18(ctx context.Context, step *Step18) error {
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel)
addedPolicy := NewIAMLockoutPolicyWriteModel()
events, err := c.addDefaultLockoutPolicy(ctx, iamAgg, addedPolicy, &step.LockoutPolicy)
if err != nil {
return nil, err
}
logging.Log("SETUP-3m99ds").Info("default lockout policy set up")
return []eventstore.EventPusher{events}, nil
}
return c.setup(ctx, step, fn)
}

View File

@ -3,15 +3,13 @@ package command
import ( import (
"context" "context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
) )
type Step4 struct { type Step4 struct {
DefaultPasswordLockoutPolicy domain.PasswordLockoutPolicy DefaultPasswordLockoutPolicy domain.LockoutPolicy
} }
func (s *Step4) Step() domain.Step { func (s *Step4) Step() domain.Step {
@ -21,19 +19,12 @@ func (s *Step4) Step() domain.Step {
func (s *Step4) execute(ctx context.Context, commandSide *Commands) error { func (s *Step4) execute(ctx context.Context, commandSide *Commands) error {
return commandSide.SetupStep4(ctx, s) return commandSide.SetupStep4(ctx, s)
} }
//This step should not be executed when a new instance is setup, because its not used anymore
//SetupStep4 is no op in favour of step 18.
//Password lockout policy is replaced by lockout policy
func (c *Commands) SetupStep4(ctx context.Context, step *Step4) error { func (c *Commands) SetupStep4(ctx context.Context, step *Step4) error {
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) { fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel) return nil, nil
event, err := c.addDefaultPasswordLockoutPolicy(ctx, iamAgg, NewIAMPasswordLockoutPolicyWriteModel(), &domain.PasswordLockoutPolicy{
MaxAttempts: step.DefaultPasswordLockoutPolicy.MaxAttempts,
ShowLockOutFailures: step.DefaultPasswordLockoutPolicy.ShowLockOutFailures,
})
if err != nil {
return nil, err
}
logging.Log("SETUP-Bfnge").Info("default password lockout policy set up")
return []eventstore.EventPusher{event}, nil
} }
return c.setup(ctx, step, fn) return c.setup(ctx, step, fn)
} }

View File

@ -194,7 +194,7 @@ func (c *Commands) PasswordCodeSent(ctx context.Context, orgID, userID string) (
return err return err
} }
func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest) (err error) { func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest, lockoutPolicy *domain.LockoutPolicy) (err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
@ -225,7 +225,15 @@ func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, passwo
_, err = c.eventstore.PushEvents(ctx, user.NewHumanPasswordCheckSucceededEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest))) _, err = c.eventstore.PushEvents(ctx, user.NewHumanPasswordCheckSucceededEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
return err return err
} }
_, err = c.eventstore.PushEvents(ctx, user.NewHumanPasswordCheckFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest))) events := make([]eventstore.EventPusher, 0)
events = append(events, user.NewHumanPasswordCheckFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
if lockoutPolicy != nil && lockoutPolicy.MaxPasswordAttempts > 0 {
if existingPassword.PasswordCheckFailedCount+1 >= lockoutPolicy.MaxPasswordAttempts {
events = append(events, user.NewUserLockedEvent(ctx, userAgg))
}
}
_, err = c.eventstore.PushEvents(ctx, events...)
logging.Log("COMMAND-9fj7s").OnError(err).Error("error create password check failed event") logging.Log("COMMAND-9fj7s").OnError(err).Error("error create password check failed event")
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-452ad", "Errors.User.Password.Invalid") return caos_errs.ThrowInvalidArgument(nil, "COMMAND-452ad", "Errors.User.Password.Invalid")
} }

View File

@ -16,9 +16,10 @@ type HumanPasswordWriteModel struct {
Secret *crypto.CryptoValue Secret *crypto.CryptoValue
SecretChangeRequired bool SecretChangeRequired bool
Code *crypto.CryptoValue Code *crypto.CryptoValue
CodeCreationDate time.Time CodeCreationDate time.Time
CodeExpiry time.Duration CodeExpiry time.Duration
PasswordCheckFailedCount uint64
UserState domain.UserState UserState domain.UserState
} }
@ -51,6 +52,7 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
wm.Secret = e.Secret wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired wm.SecretChangeRequired = e.ChangeRequired
wm.Code = nil wm.Code = nil
wm.PasswordCheckFailedCount = 0
case *user.HumanPasswordCodeAddedEvent: case *user.HumanPasswordCodeAddedEvent:
wm.Code = e.Code wm.Code = e.Code
wm.CodeCreationDate = e.CreationDate() wm.CodeCreationDate = e.CreationDate()
@ -59,6 +61,12 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
if wm.UserState == domain.UserStateInitial { if wm.UserState == domain.UserStateInitial {
wm.UserState = domain.UserStateActive wm.UserState = domain.UserStateActive
} }
case *user.HumanPasswordCheckFailedEvent:
wm.PasswordCheckFailedCount += 1
case *user.HumanPasswordCheckSucceededEvent:
wm.PasswordCheckFailedCount = 0
case *user.UserUnlockedEvent:
wm.PasswordCheckFailedCount = 0
case *user.UserRemovedEvent: case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted wm.UserState = domain.UserStateDeleted
} }
@ -78,14 +86,19 @@ func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
user.HumanPasswordChangedType, user.HumanPasswordChangedType,
user.HumanPasswordCodeAddedType, user.HumanPasswordCodeAddedType,
user.HumanEmailVerifiedType, user.HumanEmailVerifiedType,
user.HumanPasswordCheckFailedType,
user.HumanPasswordCheckSucceededType,
user.UserRemovedType, user.UserRemovedType,
user.UserUnlockedType,
user.UserV1AddedType, user.UserV1AddedType,
user.UserV1RegisteredType, user.UserV1RegisteredType,
user.UserV1InitialCodeAddedType, user.UserV1InitialCodeAddedType,
user.UserV1InitializedCheckSucceededType, user.UserV1InitializedCheckSucceededType,
user.UserV1PasswordChangedType, user.UserV1PasswordChangedType,
user.UserV1PasswordCodeAddedType, user.UserV1PasswordCodeAddedType,
user.UserV1EmailVerifiedType). user.UserV1EmailVerifiedType,
user.UserV1PasswordCheckFailedType,
user.UserV1PasswordCheckSucceededType).
Builder() Builder()
if wm.ResourceOwner != "" { if wm.ResourceOwner != "" {

View File

@ -1082,6 +1082,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
resourceOwner string resourceOwner string
password string password string
authReq *domain.AuthRequest authReq *domain.AuthRequest
lockoutPolicy *domain.LockoutPolicy
} }
type res struct { type res struct {
err func(error) bool err func(error) bool
@ -1177,7 +1178,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
}, },
}, },
{ {
name: "password not matching, precondition error", name: "password not matching lockout policy not relevant, precondition error",
fields: fields{ fields: fields{
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
@ -1238,6 +1239,82 @@ func TestCommandSide_CheckPassword(t *testing.T) {
ID: "request1", ID: "request1",
AgentID: "agent1", AgentID: "agent1",
}, },
lockoutPolicy: &domain.LockoutPolicy{},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "password not matching, max password attempts reached - user locked, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
eventFromEventPusher(
user.NewHumanEmailVerifiedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
eventFromEventPusher(
user.NewHumanPasswordChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeHash,
Algorithm: "hash",
KeyID: "",
Crypted: []byte("password"),
},
false,
"")),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
user.NewHumanPasswordCheckFailedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
&user.AuthRequestInfo{
ID: "request1",
UserAgentID: "agent1",
},
),
),
eventFromEventPusher(
user.NewUserLockedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
},
),
),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
userID: "user1",
password: "password1",
resourceOwner: "org1",
authReq: &domain.AuthRequest{
ID: "request1",
AgentID: "agent1",
},
lockoutPolicy: &domain.LockoutPolicy{
MaxPasswordAttempts: 1,
},
}, },
res: res{ res: res{
err: caos_errs.IsErrorInvalidArgument, err: caos_errs.IsErrorInvalidArgument,
@ -1315,7 +1392,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
userPasswordAlg: tt.fields.userPasswordAlg, userPasswordAlg: tt.fields.userPasswordAlg,
} }
err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq) err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq, tt.args.lockoutPolicy)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@ -48,6 +48,7 @@ type AuthRequest struct {
AllowedExternalIDPs []*IDPProvider AllowedExternalIDPs []*IDPProvider
LabelPolicy *LabelPolicy LabelPolicy *LabelPolicy
PrivacyPolicy *PrivacyPolicy PrivacyPolicy *PrivacyPolicy
LockoutPolicy *LockoutPolicy
DefaultTranslations []*CustomText DefaultTranslations []*CustomText
OrgTranslations []*CustomText OrgTranslations []*CustomText
} }

View File

@ -22,5 +22,5 @@ type IAM struct {
DefaultOrgIAMPolicy *OrgIAMPolicy DefaultOrgIAMPolicy *OrgIAMPolicy
DefaultPasswordComplexityPolicy *PasswordComplexityPolicy DefaultPasswordComplexityPolicy *PasswordComplexityPolicy
DefaultPasswordAgePolicy *PasswordAgePolicy DefaultPasswordAgePolicy *PasswordAgePolicy
DefaultPasswordLockoutPolicy *PasswordLockoutPolicy DefaultPasswordLockoutPolicy *LockoutPolicy
} }

View File

@ -18,7 +18,7 @@ type Org struct {
LabelPolicy *LabelPolicy LabelPolicy *LabelPolicy
PasswordComplexityPolicy *PasswordComplexityPolicy PasswordComplexityPolicy *PasswordComplexityPolicy
PasswordAgePolicy *PasswordAgePolicy PasswordAgePolicy *PasswordAgePolicy
PasswordLockoutPolicy *PasswordLockoutPolicy PasswordLockoutPolicy *LockoutPolicy
IDPs []*IDPConfig IDPs []*IDPConfig
} }

View File

@ -4,9 +4,10 @@ import (
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
) )
type PasswordLockoutPolicy struct { type LockoutPolicy struct {
models.ObjectRoot models.ObjectRoot
MaxAttempts uint64 Default bool
MaxPasswordAttempts uint64
ShowLockOutFailures bool ShowLockOutFailures bool
} }

View File

@ -20,6 +20,7 @@ const (
Step15 Step15
Step16 Step16
Step17 Step17
Step18
//StepCount marks the the length of possible steps (StepCount-1 == last possible step) //StepCount marks the the length of possible steps (StepCount-1 == last possible step)
StepCount StepCount
) )

View File

@ -35,7 +35,7 @@ type IAM struct {
DefaultOrgIAMPolicy *OrgIAMPolicy DefaultOrgIAMPolicy *OrgIAMPolicy
DefaultPasswordComplexityPolicy *PasswordComplexityPolicy DefaultPasswordComplexityPolicy *PasswordComplexityPolicy
DefaultPasswordAgePolicy *PasswordAgePolicy DefaultPasswordAgePolicy *PasswordAgePolicy
DefaultPasswordLockoutPolicy *PasswordLockoutPolicy DefaultLockoutPolicy *LockoutPolicy
DefaultMailTemplate *MailTemplate DefaultMailTemplate *MailTemplate
DefaultMailTexts []*MailText DefaultMailTexts []*MailText
} }

View File

@ -4,10 +4,10 @@ import (
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
) )
type PasswordLockoutPolicy struct { type LockoutPolicy struct {
models.ObjectRoot models.ObjectRoot
State PolicyState State PolicyState
MaxAttempts uint64 MaxPasswordAttempts uint64
ShowLockOutFailures bool ShowLockOutFailures bool
} }

View File

@ -5,9 +5,9 @@ import (
"time" "time"
) )
type PasswordLockoutPolicyView struct { type LockoutPolicyView struct {
AggregateID string AggregateID string
MaxAttempts uint64 MaxPasswordAttempts uint64
ShowLockOutFailures bool ShowLockOutFailures bool
Default bool Default bool
@ -16,32 +16,32 @@ type PasswordLockoutPolicyView struct {
Sequence uint64 Sequence uint64
} }
type PasswordLockoutPolicySearchRequest struct { type LockoutPolicySearchRequest struct {
Offset uint64 Offset uint64
Limit uint64 Limit uint64
SortingColumn PasswordLockoutPolicySearchKey SortingColumn LockoutPolicySearchKey
Asc bool Asc bool
Queries []*PasswordLockoutPolicySearchQuery Queries []*LockoutPolicySearchQuery
} }
type PasswordLockoutPolicySearchKey int32 type LockoutPolicySearchKey int32
const ( const (
PasswordLockoutPolicySearchKeyUnspecified PasswordLockoutPolicySearchKey = iota LockoutPolicySearchKeyUnspecified LockoutPolicySearchKey = iota
PasswordLockoutPolicySearchKeyAggregateID LockoutPolicySearchKeyAggregateID
) )
type PasswordLockoutPolicySearchQuery struct { type LockoutPolicySearchQuery struct {
Key PasswordLockoutPolicySearchKey Key LockoutPolicySearchKey
Method domain.SearchMethod Method domain.SearchMethod
Value interface{} Value interface{}
} }
type PasswordLockoutPolicySearchResponse struct { type LockoutPolicySearchResponse struct {
Offset uint64 Offset uint64
Limit uint64 Limit uint64
TotalResult uint64 TotalResult uint64
Result []*PasswordLockoutPolicyView Result []*LockoutPolicyView
Sequence uint64 Sequence uint64
Timestamp time.Time Timestamp time.Time
} }

View File

@ -36,7 +36,7 @@ type IAM struct {
DefaultOrgIAMPolicy *OrgIAMPolicy `json:"-"` DefaultOrgIAMPolicy *OrgIAMPolicy `json:"-"`
DefaultPasswordComplexityPolicy *PasswordComplexityPolicy `json:"-"` DefaultPasswordComplexityPolicy *PasswordComplexityPolicy `json:"-"`
DefaultPasswordAgePolicy *PasswordAgePolicy `json:"-"` DefaultPasswordAgePolicy *PasswordAgePolicy `json:"-"`
DefaultPasswordLockoutPolicy *PasswordLockoutPolicy `json:"-"` DefaultLockoutPolicy *LockoutPolicy `json:"-"`
} }
func IAMToModel(iam *IAM) *model.IAM { func IAMToModel(iam *IAM) *model.IAM {
@ -66,8 +66,8 @@ func IAMToModel(iam *IAM) *model.IAM {
if iam.DefaultPasswordAgePolicy != nil { if iam.DefaultPasswordAgePolicy != nil {
converted.DefaultPasswordAgePolicy = PasswordAgePolicyToModel(iam.DefaultPasswordAgePolicy) converted.DefaultPasswordAgePolicy = PasswordAgePolicyToModel(iam.DefaultPasswordAgePolicy)
} }
if iam.DefaultPasswordLockoutPolicy != nil { if iam.DefaultLockoutPolicy != nil {
converted.DefaultPasswordLockoutPolicy = PasswordLockoutPolicyToModel(iam.DefaultPasswordLockoutPolicy) converted.DefaultLockoutPolicy = LockoutPolicyToModel(iam.DefaultLockoutPolicy)
} }
if iam.DefaultOrgIAMPolicy != nil { if iam.DefaultOrgIAMPolicy != nil {
converted.DefaultOrgIAMPolicy = OrgIAMPolicyToModel(iam.DefaultOrgIAMPolicy) converted.DefaultOrgIAMPolicy = OrgIAMPolicyToModel(iam.DefaultOrgIAMPolicy)
@ -166,10 +166,10 @@ func (i *IAM) AppendEvent(event *es_models.Event) (err error) {
return i.appendAddPasswordAgePolicyEvent(event) return i.appendAddPasswordAgePolicyEvent(event)
case PasswordAgePolicyChanged: case PasswordAgePolicyChanged:
return i.appendChangePasswordAgePolicyEvent(event) return i.appendChangePasswordAgePolicyEvent(event)
case PasswordLockoutPolicyAdded: case LockoutPolicyAdded:
return i.appendAddPasswordLockoutPolicyEvent(event) return i.appendAddLockoutPolicyEvent(event)
case PasswordLockoutPolicyChanged: case LockoutPolicyChanged:
return i.appendChangePasswordLockoutPolicyEvent(event) return i.appendChangeLockoutPolicyEvent(event)
case OrgIAMPolicyAdded: case OrgIAMPolicyAdded:
return i.appendAddOrgIAMPolicyEvent(event) return i.appendAddOrgIAMPolicyEvent(event)
case OrgIAMPolicyChanged: case OrgIAMPolicyChanged:

View File

@ -0,0 +1,60 @@
package model
import (
"encoding/json"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type LockoutPolicy struct {
es_models.ObjectRoot
State int32 `json:"-"`
MaxPasswordAttempts uint64 `json:"maxPasswordAttempts"`
ShowLockOutFailures bool `json:"showLockOutFailures"`
}
func LockoutPolicyToModel(policy *LockoutPolicy) *iam_model.LockoutPolicy {
return &iam_model.LockoutPolicy{
ObjectRoot: policy.ObjectRoot,
State: iam_model.PolicyState(policy.State),
MaxPasswordAttempts: policy.MaxPasswordAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures,
}
}
func (p *LockoutPolicy) Changes(changed *LockoutPolicy) map[string]interface{} {
changes := make(map[string]interface{}, 2)
if p.MaxPasswordAttempts != changed.MaxPasswordAttempts {
changes["maxAttempts"] = changed.MaxPasswordAttempts
}
if p.ShowLockOutFailures != changed.ShowLockOutFailures {
changes["showLockOutFailures"] = changed.ShowLockOutFailures
}
return changes
}
func (i *IAM) appendAddLockoutPolicyEvent(event *es_models.Event) error {
i.DefaultLockoutPolicy = new(LockoutPolicy)
err := i.DefaultLockoutPolicy.SetData(event)
if err != nil {
return err
}
i.DefaultLockoutPolicy.ObjectRoot.CreationDate = event.CreationDate
return nil
}
func (i *IAM) appendChangeLockoutPolicyEvent(event *es_models.Event) error {
return i.DefaultLockoutPolicy.SetData(event)
}
func (p *LockoutPolicy) SetData(event *es_models.Event) error {
err := json.Unmarshal(event.Data, p)
if err != nil {
return errors.ThrowInternal(err, "EVENT-7JS9d", "unable to unmarshal data")
}
return nil
}

View File

@ -8,8 +8,8 @@ import (
func TestPasswordLockoutPolicyChanges(t *testing.T) { func TestPasswordLockoutPolicyChanges(t *testing.T) {
type args struct { type args struct {
existing *PasswordLockoutPolicy existing *LockoutPolicy
new *PasswordLockoutPolicy new *LockoutPolicy
} }
type res struct { type res struct {
changesLen int changesLen int
@ -22,8 +22,8 @@ func TestPasswordLockoutPolicyChanges(t *testing.T) {
{ {
name: "lockout policy all attributes change", name: "lockout policy all attributes change",
args: args{ args: args{
existing: &PasswordLockoutPolicy{MaxAttempts: 365, ShowLockOutFailures: true}, existing: &LockoutPolicy{MaxPasswordAttempts: 365, ShowLockOutFailures: true},
new: &PasswordLockoutPolicy{MaxAttempts: 730, ShowLockOutFailures: false}, new: &LockoutPolicy{MaxPasswordAttempts: 730, ShowLockOutFailures: false},
}, },
res: res{ res: res{
changesLen: 2, changesLen: 2,
@ -32,8 +32,8 @@ func TestPasswordLockoutPolicyChanges(t *testing.T) {
{ {
name: "no changes", name: "no changes",
args: args{ args: args{
existing: &PasswordLockoutPolicy{MaxAttempts: 10, ShowLockOutFailures: true}, existing: &LockoutPolicy{MaxPasswordAttempts: 10, ShowLockOutFailures: true},
new: &PasswordLockoutPolicy{MaxAttempts: 10, ShowLockOutFailures: true}, new: &LockoutPolicy{MaxPasswordAttempts: 10, ShowLockOutFailures: true},
}, },
res: res{ res: res{
changesLen: 0, changesLen: 0,
@ -53,7 +53,7 @@ func TestPasswordLockoutPolicyChanges(t *testing.T) {
func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) { func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
type args struct { type args struct {
iam *IAM iam *IAM
policy *PasswordLockoutPolicy policy *LockoutPolicy
event *es_models.Event event *es_models.Event
} }
tests := []struct { tests := []struct {
@ -65,10 +65,10 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
name: "append add password lockout policy event", name: "append add password lockout policy event",
args: args{ args: args{
iam: new(IAM), iam: new(IAM),
policy: &PasswordLockoutPolicy{MaxAttempts: 10, ShowLockOutFailures: true}, policy: &LockoutPolicy{MaxPasswordAttempts: 10, ShowLockOutFailures: true},
event: new(es_models.Event), event: new(es_models.Event),
}, },
result: &IAM{DefaultPasswordLockoutPolicy: &PasswordLockoutPolicy{MaxAttempts: 10, ShowLockOutFailures: true}}, result: &IAM{DefaultLockoutPolicy: &LockoutPolicy{MaxPasswordAttempts: 10, ShowLockOutFailures: true}},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -77,12 +77,12 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
data, _ := json.Marshal(tt.args.policy) data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data tt.args.event.Data = data
} }
tt.args.iam.appendAddPasswordLockoutPolicyEvent(tt.args.event) tt.args.iam.appendAddLockoutPolicyEvent(tt.args.event)
if tt.result.DefaultPasswordLockoutPolicy.MaxAttempts != tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts { if tt.result.DefaultLockoutPolicy.MaxPasswordAttempts != tt.args.iam.DefaultLockoutPolicy.MaxPasswordAttempts {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultPasswordLockoutPolicy.MaxAttempts, tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts) t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultLockoutPolicy.MaxPasswordAttempts, tt.args.iam.DefaultLockoutPolicy.MaxPasswordAttempts)
} }
if tt.result.DefaultPasswordLockoutPolicy.ShowLockOutFailures != tt.args.iam.DefaultPasswordLockoutPolicy.ShowLockOutFailures { if tt.result.DefaultLockoutPolicy.ShowLockOutFailures != tt.args.iam.DefaultLockoutPolicy.ShowLockOutFailures {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultPasswordLockoutPolicy.ShowLockOutFailures, tt.args.iam.DefaultPasswordLockoutPolicy.ShowLockOutFailures) t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultLockoutPolicy.ShowLockOutFailures, tt.args.iam.DefaultLockoutPolicy.ShowLockOutFailures)
} }
}) })
} }
@ -91,7 +91,7 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) { func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
type args struct { type args struct {
iam *IAM iam *IAM
policy *PasswordLockoutPolicy policy *LockoutPolicy
event *es_models.Event event *es_models.Event
} }
tests := []struct { tests := []struct {
@ -102,14 +102,14 @@ func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
{ {
name: "append change password lockout policy event", name: "append change password lockout policy event",
args: args{ args: args{
iam: &IAM{DefaultPasswordLockoutPolicy: &PasswordLockoutPolicy{ iam: &IAM{DefaultLockoutPolicy: &LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
}}, }},
policy: &PasswordLockoutPolicy{MaxAttempts: 5}, policy: &LockoutPolicy{MaxPasswordAttempts: 5},
event: &es_models.Event{}, event: &es_models.Event{},
}, },
result: &IAM{DefaultPasswordLockoutPolicy: &PasswordLockoutPolicy{ result: &IAM{DefaultLockoutPolicy: &LockoutPolicy{
MaxAttempts: 5, MaxPasswordAttempts: 5,
}}, }},
}, },
} }
@ -119,9 +119,9 @@ func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
data, _ := json.Marshal(tt.args.policy) data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data tt.args.event.Data = data
} }
tt.args.iam.appendChangePasswordLockoutPolicyEvent(tt.args.event) tt.args.iam.appendChangeLockoutPolicyEvent(tt.args.event)
if tt.result.DefaultPasswordLockoutPolicy.MaxAttempts != tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts { if tt.result.DefaultLockoutPolicy.MaxPasswordAttempts != tt.args.iam.DefaultLockoutPolicy.MaxPasswordAttempts {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultPasswordLockoutPolicy.MaxAttempts, tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts) t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultLockoutPolicy.MaxPasswordAttempts, tt.args.iam.DefaultLockoutPolicy.MaxPasswordAttempts)
} }
}) })
} }

View File

@ -1,69 +0,0 @@
package model
import (
"encoding/json"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type PasswordLockoutPolicy struct {
es_models.ObjectRoot
State int32 `json:"-"`
MaxAttempts uint64 `json:"maxAttempts"`
ShowLockOutFailures bool `json:"showLockOutFailures"`
}
func PasswordLockoutPolicyFromModel(policy *iam_model.PasswordLockoutPolicy) *PasswordLockoutPolicy {
return &PasswordLockoutPolicy{
ObjectRoot: policy.ObjectRoot,
State: int32(policy.State),
MaxAttempts: policy.MaxAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures,
}
}
func PasswordLockoutPolicyToModel(policy *PasswordLockoutPolicy) *iam_model.PasswordLockoutPolicy {
return &iam_model.PasswordLockoutPolicy{
ObjectRoot: policy.ObjectRoot,
State: iam_model.PolicyState(policy.State),
MaxAttempts: policy.MaxAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures,
}
}
func (p *PasswordLockoutPolicy) Changes(changed *PasswordLockoutPolicy) map[string]interface{} {
changes := make(map[string]interface{}, 2)
if p.MaxAttempts != changed.MaxAttempts {
changes["maxAttempts"] = changed.MaxAttempts
}
if p.ShowLockOutFailures != changed.ShowLockOutFailures {
changes["showLockOutFailures"] = changed.ShowLockOutFailures
}
return changes
}
func (i *IAM) appendAddPasswordLockoutPolicyEvent(event *es_models.Event) error {
i.DefaultPasswordLockoutPolicy = new(PasswordLockoutPolicy)
err := i.DefaultPasswordLockoutPolicy.SetData(event)
if err != nil {
return err
}
i.DefaultPasswordLockoutPolicy.ObjectRoot.CreationDate = event.CreationDate
return nil
}
func (i *IAM) appendChangePasswordLockoutPolicyEvent(event *es_models.Event) error {
return i.DefaultPasswordLockoutPolicy.SetData(event)
}
func (p *PasswordLockoutPolicy) SetData(event *es_models.Event) error {
err := json.Unmarshal(event.Data, p)
if err != nil {
return errors.ThrowInternal(err, "EVENT-7JS9d", "unable to unmarshal data")
}
return nil
}

View File

@ -65,8 +65,8 @@ const (
PasswordAgePolicyAdded models.EventType = "iam.policy.password.age.added" PasswordAgePolicyAdded models.EventType = "iam.policy.password.age.added"
PasswordAgePolicyChanged models.EventType = "iam.policy.password.age.changed" PasswordAgePolicyChanged models.EventType = "iam.policy.password.age.changed"
PasswordLockoutPolicyAdded models.EventType = "iam.policy.password.lockout.added" LockoutPolicyAdded models.EventType = "iam.policy.lockout.added"
PasswordLockoutPolicyChanged models.EventType = "iam.policy.password.lockout.changed" LockoutPolicyChanged models.EventType = "iam.policy.lockout.changed"
PrivacyPolicyAdded models.EventType = "iam.policy.privacy.added" PrivacyPolicyAdded models.EventType = "iam.policy.privacy.added"
PrivacyPolicyChanged models.EventType = "iam.policy.privacy.changed" PrivacyPolicyChanged models.EventType = "iam.policy.privacy.changed"

View File

@ -2,6 +2,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"github.com/caos/zitadel/internal/domain"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"time" "time"
@ -14,65 +15,67 @@ import (
) )
const ( const (
PasswordLockoutKeyAggregateID = "aggregate_id" LockoutKeyAggregateID = "aggregate_id"
) )
type PasswordLockoutPolicyView struct { type LockoutPolicyView struct {
AggregateID string `json:"-" gorm:"column:aggregate_id;primary_key"` AggregateID string `json:"-" gorm:"column:aggregate_id;primary_key"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
State int32 `json:"-" gorm:"column:lockout_policy_state"` State int32 `json:"-" gorm:"column:lockout_policy_state"`
MaxAttempts uint64 `json:"maxAttempts" gorm:"column:max_attempts"` MaxPasswordAttempts uint64 `json:"maxPasswordAttempts" gorm:"column:max_password_attempts"`
ShowLockOutFailures bool `json:"showLockOutFailures" gorm:"column:show_lockout_failures"` ShowLockOutFailures bool `json:"showLockOutFailures" gorm:"column:show_lockout_failures"`
Default bool `json:"-" gorm:"-"` Default bool `json:"-" gorm:"-"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
} }
func PasswordLockoutViewFromModel(policy *model.PasswordLockoutPolicyView) *PasswordLockoutPolicyView { func LockoutViewToModel(policy *LockoutPolicyView) *model.LockoutPolicyView {
return &PasswordLockoutPolicyView{ return &model.LockoutPolicyView{
AggregateID: policy.AggregateID, AggregateID: policy.AggregateID,
Sequence: policy.Sequence, Sequence: policy.Sequence,
CreationDate: policy.CreationDate, CreationDate: policy.CreationDate,
ChangeDate: policy.ChangeDate, ChangeDate: policy.ChangeDate,
MaxAttempts: policy.MaxAttempts, MaxPasswordAttempts: policy.MaxPasswordAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures, ShowLockOutFailures: policy.ShowLockOutFailures,
Default: policy.Default, Default: policy.Default,
} }
} }
func PasswordLockoutViewToModel(policy *PasswordLockoutPolicyView) *model.PasswordLockoutPolicyView { func (p *LockoutPolicyView) ToDomain() *domain.LockoutPolicy {
return &model.PasswordLockoutPolicyView{ return &domain.LockoutPolicy{
AggregateID: policy.AggregateID, ObjectRoot: models.ObjectRoot{
Sequence: policy.Sequence, AggregateID: p.AggregateID,
CreationDate: policy.CreationDate, CreationDate: p.CreationDate,
ChangeDate: policy.ChangeDate, ChangeDate: p.ChangeDate,
MaxAttempts: policy.MaxAttempts, Sequence: p.Sequence,
ShowLockOutFailures: policy.ShowLockOutFailures, },
Default: policy.Default, MaxPasswordAttempts: p.MaxPasswordAttempts,
ShowLockOutFailures: p.ShowLockOutFailures,
Default: p.Default,
} }
} }
func (i *PasswordLockoutPolicyView) AppendEvent(event *models.Event) (err error) { func (i *LockoutPolicyView) AppendEvent(event *models.Event) (err error) {
i.Sequence = event.Sequence i.Sequence = event.Sequence
i.ChangeDate = event.CreationDate i.ChangeDate = event.CreationDate
switch event.Type { switch event.Type {
case es_model.PasswordLockoutPolicyAdded, org_es_model.PasswordLockoutPolicyAdded: case es_model.LockoutPolicyAdded, org_es_model.LockoutPolicyAdded:
i.setRootData(event) i.setRootData(event)
i.CreationDate = event.CreationDate i.CreationDate = event.CreationDate
err = i.SetData(event) err = i.SetData(event)
case es_model.PasswordLockoutPolicyChanged, org_es_model.PasswordLockoutPolicyChanged: case es_model.LockoutPolicyChanged, org_es_model.LockoutPolicyChanged:
err = i.SetData(event) err = i.SetData(event)
} }
return err return err
} }
func (r *PasswordLockoutPolicyView) setRootData(event *models.Event) { func (r *LockoutPolicyView) setRootData(event *models.Event) {
r.AggregateID = event.AggregateID r.AggregateID = event.AggregateID
} }
func (r *PasswordLockoutPolicyView) SetData(event *models.Event) error { func (r *LockoutPolicyView) SetData(event *models.Event) error {
if err := json.Unmarshal(event.Data, r); err != nil { if err := json.Unmarshal(event.Data, r); err != nil {
logging.Log("EVEN-gHls0").WithError(err).Error("could not unmarshal event data") logging.Log("EVEN-gHls0").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "MODEL-Hs8uf", "Could not unmarshal data") return caos_errs.ThrowInternal(err, "MODEL-Hs8uf", "Could not unmarshal data")

View File

@ -6,53 +6,53 @@ import (
"github.com/caos/zitadel/internal/view/repository" "github.com/caos/zitadel/internal/view/repository"
) )
type PasswordLockoutPolicySearchRequest iam_model.PasswordLockoutPolicySearchRequest type LockoutPolicySearchRequest iam_model.LockoutPolicySearchRequest
type PasswordLockoutPolicySearchQuery iam_model.PasswordLockoutPolicySearchQuery type LockoutPolicySearchQuery iam_model.LockoutPolicySearchQuery
type PasswordLockoutPolicySearchKey iam_model.PasswordLockoutPolicySearchKey type LockoutPolicySearchKey iam_model.LockoutPolicySearchKey
func (req PasswordLockoutPolicySearchRequest) GetLimit() uint64 { func (req LockoutPolicySearchRequest) GetLimit() uint64 {
return req.Limit return req.Limit
} }
func (req PasswordLockoutPolicySearchRequest) GetOffset() uint64 { func (req LockoutPolicySearchRequest) GetOffset() uint64 {
return req.Offset return req.Offset
} }
func (req PasswordLockoutPolicySearchRequest) GetSortingColumn() repository.ColumnKey { func (req LockoutPolicySearchRequest) GetSortingColumn() repository.ColumnKey {
if req.SortingColumn == iam_model.PasswordLockoutPolicySearchKeyUnspecified { if req.SortingColumn == iam_model.LockoutPolicySearchKeyUnspecified {
return nil return nil
} }
return PasswordLockoutPolicySearchKey(req.SortingColumn) return LockoutPolicySearchKey(req.SortingColumn)
} }
func (req PasswordLockoutPolicySearchRequest) GetAsc() bool { func (req LockoutPolicySearchRequest) GetAsc() bool {
return req.Asc return req.Asc
} }
func (req PasswordLockoutPolicySearchRequest) GetQueries() []repository.SearchQuery { func (req LockoutPolicySearchRequest) GetQueries() []repository.SearchQuery {
result := make([]repository.SearchQuery, len(req.Queries)) result := make([]repository.SearchQuery, len(req.Queries))
for i, q := range req.Queries { for i, q := range req.Queries {
result[i] = PasswordLockoutPolicySearchQuery{Key: q.Key, Value: q.Value, Method: q.Method} result[i] = LockoutPolicySearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
} }
return result return result
} }
func (req PasswordLockoutPolicySearchQuery) GetKey() repository.ColumnKey { func (req LockoutPolicySearchQuery) GetKey() repository.ColumnKey {
return PasswordLockoutPolicySearchKey(req.Key) return LockoutPolicySearchKey(req.Key)
} }
func (req PasswordLockoutPolicySearchQuery) GetMethod() domain.SearchMethod { func (req LockoutPolicySearchQuery) GetMethod() domain.SearchMethod {
return req.Method return req.Method
} }
func (req PasswordLockoutPolicySearchQuery) GetValue() interface{} { func (req LockoutPolicySearchQuery) GetValue() interface{} {
return req.Value return req.Value
} }
func (key PasswordLockoutPolicySearchKey) ToColumnName() string { func (key LockoutPolicySearchKey) ToColumnName() string {
switch iam_model.PasswordLockoutPolicySearchKey(key) { switch iam_model.LockoutPolicySearchKey(key) {
case iam_model.PasswordLockoutPolicySearchKeyAggregateID: case iam_model.LockoutPolicySearchKeyAggregateID:
return PasswordLockoutKeyAggregateID return LockoutKeyAggregateID
default: default:
return "" return ""
} }

View File

@ -9,24 +9,24 @@ import (
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
) )
func GetPasswordLockoutPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.PasswordLockoutPolicyView, error) { func GetLockoutPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.LockoutPolicyView, error) {
policy := new(model.PasswordLockoutPolicyView) policy := new(model.LockoutPolicyView)
aggregateIDQuery := &model.PasswordLockoutPolicySearchQuery{Key: iam_model.PasswordLockoutPolicySearchKeyAggregateID, Value: aggregateID, Method: domain.SearchMethodEquals} aggregateIDQuery := &model.LockoutPolicySearchQuery{Key: iam_model.LockoutPolicySearchKeyAggregateID, Value: aggregateID, Method: domain.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery) query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy) err := query(db, policy)
if caos_errs.IsNotFound(err) { if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-M9fsf", "Errors.IAM.PasswordLockoutPolicy.NotExisting") return nil, caos_errs.ThrowNotFound(nil, "VIEW-M9fsf", "Errors.IAM.LockoutPolicy.NotExisting")
} }
return policy, err return policy, err
} }
func PutPasswordLockoutPolicy(db *gorm.DB, table string, policy *model.PasswordLockoutPolicyView) error { func PutLockoutPolicy(db *gorm.DB, table string, policy *model.LockoutPolicyView) error {
save := repository.PrepareSave(table) save := repository.PrepareSave(table)
return save(db, policy) return save(db, policy)
} }
func DeletePasswordLockoutPolicy(db *gorm.DB, table, aggregateID string) error { func DeleteLockoutPolicy(db *gorm.DB, table, aggregateID string) error {
delete := repository.PrepareDeleteByKey(table, model.PasswordLockoutPolicySearchKey(iam_model.PasswordLockoutPolicySearchKeyAggregateID), aggregateID) delete := repository.PrepareDeleteByKey(table, model.LockoutPolicySearchKey(iam_model.LockoutPolicySearchKeyAggregateID), aggregateID)
return delete(db) return delete(db)
} }

View File

@ -501,55 +501,55 @@ func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*ia
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_es_model.PasswordAgeViewToModel(policy), nil
} }
func (repo *OrgRepository) GetPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) { func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
policy, viewErr := repo.View.PasswordLockoutPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) policy, viewErr := repo.View.LockoutPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if viewErr != nil && !errors.IsNotFound(viewErr) { if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordLockoutPolicyView) policy = new(iam_es_model.LockoutPolicyView)
} }
events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence) events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
return repo.GetDefaultPasswordLockoutPolicy(ctx) return repo.GetDefaultLockoutPolicy(ctx)
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-mS9od").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-mS9od").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
} }
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
func (repo *OrgRepository) GetDefaultPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) { func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
policy, viewErr := repo.View.PasswordLockoutPolicyByAggregateID(repo.SystemDefaults.IamID) policy, viewErr := repo.View.LockoutPolicyByAggregateID(repo.SystemDefaults.IamID)
if viewErr != nil && !errors.IsNotFound(viewErr) { if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordLockoutPolicyView) policy = new(iam_es_model.LockoutPolicyView)
} }
events, esErr := repo.getIAMEvents(ctx, policy.Sequence) events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
return nil, errors.ThrowNotFound(nil, "EVENT-cmO9s", "Errors.IAM.PasswordLockoutPolicy.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-cmO9s", "Errors.IAM.LockoutPolicy.NotFound")
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-2Ms9f").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-2Ms9f").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
} }
policy.Default = true policy.Default = true
return iam_es_model.PasswordLockoutViewToModel(policy), nil return iam_es_model.LockoutViewToModel(policy), nil
} }
func (repo *OrgRepository) GetPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) { func (repo *OrgRepository) GetPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) {

View File

@ -71,8 +71,8 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}),
newPasswordAgePolicy( newPasswordAgePolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}),
newPasswordLockoutPolicy( newLockoutPolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("LockoutPolicy"), errorCount, es}),
newOrgIAMPolicy( newOrgIAMPolicy(
handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}),
newMailTemplate( newMailTemplate(

View File

@ -13,16 +13,16 @@ import (
) )
const ( const (
passwordLockoutPolicyTable = "management.password_lockout_policies" lockoutPolicyTable = "management.lockout_policies"
) )
type PasswordLockoutPolicy struct { type LockoutPolicy struct {
handler handler
subscription *v1.Subscription subscription *v1.Subscription
} }
func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy { func newLockoutPolicy(handler handler) *LockoutPolicy {
h := &PasswordLockoutPolicy{ h := &LockoutPolicy{
handler: handler, handler: handler,
} }
@ -31,7 +31,7 @@ func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy {
return h return h
} }
func (m *PasswordLockoutPolicy) subscribe() { func (m *LockoutPolicy) subscribe() {
m.subscription = m.es.Subscribe(m.AggregateTypes()...) m.subscription = m.es.Subscribe(m.AggregateTypes()...)
go func() { go func() {
for event := range m.subscription.Events { for event := range m.subscription.Events {
@ -40,28 +40,28 @@ func (m *PasswordLockoutPolicy) subscribe() {
}() }()
} }
func (p *PasswordLockoutPolicy) ViewModel() string { func (p *LockoutPolicy) ViewModel() string {
return passwordLockoutPolicyTable return lockoutPolicyTable
} }
func (p *PasswordLockoutPolicy) Subscription() *v1.Subscription { func (p *LockoutPolicy) Subscription() *v1.Subscription {
return p.subscription return p.subscription
} }
func (_ *PasswordLockoutPolicy) AggregateTypes() []es_models.AggregateType { func (_ *LockoutPolicy) AggregateTypes() []es_models.AggregateType {
return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate}
} }
func (p *PasswordLockoutPolicy) CurrentSequence() (uint64, error) { func (p *LockoutPolicy) CurrentSequence() (uint64, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil { if err != nil {
return 0, err return 0, err
} }
return sequence.CurrentSequence, nil return sequence.CurrentSequence, nil
} }
func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) { func (p *LockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -70,7 +70,7 @@ func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
LatestSequenceFilter(sequence.CurrentSequence), nil LatestSequenceFilter(sequence.CurrentSequence), nil
} }
func (p *PasswordLockoutPolicy) Reduce(event *es_models.Event) (err error) { func (p *LockoutPolicy) Reduce(event *es_models.Event) (err error) {
switch event.AggregateType { switch event.AggregateType {
case model.OrgAggregate, iam_es_model.IAMAggregate: case model.OrgAggregate, iam_es_model.IAMAggregate:
err = p.processPasswordLockoutPolicy(event) err = p.processPasswordLockoutPolicy(event)
@ -78,33 +78,33 @@ func (p *PasswordLockoutPolicy) Reduce(event *es_models.Event) (err error) {
return err return err
} }
func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *es_models.Event) (err error) { func (p *LockoutPolicy) processPasswordLockoutPolicy(event *es_models.Event) (err error) {
policy := new(iam_model.PasswordLockoutPolicyView) policy := new(iam_model.LockoutPolicyView)
switch event.Type { switch event.Type {
case iam_es_model.PasswordLockoutPolicyAdded, model.PasswordLockoutPolicyAdded: case iam_es_model.LockoutPolicyAdded, model.LockoutPolicyAdded:
err = policy.AppendEvent(event) err = policy.AppendEvent(event)
case iam_es_model.PasswordLockoutPolicyChanged, model.PasswordLockoutPolicyChanged: case iam_es_model.LockoutPolicyChanged, model.LockoutPolicyChanged:
policy, err = p.view.PasswordLockoutPolicyByAggregateID(event.AggregateID) policy, err = p.view.LockoutPolicyByAggregateID(event.AggregateID)
if err != nil { if err != nil {
return err return err
} }
err = policy.AppendEvent(event) err = policy.AppendEvent(event)
case model.PasswordLockoutPolicyRemoved: case model.LockoutPolicyRemoved:
return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event) return p.view.DeleteLockoutPolicy(event.AggregateID, event)
default: default:
return p.view.ProcessedPasswordLockoutPolicySequence(event) return p.view.ProcessedLockoutPolicySequence(event)
} }
if err != nil { if err != nil {
return err return err
} }
return p.view.PutPasswordLockoutPolicy(policy, event) return p.view.PutLockoutPolicy(policy, event)
} }
func (p *PasswordLockoutPolicy) OnError(event *es_models.Event, err error) error { func (p *LockoutPolicy) OnError(event *es_models.Event, err error) error {
logging.LogWithFields("SPOOL-Bms8f", "id", event.AggregateID).WithError(err).Warn("something went wrong in passwordLockout policy handler") logging.LogWithFields("SPOOL-Bms8f", "id", event.AggregateID).WithError(err).Warn("something went wrong in passwordLockout policy handler")
return spooler.HandleError(event, err, p.view.GetLatestPasswordLockoutPolicyFailedEvent, p.view.ProcessedPasswordLockoutPolicyFailedEvent, p.view.ProcessedPasswordLockoutPolicySequence, p.errorCountUntilSkip) return spooler.HandleError(event, err, p.view.GetLatestLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicySequence, p.errorCountUntilSkip)
} }
func (p *PasswordLockoutPolicy) OnSuccess() error { func (p *LockoutPolicy) OnSuccess() error {
return spooler.HandleSuccess(p.view.UpdatePasswordLockoutPolicySpoolerRunTimestamp) return spooler.HandleSuccess(p.view.UpdateLockoutPolicySpoolerRunTimestamp)
} }

View File

@ -0,0 +1,53 @@
package view
import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/repository/view"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_view "github.com/caos/zitadel/internal/view/repository"
)
const (
lockoutPolicyTable = "management.lockout_policies"
)
func (v *View) LockoutPolicyByAggregateID(aggregateID string) (*model.LockoutPolicyView, error) {
return view.GetLockoutPolicyByAggregateID(v.Db, lockoutPolicyTable, aggregateID)
}
func (v *View) PutLockoutPolicy(policy *model.LockoutPolicyView, event *models.Event) error {
err := view.PutLockoutPolicy(v.Db, lockoutPolicyTable, policy)
if err != nil {
return err
}
return v.ProcessedLockoutPolicySequence(event)
}
func (v *View) DeleteLockoutPolicy(aggregateID string, event *models.Event) error {
err := view.DeleteLockoutPolicy(v.Db, lockoutPolicyTable, aggregateID)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedLockoutPolicySequence(event)
}
func (v *View) GetLatestLockoutPolicySequence() (*global_view.CurrentSequence, error) {
return v.latestSequence(lockoutPolicyTable)
}
func (v *View) ProcessedLockoutPolicySequence(event *models.Event) error {
return v.saveCurrentSequence(lockoutPolicyTable, event)
}
func (v *View) UpdateLockoutPolicySpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(lockoutPolicyTable)
}
func (v *View) GetLatestLockoutPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
return v.latestFailedEvent(lockoutPolicyTable, sequence)
}
func (v *View) ProcessedLockoutPolicyFailedEvent(failedEvent *global_view.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -1,53 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/repository/view"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_view "github.com/caos/zitadel/internal/view/repository"
)
const (
passwordLockoutPolicyTable = "management.password_lockout_policies"
)
func (v *View) PasswordLockoutPolicyByAggregateID(aggregateID string) (*model.PasswordLockoutPolicyView, error) {
return view.GetPasswordLockoutPolicyByAggregateID(v.Db, passwordLockoutPolicyTable, aggregateID)
}
func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, event *models.Event) error {
err := view.PutPasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, policy)
if err != nil {
return err
}
return v.ProcessedPasswordLockoutPolicySequence(event)
}
func (v *View) DeletePasswordLockoutPolicy(aggregateID string, event *models.Event) error {
err := view.DeletePasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, aggregateID)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedPasswordLockoutPolicySequence(event)
}
func (v *View) GetLatestPasswordLockoutPolicySequence() (*global_view.CurrentSequence, error) {
return v.latestSequence(passwordLockoutPolicyTable)
}
func (v *View) ProcessedPasswordLockoutPolicySequence(event *models.Event) error {
return v.saveCurrentSequence(passwordLockoutPolicyTable, event)
}
func (v *View) UpdatePasswordLockoutPolicySpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(passwordLockoutPolicyTable)
}
func (v *View) GetLatestPasswordLockoutPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
return v.latestFailedEvent(passwordLockoutPolicyTable, sequence)
}
func (v *View) ProcessedPasswordLockoutPolicyFailedEvent(failedEvent *global_view.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -42,8 +42,8 @@ type OrgRepository interface {
GetPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error) GetPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error)
GetDefaultPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error) GetDefaultPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error)
GetPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) GetLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error)
GetDefaultPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error)
GetPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) GetPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error)
GetDefaultPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) GetDefaultPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error)

View File

@ -24,7 +24,7 @@ type Org struct {
MailTexts []*iam_model.MailText MailTexts []*iam_model.MailText
PasswordComplexityPolicy *iam_model.PasswordComplexityPolicy PasswordComplexityPolicy *iam_model.PasswordComplexityPolicy
PasswordAgePolicy *iam_model.PasswordAgePolicy PasswordAgePolicy *iam_model.PasswordAgePolicy
PasswordLockoutPolicy *iam_model.PasswordLockoutPolicy LockoutPolicy *iam_model.LockoutPolicy
IDPs []*iam_model.IDPConfig IDPs []*iam_model.IDPConfig
} }

View File

@ -30,7 +30,7 @@ type Org struct {
LoginPolicy *iam_es_model.LoginPolicy `json:"-"` LoginPolicy *iam_es_model.LoginPolicy `json:"-"`
PasswordComplexityPolicy *iam_es_model.PasswordComplexityPolicy `json:"-"` PasswordComplexityPolicy *iam_es_model.PasswordComplexityPolicy `json:"-"`
PasswordAgePolicy *iam_es_model.PasswordAgePolicy `json:"-"` PasswordAgePolicy *iam_es_model.PasswordAgePolicy `json:"-"`
PasswordLockoutPolicy *iam_es_model.PasswordLockoutPolicy `json:"-"` LockoutPolicy *iam_es_model.LockoutPolicy `json:"-"`
} }
func OrgToModel(org *Org) *org_model.Org { func OrgToModel(org *Org) *org_model.Org {
@ -60,8 +60,8 @@ func OrgToModel(org *Org) *org_model.Org {
if org.PasswordAgePolicy != nil { if org.PasswordAgePolicy != nil {
converted.PasswordAgePolicy = iam_es_model.PasswordAgePolicyToModel(org.PasswordAgePolicy) converted.PasswordAgePolicy = iam_es_model.PasswordAgePolicyToModel(org.PasswordAgePolicy)
} }
if org.PasswordLockoutPolicy != nil { if org.LockoutPolicy != nil {
converted.PasswordLockoutPolicy = iam_es_model.PasswordLockoutPolicyToModel(org.PasswordLockoutPolicy) converted.LockoutPolicy = iam_es_model.LockoutPolicyToModel(org.LockoutPolicy)
} }
return converted return converted
} }
@ -196,12 +196,12 @@ func (o *Org) AppendEvent(event *es_models.Event) (err error) {
err = o.appendChangePasswordAgePolicyEvent(event) err = o.appendChangePasswordAgePolicyEvent(event)
case PasswordAgePolicyRemoved: case PasswordAgePolicyRemoved:
o.appendRemovePasswordAgePolicyEvent(event) o.appendRemovePasswordAgePolicyEvent(event)
case PasswordLockoutPolicyAdded: case LockoutPolicyAdded:
err = o.appendAddPasswordLockoutPolicyEvent(event) err = o.appendAddLockoutPolicyEvent(event)
case PasswordLockoutPolicyChanged: case LockoutPolicyChanged:
err = o.appendChangePasswordLockoutPolicyEvent(event) err = o.appendChangeLockoutPolicyEvent(event)
case PasswordLockoutPolicyRemoved: case LockoutPolicyRemoved:
o.appendRemovePasswordLockoutPolicyEvent(event) o.appendRemoveLockoutPolicyEvent(event)
} }
if err != nil { if err != nil {
return err return err

View File

@ -5,20 +5,20 @@ import (
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
) )
func (o *Org) appendAddPasswordLockoutPolicyEvent(event *es_models.Event) error { func (o *Org) appendAddLockoutPolicyEvent(event *es_models.Event) error {
o.PasswordLockoutPolicy = new(iam_es_model.PasswordLockoutPolicy) o.LockoutPolicy = new(iam_es_model.LockoutPolicy)
err := o.PasswordLockoutPolicy.SetData(event) err := o.LockoutPolicy.SetData(event)
if err != nil { if err != nil {
return err return err
} }
o.PasswordLockoutPolicy.ObjectRoot.CreationDate = event.CreationDate o.LockoutPolicy.ObjectRoot.CreationDate = event.CreationDate
return nil return nil
} }
func (o *Org) appendChangePasswordLockoutPolicyEvent(event *es_models.Event) error { func (o *Org) appendChangeLockoutPolicyEvent(event *es_models.Event) error {
return o.PasswordLockoutPolicy.SetData(event) return o.LockoutPolicy.SetData(event)
} }
func (o *Org) appendRemovePasswordLockoutPolicyEvent(event *es_models.Event) { func (o *Org) appendRemoveLockoutPolicyEvent(event *es_models.Event) {
o.PasswordLockoutPolicy = nil o.LockoutPolicy = nil
} }

View File

@ -7,10 +7,10 @@ import (
"testing" "testing"
) )
func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) { func TestAppendAddLockoutPolicyEvent(t *testing.T) {
type args struct { type args struct {
org *Org org *Org
policy *iam_es_model.PasswordLockoutPolicy policy *iam_es_model.LockoutPolicy
event *es_models.Event event *es_models.Event
} }
tests := []struct { tests := []struct {
@ -19,13 +19,13 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
result *Org result *Org
}{ }{
{ {
name: "append add password age policy event", name: "append add lockout policy event",
args: args{ args: args{
org: &Org{}, org: &Org{},
policy: &iam_es_model.PasswordLockoutPolicy{MaxAttempts: 10}, policy: &iam_es_model.LockoutPolicy{MaxPasswordAttempts: 10},
event: &es_models.Event{}, event: &es_models.Event{},
}, },
result: &Org{PasswordLockoutPolicy: &iam_es_model.PasswordLockoutPolicy{MaxAttempts: 10}}, result: &Org{LockoutPolicy: &iam_es_model.LockoutPolicy{MaxPasswordAttempts: 10}},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -34,18 +34,18 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
data, _ := json.Marshal(tt.args.policy) data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data tt.args.event.Data = data
} }
tt.args.org.appendAddPasswordLockoutPolicyEvent(tt.args.event) tt.args.org.appendAddLockoutPolicyEvent(tt.args.event)
if tt.result.PasswordLockoutPolicy.MaxAttempts != tt.args.org.PasswordLockoutPolicy.MaxAttempts { if tt.result.LockoutPolicy.MaxPasswordAttempts != tt.args.org.LockoutPolicy.MaxPasswordAttempts {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.PasswordLockoutPolicy.MaxAttempts, tt.args.org.PasswordLockoutPolicy.MaxAttempts) t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.LockoutPolicy.MaxPasswordAttempts, tt.args.org.LockoutPolicy.MaxPasswordAttempts)
} }
}) })
} }
} }
func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) { func TestAppendChangeLockoutPolicyEvent(t *testing.T) {
type args struct { type args struct {
org *Org org *Org
policy *iam_es_model.PasswordLockoutPolicy policy *iam_es_model.LockoutPolicy
event *es_models.Event event *es_models.Event
} }
tests := []struct { tests := []struct {
@ -54,16 +54,16 @@ func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
result *Org result *Org
}{ }{
{ {
name: "append change password age policy event", name: "append change lockout policy event",
args: args{ args: args{
org: &Org{PasswordLockoutPolicy: &iam_es_model.PasswordLockoutPolicy{ org: &Org{LockoutPolicy: &iam_es_model.LockoutPolicy{
MaxAttempts: 10, MaxPasswordAttempts: 10,
}}, }},
policy: &iam_es_model.PasswordLockoutPolicy{MaxAttempts: 5, ShowLockOutFailures: true}, policy: &iam_es_model.LockoutPolicy{MaxPasswordAttempts: 5, ShowLockOutFailures: true},
event: &es_models.Event{}, event: &es_models.Event{},
}, },
result: &Org{PasswordLockoutPolicy: &iam_es_model.PasswordLockoutPolicy{ result: &Org{LockoutPolicy: &iam_es_model.LockoutPolicy{
MaxAttempts: 5, MaxPasswordAttempts: 5,
ShowLockOutFailures: true, ShowLockOutFailures: true,
}}, }},
}, },
@ -74,12 +74,12 @@ func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
data, _ := json.Marshal(tt.args.policy) data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data tt.args.event.Data = data
} }
tt.args.org.appendChangePasswordLockoutPolicyEvent(tt.args.event) tt.args.org.appendChangeLockoutPolicyEvent(tt.args.event)
if tt.result.PasswordLockoutPolicy.MaxAttempts != tt.args.org.PasswordLockoutPolicy.MaxAttempts { if tt.result.LockoutPolicy.MaxPasswordAttempts != tt.args.org.LockoutPolicy.MaxPasswordAttempts {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.PasswordLockoutPolicy.MaxAttempts, tt.args.org.PasswordLockoutPolicy.MaxAttempts) t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.LockoutPolicy.MaxPasswordAttempts, tt.args.org.LockoutPolicy.MaxPasswordAttempts)
} }
if tt.result.PasswordLockoutPolicy.ShowLockOutFailures != tt.args.org.PasswordLockoutPolicy.ShowLockOutFailures { if tt.result.LockoutPolicy.ShowLockOutFailures != tt.args.org.LockoutPolicy.ShowLockOutFailures {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.PasswordLockoutPolicy.ShowLockOutFailures, tt.args.org.PasswordLockoutPolicy.ShowLockOutFailures) t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.LockoutPolicy.ShowLockOutFailures, tt.args.org.LockoutPolicy.ShowLockOutFailures)
} }
}) })
} }

View File

@ -89,9 +89,9 @@ const (
PasswordAgePolicyChanged models.EventType = "org.policy.password.age.changed" PasswordAgePolicyChanged models.EventType = "org.policy.password.age.changed"
PasswordAgePolicyRemoved models.EventType = "org.policy.password.age.removed" PasswordAgePolicyRemoved models.EventType = "org.policy.password.age.removed"
PasswordLockoutPolicyAdded models.EventType = "org.policy.password.lockout.added" LockoutPolicyAdded models.EventType = "org.policy.lockout.added"
PasswordLockoutPolicyChanged models.EventType = "org.policy.password.lockout.changed" LockoutPolicyChanged models.EventType = "org.policy.lockout.changed"
PasswordLockoutPolicyRemoved models.EventType = "org.policy.password.lockout.removed" LockoutPolicyRemoved models.EventType = "org.policy.lockout.removed"
PrivacyPolicyAdded models.EventType = "org.policy.privacy.added" PrivacyPolicyAdded models.EventType = "org.policy.privacy.added"
PrivacyPolicyChanged models.EventType = "org.policy.privacy.changed" PrivacyPolicyChanged models.EventType = "org.policy.privacy.changed"

View File

@ -20,7 +20,7 @@ func readModelToIAM(readModel *ReadModel) *model.IAM {
DefaultOrgIAMPolicy: readModelToOrgIAMPolicy(&readModel.DefaultOrgIAMPolicy), DefaultOrgIAMPolicy: readModelToOrgIAMPolicy(&readModel.DefaultOrgIAMPolicy),
DefaultPasswordAgePolicy: readModelToPasswordAgePolicy(&readModel.DefaultPasswordAgePolicy), DefaultPasswordAgePolicy: readModelToPasswordAgePolicy(&readModel.DefaultPasswordAgePolicy),
DefaultPasswordComplexityPolicy: readModelToPasswordComplexityPolicy(&readModel.DefaultPasswordComplexityPolicy), DefaultPasswordComplexityPolicy: readModelToPasswordComplexityPolicy(&readModel.DefaultPasswordComplexityPolicy),
DefaultPasswordLockoutPolicy: readModelToPasswordLockoutPolicy(&readModel.DefaultPasswordLockoutPolicy), DefaultLockoutPolicy: readModelToPasswordLockoutPolicy(&readModel.DefaultPasswordLockoutPolicy),
IDPs: readModelToIDPConfigs(&readModel.IDPs), IDPs: readModelToIDPConfigs(&readModel.IDPs),
} }
} }
@ -121,10 +121,10 @@ func readModelToPasswordComplexityPolicy(readModel *IAMPasswordComplexityPolicyR
MinLength: readModel.MinLength, MinLength: readModel.MinLength,
} }
} }
func readModelToPasswordLockoutPolicy(readModel *IAMPasswordLockoutPolicyReadModel) *model.PasswordLockoutPolicy { func readModelToPasswordLockoutPolicy(readModel *IAMLockoutPolicyReadModel) *model.LockoutPolicy {
return &model.PasswordLockoutPolicy{ return &model.LockoutPolicy{
ObjectRoot: readModelToObjectRoot(readModel.PasswordLockoutPolicyReadModel.ReadModel), ObjectRoot: readModelToObjectRoot(readModel.LockoutPolicyReadModel.ReadModel),
MaxAttempts: readModel.MaxAttempts, MaxPasswordAttempts: readModel.MaxAttempts,
ShowLockOutFailures: readModel.ShowLockOutFailures, ShowLockOutFailures: readModel.ShowLockOutFailures,
} }
} }

View File

@ -25,7 +25,7 @@ type ReadModel struct {
DefaultOrgIAMPolicy IAMOrgIAMPolicyReadModel DefaultOrgIAMPolicy IAMOrgIAMPolicyReadModel
DefaultPasswordComplexityPolicy IAMPasswordComplexityPolicyReadModel DefaultPasswordComplexityPolicy IAMPasswordComplexityPolicyReadModel
DefaultPasswordAgePolicy IAMPasswordAgePolicyReadModel DefaultPasswordAgePolicy IAMPasswordAgePolicyReadModel
DefaultPasswordLockoutPolicy IAMPasswordLockoutPolicyReadModel DefaultPasswordLockoutPolicy IAMLockoutPolicyReadModel
} }
func NewReadModel(id string) *ReadModel { func NewReadModel(id string) *ReadModel {
@ -80,8 +80,8 @@ func (rm *ReadModel) AppendEvents(events ...eventstore.EventReader) {
*policy.PasswordAgePolicyChangedEvent: *policy.PasswordAgePolicyChangedEvent:
rm.DefaultPasswordAgePolicy.AppendEvents(event) rm.DefaultPasswordAgePolicy.AppendEvents(event)
case *policy.PasswordLockoutPolicyAddedEvent, case *policy.LockoutPolicyAddedEvent,
*policy.PasswordLockoutPolicyChangedEvent: *policy.LockoutPolicyChangedEvent:
rm.DefaultPasswordLockoutPolicy.AppendEvents(event) rm.DefaultPasswordLockoutPolicy.AppendEvents(event)
} }

View File

@ -6,19 +6,19 @@ import (
"github.com/caos/zitadel/internal/repository/policy" "github.com/caos/zitadel/internal/repository/policy"
) )
type IAMPasswordLockoutPolicyReadModel struct { type IAMLockoutPolicyReadModel struct {
PasswordLockoutPolicyReadModel LockoutPolicyReadModel
} }
func (rm *IAMPasswordLockoutPolicyReadModel) AppendEvents(events ...eventstore.EventReader) { func (rm *IAMLockoutPolicyReadModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events { for _, event := range events {
switch e := event.(type) { switch e := event.(type) {
case *iam.PasswordLockoutPolicyAddedEvent: case *iam.LockoutPolicyAddedEvent:
rm.PasswordLockoutPolicyReadModel.AppendEvents(&e.PasswordLockoutPolicyAddedEvent) rm.LockoutPolicyReadModel.AppendEvents(&e.LockoutPolicyAddedEvent)
case *iam.PasswordLockoutPolicyChangedEvent: case *iam.LockoutPolicyChangedEvent:
rm.PasswordLockoutPolicyReadModel.AppendEvents(&e.PasswordLockoutPolicyChangedEvent) rm.LockoutPolicyReadModel.AppendEvents(&e.LockoutPolicyChangedEvent)
case *policy.PasswordLockoutPolicyAddedEvent, *policy.PasswordLockoutPolicyChangedEvent: case *policy.LockoutPolicyAddedEvent, *policy.LockoutPolicyChangedEvent:
rm.PasswordLockoutPolicyReadModel.AppendEvents(e) rm.LockoutPolicyReadModel.AppendEvents(e)
} }
} }
} }

View File

@ -7,18 +7,18 @@ import (
) )
type OrgPasswordLockoutPolicyReadModel struct { type OrgPasswordLockoutPolicyReadModel struct {
PasswordLockoutPolicyReadModel LockoutPolicyReadModel
} }
func (rm *OrgPasswordLockoutPolicyReadModel) AppendEvents(events ...eventstore.EventReader) { func (rm *OrgPasswordLockoutPolicyReadModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events { for _, event := range events {
switch e := event.(type) { switch e := event.(type) {
case *org.PasswordLockoutPolicyAddedEvent: case *org.LockoutPolicyAddedEvent:
rm.PasswordLockoutPolicyReadModel.AppendEvents(&e.PasswordLockoutPolicyAddedEvent) rm.LockoutPolicyReadModel.AppendEvents(&e.LockoutPolicyAddedEvent)
case *org.PasswordLockoutPolicyChangedEvent: case *org.LockoutPolicyChangedEvent:
rm.PasswordLockoutPolicyReadModel.AppendEvents(&e.PasswordLockoutPolicyChangedEvent) rm.LockoutPolicyReadModel.AppendEvents(&e.LockoutPolicyChangedEvent)
case *policy.PasswordLockoutPolicyAddedEvent, *policy.PasswordLockoutPolicyChangedEvent: case *policy.LockoutPolicyAddedEvent, *policy.LockoutPolicyChangedEvent:
rm.PasswordLockoutPolicyReadModel.AppendEvents(e) rm.LockoutPolicyReadModel.AppendEvents(e)
} }
} }
} }

View File

@ -5,22 +5,22 @@ import (
"github.com/caos/zitadel/internal/repository/policy" "github.com/caos/zitadel/internal/repository/policy"
) )
type PasswordLockoutPolicyReadModel struct { type LockoutPolicyReadModel struct {
eventstore.ReadModel eventstore.ReadModel
MaxAttempts uint64 MaxAttempts uint64
ShowLockOutFailures bool ShowLockOutFailures bool
} }
func (rm *PasswordLockoutPolicyReadModel) Reduce() error { func (rm *LockoutPolicyReadModel) Reduce() error {
for _, event := range rm.Events { for _, event := range rm.Events {
switch e := event.(type) { switch e := event.(type) {
case *policy.PasswordLockoutPolicyAddedEvent: case *policy.LockoutPolicyAddedEvent:
rm.MaxAttempts = e.MaxAttempts rm.MaxAttempts = e.MaxPasswordAttempts
rm.ShowLockOutFailures = e.ShowLockOutFailures rm.ShowLockOutFailures = e.ShowLockOutFailures
case *policy.PasswordLockoutPolicyChangedEvent: case *policy.LockoutPolicyChangedEvent:
if e.MaxAttempts != nil { if e.MaxPasswordAttempts != nil {
rm.MaxAttempts = *e.MaxAttempts rm.MaxAttempts = *e.MaxPasswordAttempts
} }
if e.ShowLockOutFailures != nil { if e.ShowLockOutFailures != nil {
rm.ShowLockOutFailures = *e.ShowLockOutFailures rm.ShowLockOutFailures = *e.ShowLockOutFailures

View File

@ -32,8 +32,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(PasswordAgePolicyChangedEventType, PasswordAgePolicyChangedEventMapper). RegisterFilterEventMapper(PasswordAgePolicyChangedEventType, PasswordAgePolicyChangedEventMapper).
RegisterFilterEventMapper(PasswordComplexityPolicyAddedEventType, PasswordComplexityPolicyAddedEventMapper). RegisterFilterEventMapper(PasswordComplexityPolicyAddedEventType, PasswordComplexityPolicyAddedEventMapper).
RegisterFilterEventMapper(PasswordComplexityPolicyChangedEventType, PasswordComplexityPolicyChangedEventMapper). RegisterFilterEventMapper(PasswordComplexityPolicyChangedEventType, PasswordComplexityPolicyChangedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyAddedEventType, PasswordLockoutPolicyAddedEventMapper). RegisterFilterEventMapper(LockoutPolicyAddedEventType, LockoutPolicyAddedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyChangedEventType, PasswordLockoutPolicyChangedEventMapper). RegisterFilterEventMapper(LockoutPolicyChangedEventType, LockoutPolicyChangedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyAddedEventType, PrivacyPolicyAddedEventMapper). RegisterFilterEventMapper(PrivacyPolicyAddedEventType, PrivacyPolicyAddedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyChangedEventType, PrivacyPolicyChangedEventMapper). RegisterFilterEventMapper(PrivacyPolicyChangedEventType, PrivacyPolicyChangedEventMapper).
RegisterFilterEventMapper(MemberAddedEventType, MemberAddedEventMapper). RegisterFilterEventMapper(MemberAddedEventType, MemberAddedEventMapper).

View File

@ -9,67 +9,67 @@ import (
) )
var ( var (
PasswordLockoutPolicyAddedEventType = iamEventTypePrefix + policy.PasswordLockoutPolicyAddedEventType LockoutPolicyAddedEventType = iamEventTypePrefix + policy.LockoutPolicyAddedEventType
PasswordLockoutPolicyChangedEventType = iamEventTypePrefix + policy.PasswordLockoutPolicyChangedEventType LockoutPolicyChangedEventType = iamEventTypePrefix + policy.LockoutPolicyChangedEventType
) )
type PasswordLockoutPolicyAddedEvent struct { type LockoutPolicyAddedEvent struct {
policy.PasswordLockoutPolicyAddedEvent policy.LockoutPolicyAddedEvent
} }
func NewPasswordLockoutPolicyAddedEvent( func NewLockoutPolicyAddedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
maxAttempts uint64, maxAttempts uint64,
showLockoutFailure bool, showLockoutFailure bool,
) *PasswordLockoutPolicyAddedEvent { ) *LockoutPolicyAddedEvent {
return &PasswordLockoutPolicyAddedEvent{ return &LockoutPolicyAddedEvent{
PasswordLockoutPolicyAddedEvent: *policy.NewPasswordLockoutPolicyAddedEvent( LockoutPolicyAddedEvent: *policy.NewLockoutPolicyAddedEvent(
eventstore.NewBaseEventForPush( eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
PasswordLockoutPolicyAddedEventType), LockoutPolicyAddedEventType),
maxAttempts, maxAttempts,
showLockoutFailure), showLockoutFailure),
} }
} }
func PasswordLockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.PasswordLockoutPolicyAddedEventMapper(event) e, err := policy.LockoutPolicyAddedEventMapper(event)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PasswordLockoutPolicyAddedEvent{PasswordLockoutPolicyAddedEvent: *e.(*policy.PasswordLockoutPolicyAddedEvent)}, nil return &LockoutPolicyAddedEvent{LockoutPolicyAddedEvent: *e.(*policy.LockoutPolicyAddedEvent)}, nil
} }
type PasswordLockoutPolicyChangedEvent struct { type LockoutPolicyChangedEvent struct {
policy.PasswordLockoutPolicyChangedEvent policy.LockoutPolicyChangedEvent
} }
func NewPasswordLockoutPolicyChangedEvent( func NewLockoutPolicyChangedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
changes []policy.PasswordLockoutPolicyChanges, changes []policy.LockoutPolicyChanges,
) (*PasswordLockoutPolicyChangedEvent, error) { ) (*LockoutPolicyChangedEvent, error) {
changedEvent, err := policy.NewPasswordLockoutPolicyChangedEvent( changedEvent, err := policy.NewLockoutPolicyChangedEvent(
eventstore.NewBaseEventForPush( eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
PasswordLockoutPolicyChangedEventType), LockoutPolicyChangedEventType),
changes, changes,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PasswordLockoutPolicyChangedEvent{PasswordLockoutPolicyChangedEvent: *changedEvent}, nil return &LockoutPolicyChangedEvent{LockoutPolicyChangedEvent: *changedEvent}, nil
} }
func PasswordLockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.PasswordLockoutPolicyChangedEventMapper(event) e, err := policy.LockoutPolicyChangedEventMapper(event)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PasswordLockoutPolicyChangedEvent{PasswordLockoutPolicyChangedEvent: *e.(*policy.PasswordLockoutPolicyChangedEvent)}, nil return &LockoutPolicyChangedEvent{LockoutPolicyChangedEvent: *e.(*policy.LockoutPolicyChangedEvent)}, nil
} }

View File

@ -53,9 +53,9 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(PasswordComplexityPolicyAddedEventType, PasswordComplexityPolicyAddedEventMapper). RegisterFilterEventMapper(PasswordComplexityPolicyAddedEventType, PasswordComplexityPolicyAddedEventMapper).
RegisterFilterEventMapper(PasswordComplexityPolicyChangedEventType, PasswordComplexityPolicyChangedEventMapper). RegisterFilterEventMapper(PasswordComplexityPolicyChangedEventType, PasswordComplexityPolicyChangedEventMapper).
RegisterFilterEventMapper(PasswordComplexityPolicyRemovedEventType, PasswordComplexityPolicyRemovedEventMapper). RegisterFilterEventMapper(PasswordComplexityPolicyRemovedEventType, PasswordComplexityPolicyRemovedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyAddedEventType, PasswordLockoutPolicyAddedEventMapper). RegisterFilterEventMapper(LockoutPolicyAddedEventType, LockoutPolicyAddedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyChangedEventType, PasswordLockoutPolicyChangedEventMapper). RegisterFilterEventMapper(LockoutPolicyChangedEventType, LockoutPolicyChangedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyRemovedEventType, PasswordLockoutPolicyRemovedEventMapper). RegisterFilterEventMapper(LockoutPolicyRemovedEventType, LockoutPolicyRemovedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyAddedEventType, PrivacyPolicyAddedEventMapper). RegisterFilterEventMapper(PrivacyPolicyAddedEventType, PrivacyPolicyAddedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyChangedEventType, PrivacyPolicyChangedEventMapper). RegisterFilterEventMapper(PrivacyPolicyChangedEventType, PrivacyPolicyChangedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyRemovedEventType, PrivacyPolicyRemovedEventMapper). RegisterFilterEventMapper(PrivacyPolicyRemovedEventType, PrivacyPolicyRemovedEventMapper).

View File

@ -9,95 +9,95 @@ import (
) )
var ( var (
PasswordLockoutPolicyAddedEventType = orgEventTypePrefix + policy.PasswordLockoutPolicyAddedEventType LockoutPolicyAddedEventType = orgEventTypePrefix + policy.LockoutPolicyAddedEventType
PasswordLockoutPolicyChangedEventType = orgEventTypePrefix + policy.PasswordLockoutPolicyChangedEventType LockoutPolicyChangedEventType = orgEventTypePrefix + policy.LockoutPolicyChangedEventType
PasswordLockoutPolicyRemovedEventType = orgEventTypePrefix + policy.PasswordLockoutPolicyRemovedEventType LockoutPolicyRemovedEventType = orgEventTypePrefix + policy.LockoutPolicyRemovedEventType
) )
type PasswordLockoutPolicyAddedEvent struct { type LockoutPolicyAddedEvent struct {
policy.PasswordLockoutPolicyAddedEvent policy.LockoutPolicyAddedEvent
} }
func NewPasswordLockoutPolicyAddedEvent( func NewLockoutPolicyAddedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
maxAttempts uint64, maxAttempts uint64,
showLockoutFailure bool, showLockoutFailure bool,
) *PasswordLockoutPolicyAddedEvent { ) *LockoutPolicyAddedEvent {
return &PasswordLockoutPolicyAddedEvent{ return &LockoutPolicyAddedEvent{
PasswordLockoutPolicyAddedEvent: *policy.NewPasswordLockoutPolicyAddedEvent( LockoutPolicyAddedEvent: *policy.NewLockoutPolicyAddedEvent(
eventstore.NewBaseEventForPush( eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
PasswordLockoutPolicyAddedEventType), LockoutPolicyAddedEventType),
maxAttempts, maxAttempts,
showLockoutFailure), showLockoutFailure),
} }
} }
func PasswordLockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.PasswordLockoutPolicyAddedEventMapper(event) e, err := policy.LockoutPolicyAddedEventMapper(event)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PasswordLockoutPolicyAddedEvent{PasswordLockoutPolicyAddedEvent: *e.(*policy.PasswordLockoutPolicyAddedEvent)}, nil return &LockoutPolicyAddedEvent{LockoutPolicyAddedEvent: *e.(*policy.LockoutPolicyAddedEvent)}, nil
} }
type PasswordLockoutPolicyChangedEvent struct { type LockoutPolicyChangedEvent struct {
policy.PasswordLockoutPolicyChangedEvent policy.LockoutPolicyChangedEvent
} }
func NewPasswordLockoutPolicyChangedEvent( func NewLockoutPolicyChangedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
changes []policy.PasswordLockoutPolicyChanges, changes []policy.LockoutPolicyChanges,
) (*PasswordLockoutPolicyChangedEvent, error) { ) (*LockoutPolicyChangedEvent, error) {
changedEvent, err := policy.NewPasswordLockoutPolicyChangedEvent( changedEvent, err := policy.NewLockoutPolicyChangedEvent(
eventstore.NewBaseEventForPush( eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
PasswordLockoutPolicyChangedEventType), LockoutPolicyChangedEventType),
changes, changes,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PasswordLockoutPolicyChangedEvent{PasswordLockoutPolicyChangedEvent: *changedEvent}, nil return &LockoutPolicyChangedEvent{LockoutPolicyChangedEvent: *changedEvent}, nil
} }
func PasswordLockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.PasswordLockoutPolicyChangedEventMapper(event) e, err := policy.LockoutPolicyChangedEventMapper(event)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PasswordLockoutPolicyChangedEvent{PasswordLockoutPolicyChangedEvent: *e.(*policy.PasswordLockoutPolicyChangedEvent)}, nil return &LockoutPolicyChangedEvent{LockoutPolicyChangedEvent: *e.(*policy.LockoutPolicyChangedEvent)}, nil
} }
type PasswordLockoutPolicyRemovedEvent struct { type LockoutPolicyRemovedEvent struct {
policy.PasswordLockoutPolicyRemovedEvent policy.LockoutPolicyRemovedEvent
} }
func NewPasswordLockoutPolicyRemovedEvent( func NewLockoutPolicyRemovedEvent(
ctx context.Context, ctx context.Context,
aggregate *eventstore.Aggregate, aggregate *eventstore.Aggregate,
) *PasswordLockoutPolicyRemovedEvent { ) *LockoutPolicyRemovedEvent {
return &PasswordLockoutPolicyRemovedEvent{ return &LockoutPolicyRemovedEvent{
PasswordLockoutPolicyRemovedEvent: *policy.NewPasswordLockoutPolicyRemovedEvent( LockoutPolicyRemovedEvent: *policy.NewLockoutPolicyRemovedEvent(
eventstore.NewBaseEventForPush( eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
PasswordLockoutPolicyRemovedEventType), LockoutPolicyRemovedEventType),
), ),
} }
} }
func PasswordLockoutPolicyRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.PasswordLockoutPolicyRemovedEventMapper(event) e, err := policy.LockoutPolicyRemovedEventMapper(event)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PasswordLockoutPolicyRemovedEvent{PasswordLockoutPolicyRemovedEvent: *e.(*policy.PasswordLockoutPolicyRemovedEvent)}, nil return &LockoutPolicyRemovedEvent{LockoutPolicyRemovedEvent: *e.(*policy.LockoutPolicyRemovedEvent)}, nil
} }

View File

@ -9,41 +9,41 @@ import (
) )
const ( const (
PasswordLockoutPolicyAddedEventType = "policy.password.lockout.added" LockoutPolicyAddedEventType = "policy.lockout.added"
PasswordLockoutPolicyChangedEventType = "policy.password.lockout.changed" LockoutPolicyChangedEventType = "policy.lockout.changed"
PasswordLockoutPolicyRemovedEventType = "policy.password.lockout.removed" LockoutPolicyRemovedEventType = "policy.lockout.removed"
) )
type PasswordLockoutPolicyAddedEvent struct { type LockoutPolicyAddedEvent struct {
eventstore.BaseEvent `json:"-"` eventstore.BaseEvent `json:"-"`
MaxAttempts uint64 `json:"maxAttempts,omitempty"` MaxPasswordAttempts uint64 `json:"maxPasswordAttempts,omitempty"`
ShowLockOutFailures bool `json:"showLockOutFailures,omitempty"` ShowLockOutFailures bool `json:"showLockOutFailures,omitempty"`
} }
func (e *PasswordLockoutPolicyAddedEvent) Data() interface{} { func (e *LockoutPolicyAddedEvent) Data() interface{} {
return e return e
} }
func (e *PasswordLockoutPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { func (e *LockoutPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil return nil
} }
func NewPasswordLockoutPolicyAddedEvent( func NewLockoutPolicyAddedEvent(
base *eventstore.BaseEvent, base *eventstore.BaseEvent,
maxAttempts uint64, maxAttempts uint64,
showLockOutFailures bool, showLockOutFailures bool,
) *PasswordLockoutPolicyAddedEvent { ) *LockoutPolicyAddedEvent {
return &PasswordLockoutPolicyAddedEvent{ return &LockoutPolicyAddedEvent{
BaseEvent: *base, BaseEvent: *base,
MaxAttempts: maxAttempts, MaxPasswordAttempts: maxAttempts,
ShowLockOutFailures: showLockOutFailures, ShowLockOutFailures: showLockOutFailures,
} }
} }
func PasswordLockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &PasswordLockoutPolicyAddedEvent{ e := &LockoutPolicyAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),
} }
@ -55,29 +55,29 @@ func PasswordLockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.
return e, nil return e, nil
} }
type PasswordLockoutPolicyChangedEvent struct { type LockoutPolicyChangedEvent struct {
eventstore.BaseEvent `json:"-"` eventstore.BaseEvent `json:"-"`
MaxAttempts *uint64 `json:"maxAttempts,omitempty"` MaxPasswordAttempts *uint64 `json:"maxPasswordAttempts,omitempty"`
ShowLockOutFailures *bool `json:"showLockOutFailures,omitempty"` ShowLockOutFailures *bool `json:"showLockOutFailures,omitempty"`
} }
func (e *PasswordLockoutPolicyChangedEvent) Data() interface{} { func (e *LockoutPolicyChangedEvent) Data() interface{} {
return e return e
} }
func (e *PasswordLockoutPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { func (e *LockoutPolicyChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil return nil
} }
func NewPasswordLockoutPolicyChangedEvent( func NewLockoutPolicyChangedEvent(
base *eventstore.BaseEvent, base *eventstore.BaseEvent,
changes []PasswordLockoutPolicyChanges, changes []LockoutPolicyChanges,
) (*PasswordLockoutPolicyChangedEvent, error) { ) (*LockoutPolicyChangedEvent, error) {
if len(changes) == 0 { if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "POLICY-sdgh6", "Errors.NoChangesFound") return nil, errors.ThrowPreconditionFailed(nil, "POLICY-sdgh6", "Errors.NoChangesFound")
} }
changeEvent := &PasswordLockoutPolicyChangedEvent{ changeEvent := &LockoutPolicyChangedEvent{
BaseEvent: *base, BaseEvent: *base,
} }
for _, change := range changes { for _, change := range changes {
@ -86,22 +86,22 @@ func NewPasswordLockoutPolicyChangedEvent(
return changeEvent, nil return changeEvent, nil
} }
type PasswordLockoutPolicyChanges func(*PasswordLockoutPolicyChangedEvent) type LockoutPolicyChanges func(*LockoutPolicyChangedEvent)
func ChangeMaxAttempts(maxAttempts uint64) func(*PasswordLockoutPolicyChangedEvent) { func ChangeMaxAttempts(maxAttempts uint64) func(*LockoutPolicyChangedEvent) {
return func(e *PasswordLockoutPolicyChangedEvent) { return func(e *LockoutPolicyChangedEvent) {
e.MaxAttempts = &maxAttempts e.MaxPasswordAttempts = &maxAttempts
} }
} }
func ChangeShowLockOutFailures(showLockOutFailures bool) func(*PasswordLockoutPolicyChangedEvent) { func ChangeShowLockOutFailures(showLockOutFailures bool) func(*LockoutPolicyChangedEvent) {
return func(e *PasswordLockoutPolicyChangedEvent) { return func(e *LockoutPolicyChangedEvent) {
e.ShowLockOutFailures = &showLockOutFailures e.ShowLockOutFailures = &showLockOutFailures
} }
} }
func PasswordLockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &PasswordLockoutPolicyChangedEvent{ e := &LockoutPolicyChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),
} }
@ -113,26 +113,26 @@ func PasswordLockoutPolicyChangedEventMapper(event *repository.Event) (eventstor
return e, nil return e, nil
} }
type PasswordLockoutPolicyRemovedEvent struct { type LockoutPolicyRemovedEvent struct {
eventstore.BaseEvent `json:"-"` eventstore.BaseEvent `json:"-"`
} }
func (e *PasswordLockoutPolicyRemovedEvent) Data() interface{} { func (e *LockoutPolicyRemovedEvent) Data() interface{} {
return nil return nil
} }
func (e *PasswordLockoutPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { func (e *LockoutPolicyRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil return nil
} }
func NewPasswordLockoutPolicyRemovedEvent(base *eventstore.BaseEvent) *PasswordLockoutPolicyRemovedEvent { func NewLockoutPolicyRemovedEvent(base *eventstore.BaseEvent) *LockoutPolicyRemovedEvent {
return &PasswordLockoutPolicyRemovedEvent{ return &LockoutPolicyRemovedEvent{
BaseEvent: *base, BaseEvent: *base,
} }
} }
func PasswordLockoutPolicyRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func LockoutPolicyRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &PasswordLockoutPolicyRemovedEvent{ return &LockoutPolicyRemovedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil }, nil
} }

View File

@ -23,6 +23,7 @@ type IAMSetUp struct {
Step15 *command.Step15 Step15 *command.Step15
Step16 *command.Step16 Step16 *command.Step16
Step17 *command.Step17 Step17 *command.Step17
Step18 *command.Step18
} }
func (setup *IAMSetUp) Steps(currentDone domain.Step) ([]command.Step, error) { func (setup *IAMSetUp) Steps(currentDone domain.Step) ([]command.Step, error) {
@ -46,6 +47,7 @@ func (setup *IAMSetUp) Steps(currentDone domain.Step) ([]command.Step, error) {
setup.Step15, setup.Step15,
setup.Step16, setup.Step16,
setup.Step17, setup.Step17,
setup.Step18,
} { } {
if step.Step() <= currentDone { if step.Step() <= currentDone {
continue continue

View File

@ -231,6 +231,10 @@ func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *
} }
func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) { func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
if err != nil {
l.renderInternalError(w, r, authReq, err)
return
}
if authReq == nil || len(authReq.PossibleSteps) == 0 { if authReq == nil || len(authReq.PossibleSteps) == 0 {
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-OVOiT", "no possible steps")) l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-OVOiT", "no possible steps"))
return return
@ -292,7 +296,7 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *
func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) { func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var msg string var msg string
if err != nil { if err != nil {
msg = err.Error() _, msg = l.getErrorMessage(r, err)
} }
data := l.getBaseData(r, authReq, "Error", "Internal", msg) data := l.getBaseData(r, authReq, "Error", "Internal", msg)
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplError], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplError], data, nil)

View File

@ -288,6 +288,7 @@ Errors:
ConfirmationWrong: Passwort Bestätigung stimmt nicht überein ConfirmationWrong: Passwort Bestätigung stimmt nicht überein
Empty: Passwort ist leer Empty: Passwort ist leer
Invalid: Passwort ungültig Invalid: Passwort ungültig
InvalidAndLocked: Password ist undgültig und Benutzer wurde gesperrt, melden Sie sich bei ihrem Administrator.
PasswordComplexityPolicy: PasswordComplexityPolicy:
NotFound: Passwort Policy konnte nicht gefunden werden NotFound: Passwort Policy konnte nicht gefunden werden
MinLength: Passwort ist zu kurz MinLength: Passwort ist zu kurz
@ -312,6 +313,7 @@ Errors:
InvalidCode: Code ist ungültig InvalidCode: Code ist ungültig
NotReady: Multifaktor OTP (OneTimePassword) ist nicht bereit NotReady: Multifaktor OTP (OneTimePassword) ist nicht bereit
Locked: Benutzer ist gesperrt Locked: Benutzer ist gesperrt
SomethingWentWrong: Irgendetwas ist schief gelaufen
NotActive: Benutzer ist nicht aktiv NotActive: Benutzer ist nicht aktiv
ExternalIDP: ExternalIDP:
IDPTypeNotImplemented: IDP Typ ist nicht implementiert IDPTypeNotImplemented: IDP Typ ist nicht implementiert
@ -319,5 +321,8 @@ Errors:
GrantRequired: Der Login an diese Applikation ist nicht möglich. Der Benutzer benötigt mindestens eine Berechtigung an der Applikation. Bitte melde dich bei deinem Administrator. GrantRequired: Der Login an diese Applikation ist nicht möglich. Der Benutzer benötigt mindestens eine Berechtigung an der Applikation. Bitte melde dich bei deinem Administrator.
IdentityProvider: IdentityProvider:
InvalidConfig: Identitäts Provider Konfiguration ist ungültig InvalidConfig: Identitäts Provider Konfiguration ist ungültig
IAM:
LockoutPolicy:
NotExisting: Lockout Policy existiert nicht
optional: (optional) optional: (optional)

View File

@ -288,6 +288,7 @@ Errors:
ConfirmationWrong: Passwordconfirmation is wrong ConfirmationWrong: Passwordconfirmation is wrong
Empty: Password is empty Empty: Password is empty
Invalid: Password is invalid Invalid: Password is invalid
InvalidAndLocked: Password is invalid and user is locked, contact your administrator.
PasswordComplexityPolicy: PasswordComplexityPolicy:
NotFound: Password policy not found NotFound: Password policy not found
MinLength: Password is to short MinLength: Password is to short
@ -312,6 +313,7 @@ Errors:
InvalidCode: Invalid code InvalidCode: Invalid code
NotReady: Multifactor OTP (OneTimePassword) isn't ready NotReady: Multifactor OTP (OneTimePassword) isn't ready
Locked: User is locked Locked: User is locked
SomethingWentWrong: Something went wrong
NotActive: User is not active NotActive: User is not active
ExternalIDP: ExternalIDP:
IDPTypeNotImplemented: IDP Type is not implemented IDPTypeNotImplemented: IDP Type is not implemented
@ -319,5 +321,8 @@ Errors:
GrantRequired: Login not possible. The user is required to have at least one grant on the application. Please contact your administrator. GrantRequired: Login not possible. The user is required to have at least one grant on the application. Please contact your administrator.
IdentityProvider: IdentityProvider:
InvalidConfig: Identity Provider configuration is invalid InvalidConfig: Identity Provider configuration is invalid
IAM:
LockoutPolicy:
NotExisting: Lockout Policy not existing
optional: (optional) optional: (optional)

View File

@ -0,0 +1,45 @@
CREATE TABLE management.lockout_policies (
aggregate_id TEXT,
creation_date TIMESTAMPTZ,
change_date TIMESTAMPTZ,
lockout_policy_state SMALLINT,
sequence BIGINT,
max_password_attempts BIGINT,
show_lockout_failures BOOLEAN,
PRIMARY KEY (aggregate_id)
);
CREATE TABLE adminapi.lockout_policies (
aggregate_id TEXT,
creation_date TIMESTAMPTZ,
change_date TIMESTAMPTZ,
lockout_policy_state SMALLINT,
sequence BIGINT,
max_password_attempts BIGINT,
show_lockout_failures BOOLEAN,
PRIMARY KEY (aggregate_id)
);
CREATE TABLE auth.lockout_policies (
aggregate_id TEXT,
creation_date TIMESTAMPTZ,
change_date TIMESTAMPTZ,
lockout_policy_state SMALLINT,
sequence BIGINT,
max_password_attempts BIGINT,
show_lockout_failures BOOLEAN,
PRIMARY KEY (aggregate_id)
);
DROP TABLE management.password_lockout_policies;
DROP TABLE adminapi.password_lockout_policies;
DROP TABLE auth.password_lockout_policies;

View File

@ -1425,10 +1425,10 @@ service AdminService {
}; };
} }
//Returns the password lockout policy defined by the administrators of ZITADEL //Returns the lockout policy defined by the administrators of ZITADEL
rpc GetPasswordLockoutPolicy(GetPasswordLockoutPolicyRequest) returns (GetPasswordLockoutPolicyResponse) { rpc GetLockoutPolicy(GetLockoutPolicyRequest) returns (GetLockoutPolicyResponse) {
option (google.api.http) = { option (google.api.http) = {
get: "/policies/password/lockout"; get: "/policies/lockout";
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
@ -1437,20 +1437,19 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "policy"; tags: "policy";
tags: "password policy"; tags: "lockout policy";
tags: "password lockout policy";
responses: { responses: {
key: "200"; key: "200";
value: { value: {
description: "default password lockout policy"; description: "default lockout policy";
}; };
}; };
}; };
} }
//Updates the default password lockout policy of ZITADEL //Updates the default lockout policy of ZITADEL
// it impacts all organisations without a customised policy // it impacts all organisations without a customised policy
rpc UpdatePasswordLockoutPolicy(UpdatePasswordLockoutPolicyRequest) returns (UpdatePasswordLockoutPolicyResponse) { rpc UpdateLockoutPolicy(UpdateLockoutPolicyRequest) returns (UpdateLockoutPolicyResponse) {
option (google.api.http) = { option (google.api.http) = {
put: "/policies/password/lockout"; put: "/policies/password/lockout";
body: "*"; body: "*";
@ -3086,25 +3085,23 @@ message UpdatePasswordAgePolicyResponse {
} }
//This is an empty request //This is an empty request
message GetPasswordLockoutPolicyRequest {} message GetLockoutPolicyRequest {}
message GetPasswordLockoutPolicyResponse { message GetLockoutPolicyResponse {
zitadel.policy.v1.PasswordLockoutPolicy policy = 1; zitadel.policy.v1.LockoutPolicy policy = 1;
} }
message UpdatePasswordLockoutPolicyRequest { message UpdateLockoutPolicyRequest {
// failed attempts until a user gets locked // failed attempts until a user gets locked
uint32 max_attempts = 1 [ uint32 max_password_attempts = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Maximum attempts before the account gets locked. Attempts are reset as soon as the password is entered correct or the password is reset." description: "Maximum password check attempts before the account gets locked. Attempts are reset as soon as the password is entered correct or the password is reset."
example: "\"10\"" example: "\"10\""
} }
]; ];
// If an error should be displayed during a lockout or not
bool show_lockout_failure = 2;
} }
message UpdatePasswordLockoutPolicyResponse { message UpdateLockoutPolicyResponse {
zitadel.v1.ObjectDetails details = 1; zitadel.v1.ObjectDetails details = 1;
} }

View File

@ -1971,10 +1971,9 @@ service ManagementService {
}; };
} }
// The password lockout policy is not used at the moment rpc GetLockoutPolicy(GetLockoutPolicyRequest) returns (GetLockoutPolicyResponse) {
rpc GetPasswordLockoutPolicy(GetPasswordLockoutPolicyRequest) returns (GetPasswordLockoutPolicyResponse) {
option (google.api.http) = { option (google.api.http) = {
get: "/policies/password/lockout" get: "/policies/lockout"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
@ -1982,10 +1981,9 @@ service ManagementService {
}; };
} }
// The password lockout policy is not used at the moment rpc GetDefaultLockoutPolicy(GetDefaultLockoutPolicyRequest) returns (GetDefaultLockoutPolicyResponse) {
rpc GetDefaultPasswordLockoutPolicy(GetDefaultPasswordLockoutPolicyRequest) returns (GetDefaultPasswordLockoutPolicyResponse) {
option (google.api.http) = { option (google.api.http) = {
get: "/policies/default/password/lockout" get: "/policies/default/lockout"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
@ -1993,10 +1991,9 @@ service ManagementService {
}; };
} }
// The password lockout policy is not used at the moment rpc AddCustomLockoutPolicy(AddCustomLockoutPolicyRequest) returns (AddCustomLockoutPolicyResponse) {
rpc AddCustomPasswordLockoutPolicy(AddCustomPasswordLockoutPolicyRequest) returns (AddCustomPasswordLockoutPolicyResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/policies/password/lockout" post: "/policies/lockout"
body: "*" body: "*"
}; };
@ -2005,10 +2002,9 @@ service ManagementService {
}; };
} }
// The password lockout policy is not used at the moment rpc UpdateCustomLockoutPolicy(UpdateCustomLockoutPolicyRequest) returns (UpdateCustomLockoutPolicyResponse) {
rpc UpdateCustomPasswordLockoutPolicy(UpdateCustomPasswordLockoutPolicyRequest) returns (UpdateCustomPasswordLockoutPolicyResponse) {
option (google.api.http) = { option (google.api.http) = {
put: "/policies/password/lockout" put: "/policies/lockout"
body: "*" body: "*"
}; };
@ -2017,10 +2013,9 @@ service ManagementService {
}; };
} }
// The password lockout policy is not used at the moment rpc ResetLockoutPolicyToDefault(ResetLockoutPolicyToDefaultRequest) returns (ResetLockoutPolicyToDefaultResponse) {
rpc ResetPasswordLockoutPolicyToDefault(ResetPasswordLockoutPolicyToDefaultRequest) returns (ResetPasswordLockoutPolicyToDefaultResponse) {
option (google.api.http) = { option (google.api.http) = {
delete: "/policies/password/lockout" delete: "/policies/lockout"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
@ -4275,42 +4270,40 @@ message ResetPasswordAgePolicyToDefaultResponse {
} }
//This is an empty request //This is an empty request
message GetPasswordLockoutPolicyRequest {} message GetLockoutPolicyRequest {}
message GetPasswordLockoutPolicyResponse { message GetLockoutPolicyResponse {
zitadel.policy.v1.PasswordLockoutPolicy policy = 1; zitadel.policy.v1.LockoutPolicy policy = 1;
bool is_default = 2; bool is_default = 2;
} }
//This is an empty request //This is an empty request
message GetDefaultPasswordLockoutPolicyRequest {} message GetDefaultLockoutPolicyRequest {}
message GetDefaultPasswordLockoutPolicyResponse { message GetDefaultLockoutPolicyResponse {
zitadel.policy.v1.PasswordLockoutPolicy policy = 1; zitadel.policy.v1.LockoutPolicy policy = 1;
} }
message AddCustomPasswordLockoutPolicyRequest { message AddCustomLockoutPolicyRequest {
uint32 max_attempts = 1; uint32 max_password_attempts = 1;
bool show_lockout_failure = 2;
} }
message AddCustomPasswordLockoutPolicyResponse { message AddCustomLockoutPolicyResponse {
zitadel.v1.ObjectDetails details = 1; zitadel.v1.ObjectDetails details = 1;
} }
message UpdateCustomPasswordLockoutPolicyRequest { message UpdateCustomLockoutPolicyRequest {
uint32 max_attempts = 1; uint32 max_password_attempts = 1;
bool show_lockout_failure = 2;
} }
message UpdateCustomPasswordLockoutPolicyResponse { message UpdateCustomLockoutPolicyResponse {
zitadel.v1.ObjectDetails details = 1; zitadel.v1.ObjectDetails details = 1;
} }
//This is an empty request //This is an empty request
message ResetPasswordLockoutPolicyToDefaultRequest {} message ResetLockoutPolicyToDefaultRequest {}
message ResetPasswordLockoutPolicyToDefaultResponse { message ResetLockoutPolicyToDefaultResponse {
zitadel.v1.ObjectDetails details = 1; zitadel.v1.ObjectDetails details = 1;
} }

Some files were not shown because too many files have changed in this diff Show More