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

|
||||
|
||||
## 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.
|
||||
|
@ -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) {
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
|
||||
|
31
internal/api/grpc/admin/lockout.go
Normal file
31
internal/api/grpc/admin/lockout.go
Normal 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
|
||||
}
|
12
internal/api/grpc/admin/lockout_converter.go
Normal file
12
internal/api/grpc/admin/lockout_converter.go
Normal 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),
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
63
internal/api/grpc/management/policy_lockout.go
Normal file
63
internal/api/grpc/management/policy_lockout.go
Normal 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
|
||||
}
|
18
internal/api/grpc/management/policy_lockout_converter.go
Normal file
18
internal/api/grpc/management/policy_lockout_converter.go
Normal 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),
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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{
|
||||
IsDefault: policy.Default,
|
||||
MaxAttempts: policy.MaxAttempts,
|
||||
ShowLockoutFailure: policy.ShowLockOutFailures,
|
||||
func ModelLockoutPolicyToPb(policy *model.LockoutPolicyView) *policy_pb.LockoutPolicy {
|
||||
return &policy_pb.LockoutPolicy{
|
||||
IsDefault: policy.Default,
|
||||
MaxPasswordAttempts: policy.MaxPasswordAttempts,
|
||||
Details: object.ToViewDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.CreationDate,
|
||||
|
@ -35,14 +35,15 @@ type AuthRequestRepo struct {
|
||||
View *view.View
|
||||
Eventstore v1.Eventstore
|
||||
|
||||
UserSessionViewProvider userSessionViewProvider
|
||||
UserViewProvider userViewProvider
|
||||
UserCommandProvider userCommandProvider
|
||||
UserEventProvider userEventProvider
|
||||
OrgViewProvider orgViewProvider
|
||||
LoginPolicyViewProvider loginPolicyViewProvider
|
||||
IDPProviderViewProvider idpProviderViewProvider
|
||||
UserGrantProvider userGrantProvider
|
||||
UserSessionViewProvider userSessionViewProvider
|
||||
UserViewProvider userViewProvider
|
||||
UserCommandProvider userCommandProvider
|
||||
UserEventProvider userEventProvider
|
||||
OrgViewProvider orgViewProvider
|
||||
LoginPolicyViewProvider loginPolicyViewProvider
|
||||
LockoutPolicyViewProvider lockoutPolicyViewProvider
|
||||
IDPProviderViewProvider idpProviderViewProvider
|
||||
UserGrantProvider userGrantProvider
|
||||
|
||||
IdGenerator id.Generator
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
@ -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{}},
|
||||
@ -512,8 +561,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
userViewProvider: &mockViewUser{
|
||||
PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}},
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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},
|
||||
@ -528,8 +582,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordSet: true,
|
||||
PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}},
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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},
|
||||
@ -550,7 +609,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
IsEmailVerified: false,
|
||||
MFAMaxSetUp: int32(model.MFALevelMultiFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
userEventProvider: &mockEventUser{},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
MultiFactorCheckLifeTime: 10 * time.Hour,
|
||||
},
|
||||
@ -572,7 +636,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordInitRequired: true,
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
},
|
||||
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||
[]domain.NextStep{&domain.InitPasswordStep{}},
|
||||
@ -588,7 +657,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
IsEmailVerified: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
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,
|
||||
},
|
||||
@ -634,8 +713,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
userViewProvider: &mockViewUser{
|
||||
PasswordSet: true,
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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},
|
||||
@ -654,9 +738,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
IsEmailVerified: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
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,
|
||||
},
|
||||
@ -682,8 +771,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
OTPState: int32(user_model.MFAStateReady),
|
||||
MFAMaxSetUp: int32(model.MFALevelMultiFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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,
|
||||
},
|
||||
@ -710,8 +804,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
OTPState: int32(user_model.MFAStateReady),
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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,
|
||||
},
|
||||
@ -739,8 +838,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
OTPState: int32(user_model.MFAStateReady),
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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,
|
||||
@ -771,8 +875,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
IsEmailVerified: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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,
|
||||
},
|
||||
@ -797,8 +906,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordSet: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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,
|
||||
},
|
||||
@ -823,8 +937,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordChangeRequired: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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,
|
||||
},
|
||||
@ -849,9 +968,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
IsEmailVerified: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
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,
|
||||
},
|
||||
@ -877,9 +1001,14 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
IsEmailVerified: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
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,
|
||||
@ -995,8 +1139,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
IsEmailVerified: true,
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
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,
|
||||
|
@ -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}),
|
||||
}
|
||||
}
|
||||
|
||||
|
110
internal/auth/repository/eventsourcing/handler/lockout_policy.go
Normal file
110
internal/auth/repository/eventsourcing/handler/lockout_policy.go
Normal 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)
|
||||
}
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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),
|
||||
},
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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),
|
||||
},
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
35
internal/command/setup_step18.go
Normal file
35
internal/command/setup_step18.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ type HumanPasswordWriteModel struct {
|
||||
Secret *crypto.CryptoValue
|
||||
SecretChangeRequired bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
CodeCreationDate time.Time
|
||||
CodeExpiry time.Duration
|
||||
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 != "" {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ type AuthRequest struct {
|
||||
AllowedExternalIDPs []*IDPProvider
|
||||
LabelPolicy *LabelPolicy
|
||||
PrivacyPolicy *PrivacyPolicy
|
||||
LockoutPolicy *LockoutPolicy
|
||||
DefaultTranslations []*CustomText
|
||||
OrgTranslations []*CustomText
|
||||
}
|
||||
|
@ -22,5 +22,5 @@ type IAM struct {
|
||||
DefaultOrgIAMPolicy *OrgIAMPolicy
|
||||
DefaultPasswordComplexityPolicy *PasswordComplexityPolicy
|
||||
DefaultPasswordAgePolicy *PasswordAgePolicy
|
||||
DefaultPasswordLockoutPolicy *PasswordLockoutPolicy
|
||||
DefaultPasswordLockoutPolicy *LockoutPolicy
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ type Org struct {
|
||||
LabelPolicy *LabelPolicy
|
||||
PasswordComplexityPolicy *PasswordComplexityPolicy
|
||||
PasswordAgePolicy *PasswordAgePolicy
|
||||
PasswordLockoutPolicy *PasswordLockoutPolicy
|
||||
PasswordLockoutPolicy *LockoutPolicy
|
||||
IDPs []*IDPConfig
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ const (
|
||||
Step15
|
||||
Step16
|
||||
Step17
|
||||
Step18
|
||||
//StepCount marks the the length of possible steps (StepCount-1 == last possible step)
|
||||
StepCount
|
||||
)
|
||||
|
@ -35,7 +35,7 @@ type IAM struct {
|
||||
DefaultOrgIAMPolicy *OrgIAMPolicy
|
||||
DefaultPasswordComplexityPolicy *PasswordComplexityPolicy
|
||||
DefaultPasswordAgePolicy *PasswordAgePolicy
|
||||
DefaultPasswordLockoutPolicy *PasswordLockoutPolicy
|
||||
DefaultLockoutPolicy *LockoutPolicy
|
||||
DefaultMailTemplate *MailTemplate
|
||||
DefaultMailTexts []*MailText
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
@ -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
|
||||
}
|
@ -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"
|
||||
|
@ -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")
|
||||
|
@ -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 ""
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
45
migrations/cockroach/V1.59__user_lock.sql
Normal file
45
migrations/cockroach/V1.59__user_lock.sql
Normal 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;
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user