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 {
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")
apis := api.Create(conf.API, conf.InternalAuthZ, authZRepo, authRepo, repo, conf.SystemDefaults)

View File

@ -74,7 +74,7 @@ SetUp:
ExpireWarnDays: 0
Step4:
DefaultPasswordLockoutPolicy:
MaxAttempts: 5
MaxPasswordAttempts: 5
ShowLockOutFailures: false
Step5:
DefaultOrgIAMPolicy:
@ -193,3 +193,7 @@ SetUp:
PrivacyPolicy:
TOSLink: https://docs.zitadel.ch/docs/legal/terms-of-service
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",
"moment": "^2.29.1",
"ngx-color": "^7.2.0",
"ngx-image-cropper": "^3.3.5",
"ngx-quicklink": "^0.2.6",
"rxjs": "~6.6.7",
"tinycolor2": "^1.4.2",
@ -10368,6 +10369,24 @@
"@angular/core": ">=12.0.0-0"
}
},
"node_modules/ngx-image-cropper": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/ngx-image-cropper/-/ngx-image-cropper-3.3.5.tgz",
"integrity": "sha512-0yRVKG5XAbVo3rOaj/iFDlekGsxEqXKU9iXFbjyvHvRT2DFs+AjwtyvINsHCWw+4ed9yA4Y+wLIUNqzA0bfxLw==",
"dependencies": {
"tslib": "^1.9.0"
},
"peerDependencies": {
"@angular/common": ">=8.0.0",
"@angular/core": ">=8.0.0",
"@angular/platform-browser": ">=8.0.0"
}
},
"node_modules/ngx-image-cropper/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/ngx-quicklink": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz",
@ -27536,6 +27555,21 @@
"tslib": "^2.1.0"
}
},
"ngx-image-cropper": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/ngx-image-cropper/-/ngx-image-cropper-3.3.5.tgz",
"integrity": "sha512-0yRVKG5XAbVo3rOaj/iFDlekGsxEqXKU9iXFbjyvHvRT2DFs+AjwtyvINsHCWw+4ed9yA4Y+wLIUNqzA0bfxLw==",
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"ngx-quicklink": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/ngx-quicklink/-/ngx-quicklink-0.2.7.tgz",

View File

@ -1,10 +1,10 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.PWD_LOCKOUT.TITLE' | translate" [description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate">
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
<cnsl-info-section class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
<ng-template appHasRole [appHasRole]="['policy.delete']">
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="resetPolicy()" mat-stroked-button>
{{'POLICY.RESET' | translate}}
</button>
</ng-template>
@ -14,22 +14,15 @@
<span class="left-desc">{{'POLICY.DATA.MAXATTEMPTS' | translate}}</span>
<span class="fill-space"></span>
<div class="length-wrapper">
<button mat-icon-button (click)="incrementMaxAttempts()">
<mat-icon>add</mat-icon>
</button>
<span>{{lockoutData?.maxAttempts}}</span>
<button mat-icon-button (click)="decrementMaxAttempts()">
<mat-icon>remove</mat-icon>
</button>
<span>{{lockoutData?.maxPasswordAttempts}}</span>
<button mat-icon-button (click)="incrementMaxAttempts()">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
<div class="row">
<span class="left-desc">{{'POLICY.DATA.SHOWLOCKOUTFAILURES' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="showLockoutFailure" ngDefaultControl
[(ngModel)]="lockoutData.showLockoutFailure">
</mat-slide-toggle>
</div>
</div>
<div class="btn-container">

View File

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

View File

@ -3,13 +3,11 @@ import { FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { GetLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetPasswordLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse,
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetPasswordLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
GetLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { PasswordLockoutPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { LockoutPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@ -27,7 +25,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
public lockoutForm!: FormGroup;
public lockoutData!: PasswordLockoutPolicy.AsObject;
public lockoutData!: LockoutPolicy.AsObject;
private sub: Subscription = new Subscription();
public PolicyComponentServiceType: any = PolicyComponentServiceType;
@ -70,15 +68,15 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
Promise<AdminGetPasswordLockoutPolicyResponse.AsObject | MgmtGetPasswordLockoutPolicyResponse.AsObject> {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).getPasswordLockoutPolicy();
return (this.service as ManagementService).getLockoutPolicy();
case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).getPasswordLockoutPolicy();
return (this.service as AdminService).getLockoutPolicy();
}
}
public removePolicy(): void {
public resetPolicy(): void {
if (this.service instanceof ManagementService) {
this.service.resetPasswordLockoutPolicyToDefault().then(() => {
this.service.resetLockoutPolicyToDefault().then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
this.fetchData();
}).catch(error => {
@ -88,42 +86,39 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
}
public incrementMaxAttempts(): void {
if (this.lockoutData?.maxAttempts !== undefined) {
this.lockoutData.maxAttempts++;
if (this.lockoutData?.maxPasswordAttempts !== undefined) {
this.lockoutData.maxPasswordAttempts++;
}
}
public decrementMaxAttempts(): void {
if (this.lockoutData?.maxAttempts && this.lockoutData?.maxAttempts > 0) {
this.lockoutData.maxAttempts--;
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.updatePasswordLockoutPolicy(
this.lockoutData.maxAttempts,
this.lockoutData.showLockoutFailure,
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 PasswordLockoutPolicy.AsObject).isDefault) {
promise = this.service.addCustomPasswordLockoutPolicy(
this.lockoutData.maxAttempts,
this.lockoutData.showLockoutFailure,
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.updateCustomPasswordLockoutPolicy(
this.lockoutData.maxAttempts,
this.lockoutData.showLockoutFailure,
promise = this.service.updateCustomLockoutPolicy(
this.lockoutData.maxPasswordAttempts,
).then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
}).catch(error => {
@ -135,7 +130,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
public get isDefault(): boolean {
if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.lockoutData as PasswordLockoutPolicy.AsObject).isDefault;
return (this.lockoutData as LockoutPolicy.AsObject).isDefault;
} else {
return false;
}

View File

@ -9,8 +9,8 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { LinksModule } from '../../links/links.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { PasswordLockoutPolicyRoutingModule } from './password-lockout-policy-routing.module';
import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component';
@ -28,6 +28,7 @@ import { PasswordLockoutPolicyComponent } from './password-lockout-policy.compon
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
InfoSectionModule,
],
})
export class PasswordLockoutPolicyModule { }

View File

@ -25,6 +25,18 @@ export const COMPLEXITY_POLICY: GridPolicy = {
color: 'yellow',
};
export const LOCKOUT_POLICY: GridPolicy = {
i18nTitle: 'POLICY.PWD_LOCKOUT.TITLE',
i18nDesc: 'POLICY.PWD_LOCKOUT.DESCRIPTION',
iamRouterLink: ['/iam', 'policy', PolicyComponentType.LOCKOUT],
orgRouterLink: ['/org', 'policy', PolicyComponentType.LOCKOUT],
iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
tags: ['login', 'security'],
icon: 'las la-lock',
color: 'yellow',
};
export const IAM_POLICY = {
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
@ -99,6 +111,7 @@ export const LOGIN_TEXTS_POLICY = {
export const POLICIES: GridPolicy[] = [
COMPLEXITY_POLICY,
LOCKOUT_POLICY,
IAM_POLICY,
LOGIN_POLICY,
PRIVATELABEL_POLICY,

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 { ChangesModule } from 'src/app/modules/changes/changes.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { MachineKeysModule } from 'src/app/modules/machine-keys/machine-keys.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
@ -104,6 +105,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
LocalizedDatePipeModule,
InputModule,
MachineKeysModule,
InfoSectionModule,
],
})
export class UserDetailModule { }

View File

@ -14,6 +14,11 @@
<span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
<button class="unlock-button" mat-stroked-button color="warn"
*ngIf="user?.state === UserState.USER_STATE_LOCKED"
(click)="unlockUser()">{{'USER.PAGES.UNLOCK' |
translate}}</button>
<button class="state-button" mat-stroked-button color="warn"
*ngIf="user?.state !== UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
@ -26,6 +31,7 @@
<mat-progress-bar *ngIf="loading" color="primary" mode="indeterminate"></mat-progress-bar>
<cnsl-info-section class="locked" *ngIf="user?.state === UserState.USER_STATE_LOCKED" type="WARN">{{'USER.PAGES.LOCKEDDESCRIPTION' | translate}}</cnsl-info-section>
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
<app-card title="{{ 'USER.PAGES.LOGINNAMES' | translate }}"

View File

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

View File

@ -7,7 +7,7 @@ import { take } from 'rxjs/operators';
import { ChangeType } from 'src/app/modules/changes/changes.component';
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { SendHumanResetPasswordNotificationRequest } from 'src/app/proto/generated/zitadel/management_pb';
import { SendHumanResetPasswordNotificationRequest, UnlockUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
import { Email, Gender, Machine, Phone, Profile, User, UserState } from 'src/app/proto/generated/zitadel/user_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@ -63,6 +63,17 @@ export class UserDetailComponent implements OnInit {
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(() => {

View File

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

View File

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

View File

@ -174,7 +174,9 @@
"REACTIVATE": "Reaktivieren",
"DEACTIVATE": "Deaktivieren",
"FILTER": "Filter",
"DELETE": "Benutzer löschen"
"DELETE": "Benutzer löschen",
"UNLOCK": "Benutzer entsperren",
"LOCKEDDESCRIPTION":"Dieser Benutzer wurde aufgrund der Überschreitung der maximalen Anmeldeversuche gesperrt und muss zur erneuten Verwendung entsperrt werden."
},
"DIALOG": {
"DELETE_TITLE": "User löschen",
@ -421,7 +423,8 @@
"STATE": {
"0": "Unbekannt",
"1": "Aktiv",
"2": "Abgelaufen"
"2": "Abgelaufen",
"4": "Gesperrt"
},
"SEARCH": {
"FOUND": "Gefunden"
@ -460,7 +463,8 @@
"SELECTEDKEYSDELETED": "Selektierte Schlüssel gelöscht.",
"KEYADDED": "Schlüssel hinzugefügt!",
"MACHINEADDED": "Service User erstellt!",
"DELETED": "Benutzer erfolgreich gelöscht!"
"DELETED": "Benutzer erfolgreich gelöscht!",
"UNLOCKED":"Benutzer erfolgreich freigeschaltet!"
},
"MEMBERSHIPS": {
"TITLE": "ZITADEL Manager-Rollen",
@ -688,7 +692,7 @@
},
"PWD_LOCKOUT": {
"TITLE": "Passwortsperre",
"DESCRIPTION": "Standardmässig sind die Passwortwiederholungen bei Falscheingabe nicht begrenzt. Du musst diese Richtlinie installieren, wenn Du Wiederholungsversuche anzeigen, oder eine maximale Anzahl von wiederholten Passworteingaben festlegen möchtest."
"DESCRIPTION": "Lege eine maximale Anzahl an Passwordwiederholungen fest, nachdem Accounts gesperrt werden sollen."
},
"IAM_POLICY": {
"TITLE": "Zugangseinstellungen IAM",

View File

@ -174,7 +174,9 @@
"REACTIVATE": "Reactivate",
"DEACTIVATE": "Deactivate",
"FILTER": "Filter",
"DELETE": "Delete User"
"DELETE": "Delete User",
"UNLOCK": "Unlock User",
"LOCKEDDESCRIPTION":"This user has been locked out due to exceeding the maximum login attempts and must be unlocked to be used again."
},
"DIALOG": {
"DELETE_TITLE": "Delete User",
@ -421,7 +423,8 @@
"STATE": {
"0": "Unknown",
"1": "Active",
"2": "Expired"
"2": "Expired",
"4": "Locked"
},
"SEARCH": {
"FOUND": "Found"
@ -460,7 +463,8 @@
"SELECTEDKEYSDELETED": "Selected keys deleted.",
"KEYADDED": "Key added!",
"MACHINEADDED": "Service User created!",
"DELETED": "User deleted successfully!"
"DELETED": "User deleted successfully!",
"UNLOCKED":"User unlocked successfully!"
},
"MEMBERSHIPS": {
"TITLE": "ZITADEL Manager Roles",
@ -688,7 +692,7 @@
},
"PWD_LOCKOUT": {
"TITLE": "Password Lockout",
"DESCRIPTION": "Password retries are infinite in default mode. You have to apply this policy if you want to show the number of retries, or set a maximum number of retries after which the account will be blocked."
"DESCRIPTION": "Set a maximum number of passwordretries, after which accounts will be blocked."
},
"IAM_POLICY": {
"TITLE": "IAM Access Preferences",

View File

@ -615,24 +615,24 @@ it impacts all organisations without a customised policy
PUT: /policies/password/age
### GetPasswordLockoutPolicy
### GetLockoutPolicy
> **rpc** GetPasswordLockoutPolicy([GetPasswordLockoutPolicyRequest](#getpasswordlockoutpolicyrequest))
[GetPasswordLockoutPolicyResponse](#getpasswordlockoutpolicyresponse)
> **rpc** GetLockoutPolicy([GetLockoutPolicyRequest](#getlockoutpolicyrequest))
[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))
[UpdatePasswordLockoutPolicyResponse](#updatepasswordlockoutpolicyresponse)
> **rpc** UpdateLockoutPolicy([UpdateLockoutPolicyRequest](#updatelockoutpolicyrequest))
[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
@ -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
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
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
@ -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

View File

@ -1926,64 +1926,64 @@ The password age policy is not used at the moment
DELETE: /policies/password/age
### GetPasswordLockoutPolicy
### GetLockoutPolicy
> **rpc** GetPasswordLockoutPolicy([GetPasswordLockoutPolicyRequest](#getpasswordlockoutpolicyrequest))
[GetPasswordLockoutPolicyResponse](#getpasswordlockoutpolicyresponse)
The password lockout policy is not used at the moment
> **rpc** GetLockoutPolicy([GetLockoutPolicyRequest](#getlockoutpolicyrequest))
[GetLockoutPolicyResponse](#getlockoutpolicyresponse)
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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
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
@ -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
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
@ -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

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
@ -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

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)
## 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
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
}
func (repo *IAMRepository) GetDefaultPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) {
policy, viewErr := repo.View.PasswordLockoutPolicyByAggregateID(repo.SystemDefaults.IamID)
func (repo *IAMRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
policy, viewErr := repo.View.LockoutPolicyByAggregateID(repo.SystemDefaults.IamID)
if viewErr != nil && !caos_errs.IsNotFound(viewErr) {
return nil, 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)
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 {
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
for _, event := range events {
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) {

View File

@ -3,6 +3,7 @@ package handler
import (
"time"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/static"
@ -31,7 +32,7 @@ func (h *handler) Eventstore() v1.Eventstore {
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{
newOrg(
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}),
newPasswordAgePolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}),
newPasswordLockoutPolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount, es}),
newLockoutPolicy(
handler{view, bulkLimit, configs.cycleDuration("LockoutPolicy"), errorCount, es}),
newOrgIAMPolicy(
handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}),
newExternalIDP(

View File

@ -13,16 +13,16 @@ import (
)
const (
passwordLockoutPolicyTable = "adminapi.password_lockout_policies"
lockoutPolicyTable = "adminapi.lockout_policies"
)
type PasswordLockoutPolicy struct {
type LockoutPolicy struct {
handler
subscription *v1.Subscription
}
func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy {
h := &PasswordLockoutPolicy{
func newLockoutPolicy(handler handler) *LockoutPolicy {
h := &LockoutPolicy{
handler: handler,
}
@ -31,7 +31,7 @@ func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy {
return h
}
func (p *PasswordLockoutPolicy) subscribe() {
func (p *LockoutPolicy) subscribe() {
p.subscription = p.es.Subscribe(p.AggregateTypes()...)
go func() {
for event := range p.subscription.Events {
@ -40,28 +40,28 @@ func (p *PasswordLockoutPolicy) subscribe() {
}()
}
func (p *PasswordLockoutPolicy) ViewModel() string {
return passwordLockoutPolicyTable
func (p *LockoutPolicy) ViewModel() string {
return lockoutPolicyTable
}
func (m *PasswordLockoutPolicy) Subscription() *v1.Subscription {
func (m *LockoutPolicy) Subscription() *v1.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}
}
func (p *PasswordLockoutPolicy) CurrentSequence() (uint64, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence()
func (p *LockoutPolicy) CurrentSequence() (uint64, error) {
sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence()
func (p *LockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil {
return nil, err
}
@ -70,41 +70,41 @@ func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
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 {
case model.OrgAggregate, iam_es_model.IAMAggregate:
err = p.processPasswordLockoutPolicy(event)
err = p.processLockoutPolicy(event)
}
return err
}
func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *es_models.Event) (err error) {
policy := new(iam_model.PasswordLockoutPolicyView)
func (p *LockoutPolicy) processLockoutPolicy(event *es_models.Event) (err error) {
policy := new(iam_model.LockoutPolicyView)
switch event.Type {
case iam_es_model.PasswordLockoutPolicyAdded, model.PasswordLockoutPolicyAdded:
case iam_es_model.LockoutPolicyAdded, model.LockoutPolicyAdded:
err = policy.AppendEvent(event)
case iam_es_model.PasswordLockoutPolicyChanged, model.PasswordLockoutPolicyChanged:
policy, err = p.view.PasswordLockoutPolicyByAggregateID(event.AggregateID)
case iam_es_model.LockoutPolicyChanged, model.LockoutPolicyChanged:
policy, err = p.view.LockoutPolicyByAggregateID(event.AggregateID)
if err != nil {
return err
}
err = policy.AppendEvent(event)
case model.PasswordLockoutPolicyRemoved:
return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event)
case model.LockoutPolicyRemoved:
return p.view.DeleteLockoutPolicy(event.AggregateID, event)
default:
return p.view.ProcessedPasswordLockoutPolicySequence(event)
return p.view.ProcessedLockoutPolicySequence(event)
}
if err != nil {
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 {
logging.LogWithFields("SPOOL-nD8sie", "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)
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 Lockout policy handler")
return spooler.HandleError(event, err, p.view.GetLatestLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicyFailedEvent, p.view.ProcessedLockoutPolicySequence, p.errorCountUntilSkip)
}
func (p *PasswordLockoutPolicy) OnSuccess() error {
return spooler.HandleSuccess(p.view.UpdatePasswordLockoutPolicySpoolerRunTimestamp)
func (p *LockoutPolicy) OnSuccess() error {
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/spooler"
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"
"github.com/caos/zitadel/internal/config/types"
"github.com/caos/zitadel/internal/eventstore/v1"
@ -34,7 +35,7 @@ type EsRepository struct {
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)
if err != nil {
return nil, err
@ -48,7 +49,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, s
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/"
statikLoginFS, err := fs.NewWithNamespace("login")

View File

@ -2,6 +2,7 @@ package spooler
import (
"database/sql"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/static"
@ -18,12 +19,12 @@ type SpoolerConfig struct {
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{
Eventstore: es,
Locker: &locker{dbClient: sql},
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.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)
GetDefaultPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error)
GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, 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"
)
func ModelPasswordLockoutPolicyToPb(policy *model.PasswordLockoutPolicyView) *policy_pb.PasswordLockoutPolicy {
return &policy_pb.PasswordLockoutPolicy{
func ModelLockoutPolicyToPb(policy *model.LockoutPolicyView) *policy_pb.LockoutPolicy {
return &policy_pb.LockoutPolicy{
IsDefault: policy.Default,
MaxAttempts: policy.MaxAttempts,
ShowLockoutFailure: policy.ShowLockOutFailures,
MaxPasswordAttempts: policy.MaxPasswordAttempts,
Details: object.ToViewDetailsPb(
policy.Sequence,
policy.CreationDate,

View File

@ -41,6 +41,7 @@ type AuthRequestRepo struct {
UserEventProvider userEventProvider
OrgViewProvider orgViewProvider
LoginPolicyViewProvider loginPolicyViewProvider
LockoutPolicyViewProvider lockoutPolicyViewProvider
IDPProviderViewProvider idpProviderViewProvider
UserGrantProvider userGrantProvider
@ -69,6 +70,10 @@ type loginPolicyViewProvider interface {
LoginPolicyByAggregateID(string) (*iam_view_model.LoginPolicyView, error)
}
type lockoutPolicyViewProvider interface {
LockoutPolicyByAggregateID(string) (*iam_view_model.LockoutPolicyView, error)
}
type idpProviderViewProvider interface {
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 {
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 {
return err
}
@ -262,7 +267,11 @@ func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, res
if err != nil {
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) {
@ -414,6 +423,10 @@ func (repo *AuthRequestRepo) getAuthRequestEnsureUser(ctx context.Context, authR
if request.UserID != userID {
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
}
@ -466,6 +479,11 @@ func (repo *AuthRequestRepo) fillPolicies(ctx context.Context, request *domain.A
if idpProviders != nil {
request.AllowedExternalIDPs = idpProviders
}
lockoutPolicy, err := repo.getLockoutPolicy(ctx, orgID)
if err != nil {
return err
}
request.LockoutPolicy = lockoutPolicy
privacyPolicy, err := repo.getPrivacyPolicy(ctx, orgID)
if err != nil {
return err
@ -587,7 +605,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
}
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 {
return nil, err
}
@ -795,6 +813,21 @@ func (repo *AuthRequestRepo) getPrivacyPolicy(ctx context.Context, orgID string)
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) {
policy, err := repo.View.LabelPolicyByAggregateIDAndState(orgID, int32(domain.LabelPolicyStateActive))
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
}
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)
if err != nil {
return nil, err
@ -930,7 +964,6 @@ func activeUserByID(ctx context.Context, userViewProvider userViewProvider, user
if user.HumanView == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-Lm69x", "Errors.User.NotHuman")
}
if user.State == user_model.UserStateLocked || user.State == user_model.UserStateSuspend {
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
}
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) {
return &user_view_model.UserView{
State: int32(user_model.UserStateActive),
@ -229,6 +237,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
orgViewProvider orgViewProvider
userGrantProvider userGrantProvider
loginPolicyProvider loginPolicyViewProvider
lockoutPolicyProvider lockoutPolicyViewProvider
PasswordCheckLifeTime time.Duration
ExternalLoginCheckLifeTime time.Duration
MFAInitSkippedLifeTime time.Duration
@ -404,6 +413,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
},
args{&domain.AuthRequest{UserID: "UserID"}, false},
nil,
@ -420,6 +434,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
},
args{&domain.AuthRequest{UserID: "UserID"}, false},
nil,
@ -431,6 +450,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewErrOrg{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
},
args{&domain.AuthRequest{UserID: "UserID"}, false},
nil,
@ -442,6 +466,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateInactive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
},
args{&domain.AuthRequest{UserID: "UserID"}, false},
nil,
@ -456,6 +485,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.PasswordStep{}},
@ -468,6 +502,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
},
args{&domain.AuthRequest{UserID: "UserID"}, false},
nil,
@ -483,6 +522,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
},
args{&domain.AuthRequest{UserID: "UserID"}, false},
[]domain.NextStep{&domain.InitUserStep{
@ -500,6 +544,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
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},
[]domain.NextStep{&domain.PasswordlessRegistrationPromptStep{}},
@ -514,6 +563,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
MultiFactorCheckLifeTime: 10 * time.Hour,
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
@ -530,6 +584,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
MultiFactorCheckLifeTime: 10 * time.Hour,
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{PasswordlessType: domain.PasswordlessTypeAllowed}}, false},
@ -551,6 +610,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelMultiFactor),
},
userEventProvider: &mockEventUser{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour,
},
@ -572,6 +636,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordInitRequired: true,
},
userEventProvider: &mockEventUser{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
@ -589,6 +658,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
},
userEventProvider: &mockEventUser{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -613,6 +687,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
loginPolicyProvider: &mockLoginPolicy{
policy: &iam_view_model.LoginPolicyView{},
},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -636,6 +715,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
},
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
@ -657,6 +741,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
SecondFactorCheckLifeTime: 18 * time.Hour,
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
},
@ -684,6 +773,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -712,6 +806,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -741,6 +840,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
@ -773,6 +877,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -799,6 +908,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -825,6 +939,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -852,6 +971,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -880,6 +1004,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -912,6 +1041,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
roleCheck: true,
userGrants: 0,
},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -944,6 +1078,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
roleCheck: true,
userGrants: 2,
},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
@ -969,6 +1108,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
IsEmailVerified: true,
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
SecondFactorCheckLifeTime: 18 * time.Hour,
@ -997,6 +1141,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
},
},
SecondFactorCheckLifeTime: 18 * time.Hour,
PasswordCheckLifeTime: 10 * 24 * time.Hour,
},
@ -1024,6 +1173,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
OrgViewProvider: tt.fields.orgViewProvider,
UserGrantProvider: tt.fields.userGrantProvider,
LoginPolicyViewProvider: tt.fields.loginPolicyProvider,
LockoutPolicyViewProvider: tt.fields.lockoutPolicyProvider,
PasswordCheckLifeTime: tt.fields.PasswordCheckLifeTime,
ExternalLoginCheckLifeTime: tt.fields.ExternalLoginCheckLifeTime,
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}),
newCustomText(handler{view, bulkLimit, configs.cycleDuration("CustomTexts"), 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,
IDPProviderViewProvider: view,
LoginPolicyViewProvider: view,
LockoutPolicyViewProvider: view,
UserGrantProvider: view,
IdGenerator: idGenerator,
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 {
return &domain.PasswordLockoutPolicy{
func writeModelToLockoutPolicy(wm *LockoutPolicyWriteModel) *domain.LockoutPolicy {
return &domain.LockoutPolicy{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
MaxAttempts: wm.MaxAttempts,
MaxPasswordAttempts: wm.MaxPasswordAttempts,
ShowLockOutFailures: wm.ShowLockOutFailures,
}
}

View File

@ -9,10 +9,10 @@ import (
"github.com/caos/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddDefaultPasswordLockoutPolicy(ctx context.Context, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
addedPolicy := NewIAMPasswordLockoutPolicyWriteModel()
func (c *Commands) AddDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
addedPolicy := NewIAMLockoutPolicyWriteModel()
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.WriteModel)
event, err := c.addDefaultPasswordLockoutPolicy(ctx, iamAgg, addedPolicy, policy)
event, err := c.addDefaultLockoutPolicy(ctx, iamAgg, addedPolicy, policy)
if err != nil {
return nil, err
}
@ -25,34 +25,34 @@ func (c *Commands) AddDefaultPasswordLockoutPolicy(ctx context.Context, policy *
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)
if err != nil {
return nil, err
}
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) {
existingPolicy, err := c.defaultPasswordLockoutPolicyWriteModelByID(ctx)
func (c *Commands) ChangeDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
existingPolicy, err := c.defaultLockoutPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
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)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.MaxAttempts, policy.ShowLockOutFailures)
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
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)
@ -63,14 +63,14 @@ func (c *Commands) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, polic
if err != nil {
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)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMPasswordLockoutPolicyWriteModel()
writeModel := NewIAMLockoutPolicyWriteModel()
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err

View File

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

View File

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

View File

@ -7,21 +7,21 @@ import (
"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 == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-8fJif", "Errors.ResourceOwnerMissing")
}
addedPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
addedPolicy := NewOrgLockoutPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
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)
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 {
return nil, err
}
@ -29,26 +29,26 @@ func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner s
if err != nil {
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 == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3J9fs", "Errors.ResourceOwnerMissing")
}
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
existingPolicy := NewOrgLockoutPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
if err != nil {
return nil, err
}
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)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxAttempts, policy.ShowLockOutFailures)
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
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)
@ -59,23 +59,23 @@ func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwne
if err != nil {
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 == "" {
return caos_errs.ThrowInvalidArgument(nil, "Org-4J9fs", "Errors.ResourceOwnerMissing")
}
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(orgID)
existingPolicy := NewOrgLockoutPolicyWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
if err != nil {
return err
}
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)
_, err = c.eventstore.PushEvents(ctx, org.NewPasswordLockoutPolicyRemovedEvent(ctx, orgAgg))
_, err = c.eventstore.PushEvents(ctx, org.NewLockoutPolicyRemovedEvent(ctx, orgAgg))
return err
}

View File

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

View File

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

View File

@ -6,29 +6,29 @@ import (
"github.com/caos/zitadel/internal/repository/policy"
)
type PasswordLockoutPolicyWriteModel struct {
type LockoutPolicyWriteModel struct {
eventstore.WriteModel
MaxAttempts uint64
MaxPasswordAttempts uint64
ShowLockOutFailures bool
State domain.PolicyState
}
func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
func (wm *LockoutPolicyWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.PasswordLockoutPolicyAddedEvent:
wm.MaxAttempts = e.MaxAttempts
case *policy.LockoutPolicyAddedEvent:
wm.MaxPasswordAttempts = e.MaxPasswordAttempts
wm.ShowLockOutFailures = e.ShowLockOutFailures
wm.State = domain.PolicyStateActive
case *policy.PasswordLockoutPolicyChangedEvent:
if e.MaxAttempts != nil {
wm.MaxAttempts = *e.MaxAttempts
case *policy.LockoutPolicyChangedEvent:
if e.MaxPasswordAttempts != nil {
wm.MaxPasswordAttempts = *e.MaxPasswordAttempts
}
if e.ShowLockOutFailures != nil {
wm.ShowLockOutFailures = *e.ShowLockOutFailures
}
case *policy.PasswordLockoutPolicyRemovedEvent:
case *policy.LockoutPolicyRemovedEvent:
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 (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
)
type Step4 struct {
DefaultPasswordLockoutPolicy domain.PasswordLockoutPolicy
DefaultPasswordLockoutPolicy domain.LockoutPolicy
}
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 {
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 {
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel)
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 nil, nil
}
return c.setup(ctx, step, fn)
}

View File

@ -194,7 +194,7 @@ func (c *Commands) PasswordCodeSent(ctx context.Context, orgID, userID string) (
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)
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)))
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")
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-452ad", "Errors.User.Password.Invalid")
}

View File

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

View File

@ -1082,6 +1082,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
resourceOwner string
password string
authReq *domain.AuthRequest
lockoutPolicy *domain.LockoutPolicy
}
type res struct {
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{
eventstore: eventstoreExpect(
t,
@ -1238,6 +1239,82 @@ func TestCommandSide_CheckPassword(t *testing.T) {
ID: "request1",
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{
err: caos_errs.IsErrorInvalidArgument,
@ -1315,7 +1392,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
eventstore: tt.fields.eventstore,
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 {
assert.NoError(t, err)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,7 +36,7 @@ type IAM struct {
DefaultOrgIAMPolicy *OrgIAMPolicy `json:"-"`
DefaultPasswordComplexityPolicy *PasswordComplexityPolicy `json:"-"`
DefaultPasswordAgePolicy *PasswordAgePolicy `json:"-"`
DefaultPasswordLockoutPolicy *PasswordLockoutPolicy `json:"-"`
DefaultLockoutPolicy *LockoutPolicy `json:"-"`
}
func IAMToModel(iam *IAM) *model.IAM {
@ -66,8 +66,8 @@ func IAMToModel(iam *IAM) *model.IAM {
if iam.DefaultPasswordAgePolicy != nil {
converted.DefaultPasswordAgePolicy = PasswordAgePolicyToModel(iam.DefaultPasswordAgePolicy)
}
if iam.DefaultPasswordLockoutPolicy != nil {
converted.DefaultPasswordLockoutPolicy = PasswordLockoutPolicyToModel(iam.DefaultPasswordLockoutPolicy)
if iam.DefaultLockoutPolicy != nil {
converted.DefaultLockoutPolicy = LockoutPolicyToModel(iam.DefaultLockoutPolicy)
}
if iam.DefaultOrgIAMPolicy != nil {
converted.DefaultOrgIAMPolicy = OrgIAMPolicyToModel(iam.DefaultOrgIAMPolicy)
@ -166,10 +166,10 @@ func (i *IAM) AppendEvent(event *es_models.Event) (err error) {
return i.appendAddPasswordAgePolicyEvent(event)
case PasswordAgePolicyChanged:
return i.appendChangePasswordAgePolicyEvent(event)
case PasswordLockoutPolicyAdded:
return i.appendAddPasswordLockoutPolicyEvent(event)
case PasswordLockoutPolicyChanged:
return i.appendChangePasswordLockoutPolicyEvent(event)
case LockoutPolicyAdded:
return i.appendAddLockoutPolicyEvent(event)
case LockoutPolicyChanged:
return i.appendChangeLockoutPolicyEvent(event)
case OrgIAMPolicyAdded:
return i.appendAddOrgIAMPolicyEvent(event)
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) {
type args struct {
existing *PasswordLockoutPolicy
new *PasswordLockoutPolicy
existing *LockoutPolicy
new *LockoutPolicy
}
type res struct {
changesLen int
@ -22,8 +22,8 @@ func TestPasswordLockoutPolicyChanges(t *testing.T) {
{
name: "lockout policy all attributes change",
args: args{
existing: &PasswordLockoutPolicy{MaxAttempts: 365, ShowLockOutFailures: true},
new: &PasswordLockoutPolicy{MaxAttempts: 730, ShowLockOutFailures: false},
existing: &LockoutPolicy{MaxPasswordAttempts: 365, ShowLockOutFailures: true},
new: &LockoutPolicy{MaxPasswordAttempts: 730, ShowLockOutFailures: false},
},
res: res{
changesLen: 2,
@ -32,8 +32,8 @@ func TestPasswordLockoutPolicyChanges(t *testing.T) {
{
name: "no changes",
args: args{
existing: &PasswordLockoutPolicy{MaxAttempts: 10, ShowLockOutFailures: true},
new: &PasswordLockoutPolicy{MaxAttempts: 10, ShowLockOutFailures: true},
existing: &LockoutPolicy{MaxPasswordAttempts: 10, ShowLockOutFailures: true},
new: &LockoutPolicy{MaxPasswordAttempts: 10, ShowLockOutFailures: true},
},
res: res{
changesLen: 0,
@ -53,7 +53,7 @@ func TestPasswordLockoutPolicyChanges(t *testing.T) {
func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
type args struct {
iam *IAM
policy *PasswordLockoutPolicy
policy *LockoutPolicy
event *es_models.Event
}
tests := []struct {
@ -65,10 +65,10 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
name: "append add password lockout policy event",
args: args{
iam: new(IAM),
policy: &PasswordLockoutPolicy{MaxAttempts: 10, ShowLockOutFailures: true},
policy: &LockoutPolicy{MaxPasswordAttempts: 10, ShowLockOutFailures: true},
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 {
@ -77,12 +77,12 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data
}
tt.args.iam.appendAddPasswordLockoutPolicyEvent(tt.args.event)
if tt.result.DefaultPasswordLockoutPolicy.MaxAttempts != tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultPasswordLockoutPolicy.MaxAttempts, tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts)
tt.args.iam.appendAddLockoutPolicyEvent(tt.args.event)
if tt.result.DefaultLockoutPolicy.MaxPasswordAttempts != tt.args.iam.DefaultLockoutPolicy.MaxPasswordAttempts {
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 {
t.Errorf("got wrong result: expected: %v, actual: %v ", 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.DefaultLockoutPolicy.ShowLockOutFailures, tt.args.iam.DefaultLockoutPolicy.ShowLockOutFailures)
}
})
}
@ -91,7 +91,7 @@ func TestAppendAddPasswordLockoutPolicyEvent(t *testing.T) {
func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
type args struct {
iam *IAM
policy *PasswordLockoutPolicy
policy *LockoutPolicy
event *es_models.Event
}
tests := []struct {
@ -102,14 +102,14 @@ func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
{
name: "append change password lockout policy event",
args: args{
iam: &IAM{DefaultPasswordLockoutPolicy: &PasswordLockoutPolicy{
MaxAttempts: 10,
iam: &IAM{DefaultLockoutPolicy: &LockoutPolicy{
MaxPasswordAttempts: 10,
}},
policy: &PasswordLockoutPolicy{MaxAttempts: 5},
policy: &LockoutPolicy{MaxPasswordAttempts: 5},
event: &es_models.Event{},
},
result: &IAM{DefaultPasswordLockoutPolicy: &PasswordLockoutPolicy{
MaxAttempts: 5,
result: &IAM{DefaultLockoutPolicy: &LockoutPolicy{
MaxPasswordAttempts: 5,
}},
},
}
@ -119,9 +119,9 @@ func TestAppendChangePasswordLockoutPolicyEvent(t *testing.T) {
data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data
}
tt.args.iam.appendChangePasswordLockoutPolicyEvent(tt.args.event)
if tt.result.DefaultPasswordLockoutPolicy.MaxAttempts != tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultPasswordLockoutPolicy.MaxAttempts, tt.args.iam.DefaultPasswordLockoutPolicy.MaxAttempts)
tt.args.iam.appendChangeLockoutPolicyEvent(tt.args.event)
if tt.result.DefaultLockoutPolicy.MaxPasswordAttempts != tt.args.iam.DefaultLockoutPolicy.MaxPasswordAttempts {
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"
PasswordAgePolicyChanged models.EventType = "iam.policy.password.age.changed"
PasswordLockoutPolicyAdded models.EventType = "iam.policy.password.lockout.added"
PasswordLockoutPolicyChanged models.EventType = "iam.policy.password.lockout.changed"
LockoutPolicyAdded models.EventType = "iam.policy.lockout.added"
LockoutPolicyChanged models.EventType = "iam.policy.lockout.changed"
PrivacyPolicyAdded models.EventType = "iam.policy.privacy.added"
PrivacyPolicyChanged models.EventType = "iam.policy.privacy.changed"

View File

@ -2,6 +2,7 @@ package model
import (
"encoding/json"
"github.com/caos/zitadel/internal/domain"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"time"
@ -14,65 +15,67 @@ import (
)
const (
PasswordLockoutKeyAggregateID = "aggregate_id"
LockoutKeyAggregateID = "aggregate_id"
)
type PasswordLockoutPolicyView struct {
type LockoutPolicyView struct {
AggregateID string `json:"-" gorm:"column:aggregate_id;primary_key"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
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"`
Default bool `json:"-" gorm:"-"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
}
func PasswordLockoutViewFromModel(policy *model.PasswordLockoutPolicyView) *PasswordLockoutPolicyView {
return &PasswordLockoutPolicyView{
func LockoutViewToModel(policy *LockoutPolicyView) *model.LockoutPolicyView {
return &model.LockoutPolicyView{
AggregateID: policy.AggregateID,
Sequence: policy.Sequence,
CreationDate: policy.CreationDate,
ChangeDate: policy.ChangeDate,
MaxAttempts: policy.MaxAttempts,
MaxPasswordAttempts: policy.MaxPasswordAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures,
Default: policy.Default,
}
}
func PasswordLockoutViewToModel(policy *PasswordLockoutPolicyView) *model.PasswordLockoutPolicyView {
return &model.PasswordLockoutPolicyView{
AggregateID: policy.AggregateID,
Sequence: policy.Sequence,
CreationDate: policy.CreationDate,
ChangeDate: policy.ChangeDate,
MaxAttempts: policy.MaxAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures,
Default: policy.Default,
func (p *LockoutPolicyView) ToDomain() *domain.LockoutPolicy {
return &domain.LockoutPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: p.AggregateID,
CreationDate: p.CreationDate,
ChangeDate: p.ChangeDate,
Sequence: p.Sequence,
},
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.ChangeDate = event.CreationDate
switch event.Type {
case es_model.PasswordLockoutPolicyAdded, org_es_model.PasswordLockoutPolicyAdded:
case es_model.LockoutPolicyAdded, org_es_model.LockoutPolicyAdded:
i.setRootData(event)
i.CreationDate = event.CreationDate
err = i.SetData(event)
case es_model.PasswordLockoutPolicyChanged, org_es_model.PasswordLockoutPolicyChanged:
case es_model.LockoutPolicyChanged, org_es_model.LockoutPolicyChanged:
err = i.SetData(event)
}
return err
}
func (r *PasswordLockoutPolicyView) setRootData(event *models.Event) {
func (r *LockoutPolicyView) setRootData(event *models.Event) {
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 {
logging.Log("EVEN-gHls0").WithError(err).Error("could not unmarshal event 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"
)
type PasswordLockoutPolicySearchRequest iam_model.PasswordLockoutPolicySearchRequest
type PasswordLockoutPolicySearchQuery iam_model.PasswordLockoutPolicySearchQuery
type PasswordLockoutPolicySearchKey iam_model.PasswordLockoutPolicySearchKey
type LockoutPolicySearchRequest iam_model.LockoutPolicySearchRequest
type LockoutPolicySearchQuery iam_model.LockoutPolicySearchQuery
type LockoutPolicySearchKey iam_model.LockoutPolicySearchKey
func (req PasswordLockoutPolicySearchRequest) GetLimit() uint64 {
func (req LockoutPolicySearchRequest) GetLimit() uint64 {
return req.Limit
}
func (req PasswordLockoutPolicySearchRequest) GetOffset() uint64 {
func (req LockoutPolicySearchRequest) GetOffset() uint64 {
return req.Offset
}
func (req PasswordLockoutPolicySearchRequest) GetSortingColumn() repository.ColumnKey {
if req.SortingColumn == iam_model.PasswordLockoutPolicySearchKeyUnspecified {
func (req LockoutPolicySearchRequest) GetSortingColumn() repository.ColumnKey {
if req.SortingColumn == iam_model.LockoutPolicySearchKeyUnspecified {
return nil
}
return PasswordLockoutPolicySearchKey(req.SortingColumn)
return LockoutPolicySearchKey(req.SortingColumn)
}
func (req PasswordLockoutPolicySearchRequest) GetAsc() bool {
func (req LockoutPolicySearchRequest) GetAsc() bool {
return req.Asc
}
func (req PasswordLockoutPolicySearchRequest) GetQueries() []repository.SearchQuery {
func (req LockoutPolicySearchRequest) GetQueries() []repository.SearchQuery {
result := make([]repository.SearchQuery, len(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
}
func (req PasswordLockoutPolicySearchQuery) GetKey() repository.ColumnKey {
return PasswordLockoutPolicySearchKey(req.Key)
func (req LockoutPolicySearchQuery) GetKey() repository.ColumnKey {
return LockoutPolicySearchKey(req.Key)
}
func (req PasswordLockoutPolicySearchQuery) GetMethod() domain.SearchMethod {
func (req LockoutPolicySearchQuery) GetMethod() domain.SearchMethod {
return req.Method
}
func (req PasswordLockoutPolicySearchQuery) GetValue() interface{} {
func (req LockoutPolicySearchQuery) GetValue() interface{} {
return req.Value
}
func (key PasswordLockoutPolicySearchKey) ToColumnName() string {
switch iam_model.PasswordLockoutPolicySearchKey(key) {
case iam_model.PasswordLockoutPolicySearchKeyAggregateID:
return PasswordLockoutKeyAggregateID
func (key LockoutPolicySearchKey) ToColumnName() string {
switch iam_model.LockoutPolicySearchKey(key) {
case iam_model.LockoutPolicySearchKeyAggregateID:
return LockoutKeyAggregateID
default:
return ""
}

View File

@ -9,24 +9,24 @@ import (
"github.com/jinzhu/gorm"
)
func GetPasswordLockoutPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.PasswordLockoutPolicyView, error) {
policy := new(model.PasswordLockoutPolicyView)
aggregateIDQuery := &model.PasswordLockoutPolicySearchQuery{Key: iam_model.PasswordLockoutPolicySearchKeyAggregateID, Value: aggregateID, Method: domain.SearchMethodEquals}
func GetLockoutPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.LockoutPolicyView, error) {
policy := new(model.LockoutPolicyView)
aggregateIDQuery := &model.LockoutPolicySearchQuery{Key: iam_model.LockoutPolicySearchKeyAggregateID, Value: aggregateID, Method: domain.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy)
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
}
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)
return save(db, policy)
}
func DeletePasswordLockoutPolicy(db *gorm.DB, table, aggregateID string) error {
delete := repository.PrepareDeleteByKey(table, model.PasswordLockoutPolicySearchKey(iam_model.PasswordLockoutPolicySearchKeyAggregateID), aggregateID)
func DeleteLockoutPolicy(db *gorm.DB, table, aggregateID string) error {
delete := repository.PrepareDeleteByKey(table, model.LockoutPolicySearchKey(iam_model.LockoutPolicySearchKeyAggregateID), aggregateID)
return delete(db)
}

View File

@ -501,55 +501,55 @@ func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*ia
return iam_es_model.PasswordAgeViewToModel(policy), nil
}
func (repo *OrgRepository) GetPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error) {
policy, viewErr := repo.View.PasswordLockoutPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
policy, viewErr := repo.View.LockoutPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, 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)
if errors.IsNotFound(viewErr) && len(events) == 0 {
return repo.GetDefaultPasswordLockoutPolicy(ctx)
return repo.GetDefaultLockoutPolicy(ctx)
}
if esErr != nil {
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
for _, event := range events {
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) {
policy, viewErr := repo.View.PasswordLockoutPolicyByAggregateID(repo.SystemDefaults.IamID)
func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
policy, viewErr := repo.View.LockoutPolicyByAggregateID(repo.SystemDefaults.IamID)
if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, viewErr
}
if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordLockoutPolicyView)
policy = new(iam_es_model.LockoutPolicyView)
}
events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
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 {
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
for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordLockoutViewToModel(policy), nil
return iam_es_model.LockoutViewToModel(policy), nil
}
}
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) {

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}),
newPasswordAgePolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}),
newPasswordLockoutPolicy(
handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount, es}),
newLockoutPolicy(
handler{view, bulkLimit, configs.cycleDuration("LockoutPolicy"), errorCount, es}),
newOrgIAMPolicy(
handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}),
newMailTemplate(

View File

@ -13,16 +13,16 @@ import (
)
const (
passwordLockoutPolicyTable = "management.password_lockout_policies"
lockoutPolicyTable = "management.lockout_policies"
)
type PasswordLockoutPolicy struct {
type LockoutPolicy struct {
handler
subscription *v1.Subscription
}
func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy {
h := &PasswordLockoutPolicy{
func newLockoutPolicy(handler handler) *LockoutPolicy {
h := &LockoutPolicy{
handler: handler,
}
@ -31,7 +31,7 @@ func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy {
return h
}
func (m *PasswordLockoutPolicy) subscribe() {
func (m *LockoutPolicy) subscribe() {
m.subscription = m.es.Subscribe(m.AggregateTypes()...)
go func() {
for event := range m.subscription.Events {
@ -40,28 +40,28 @@ func (m *PasswordLockoutPolicy) subscribe() {
}()
}
func (p *PasswordLockoutPolicy) ViewModel() string {
return passwordLockoutPolicyTable
func (p *LockoutPolicy) ViewModel() string {
return lockoutPolicyTable
}
func (p *PasswordLockoutPolicy) Subscription() *v1.Subscription {
func (p *LockoutPolicy) Subscription() *v1.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}
}
func (p *PasswordLockoutPolicy) CurrentSequence() (uint64, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence()
func (p *LockoutPolicy) CurrentSequence() (uint64, error) {
sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := p.view.GetLatestPasswordLockoutPolicySequence()
func (p *LockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := p.view.GetLatestLockoutPolicySequence()
if err != nil {
return nil, err
}
@ -70,7 +70,7 @@ func (p *PasswordLockoutPolicy) EventQuery() (*es_models.SearchQuery, error) {
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 {
case model.OrgAggregate, iam_es_model.IAMAggregate:
err = p.processPasswordLockoutPolicy(event)
@ -78,33 +78,33 @@ func (p *PasswordLockoutPolicy) Reduce(event *es_models.Event) (err error) {
return err
}
func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *es_models.Event) (err error) {
policy := new(iam_model.PasswordLockoutPolicyView)
func (p *LockoutPolicy) processPasswordLockoutPolicy(event *es_models.Event) (err error) {
policy := new(iam_model.LockoutPolicyView)
switch event.Type {
case iam_es_model.PasswordLockoutPolicyAdded, model.PasswordLockoutPolicyAdded:
case iam_es_model.LockoutPolicyAdded, model.LockoutPolicyAdded:
err = policy.AppendEvent(event)
case iam_es_model.PasswordLockoutPolicyChanged, model.PasswordLockoutPolicyChanged:
policy, err = p.view.PasswordLockoutPolicyByAggregateID(event.AggregateID)
case iam_es_model.LockoutPolicyChanged, model.LockoutPolicyChanged:
policy, err = p.view.LockoutPolicyByAggregateID(event.AggregateID)
if err != nil {
return err
}
err = policy.AppendEvent(event)
case model.PasswordLockoutPolicyRemoved:
return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event)
case model.LockoutPolicyRemoved:
return p.view.DeleteLockoutPolicy(event.AggregateID, event)
default:
return p.view.ProcessedPasswordLockoutPolicySequence(event)
return p.view.ProcessedLockoutPolicySequence(event)
}
if err != nil {
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")
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 {
return spooler.HandleSuccess(p.view.UpdatePasswordLockoutPolicySpoolerRunTimestamp)
func (p *LockoutPolicy) OnSuccess() error {
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)
GetDefaultPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error)
GetPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error)
GetDefaultPasswordLockoutPolicy(ctx context.Context) (*iam_model.PasswordLockoutPolicyView, error)
GetLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error)
GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error)
GetPrivacyPolicy(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
PasswordComplexityPolicy *iam_model.PasswordComplexityPolicy
PasswordAgePolicy *iam_model.PasswordAgePolicy
PasswordLockoutPolicy *iam_model.PasswordLockoutPolicy
LockoutPolicy *iam_model.LockoutPolicy
IDPs []*iam_model.IDPConfig
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,67 +9,67 @@ import (
)
var (
PasswordLockoutPolicyAddedEventType = iamEventTypePrefix + policy.PasswordLockoutPolicyAddedEventType
PasswordLockoutPolicyChangedEventType = iamEventTypePrefix + policy.PasswordLockoutPolicyChangedEventType
LockoutPolicyAddedEventType = iamEventTypePrefix + policy.LockoutPolicyAddedEventType
LockoutPolicyChangedEventType = iamEventTypePrefix + policy.LockoutPolicyChangedEventType
)
type PasswordLockoutPolicyAddedEvent struct {
policy.PasswordLockoutPolicyAddedEvent
type LockoutPolicyAddedEvent struct {
policy.LockoutPolicyAddedEvent
}
func NewPasswordLockoutPolicyAddedEvent(
func NewLockoutPolicyAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
maxAttempts uint64,
showLockoutFailure bool,
) *PasswordLockoutPolicyAddedEvent {
return &PasswordLockoutPolicyAddedEvent{
PasswordLockoutPolicyAddedEvent: *policy.NewPasswordLockoutPolicyAddedEvent(
) *LockoutPolicyAddedEvent {
return &LockoutPolicyAddedEvent{
LockoutPolicyAddedEvent: *policy.NewLockoutPolicyAddedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
PasswordLockoutPolicyAddedEventType),
LockoutPolicyAddedEventType),
maxAttempts,
showLockoutFailure),
}
}
func PasswordLockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.PasswordLockoutPolicyAddedEventMapper(event)
func LockoutPolicyAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.LockoutPolicyAddedEventMapper(event)
if err != nil {
return nil, err
}
return &PasswordLockoutPolicyAddedEvent{PasswordLockoutPolicyAddedEvent: *e.(*policy.PasswordLockoutPolicyAddedEvent)}, nil
return &LockoutPolicyAddedEvent{LockoutPolicyAddedEvent: *e.(*policy.LockoutPolicyAddedEvent)}, nil
}
type PasswordLockoutPolicyChangedEvent struct {
policy.PasswordLockoutPolicyChangedEvent
type LockoutPolicyChangedEvent struct {
policy.LockoutPolicyChangedEvent
}
func NewPasswordLockoutPolicyChangedEvent(
func NewLockoutPolicyChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []policy.PasswordLockoutPolicyChanges,
) (*PasswordLockoutPolicyChangedEvent, error) {
changedEvent, err := policy.NewPasswordLockoutPolicyChangedEvent(
changes []policy.LockoutPolicyChanges,
) (*LockoutPolicyChangedEvent, error) {
changedEvent, err := policy.NewLockoutPolicyChangedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
PasswordLockoutPolicyChangedEventType),
LockoutPolicyChangedEventType),
changes,
)
if err != nil {
return nil, err
}
return &PasswordLockoutPolicyChangedEvent{PasswordLockoutPolicyChangedEvent: *changedEvent}, nil
return &LockoutPolicyChangedEvent{LockoutPolicyChangedEvent: *changedEvent}, nil
}
func PasswordLockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.PasswordLockoutPolicyChangedEventMapper(event)
func LockoutPolicyChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := policy.LockoutPolicyChangedEventMapper(event)
if err != nil {
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(PasswordComplexityPolicyChangedEventType, PasswordComplexityPolicyChangedEventMapper).
RegisterFilterEventMapper(PasswordComplexityPolicyRemovedEventType, PasswordComplexityPolicyRemovedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyAddedEventType, PasswordLockoutPolicyAddedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyChangedEventType, PasswordLockoutPolicyChangedEventMapper).
RegisterFilterEventMapper(PasswordLockoutPolicyRemovedEventType, PasswordLockoutPolicyRemovedEventMapper).
RegisterFilterEventMapper(LockoutPolicyAddedEventType, LockoutPolicyAddedEventMapper).
RegisterFilterEventMapper(LockoutPolicyChangedEventType, LockoutPolicyChangedEventMapper).
RegisterFilterEventMapper(LockoutPolicyRemovedEventType, LockoutPolicyRemovedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyAddedEventType, PrivacyPolicyAddedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyChangedEventType, PrivacyPolicyChangedEventMapper).
RegisterFilterEventMapper(PrivacyPolicyRemovedEventType, PrivacyPolicyRemovedEventMapper).

View File

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

View File

@ -23,6 +23,7 @@ type IAMSetUp struct {
Step15 *command.Step15
Step16 *command.Step16
Step17 *command.Step17
Step18 *command.Step18
}
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.Step16,
setup.Step17,
setup.Step18,
} {
if step.Step() <= currentDone {
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) {
if err != nil {
l.renderInternalError(w, r, authReq, err)
return
}
if authReq == nil || len(authReq.PossibleSteps) == 0 {
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-OVOiT", "no possible steps"))
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) {
var msg string
if err != nil {
msg = err.Error()
_, msg = l.getErrorMessage(r, err)
}
data := l.getBaseData(r, authReq, "Error", "Internal", msg)
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
Empty: Passwort ist leer
Invalid: Passwort ungültig
InvalidAndLocked: Password ist undgültig und Benutzer wurde gesperrt, melden Sie sich bei ihrem Administrator.
PasswordComplexityPolicy:
NotFound: Passwort Policy konnte nicht gefunden werden
MinLength: Passwort ist zu kurz
@ -312,6 +313,7 @@ Errors:
InvalidCode: Code ist ungültig
NotReady: Multifaktor OTP (OneTimePassword) ist nicht bereit
Locked: Benutzer ist gesperrt
SomethingWentWrong: Irgendetwas ist schief gelaufen
NotActive: Benutzer ist nicht aktiv
ExternalIDP:
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.
IdentityProvider:
InvalidConfig: Identitäts Provider Konfiguration ist ungültig
IAM:
LockoutPolicy:
NotExisting: Lockout Policy existiert nicht
optional: (optional)

View File

@ -288,6 +288,7 @@ Errors:
ConfirmationWrong: Passwordconfirmation is wrong
Empty: Password is empty
Invalid: Password is invalid
InvalidAndLocked: Password is invalid and user is locked, contact your administrator.
PasswordComplexityPolicy:
NotFound: Password policy not found
MinLength: Password is to short
@ -312,6 +313,7 @@ Errors:
InvalidCode: Invalid code
NotReady: Multifactor OTP (OneTimePassword) isn't ready
Locked: User is locked
SomethingWentWrong: Something went wrong
NotActive: User is not active
ExternalIDP:
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.
IdentityProvider:
InvalidConfig: Identity Provider configuration is invalid
IAM:
LockoutPolicy:
NotExisting: Lockout Policy not existing
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
rpc GetPasswordLockoutPolicy(GetPasswordLockoutPolicyRequest) returns (GetPasswordLockoutPolicyResponse) {
//Returns the lockout policy defined by the administrators of ZITADEL
rpc GetLockoutPolicy(GetLockoutPolicyRequest) returns (GetLockoutPolicyResponse) {
option (google.api.http) = {
get: "/policies/password/lockout";
get: "/policies/lockout";
};
option (zitadel.v1.auth_option) = {
@ -1437,20 +1437,19 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "policy";
tags: "password policy";
tags: "password lockout policy";
tags: "lockout policy";
responses: {
key: "200";
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
rpc UpdatePasswordLockoutPolicy(UpdatePasswordLockoutPolicyRequest) returns (UpdatePasswordLockoutPolicyResponse) {
rpc UpdateLockoutPolicy(UpdateLockoutPolicyRequest) returns (UpdateLockoutPolicyResponse) {
option (google.api.http) = {
put: "/policies/password/lockout";
body: "*";
@ -3086,25 +3085,23 @@ message UpdatePasswordAgePolicyResponse {
}
//This is an empty request
message GetPasswordLockoutPolicyRequest {}
message GetLockoutPolicyRequest {}
message GetPasswordLockoutPolicyResponse {
zitadel.policy.v1.PasswordLockoutPolicy policy = 1;
message GetLockoutPolicyResponse {
zitadel.policy.v1.LockoutPolicy policy = 1;
}
message UpdatePasswordLockoutPolicyRequest {
message UpdateLockoutPolicyRequest {
// failed attempts until a user gets locked
uint32 max_attempts = 1 [
uint32 max_password_attempts = 1 [
(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\""
}
];
// 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;
}

View File

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

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