From cd44213e998c9cd276aafc24619e6641acc99dea Mon Sep 17 00:00:00 2001 From: mffap Date: Mon, 14 Dec 2020 09:40:09 +0100 Subject: [PATCH 01/22] chore: Update LICENSE (#1087) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 261eeb9e9f..a1ea99bb88 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2020 CAOS AG Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From c6fed8ae865288f1ed23c052f3544a10c50ac75c Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 14 Dec 2020 10:04:15 +0100 Subject: [PATCH 02/22] feat(console): u2f (#1080) * fix user table count * grpc ge * move grpc * u2f * add u2f funcs * rm local grpc, u2f dialog * dialog u2f * 2fa button * mfa u2f credentialoptions * decode base64 to bytearray, id, challenge * u2f verify * spinner, remove, attribute col * delete mfa * add forcemfa to policy * add id to remove * fix: add missing remove u2f in management * user mgmt u2f delete, login policy * rm log * show attr in mgmt user mfa * add missing id of mfa * mfa table * multifaktor for admin, org * add secondfactor to gen component * remove circular dependency * lint * revert identity prov * add divider * login policy lint * Update console/src/app/modules/policies/login-policy/login-policy.component.html * Update console/src/app/modules/policies/login-policy/login-policy.component.html Co-authored-by: Maximilian Peintner Co-authored-by: Livio Amstutz --- .../dialog-add-type.component.html | 19 ++ .../dialog-add-type.component.scss | 22 ++ .../dialog-add-type.component.ts | 33 +++ .../mfa-table/mfa-table.component.html | 15 ++ .../mfa-table/mfa-table.component.scss | 42 ++++ .../mfa-table/mfa-table.component.spec.ts | 25 ++ .../modules/mfa-table/mfa-table.component.ts | 220 ++++++++++++++++++ .../app/modules/mfa-table/mfa-table.module.ts | 44 ++++ .../login-policy/login-policy.component.html | 31 ++- .../login-policy/login-policy.component.scss | 12 + .../login-policy/login-policy.component.ts | 7 + .../login-policy/login-policy.module.ts | 2 + .../auth-user-mfa.component.html | 17 +- .../auth-user-mfa.component.scss | 1 + .../auth-user-mfa/auth-user-mfa.component.ts | 90 ++++++- .../dialog-otp/dialog-otp.component.html | 4 +- .../dialog-u2f/dialog-u2f.component.html | 18 ++ .../dialog-u2f/dialog-u2f.component.scss | 27 +++ .../dialog-u2f/dialog-u2f.component.ts | 89 +++++++ .../users/user-detail/user-detail.module.ts | 2 + .../user-mfa/user-mfa.component.html | 12 +- .../user-mfa/user-mfa.component.ts | 27 ++- .../user-table/user-table.component.html | 2 +- console/src/app/services/admin.service.ts | 30 +++ console/src/app/services/grpc-auth.service.ts | 83 ++++--- console/src/app/services/mgmt.service.ts | 37 +++ console/src/assets/i18n/de.json | 49 +++- console/src/assets/i18n/en.json | 47 +++- internal/api/grpc/management/user.go | 5 + .../api/grpc/management/user_converter.go | 4 +- .../eventsourcing/eventstore/user.go | 4 + internal/management/repository/user.go | 1 + pkg/grpc/management/proto/management.proto | 16 ++ 33 files changed, 972 insertions(+), 65 deletions(-) create mode 100644 console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html create mode 100644 console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss create mode 100644 console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts create mode 100644 console/src/app/modules/mfa-table/mfa-table.component.html create mode 100644 console/src/app/modules/mfa-table/mfa-table.component.scss create mode 100644 console/src/app/modules/mfa-table/mfa-table.component.spec.ts create mode 100644 console/src/app/modules/mfa-table/mfa-table.component.ts create mode 100644 console/src/app/modules/mfa-table/mfa-table.module.ts create mode 100644 console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html create mode 100644 console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.scss create mode 100644 console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.ts diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html new file mode 100644 index 0000000000..f77383e17f --- /dev/null +++ b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html @@ -0,0 +1,19 @@ +

{{data.title | translate}}

+
+

{{data.desc | translate}}

+ + + {{'MFA.TYPE' | translate}} + + + {{(data.componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}} + + + +
+
+ + +
\ No newline at end of file diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss new file mode 100644 index 0000000000..2a10cfe140 --- /dev/null +++ b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss @@ -0,0 +1,22 @@ +.title { + font-size: 1.5rem; +} + +.desc { + font-size: 14px; + color: var(--grey); +} + +.form-field { + width: 100%; +} + +.action { + display: flex; + justify-content: flex-end; + + button { + margin-left: .5rem; + border-radius: .5rem; + } +} diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts new file mode 100644 index 0000000000..befb157abf --- /dev/null +++ b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts @@ -0,0 +1,33 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +import { MultiFactorType as AdminMultiFactorType } from 'src/app/proto/generated/admin_pb'; +import { MultiFactorType as MgmtMultiFactorType } from 'src/app/proto/generated/management_pb'; + +enum LoginMethodComponentType { + MultiFactor = 1, + SecondFactor = 2, +} + +@Component({ + selector: 'app-dialog-add-type', + templateUrl: './dialog-add-type.component.html', + styleUrls: ['./dialog-add-type.component.scss'], +}) +export class DialogAddTypeComponent { + public LoginMethodComponentType: any = LoginMethodComponentType; + public newMfaType!: AdminMultiFactorType | MgmtMultiFactorType; + public availableMfaTypes: Array = []; + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any) { + this.availableMfaTypes = data.types; + } + + public closeDialog(): void { + this.dialogRef.close(); + } + + public closeDialogWithCode(): void { + this.dialogRef.close(this.newMfaType); + } +} diff --git a/console/src/app/modules/mfa-table/mfa-table.component.html b/console/src/app/modules/mfa-table/mfa-table.component.html new file mode 100644 index 0000000000..3d45e4c2f5 --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.html @@ -0,0 +1,15 @@ +
+ +
+
+
+ + {{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}} +
+
+ add +
+
\ No newline at end of file diff --git a/console/src/app/modules/mfa-table/mfa-table.component.scss b/console/src/app/modules/mfa-table/mfa-table.component.scss new file mode 100644 index 0000000000..f0fe7e80a7 --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.scss @@ -0,0 +1,42 @@ + +.t .sp_wrapper { + display: block; +} + +.mfa-list { + display: flex; + flex-wrap: wrap; + margin: 0 -.5rem; + + .mfa { + border: 1px solid var(--grey); + border-radius: .5rem; + display: grid; + align-items: center; + justify-content: center; + margin: .5rem; + padding: 10px; + cursor: pointer; + position: relative; + min-height: 70px; + min-width: 150px; + + .rm { + position: absolute; + top: 0; + left: 0; + transform: translateX(-50%) translateY(-50%); + cursor: pointer; + + &[disabled] { + display: none; + } + } + + &:not(.disabled) { + &:hover { + background-color: #ffffff10; + } + } + } +} diff --git a/console/src/app/modules/mfa-table/mfa-table.component.spec.ts b/console/src/app/modules/mfa-table/mfa-table.component.spec.ts new file mode 100644 index 0000000000..24992ccaff --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { MfaTableComponent } from './mfa-table.component'; + +describe('MfaTableComponent', () => { + let component: MfaTableComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [MfaTableComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MfaTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/mfa-table/mfa-table.component.ts b/console/src/app/modules/mfa-table/mfa-table.component.ts new file mode 100644 index 0000000000..401e90af48 --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.ts @@ -0,0 +1,220 @@ +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatPaginator } from '@angular/material/paginator'; +import { TranslateService } from '@ngx-translate/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { + MultiFactor as AdminMultiFactor, MultiFactorType as AdminMultiFactorType, + SecondFactor as AdminSecondFactor, SecondFactorType as AdminSecondFactorType, +} from 'src/app/proto/generated/admin_pb'; +import { + MultiFactor as MgmtMultiFactor, MultiFactorType as MgmtMultiFactorType, + SecondFactor as MgmtSecondFactor, SecondFactorType as MgmtSecondFactorType, +} from 'src/app/proto/generated/management_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'; + +import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; +import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; +import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component'; + +export enum LoginMethodComponentType { + MultiFactor = 1, + SecondFactor = 2, +} + +@Component({ + selector: 'app-mfa-table', + templateUrl: './mfa-table.component.html', + styleUrls: ['./mfa-table.component.scss'], +}) +export class MfaTableComponent implements OnInit { + public LoginMethodComponentType: any = LoginMethodComponentType; + @Input() componentType!: LoginMethodComponentType; + @Input() public serviceType!: PolicyComponentServiceType; + @Input() service!: AdminService | ManagementService; + @Input() disabled: boolean = false; + @ViewChild(MatPaginator) public paginator!: MatPaginator; + public mfas: Array = []; + + private loadingSubject: BehaviorSubject = new BehaviorSubject(false); + public loading$: Observable = this.loadingSubject.asObservable(); + + public PolicyComponentServiceType: any = PolicyComponentServiceType; + + constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { } + + public ngOnInit(): void { + this.getData(); + } + + public removeMfa(type: MgmtMultiFactorType | AdminMultiFactorType | MgmtSecondFactorType | AdminSecondFactorType): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'MFA.DELETE.TITLE', + descriptionKey: 'MFA.DELETE.DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new MgmtMultiFactor(); + req.setMultiFactor(type as MgmtMultiFactorType); + (this.service as ManagementService).RemoveMultiFactorFromLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new MgmtSecondFactor(); + req.setSecondFactor(type as MgmtSecondFactorType); + (this.service as ManagementService).RemoveSecondFactorFromLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new AdminMultiFactor(); + req.setMultiFactor(type as AdminMultiFactorType); + (this.service as AdminService).RemoveMultiFactorFromDefaultLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new AdminSecondFactor(); + req.setSecondFactor(type as AdminSecondFactorType); + (this.service as AdminService).RemoveSecondFactorFromDefaultLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } + } + } + }); + } + + public addMfa(): void { + + let selection: any[] = []; + + if (this.componentType === LoginMethodComponentType.MultiFactor) { + selection = this.serviceType === PolicyComponentServiceType.MGMT ? + [MgmtMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] : + this.serviceType === PolicyComponentServiceType.ADMIN ? + [AdminMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] : + []; + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + selection = this.serviceType === PolicyComponentServiceType.MGMT ? + [MgmtSecondFactorType.SECONDFACTORTYPE_U2F, MgmtSecondFactorType.SECONDFACTORTYPE_U2F] : + this.serviceType === PolicyComponentServiceType.ADMIN ? + [AdminSecondFactorType.SECONDFACTORTYPE_OTP, AdminSecondFactorType.SECONDFACTORTYPE_U2F] : + []; + } + const dialogRef = this.dialog.open(DialogAddTypeComponent, { + data: { + title: 'MFA.CREATE.TITLE', + desc: 'MFA.CREATE.DESCRIPTION', + componentType: this.componentType, + types: selection, + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe((mfaType: AdminMultiFactorType | MgmtMultiFactorType | + AdminSecondFactorType | MgmtSecondFactorType) => { + if (mfaType) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new MgmtMultiFactor(); + req.setMultiFactor(mfaType as MgmtMultiFactorType); + (this.service as ManagementService).AddMultiFactorToLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new MgmtSecondFactor(); + req.setSecondFactor(mfaType as MgmtSecondFactorType); + (this.service as ManagementService).AddSecondFactorToLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new AdminMultiFactor(); + req.setMultiFactor(mfaType as AdminMultiFactorType); + (this.service as AdminService).addMultiFactorToDefaultLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new AdminSecondFactor(); + req.setSecondFactor(mfaType as AdminSecondFactorType); + (this.service as AdminService).AddSecondFactorToDefaultLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } + } + } + }); + } + + private async getData(): Promise { + this.loadingSubject.next(true); + + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + (this.service as ManagementService).GetLoginPolicyMultiFactors().then(resp => { + this.mfas = resp.toObject().multiFactorsList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + (this.service as ManagementService).GetLoginPolicySecondFactors().then(resp => { + this.mfas = resp.toObject().secondFactorsList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + (this.service as AdminService).getDefaultLoginPolicyMultiFactors().then(resp => { + this.mfas = resp.toObject().multiFactorsList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + (this.service as AdminService).GetDefaultLoginPolicySecondFactors().then(resp => { + this.mfas = resp.toObject().secondFactorsList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } + } + } + + public refreshPageAfterTimout(to: number): void { + setTimeout(() => { + this.getData(); + }, to); + } +} diff --git a/console/src/app/modules/mfa-table/mfa-table.module.ts b/console/src/app/modules/mfa-table/mfa-table.module.ts new file mode 100644 index 0000000000..8db0c2cf9d --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.module.ts @@ -0,0 +1,44 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatTableModule } from '@angular/material/table'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterModule } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; +import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; +import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module'; +import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module'; +import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module'; +import { TruncatePipeModule } from 'src/app/pipes/truncate-pipe/truncate-pipe.module'; + +import { MfaTableComponent } from './mfa-table.component'; +import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component'; +import { InputModule } from '../input/input.module'; +import { MatSelectModule } from '@angular/material/select'; + +@NgModule({ + declarations: [MfaTableComponent, DialogAddTypeComponent], + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatIconModule, + InputModule, + MatSelectModule, + MatTooltipModule, + TranslateModule, + TimestampToDatePipeModule, + HasRoleModule, + MatProgressSpinnerModule, + ], + exports: [ + MfaTableComponent, + ], +}) +export class MfaTableModule { } diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html index dcd843ab65..90fca4d406 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy.component.html @@ -44,8 +44,34 @@

{{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}}

+
+ + {{'POLICY.DATA.FORCEMFA' | translate}} + +

{{'POLICY.DATA.FORCEMFA_DESC' | translate}}

+
+ + +
+ +

{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}

+

{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}

+ + + +

{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}

+

{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}

+ + +

{{'LOGINPOLICY.IDPS' | translate}}

@@ -63,9 +89,6 @@
- -

{{ 'IDP.LIST.TITLE' | translate }}

{{ 'IDP.LIST.DESCRIPTION' | translate }}

@@ -73,4 +96,4 @@ [disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false">
- \ No newline at end of file + diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.scss b/console/src/app/modules/policies/login-policy/login-policy.component.scss index ea690233c2..5d2b27fbc2 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.scss +++ b/console/src/app/modules/policies/login-policy/login-policy.component.scss @@ -37,6 +37,11 @@ width: 100%; } +.subdesc { + color: var(--grey); + font-size: 14px; +} + .idps { display: flex; margin: 0 -.5rem; @@ -93,3 +98,10 @@ } } } + +.divider { + width: 100%; + height: 1px; + background-color: var(--grey); + margin: 1rem 0; +} diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.ts b/console/src/app/modules/policies/login-policy/login-policy.component.ts index 119a251433..69d1be4667 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.component.ts @@ -22,6 +22,7 @@ import { ToastService } from 'src/app/services/toast.service'; import { PolicyComponentServiceType } from '../policy-component-types.enum'; import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component'; +import { LoginMethodComponentType } from 'src/app/modules/mfa-table/mfa-table.component'; @Component({ selector: 'app-login-policy', @@ -29,6 +30,7 @@ import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component styleUrls: ['./login-policy.component.scss'], }) export class LoginPolicyComponent implements OnDestroy { + public LoginMethodComponentType: any = LoginMethodComponentType; public loginData!: LoginPolicyView.AsObject | DefaultLoginPolicyView.AsObject; private sub: Subscription = new Subscription(); @@ -112,6 +114,8 @@ export class LoginPolicyComponent implements OnDestroy { mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp); mgmtreq.setAllowRegister(this.loginData.allowRegister); mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); + mgmtreq.setForceMfa(this.loginData.forceMfa); + // console.log(mgmtreq.toObject()); if ((this.loginData as LoginPolicyView.AsObject).pb_default) { return (this.service as ManagementService).CreateLoginPolicy(mgmtreq); } else { @@ -122,6 +126,9 @@ export class LoginPolicyComponent implements OnDestroy { adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp); adminreq.setAllowRegister(this.loginData.allowRegister); adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); + adminreq.setForceMfa(this.loginData.forceMfa); + // console.log(adminreq.toObject()); + return (this.service as AdminService).UpdateDefaultLoginPolicy(adminreq); } } diff --git a/console/src/app/modules/policies/login-policy/login-policy.module.ts b/console/src/app/modules/policies/login-policy/login-policy.module.ts index 8ea3fe5a67..65cfe1cbf6 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.module.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.module.ts @@ -17,6 +17,7 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module'; import { LoginPolicyRoutingModule } from './login-policy-routing.module'; import { LoginPolicyComponent } from './login-policy.component'; +import { MfaTableModule } from 'src/app/modules/mfa-table/mfa-table.module'; @NgModule({ declarations: [LoginPolicyComponent], @@ -36,6 +37,7 @@ import { LoginPolicyComponent } from './login-policy.component'; DetailLayoutModule, AddIdpDialogModule, IdpTableModule, + MfaTableModule, MatProgressSpinnerModule, ], }) diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html index 9f2b0c7ecd..d3299fc4a3 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html @@ -1,11 +1,19 @@ - + + + + + + @@ -35,6 +43,11 @@ matTooltip="{{'ACTIONS.NEW' | translate}}"> {{'USER.MFA.OTP' | translate}} +
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss index 411f391e2f..3f9facae7c 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss @@ -2,6 +2,7 @@ .add-row { display: flex; margin: -.5rem; + flex-wrap: wrap; .button { margin: .5rem; diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts index ea9baa7253..052dd46a14 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts @@ -4,11 +4,32 @@ import { MatSort } from '@angular/material/sort'; import { MatTable, MatTableDataSource } from '@angular/material/table'; import { BehaviorSubject, Observable } from 'rxjs'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; -import { MfaOtpResponse, MFAState, MfaType, MultiFactor } from 'src/app/proto/generated/auth_pb'; +import { MfaOtpResponse, MFAState, MfaType, MultiFactor, WebAuthNResponse } from 'src/app/proto/generated/auth_pb'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ToastService } from 'src/app/services/toast.service'; import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component'; +import { DialogU2FComponent } from '../dialog-u2f/dialog-u2f.component'; + +export function _base64ToArrayBuffer(base64: string): any { + const binaryString = atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} + +export interface WebAuthNOptions { + challenge: string; + rp: { name: string, id: string; }; + user: { name: string, id: string, displayName: string; }; + pubKeyCredParams: any; + authenticatorSelection: { userVerification: string; }; + timeout: number; + attestation: string; +} @Component({ selector: 'app-auth-user-mfa', @@ -16,7 +37,7 @@ import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component'; styleUrls: ['./auth-user-mfa.component.scss'], }) export class AuthUserMfaComponent implements OnInit, OnDestroy { - public displayedColumns: string[] = ['type', 'state', 'actions']; + public displayedColumns: string[] = ['type', 'attr', 'state', 'actions']; private loadingSubject: BehaviorSubject = new BehaviorSubject(false); public loading$: Observable = this.loadingSubject.asObservable(); @@ -29,10 +50,12 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { public error: string = ''; public otpAvailable: boolean = false; - constructor(private service: GrpcAuthService, private toast: ToastService, private dialog: MatDialog) { } + constructor(private service: GrpcAuthService, + private toast: ToastService, + private dialog: MatDialog) { } public ngOnInit(): void { - this.getOTP(); + this.getMFAs(); } public ngOnDestroy(): void { @@ -50,7 +73,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { dialogRef.afterClosed().subscribe((code) => { if (code) { this.service.VerifyMfaOTP(code).then(() => { - this.getOTP(); + this.getMFAs(); }); } }); @@ -59,7 +82,40 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { }); } - public getOTP(): void { + public verifyU2f(): void { + + } + + public addU2F(): void { + this.service.AddMyMfaU2F().then((u2fresp) => { + const webauthn: WebAuthNResponse.AsObject = u2fresp.toObject(); + const credOptions: CredentialCreationOptions = JSON.parse(atob(webauthn.publicKey as string)); + + if (credOptions.publicKey?.challenge) { + credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any); + credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any); + const dialogRef = this.dialog.open(DialogU2FComponent, { + width: '400px', + data: { + credOptions, + }, + }); + + dialogRef.afterClosed().subscribe(done => { + if (done) { + this.getMFAs(); + } else { + this.getMFAs(); + } + }); + } + + }, error => { + this.toast.showError(error); + }); + } + + public getMFAs(): void { this.service.GetMyMfas().then(mfas => { this.dataSource = new MatTableDataSource(mfas.toObject().mfasList); this.dataSource.sort = this.sort; @@ -73,13 +129,13 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { }); } - public deleteMFA(type: MfaType): void { + public deleteMFA(type: MfaType, id?: string): void { const dialogRef = this.dialog.open(WarnDialogComponent, { data: { confirmKey: 'ACTIONS.DELETE', cancelKey: 'ACTIONS.CANCEL', - titleKey: 'USER.MFA.DIALOG.OTP_DELETE_TITLE', - descriptionKey: 'USER.MFA.DIALOG.OTP_DELETE_DESCRIPTION', + titleKey: 'USER.MFA.DIALOG.MFA_DELETE_TITLE', + descriptionKey: 'USER.MFA.DIALOG.MFA_DELETE_DESCRIPTION', }, width: '400px', }); @@ -94,7 +150,19 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { if (index > -1) { this.dataSource.data.splice(index, 1); } - this.getOTP(); + this.getMFAs(); + }).catch(error => { + this.toast.showError(error); + }); + } else if (type === MfaType.MFATYPE_U2F && id) { + this.service.RemoveMyMfaU2F(id).then(() => { + this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + + const index = this.dataSource.data.findIndex(mfa => mfa.type === type); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); }).catch(error => { this.toast.showError(error); }); @@ -102,4 +170,6 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { } }); } + + } diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html index 9a4eb065e1..0d5aa2426b 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html @@ -1,6 +1,6 @@

{{'USER.MFA.OTP_DIALOG_TITLE' | translate}}

-

{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}

+

{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}

@@ -15,4 +15,4 @@ -
\ No newline at end of file +
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html new file mode 100644 index 0000000000..b649faae0c --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html @@ -0,0 +1,18 @@ +

{{'USER.MFA.U2F_DIALOG_TITLE' | translate}}

+
+

{{'USER.MFA.U2F_DIALOG_DESCRIPTION' | translate}}

+ + + {{'USER.MFA.U2F_NAME' | translate}} + + + + + +

{{error}}

+
+
+ + +
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.scss b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.scss new file mode 100644 index 0000000000..94688b0fb8 --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.scss @@ -0,0 +1,27 @@ +.qrcode-wrapper { + display: flex; + align-content: center; + + .qrcode { + margin: 1rem auto; + } +} + +.form-field { + width: 100%; +} + +.error { + color: #f44336; + font-size: 14px; +} + +.action { + display: flex; + justify-content: flex-end; + + button { + margin-left: .5rem; + border-radius: .5rem; + } +} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.ts new file mode 100644 index 0000000000..5ca1afb86c --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.ts @@ -0,0 +1,89 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; +import { take } from 'rxjs/operators'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { ToastService } from 'src/app/services/toast.service'; + +export function _arrayBufferToBase64(buffer: any): string { + let binary = ''; + const bytes = new Uint8Array(buffer); + const len = bytes.byteLength; + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary).replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); +} + +@Component({ + selector: 'app-dialog-u2f', + templateUrl: './dialog-u2f.component.html', + styleUrls: ['./dialog-u2f.component.scss'], +}) +export class DialogU2FComponent { + public name: string = ''; + public error: string = ''; + public loading: boolean = false; + + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: { credOptions: any; }, + private service: GrpcAuthService, private translate: TranslateService, private toast: ToastService) { } + + public closeDialog(): void { + this.dialogRef.close(); + } + + public closeDialogWithCode(): void { + this.error = ''; + this.loading = true; + if (this.name && this.data.credOptions.publicKey) { + // this.data.credOptions.publicKey.rp.id = 'localhost'; + navigator.credentials.create(this.data.credOptions).then((resp) => { + if (resp && + (resp as any).response.attestationObject && + (resp as any).response.clientDataJSON && + (resp as any).rawId) { + + const attestationObject = (resp as any).response.attestationObject; + const clientDataJSON = (resp as any).response.clientDataJSON; + const rawId = (resp as any).rawId; + + const data = JSON.stringify({ + id: resp.id, + rawId: _arrayBufferToBase64(rawId), + type: resp.type, + response: { + attestationObject: _arrayBufferToBase64(attestationObject), + clientDataJSON: _arrayBufferToBase64(clientDataJSON), + }, + }); + + const base64 = btoa(data); + this.service.VerifyMyMfaU2F(base64, this.name).then(() => { + this.translate.get('USER.MFA.U2F_SUCCESS').pipe(take(1)).subscribe(msg => { + this.toast.showInfo(msg); + }); + this.dialogRef.close(true); + this.loading = false; + }).catch(error => { + this.loading = false; + this.toast.showError(error); + }); + } else { + this.loading = false; + this.translate.get('USER.MFA.U2F_ERROR').pipe(take(1)).subscribe(msg => { + this.toast.showInfo(msg); + }); + this.dialogRef.close(true); + } + }).catch(error => { + this.loading = false; + this.error = error; + this.toast.showInfo(error.message); + }); + } + + } +} diff --git a/console/src/app/pages/users/user-detail/user-detail.module.ts b/console/src/app/pages/users/user-detail/user-detail.module.ts index a217c0e7b8..f33c87fc5b 100644 --- a/console/src/app/pages/users/user-detail/user-detail.module.ts +++ b/console/src/app/pages/users/user-detail/user-detail.module.ts @@ -33,6 +33,7 @@ import { AuthUserDetailComponent } from './auth-user-detail/auth-user-detail.com import { AuthUserMfaComponent } from './auth-user-detail/auth-user-mfa/auth-user-mfa.component'; import { CodeDialogComponent } from './auth-user-detail/code-dialog/code-dialog.component'; import { DialogOtpComponent } from './auth-user-detail/dialog-otp/dialog-otp.component'; +import { DialogU2FComponent } from './auth-user-detail/dialog-u2f/dialog-u2f.component'; import { EditDialogComponent } from './auth-user-detail/edit-dialog/edit-dialog.component'; import { ResendEmailDialogComponent } from './auth-user-detail/resend-email-dialog/resend-email-dialog.component'; import { ThemeSettingComponent } from './auth-user-detail/theme-setting/theme-setting.component'; @@ -65,6 +66,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component'; ExternalIdpsComponent, ContactComponent, ResendEmailDialogComponent, + DialogU2FComponent, ], imports: [ UserDetailRoutingModule, diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html index eb234e45d8..8af7f0a87c 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html @@ -1,11 +1,19 @@ - +
{{ 'USER.MFA.TABLETYPE' | translate }} {{'USER.MFA.TYPE.'+ mfa.type | translate}} {{ 'USER.MFA.ATTRIBUTE' | translate }} + {{ mfa.attr }} + + {{ 'USER.MFA.TABLESTATE' | translate }} @@ -20,7 +28,7 @@ {{ 'USER.MFA.TABLEACTIONS' | translate }}
+ + + + + diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts index e91c170d77..58e25f23cf 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts @@ -20,7 +20,7 @@ export interface MFAItem { styleUrls: ['./user-mfa.component.scss'], }) export class UserMfaComponent implements OnInit, OnDestroy { - public displayedColumns: string[] = ['type', 'state', 'actions']; + public displayedColumns: string[] = ['type', 'attr', 'state', 'actions']; @Input() private user!: UserView.AsObject; public mfaSubject: BehaviorSubject = new BehaviorSubject([]); private loadingSubject: BehaviorSubject = new BehaviorSubject(false); @@ -37,7 +37,7 @@ export class UserMfaComponent implements OnInit, OnDestroy { constructor(private mgmtUserService: ManagementService, private dialog: MatDialog, private toast: ToastService) { } public ngOnInit(): void { - this.getOTP(); + this.getMFAs(); } public ngOnDestroy(): void { @@ -45,7 +45,7 @@ export class UserMfaComponent implements OnInit, OnDestroy { this.loadingSubject.complete(); } - public getOTP(): void { + public getMFAs(): void { this.mgmtUserService.getUserMfas(this.user.id).then(mfas => { this.dataSource = new MatTableDataSource(mfas.toObject().mfasList); this.dataSource.sort = this.sort; @@ -54,13 +54,14 @@ export class UserMfaComponent implements OnInit, OnDestroy { }); } - public deleteMFA(type: MfaType): void { + public deleteMFA(type: MfaType, id?: string): void { + console.log(type, id); const dialogRef = this.dialog.open(WarnDialogComponent, { data: { confirmKey: 'ACTIONS.DELETE', cancelKey: 'ACTIONS.CANCEL', - titleKey: 'USER.MFA.DIALOG.OTP_DELETE_TITLE', - descriptionKey: 'USER.MFA.DIALOG.OTP_DELETE_DESCRIPTION', + titleKey: 'USER.MFA.DIALOG.MFA_DELETE_TITLE', + descriptionKey: 'USER.MFA.DIALOG.MFA_DELETE_DESCRIPTION', }, width: '400px', }); @@ -75,7 +76,19 @@ export class UserMfaComponent implements OnInit, OnDestroy { if (index > -1) { this.dataSource.data.splice(index, 1); } - this.getOTP(); + this.getMFAs(); + }).catch(error => { + this.toast.showError(error); + }); + } else if (type === MfaType.MFATYPE_U2F && id) { + this.mgmtUserService.RemoveMfaU2F(id).then(() => { + this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + + const index = this.dataSource.data.findIndex(mfa => mfa.type === type); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); }).catch(error => { this.toast.showError(error); }); diff --git a/console/src/app/pages/users/user-list/user-table/user-table.component.html b/console/src/app/pages/users/user-list/user-table/user-table.component.html index a508c5d858..74d0cad53e 100644 --- a/console/src/app/pages/users/user-list/user-table/user-table.component.html +++ b/console/src/app/pages/users/user-list/user-table/user-table.component.html @@ -1,4 +1,4 @@ - diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index adad72a275..4b14b81f3c 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -36,6 +36,8 @@ import { IdpSearchRequest, IdpSearchResponse, IdpView, + MultiFactor, + MultiFactorsResult, OidcIdpConfig, OidcIdpConfigCreate, OidcIdpConfigUpdate, @@ -46,6 +48,8 @@ import { OrgSetUpRequest, OrgSetUpResponse, RemoveIamMemberRequest, + SecondFactor, + SecondFactorsResult, ViewID, Views, } from '../proto/generated/admin_pb'; @@ -73,6 +77,32 @@ export class AdminService { return this.grpcService.admin.setUpOrg(req); } + public getDefaultLoginPolicyMultiFactors(): Promise { + const req = new Empty(); + return this.grpcService.admin.getDefaultLoginPolicyMultiFactors(req); + } + + public addMultiFactorToDefaultLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.admin.addMultiFactorToDefaultLoginPolicy(req); + } + + public RemoveMultiFactorFromDefaultLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.admin.removeMultiFactorFromDefaultLoginPolicy(req); + } + + public GetDefaultLoginPolicySecondFactors(): Promise { + const req = new Empty(); + return this.grpcService.admin.getDefaultLoginPolicySecondFactors(req); + } + + public AddSecondFactorToDefaultLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.admin.addSecondFactorToDefaultLoginPolicy(req); + } + + public RemoveSecondFactorFromDefaultLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.admin.removeSecondFactorFromDefaultLoginPolicy(req); + } + public GetIamMemberRoles(): Promise { const req = new Empty(); return this.grpcService.admin.getIamMemberRoles(req); diff --git a/console/src/app/services/grpc-auth.service.ts b/console/src/app/services/grpc-auth.service.ts index 67d5f4def4..5d54ddc061 100644 --- a/console/src/app/services/grpc-auth.service.ts +++ b/console/src/app/services/grpc-auth.service.ts @@ -5,34 +5,37 @@ import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs'; import { catchError, filter, finalize, first, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators'; import { - Changes, - ChangesRequest, - ExternalIDPRemoveRequest, - ExternalIDPSearchRequest, - ExternalIDPSearchResponse, - Gender, - MfaOtpResponse, - MultiFactors, - MyPermissions, - MyProjectOrgSearchQuery, - MyProjectOrgSearchRequest, - MyProjectOrgSearchResponse, - Org, - PasswordChange, - PasswordComplexityPolicy, - UpdateUserAddressRequest, - UpdateUserEmailRequest, - UpdateUserPhoneRequest, - UpdateUserProfileRequest, - UserAddress, - UserEmail, - UserPhone, - UserProfile, - UserProfileView, - UserSessionViews, - UserView, - VerifyMfaOtp, - VerifyUserPhoneRequest, + Changes, + ChangesRequest, + ExternalIDPRemoveRequest, + ExternalIDPSearchRequest, + ExternalIDPSearchResponse, + Gender, + MfaOtpResponse, + MultiFactors, + MyPermissions, + MyProjectOrgSearchQuery, + MyProjectOrgSearchRequest, + MyProjectOrgSearchResponse, + Org, + PasswordChange, + PasswordComplexityPolicy, + UpdateUserAddressRequest, + UpdateUserEmailRequest, + UpdateUserPhoneRequest, + UpdateUserProfileRequest, + UserAddress, + UserEmail, + UserPhone, + UserProfile, + UserProfileView, + UserSessionViews, + UserView, + VerifyMfaOtp, + VerifyUserPhoneRequest, + VerifyWebAuthN, + WebAuthNResponse, + WebAuthNTokenID, } from '../proto/generated/auth_pb'; import { GrpcService } from './grpc.service'; import { StorageKey, StorageService } from './storage.service'; @@ -328,9 +331,31 @@ export class GrpcAuthService { ); } + public AddMyMfaU2F(): Promise { + return this.grpcService.auth.addMyMfaU2F( + new Empty(), + ); + } + + public RemoveMyMfaU2F(id: string): Promise { + const req = new WebAuthNTokenID(); + req.setId(id); + return this.grpcService.auth.removeMyMfaU2F(req); + } + + public VerifyMyMfaU2F(credential: string, tokenname: string): Promise { + const req = new VerifyWebAuthN(); + req.setPublicKeyCredential(credential); + req.setTokenName(tokenname); + + return this.grpcService.auth.verifyMyMfaU2F( + req, + ); + } + public RemoveMfaOTP(): Promise { return this.grpcService.auth.removeMfaOTP( - new Empty(), + new Empty(), ); } diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index 31fef046a0..d2ce0fbb32 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { Empty } from 'google-protobuf/google/protobuf/empty_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { BehaviorSubject } from 'rxjs'; +import { MultiFactorsResult } from '../proto/generated/admin_pb'; import { AddMachineKeyRequest, @@ -50,6 +51,7 @@ import { MachineKeySearchResponse, MachineKeyType, MachineResponse, + MultiFactor, NotificationType, OIDCApplicationCreate, OIDCConfig, @@ -122,6 +124,8 @@ import { ProjectView, RemoveOrgDomainRequest, RemoveOrgMemberRequest, + SecondFactor, + SecondFactorsResult, SetPasswordNotificationRequest, UpdateMachineRequest, UpdateUserAddressRequest, @@ -152,6 +156,7 @@ import { UserSearchResponse, UserView, ValidateOrgDomainRequest, + WebAuthNTokenID, ZitadelDocs, } from '../proto/generated/management_pb'; import { GrpcService } from './grpc.service'; @@ -185,6 +190,32 @@ export class ManagementService { return this.grpcService.mgmt.searchIdps(req); } + public GetLoginPolicyMultiFactors(): Promise { + const req = new Empty(); + return this.grpcService.mgmt.getLoginPolicyMultiFactors(req); + } + + public AddMultiFactorToLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.mgmt.addMultiFactorToLoginPolicy(req); + } + + public RemoveMultiFactorFromLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.mgmt.removeMultiFactorFromLoginPolicy(req); + } + + public GetLoginPolicySecondFactors(): Promise { + const req = new Empty(); + return this.grpcService.mgmt.getLoginPolicySecondFactors(req); + } + + public AddSecondFactorToLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.mgmt.addSecondFactorToLoginPolicy(req); + } + + public RemoveSecondFactorFromLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.mgmt.removeSecondFactorFromLoginPolicy(req); + } + public GetLoginPolicy(): Promise { const req = new Empty(); return this.grpcService.mgmt.getLoginPolicy(req); @@ -683,6 +714,12 @@ export class ManagementService { return this.grpcService.mgmt.removeMfaOTP(req); } + public RemoveMfaU2F(id: string): Promise { + const req = new WebAuthNTokenID(); + req.setId(id); + return this.grpcService.mgmt.removeMfaU2F(req); + } + public SaveUserProfile( id: string, firstName?: string, diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index a003ec328b..81d6e41d33 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -155,6 +155,7 @@ "MFA": { "TABLETYPE":"Typ", "TABLESTATE":"Status", + "ATTRIBUTE":"Attribut", "TABLEACTIONS":"Aktionen", "TITLE": "Multifaktor-Authentisierung", "DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um Dein Konto optimal zu schützen.", @@ -162,6 +163,12 @@ "OTP": "OTP konfigurieren", "OTP_DIALOG_TITLE": "OTP hinzufügen", "OTP_DIALOG_DESCRIPTION": "Scanne den QR-Code mit einer Authenticator App und verifiziere den erhaltenen Code, um OTP zu aktivieren.", + "U2F":"U2F hinzufügen", + "U2F_DIALOG_TITLE": "U2F hinzufügen", + "U2F_DIALOG_DESCRIPTION": "Gib einen Namen für den von dir verwendeten Universellen Multifaktor an.", + "U2F_SUCCESS":"U2F erfolgreich erstellt!", + "U2F_ERROR":"Ein Fehler ist aufgetreten!", + "U2F_NAME":"U2F Name", "TYPE": { "0":"Keine MFA definiert", "1":"OTP", @@ -174,8 +181,8 @@ "3": "Gelöscht" }, "DIALOG": { - "OTP_DELETE_TITLE":"OTP Faktor entfernen", - "OTP_DELETE_DESCRIPTION":"Sie sind im Begriff OTP als Zweitfaktormethode zu entfernen. Sind sie sicher?" + "MFA_DELETE_TITLE":"Zweiten Faktor entfernen", + "MFA_DELETE_DESCRIPTION":"Sie sind im Begriff eine Zweitfaktormethode zu entfernen. Sind sie sicher?" } }, "EXTERNALIDP": { @@ -345,6 +352,7 @@ "PHONEVERIFICATIONSENT":"Bestätigungscode per Telefonnummer gesendet.", "EMAILVERIFICATIONSENT":"Bestätigungscode per E-Mail gesendet.", "OTPREMOVED":"OTP entfernt.", + "U2FREMOVED":"U2F entfernt.", "INITIALPASSWORDSET":"Initiales Passwort gesetzt.", "PASSWORDNOTIFICATIONSENT":"Passwortänderung mittgeteilt.", "PASSWORDCHANGED":"Passwort geändert.", @@ -551,7 +559,9 @@ "ALLOWREGISTER":"Registrieren erlaubt", "ALLOWUSERNAMEPASSWORD_DESC":"Der konventionelle Login mit Benutzername und Passwort wird erlaubt.", "ALLOWEXTERNALIDP_DESC":"Der Login wird für die darunter liegenden Identity Provider erlaubt.", - "ALLOWREGISTER_DESC":"Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers." + "ALLOWREGISTER_DESC":"Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.", + "FORCEMFA":"Mfa erzwingen", + "FORCEMFA_DESC":"Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden." }, "RESET":"Richtlinie zurücksetzen", "CREATECUSTOM":"Benutzerdefinierte Richtlinie erstellen", @@ -763,7 +773,7 @@ }, "IDP":{ "LIST": { - "TITLE":"Identitäts Providers", + "TITLE":"Identitäts Provider", "DESCRIPTION":"Definieren Sie hier Ihre zusätzlichen Idps, die sie für die Authentifizierung in Ihren Organisationen verwenden können." }, "CREATE": { @@ -826,6 +836,37 @@ "DELETED":"Idp erfolgreich gelöscht!" } }, + "MFA":{ + "LIST": { + "MULTIFACTORTITLE":"Multifaktoren", + "MULTIFACTORDESCRIPTION":"Definieren Sie hier Ihre Multifaktoren, die sie für die Authentifizierung verwenden können.", + "SECONDFACTORTITLE":"Zweitfaktoren", + "SECONDFACTORDESCRIPTION":"Definieren Sie hier Ihre Zweitfaktoren, die sie für die Authentifizierung verwenden können." + }, + "CREATE": { + "TITLE":"Neuer Faktor", + "DESCRIPTION":"Definieren Sie hier den gewünschten Typ." + }, + "DELETE": { + "TITLE":"Faktor löschen", + "DESCRIPTION":"Sie sind im Begriff einen Faktor zu löschen. Die daruch hervorgerufenen Änderungen sind unwiederruflich. Wollen Sie dies wirklich tun?" + }, + "TOAST": { + "ADDED":"Erfolgreich hinzugefügt.", + "SAVED": "Erfolgreich gespeichert.", + "DELETED":"Mfa erfolgreich gelöscht!" + }, + "TYPE":"Typ", + "MULTIFACTORTYPES": { + "0":"Unknown", + "1":"U2F with Pin" + }, + "SECONDFACTORTYPES": { + "0":"Unknown", + "1":"OTP", + "2":"U2F" + } + }, "LOGINPOLICY": { "CREATE": { "TITLE":"Login Policy", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index b4f01ba663..c2ab9c5a97 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -155,6 +155,7 @@ "MFA": { "TABLETYPE":"Type", "TABLESTATE":"Status", + "ATTRIBUTE":"Attribut", "TABLEACTIONS":"Actions", "TITLE": "Multifactor Authentication", "DESCRIPTION": "Add a second factor to ensure optimal security for your account.", @@ -162,6 +163,12 @@ "OTP": "Configure OTP", "OTP_DIALOG_TITLE": "Add OTP", "OTP_DIALOG_DESCRIPTION": "Scan the QR code with an authenticator app and enter the code below to verify and activate the OTP method.", + "U2F":"Add U2F", + "U2F_DIALOG_TITLE": "Verify U2F", + "U2F_DIALOG_DESCRIPTION": "Enter a name for your used universal Multifactor.", + "U2F_SUCCESS":"U2F created successfully!", + "U2F_ERROR":"An error during U2F setup occurred!", + "U2F_NAME":"U2F Name", "TYPE": { "0": "No MFA defined", "1": "OTP", @@ -174,8 +181,8 @@ "3": "Deleted" }, "DIALOG": { - "OTP_DELETE_TITLE":"Remove OTP Factor", - "OTP_DELETE_DESCRIPTION":"You are about to delete OTP as second factor. Are you sure?" + "MFA_DELETE_TITLE":"Remove Secondfactor", + "MFA_DELETE_DESCRIPTION":"You are about to delete a second factor. Are you sure?" } }, "EXTERNALIDP": { @@ -345,6 +352,7 @@ "PHONEVERIFICATIONSENT":"Phone verification code sent.", "EMAILVERIFICATIONSENT":"E-mail verification code sent.", "OTPREMOVED":"OTP removed.", + "U2FREMOVED":"U2F removed.", "INITIALPASSWORDSET":"Initial password set.", "PASSWORDNOTIFICATIONSENT":"Password change notification sent.", "PASSWORDCHANGED":"Password changed successfully.", @@ -551,7 +559,9 @@ "ALLOWREGISTER":"Register allowed", "ALLOWUSERNAMEPASSWORD_DESC":"The conventional login with user name and password is allowed.", "ALLOWEXTERNALIDP_DESC":"The login is allowed for the underlying identity providers", - "ALLOWREGISTER_DESC":"If the option is selected, an additional step for registering a user appears in the login." + "ALLOWREGISTER_DESC":"If the option is selected, an additional step for registering a user appears in the login.", + "FORCEMFA":"Force MFA", + "FORCEMFA_DESC":"If the option is selected, users have to configure a second factor for login." }, "RESET":"Reset Policy", "CREATECUSTOM":"Create Custom Policy", @@ -826,6 +836,37 @@ "DELETED":"Idp removed successfully!" } }, + "MFA":{ + "LIST": { + "MULTIFACTORTITLE":"Multifactors", + "MULTIFACTORDESCRIPTION":"Define your Multifactors for Authentication here.", + "SECONDFACTORTITLE":"Secondfactors", + "SECONDFACTORDESCRIPTION":"Define your Secondfactors for Authentication here." + }, + "CREATE": { + "TITLE":"New Factor", + "DESCRIPTION":"Select your new Factor type." + }, + "DELETE": { + "TITLE":"Delete Factor", + "DESCRIPTION":"You are about to delete a Factor from Login Policy. Are you sure?" + }, + "TOAST": { + "ADDED":"Added successfully.", + "SAVED": "Saved successfully.", + "DELETED":"Removed successfully" + }, + "TYPE":"Type", + "MULTIFACTORTYPES": { + "0":"Unknown", + "1":"U2F with Pin" + }, + "SECONDFACTORTYPES": { + "0":"Unknown", + "1":"OTP", + "2":"U2F" + } + }, "LOGINPOLICY": { "CREATE": { "TITLE":"Login Policy", diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index 031503118c..a4d9ad2cd0 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -226,6 +226,11 @@ func (s *Server) RemoveMfaOTP(ctx context.Context, userID *management.UserID) (* return &empty.Empty{}, err } +func (s *Server) RemoveMfaU2F(ctx context.Context, webAuthNTokenID *management.WebAuthNTokenID) (*empty.Empty, error) { + err := s.user.RemoveU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id) + return &empty.Empty{}, err +} + func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) { request := userMembershipSearchRequestsToModel(in) request.AppendUserIDQuery(in.UserId) diff --git a/internal/api/grpc/management/user_converter.go b/internal/api/grpc/management/user_converter.go index 645a07c0ea..6b1dfe07eb 100644 --- a/internal/api/grpc/management/user_converter.go +++ b/internal/api/grpc/management/user_converter.go @@ -2,14 +2,15 @@ package management import ( "encoding/json" + "github.com/caos/logging" - "github.com/caos/zitadel/internal/model" "github.com/golang/protobuf/ptypes" "golang.org/x/text/language" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/structpb" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/model" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/pkg/grpc/management" "github.com/caos/zitadel/pkg/grpc/message" @@ -504,6 +505,7 @@ func mfaFromModel(mfa *usr_model.MultiFactor) *management.UserMultiFactor { State: mfaStateFromModel(mfa.State), Type: mfaTypeFromModel(mfa.Type), Attribute: mfa.Attribute, + Id: mfa.ID, } } diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index a9c77c84b8..08e5ced20f 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -231,6 +231,10 @@ func (repo *UserRepo) RemoveOTP(ctx context.Context, userID string) error { return repo.UserEvents.RemoveOTP(ctx, userID) } +func (repo *UserRepo) RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error { + return repo.UserEvents.RemoveU2FToken(ctx, userID, webAuthNTokenID) +} + func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) { policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) if err != nil && caos_errs.IsNotFound(err) { diff --git a/internal/management/repository/user.go b/internal/management/repository/user.go index 92a2364c11..b1a23581d9 100644 --- a/internal/management/repository/user.go +++ b/internal/management/repository/user.go @@ -32,6 +32,7 @@ type UserRepository interface { UserMFAs(ctx context.Context, userID string) ([]*model.MultiFactor, error) RemoveOTP(ctx context.Context, userID string) error + RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error) RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error diff --git a/pkg/grpc/management/proto/management.proto b/pkg/grpc/management/proto/management.proto index cd02b8f79e..e385818c93 100644 --- a/pkg/grpc/management/proto/management.proto +++ b/pkg/grpc/management/proto/management.proto @@ -399,6 +399,16 @@ service ManagementService { }; } + rpc RemoveMfaU2F(WebAuthNTokenID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{user_id}/mfas/u2f/{id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + // Sends an Notification (Email/SMS) with a password reset Link rpc SendSetPasswordNotification(SetPasswordNotificationRequest) returns (google.protobuf.Empty) { option (google.api.http) = { @@ -1646,6 +1656,11 @@ message UserID { string id = 1 [(validate.rules).string.min_len = 1]; } +message WebAuthNTokenID { + string user_id = 1 [(validate.rules).string.min_len = 1]; + string id = 2 [(validate.rules).string.min_len = 1]; +} + message LoginName { string login_name = 1 [(validate.rules).string.min_len = 1]; } @@ -2030,6 +2045,7 @@ message UserMultiFactor { MfaType type = 1; MFAState state = 2; string attribute = 3; + string id = 4; } enum MfaType { From b71a444e868dcf488448be7457d18cc217d5f08d Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 14 Dec 2020 10:54:29 +0100 Subject: [PATCH 03/22] fix: primary domain claim (#1082) * fix: primary domain scope (overwrite by roles and rogue `:`) * disable wrong users * fix test * show requested org name * only show domain when selected --- internal/api/oidc/client.go | 21 ++++-- internal/auth/repository/auth_request.go | 2 - .../eventsourcing/eventstore/auth_request.go | 73 ++++++++----------- .../eventstore/auth_request_test.go | 60 +++++++++++++-- internal/auth_request/model/auth_request.go | 8 +- internal/auth_request/model/next_step.go | 9 ++- internal/auth_request/model/request.go | 1 + internal/static/i18n/de.yaml | 2 + internal/static/i18n/en.yaml | 2 + .../login/handler/external_login_handler.go | 14 +--- .../handler/external_register_handler.go | 14 +--- internal/ui/login/handler/register_handler.go | 17 ++--- internal/ui/login/handler/renderer.go | 21 +++--- internal/ui/login/static/i18n/de.yaml | 4 + internal/ui/login/static/i18n/en.yaml | 4 + .../static/resources/themes/caos/css/dark.css | 23 ++++-- .../resources/themes/caos/css/dark.css.map | 2 +- .../resources/themes/caos/css/light.css | 23 ++++-- .../resources/themes/caos/css/light.css.map | 2 +- .../static/resources/themes/scss/main.scss | 29 ++++++-- .../resources/themes/zitadel/css/dark.css | 23 ++++-- .../resources/themes/zitadel/css/dark.css.map | 2 +- .../resources/themes/zitadel/css/light.css | 23 ++++-- .../themes/zitadel/css/light.css.map | 2 +- internal/ui/login/static/templates/login.html | 4 +- .../login/static/templates/select_user.html | 6 +- internal/user/repository/view/user_view.go | 2 +- 27 files changed, 245 insertions(+), 148 deletions(-) diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 760c8a007d..39fffdba0d 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -2,17 +2,16 @@ package oidc import ( "context" - "github.com/caos/zitadel/internal/auth_request/model" "strings" - "golang.org/x/text/language" - "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/op" + "golang.org/x/text/language" + "gopkg.in/square/go-jose.v2" "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/http" + "github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/errors" proj_model "github.com/caos/zitadel/internal/project/model" @@ -155,7 +154,7 @@ func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID, applicati roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix)) } if strings.HasPrefix(scope, model.OrgDomainPrimaryScope) { - userInfo.AppendClaims(model.OrgDomainPrimaryScope, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)) + userInfo.AppendClaims(model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)) } } } @@ -180,7 +179,7 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie if strings.HasPrefix(scope, ScopeProjectRolePrefix) { roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix)) } else if strings.HasPrefix(scope, model.OrgDomainPrimaryScope) { - claims = map[string]interface{}{model.OrgDomainPrimaryScope: strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)} + claims = appendClaim(claims, model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)) } } if len(roles) == 0 || clientID == "" { @@ -191,7 +190,7 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie return nil, err } if len(projectRoles) > 0 { - claims = map[string]interface{}{ClaimProjectRoles: projectRoles} + claims = appendClaim(claims, ClaimProjectRoles, projectRoles) } return claims, err } @@ -240,3 +239,11 @@ func getGender(gender user_model.Gender) string { } return "" } + +func appendClaim(claims map[string]interface{}, claim string, value interface{}) map[string]interface{} { + if claims == nil { + claims = make(map[string]interface{}) + } + claims[claim] = value + return claims +} diff --git a/internal/auth/repository/auth_request.go b/internal/auth/repository/auth_request.go index 3aaab44596..8425c68a06 100644 --- a/internal/auth/repository/auth_request.go +++ b/internal/auth/repository/auth_request.go @@ -30,6 +30,4 @@ type AuthRequestRepository interface { LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) error AutoRegisterExternalUser(ctx context.Context, user *user_model.User, externalIDP *user_model.ExternalIDP, member *org_model.OrgMember, authReqID, userAgentID, resourceOwner string, info *model.BrowserInfo) error ResetLinkingUsers(ctx context.Context, authReqID, userAgentID string) error - - GetOrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 68189e5890..0c456b1b5e 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -110,6 +110,9 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *mod } request.Audience = appIDs request.AppendAudIfNotExisting(app.ProjectID) + if err := setOrgID(repo.OrgViewProvider, request); err != nil { + return nil, err + } if request.LoginHint != "" { err = repo.checkLoginName(ctx, request, request.LoginHint) logging.LogWithFields("EVENT-aG311", "login name", request.LoginHint, "id", request.ID, "applicationID", request.ApplicationID, "traceID", tracing.TraceIDFromCtx(ctx)).OnError(err).Debug("login hint invalid") @@ -238,6 +241,9 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge if err != nil { return err } + if request.RequestedOrgID != "" && request.RequestedOrgID != user.ResourceOwner { + return errors.ThrowPreconditionFailed(nil, "EVENT-fJe2a", "Errors.User.NotAllowedOrg") + } request.SetUserInfo(user.ID, user.PreferredLoginName, user.DisplayName, user.ResourceOwner) return repo.AuthRequests.UpdateAuthRequest(ctx, request) } @@ -442,16 +448,9 @@ func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context, } func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model.AuthRequest) error { - orgID := request.UserOrgID + orgID := request.RequestedOrgID if orgID == "" { - primaryDomain := request.GetScopeOrgPrimaryDomain() - if primaryDomain != "" { - org, err := repo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - return err - } - orgID = org.ID - } + orgID = request.UserOrgID } if orgID == "" { orgID = repo.IAMID @@ -469,19 +468,9 @@ func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model } func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.AuthRequest, loginName string) (err error) { - primaryDomain := request.GetScopeOrgPrimaryDomain() - orgID := "" - if primaryDomain != "" { - org, err := repo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - return err - } - orgID = org.ID - } - user := new(user_view_model.UserView) - if orgID != "" { - user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, orgID) + if request.RequestedOrgID != "" { + user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, request.RequestedOrgID) } else { user, err = repo.View.UserByLoginName(loginName) if err == nil { @@ -499,14 +488,6 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model. return nil } -func (repo AuthRequestRepo) GetOrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) { - org, err := repo.OrgViewProvider.OrgByPrimaryDomain(primaryDomain) - if err != nil { - return nil, err - } - return org_view_model.OrgToModel(org), nil -} - func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *model.AuthRequest, user *user_view_model.UserView) error { loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, user.ResourceOwner) if err != nil { @@ -537,15 +518,9 @@ func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *model.AuthRequest } func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest, idpConfigID, externalUserID string) (err error) { - primaryDomain := request.GetScopeOrgPrimaryDomain() externalIDP := new(user_view_model.ExternalIDPView) - org := new(org_model.OrgView) - if primaryDomain != "" { - org, err = repo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - return err - } - externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, org.ID) + if request.RequestedOrgID != "" { + externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, request.RequestedOrgID) } else { externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigID(externalUserID, idpConfigID) } @@ -653,10 +628,11 @@ func (repo *AuthRequestRepo) usersForUserSelection(request *model.AuthRequest) ( users := make([]model.UserSelection, len(userSessions)) for i, session := range userSessions { users[i] = model.UserSelection{ - UserID: session.UserID, - DisplayName: session.DisplayName, - LoginName: session.LoginName, - UserSessionState: session.State, + UserID: session.UserID, + DisplayName: session.DisplayName, + LoginName: session.LoginName, + UserSessionState: session.State, + SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner, } } return users, nil @@ -753,6 +729,21 @@ func (repo *AuthRequestRepo) getLoginPolicy(ctx context.Context, orgID string) ( return iam_es_model.LoginPolicyViewToModel(policy), err } +func setOrgID(orgViewProvider orgViewProvider, request *model.AuthRequest) error { + primaryDomain := request.GetScopeOrgPrimaryDomain() + if primaryDomain == "" { + return nil + } + + org, err := orgViewProvider.OrgByPrimaryDomain(primaryDomain) + if err != nil { + return err + } + request.RequestedOrgID = org.ID + request.RequestedOrgName = org.Name + return nil +} + func getLoginPolicyIDPProviders(provider idpProviderViewProvider, iamID, orgID string, defaultPolicy bool) ([]*iam_model.IDPProviderView, error) { if defaultPolicy { idpProviders, err := provider.IDPProvidersByAggregateIDAndState(iamID, iam_model.IDPConfigStateActive) diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index c3de87ad30..32883f97f7 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -56,8 +56,9 @@ type mockViewUserSession struct { } type mockUser struct { - UserID string - LoginName string + UserID string + LoginName string + ResourceOwner string } func (m *mockViewUserSession) UserSessionByIDs(string, string) (*user_view_model.UserSessionView, error) { @@ -74,8 +75,9 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string) ([]*user_view_model. sessions := make([]*user_view_model.UserSessionView, len(m.Users)) for i, user := range m.Users { sessions[i] = &user_view_model.UserSessionView{ - UserID: user.UserID, - LoginName: user.LoginName, + UserID: user.UserID, + LoginName: user.LoginName, + ResourceOwner: user.ResourceOwner, } } return sessions, nil @@ -270,10 +272,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { { "id1", "loginname1", + "orgID1", }, { "id2", "loginname2", + "orgID2", }, }, }, @@ -285,12 +289,52 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { &model.SelectUserStep{ Users: []model.UserSelection{ { - UserID: "id1", - LoginName: "loginname1", + UserID: "id1", + LoginName: "loginname1", + SelectionPossible: true, }, { - UserID: "id2", - LoginName: "loginname2", + UserID: "id2", + LoginName: "loginname2", + SelectionPossible: true, + }, + }, + }}, + nil, + }, + { + "user not set, primary domain set, prompt select account, login and select account steps", + fields{ + userSessionViewProvider: &mockViewUserSession{ + Users: []mockUser{ + { + "id1", + "loginname1", + "orgID1", + }, + { + "id2", + "loginname2", + "orgID2", + }, + }, + }, + userEventProvider: &mockEventUser{}, + }, + args{&model.AuthRequest{Prompt: model.PromptSelectAccount, RequestedOrgID: "orgID1"}, false}, + []model.NextStep{ + &model.LoginStep{}, + &model.SelectUserStep{ + Users: []model.UserSelection{ + { + UserID: "id1", + LoginName: "loginname1", + SelectionPossible: true, + }, + { + UserID: "id2", + LoginName: "loginname2", + SelectionPossible: false, }, }, }}, diff --git a/internal/auth_request/model/auth_request.go b/internal/auth_request/model/auth_request.go index bd51897afd..df74e85d39 100644 --- a/internal/auth_request/model/auth_request.go +++ b/internal/auth_request/model/auth_request.go @@ -1,11 +1,13 @@ package model import ( - "github.com/caos/zitadel/internal/iam/model" - "golang.org/x/text/language" "strings" "time" + "golang.org/x/text/language" + + "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/errors" ) @@ -30,6 +32,8 @@ type AuthRequest struct { LoginName string DisplayName string UserOrgID string + RequestedOrgID string + RequestedOrgName string SelectedIDPConfigID string LinkingUsers []*ExternalUser PossibleSteps []NextStep diff --git a/internal/auth_request/model/next_step.go b/internal/auth_request/model/next_step.go index 3312244e11..b3a3a07733 100644 --- a/internal/auth_request/model/next_step.go +++ b/internal/auth_request/model/next_step.go @@ -48,10 +48,11 @@ func (s *SelectUserStep) Type() NextStepType { } type UserSelection struct { - UserID string - DisplayName string - LoginName string - UserSessionState UserSessionState + UserID string + DisplayName string + LoginName string + UserSessionState UserSessionState + SelectionPossible bool } type InitUserStep struct { diff --git a/internal/auth_request/model/request.go b/internal/auth_request/model/request.go index 72f10ce49f..43f8c93eaf 100644 --- a/internal/auth_request/model/request.go +++ b/internal/auth_request/model/request.go @@ -20,6 +20,7 @@ const ( const ( OrgDomainPrimaryScope = "urn:zitadel:iam:org:domain:primary:" + OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary" ProjectIDScope = "urn:zitadel:iam:org:project:id:" AudSuffix = ":aud" ) diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index ac68d86282..03810e5730 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -4,6 +4,8 @@ Errors: OriginNotAllowed: Dieser "Origin" ist nicht freigeschaltet User: NotFound: Benutzer konnte nicht gefunden werden + NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden + NotAllowedOrg: Benutzer gehört nicht der benötigten Organisation an UserIDMissing: User ID fehlt OrgIamPolicyNil: Organisations Policy ist leer EmailAsUsernameNotAllowed: Benutzername darf keine E-Mail Adresse sein diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index fd6a101090..637845077b 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -4,6 +4,8 @@ Errors: OriginNotAllowed: This "Origin" is not allowed User: NotFound: User could not be found + NotFoundOnOrg: User could not be found on chosen organisation + NotAllowedOrg: User is no member of the required organisation UserIDMissing: User ID missing OrgIamPolicyNil: Organisation Policy is empty EmailAsUsernameNotAllowed: Email is not allowed as username diff --git a/internal/ui/login/handler/external_login_handler.go b/internal/ui/login/handler/external_login_handler.go index 2bb2d0d6e7..1d7455e91f 100644 --- a/internal/ui/login/handler/external_login_handler.go +++ b/internal/ui/login/handler/external_login_handler.go @@ -188,16 +188,10 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID}, Roles: []string{orgProjectCreatorRole}, } - if authReq.GetScopeOrgPrimaryDomain() != "" { - primaryDomain := authReq.GetScopeOrgPrimaryDomain() - org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, err) - } - if org.ID != iam.GlobalOrgID { - member = nil - resourceOwner = org.ID - } + + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + member = nil + resourceOwner = authReq.RequestedOrgID } orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner) diff --git a/internal/ui/login/handler/external_register_handler.go b/internal/ui/login/handler/external_register_handler.go index 0d8079adeb..3d2834bb47 100644 --- a/internal/ui/login/handler/external_register_handler.go +++ b/internal/ui/login/handler/external_register_handler.go @@ -85,17 +85,9 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques Roles: []string{orgProjectCreatorRole}, } - if authReq.GetScopeOrgPrimaryDomain() != "" { - primaryDomain := authReq.GetScopeOrgPrimaryDomain() - org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - l.renderRegisterOption(w, r, authReq, err) - return - } - if org.ID != iam.GlobalOrgID { - member = nil - resourceOwner = org.ID - } + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + member = nil + resourceOwner = authReq.RequestedOrgID } orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner) if err != nil { diff --git a/internal/ui/login/handler/register_handler.go b/internal/ui/login/handler/register_handler.go index 4ec4eb1bf8..ffb9d91731 100644 --- a/internal/ui/login/handler/register_handler.go +++ b/internal/ui/login/handler/register_handler.go @@ -1,9 +1,10 @@ package handler import ( - "golang.org/x/text/language" "net/http" + "golang.org/x/text/language" + "github.com/caos/zitadel/internal/auth_request/model" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" @@ -71,17 +72,9 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID}, Roles: []string{orgProjectCreatorRole}, } - if authRequest.GetScopeOrgPrimaryDomain() != "" { - primaryDomain := authRequest.GetScopeOrgPrimaryDomain() - org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - l.renderRegisterOption(w, r, authRequest, err) - return - } - if org.ID != iam.GlobalOrgID { - member = nil - resourceOwner = org.ID - } + if authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != iam.GlobalOrgID { + member = nil + resourceOwner = authRequest.RequestedOrgID } user, err := l.authRepo.Register(setContext(r.Context(), resourceOwner), data.toUserModel(), member, resourceOwner) if err != nil { diff --git a/internal/ui/login/handler/renderer.go b/internal/ui/login/handler/renderer.go index dad96049cb..6dbf8d2dce 100644 --- a/internal/ui/login/handler/renderer.go +++ b/internal/ui/login/handler/renderer.go @@ -267,6 +267,7 @@ func (l *Login) getBaseData(r *http.Request, authReq *model.AuthRequest, title s Theme: l.getTheme(r), ThemeMode: l.getThemeMode(r), OrgID: l.getOrgID(authReq), + OrgName: l.getOrgName(authReq), AuthReqID: getRequestID(authReq, r), CSRF: csrf.TemplateField(r), Nonce: http_mw.GetNonce(r), @@ -312,20 +313,17 @@ func (l *Login) getOrgID(authReq *model.AuthRequest) string { if authReq == nil { return "" } - if authReq.UserOrgID != "" { - return authReq.UserOrgID + if authReq.RequestedOrgID != "" { + return authReq.RequestedOrgID } - if authReq.Request == nil { + return authReq.UserOrgID +} + +func (l *Login) getOrgName(authReq *model.AuthRequest) string { + if authReq == nil { return "" } - primaryDomain := authReq.GetScopeOrgPrimaryDomain() - if primaryDomain != "" { - org, _ := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if org != nil { - return org.ID - } - } - return "" + return authReq.RequestedOrgName } func getRequestID(authReq *model.AuthRequest, r *http.Request) string { @@ -355,6 +353,7 @@ type baseData struct { Theme string ThemeMode string OrgID string + OrgName string AuthReqID string CSRF template.HTML Nonce string diff --git a/internal/ui/login/static/i18n/de.yaml b/internal/ui/login/static/i18n/de.yaml index d0a303da13..8a4fe747fd 100644 --- a/internal/ui/login/static/i18n/de.yaml +++ b/internal/ui/login/static/i18n/de.yaml @@ -16,6 +16,7 @@ Login: Loginname: Loginname LoginnamePlaceHolder: username@domain ExternalLogin: Melde dich mit einem externen Benutzer an + MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören. UserSelection: Title: Account auswählen @@ -25,6 +26,7 @@ UserSelection: OtherUser: Anderer Benutzer SessionState0: aktiv SessionState1: inaktiv + MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören. UsernameChange: Title: Usernamen ändern @@ -216,6 +218,8 @@ Errors: RequestTypeNotSupported: Requesttyp wird nicht unterstürzt User: NotFound: Benutzer konnte nicht gefunden werden + NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden + NotAllowedOrg: Benutzer gehört nicht der benötigten Organisation an NotMatchingUserID: User stimm nicht mit User in Auth Request überein UserIDMissing: UserID ist leer Invalid: Userdaten sind ungültig diff --git a/internal/ui/login/static/i18n/en.yaml b/internal/ui/login/static/i18n/en.yaml index a66f72f465..67793d2045 100644 --- a/internal/ui/login/static/i18n/en.yaml +++ b/internal/ui/login/static/i18n/en.yaml @@ -6,6 +6,7 @@ Login: Loginname: Loginname LoginnamePlaceHolder: username@domain ExternalLogin: Login with an external user. + MustBeMemberOfOrg: The user must be mermber of the {{.OrgDomain}} organisation. UserSelection: Title: Select account @@ -15,6 +16,7 @@ UserSelection: OtherUser: Other User SessionState0: active SessionState1: inactive + MustBeMemberOfOrg: The user must be mermber of the {{.OrgDomain}} organisation. Password: Title: Password @@ -216,6 +218,8 @@ Errors: RequestTypeNotSupported: Request type is not supported User: NotFound: User could not be found + NotFoundOnOrg: User could not be found on chosen organisation + NotAllowedOrg: User is no member of the required organisation NotMatchingUserID: User and user in authrequest don't match UserIDMissing: UserID is empty Invalid: Invalid userdata diff --git a/internal/ui/login/static/resources/themes/caos/css/dark.css b/internal/ui/login/static/resources/themes/caos/css/dark.css index 49c8e83bed..7506180196 100644 --- a/internal/ui/login/static/resources/themes/caos/css/dark.css +++ b/internal/ui/login/static/resources/themes/caos/css/dark.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -227,6 +226,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -334,10 +334,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -360,7 +356,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -368,6 +364,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/caos/css/dark.css.map b/internal/ui/login/static/resources/themes/caos/css/dark.css.map index 2e58686f16..5c2f220cfe 100644 --- a/internal/ui/login/static/resources/themes/caos/css/dark.css.map +++ b/internal/ui/login/static/resources/themes/caos/css/dark.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCNc;EDOd,OCNQ;EDOR;EACA;EACA;;;AAMJ;EACI,OChBQ;EDiBR,aCvBS;EDwBT;EACA,WE9BS;EF+BT;;;AAGJ;EACI,OCxBQ;EDyBR,aC/BS;EDgCT;EACA,WErCU;;;AFwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC5DW;ED6DX;EACA;;AAEA;EACI,OChEY;;ADmEhB;EACI;;;AAIR;EACI,kBC5Ec;ED6Ed,OC3EW;ED4EX;EACA;EACA;EACA;EACA,QE7FU;EF8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCzFY;ED0FZ,OC7FU;ED8FV;;AAGJ;EACI,kBChGO;EDiGP,OClGI;EDmGJ;;AACA;EACI,kBCnGQ;;ADuGhB;EACI,kBE7FW;EF8FX;;AAEA;EACI,kBEjGO;EFkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OErGa;EFsGb,kBErGmB;;AFuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBEnImB;EFoInB,OClJQ;EDmJR,QE/JU;EFgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EE9JN;;AACA;EFyJE;IExJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEtRO;EFuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OElTP;;AFyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE5UV;;AACA;EFuUM;IEtUJ;IACA;;;AF8UQ;EACI;EACA;EEnVd;;AACA;EFgVU;IE/UR;IACA;;;AFqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE1VN;;AF+VE;EACI,OEjWL;;AFsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC7YI;;ADgZR;EACI,MClZU;;;ADuZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OEjbO;;;AFobX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCLc;EDMd,OCLQ;EDMR;EACA;EACA;;;AAMJ;EACI,OCfQ;EDgBR,aCtBS;EDuBT;EACA,WE7BS;EF8BT;;;AAGJ;EACI,OCvBQ;EDwBR,aC9BS;ED+BT;EACA,WEpCU;;;AFuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC3DW;ED4DX;EACA;;AAEA;EACI,OC/DY;;ADkEhB;EACI;;;AAIR;EACI,kBC3Ec;ED4Ed,OC1EW;ED2EX;EACA;EACA;EACA;EACA,QE5FU;EF6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCxFY;EDyFZ,OC5FU;ED6FV;;AAGJ;EACI,kBC/FO;EDgGP,OCjGI;EDkGJ;;AACA;EACI,kBClGQ;;ADsGhB;EACI,kBE5FW;EF6FX;;AAEA;EACI,kBEhGO;EFiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OEpGa;EFqGb,kBEpGmB;;AFsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBElImB;EFmInB,OCjJQ;EDkJR,QE9JU;EF+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EE9JN;;AACA;EFwJE;IEvJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cElRO;EFmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OE9SP;;AFmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBC1UE;;AD8UF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE7VV;;AACA;EFwVM;IEvVJ;IACA;;;AF+VQ;EACI;EACA;EEpWd;;AACA;EFiWU;IEhWR;IACA;;;AFsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE3WN;;AFgXE;EACI,OElXL;;AFuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC9ZI;;ADiaR;EACI,MCnaU;;;ADwad;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OElcO;;;AFqcX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/caos/css/light.css b/internal/ui/login/static/resources/themes/caos/css/light.css index c4cd152ea1..1d905b2969 100644 --- a/internal/ui/login/static/resources/themes/caos/css/light.css +++ b/internal/ui/login/static/resources/themes/caos/css/light.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -227,6 +226,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -334,10 +334,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -360,7 +356,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -368,6 +364,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/caos/css/light.css.map b/internal/ui/login/static/resources/themes/caos/css/light.css.map index 6c3f74693e..18d1ec38a3 100644 --- a/internal/ui/login/static/resources/themes/caos/css/light.css.map +++ b/internal/ui/login/static/resources/themes/caos/css/light.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCNc;EDOd,OCNQ;EDOR;EACA;EACA;;;AAMJ;EACI,OChBQ;EDiBR,aCvBS;EDwBT;EACA,WE9BS;EF+BT;;;AAGJ;EACI,OCxBQ;EDyBR,aC/BS;EDgCT;EACA,WErCU;;;AFwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC5DW;ED6DX;EACA;;AAEA;EACI,OChEY;;ADmEhB;EACI;;;AAIR;EACI,kBC5Ec;ED6Ed,OC3EW;ED4EX;EACA;EACA;EACA;EACA,QE7FU;EF8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCzFY;ED0FZ,OC7FU;ED8FV;;AAGJ;EACI,kBChGO;EDiGP,OClGI;EDmGJ;;AACA;EACI,kBCnGQ;;ADuGhB;EACI,kBE7FW;EF8FX;;AAEA;EACI,kBEjGO;EFkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OErGa;EFsGb,kBErGmB;;AFuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBEnImB;EFoInB,OClJQ;EDmJR,QE/JU;EFgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EE9JN;;AACA;EFyJE;IExJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEtRO;EFuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OElTP;;AFyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE5UV;;AACA;EFuUM;IEtUJ;IACA;;;AF8UQ;EACI;EACA;EEnVd;;AACA;EFgVU;IE/UR;IACA;;;AFqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE1VN;;AF+VE;EACI,OEjWL;;AFsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC7YI;;ADgZR;EACI,MClZU;;;ADuZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OEjbO;;;AFobX;EACI;;;AAGJ;EACI;;;AG9dJ;EACI,kBFeQ;EEdR,OFac;;AERd;EACI;;AAGJ;EACI,OFGU;;AEAd;EACI;EACA;EACA;;AAEA;EACI,kBFIa;EEHb;EACA,ODyBgB;;ACtBpB;EACI,kBFVG;EEWH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBFdI;;AEkBZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,OFhCM;;AEkCN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,OF9EU;;AEkFV;EACI,MFnFM;;AEsFV;EACI,MFtFA;;AE0FR;EAEQ;;;AAMR;EACI,OFpGU;;AEwGb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCLc;EDMd,OCLQ;EDMR;EACA;EACA;;;AAMJ;EACI,OCfQ;EDgBR,aCtBS;EDuBT;EACA,WE7BS;EF8BT;;;AAGJ;EACI,OCvBQ;EDwBR,aC9BS;ED+BT;EACA,WEpCU;;;AFuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC3DW;ED4DX;EACA;;AAEA;EACI,OC/DY;;ADkEhB;EACI;;;AAIR;EACI,kBC3Ec;ED4Ed,OC1EW;ED2EX;EACA;EACA;EACA;EACA,QE5FU;EF6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCxFY;EDyFZ,OC5FU;ED6FV;;AAGJ;EACI,kBC/FO;EDgGP,OCjGI;EDkGJ;;AACA;EACI,kBClGQ;;ADsGhB;EACI,kBE5FW;EF6FX;;AAEA;EACI,kBEhGO;EFiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OEpGa;EFqGb,kBEpGmB;;AFsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBElImB;EFmInB,OCjJQ;EDkJR,QE9JU;EF+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EE9JN;;AACA;EFwJE;IEvJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cElRO;EFmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OE9SP;;AFmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBC1UE;;AD8UF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE7VV;;AACA;EFwVM;IEvVJ;IACA;;;AF+VQ;EACI;EACA;EEpWd;;AACA;EFiWU;IEhWR;IACA;;;AFsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE3WN;;AFgXE;EACI,OElXL;;AFuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC9ZI;;ADiaR;EACI,MCnaU;;;ADwad;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OElcO;;;AFqcX;EACI;;;AAGJ;EACI;;;AG/eJ;EACI,kBFeQ;EEdR,OFac;;AERd;EACI;;AAGJ;EACI,OFGU;;AEAd;EACI;EACA;EACA;;AAEA;EACI,kBFIa;EEHb;EACA,ODyBgB;;ACtBpB;EACI,kBFVG;EEWH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBFdI;;AEkBZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,OFhCM;;AEkCN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,OF9EU;;AEkFV;EACI,MFnFM;;AEsFV;EACI,MFtFA;;AE0FR;EAEQ;;;AAMR;EACI,OFpGU;;AEwGb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/scss/main.scss b/internal/ui/login/static/resources/themes/scss/main.scss index 137c9081ce..30296adb94 100644 --- a/internal/ui/login/static/resources/themes/scss/main.scss +++ b/internal/ui/login/static/resources/themes/scss/main.scss @@ -5,7 +5,6 @@ font-family: $standardFont; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -174,6 +173,7 @@ input:not([type='radio']), select { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; @include retina-background-image($profileImgDark, "png", false, 80px, 80px); } @@ -296,10 +296,6 @@ form { button.user-selection { @extend %profile-image; - .profile-image { - height: 80px; - width: 80px; - } .sessionstate { display: inline-block; @@ -327,7 +323,7 @@ form { position: relative; &.names { - margin: 15px; + margin: 0 15px; .displayname { font-size: 1.4rem; @@ -337,6 +333,27 @@ form { } } } + + &:disabled { + background: transparent; + border: none; + cursor: not-allowed; + + .profile-image { + opacity: 0.3; + } + + .sessionstate { + background-color: $backgroundColor; + } + + .names { + .displayname, .loginname { + font-style: italic; + color: #444444; + } + } + } } button.other-user { diff --git a/internal/ui/login/static/resources/themes/zitadel/css/dark.css b/internal/ui/login/static/resources/themes/zitadel/css/dark.css index f289f43bc1..e9f2328c89 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/dark.css +++ b/internal/ui/login/static/resources/themes/zitadel/css/dark.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -228,6 +227,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -335,10 +335,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -361,7 +357,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -369,6 +365,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map b/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map index bab7b8d3dc..7bb0985450 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map +++ b/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCGc;EDFd,OCGQ;EDFR;EACA;EACA;EAEI;;;AAIR;EACI,OCPQ;EDQR,aChCS;EDiCT;EACA,WC9BS;ED+BT;;;AAGJ;EACI,OCfQ;EDgBR,aCxCS;EDyCT;EACA,WCrCU;;;ADwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCnDW;EDoDX;EACA;;AAEA;EACI,OCvDY;;AD0DhB;EACI;;;AAIR;EACI,kBCnEc;EDoEd,OClEW;EDmEX;EACA;EACA;EACA;EACA,QC7FU;ED8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBChFY;EDiFZ,OCpFU;EDqFV;;AAGJ;EACI,kBCvFO;EDwFP,OCzFI;ED0FJ;;AACA;EACI,kBC1FQ;;AD8FhB;EACI,kBC7FW;ED8FX;;AAEA;EACI,kBCjGO;EDkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCrGa;EDsGb,kBCrGmB;;ADuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBCnImB;EDoInB,OCzIQ;ED0IR,QC/JU;EDgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EC9JN;;AACA;EDyJE;ICxJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCtRO;EDuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OClTP;;ADyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC5UV;;AACA;EDuUM;ICtUJ;IACA;;;AD8UQ;EACI;EACA;ECnVd;;AACA;EDgVU;IC/UR;IACA;;;ADqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC1VN;;AD+VE;EACI,OCjWL;;ADsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCpYI;;ADuYR;EACI,MCzYU;;;AD8Yd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OCjbO;;;ADobX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCIc;EDHd,OCIQ;EDHR;EACA;EACA;EAEI;;;AAIR;EACI,OCNQ;EDOR,aC/BS;EDgCT;EACA,WC7BS;ED8BT;;;AAGJ;EACI,OCdQ;EDeR,aCvCS;EDwCT;EACA,WCpCU;;;ADuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OClDW;EDmDX;EACA;;AAEA;EACI,OCtDY;;ADyDhB;EACI;;;AAIR;EACI,kBClEc;EDmEd,OCjEW;EDkEX;EACA;EACA;EACA;EACA,QC5FU;ED6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBC/EY;EDgFZ,OCnFU;EDoFV;;AAGJ;EACI,kBCtFO;EDuFP,OCxFI;EDyFJ;;AACA;EACI,kBCzFQ;;AD6FhB;EACI,kBC5FW;ED6FX;;AAEA;EACI,kBChGO;EDiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCpGa;EDqGb,kBCpGmB;;ADsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBClImB;EDmInB,OCxIQ;EDyIR,QC9JU;ED+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EC9JN;;AACA;EDwJE;ICvJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cClRO;EDmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OC9SP;;ADmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBCjUE;;ADqUF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC7VV;;AACA;EDwVM;ICvVJ;IACA;;;AD+VQ;EACI;EACA;ECpWd;;AACA;EDiWU;IChWR;IACA;;;ADsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC3WN;;ADgXE;EACI,OClXL;;ADuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCrZI;;ADwZR;EACI,MC1ZU;;;AD+Zd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OClcO;;;ADqcX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/zitadel/css/light.css b/internal/ui/login/static/resources/themes/zitadel/css/light.css index eae167bc73..26432267ba 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/light.css +++ b/internal/ui/login/static/resources/themes/zitadel/css/light.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -228,6 +227,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -335,10 +335,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -361,7 +357,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -369,6 +365,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/zitadel/css/light.css.map b/internal/ui/login/static/resources/themes/zitadel/css/light.css.map index b7dfa7f778..ab57d44c72 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/light.css.map +++ b/internal/ui/login/static/resources/themes/zitadel/css/light.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCGc;EDFd,OCGQ;EDFR;EACA;EACA;EAEI;;;AAIR;EACI,OCPQ;EDQR,aChCS;EDiCT;EACA,WC9BS;ED+BT;;;AAGJ;EACI,OCfQ;EDgBR,aCxCS;EDyCT;EACA,WCrCU;;;ADwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCnDW;EDoDX;EACA;;AAEA;EACI,OCvDY;;AD0DhB;EACI;;;AAIR;EACI,kBCnEc;EDoEd,OClEW;EDmEX;EACA;EACA;EACA;EACA,QC7FU;ED8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBChFY;EDiFZ,OCpFU;EDqFV;;AAGJ;EACI,kBCvFO;EDwFP,OCzFI;ED0FJ;;AACA;EACI,kBC1FQ;;AD8FhB;EACI,kBC7FW;ED8FX;;AAEA;EACI,kBCjGO;EDkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCrGa;EDsGb,kBCrGmB;;ADuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBCnImB;EDoInB,OCzIQ;ED0IR,QC/JU;EDgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EC9JN;;AACA;EDyJE;ICxJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCtRO;EDuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OClTP;;ADyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC5UV;;AACA;EDuUM;ICtUJ;IACA;;;AD8UQ;EACI;EACA;ECnVd;;AACA;EDgVU;IC/UR;IACA;;;ADqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC1VN;;AD+VE;EACI,OCjWL;;ADsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCpYI;;ADuYR;EACI,MCzYU;;;AD8Yd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OCjbO;;;ADobX;EACI;;;AAGJ;EACI;;;AE9dJ;EACI,kBD2CmB;EC1CnB,ODsBc;ECpBV;;AAGJ;EACI;;AAGJ;EACI,ODYU;;ACTd;EACI,kBD4Be;EC3Bf,ODSO;ECRP;;AAEA;EACI,kBD0Ba;ECzBb;EACA,ODyBgB;;ACtBpB;EACI,kBDDG;ECEH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBDLI;;ACSZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,ODvBM;;ACyBN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,ODrEU;;ACyEV;EACI,MD1EM;;AC6EV;EACI,MD1DW;;ACsEnB;EACI,OD3FU;;AC+Fb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCIc;EDHd,OCIQ;EDHR;EACA;EACA;EAEI;;;AAIR;EACI,OCNQ;EDOR,aC/BS;EDgCT;EACA,WC7BS;ED8BT;;;AAGJ;EACI,OCdQ;EDeR,aCvCS;EDwCT;EACA,WCpCU;;;ADuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OClDW;EDmDX;EACA;;AAEA;EACI,OCtDY;;ADyDhB;EACI;;;AAIR;EACI,kBClEc;EDmEd,OCjEW;EDkEX;EACA;EACA;EACA;EACA,QC5FU;ED6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBC/EY;EDgFZ,OCnFU;EDoFV;;AAGJ;EACI,kBCtFO;EDuFP,OCxFI;EDyFJ;;AACA;EACI,kBCzFQ;;AD6FhB;EACI,kBC5FW;ED6FX;;AAEA;EACI,kBChGO;EDiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCpGa;EDqGb,kBCpGmB;;ADsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBClImB;EDmInB,OCxIQ;EDyIR,QC9JU;ED+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EC9JN;;AACA;EDwJE;ICvJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cClRO;EDmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OC9SP;;ADmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBCjUE;;ADqUF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC7VV;;AACA;EDwVM;ICvVJ;IACA;;;AD+VQ;EACI;EACA;ECpWd;;AACA;EDiWU;IChWR;IACA;;;ADsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC3WN;;ADgXE;EACI,OClXL;;ADuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCrZI;;ADwZR;EACI,MC1ZU;;;AD+Zd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OClcO;;;ADqcX;EACI;;;AAGJ;EACI;;;AE/eJ;EACI,kBD2CmB;EC1CnB,ODsBc;ECpBV;;AAGJ;EACI;;AAGJ;EACI,ODYU;;ACTd;EACI,kBD4Be;EC3Bf,ODSO;ECRP;;AAEA;EACI,kBD0Ba;ECzBb;EACA,ODyBgB;;ACtBpB;EACI,kBDDG;ECEH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBDLI;;ACSZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,ODvBM;;ACyBN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,ODrEU;;ACyEV;EACI,MD1EM;;AC6EV;EACI,MD1DW;;ACsEnB;EACI,OD3FU;;AC+Fb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file diff --git a/internal/ui/login/static/templates/login.html b/internal/ui/login/static/templates/login.html index 0df0371c3c..347770c129 100644 --- a/internal/ui/login/static/templates/login.html +++ b/internal/ui/login/static/templates/login.html @@ -4,10 +4,10 @@
{{if .Linking}}

{{t "Login.TitleLinking"}}

-

{{t "Login.DescriptionLinking"}}

+

{{t "Login.DescriptionLinking"}}{{if .OrgName}} {{t "Login.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{else}}

{{t "Login.Title"}}

-

{{t "Login.Description"}}

+

{{t "Login.Description"}}{{if .OrgName}} {{t "Login.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{end}}
diff --git a/internal/ui/login/static/templates/select_user.html b/internal/ui/login/static/templates/select_user.html index 26f7c02ec1..fc09fa66b6 100644 --- a/internal/ui/login/static/templates/select_user.html +++ b/internal/ui/login/static/templates/select_user.html @@ -3,10 +3,10 @@
{{if .Linking}}

{{t "UserSelection.TitleLinking"}}

-

{{t "UserSelection.DescriptionLinking"}}

+

{{t "UserSelection.DescriptionLinking"}}{{if .OrgName}} {{t "UserSelection.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{else}}

{{t "UserSelection.Title"}}

-

{{t "UserSelection.Description"}}

+

{{t "UserSelection.Description"}}{{if .OrgName}} {{t "UserSelection.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{end}}
@@ -22,7 +22,7 @@
{{ range $user := .Users }} {{ $sessionState := (printf "sessionstate-%v" $user.UserSessionState) }} - - +
\ No newline at end of file diff --git a/console/src/app/modules/mfa-table/mfa-table.component.ts b/console/src/app/modules/mfa-table/mfa-table.component.ts index 401e90af48..a8c425b705 100644 --- a/console/src/app/modules/mfa-table/mfa-table.component.ts +++ b/console/src/app/modules/mfa-table/mfa-table.component.ts @@ -4,12 +4,16 @@ import { MatPaginator } from '@angular/material/paginator'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { - MultiFactor as AdminMultiFactor, MultiFactorType as AdminMultiFactorType, - SecondFactor as AdminSecondFactor, SecondFactorType as AdminSecondFactorType, + MultiFactor as AdminMultiFactor, + MultiFactorType as AdminMultiFactorType, + SecondFactor as AdminSecondFactor, + SecondFactorType as AdminSecondFactorType, } from 'src/app/proto/generated/admin_pb'; import { - MultiFactor as MgmtMultiFactor, MultiFactorType as MgmtMultiFactorType, - SecondFactor as MgmtSecondFactor, SecondFactorType as MgmtSecondFactorType, + MultiFactor as MgmtMultiFactor, + MultiFactorType as MgmtMultiFactorType, + SecondFactor as MgmtSecondFactor, + SecondFactorType as MgmtSecondFactorType, } from 'src/app/proto/generated/management_pb'; import { AdminService } from 'src/app/services/admin.service'; import { ManagementService } from 'src/app/services/mgmt.service'; @@ -111,11 +115,19 @@ export class MfaTableComponent implements OnInit { []; } else if (this.componentType === LoginMethodComponentType.SecondFactor) { selection = this.serviceType === PolicyComponentServiceType.MGMT ? - [MgmtSecondFactorType.SECONDFACTORTYPE_U2F, MgmtSecondFactorType.SECONDFACTORTYPE_U2F] : + [MgmtSecondFactorType.SECONDFACTORTYPE_U2F, MgmtSecondFactorType.SECONDFACTORTYPE_OTP] : this.serviceType === PolicyComponentServiceType.ADMIN ? [AdminSecondFactorType.SECONDFACTORTYPE_OTP, AdminSecondFactorType.SECONDFACTORTYPE_U2F] : []; } + + this.mfas.forEach(mfa => { + const index = selection.findIndex(sel => sel === mfa); + if (index > -1) { + selection.splice(index, 1); + } + }); + const dialogRef = this.dialog.open(DialogAddTypeComponent, { data: { title: 'MFA.CREATE.TITLE', diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html index 90fca4d406..f76da7bc9c 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy.component.html @@ -51,6 +51,16 @@

{{'POLICY.DATA.FORCEMFA_DESC' | translate}}

+
+ + {{'MFA.TYPE' | translate}} + + + {{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}} + + + +
{{ 'USER.MFA.TABLETYPE' | translate }} {{'USER.MFA.TYPE.'+ mfa.type | translate}} {{ 'USER.MFA.ATTRIBUTE' | translate }} + {{ mfa.attr }} + + {{ 'USER.MFA.TABLESTATE' | translate }} @@ -21,7 +29,7 @@ {{ 'USER.MFA.TABLEACTIONS' | translate }}
+ + + + + + + + + + + + + + + + + +
{{ 'USER.PASSWORDLESS.NAME' | translate }} + {{ mfa?.name }} + + {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} + {{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}} + + + {{ 'USER.PASSWORDLESS.TABLEACTIONS' | translate }} + +
+
+
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.scss b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.scss new file mode 100644 index 0000000000..3f9facae7c --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.scss @@ -0,0 +1,41 @@ + +.add-row { + display: flex; + margin: -.5rem; + flex-wrap: wrap; + + .button { + margin: .5rem; + margin-top: 1rem; + + .icon { + margin-right: .5rem; + } + } +} + +.centered { + display: flex; + align-items: center; + + i { + margin-left: 1rem; + color: var(--color-main); + } +} + +.table { + width: 100%; + + td, + th { + &:first-child { + padding-left: 0; + padding-right: 1rem; + } + + &:last-child { + padding-right: 0; + } + } +} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.spec.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.spec.ts new file mode 100644 index 0000000000..fbee4d4c7f --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { AuthPasswordlessComponent } from './auth-passwordless.component'; + +describe('AuthPasswordlessComponent', () => { + let component: AuthPasswordlessComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AuthPasswordlessComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AuthPasswordlessComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts new file mode 100644 index 0000000000..2bde75405e --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts @@ -0,0 +1,114 @@ +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSort } from '@angular/material/sort'; +import { MatTable, MatTableDataSource } from '@angular/material/table'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; +import { MFAState, WebAuthNResponse, WebAuthNToken } from 'src/app/proto/generated/auth_pb'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { ToastService } from 'src/app/services/toast.service'; + +import { _base64ToArrayBuffer } from '../../u2f-util'; +import { DialogU2FComponent, U2FComponentDestination } from '../dialog-u2f/dialog-u2f.component'; + +export interface WebAuthNOptions { + challenge: string; + rp: { name: string, id: string; }; + user: { name: string, id: string, displayName: string; }; + pubKeyCredParams: any; + authenticatorSelection: { userVerification: string; }; + timeout: number; + attestation: string; +} + +@Component({ + selector: 'app-auth-passwordless', + templateUrl: './auth-passwordless.component.html', + styleUrls: ['./auth-passwordless.component.scss'], +}) +export class AuthPasswordlessComponent implements OnInit, OnDestroy { + public displayedColumns: string[] = ['name', 'state', 'actions']; + private loadingSubject: BehaviorSubject = new BehaviorSubject(false); + public loading$: Observable = this.loadingSubject.asObservable(); + + @ViewChild(MatTable) public table!: MatTable; + @ViewChild(MatSort) public sort!: MatSort; + public dataSource!: MatTableDataSource; + + public MFAState: any = MFAState; + public error: string = ''; + + constructor(private service: GrpcAuthService, + private toast: ToastService, + private dialog: MatDialog) { } + + public ngOnInit(): void { + this.getPasswordless(); + } + + public ngOnDestroy(): void { + this.loadingSubject.complete(); + } + + public addPasswordless(): void { + this.service.AddMyPasswordless().then((u2fresp) => { + const webauthn: WebAuthNResponse.AsObject = u2fresp.toObject(); + const credOptions: CredentialCreationOptions = JSON.parse(atob(webauthn.publicKey as string)); + + if (credOptions.publicKey?.challenge) { + credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any); + credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any); + const dialogRef = this.dialog.open(DialogU2FComponent, { + width: '400px', + data: { + credOptions, + type: U2FComponentDestination.PASSWORDLESS, + }, + }); + + dialogRef.afterClosed().subscribe(done => { + if (done) { + this.getPasswordless(); + } else { + this.getPasswordless(); + } + }); + } + + }, error => { + this.toast.showError(error); + }); + } + + public getPasswordless(): void { + this.service.GetMyPasswordless().then(passwordless => { + this.dataSource = new MatTableDataSource(passwordless.toObject().tokensList); + this.dataSource.sort = this.sort; + }).catch(error => { + this.error = error.message; + }); + } + + public deletePasswordless(id?: string): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'USER.PASSWORDLESS.DIALOG.DELETE_TITLE', + descriptionKey: 'USER.PASSWORDLESS.DIALOG.DELETE_DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp && id) { + this.service.RemoveMyPasswordless(id).then(() => { + this.toast.showInfo('USER.TOAST.PASSWORDLESSREMOVED', true); + this.getPasswordless(); + }).catch(error => { + this.toast.showError(error); + }); + } + }); + } +} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html index 4f65a79667..e28affdae5 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html @@ -51,6 +51,8 @@ + + {{ 'USER.MFA.ATTRIBUTE' | translate }} - - {{ mfa.attr }} + + {{ mfa?.attribute }} @@ -43,7 +43,7 @@ matTooltip="{{'ACTIONS.NEW' | translate}}"> {{'USER.MFA.OTP' | translate}} - - - + \ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html index 37056e74fe..81ded0f7ef 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html @@ -5,7 +5,7 @@
{{data.labelKey | translate }} - +
diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 41254071f1..7f12d7b93f 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -909,6 +909,7 @@ "DESCRIPTION":"Sie können vordefinierte oder selbsterstellten Provider auswählen", "SELECTIDPS":"Identity Provider" }, + "PASSWORDLESS":"Passwordloser Login", "PASSWORDLESSTYPE": { "0":"Nicht erlaubt", "1":"Erlaubt" diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index dd79f04a8a..ee90a3b6c8 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -909,6 +909,7 @@ "DESCRIPTION":"You can select predefined or selfcreated providers for authentication.", "SELECTIDPS":"Identity providers" }, + "PASSWORDLESS":"Passwordless Login", "PASSWORDLESSTYPE": { "0":"Not allowed", "1":"Allowed" diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 0c456b1b5e..bc38d00575 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -643,7 +643,7 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user return &model.InitUserStep{PasswordSet: user.PasswordSet} } - if user.IsPasswordlessReady() { + if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() { if !checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { return &model.PasswordlessStep{} } @@ -815,9 +815,8 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve case es_model.UserRemoved: return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dG2fe", "Errors.User.NotActive") } - if err := sessionCopy.AppendEvent(event); err != nil { - return user_view_model.UserSessionToModel(&sessionCopy), nil - } + err := sessionCopy.AppendEvent(event) + logging.Log("EVENT-qbhj3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("error appending event") } return user_view_model.UserSessionToModel(&sessionCopy), nil } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index 32883f97f7..67fef8b588 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -430,7 +430,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { userEventProvider: &mockEventUser{}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false}, []model.NextStep{&model.PasswordStep{}}, nil, }, @@ -475,7 +475,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, MultiFactorCheckLifeTime: 10 * time.Hour, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{PasswordlessType: iam_model.PasswordlessTypeAllowed}}, false}, []model.NextStep{&model.PasswordlessStep{}}, nil, }, @@ -500,7 +500,8 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { args{&model.AuthRequest{ UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{ - MultiFactors: []iam_model.MultiFactorType{iam_model.MultiFactorTypeU2FWithPIN}, + PasswordlessType: iam_model.PasswordlessTypeAllowed, + MultiFactors: []iam_model.MultiFactorType{iam_model.MultiFactorTypeU2FWithPIN}, }, }, false}, []model.NextStep{&model.VerifyEMailStep{}}, @@ -514,7 +515,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { userEventProvider: &mockEventUser{}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false}, []model.NextStep{&model.InitPasswordStep{}}, nil, }, @@ -578,7 +579,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, PasswordCheckLifeTime: 10 * 24 * time.Hour, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false}, []model.NextStep{&model.PasswordStep{}}, nil, }, @@ -887,6 +888,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { args{ &model.AuthRequest{ UserID: "UserID", + LoginPolicy: &iam_model.LoginPolicyView{}, SelectedIDPConfigID: "IDPConfigID", LinkingUsers: []*model.ExternalUser{{IDPConfigID: "IDPConfigID", ExternalUserID: "UserID", DisplayName: "DisplayName"}}, }, false}, diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index 1421b97929..d4e96c32e3 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -303,11 +303,26 @@ func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error { } func (repo *UserRepo) AddMFAU2F(ctx context.Context, userID string) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddU2F(ctx, userID, true) + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddU2F(ctx, userID, accountName, true) } func (repo *UserRepo) AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddU2F(ctx, authz.GetCtxData(ctx).UserID, false) + userID := authz.GetCtxData(ctx).UserID + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-Ghwl1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddU2F(ctx, userID, accountName, false) } func (repo *UserRepo) VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error { @@ -331,7 +346,14 @@ func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*mo } func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddPasswordless(ctx, userID, true) + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-Vj2k1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddPasswordless(ctx, userID, accountName, true) } func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) { @@ -339,7 +361,15 @@ func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNT } func (repo *UserRepo) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, false) + userID := authz.GetCtxData(ctx).UserID + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-AEq21").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, accountName, false) } func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error { diff --git a/internal/iam/model/iam.go b/internal/iam/model/iam.go index f80ea3b400..41ba7a6509 100644 --- a/internal/iam/model/iam.go +++ b/internal/iam/model/iam.go @@ -15,6 +15,7 @@ const ( Step6 Step7 Step8 + Step9 //StepCount marks the the length of possible steps (StepCount-1 == last possible step) StepCount ) diff --git a/internal/iam/repository/eventsourcing/eventstore.go b/internal/iam/repository/eventsourcing/eventstore.go index e1ed4d2d26..cad7dc09c0 100644 --- a/internal/iam/repository/eventsourcing/eventstore.go +++ b/internal/iam/repository/eventsourcing/eventstore.go @@ -525,20 +525,31 @@ func (es *IAMEventstore) AddLoginPolicy(ctx context.Context, policy *iam_model.L return model.LoginPolicyToModel(repoIam.DefaultLoginPolicy), nil } -func (es *IAMEventstore) ChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) { +func (es *IAMEventstore) PrepareChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*model.IAM, *models.Aggregate, error) { if policy == nil || !policy.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-3M0so", "Errors.IAM.LoginPolicyInvalid") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-3M0so", "Errors.IAM.LoginPolicyInvalid") } iam, err := es.IAMByID(ctx, policy.AggregateID) if err != nil { - return nil, err + return nil, nil, err } repoIam := model.IAMFromModel(iam) repoLoginPolicy := model.LoginPolicyFromModel(policy) - addAggregate := LoginPolicyChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoLoginPolicy) - err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate) + changeAgg, err := LoginPolicyChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoLoginPolicy)(ctx) + if err != nil { + return nil, nil, err + } + return repoIam, changeAgg, nil +} + +func (es *IAMEventstore) ChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) { + repoIam, changeAggregate, err := es.PrepareChangeLoginPolicy(ctx, policy) + if err != nil { + return nil, err + } + err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIam.AppendEvents, changeAggregate) if err != nil { return nil, err } @@ -665,27 +676,38 @@ func (es *IAMEventstore) RemoveSecondFactorFromLoginPolicy(ctx context.Context, return nil } -func (es *IAMEventstore) AddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (iam_model.MultiFactorType, error) { +func (es *IAMEventstore) PrepareAddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (*model.IAM, *models.Aggregate, error) { if mfa == iam_model.MultiFactorTypeUnspecified { - return 0, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2Dh7J", "Errors.IAM.LoginPolicy.MFA.Unspecified") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2Dh7J", "Errors.IAM.LoginPolicy.MFA.Unspecified") } iam, err := es.IAMByID(ctx, aggregateID) if err != nil { - return 0, err + return nil, nil, err } if _, m := iam.DefaultLoginPolicy.GetMultiFactor(mfa); m != 0 { - return 0, caos_errs.ThrowAlreadyExists(nil, "EVENT-4Rk09", "Errors.IAM.LoginPolicy.MFA.AlreadyExists") + return nil, nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-4Rk09", "Errors.IAM.LoginPolicy.MFA.AlreadyExists") } repoIam := model.IAMFromModel(iam) repoMFA := model.MultiFactorFromModel(mfa) - addAggregate := LoginPolicyMultiFactorAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMFA) - err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate) + addAggregate, err := LoginPolicyMultiFactorAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMFA)(ctx) + if err != nil { + return nil, nil, err + } + return repoIam, addAggregate, nil +} + +func (es *IAMEventstore) AddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (iam_model.MultiFactorType, error) { + repoIAM, addAggregate, err := es.PrepareAddMultiFactorToLoginPolicy(ctx, aggregateID, mfa) if err != nil { return 0, err } - es.iamCache.cacheIAM(repoIam) - if _, m := model.GetMFA(repoIam.DefaultLoginPolicy.MultiFactors, int32(mfa)); m != 0 { + err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIAM.AppendEvents, addAggregate) + if err != nil { + return 0, err + } + es.iamCache.cacheIAM(repoIAM) + if _, m := model.GetMFA(repoIAM.DefaultLoginPolicy.MultiFactors, int32(mfa)); m != 0 { return iam_model.MultiFactorType(m), nil } return 0, caos_errs.ThrowInternal(nil, "EVENT-5N9so", "Errors.Internal") diff --git a/internal/setup/config.go b/internal/setup/config.go index 93e0d4926b..c4e3149a4c 100644 --- a/internal/setup/config.go +++ b/internal/setup/config.go @@ -14,6 +14,7 @@ type IAMSetUp struct { Step6 *Step6 Step7 *Step7 Step8 *Step8 + Step9 *Step9 } func (setup *IAMSetUp) steps(currentDone iam_model.Step) ([]step, error) { @@ -29,6 +30,7 @@ func (setup *IAMSetUp) steps(currentDone iam_model.Step) ([]step, error) { setup.Step6, setup.Step7, setup.Step8, + setup.Step9, } { if step.step() <= currentDone { continue diff --git a/internal/setup/step9.go b/internal/setup/step9.go new file mode 100644 index 0000000000..2733dbb51a --- /dev/null +++ b/internal/setup/step9.go @@ -0,0 +1,74 @@ +package setup + +import ( + "context" + + "github.com/caos/logging" + + "github.com/caos/zitadel/internal/eventstore/models" + es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" + iam_model "github.com/caos/zitadel/internal/iam/model" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" +) + +type Step9 struct { + Passwordless bool + + setup *Setup +} + +func (step *Step9) isNil() bool { + return step == nil +} + +func (step *Step9) step() iam_model.Step { + return iam_model.Step9 +} + +func (step *Step9) init(setup *Setup) { + step.setup = setup +} + +func (step *Step9) execute(ctx context.Context) (*iam_model.IAM, error) { + if !step.Passwordless { + return step.setup.IamEvents.IAMByID(ctx, step.setup.iamID) + } + iam, agg, err := step.setPasswordlessAllowedInPolicy(ctx) + if err != nil { + logging.Log("SETUP-Gdbjq").WithField("step", step.step()).WithError(err).Error("unable to finish setup (add default mfa to login policy)") + return nil, err + } + iam, agg2, err := step.addMFAToPolicy(ctx) + if err != nil { + logging.Log("SETUP-Gdbjq").WithField("step", step.step()).WithError(err).Error("unable to finish setup (add default mfa to login policy)") + return nil, err + } + agg.Events = append(agg.Events, agg2.Events...) + iam, agg, push, err := step.setup.IamEvents.PrepareSetupDone(ctx, iam, agg, step.step()) + if err != nil { + logging.Log("SETUP-Cnf21").WithField("step", step.step()).WithError(err).Error("unable to finish setup (prepare setup done)") + return nil, err + } + err = es_sdk.PushAggregates(ctx, push, iam.AppendEvents, agg) + if err != nil { + logging.Log("SETUP-NFq21").WithField("step", step.step()).WithError(err).Error("unable to finish setup") + return nil, err + } + return iam_es_model.IAMToModel(iam), nil +} + +func (step *Step9) setPasswordlessAllowedInPolicy(ctx context.Context) (*iam_es_model.IAM, *models.Aggregate, error) { + logging.Log("SETUP-DAd1h").Info("enabling passwordless in loginPolicy") + iam, err := step.setup.IamEvents.IAMByID(ctx, step.setup.iamID) + if err != nil { + return nil, nil, err + } + iam.DefaultLoginPolicy.AggregateID = step.setup.iamID + iam.DefaultLoginPolicy.PasswordlessType = iam_model.PasswordlessTypeAllowed + return step.setup.IamEvents.PrepareChangeLoginPolicy(ctx, iam.DefaultLoginPolicy) +} + +func (step *Step9) addMFAToPolicy(ctx context.Context) (*iam_es_model.IAM, *models.Aggregate, error) { + logging.Log("SETUP-DAd1h").Info("adding MFA to loginPolicy") + return step.setup.IamEvents.PrepareAddMultiFactorToLoginPolicy(ctx, step.setup.iamID, iam_model.MultiFactorTypeU2FWithPIN) +} diff --git a/internal/user/repository/eventsourcing/eventstore.go b/internal/user/repository/eventsourcing/eventstore.go index 7e5191d0db..acbf78048b 100644 --- a/internal/user/repository/eventsourcing/eventstore.go +++ b/internal/user/repository/eventsourcing/eventstore.go @@ -1302,12 +1302,12 @@ func (es *UserEventstore) verifyMFAOTP(otp *usr_model.OTP, code string) error { return nil } -func (es *UserEventstore) AddU2F(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) { +func (es *UserEventstore) AddU2F(ctx context.Context, userID string, accountName string, isLoginUI bool) (*usr_model.WebAuthNToken, error) { user, err := es.HumanByID(ctx, userID) if err != nil { return nil, err } - webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementDiscouraged, isLoginUI, user.U2FTokens...) + webAuthN, err := es.webauthn.BeginRegistration(user, accountName, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementDiscouraged, isLoginUI, user.U2FTokens...) if err != nil { return nil, err } @@ -1418,12 +1418,12 @@ func (es *UserEventstore) GetPasswordless(ctx context.Context, userID string) ([ return user.PasswordlessTokens, nil } -func (es *UserEventstore) AddPasswordless(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) { +func (es *UserEventstore) AddPasswordless(ctx context.Context, userID, accountName string, isLoginUI bool) (*usr_model.WebAuthNToken, error) { user, err := es.HumanByID(ctx, userID) if err != nil { return nil, err } - webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementRequired, isLoginUI, user.PasswordlessTokens...) + webAuthN, err := es.webauthn.BeginRegistration(user, accountName, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementRequired, isLoginUI, user.PasswordlessTokens...) if err != nil { return nil, err } diff --git a/internal/user/repository/view/model/user_session.go b/internal/user/repository/view/model/user_session.go index 514c829cf6..b64fb1b303 100644 --- a/internal/user/repository/view/model/user_session.go +++ b/internal/user/repository/view/model/user_session.go @@ -155,8 +155,12 @@ func (v *UserSessionView) AppendEvent(event *models.Event) error { es_model.HumanSignedOut, es_model.UserLocked, es_model.UserDeactivated: + v.PasswordlessVerification = time.Time{} v.PasswordVerification = time.Time{} v.SecondFactorVerification = time.Time{} + v.SecondFactorVerificationType = int32(req_model.MFALevelNotSetUp) + v.MultiFactorVerification = time.Time{} + v.MultiFactorVerificationType = int32(req_model.MFALevelNotSetUp) v.ExternalLoginVerification = time.Time{} v.State = int32(req_model.UserSessionStateTerminated) case es_model.HumanExternalIDPRemoved, es_model.HumanExternalIDPCascadeRemoved: diff --git a/internal/webauthn/webauthn.go b/internal/webauthn/webauthn.go index 8c1fa0093f..b003b3d5f6 100644 --- a/internal/webauthn/webauthn.go +++ b/internal/webauthn/webauthn.go @@ -42,6 +42,7 @@ func StartServer(sd systemdefaults.WebAuthN) (*WebAuthN, error) { type webUser struct { *usr_model.User + accountName string credentials []webauthn.Credential } @@ -50,7 +51,10 @@ func (u *webUser) WebAuthnID() []byte { } func (u *webUser) WebAuthnName() string { - return u.PreferredLoginName + if u.accountName != "" { + return u.accountName + } + return u.UserName } func (u *webUser) WebAuthnDisplayName() string { @@ -65,7 +69,7 @@ func (u *webUser) WebAuthnCredentials() []webauthn.Credential { return u.credentials } -func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.AuthenticatorAttachment, userVerification usr_model.UserVerificationRequirement, isLoginUI bool, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNToken, error) { +func (w *WebAuthN) BeginRegistration(user *usr_model.User, accountName string, authType usr_model.AuthenticatorAttachment, userVerification usr_model.UserVerificationRequirement, isLoginUI bool, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNToken, error) { creds := WebAuthNsToCredentials(webAuthNs) existing := make([]protocol.CredentialDescriptor, len(creds)) for i, cred := range creds { @@ -77,6 +81,7 @@ func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.Au credentialOptions, sessionData, err := w.web(isLoginUI).BeginRegistration( &webUser{ User: user, + accountName: accountName, credentials: creds, }, webauthn.WithAuthenticatorSelection(protocol.AuthenticatorSelection{ From 40ced9154e8b355ca1bc89212e3c4ab7298df0c2 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 18 Dec 2020 10:21:14 +0100 Subject: [PATCH 13/22] fix: bind type to u2f dialog component (#1119) --- .../auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts index 2926ce8e22..972d7172ae 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts @@ -10,7 +10,7 @@ import { ToastService } from 'src/app/services/toast.service'; import { _base64ToArrayBuffer } from '../../u2f-util'; import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component'; -import { DialogU2FComponent } from '../dialog-u2f/dialog-u2f.component'; +import { DialogU2FComponent, U2FComponentDestination } from '../dialog-u2f/dialog-u2f.component'; export interface WebAuthNOptions { challenge: string; @@ -93,6 +93,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { width: '400px', data: { credOptions, + type: U2FComponentDestination.MFA, }, }); From 410a53f15b029e6b5e1c8d80dd6ee2db4a3f727f Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 18 Dec 2020 13:42:21 +0100 Subject: [PATCH 14/22] fix: enable login with password when passwordless set up (#1120) * fix: enable login with password when passwordless set up * enable only it allowed --- .../eventsourcing/eventstore/auth_request.go | 22 ++++++++------ .../eventstore/auth_request_test.go | 29 +++++++++++++++++++ .../handler/passwordless_login_handler.go | 29 ++++++++++++++++--- internal/ui/login/static/i18n/de.yaml | 1 + internal/ui/login/static/i18n/en.yaml | 1 + .../login/static/templates/passwordless.html | 3 ++ 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index bc38d00575..92ef6aada6 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -643,24 +643,28 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user return &model.InitUserStep{PasswordSet: user.PasswordSet} } + var step model.NextStep if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() { - if !checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { - return &model.PasswordlessStep{} + if checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { + request.AuthTime = userSession.PasswordlessVerification + return nil } - request.AuthTime = userSession.PasswordlessVerification - return nil + step = &model.PasswordlessStep{} } if !user.PasswordSet { return &model.InitPasswordStep{} } - if !checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) { - return &model.PasswordStep{} + if checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) { + request.PasswordVerified = true + request.AuthTime = userSession.PasswordVerification + return nil } - request.PasswordVerified = true - request.AuthTime = userSession.PasswordVerification - return nil + if step != nil { + return step + } + return &model.PasswordStep{} } func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *model.AuthRequest, user *user_model.UserView) (model.NextStep, bool, error) { diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index 67fef8b588..f6085c1ebf 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -611,6 +611,35 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { []model.NextStep{&model.RedirectToCallbackStep{}}, nil, }, + { + "password verified, passwordless set up, mfa not verified, mfa check step", + fields{ + userSessionViewProvider: &mockViewUserSession{ + PasswordVerification: time.Now().UTC().Add(-5 * time.Minute), + }, + userViewProvider: &mockViewUser{ + PasswordSet: true, + PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}}, + OTPState: int32(user_model.MFAStateReady), + MFAMaxSetUp: int32(model.MFALevelMultiFactor), + }, + userEventProvider: &mockEventUser{}, + orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, + PasswordCheckLifeTime: 10 * 24 * time.Hour, + SecondFactorCheckLifeTime: 18 * time.Hour, + }, + args{ + &model.AuthRequest{ + UserID: "UserID", + LoginPolicy: &iam_model.LoginPolicyView{ + SecondFactors: []iam_model.SecondFactorType{iam_model.SecondFactorTypeOTP}, + }, + }, false}, + []model.NextStep{&model.MFAVerificationStep{ + MFAProviders: []model.MFAType{model.MFATypeOTP}, + }}, + nil, + }, { "mfa not verified, mfa check step", fields{ diff --git a/internal/ui/login/handler/passwordless_login_handler.go b/internal/ui/login/handler/passwordless_login_handler.go index ee0ae68579..7523e3d1f6 100644 --- a/internal/ui/login/handler/passwordless_login_handler.go +++ b/internal/ui/login/handler/passwordless_login_handler.go @@ -13,6 +13,16 @@ const ( tmplPasswordlessVerification = "passwordlessverification" ) +type passwordlessData struct { + webAuthNData + PasswordLogin bool +} + +type passwordlessFormData struct { + webAuthNFormData + PasswordLogin bool `schema:"passwordlogin"` +} + func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { var errType, errMessage, credentialData string var webAuthNLogin *user_model.WebAuthNLogin @@ -26,20 +36,31 @@ func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Re if webAuthNLogin != nil { credentialData = base64.RawURLEncoding.EncodeToString(webAuthNLogin.CredentialAssertionData) } - data := &webAuthNData{ - userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage), - CredentialCreationData: credentialData, + var passwordLogin bool + if authReq.LoginPolicy != nil { + passwordLogin = authReq.LoginPolicy.AllowUsernamePassword + } + data := &passwordlessData{ + webAuthNData{ + userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage), + CredentialCreationData: credentialData, + }, + passwordLogin, } l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplPasswordlessVerification], data, nil) } func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Request) { - formData := new(webAuthNFormData) + formData := new(passwordlessFormData) authReq, err := l.getAuthRequestAndParseData(r, formData) if err != nil { l.renderError(w, r, authReq, err) return } + if formData.PasswordLogin { + l.renderPassword(w, r, authReq, nil) + return + } credData, err := base64.URLEncoding.DecodeString(formData.CredentialData) if err != nil { l.renderPasswordlessVerification(w, r, authReq, err) diff --git a/internal/ui/login/static/i18n/de.yaml b/internal/ui/login/static/i18n/de.yaml index 8a4fe747fd..fc86aaa0ad 100644 --- a/internal/ui/login/static/i18n/de.yaml +++ b/internal/ui/login/static/i18n/de.yaml @@ -209,6 +209,7 @@ Actions: RegisterToken: Token registrieren ValidateToken: Token validieren Recreate: erneut erstellen + PasswordLogin: Mit Passwort anmelden Errors: Internal: Es ist ein interner Fehler aufgetreten diff --git a/internal/ui/login/static/i18n/en.yaml b/internal/ui/login/static/i18n/en.yaml index 67793d2045..73ae3d217a 100644 --- a/internal/ui/login/static/i18n/en.yaml +++ b/internal/ui/login/static/i18n/en.yaml @@ -209,6 +209,7 @@ Actions: RegisterToken: Register Token ValidateToken: Validate Token Recreate: recreate + PasswordLogin: Login with password Errors: Internal: An internal error occured diff --git a/internal/ui/login/static/templates/passwordless.html b/internal/ui/login/static/templates/passwordless.html index 120889be6d..70276780e2 100644 --- a/internal/ui/login/static/templates/passwordless.html +++ b/internal/ui/login/static/templates/passwordless.html @@ -26,6 +26,9 @@ {{ template "error-message" .}}
+ {{if .PasswordLogin}} + + {{end}} From e15fc0b92bc2f849017c05db7399300880fb2a17 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 18 Dec 2020 16:30:57 +0100 Subject: [PATCH 15/22] fix: remove u2f with pin from 2fa check (#1121) * fix: remove u2f with pin from 2fa check * show error message on mfa init verify --- internal/ui/login/handler/mfa_verify_handler.go | 3 +++ .../ui/login/static/templates/mfa_init_verify.html | 2 ++ internal/user/model/user_view.go | 13 ------------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/internal/ui/login/handler/mfa_verify_handler.go b/internal/ui/login/handler/mfa_verify_handler.go index 5e3b5c5a8f..3d284e01aa 100644 --- a/internal/ui/login/handler/mfa_verify_handler.go +++ b/internal/ui/login/handler/mfa_verify_handler.go @@ -60,6 +60,9 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request, case model.MFATypeOTP: data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeOTP) data.SelectedMFAProvider = model.MFATypeOTP + default: + l.renderError(w, r, authReq, err) + return } l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAVerify], data, nil) } diff --git a/internal/ui/login/static/templates/mfa_init_verify.html b/internal/ui/login/static/templates/mfa_init_verify.html index de9ff51e14..98a51e787c 100644 --- a/internal/ui/login/static/templates/mfa_init_verify.html +++ b/internal/ui/login/static/templates/mfa_init_verify.html @@ -35,6 +35,8 @@
{{end}} + {{ template "error-message" .}} +
diff --git a/internal/user/model/user_view.go b/internal/user/model/user_view.go index 24d3efeaf6..72ed0af935 100644 --- a/internal/user/model/user_view.go +++ b/internal/user/model/user_view.go @@ -163,19 +163,6 @@ func (u *UserView) MFATypesAllowed(level req_model.MFALevel, policy *iam_model.L } } //PLANNED: add sms - fallthrough - case req_model.MFALevelMultiFactor: - if policy.HasMultiFactors() { - for _, mfaType := range policy.MultiFactors { - switch mfaType { - case iam_model.MultiFactorTypeU2FWithPIN: - if u.IsPasswordlessReady() { - types = append(types, req_model.MFATypeU2FUserVerification) - } - } - } - } - //PLANNED: add token } return types, required } From dd5e4acd247df89a08e0c29a8b69eaddcd06d923 Mon Sep 17 00:00:00 2001 From: Silvan Date: Fri, 18 Dec 2020 16:47:45 +0100 Subject: [PATCH 16/22] fix(event handling): use internal pubsub for view update (#1118) * start sub * start implement subsciptions * start subscription * implementation for member done * admin done * fix: tests * extend handlers * prepary notification * no errors in adminapi * changed current sequence in all packages * ignore mocks * works * subscriptions as singleton * tests * refactor: rename function scope var --- .gitignore | 1 + cmd/zitadel/startup.yaml | 6 +- .../eventsourcing/eventstore/administrator.go | 5 +- .../eventsourcing/eventstore/iam.go | 9 +- .../eventsourcing/eventstore/org.go | 2 +- .../eventsourcing/handler/handler.go | 71 +++++++---- .../eventsourcing/handler/iam_member.go | 83 +++++++++---- .../eventsourcing/handler/idp_config.go | 64 +++++++--- .../eventsourcing/handler/idp_providers.go | 77 +++++++++--- .../eventsourcing/handler/label_policy.go | 60 ++++++--- .../eventsourcing/handler/login_policy.go | 51 ++++++-- .../repository/eventsourcing/handler/org.go | 48 ++++++-- .../eventsourcing/handler/org_iam_policy.go | 64 +++++++--- .../handler/password_age_policy.go | 52 ++++++-- .../handler/password_complexity_policy.go | 52 ++++++-- .../handler/password_lockout_policy.go | 52 ++++++-- .../repository/eventsourcing/handler/user.go | 64 ++++++++-- .../handler/user_external_idps.go | 65 ++++++++-- .../repository/eventsourcing/spooler/lock.go | 4 +- .../eventsourcing/view/external_idps.go | 26 ++-- .../eventsourcing/view/iam_member.go | 26 ++-- .../eventsourcing/view/idp_configs.go | 18 +-- .../eventsourcing/view/idp_providers.go | 22 ++-- .../eventsourcing/view/label_policies.go | 14 +-- .../eventsourcing/view/login_policies.go | 18 +-- .../repository/eventsourcing/view/org.go | 14 +-- .../eventsourcing/view/org_iam_policy.go | 18 +-- .../eventsourcing/view/password_age_policy.go | 18 +-- .../view/password_complexity_policy.go | 18 +-- .../view/password_lockout_policy.go | 18 +-- .../repository/eventsourcing/view/sequence.go | 21 ++-- .../repository/eventsourcing/view/user.go | 24 ++-- .../eventsourcing/eventstore/org.go | 2 +- .../eventsourcing/eventstore/user.go | 2 +- .../eventsourcing/eventstore/user_grant.go | 2 +- .../eventsourcing/handler/application.go | 55 +++++++-- .../eventsourcing/handler/handler.go | 84 +++++++++---- .../eventsourcing/handler/idp_config.go | 52 ++++++-- .../eventsourcing/handler/idp_providers.go | 63 ++++++++-- .../repository/eventsourcing/handler/key.go | 54 ++++++-- .../eventsourcing/handler/login_policy.go | 52 ++++++-- .../eventsourcing/handler/machine_keys.go | 74 ++++++++--- .../repository/eventsourcing/handler/org.go | 49 ++++++-- .../eventsourcing/handler/org_iam_policy.go | 73 ++++++++--- .../handler/password_complexity_policy.go | 73 ++++++++--- .../eventsourcing/handler/project_role.go | 77 +++++++++--- .../repository/eventsourcing/handler/token.go | 64 ++++++++-- .../repository/eventsourcing/handler/user.go | 84 +++++++++---- .../handler/user_external_idps.go | 65 ++++++++-- .../eventsourcing/handler/user_grant.go | 90 ++++++++++---- .../eventsourcing/handler/user_membership.go | 83 +++++++++---- .../eventsourcing/handler/user_session.go | 66 +++++++--- .../repository/eventsourcing/spooler/lock.go | 7 +- .../eventsourcing/view/application.go | 21 ++-- .../eventsourcing/view/external_idps.go | 26 ++-- .../eventsourcing/view/idp_configs.go | 18 +-- .../eventsourcing/view/idp_providers.go | 26 ++-- .../auth/repository/eventsourcing/view/key.go | 22 ++-- .../eventsourcing/view/login_policies.go | 18 +-- .../eventsourcing/view/machine_keys.go | 24 ++-- .../auth/repository/eventsourcing/view/org.go | 14 +-- .../eventsourcing/view/org_iam_policy.go | 18 +-- .../view/password_complexity_policy.go | 18 +-- .../eventsourcing/view/project_role.go | 20 +-- .../repository/eventsourcing/view/sequence.go | 17 ++- .../repository/eventsourcing/view/token.go | 34 +++--- .../repository/eventsourcing/view/user.go | 22 ++-- .../eventsourcing/view/user_grant.go | 22 ++-- .../eventsourcing/view/user_membership.go | 34 +++--- .../eventsourcing/view/user_session.go | 22 ++-- .../eventsourcing/handler/application.go | 50 ++++++-- .../eventsourcing/handler/handler.go | 28 +++-- .../repository/eventsourcing/handler/org.go | 49 ++++++-- .../eventsourcing/handler/user_grant.go | 76 +++++++++--- .../repository/eventsourcing/repository.go | 3 +- .../repository/eventsourcing/spooler/lock.go | 3 +- .../eventsourcing/view/application.go | 18 +-- .../repository/eventsourcing/view/org.go | 14 +-- .../repository/eventsourcing/view/sequence.go | 17 ++- .../repository/eventsourcing/view/token.go | 22 ++-- .../eventsourcing/view/user_grant.go | 18 +-- internal/eventstore/eventstore.go | 3 + internal/eventstore/mock/eventstore.mock.go | 19 +++ internal/eventstore/models/aggregate.go | 4 + internal/eventstore/models/search_query.go | 36 ++++-- .../eventstore/models/search_query_old.go | 6 + .../eventstore/models/search_query_test.go | 2 +- internal/eventstore/query/handler.go | 41 +++++++ internal/eventstore/spooler/spooler.go | 4 +- internal/eventstore/spooler/spooler_test.go | 16 ++- internal/eventstore/subscription.go | 73 +++++++++++ .../eventsourcing/model/login_policy.go | 1 + .../eventsourcing/model/org_iam_policy.go | 1 + .../model/password_age_policy.go | 1 + .../model/password_complexity_policy.go | 1 + .../model/password_lockout_policy.go | 1 + .../eventsourcing/eventstore/org.go | 8 +- .../eventsourcing/eventstore/project.go | 14 +-- .../eventsourcing/eventstore/user.go | 8 +- .../eventsourcing/eventstore/user_grant.go | 2 +- .../eventsourcing/handler/application.go | 60 +++++++-- .../eventsourcing/handler/handler.go | 105 ++++++++++------ .../eventsourcing/handler/idp_config.go | 60 +++++++-- .../eventsourcing/handler/idp_providers.go | 79 +++++++++--- .../eventsourcing/handler/label_policy.go | 53 ++++++-- .../eventsourcing/handler/login_policy.go | 55 +++++++-- .../eventsourcing/handler/machine_keys.go | 57 +++++++-- .../repository/eventsourcing/handler/org.go | 50 ++++++-- .../eventsourcing/handler/org_domain.go | 55 +++++++-- .../eventsourcing/handler/org_iam_policy.go | 55 +++++++-- .../eventsourcing/handler/org_member.go | 68 ++++++++--- .../handler/password_age_policy.go | 57 +++++++-- .../handler/password_complexity_policy.go | 55 +++++++-- .../handler/password_lockout_policy.go | 52 ++++++-- .../eventsourcing/handler/project.go | 50 ++++++-- .../eventsourcing/handler/project_grant.go | 67 +++++++--- .../handler/project_grant_member.go | 65 +++++++--- .../eventsourcing/handler/project_member.go | 64 ++++++++-- .../eventsourcing/handler/project_role.go | 56 +++++++-- .../repository/eventsourcing/handler/user.go | 75 +++++++++--- .../handler/user_external_idps.go | 84 +++++++++---- .../eventsourcing/handler/user_grant.go | 100 ++++++++++----- .../eventsourcing/handler/user_membership.go | 115 ++++++++++++------ .../repository/eventsourcing/spooler/lock.go | 3 +- .../eventsourcing/view/application.go | 22 ++-- .../eventsourcing/view/external_idps.go | 26 ++-- .../eventsourcing/view/idp_configs.go | 18 +-- .../eventsourcing/view/idp_providers.go | 26 ++-- .../eventsourcing/view/label_policies.go | 18 +-- .../eventsourcing/view/login_policies.go | 18 +-- .../eventsourcing/view/machine_keys.go | 24 ++-- .../repository/eventsourcing/view/org.go | 14 +-- .../eventsourcing/view/org_domain.go | 24 ++-- .../eventsourcing/view/org_iam_policy.go | 18 +-- .../eventsourcing/view/org_member.go | 26 ++-- .../eventsourcing/view/password_age_policy.go | 18 +-- .../view/password_complexity_policy.go | 18 +-- .../view/password_lockout_policy.go | 18 +-- .../repository/eventsourcing/view/project.go | 18 +-- .../eventsourcing/view/project_grant.go | 22 ++-- .../view/project_grant_member.go | 22 ++-- .../eventsourcing/view/project_member.go | 22 ++-- .../eventsourcing/view/project_role.go | 18 +-- .../repository/eventsourcing/view/sequence.go | 17 ++- .../repository/eventsourcing/view/user.go | 24 ++-- .../eventsourcing/view/user_grant.go | 22 ++-- .../eventsourcing/view/user_membership.go | 34 +++--- .../eventsourcing/handler/handler.go | 44 ++++--- .../eventsourcing/handler/notification.go | 81 +++++++++--- .../eventsourcing/handler/notify_user.go | 92 ++++++++++---- .../repository/eventsourcing/spooler/lock.go | 3 +- .../eventsourcing/view/notification.go | 10 +- .../eventsourcing/view/notify_user.go | 20 +-- .../repository/eventsourcing/view/sequence.go | 17 ++- .../org/repository/view/org_member_view.go | 2 +- internal/user/repository/view/model/token.go | 3 +- internal/view/model/view.go | 1 + internal/view/repository/sequence.go | 74 +++++++++-- .../V1.26__current_sequence_table.sql | 21 ++++ .../management/mock/management.proto.mock.go | 40 ++++++ 160 files changed, 4010 insertions(+), 1596 deletions(-) create mode 100644 internal/eventstore/subscription.go create mode 100644 migrations/cockroach/V1.26__current_sequence_table.sql diff --git a/.gitignore b/.gitignore index 308614e2b8..8e5b5e4e02 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ tmp/ console/src/app/proto/generated/ pkg/grpc/*/*.pb.* +pkg/grpc/*/mock/*.mock.go pkg/grpc/*/*.swagger.json \ No newline at end of file diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 2d81a82c51..9f0f011d60 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -294,8 +294,4 @@ Notification: ConcurrentWorkers: 1 BulkLimit: 100 FailureCountUntilSkip: 5 - Handlers: - Notification: - MinimumCycleDuration: 5s - User: - MinimumCycleDuration: 5s \ No newline at end of file + Handlers: \ No newline at end of file diff --git a/internal/admin/repository/eventsourcing/eventstore/administrator.go b/internal/admin/repository/eventsourcing/eventstore/administrator.go index 52ab002cac..b36717481e 100644 --- a/internal/admin/repository/eventsourcing/eventstore/administrator.go +++ b/internal/admin/repository/eventsourcing/eventstore/administrator.go @@ -2,10 +2,11 @@ package eventstore import ( "context" + "time" + "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" view_model "github.com/caos/zitadel/internal/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) var dbList = []string{"management", "auth", "authz", "adminapi", "notification"} @@ -47,7 +48,7 @@ func (repo *AdministratorRepo) GetViews() ([]*view_model.View, error) { } func (repo *AdministratorRepo) GetSpoolerDiv(database, view string) int64 { - sequence, err := repo.View.GetCurrentSequence(database, view) + sequence, err := repo.View.GetCurrentSequence(database, view, "") if err != nil { return 0 diff --git a/internal/admin/repository/eventsourcing/eventstore/iam.go b/internal/admin/repository/eventsourcing/eventstore/iam.go index 617cca2636..a08aad4b34 100644 --- a/internal/admin/repository/eventsourcing/eventstore/iam.go +++ b/internal/admin/repository/eventsourcing/eventstore/iam.go @@ -2,9 +2,10 @@ package eventstore import ( "context" - caos_errs "github.com/caos/zitadel/internal/errors" "strings" + caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/logging" admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" "github.com/caos/zitadel/internal/config/systemdefaults" @@ -54,7 +55,7 @@ func (repo *IAMRepository) RemoveIAMMember(ctx context.Context, userID string) e func (repo *IAMRepository) SearchIAMMembers(ctx context.Context, request *iam_model.IAMMemberSearchRequest) (*iam_model.IAMMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestIAMMemberSequence() + sequence, err := repo.View.GetLatestIAMMemberSequence("") logging.Log("EVENT-Slkci").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest iam sequence") members, count, err := repo.View.SearchIAMMembers(request) if err != nil { @@ -156,7 +157,7 @@ func (repo *IAMRepository) ChangeOidcIDPConfig(ctx context.Context, oidcConfig * func (repo *IAMRepository) SearchIDPConfigs(ctx context.Context, request *iam_model.IDPConfigSearchRequest) (*iam_model.IDPConfigSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestIDPConfigSequence() + sequence, err := repo.View.GetLatestIDPConfigSequence("") logging.Log("EVENT-Dk8si").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest idp config sequence") idps, count, err := repo.View.SearchIDPConfigs(request) if err != nil { @@ -248,7 +249,7 @@ func (repo *IAMRepository) ChangeDefaultLoginPolicy(ctx context.Context, policy func (repo *IAMRepository) SearchDefaultIDPProviders(ctx context.Context, request *iam_model.IDPProviderSearchRequest) (*iam_model.IDPProviderSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.AppendAggregateIDQuery(repo.SystemDefaults.IamID) - sequence, err := repo.View.GetLatestIDPProviderSequence() + sequence, err := repo.View.GetLatestIDPProviderSequence("") logging.Log("EVENT-Tuiks").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest iam sequence") providers, count, err := repo.View.SearchIDPProviders(request) if err != nil { diff --git a/internal/admin/repository/eventsourcing/eventstore/org.go b/internal/admin/repository/eventsourcing/eventstore/org.go index 78400eae29..59718f45be 100644 --- a/internal/admin/repository/eventsourcing/eventstore/org.go +++ b/internal/admin/repository/eventsourcing/eventstore/org.go @@ -87,7 +87,7 @@ func (repo *OrgRepo) OrgByID(ctx context.Context, id string) (*org_model.Org, er func (repo *OrgRepo) SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) { query.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestOrgSequence() + sequence, err := repo.View.GetLatestOrgSequence("") logging.Log("EVENT-LXo9w").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest iam sequence") orgs, count, err := repo.View.SearchOrgs(query) if err != nil { diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go index 25f3b69223..60dcb9dac1 100644 --- a/internal/admin/repository/eventsourcing/handler/handler.go +++ b/internal/admin/repository/eventsourcing/handler/handler.go @@ -3,13 +3,12 @@ package handler import ( "time" - "github.com/caos/zitadel/internal/config/systemdefaults" - iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" + "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/query" + iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" ) @@ -25,6 +24,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -33,31 +38,55 @@ type EventstoreRepos struct { OrgEvents *org_event.OrgEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { return []query.Handler{ - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, - &IamMember{handler: handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount}, - userEvents: repos.UserEvents}, - &IDPConfig{handler: handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount}}, - &LabelPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount}}, - &LoginPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount}}, - &IDPProvider{handler: handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount}, - systemDefaults: defaults, iamEvents: repos.IamEvents, orgEvents: repos.OrgEvents}, - &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - eventstore: eventstore, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, systemDefaults: defaults}, - &PasswordComplexityPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount}}, - &PasswordAgePolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount}}, - &PasswordLockoutPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount}}, - &OrgIAMPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount}}, - &ExternalIDP{handler: handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount}, - orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, systemDefaults: defaults}, + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), + newIAMMember( + handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount, es}, + repos.UserEvents), + newIDPConfig( + handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), + newLabelPolicy( + handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}), + newLoginPolicy( + handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount, es}), + newIDPProvider( + handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, + defaults, + repos.IamEvents, + repos.OrgEvents), + newUser( + handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IamEvents, + defaults), + newPasswordComplexityPolicy( + 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}), + newOrgIAMPolicy( + handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), + newExternalIDP( + handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount, es}, + defaults, + repos.IamEvents, + repos.OrgEvents), + } +} + +func subscribe(es eventstore.Eventstore, handlers []query.Handler) { + for _, handler := range handlers { + es.Subscribe(handler.AggregateTypes()...) } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } diff --git a/internal/admin/repository/eventsourcing/handler/iam_member.go b/internal/admin/repository/eventsourcing/handler/iam_member.go index 53d628b562..19db852e42 100644 --- a/internal/admin/repository/eventsourcing/handler/iam_member.go +++ b/internal/admin/repository/eventsourcing/handler/iam_member.go @@ -4,9 +4,9 @@ import ( "context" "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" @@ -15,30 +15,63 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type IamMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( iamMemberTable = "adminapi.iam_members" ) -func (m *IamMember) ViewModel() string { +type IAMMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newIAMMember(handler handler, userEvents *usr_event.UserEventstore) *IAMMember { + iamMember := &IAMMember{ + handler: handler, + userEvents: userEvents, + } + + iamMember.subscribe() + + return iamMember +} + +func (m *IAMMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + +func (m *IAMMember) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := m.view.GetLatestIAMMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *IAMMember) ViewModel() string { return iamMemberTable } -func (m *IamMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestIAMMemberSequence() +func (m *IAMMember) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate, usr_es_model.UserAggregate} +} + +func (m *IAMMember) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestIAMMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *IamMember) Reduce(event *models.Event) (err error) { +func (m *IAMMember) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: err = m.processIamMember(event) @@ -48,7 +81,7 @@ func (m *IamMember) Reduce(event *models.Event) (err error) { return err } -func (m *IamMember) processIamMember(event *models.Event) (err error) { +func (m *IAMMember) processIamMember(event *es_models.Event) (err error) { member := new(iam_model.IAMMemberView) switch event.Type { case model.IAMMemberAdded: @@ -72,17 +105,17 @@ func (m *IamMember) processIamMember(event *models.Event) (err error) { if err != nil { return err } - return m.view.DeleteIAMMember(event.AggregateID, member.UserID, event.Sequence, event.CreationDate) + return m.view.DeleteIAMMember(event.AggregateID, member.UserID, event) default: - return m.view.ProcessedIAMMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIAMMemberSequence(event) } if err != nil { return err } - return m.view.PutIAMMember(member, member.Sequence, event.CreationDate) + return m.view.PutIAMMember(member, event) } -func (m *IamMember) processUser(event *models.Event) (err error) { +func (m *IAMMember) processUser(event *es_models.Event) (err error) { switch event.Type { case usr_es_model.UserProfileChanged, usr_es_model.UserEmailChanged, @@ -94,7 +127,7 @@ func (m *IamMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return m.view.ProcessedIAMMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIAMMemberSequence(event) } user, err := m.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -103,15 +136,15 @@ func (m *IamMember) processUser(event *models.Event) (err error) { for _, member := range members { m.fillUserData(member, user) } - return m.view.PutIAMMembers(members, event.Sequence, event.CreationDate) + return m.view.PutIAMMembers(members, event) case usr_es_model.UserRemoved: - return m.view.DeleteIAMMembersByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteIAMMembersByUserID(event.AggregateID, event) default: - return m.view.ProcessedIAMMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIAMMemberSequence(event) } } -func (m *IamMember) fillData(member *iam_model.IAMMemberView) (err error) { +func (m *IAMMember) fillData(member *iam_model.IAMMemberView) (err error) { user, err := m.userEvents.UserByID(context.Background(), member.UserID) if err != nil { return err @@ -120,7 +153,7 @@ func (m *IamMember) fillData(member *iam_model.IAMMemberView) (err error) { return nil } -func (m *IamMember) fillUserData(member *iam_model.IAMMemberView, user *usr_model.User) { +func (m *IAMMember) fillUserData(member *iam_model.IAMMemberView, user *usr_model.User) { member.UserName = user.UserName if user.Human != nil { member.FirstName = user.FirstName @@ -132,11 +165,11 @@ func (m *IamMember) fillUserData(member *iam_model.IAMMemberView, user *usr_mode member.DisplayName = user.Machine.Name } } -func (m *IamMember) OnError(event *models.Event, err error) error { +func (m *IAMMember) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Ld9ow", "id", event.AggregateID).WithError(err).Warn("something went wrong in iammember handler") return spooler.HandleError(event, err, m.view.GetLatestIAMMemberFailedEvent, m.view.ProcessedIAMMemberFailedEvent, m.view.ProcessedIAMMemberSequence, m.errorCountUntilSkip) } -func (m *IamMember) OnSuccess() error { +func (m *IAMMember) OnSuccess() error { return spooler.HandleSuccess(m.view.UpdateIAMMemberSpoolerRunTimestamp) } diff --git a/internal/admin/repository/eventsourcing/handler/idp_config.go b/internal/admin/repository/eventsourcing/handler/idp_config.go index be8d43d7dc..bf0dc97104 100644 --- a/internal/admin/repository/eventsourcing/handler/idp_config.go +++ b/internal/admin/repository/eventsourcing/handler/idp_config.go @@ -2,38 +2,70 @@ package handler import ( "github.com/caos/logging" - iam_model "github.com/caos/zitadel/internal/iam/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) -type IDPConfig struct { - handler -} - const ( idpConfigTable = "adminapi.idp_configs" ) +type IDPConfig struct { + handler + subscription *eventstore.Subscription +} + +func newIDPConfig(handler handler) *IDPConfig { + h := &IDPConfig{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (i *IDPConfig) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} + func (i *IDPConfig) ViewModel() string { return idpConfigTable } -func (i *IDPConfig) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPConfigSequence() +func (i *IDPConfig) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate} +} + +func (i *IDPConfig) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPConfigSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (i *IDPConfig) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := i.view.GetLatestIDPConfigSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (i *IDPConfig) Reduce(event *models.Event) (err error) { +func (i *IDPConfig) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: err = i.processIDPConfig(event) @@ -41,7 +73,7 @@ func (i *IDPConfig) Reduce(event *models.Event) (err error) { return err } -func (i *IDPConfig) processIDPConfig(event *models.Event) (err error) { +func (i *IDPConfig) processIDPConfig(event *es_models.Event) (err error) { idp := new(iam_view_model.IDPConfigView) switch event.Type { case model.IDPConfigAdded: @@ -63,17 +95,17 @@ func (i *IDPConfig) processIDPConfig(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteIDPConfig(idp.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPConfig(idp.IDPConfigID, event) default: - return i.view.ProcessedIDPConfigSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPConfigSequence(event) } if err != nil { return err } - return i.view.PutIDPConfig(idp, idp.Sequence, event.CreationDate) + return i.view.PutIDPConfig(idp, event) } -func (i *IDPConfig) OnError(event *models.Event, err error) error { +func (i *IDPConfig) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Mslo9", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp config handler") return spooler.HandleError(event, err, i.view.GetLatestIDPConfigFailedEvent, i.view.ProcessedIDPConfigFailedEvent, i.view.ProcessedIDPConfigSequence, i.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/idp_providers.go b/internal/admin/repository/eventsourcing/handler/idp_providers.go index 0e27ee11ec..06c44c4c0a 100644 --- a/internal/admin/repository/eventsourcing/handler/idp_providers.go +++ b/internal/admin/repository/eventsourcing/handler/idp_providers.go @@ -2,18 +2,23 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" - "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" - org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" + org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" +) + +const ( + idpProviderTable = "adminapi.idp_providers" ) type IDPProvider struct { @@ -21,27 +26,63 @@ type IDPProvider struct { systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_events.OrgEventstore + subscription *eventstore.Subscription } -const ( - idpProviderTable = "adminapi.idp_providers" -) +func newIDPProvider( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_events.OrgEventstore, +) *IDPProvider { + h := &IDPProvider{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (i *IDPProvider) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *IDPProvider) ViewModel() string { return idpProviderTable } -func (i *IDPProvider) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPProviderSequence() +func (i *IDPProvider) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *IDPProvider) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPProviderSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (i *IDPProvider) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := i.view.GetLatestIDPProviderSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (i *IDPProvider) Reduce(event *models.Event) (err error) { +func (i *IDPProvider) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate, org_es_model.OrgAggregate: err = i.processIdpProvider(event) @@ -49,7 +90,7 @@ func (i *IDPProvider) Reduce(event *models.Event) (err error) { return err } -func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { +func (i *IDPProvider) processIdpProvider(event *es_models.Event) (err error) { provider := new(iam_view_model.IDPProviderView) switch event.Type { case model.LoginPolicyIDPProviderAdded, org_es_model.LoginPolicyIDPProviderAdded: @@ -64,7 +105,7 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event) case model.IDPConfigChanged, org_es_model.IDPConfigChanged: esConfig := new(iam_view_model.IDPConfigView) providerType := iam_model.IDPProviderTypeSystem @@ -83,14 +124,14 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { for _, provider := range providers { i.fillConfigData(provider, config) } - return i.view.PutIDPProviders(event.Sequence, event.CreationDate, providers...) + return i.view.PutIDPProviders(event, providers...) default: - return i.view.ProcessedIDPProviderSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPProviderSequence(event) } if err != nil { return err } - return i.view.PutIDPProvider(provider, provider.Sequence, event.CreationDate) + return i.view.PutIDPProvider(provider, event) } func (i *IDPProvider) fillData(provider *iam_view_model.IDPProviderView) (err error) { @@ -114,7 +155,7 @@ func (i *IDPProvider) fillConfigData(provider *iam_view_model.IDPProviderView, c provider.IDPState = int32(config.State) } -func (i *IDPProvider) OnError(event *models.Event, err error) error { +func (i *IDPProvider) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Msj8c", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp provider handler") return spooler.HandleError(event, err, i.view.GetLatestIDPProviderFailedEvent, i.view.ProcessedIDPProviderFailedEvent, i.view.ProcessedIDPProviderSequence, i.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/label_policy.go b/internal/admin/repository/eventsourcing/handler/label_policy.go index 3f81c0060d..c1604fba69 100644 --- a/internal/admin/repository/eventsourcing/handler/label_policy.go +++ b/internal/admin/repository/eventsourcing/handler/label_policy.go @@ -2,37 +2,69 @@ package handler import ( "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) -type LabelPolicy struct { - handler -} - const ( labelPolicyTable = "adminapi.label_policies" ) +type LabelPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLabelPolicy(handler handler) *LabelPolicy { + h := &LabelPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *LabelPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *LabelPolicy) ViewModel() string { return labelPolicyTable } -func (p *LabelPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestLabelPolicySequence() +func (p *LabelPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate} +} + +func (p *LabelPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestLabelPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *LabelPolicy) Reduce(event *models.Event) (err error) { +func (p *LabelPolicy) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := p.view.GetLatestLabelPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *LabelPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: err = p.processLabelPolicy(event) @@ -40,7 +72,7 @@ func (p *LabelPolicy) Reduce(event *models.Event) (err error) { return err } -func (p *LabelPolicy) processLabelPolicy(event *models.Event) (err error) { +func (p *LabelPolicy) processLabelPolicy(event *es_models.Event) (err error) { policy := new(iam_model.LabelPolicyView) switch event.Type { case model.LabelPolicyAdded: @@ -52,15 +84,15 @@ func (p *LabelPolicy) processLabelPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) default: - return p.view.ProcessedLabelPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedLabelPolicySequence(event) } if err != nil { return err } - return p.view.PutLabelPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutLabelPolicy(policy, event) } -func (p *LabelPolicy) OnError(event *models.Event, err error) error { +func (p *LabelPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Wj8sf", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler") return spooler.HandleError(event, err, p.view.GetLatestLabelPolicyFailedEvent, p.view.ProcessedLabelPolicyFailedEvent, p.view.ProcessedLabelPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/login_policy.go b/internal/admin/repository/eventsourcing/handler/login_policy.go index c24100ca69..032f2a4349 100644 --- a/internal/admin/repository/eventsourcing/handler/login_policy.go +++ b/internal/admin/repository/eventsourcing/handler/login_policy.go @@ -2,36 +2,69 @@ package handler import ( "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) -type LoginPolicy struct { - handler -} - const ( loginPolicyTable = "adminapi.login_policies" ) +type LoginPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLoginPolicy(handler handler) *LoginPolicy { + h := &LoginPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *LoginPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *LoginPolicy) ViewModel() string { return loginPolicyTable } +func (p *LoginPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.IAMAggregate} +} + func (p *LoginPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestLoginPolicySequence() + sequence, err := p.view.GetLatestLoginPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } +func (p *LoginPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestLoginPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *LoginPolicy) Reduce(event *models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: @@ -56,12 +89,12 @@ func (p *LoginPolicy) processLoginPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) default: - return p.view.ProcessedLoginPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedLoginPolicySequence(event) } if err != nil { return err } - return p.view.PutLoginPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutLoginPolicy(policy, event) } func (p *LoginPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/org.go b/internal/admin/repository/eventsourcing/handler/org.go index eb64e60ea7..02ace32943 100644 --- a/internal/admin/repository/eventsourcing/handler/org.go +++ b/internal/admin/repository/eventsourcing/handler/org.go @@ -3,33 +3,67 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "adminapi.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (o *Org) subscribe() { + o.subscription = o.es.Subscribe(o.AggregateTypes()...) + go func() { + for event := range o.subscription.Events { + query.ReduceEvent(o, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (o *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } return eventsourcing.OrgQuery(sequence.CurrentSequence), nil } +func (o *Org) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) Reduce(event *es_models.Event) error { org := new(org_model.OrgView) @@ -53,10 +87,10 @@ func (o *Org) Reduce(event *es_models.Event) error { return err } default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/admin/repository/eventsourcing/handler/org_iam_policy.go b/internal/admin/repository/eventsourcing/handler/org_iam_policy.go index 99c01e73e1..a98db79c09 100644 --- a/internal/admin/repository/eventsourcing/handler/org_iam_policy.go +++ b/internal/admin/repository/eventsourcing/handler/org_iam_policy.go @@ -2,38 +2,70 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type OrgIAMPolicy struct { - handler -} - const ( orgIAMPolicyTable = "adminapi.org_iam_policies" ) +type OrgIAMPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newOrgIAMPolicy(handler handler) *OrgIAMPolicy { + h := &OrgIAMPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *OrgIAMPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *OrgIAMPolicy) ViewModel() string { return orgIAMPolicyTable } -func (p *OrgIAMPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestOrgIAMPolicySequence() +func (p *OrgIAMPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *OrgIAMPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *OrgIAMPolicy) Reduce(event *models.Event) (err error) { +func (p *OrgIAMPolicy) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *OrgIAMPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.OrgAggregate, iam_es_model.IAMAggregate: err = p.processOrgIAMPolicy(event) @@ -41,7 +73,7 @@ func (p *OrgIAMPolicy) Reduce(event *models.Event) (err error) { return err } -func (p *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { +func (p *OrgIAMPolicy) processOrgIAMPolicy(event *es_models.Event) (err error) { policy := new(iam_model.OrgIAMPolicyView) switch event.Type { case iam_es_model.OrgIAMPolicyAdded, model.OrgIAMPolicyAdded: @@ -53,17 +85,17 @@ func (p *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.OrgIAMPolicyRemoved: - return p.view.DeleteOrgIAMPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeleteOrgIAMPolicy(event.AggregateID, event) default: - return p.view.ProcessedOrgIAMPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedOrgIAMPolicySequence(event) } if err != nil { return err } - return p.view.PutOrgIAMPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutOrgIAMPolicy(policy, event) } -func (p *OrgIAMPolicy) OnError(event *models.Event, err error) error { +func (p *OrgIAMPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Wm8fs", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgIAM policy handler") return spooler.HandleError(event, err, p.view.GetLatestOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/password_age_policy.go b/internal/admin/repository/eventsourcing/handler/password_age_policy.go index 3608ec446e..7cfa1b2bb2 100644 --- a/internal/admin/repository/eventsourcing/handler/password_age_policy.go +++ b/internal/admin/repository/eventsourcing/handler/password_age_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordAgePolicy struct { - handler -} - const ( passwordAgePolicyTable = "adminapi.password_age_policies" ) +type PasswordAgePolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordAgePolicy(handler handler) *PasswordAgePolicy { + h := &PasswordAgePolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordAgePolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordAgePolicy) ViewModel() string { return passwordAgePolicyTable } +func (p *PasswordAgePolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordAgePolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordAgePolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordAgePolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordAgePolicySequence() + sequence, err := p.view.GetLatestPasswordAgePolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordAgePolicy) processPasswordAgePolicy(event *models.Event) (err e } err = policy.AppendEvent(event) case model.PasswordAgePolicyRemoved: - return p.view.DeletePasswordAgePolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordAgePolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordAgePolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordAgePolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordAgePolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordAgePolicy(policy, event) } func (p *PasswordAgePolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go b/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go index f1d2898218..edab685d23 100644 --- a/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go +++ b/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordComplexityPolicy struct { - handler -} - const ( passwordComplexityPolicyTable = "adminapi.password_complexity_policies" ) +type PasswordComplexityPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordComplexityPolicy(handler handler) *PasswordComplexityPolicy { + h := &PasswordComplexityPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordComplexityPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordComplexityPolicy) ViewModel() string { return passwordComplexityPolicyTable } +func (p *PasswordComplexityPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordComplexityPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordComplexityPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordComplexityPolicySequence() + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *models } err = policy.AppendEvent(event) case model.PasswordComplexityPolicyRemoved: - return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordComplexityPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordComplexityPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordComplexityPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordComplexityPolicy(policy, event) } func (p *PasswordComplexityPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go b/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go index fd087adda7..99a8393cb8 100644 --- a/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go +++ b/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordLockoutPolicy struct { - handler -} - const ( passwordLockoutPolicyTable = "adminapi.password_lockout_policies" ) +type PasswordLockoutPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy { + h := &PasswordLockoutPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordLockoutPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordLockoutPolicy) ViewModel() string { return passwordLockoutPolicyTable } +func (p *PasswordLockoutPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordLockoutPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordLockoutPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *models.Event } err = policy.AppendEvent(event) case model.PasswordLockoutPolicyRemoved: - return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordLockoutPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordLockoutPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordLockoutPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordLockoutPolicy(policy, event) } func (p *PasswordLockoutPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/user.go b/internal/admin/repository/eventsourcing/handler/user.go index f0f26afc4f..eb3a4bd83f 100644 --- a/internal/admin/repository/eventsourcing/handler/user.go +++ b/internal/admin/repository/eventsourcing/handler/user.go @@ -2,6 +2,7 @@ package handler import ( "context" + "github.com/caos/zitadel/internal/config/systemdefaults" iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" @@ -10,6 +11,7 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" @@ -18,29 +20,69 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + userTable = "adminapi.users" +) + type User struct { handler eventstore eventstore.Eventstore orgEvents *org_events.OrgEventstore iamEvents *iam_es.IAMEventstore systemDefaults systemdefaults.SystemDefaults + subscription *eventstore.Subscription } -const ( - userTable = "adminapi.users" -) +func newUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + systemDefaults systemdefaults.SystemDefaults, +) *User { + h := &User{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + systemDefaults: systemDefaults, + } + + h.subscribe() + + return h +} + +func (u *User) subscribe() { + u.subscription = u.es.Subscribe(u.AggregateTypes()...) + go func() { + for event := range u.subscription.Events { + query.ReduceEvent(u, event) + } + }() +} func (u *User) ViewModel() string { return userTable } +func (u *User) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (u *User) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *User) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSequence() + sequence, err := u.view.GetLatestUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -116,14 +158,14 @@ func (u *User) ProcessUser(event *models.Event) (err error) { } err = u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUser(event.AggregateID, event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } if err != nil { return err } - return u.view.PutUser(user, user.Sequence, event.CreationDate) + return u.view.PutUser(user, event) } func (u *User) ProcessOrg(event *models.Event) (err error) { @@ -137,7 +179,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } } @@ -160,7 +202,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.SetLoginNames(policy, org.Domains) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { @@ -185,7 +227,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { diff --git a/internal/admin/repository/eventsourcing/handler/user_external_idps.go b/internal/admin/repository/eventsourcing/handler/user_external_idps.go index cc4d979dc3..cf4e4d05ce 100644 --- a/internal/admin/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/admin/repository/eventsourcing/handler/user_external_idps.go @@ -2,9 +2,11 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" @@ -14,33 +16,74 @@ import ( "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" ) +const ( + externalIDPTable = "adminapi.user_external_idps" +) + type ExternalIDP struct { handler systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - externalIDPTable = "adminapi.user_external_idps" -) +func newExternalIDP( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *ExternalIDP { + h := &ExternalIDP{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (i *ExternalIDP) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *ExternalIDP) ViewModel() string { return externalIDPTable } +func (i *ExternalIDP) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *ExternalIDP) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestExternalIDPSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *ExternalIDP) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestExternalIDPSequence() + sequence, err := i.view.GetLatestExternalIDPSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -68,16 +111,16 @@ func (i *ExternalIDP) processUser(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event) case model.UserRemoved: - return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } if err != nil { return err } - return i.view.PutExternalIDP(externalIDP, externalIDP.Sequence, event.CreationDate) + return i.view.PutExternalIDP(externalIDP, event) } func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { @@ -105,9 +148,9 @@ func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { for _, provider := range exterinalIDPs { i.fillConfigData(provider, config) } - return i.view.PutExternalIDPs(event.Sequence, event.CreationDate, exterinalIDPs...) + return i.view.PutExternalIDPs(event, exterinalIDPs...) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } return nil } diff --git a/internal/admin/repository/eventsourcing/spooler/lock.go b/internal/admin/repository/eventsourcing/spooler/lock.go index 76317dc700..0c04a3bf07 100644 --- a/internal/admin/repository/eventsourcing/spooler/lock.go +++ b/internal/admin/repository/eventsourcing/spooler/lock.go @@ -3,8 +3,6 @@ package spooler import ( "database/sql" "time" - - es_locker "github.com/caos/zitadel/internal/eventstore/locker" ) const ( @@ -25,5 +23,5 @@ type lock struct { } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) + return nil } diff --git a/internal/admin/repository/eventsourcing/view/external_idps.go b/internal/admin/repository/eventsourcing/view/external_idps.go index 8f21e7de86..384c1d81d0 100644 --- a/internal/admin/repository/eventsourcing/view/external_idps.go +++ b/internal/admin/repository/eventsourcing/view/external_idps.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -33,44 +33,44 @@ func (v *View) SearchExternalIDPs(request *usr_model.ExternalIDPSearchRequest) ( return view.SearchExternalIDPs(v.Db, externalIDPTable, request) } -func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, event *models.Event) error { err := view.PutExternalIDP(v.Db, externalIDPTable, externalIDP) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) PutExternalIDPs(sequence uint64, eventTimestamp time.Time, externalIDPs ...*model.ExternalIDPView) error { +func (v *View) PutExternalIDPs(event *models.Event, externalIDPs ...*model.ExternalIDPView) error { err := view.PutExternalIDPs(v.Db, externalIDPTable, externalIDPs...) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, event *models.Event) error { err := view.DeleteExternalIDP(v.Db, externalIDPTable, externalUserID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDPsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDPsByUserID(userID string, event *models.Event) error { err := view.DeleteExternalIDPsByUserID(v.Db, externalIDPTable, userID) if err != nil { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) GetLatestExternalIDPSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(externalIDPTable) +func (v *View) GetLatestExternalIDPSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(externalIDPTable, aggregateType) } -func (v *View) ProcessedExternalIDPSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(externalIDPTable, eventSequence, eventTimestamp) +func (v *View) ProcessedExternalIDPSequence(event *models.Event) error { + return v.saveCurrentSequence(externalIDPTable, event) } func (v *View) UpdateExternalIDPSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/iam_member.go b/internal/admin/repository/eventsourcing/view/iam_member.go index 250a0042fa..d215ca0b5b 100644 --- a/internal/admin/repository/eventsourcing/view/iam_member.go +++ b/internal/admin/repository/eventsourcing/view/iam_member.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "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" - "time" ) const ( @@ -25,44 +25,44 @@ func (v *View) IAMMembersByUserID(userID string) ([]*model.IAMMemberView, error) return view.IAMMembersByUserID(v.Db, iamMemberTable, userID) } -func (v *View) PutIAMMember(org *model.IAMMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIAMMember(org *model.IAMMemberView, event *models.Event) error { err := view.PutIAMMember(v.Db, iamMemberTable, org) if err != nil { return err } - return v.ProcessedIAMMemberSequence(sequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) PutIAMMembers(members []*model.IAMMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIAMMembers(members []*model.IAMMemberView, event *models.Event) error { err := view.PutIAMMembers(v.Db, iamMemberTable, members...) if err != nil { return err } - return v.ProcessedIAMMemberSequence(sequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) DeleteIAMMember(iamID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIAMMember(iamID, userID string, event *models.Event) error { err := view.DeleteIAMMember(v.Db, iamMemberTable, iamID, userID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIAMMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) DeleteIAMMembersByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIAMMembersByUserID(userID string, event *models.Event) error { err := view.DeleteIAMMembersByUserID(v.Db, iamMemberTable, userID) if err != nil { return err } - return v.ProcessedIAMMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) GetLatestIAMMemberSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(iamMemberTable) +func (v *View) GetLatestIAMMemberSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(iamMemberTable, aggregateType) } -func (v *View) ProcessedIAMMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(iamMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIAMMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(iamMemberTable, event) } func (v *View) UpdateIAMMemberSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/idp_configs.go b/internal/admin/repository/eventsourcing/view/idp_configs.go index 197c37d09b..5a120de265 100644 --- a/internal/admin/repository/eventsourcing/view/idp_configs.go +++ b/internal/admin/repository/eventsourcing/view/idp_configs.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "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" - "time" ) const ( @@ -21,28 +21,28 @@ func (v *View) SearchIDPConfigs(request *iam_model.IDPConfigSearchRequest) ([]*m return view.SearchIDPs(v.Db, idpConfigTable, request) } -func (v *View) PutIDPConfig(idp *model.IDPConfigView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPConfig(idp *model.IDPConfigView, event *models.Event) error { err := view.PutIDP(v.Db, idpConfigTable, idp) if err != nil { return err } - return v.ProcessedIDPConfigSequence(sequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) DeleteIDPConfig(idpID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPConfig(idpID string, event *models.Event) error { err := view.DeleteIDP(v.Db, idpConfigTable, idpID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPConfigSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) GetLatestIDPConfigSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpConfigTable) +func (v *View) GetLatestIDPConfigSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpConfigTable, aggregateType) } -func (v *View) ProcessedIDPConfigSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpConfigTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPConfigSequence(event *models.Event) error { + return v.saveCurrentSequence(idpConfigTable, event) } func (v *View) UpdateIDPConfigSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/idp_providers.go b/internal/admin/repository/eventsourcing/view/idp_providers.go index 10b5886945..26a682379d 100644 --- a/internal/admin/repository/eventsourcing/view/idp_providers.go +++ b/internal/admin/repository/eventsourcing/view/idp_providers.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "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" - "time" ) const ( @@ -25,36 +25,36 @@ func (v *View) SearchIDPProviders(request *iam_model.IDPProviderSearchRequest) ( return view.SearchIDPProviders(v.Db, idpProviderTable, request) } -func (v *View) PutIDPProvider(provider *model.IDPProviderView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPProvider(provider *model.IDPProviderView, event *models.Event) error { err := view.PutIDPProvider(v.Db, idpProviderTable, provider) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) PutIDPProviders(sequence uint64, eventTimestamp time.Time, providers ...*model.IDPProviderView) error { +func (v *View) PutIDPProviders(event *models.Event, providers ...*model.IDPProviderView) error { err := view.PutIDPProviders(v.Db, idpProviderTable, providers...) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, event *models.Event) error { err := view.DeleteIDPProvider(v.Db, idpProviderTable, aggregateID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) GetLatestIDPProviderSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpProviderTable) +func (v *View) GetLatestIDPProviderSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpProviderTable, aggregateType) } -func (v *View) ProcessedIDPProviderSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpProviderTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPProviderSequence(event *models.Event) error { + return v.saveCurrentSequence(idpProviderTable, event) } func (v *View) UpdateIDPProviderSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/label_policies.go b/internal/admin/repository/eventsourcing/view/label_policies.go index f4e921ac1c..9b1a46cee1 100644 --- a/internal/admin/repository/eventsourcing/view/label_policies.go +++ b/internal/admin/repository/eventsourcing/view/label_policies.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -15,20 +15,20 @@ func (v *View) LabelPolicyByAggregateID(aggregateID string) (*model.LabelPolicyV return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTable, aggregateID) } -func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error { err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy) if err != nil { return err } - return v.ProcessedLabelPolicySequence(sequence, eventTimestamp) + return v.ProcessedLabelPolicySequence(event) } -func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(labelPolicyTable) +func (v *View) GetLatestLabelPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(labelPolicyTable, aggregateType) } -func (v *View) ProcessedLabelPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(labelPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLabelPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(labelPolicyTable, event) } func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/login_policies.go b/internal/admin/repository/eventsourcing/view/login_policies.go index 03e4451ffb..7e71983724 100644 --- a/internal/admin/repository/eventsourcing/view/login_policies.go +++ b/internal/admin/repository/eventsourcing/view/login_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LoginPolicyByAggregateID(aggregateID string) (*model.LoginPolicyV return view.GetLoginPolicyByAggregateID(v.Db, loginPolicyTable, aggregateID) } -func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, event *models.Event) error { err := view.PutLoginPolicy(v.Db, loginPolicyTable, policy) if err != nil { return err } - return v.ProcessedLoginPolicySequence(sequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) DeleteLoginPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLoginPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLoginPolicy(v.Db, loginPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLoginPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) GetLatestLoginPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(loginPolicyTable) +func (v *View) GetLatestLoginPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(loginPolicyTable, aggregateType) } -func (v *View) ProcessedLoginPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(loginPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLoginPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(loginPolicyTable, event) } func (v *View) UpdateLoginPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/org.go b/internal/admin/repository/eventsourcing/view/org.go index ee9f9fb113..b5d104a7ed 100644 --- a/internal/admin/repository/eventsourcing/view/org.go +++ b/internal/admin/repository/eventsourcing/view/org.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" org_view "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -20,12 +20,12 @@ func (v *View) SearchOrgs(query *org_model.OrgSearchRequest) ([]*model.OrgView, return org_view.SearchOrgs(v.Db, orgTable, query) } -func (v *View) PutOrg(org *model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -40,10 +40,10 @@ func (v *View) UpdateOrgSpoolerRunTimestamp() error { return v.updateSpoolerRunSequence(orgTable) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } diff --git a/internal/admin/repository/eventsourcing/view/org_iam_policy.go b/internal/admin/repository/eventsourcing/view/org_iam_policy.go index 3168a78152..5d13779e48 100644 --- a/internal/admin/repository/eventsourcing/view/org_iam_policy.go +++ b/internal/admin/repository/eventsourcing/view/org_iam_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) OrgIAMPolicyByAggregateID(aggregateID string) (*model.OrgIAMPolic return view.GetOrgIAMPolicyByAggregateID(v.Db, orgIAMPolicyTable, aggregateID) } -func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, event *models.Event) error { err := view.PutOrgIAMPolicy(v.Db, orgIAMPolicyTable, policy) if err != nil { return err } - return v.ProcessedOrgIAMPolicySequence(sequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) DeleteOrgIAMPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgIAMPolicy(aggregateID string, event *models.Event) error { err := view.DeleteOrgIAMPolicy(v.Db, orgIAMPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedOrgIAMPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) GetLatestOrgIAMPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(orgIAMPolicyTable) +func (v *View) GetLatestOrgIAMPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(orgIAMPolicyTable, aggregateType) } -func (v *View) ProcessedOrgIAMPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgIAMPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgIAMPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(orgIAMPolicyTable, event) } func (v *View) UpdateOrgIAMPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/password_age_policy.go b/internal/admin/repository/eventsourcing/view/password_age_policy.go index 419b537830..b1cbe535c4 100644 --- a/internal/admin/repository/eventsourcing/view/password_age_policy.go +++ b/internal/admin/repository/eventsourcing/view/password_age_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordAgePolicyByAggregateID(aggregateID string) (*model.Passwo return view.GetPasswordAgePolicyByAggregateID(v.Db, passwordAgePolicyTable, aggregateID) } -func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, event *models.Event) error { err := view.PutPasswordAgePolicy(v.Db, passwordAgePolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordAgePolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) DeletePasswordAgePolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordAgePolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordAgePolicy(v.Db, passwordAgePolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordAgePolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) GetLatestPasswordAgePolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordAgePolicyTable) +func (v *View) GetLatestPasswordAgePolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordAgePolicyTable, aggregateType) } -func (v *View) ProcessedPasswordAgePolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordAgePolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordAgePolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordAgePolicyTable, event) } func (v *View) UpdateProcessedPasswordAgePolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/password_complexity_policy.go b/internal/admin/repository/eventsourcing/view/password_complexity_policy.go index 2b1af7d375..44315b9666 100644 --- a/internal/admin/repository/eventsourcing/view/password_complexity_policy.go +++ b/internal/admin/repository/eventsourcing/view/password_complexity_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordComplexityPolicyByAggregateID(aggregateID string) (*model return view.GetPasswordComplexityPolicyByAggregateID(v.Db, passwordComplexityPolicyTable, aggregateID) } -func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, event *models.Event) error { err := view.PutPasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordComplexityPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) DeletePasswordComplexityPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordComplexityPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordComplexityPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) GetLatestPasswordComplexityPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordComplexityPolicyTable) +func (v *View) GetLatestPasswordComplexityPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordComplexityPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordComplexityPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordComplexityPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordComplexityPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordComplexityPolicyTable, event) } func (v *View) UpdatePasswordComplexityPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/password_lockout_policy.go b/internal/admin/repository/eventsourcing/view/password_lockout_policy.go index d20643d37c..805aab3fc3 100644 --- a/internal/admin/repository/eventsourcing/view/password_lockout_policy.go +++ b/internal/admin/repository/eventsourcing/view/password_lockout_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordLockoutPolicyByAggregateID(aggregateID string) (*model.Pa return view.GetPasswordLockoutPolicyByAggregateID(v.Db, passwordLockoutPolicyTable, aggregateID) } -func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, sequence uint64, eventTimestamp time.Time) error { +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(sequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) DeletePasswordLockoutPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +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(eventSequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) GetLatestPasswordLockoutPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordLockoutPolicyTable) +func (v *View) GetLatestPasswordLockoutPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordLockoutPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordLockoutPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordLockoutPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordLockoutPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordLockoutPolicyTable, event) } func (v *View) UpdatePasswordLockoutPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/sequence.go b/internal/admin/repository/eventsourcing/view/sequence.go index cda4580a6b..5915da9b56 100644 --- a/internal/admin/repository/eventsourcing/view/sequence.go +++ b/internal/admin/repository/eventsourcing/view/sequence.go @@ -1,20 +1,22 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "adminapi.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimeStamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimeStamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) AllCurrentSequences(db string) ([]*repository.CurrentSequence, error) { @@ -22,7 +24,7 @@ func (v *View) AllCurrentSequences(db string) ([]*repository.CurrentSequence, er } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -30,13 +32,16 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } -func (v *View) GetCurrentSequence(db, viewName string) (*repository.CurrentSequence, error) { +func (v *View) GetCurrentSequence(db, viewName, aggregateType string) (*repository.CurrentSequence, error) { sequenceTable := db + ".current_sequences" fullView := db + "." + viewName - return repository.LatestSequence(v.Db, sequenceTable, fullView) + return repository.LatestSequence(v.Db, sequenceTable, fullView, aggregateType) } func (v *View) ClearView(db, viewName string) error { diff --git a/internal/admin/repository/eventsourcing/view/user.go b/internal/admin/repository/eventsourcing/view/user.go index 3de68472cf..6e34155c96 100644 --- a/internal/admin/repository/eventsourcing/view/user.go +++ b/internal/admin/repository/eventsourcing/view/user.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -40,39 +40,39 @@ func (v *View) UserMFAs(userID string) ([]*usr_model.MultiFactor, error) { return view.UserMFAs(v.Db, userTable, userID) } -func (v *View) PutUsers(user []*model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUsers(user []*model.UserView, event *models.Event) error { err := view.PutUsers(v.Db, userTable, user...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) PutUser(user *model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUser(user *model.UserView, event *models.Event) error { err := view.PutUser(v.Db, userTable, user) if err != nil { return err } - if sequence != 0 { - return v.ProcessedUserSequence(sequence, eventTimestamp) + if event.Sequence != 0 { + return v.ProcessedUserSequence(event) } return nil } -func (v *View) DeleteUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) if err != nil { return nil } - return v.ProcessedUserSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) GetLatestUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userTable) +func (v *View) GetLatestUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userTable, aggregateType) } -func (v *View) ProcessedUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSequence(event *models.Event) error { + return v.saveCurrentSequence(userTable, event) } func (v *View) UpdateUserSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/eventstore/org.go b/internal/auth/repository/eventsourcing/eventstore/org.go index c5f3ecced0..9339b1be70 100644 --- a/internal/auth/repository/eventsourcing/eventstore/org.go +++ b/internal/auth/repository/eventsourcing/eventstore/org.go @@ -36,7 +36,7 @@ type OrgRepository struct { func (repo *OrgRepository) SearchOrgs(ctx context.Context, request *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestOrgSequence() + sequence, err := repo.View.GetLatestOrgSequence("") logging.Log("EVENT-7Udhz").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest org sequence") members, count, err := repo.View.SearchOrgs(request) if err != nil { diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index d4e96c32e3..392ff2a793 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -109,7 +109,7 @@ func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profil func (repo *UserRepo) SearchMyExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, seqErr := repo.View.GetLatestExternalIDPSequence() + sequence, seqErr := repo.View.GetLatestExternalIDPSequence("") logging.Log("EVENT-5Jsi8").OnError(seqErr).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest user sequence") request.AppendUserQuery(authz.GetCtxData(ctx).UserID) externalIDPS, count, err := repo.View.SearchExternalIDPs(request) diff --git a/internal/auth/repository/eventsourcing/eventstore/user_grant.go b/internal/auth/repository/eventsourcing/eventstore/user_grant.go index cba39e4314..d9ba6bc842 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/auth/repository/eventsourcing/eventstore/user_grant.go @@ -29,7 +29,7 @@ type UserGrantRepo struct { func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestUserGrantSequence() + sequence, err := repo.View.GetLatestUserGrantSequence("") logging.Log("EVENT-Hd7s3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest user grant sequence") request.Queries = append(request.Queries, &grant_model.UserGrantSearchQuery{Key: grant_model.UserGrantSearchKeyUserID, Method: global_model.SearchMethodEquals, Value: authz.GetCtxData(ctx).UserID}) grants, count, err := repo.View.SearchUserGrants(request) diff --git a/internal/auth/repository/eventsourcing/handler/application.go b/internal/auth/repository/eventsourcing/handler/application.go index 377e0b0499..2ce66a2c22 100644 --- a/internal/auth/repository/eventsourcing/handler/application.go +++ b/internal/auth/repository/eventsourcing/handler/application.go @@ -5,7 +5,9 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -13,19 +15,52 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Application struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( applicationTable = "auth.applications" ) +type Application struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newApplication(handler handler, projectEvents *proj_event.ProjectEventstore) *Application { + h := &Application{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (a *Application) subscribe() { + a.subscription = a.es.Subscribe(a.AggregateTypes()...) + go func() { + for event := range a.subscription.Events { + query.ReduceEvent(a, event) + } + }() +} + func (a *Application) ViewModel() string { return applicationTable } +func (_ *Application) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (a *Application) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := a.view.GetLatestApplicationSequence() + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (a *Application) EventQuery() (*models.SearchQuery, error) { sequence, err := a.view.GetLatestApplicationSequence() if err != nil { @@ -65,30 +100,30 @@ func (a *Application) Reduce(event *models.Event) (err error) { if err != nil { return err } - return a.view.DeleteApplication(app.ID, event.Sequence, event.CreationDate) + return a.view.DeleteApplication(app.ID, event) case es_model.ProjectChanged: apps, err := a.view.ApplicationsByProjectID(event.AggregateID) if err != nil { return err } if len(apps) == 0 { - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } for _, app := range apps { if err := app.AppendEvent(event); err != nil { return err } } - return a.view.PutApplications(apps, event.Sequence, event.CreationDate) + return a.view.PutApplications(apps, event) case es_model.ProjectRemoved: return a.view.DeleteApplicationsByProjectID(event.AggregateID) default: - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } if err != nil { return err } - return a.view.PutApplication(app, event.CreationDate) + return a.view.PutApplication(app, event) } func (a *Application) OnError(event *models.Event, spoolerError error) error { diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index d641ec39db..0235304ce7 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -26,6 +26,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -35,39 +41,65 @@ type EventstoreRepos struct { IamEvents *iam_events.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { return []query.Handler{ - &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, iamID: systemDefaults.IamID}, - &UserSession{handler: handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount}, userEvents: repos.UserEvents}, - &UserMembership{handler: handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount}, orgEvents: repos.OrgEvents, projectEvents: repos.ProjectEvents}, - &Token{handler: handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount}, ProjectEvents: repos.ProjectEvents}, - &Key{handler: handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount}}, - &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, projectEvents: repos.ProjectEvents}, - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, - &UserGrant{ - handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, - eventstore: eventstore, - userEvents: repos.UserEvents, - orgEvents: repos.OrgEvents, - projectEvents: repos.ProjectEvents, - iamEvents: repos.IamEvents, - iamID: systemDefaults.IamID}, - &MachineKeys{handler: handler{view, bulkLimit, configs.cycleDuration("MachineKey"), errorCount}}, - &LoginPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount}}, - &IDPConfig{handler: handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount}}, - &IDPProvider{handler: handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount}, systemDefaults: systemDefaults, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents}, - &ExternalIDP{handler: handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount}, systemDefaults: systemDefaults, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents}, - &PasswordComplexityPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount}}, - &OrgIAMPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount}}, - &ProjectRole{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount}, projectEvents: repos.ProjectEvents}, + newUser( + handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IamEvents, + systemDefaults.IamID), + newUserSession( + handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount, es}, + repos.UserEvents), + newUserMembership( + handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}, + repos.OrgEvents, + repos.ProjectEvents), + newToken( + handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount, es}, + repos.ProjectEvents), + newKey( + handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount, es}), + newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}, + repos.ProjectEvents), + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), + newUserGrant( + handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, + repos.ProjectEvents, + repos.UserEvents, + repos.OrgEvents, + repos.IamEvents, + systemDefaults.IamID), + newMachineKeys( + handler{view, bulkLimit, configs.cycleDuration("MachineKey"), errorCount, es}), + newLoginPolicy( + handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount, es}), + newIDPConfig( + handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), + newIDPProvider( + handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, + systemDefaults, + repos.IamEvents, + repos.OrgEvents), + newExternalIDP( + handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount, es}, + systemDefaults, + repos.IamEvents, + repos.OrgEvents), + newPasswordComplexityPolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}), + newOrgIAMPolicy( + handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), + newProjectRole(handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount, es}, + repos.ProjectEvents), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } diff --git a/internal/auth/repository/eventsourcing/handler/idp_config.go b/internal/auth/repository/eventsourcing/handler/idp_config.go index e6c21a6260..b070d89c8a 100644 --- a/internal/auth/repository/eventsourcing/handler/idp_config.go +++ b/internal/auth/repository/eventsourcing/handler/idp_config.go @@ -2,8 +2,10 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" @@ -11,25 +13,57 @@ import ( "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type IDPConfig struct { - handler -} - const ( idpConfigTable = "auth.idp_configs" ) +type IDPConfig struct { + handler + subscription *eventstore.Subscription +} + +func newIDPConfig(h handler) *IDPConfig { + idpConfig := &IDPConfig{ + handler: h, + } + + idpConfig.subscribe() + + return idpConfig +} + +func (i *IDPConfig) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} + func (i *IDPConfig) ViewModel() string { return idpConfigTable } +func (_ *IDPConfig) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (i *IDPConfig) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPConfigSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *IDPConfig) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPConfigSequence() + sequence, err := i.view.GetLatestIDPConfigSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -66,14 +100,14 @@ func (i *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, eve if err != nil { return err } - return i.view.DeleteIDPConfig(idp.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPConfig(idp.IDPConfigID, event) default: - return i.view.ProcessedIDPConfigSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPConfigSequence(event) } if err != nil { return err } - return i.view.PutIDPConfig(idp, idp.Sequence, event.CreationDate) + return i.view.PutIDPConfig(idp, event) } func (i *IDPConfig) OnError(event *models.Event, err error) error { diff --git a/internal/auth/repository/eventsourcing/handler/idp_providers.go b/internal/auth/repository/eventsourcing/handler/idp_providers.go index b92a20a3b4..8622fc57b1 100644 --- a/internal/auth/repository/eventsourcing/handler/idp_providers.go +++ b/internal/auth/repository/eventsourcing/handler/idp_providers.go @@ -2,42 +2,85 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) +const ( + idpProviderTable = "auth.idp_providers" +) + type IDPProvider struct { handler systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - idpProviderTable = "auth.idp_providers" -) +func newIDPProvider( + h handler, + defaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *IDPProvider { + idpProvider := &IDPProvider{ + handler: h, + systemDefaults: defaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + idpProvider.subscribe() + + return idpProvider +} + +func (i *IDPProvider) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *IDPProvider) ViewModel() string { return idpProviderTable } +func (_ *IDPProvider) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *IDPProvider) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPProviderSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *IDPProvider) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPProviderSequence() + sequence, err := i.view.GetLatestIDPProviderSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -64,7 +107,7 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event) case model.IDPConfigChanged, org_es_model.IDPConfigChanged: esConfig := new(iam_view_model.IDPConfigView) providerType := iam_model.IDPProviderTypeSystem @@ -88,16 +131,16 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { for _, provider := range providers { i.fillConfigData(provider, config) } - return i.view.PutIDPProviders(event.Sequence, event.CreationDate, providers...) + return i.view.PutIDPProviders(event, providers...) case org_es_model.LoginPolicyRemoved: - return i.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event) default: - return i.view.ProcessedIDPProviderSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPProviderSequence(event) } if err != nil { return err } - return i.view.PutIDPProvider(provider, provider.Sequence, event.CreationDate) + return i.view.PutIDPProvider(provider, event) } func (i *IDPProvider) fillData(provider *iam_view_model.IDPProviderView) (err error) { diff --git a/internal/auth/repository/eventsourcing/handler/key.go b/internal/auth/repository/eventsourcing/handler/key.go index 48f224cc29..54703a2509 100644 --- a/internal/auth/repository/eventsourcing/handler/key.go +++ b/internal/auth/repository/eventsourcing/handler/key.go @@ -3,30 +3,62 @@ package handler import ( "time" - es_model "github.com/caos/zitadel/internal/key/repository/eventsourcing/model" - "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/key/repository/eventsourcing" + es_model "github.com/caos/zitadel/internal/key/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/key/repository/view/model" ) -type Key struct { - handler -} - const ( keyTable = "auth.keys" ) +type Key struct { + handler + subscription *eventstore.Subscription +} + +func newKey(handler handler) *Key { + h := &Key{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *Key) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (k *Key) ViewModel() string { return keyTable } +func (_ *Key) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.KeyPairAggregate} +} + +func (k *Key) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := k.view.GetLatestKeySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (k *Key) EventQuery() (*models.SearchQuery, error) { - sequence, err := k.view.GetLatestKeySequence() + sequence, err := k.view.GetLatestKeySequence("") if err != nil { return nil, err } @@ -41,11 +73,11 @@ func (k *Key) Reduce(event *models.Event) error { return err } if privateKey.Expiry.Before(time.Now()) && publicKey.Expiry.Before(time.Now()) { - return k.view.ProcessedKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedKeySequence(event) } - return k.view.PutKeys(privateKey, publicKey, event.Sequence, event.CreationDate) + return k.view.PutKeys(privateKey, publicKey, event) default: - return k.view.ProcessedKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedKeySequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/login_policy.go b/internal/auth/repository/eventsourcing/handler/login_policy.go index b92dd17a9e..35700d80c7 100644 --- a/internal/auth/repository/eventsourcing/handler/login_policy.go +++ b/internal/auth/repository/eventsourcing/handler/login_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type LoginPolicy struct { - handler -} - const ( loginPolicyTable = "auth.login_policies" ) +type LoginPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLoginPolicy(handler handler) *LoginPolicy { + h := &LoginPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *LoginPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *LoginPolicy) ViewModel() string { return loginPolicyTable } +func (_ *LoginPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *LoginPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestLoginPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *LoginPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestLoginPolicySequence() + sequence, err := p.view.GetLatestLoginPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -57,14 +91,14 @@ func (p *LoginPolicy) processLoginPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.LoginPolicyRemoved: - return p.view.DeleteLoginPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeleteLoginPolicy(event.AggregateID, event) default: - return p.view.ProcessedLoginPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedLoginPolicySequence(event) } if err != nil { return err } - return p.view.PutLoginPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutLoginPolicy(policy, event) } func (p *LoginPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/auth/repository/eventsourcing/handler/machine_keys.go b/internal/auth/repository/eventsourcing/handler/machine_keys.go index f9af7e86fb..0ca0532c26 100644 --- a/internal/auth/repository/eventsourcing/handler/machine_keys.go +++ b/internal/auth/repository/eventsourcing/handler/machine_keys.go @@ -5,73 +5,107 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type MachineKeys struct { - handler -} - const ( machineKeysTable = "auth.machine_keys" ) -func (d *MachineKeys) ViewModel() string { +type MachineKeys struct { + handler + subscription *eventstore.Subscription +} + +func newMachineKeys(handler handler) *MachineKeys { + h := &MachineKeys{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *MachineKeys) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + +func (k *MachineKeys) ViewModel() string { return machineKeysTable } -func (d *MachineKeys) EventQuery() (*models.SearchQuery, error) { - sequence, err := d.view.GetLatestMachineKeySequence() +func (_ *MachineKeys) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate} +} + +func (k *MachineKeys) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := k.view.GetLatestMachineKeySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (k *MachineKeys) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := k.view.GetLatestMachineKeySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate). + AggregateTypeFilter(k.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (d *MachineKeys) Reduce(event *models.Event) (err error) { +func (k *MachineKeys) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.UserAggregate: - err = d.processMachineKeys(event) + err = k.processMachineKeys(event) } return err } -func (d *MachineKeys) processMachineKeys(event *models.Event) (err error) { +func (k *MachineKeys) processMachineKeys(event *es_models.Event) (err error) { key := new(usr_model.MachineKeyView) switch event.Type { case model.MachineKeyAdded: err = key.AppendEvent(event) if key.ExpirationDate.Before(time.Now()) { - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedMachineKeySequence(event) } case model.MachineKeyRemoved: err = key.SetData(event) if err != nil { return err } - return d.view.DeleteMachineKey(key.ID, event.Sequence, event.CreationDate) + return k.view.DeleteMachineKey(key.ID, event) case model.UserRemoved: - return d.view.DeleteMachineKeysByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return k.view.DeleteMachineKeysByUserID(event.AggregateID, event) default: - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedMachineKeySequence(event) } if err != nil { return err } - return d.view.PutMachineKey(key, key.Sequence, event.CreationDate) + return k.view.PutMachineKey(key, event) } -func (d *MachineKeys) OnError(event *models.Event, err error) error { +func (k *MachineKeys) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-S9fe", "id", event.AggregateID).WithError(err).Warn("something went wrong in machine key handler") - return spooler.HandleError(event, err, d.view.GetLatestMachineKeyFailedEvent, d.view.ProcessedMachineKeyFailedEvent, d.view.ProcessedMachineKeySequence, d.errorCountUntilSkip) + return spooler.HandleError(event, err, k.view.GetLatestMachineKeyFailedEvent, k.view.ProcessedMachineKeyFailedEvent, k.view.ProcessedMachineKeySequence, k.errorCountUntilSkip) } -func (d *MachineKeys) OnSuccess() error { - return spooler.HandleSuccess(d.view.UpdateMachineKeySpoolerRunTimestamp) +func (k *MachineKeys) OnSuccess() error { + return spooler.HandleSuccess(k.view.UpdateMachineKeySpoolerRunTimestamp) } diff --git a/internal/auth/repository/eventsourcing/handler/org.go b/internal/auth/repository/eventsourcing/handler/org.go index 909cfefc4a..ea87eed2f2 100644 --- a/internal/auth/repository/eventsourcing/handler/org.go +++ b/internal/auth/repository/eventsourcing/handler/org.go @@ -3,27 +3,62 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "auth.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (o *Org) subscribe() { + o.subscription = o.es.Subscribe(o.AggregateTypes()...) + go func() { + for event := range o.subscription.Events { + query.ReduceEvent(o, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (_ *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (o *Org) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } @@ -58,13 +93,13 @@ func (o *Org) Reduce(event *es_models.Event) (err error) { } org.Domain = domain.Domain default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } if err != nil { return err } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/auth/repository/eventsourcing/handler/org_iam_policy.go b/internal/auth/repository/eventsourcing/handler/org_iam_policy.go index ac28b5c7c4..9d9d230a96 100644 --- a/internal/auth/repository/eventsourcing/handler/org_iam_policy.go +++ b/internal/auth/repository/eventsourcing/handler/org_iam_policy.go @@ -2,68 +2,101 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type OrgIAMPolicy struct { - handler -} - const ( orgIAMPolicyTable = "auth.org_iam_policies" ) +type OrgIAMPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newOrgIAMPolicy(handler handler) *OrgIAMPolicy { + h := &OrgIAMPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *OrgIAMPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *OrgIAMPolicy) ViewModel() string { return orgIAMPolicyTable } -func (p *OrgIAMPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestOrgIAMPolicySequence() +func (_ *OrgIAMPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{org_es_model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *OrgIAMPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *OrgIAMPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *OrgIAMPolicy) Reduce(event *models.Event) (err error) { +func (p *OrgIAMPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { - case model.OrgAggregate, iam_es_model.IAMAggregate: + case org_es_model.OrgAggregate, iam_es_model.IAMAggregate: err = p.processOrgIAMPolicy(event) } return err } -func (p *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { +func (p *OrgIAMPolicy) processOrgIAMPolicy(event *es_models.Event) (err error) { policy := new(iam_model.OrgIAMPolicyView) switch event.Type { - case iam_es_model.OrgIAMPolicyAdded, model.OrgIAMPolicyAdded: + case iam_es_model.OrgIAMPolicyAdded, org_es_model.OrgIAMPolicyAdded: err = policy.AppendEvent(event) - case iam_es_model.OrgIAMPolicyChanged, model.OrgIAMPolicyChanged: + case iam_es_model.OrgIAMPolicyChanged, org_es_model.OrgIAMPolicyChanged: policy, err = p.view.OrgIAMPolicyByAggregateID(event.AggregateID) if err != nil { return err } err = policy.AppendEvent(event) - case model.OrgIAMPolicyRemoved: - return p.view.DeleteOrgIAMPolicy(event.AggregateID, event.Sequence, event.CreationDate) + case org_es_model.OrgIAMPolicyRemoved: + return p.view.DeleteOrgIAMPolicy(event.AggregateID, event) default: - return p.view.ProcessedOrgIAMPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedOrgIAMPolicySequence(event) } if err != nil { return err } - return p.view.PutOrgIAMPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutOrgIAMPolicy(policy, event) } -func (p *OrgIAMPolicy) OnError(event *models.Event, err error) error { +func (p *OrgIAMPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-3Gj8s", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgIAM policy handler") return spooler.HandleError(event, err, p.view.GetLatestOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go b/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go index d3656a164a..65a86676a8 100644 --- a/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go +++ b/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go @@ -2,68 +2,101 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordComplexityPolicy struct { - handler -} - const ( passwordComplexityPolicyTable = "auth.password_complexity_policies" ) +type PasswordComplexityPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordComplexityPolicy(handler handler) *PasswordComplexityPolicy { + h := &PasswordComplexityPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordComplexityPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordComplexityPolicy) ViewModel() string { return passwordComplexityPolicyTable } -func (p *PasswordComplexityPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordComplexityPolicySequence() +func (_ *PasswordComplexityPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{org_es_model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordComplexityPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *PasswordComplexityPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *PasswordComplexityPolicy) Reduce(event *models.Event) (err error) { +func (p *PasswordComplexityPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { - case model.OrgAggregate, iam_es_model.IAMAggregate: + case org_es_model.OrgAggregate, iam_es_model.IAMAggregate: err = p.processPasswordComplexityPolicy(event) } return err } -func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *models.Event) (err error) { +func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *es_models.Event) (err error) { policy := new(iam_model.PasswordComplexityPolicyView) switch event.Type { - case iam_es_model.PasswordComplexityPolicyAdded, model.PasswordComplexityPolicyAdded: + case iam_es_model.PasswordComplexityPolicyAdded, org_es_model.PasswordComplexityPolicyAdded: err = policy.AppendEvent(event) - case iam_es_model.PasswordComplexityPolicyChanged, model.PasswordComplexityPolicyChanged: + case iam_es_model.PasswordComplexityPolicyChanged, org_es_model.PasswordComplexityPolicyChanged: policy, err = p.view.PasswordComplexityPolicyByAggregateID(event.AggregateID) if err != nil { return err } err = policy.AppendEvent(event) - case model.PasswordComplexityPolicyRemoved: - return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event.Sequence, event.CreationDate) + case org_es_model.PasswordComplexityPolicyRemoved: + return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordComplexityPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordComplexityPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordComplexityPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordComplexityPolicy(policy, event) } -func (p *PasswordComplexityPolicy) OnError(event *models.Event, err error) error { +func (p *PasswordComplexityPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-4Djo9", "id", event.AggregateID).WithError(err).Warn("something went wrong in passwordComplexity policy handler") return spooler.HandleError(event, err, p.view.GetLatestPasswordComplexityPolicyFailedEvent, p.view.ProcessedPasswordComplexityPolicyFailedEvent, p.view.ProcessedPasswordComplexityPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/auth/repository/eventsourcing/handler/project_role.go b/internal/auth/repository/eventsourcing/handler/project_role.go index c469019b9f..9d7e684c7c 100644 --- a/internal/auth/repository/eventsourcing/handler/project_role.go +++ b/internal/auth/repository/eventsourcing/handler/project_role.go @@ -3,41 +3,80 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" - "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" - es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" + proj_events "github.com/caos/zitadel/internal/project/repository/eventsourcing" + "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type ProjectRole struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( projectRoleTable = "auth.project_roles" ) +type ProjectRole struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newProjectRole( + handler handler, + projectEvents *proj_events.ProjectEventstore, +) *ProjectRole { + h := &ProjectRole{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (k *ProjectRole) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (p *ProjectRole) ViewModel() string { return projectRoleTable } -func (p *ProjectRole) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectRoleSequence() +func (_ *ProjectRole) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.ProjectAggregate} +} + +func (p *ProjectRole) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectRoleSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *ProjectRole) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestProjectRoleSequence("") if err != nil { return nil, err } - return eventsourcing.ProjectQuery(sequence.CurrentSequence), nil + return proj_events.ProjectQuery(sequence.CurrentSequence), nil } -func (p *ProjectRole) Reduce(event *models.Event) (err error) { +func (p *ProjectRole) Reduce(event *es_models.Event) (err error) { role := new(view_model.ProjectRoleView) switch event.Type { - case es_model.ProjectRoleAdded: + case model.ProjectRoleAdded: err = role.AppendEvent(event) - case es_model.ProjectRoleChanged: + case model.ProjectRoleChanged: err = role.SetData(event) if err != nil { return err @@ -47,24 +86,24 @@ func (p *ProjectRole) Reduce(event *models.Event) (err error) { return err } err = role.AppendEvent(event) - case es_model.ProjectRoleRemoved: + case model.ProjectRoleRemoved: err = role.SetData(event) if err != nil { return err } - return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event.Sequence, event.CreationDate) - case es_model.ProjectRemoved: + return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event) + case model.ProjectRemoved: return p.view.DeleteProjectRolesByProjectID(event.AggregateID) default: - return p.view.ProcessedProjectRoleSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectRoleSequence(event) } if err != nil { return err } - return p.view.PutProjectRole(role, event.CreationDate) + return p.view.PutProjectRole(role, event) } -func (p *ProjectRole) OnError(event *models.Event, err error) error { +func (p *ProjectRole) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-lso9w", "id", event.AggregateID).WithError(err).Warn("something went wrong in project role handler") return spooler.HandleError(event, err, p.view.GetLatestProjectRoleFailedEvent, p.view.ProcessedProjectRoleFailedEvent, p.view.ProcessedProjectRoleSequence, p.errorCountUntilSkip) } diff --git a/internal/auth/repository/eventsourcing/handler/token.go b/internal/auth/repository/eventsourcing/handler/token.go index 2c056cc064..b98e811bda 100644 --- a/internal/auth/repository/eventsourcing/handler/token.go +++ b/internal/auth/repository/eventsourcing/handler/token.go @@ -6,8 +6,10 @@ import ( "github.com/caos/logging" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" project_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" @@ -15,21 +17,57 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type Token struct { - handler - ProjectEvents *proj_event.ProjectEventstore -} - const ( tokenTable = "auth.tokens" ) +type Token struct { + handler + ProjectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newToken( + handler handler, + projectEvents *proj_event.ProjectEventstore, +) *Token { + h := &Token{ + handler: handler, + ProjectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (t *Token) subscribe() { + t.subscription = t.es.Subscribe(t.AggregateTypes()...) + go func() { + for event := range t.subscription.Events { + query.ReduceEvent(t, event) + } + }() +} + func (t *Token) ViewModel() string { return tokenTable } +func (_ *Token) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{user_es_model.UserAggregate, project_es_model.ProjectAggregate} +} + +func (p *Token) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestTokenSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (t *Token) EventQuery() (*models.SearchQuery, error) { - sequence, err := t.view.GetLatestTokenSequence() + sequence, err := t.view.GetLatestTokenSequence("") if err != nil { return nil, err } @@ -49,7 +87,7 @@ func (t *Token) Reduce(event *models.Event) (err error) { if err != nil { return err } - return t.view.PutToken(token, event.CreationDate) + return t.view.PutToken(token, event) case user_es_model.UserProfileChanged, user_es_model.HumanProfileChanged: user := new(view_model.UserView) @@ -61,25 +99,25 @@ func (t *Token) Reduce(event *models.Event) (err error) { for _, token := range tokens { token.PreferredLanguage = user.PreferredLanguage } - return t.view.PutTokens(tokens, event.Sequence, event.CreationDate) + return t.view.PutTokens(tokens, event) case user_es_model.SignedOut, user_es_model.HumanSignedOut: id, err := agentIDFromSession(event) if err != nil { return err } - return t.view.DeleteSessionTokens(id, event.AggregateID, event.Sequence, event.CreationDate) + return t.view.DeleteSessionTokens(id, event.AggregateID, event) case user_es_model.UserLocked, user_es_model.UserDeactivated, user_es_model.UserRemoved: - return t.view.DeleteUserTokens(event.AggregateID, event.Sequence, event.CreationDate) + return t.view.DeleteUserTokens(event.AggregateID, event) case project_es_model.ApplicationDeactivated, project_es_model.ApplicationRemoved: application, err := applicationFromSession(event) if err != nil { return err } - return t.view.DeleteApplicationTokens(event.Sequence, event.CreationDate, application.AppID) + return t.view.DeleteApplicationTokens(event, application.AppID) case project_es_model.ProjectDeactivated, project_es_model.ProjectRemoved: project, err := t.ProjectEvents.ProjectByID(context.Background(), event.AggregateID) @@ -90,9 +128,9 @@ func (t *Token) Reduce(event *models.Event) (err error) { for _, app := range project.Applications { applicationsIDs = append(applicationsIDs, app.AppID) } - return t.view.DeleteApplicationTokens(event.Sequence, event.CreationDate, applicationsIDs...) + return t.view.DeleteApplicationTokens(event, applicationsIDs...) default: - return t.view.ProcessedTokenSequence(event.Sequence, event.CreationDate) + return t.view.ProcessedTokenSequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/user.go b/internal/auth/repository/eventsourcing/handler/user.go index 8b1ceb091c..f960ab720d 100644 --- a/internal/auth/repository/eventsourcing/handler/user.go +++ b/internal/auth/repository/eventsourcing/handler/user.go @@ -2,45 +2,83 @@ package handler import ( "context" - iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type User struct { - handler - eventstore eventstore.Eventstore - orgEvents *org_events.OrgEventstore - iamEvents *iam_es.IAMEventstore - iamID string -} - const ( userTable = "auth.users" ) +type User struct { + handler + orgEvents *org_events.OrgEventstore + iamEvents *iam_es.IAMEventstore + iamID string + subscription *eventstore.Subscription +} + +func newUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + iamID string, +) *User { + h := &User{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *User) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (u *User) ViewModel() string { return userTable } +func (_ *User) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (u *User) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *User) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSequence() + sequence, err := u.view.GetLatestUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -120,14 +158,14 @@ func (u *User) ProcessUser(event *models.Event) (err error) { } err = u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUser(event.AggregateID, event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } if err != nil { return err } - return u.view.PutUser(user, user.Sequence, event.CreationDate) + return u.view.PutUser(user, event) } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { @@ -158,7 +196,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } } @@ -181,7 +219,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.SetLoginNames(policy, org.Domains) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { @@ -206,7 +244,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) } - return u.view.PutUsers(users, 0, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) OnError(event *models.Event, err error) error { diff --git a/internal/auth/repository/eventsourcing/handler/user_external_idps.go b/internal/auth/repository/eventsourcing/handler/user_external_idps.go index 826d79170c..8dcaebe3cb 100644 --- a/internal/auth/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/auth/repository/eventsourcing/handler/user_external_idps.go @@ -2,11 +2,14 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" @@ -18,28 +21,68 @@ import ( usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + externalIDPTable = "auth.user_external_idps" +) + type ExternalIDP struct { handler systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - externalIDPTable = "auth.user_external_idps" -) +func newExternalIDP( + handler handler, + defaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *ExternalIDP { + h := &ExternalIDP{ + handler: handler, + systemDefaults: defaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (i *ExternalIDP) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *ExternalIDP) ViewModel() string { return externalIDPTable } +func (_ *ExternalIDP) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *ExternalIDP) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestExternalIDPSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *ExternalIDP) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestExternalIDPSequence() + sequence, err := i.view.GetLatestExternalIDPSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -67,16 +110,16 @@ func (i *ExternalIDP) processUser(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event) case model.UserRemoved: - return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } if err != nil { return err } - return i.view.PutExternalIDP(externalIDP, externalIDP.Sequence, event.CreationDate) + return i.view.PutExternalIDP(externalIDP, event) } func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { @@ -104,9 +147,9 @@ func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { for _, provider := range exterinalIDPs { i.fillConfigData(provider, config) } - return i.view.PutExternalIDPs(event.Sequence, event.CreationDate, exterinalIDPs...) + return i.view.PutExternalIDPs(event, exterinalIDPs...) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } return nil } diff --git a/internal/auth/repository/eventsourcing/handler/user_grant.go b/internal/auth/repository/eventsourcing/handler/user_grant.go index c61f831162..c2288891ce 100644 --- a/internal/auth/repository/eventsourcing/handler/user_grant.go +++ b/internal/auth/repository/eventsourcing/handler/user_grant.go @@ -10,6 +10,7 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing" @@ -27,25 +28,68 @@ import ( view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model" ) +const ( + userGrantTable = "auth.user_grants" +) + type UserGrant struct { handler - eventstore eventstore.Eventstore projectEvents *proj_event.ProjectEventstore userEvents *usr_events.UserEventstore orgEvents *org_events.OrgEventstore iamEvents *iam_events.IAMEventstore iamID string iamProjectID string + subscription *eventstore.Subscription } -const ( - userGrantTable = "auth.user_grants" -) +func newUserGrant( + handler handler, + projectEvents *proj_event.ProjectEventstore, + userEvents *usr_events.UserEventstore, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_events.IAMEventstore, + iamID string, +) *UserGrant { + h := &UserGrant{ + handler: handler, + projectEvents: projectEvents, + userEvents: userEvents, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *UserGrant) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} func (u *UserGrant) ViewModel() string { return userGrantTable } +func (_ *UserGrant) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{grant_es_model.UserGrantAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate} +} + +func (u *UserGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { if u.iamProjectID == "" { err := u.setIamProjectID() @@ -53,12 +97,12 @@ func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { return nil, err } } - sequence, err := u.view.GetLatestUserGrantSequence() + sequence, err := u.view.GetLatestUserGrantSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(grant_es_model.UserGrantAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -97,14 +141,14 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) { } err = grant.AppendEvent(event) case grant_es_model.UserGrantRemoved, grant_es_model.UserGrantCascadeRemoved: - return u.view.DeleteUserGrant(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(event.AggregateID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if err != nil { return err } - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) } func (u *UserGrant) processUser(event *models.Event) (err error) { @@ -119,7 +163,7 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { return err } if len(grants) == 0 { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } user, err := u.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -128,9 +172,9 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { for _, grant := range grants { u.fillUserData(grant, user) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -148,7 +192,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { for _, grant := range grants { u.fillProjectData(grant, project) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectMemberChanged, proj_es_model.ProjectMemberRemoved: member := new(proj_es_model.ProjectMember) member.SetData(event) @@ -158,7 +202,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "PROJECT_GRANT", member.GrantID, member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -169,7 +213,7 @@ func (u *UserGrant) processOrg(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "ORG", "", member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -207,16 +251,16 @@ func (u *UserGrant) processIAMMember(event *models.Event, rolePrefix string, suf } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case iam_es_model.IAMMemberRemoved: member.SetData(event) grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID) if err != nil { return err } - return u.view.DeleteUserGrant(grant.ID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(grant.ID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -252,7 +296,7 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case org_es_model.OrgMemberRemoved, proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectGrantMemberRemoved: @@ -262,18 +306,18 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st return err } if errors.IsNotFound(err) { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if roleSuffix != "" { roleKeys = suffixRoles(roleSuffix, roleKeys) } if grant.RoleKeys == nil { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } grant.RoleKeys = mergeExistingRoles(rolePrefix, roleSuffix, grant.RoleKeys, nil) - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/user_membership.go b/internal/auth/repository/eventsourcing/handler/user_membership.go index 7b231a78b5..ddc6fa6055 100644 --- a/internal/auth/repository/eventsourcing/handler/user_membership.go +++ b/internal/auth/repository/eventsourcing/handler/user_membership.go @@ -2,6 +2,8 @@ package handler import ( "context" + + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" @@ -14,33 +16,72 @@ import ( "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + userMembershipTable = "auth.user_memberships" +) + type UserMembership struct { handler orgEvents *org_event.OrgEventstore projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription } -const ( - userMembershipTable = "auth.user_memberships" -) +func newUserMembership( + handler handler, + orgEvents *org_event.OrgEventstore, + projectEvents *proj_event.ProjectEventstore, +) *UserMembership { + h := &UserMembership{ + handler: handler, + orgEvents: orgEvents, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (m *UserMembership) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (m *UserMembership) ViewModel() string { return userMembershipTable } +func (_ *UserMembership) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate} +} + +func (m *UserMembership) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestUserMembershipSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *UserMembership) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestUserMembershipSequence() + sequence, err := m.view.GetLatestUserMembershipSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -74,14 +115,14 @@ func (m *UserMembership) processIam(event *models.Event) (err error) { } err = member.AppendEvent(event) case iam_es_model.IAMMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillIamDisplayName(member *usr_es_model.UserMembershipView) { @@ -105,16 +146,16 @@ func (m *UserMembership) processOrg(event *models.Event) (err error) { } err = member.AppendEvent(event) case org_es_model.OrgMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event) case org_es_model.OrgChanged: return m.updateOrgName(event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillOrgName(member *usr_es_model.UserMembershipView) (err error) { @@ -145,7 +186,7 @@ func (m *UserMembership) updateOrgName(event *models.Event) error { membership.DisplayName = org.Name } } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } func (m *UserMembership) processProject(event *models.Event) (err error) { @@ -168,7 +209,7 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event) case proj_es_model.ProjectGrantMemberChanged: member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant) if err != nil { @@ -176,20 +217,20 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectGrantMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event) case proj_es_model.ProjectChanged: return m.updateProjectDisplayName(event) case proj_es_model.ProjectRemoved: - return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event) case proj_es_model.ProjectGrantRemoved: - return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembershipView) (err error) { @@ -214,15 +255,15 @@ func (m *UserMembership) updateProjectDisplayName(event *models.Event) error { for _, membership := range memberships { membership.DisplayName = project.Name } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } func (m *UserMembership) processUser(event *models.Event) (err error) { switch event.Type { case model.UserRemoved: - return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/user_session.go b/internal/auth/repository/eventsourcing/handler/user_session.go index b55aacfd63..7ef182f6ae 100644 --- a/internal/auth/repository/eventsourcing/handler/user_session.go +++ b/internal/auth/repository/eventsourcing/handler/user_session.go @@ -1,34 +1,70 @@ package handler import ( + "github.com/caos/logging" req_model "github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/errors" - es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - - "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/user/repository/eventsourcing" user_events "github.com/caos/zitadel/internal/user/repository/eventsourcing" + es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type UserSession struct { - handler - userEvents *user_events.UserEventstore -} - const ( userSessionTable = "auth.user_sessions" ) +type UserSession struct { + handler + userEvents *user_events.UserEventstore + subscription *eventstore.Subscription +} + +func newUserSession( + handler handler, + userEvents *user_events.UserEventstore, +) *UserSession { + h := &UserSession{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (k *UserSession) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (u *UserSession) ViewModel() string { return userSessionTable } +func (_ *UserSession) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate} +} + +func (u *UserSession) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSessionSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *UserSession) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSessionSequence() + sequence, err := u.view.GetLatestUserSessionSequence("") if err != nil { return nil, err } @@ -90,7 +126,7 @@ func (u *UserSession) Reduce(event *models.Event) (err error) { return err } if len(sessions) == 0 { - return u.view.ProcessedUserSessionSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSessionSequence(event) } for _, session := range sessions { if err := session.AppendEvent(event); err != nil { @@ -100,11 +136,11 @@ func (u *UserSession) Reduce(event *models.Event) (err error) { return err } } - return u.view.PutUserSessions(sessions, event.Sequence, event.CreationDate) + return u.view.PutUserSessions(sessions, event) case es_model.UserRemoved: - return u.view.DeleteUserSessions(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUserSessions(event.AggregateID, event) default: - return u.view.ProcessedUserSessionSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSessionSequence(event) } } @@ -124,7 +160,7 @@ func (u *UserSession) updateSession(session *view_model.UserSessionView, event * if err := u.fillUserInfo(session, event.AggregateID); err != nil { return err } - return u.view.PutUserSession(session, event.CreationDate) + return u.view.PutUserSession(session, event) } func (u *UserSession) fillUserInfo(session *view_model.UserSessionView, id string) error { diff --git a/internal/auth/repository/eventsourcing/spooler/lock.go b/internal/auth/repository/eventsourcing/spooler/lock.go index 1a7ea2990e..5fdbc5f0ae 100644 --- a/internal/auth/repository/eventsourcing/spooler/lock.go +++ b/internal/auth/repository/eventsourcing/spooler/lock.go @@ -2,18 +2,13 @@ package spooler import ( "database/sql" - es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) -const ( - lockTable = "auth.locks" -) - type locker struct { dbClient *sql.DB } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) + return nil } diff --git a/internal/auth/repository/eventsourcing/view/application.go b/internal/auth/repository/eventsourcing/view/application.go index 556d31d251..dabe7727cb 100644 --- a/internal/auth/repository/eventsourcing/view/application.go +++ b/internal/auth/repository/eventsourcing/view/application.go @@ -2,13 +2,14 @@ package view import ( "context" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" global_model "github.com/caos/zitadel/internal/model" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -27,28 +28,28 @@ func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) return view.SearchApplications(v.Db, applicationTable, request) } -func (v *View) PutApplication(app *model.ApplicationView, eventTimestamp time.Time) error { +func (v *View) PutApplication(app *model.ApplicationView, event *models.Event) error { err := view.PutApplication(v.Db, applicationTable, app) if err != nil { return err } - return v.ProcessedApplicationSequence(app.Sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) PutApplications(apps []*model.ApplicationView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Event) error { err := view.PutApplications(v.Db, applicationTable, apps...) if err != nil { return err } - return v.ProcessedApplicationSequence(sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) DeleteApplication(appID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) if err != nil { return nil } - return v.ProcessedApplicationSequence(eventSequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } func (v *View) DeleteApplicationsByProjectID(projectID string) error { @@ -56,11 +57,11 @@ func (v *View) DeleteApplicationsByProjectID(projectID string) error { } func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(applicationTable) + return v.latestSequence(applicationTable, "") } -func (v *View) ProcessedApplicationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(applicationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedApplicationSequence(event *models.Event) error { + return v.saveCurrentSequence(applicationTable, event) } func (v *View) UpdateApplicationSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/external_idps.go b/internal/auth/repository/eventsourcing/view/external_idps.go index 1381611037..2928421cdc 100644 --- a/internal/auth/repository/eventsourcing/view/external_idps.go +++ b/internal/auth/repository/eventsourcing/view/external_idps.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -33,44 +33,44 @@ func (v *View) SearchExternalIDPs(request *usr_model.ExternalIDPSearchRequest) ( return view.SearchExternalIDPs(v.Db, externalIDPTable, request) } -func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, event *models.Event) error { err := view.PutExternalIDP(v.Db, externalIDPTable, externalIDP) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) PutExternalIDPs(sequence uint64, eventTimestamp time.Time, externalIDPs ...*model.ExternalIDPView) error { +func (v *View) PutExternalIDPs(event *models.Event, externalIDPs ...*model.ExternalIDPView) error { err := view.PutExternalIDPs(v.Db, externalIDPTable, externalIDPs...) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, event *models.Event) error { err := view.DeleteExternalIDP(v.Db, externalIDPTable, externalUserID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDPsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDPsByUserID(userID string, event *models.Event) error { err := view.DeleteExternalIDPsByUserID(v.Db, externalIDPTable, userID) if err != nil { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) GetLatestExternalIDPSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(externalIDPTable) +func (v *View) GetLatestExternalIDPSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(externalIDPTable, aggregateType) } -func (v *View) ProcessedExternalIDPSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(externalIDPTable, eventSequence, eventTimestamp) +func (v *View) ProcessedExternalIDPSequence(event *models.Event) error { + return v.saveCurrentSequence(externalIDPTable, event) } func (v *View) UpdateExternalIDPSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/idp_configs.go b/internal/auth/repository/eventsourcing/view/idp_configs.go index df591a7a00..65a932b2d1 100644 --- a/internal/auth/repository/eventsourcing/view/idp_configs.go +++ b/internal/auth/repository/eventsourcing/view/idp_configs.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -25,28 +25,28 @@ func (v *View) SearchIDPConfigs(request *iam_model.IDPConfigSearchRequest) ([]*i return view.SearchIDPs(v.Db, idpConfigTable, request) } -func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, event *models.Event) error { err := view.PutIDP(v.Db, idpConfigTable, idp) if err != nil { return err } - return v.ProcessedIDPConfigSequence(sequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) DeleteIDPConfig(idpID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPConfig(idpID string, event *models.Event) error { err := view.DeleteIDP(v.Db, idpConfigTable, idpID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPConfigSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) GetLatestIDPConfigSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpConfigTable) +func (v *View) GetLatestIDPConfigSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpConfigTable, aggregateType) } -func (v *View) ProcessedIDPConfigSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpConfigTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPConfigSequence(event *models.Event) error { + return v.saveCurrentSequence(idpConfigTable, event) } func (v *View) UpdateIDPConfigSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/idp_providers.go b/internal/auth/repository/eventsourcing/view/idp_providers.go index cf81e63745..afd25c15f8 100644 --- a/internal/auth/repository/eventsourcing/view/idp_providers.go +++ b/internal/auth/repository/eventsourcing/view/idp_providers.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "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" - "time" ) const ( @@ -29,44 +29,44 @@ func (v *View) SearchIDPProviders(request *iam_model.IDPProviderSearchRequest) ( return view.SearchIDPProviders(v.Db, idpProviderTable, request) } -func (v *View) PutIDPProvider(provider *model.IDPProviderView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPProvider(provider *model.IDPProviderView, event *models.Event) error { err := view.PutIDPProvider(v.Db, idpProviderTable, provider) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) PutIDPProviders(sequence uint64, eventTimestamp time.Time, providers ...*model.IDPProviderView) error { +func (v *View) PutIDPProviders(event *models.Event, providers ...*model.IDPProviderView) error { err := view.PutIDPProviders(v.Db, idpProviderTable, providers...) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, event *models.Event) error { err := view.DeleteIDPProvider(v.Db, idpProviderTable, aggregateID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteIDPProvidersByAggregateID(v.Db, idpProviderTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) GetLatestIDPProviderSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpProviderTable) +func (v *View) GetLatestIDPProviderSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpProviderTable, aggregateType) } -func (v *View) ProcessedIDPProviderSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpProviderTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPProviderSequence(event *models.Event) error { + return v.saveCurrentSequence(idpProviderTable, event) } func (v *View) UpdateIDPProviderSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/key.go b/internal/auth/repository/eventsourcing/view/key.go index c0c0f7d357..49b65cf38d 100644 --- a/internal/auth/repository/eventsourcing/view/key.go +++ b/internal/auth/repository/eventsourcing/view/key.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/key/repository/view" "github.com/caos/zitadel/internal/key/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,36 +32,36 @@ func (v *View) GetActiveKeySet() ([]*key_model.PublicKey, error) { return key_model.PublicKeysFromKeyView(model.KeyViewsToModel(keys), v.keyAlgorithm) } -func (v *View) PutKeys(privateKey, publicKey *model.KeyView, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) PutKeys(privateKey, publicKey *model.KeyView, event *models.Event) error { err := view.PutKeys(v.Db, keyTable, privateKey, publicKey) if err != nil { return err } - return v.ProcessedKeySequence(eventSequence, eventTimestamp) + return v.ProcessedKeySequence(event) } -func (v *View) DeleteKey(keyID string, private bool, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteKey(keyID string, private bool, event *models.Event) error { err := view.DeleteKey(v.Db, keyTable, keyID, private) if err != nil { return nil } - return v.ProcessedKeySequence(eventSequence, eventTimestamp) + return v.ProcessedKeySequence(event) } -func (v *View) DeleteKeyPair(keyID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteKeyPair(keyID string, event *models.Event) error { err := view.DeleteKeyPair(v.Db, keyTable, keyID) if err != nil { return nil } - return v.ProcessedKeySequence(eventSequence, eventTimestamp) + return v.ProcessedKeySequence(event) } -func (v *View) GetLatestKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(keyTable) +func (v *View) GetLatestKeySequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(keyTable, aggregateType) } -func (v *View) ProcessedKeySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(keyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedKeySequence(event *models.Event) error { + return v.saveCurrentSequence(keyTable, event) } func (v *View) UpdateKeySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/login_policies.go b/internal/auth/repository/eventsourcing/view/login_policies.go index 2da8862129..2687cd8b6d 100644 --- a/internal/auth/repository/eventsourcing/view/login_policies.go +++ b/internal/auth/repository/eventsourcing/view/login_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LoginPolicyByAggregateID(aggregateID string) (*model.LoginPolicyV return view.GetLoginPolicyByAggregateID(v.Db, loginPolicyTable, aggregateID) } -func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, event *models.Event) error { err := view.PutLoginPolicy(v.Db, loginPolicyTable, policy) if err != nil { return err } - return v.ProcessedLoginPolicySequence(sequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) DeleteLoginPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLoginPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLoginPolicy(v.Db, loginPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLoginPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) GetLatestLoginPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(loginPolicyTable) +func (v *View) GetLatestLoginPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(loginPolicyTable, aggregateType) } -func (v *View) ProcessedLoginPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(loginPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLoginPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(loginPolicyTable, event) } func (v *View) UpdateLoginPolicySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/machine_keys.go b/internal/auth/repository/eventsourcing/view/machine_keys.go index ac1a442566..4aab211292 100644 --- a/internal/auth/repository/eventsourcing/view/machine_keys.go +++ b/internal/auth/repository/eventsourcing/view/machine_keys.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,39 +28,39 @@ func (v *View) SearchMachineKeys(request *usr_model.MachineKeySearchRequest) ([] return view.SearchMachineKeys(v.Db, machineKeyTable, request) } -func (v *View) PutMachineKey(key *model.MachineKeyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutMachineKey(key *model.MachineKeyView, event *models.Event) error { err := view.PutMachineKey(v.Db, machineKeyTable, key) if err != nil { return err } - if sequence != 0 { - return v.ProcessedMachineKeySequence(sequence, eventTimestamp) + if event.Sequence != 0 { + return v.ProcessedMachineKeySequence(event) } return nil } -func (v *View) DeleteMachineKey(keyID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) if err != nil { return nil } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) DeleteMachineKeysByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) if err != nil { return nil } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) GetLatestMachineKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(machineKeyTable) +func (v *View) GetLatestMachineKeySequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(machineKeyTable, aggregateType) } -func (v *View) ProcessedMachineKeySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(machineKeyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedMachineKeySequence(event *models.Event) error { + return v.saveCurrentSequence(machineKeyTable, event) } func (v *View) UpdateMachineKeySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/org.go b/internal/auth/repository/eventsourcing/view/org.go index 9f489f5c30..1b9a7c1b4c 100644 --- a/internal/auth/repository/eventsourcing/view/org.go +++ b/internal/auth/repository/eventsourcing/view/org.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/org/model" org_view "github.com/caos/zitadel/internal/org/repository/view" org_model "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,12 +24,12 @@ func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, ui return org_view.SearchOrgs(v.Db, orgTable, req) } -func (v *View) PutOrg(org *org_model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *org_model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -44,10 +44,10 @@ func (v *View) UpdateOrgSpoolerRunTimestamp() error { return v.updateSpoolerRunSequence(orgTable) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } diff --git a/internal/auth/repository/eventsourcing/view/org_iam_policy.go b/internal/auth/repository/eventsourcing/view/org_iam_policy.go index a9ee5a295f..a05592dae3 100644 --- a/internal/auth/repository/eventsourcing/view/org_iam_policy.go +++ b/internal/auth/repository/eventsourcing/view/org_iam_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) OrgIAMPolicyByAggregateID(aggregateID string) (*model.OrgIAMPolic return view.GetOrgIAMPolicyByAggregateID(v.Db, orgIAMPolicyTable, aggregateID) } -func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, event *models.Event) error { err := view.PutOrgIAMPolicy(v.Db, orgIAMPolicyTable, policy) if err != nil { return err } - return v.ProcessedOrgIAMPolicySequence(sequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) DeleteOrgIAMPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgIAMPolicy(aggregateID string, event *models.Event) error { err := view.DeleteOrgIAMPolicy(v.Db, orgIAMPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedOrgIAMPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) GetLatestOrgIAMPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(orgIAMPolicyTable) +func (v *View) GetLatestOrgIAMPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(orgIAMPolicyTable, aggregateType) } -func (v *View) ProcessedOrgIAMPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgIAMPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgIAMPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(orgIAMPolicyTable, event) } func (v *View) UpdateOrgIAMPolicySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/password_complexity_policy.go b/internal/auth/repository/eventsourcing/view/password_complexity_policy.go index 8259c89da0..43ce74f8c1 100644 --- a/internal/auth/repository/eventsourcing/view/password_complexity_policy.go +++ b/internal/auth/repository/eventsourcing/view/password_complexity_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordComplexityPolicyByAggregateID(aggregateID string) (*model return view.GetPasswordComplexityPolicyByAggregateID(v.Db, passwordComplexityPolicyTable, aggregateID) } -func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, event *models.Event) error { err := view.PutPasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordComplexityPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) DeletePasswordComplexityPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordComplexityPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordComplexityPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) GetLatestPasswordComplexityPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordComplexityPolicyTable) +func (v *View) GetLatestPasswordComplexityPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordComplexityPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordComplexityPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordComplexityPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordComplexityPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordComplexityPolicyTable, event) } func (v *View) UpdatePasswordComplexityPolicySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/project_role.go b/internal/auth/repository/eventsourcing/view/project_role.go index 66c287ffa1..5c2751e154 100644 --- a/internal/auth/repository/eventsourcing/view/project_role.go +++ b/internal/auth/repository/eventsourcing/view/project_role.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,32 +32,32 @@ func (v *View) SearchProjectRoles(request *proj_model.ProjectRoleSearchRequest) return view.SearchProjectRoles(v.Db, projectRoleTable, request) } -func (v *View) PutProjectRole(project *model.ProjectRoleView, eventTimestamp time.Time) error { - err := view.PutProjectRole(v.Db, projectRoleTable, project) +func (v *View) PutProjectRole(role *model.ProjectRoleView, event *models.Event) error { + err := view.PutProjectRole(v.Db, projectRoleTable, role) if err != nil { return err } - return v.ProcessedProjectRoleSequence(project.Sequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } -func (v *View) DeleteProjectRole(projectID, orgID, key string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectRole(projectID, orgID, key string, event *models.Event) error { err := view.DeleteProjectRole(v.Db, projectRoleTable, projectID, orgID, key) if err != nil { return nil } - return v.ProcessedProjectRoleSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } func (v *View) DeleteProjectRolesByProjectID(projectID string) error { return view.DeleteProjectRolesByProjectID(v.Db, projectRoleTable, projectID) } -func (v *View) GetLatestProjectRoleSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectRoleTable) +func (v *View) GetLatestProjectRoleSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectRoleTable, aggregateType) } -func (v *View) ProcessedProjectRoleSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectRoleTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectRoleSequence(event *models.Event) error { + return v.saveCurrentSequence(projectRoleTable, event) } func (v *View) UpdateProjectRoleSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/sequence.go b/internal/auth/repository/eventsourcing/view/sequence.go index 4ac2ab80cc..d9eb6c7a64 100644 --- a/internal/auth/repository/eventsourcing/view/sequence.go +++ b/internal/auth/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "auth.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/auth/repository/eventsourcing/view/token.go b/internal/auth/repository/eventsourcing/view/token.go index 4cb7c41844..67644c0865 100644 --- a/internal/auth/repository/eventsourcing/view/token.go +++ b/internal/auth/repository/eventsourcing/view/token.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_view "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -19,60 +19,60 @@ func (v *View) TokensByUserID(userID string) ([]*model.TokenView, error) { return usr_view.TokensByUserID(v.Db, tokenTable, userID) } -func (v *View) PutToken(token *model.TokenView, eventTimestamp time.Time) error { +func (v *View) PutToken(token *model.TokenView, event *models.Event) error { err := usr_view.PutToken(v.Db, tokenTable, token) if err != nil { return err } - return v.ProcessedTokenSequence(token.Sequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) PutTokens(token []*model.TokenView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutTokens(token []*model.TokenView, event *models.Event) error { err := usr_view.PutTokens(v.Db, tokenTable, token...) if err != nil { return err } - return v.ProcessedTokenSequence(sequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteToken(tokenID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteToken(tokenID string, event *models.Event) error { err := usr_view.DeleteToken(v.Db, tokenTable, tokenID) if err != nil { return nil } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteSessionTokens(agentID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteSessionTokens(agentID, userID string, event *models.Event) error { err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, userID) if err != nil { return nil } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteUserTokens(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserTokens(userID string, event *models.Event) error { err := usr_view.DeleteUserTokens(v.Db, tokenTable, userID) if err != nil { return nil } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteApplicationTokens(eventSequence uint64, eventTimestamp time.Time, ids ...string) error { +func (v *View) DeleteApplicationTokens(event *models.Event, ids ...string) error { err := usr_view.DeleteApplicationTokens(v.Db, tokenTable, ids) if err != nil { return nil } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) GetLatestTokenSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(tokenTable) +func (v *View) GetLatestTokenSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(tokenTable, aggregateType) } -func (v *View) ProcessedTokenSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(tokenTable, eventSequence, eventTimestamp) +func (v *View) ProcessedTokenSequence(event *models.Event) error { + return v.saveCurrentSequence(tokenTable, event) } func (v *View) UpdateTokenSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user.go b/internal/auth/repository/eventsourcing/view/user.go index e8b0591d22..f7eaea8505 100644 --- a/internal/auth/repository/eventsourcing/view/user.go +++ b/internal/auth/repository/eventsourcing/view/user.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -52,36 +52,36 @@ func (v *View) UserMFAs(userID string) ([]*usr_model.MultiFactor, error) { return view.UserMFAs(v.Db, userTable, userID) } -func (v *View) PutUser(user *model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUser(user *model.UserView, event *models.Event) error { err := view.PutUser(v.Db, userTable, user) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) PutUsers(users []*model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUsers(users []*model.UserView, event *models.Event) error { err := view.PutUsers(v.Db, userTable, users...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) DeleteUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) if err != nil { return nil } - return v.ProcessedUserSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) GetLatestUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userTable) +func (v *View) GetLatestUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userTable, aggregateType) } -func (v *View) ProcessedUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSequence(event *models.Event) error { + return v.saveCurrentSequence(userTable, event) } func (v *View) UpdateUserSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user_grant.go b/internal/auth/repository/eventsourcing/view/user_grant.go index 230e9f5c8a..4121a210ff 100644 --- a/internal/auth/repository/eventsourcing/view/user_grant.go +++ b/internal/auth/repository/eventsourcing/view/user_grant.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" "github.com/caos/zitadel/internal/usergrant/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -36,36 +36,36 @@ func (v *View) SearchUserGrants(request *grant_model.UserGrantSearchRequest) ([] return view.SearchUserGrants(v.Db, userGrantTable, request) } -func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) error { err := view.PutUserGrant(v.Db, userGrantTable, grant) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) PutUserGrants(grants []*model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrants(grants []*model.UserGrantView, event *models.Event) error { err := view.PutUserGrants(v.Db, userGrantTable, grants...) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) DeleteUserGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) if err != nil { return nil } - return v.ProcessedUserGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) GetLatestUserGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userGrantTable) +func (v *View) GetLatestUserGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userGrantTable, aggregateType) } -func (v *View) ProcessedUserGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userGrantTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(userGrantTable, event) } func (v *View) UpdateUserGrantSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user_membership.go b/internal/auth/repository/eventsourcing/view/user_membership.go index 84607584ab..d13ec0ec9e 100644 --- a/internal/auth/repository/eventsourcing/view/user_membership.go +++ b/internal/auth/repository/eventsourcing/view/user_membership.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,60 +28,60 @@ func (v *View) SearchUserMemberships(request *usr_model.UserMembershipSearchRequ return view.SearchUserMemberships(v.Db, userMembershipTable, request) } -func (v *View) PutUserMembership(membership *model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserMembership(membership *model.UserMembershipView, event *models.Event) error { err := view.PutUserMembership(v.Db, userMembershipTable, membership) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, event *models.Event) error { err := view.PutUserMemberships(v.Db, userMembershipTable, memberships...) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, event *models.Event) error { err := view.DeleteUserMembership(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByUserID(userID string, event *models.Event) error { err := view.DeleteUserMembershipsByUserID(v.Db, userMembershipTable, userID) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateIDAndObjectID(v.Db, userMembershipTable, aggregateID, objectID) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) GetLatestUserMembershipSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userMembershipTable) +func (v *View) GetLatestUserMembershipSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userMembershipTable, aggregateType) } -func (v *View) ProcessedUserMembershipSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userMembershipTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserMembershipSequence(event *models.Event) error { + return v.saveCurrentSequence(userMembershipTable, event) } func (v *View) UpdateUserMembershipSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user_session.go b/internal/auth/repository/eventsourcing/view/user_session.go index 805cc04aef..f0ac1ae3cd 100644 --- a/internal/auth/repository/eventsourcing/view/user_session.go +++ b/internal/auth/repository/eventsourcing/view/user_session.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -27,36 +27,36 @@ func (v *View) ActiveUserSessions() ([]*model.UserSessionView, error) { return view.ActiveUserSessions(v.Db, userSessionTable) } -func (v *View) PutUserSession(userSession *model.UserSessionView, eventTimestamp time.Time) error { +func (v *View) PutUserSession(userSession *model.UserSessionView, event *models.Event) error { err := view.PutUserSession(v.Db, userSessionTable, userSession) if err != nil { return err } - return v.ProcessedUserSessionSequence(userSession.Sequence, eventTimestamp) + return v.ProcessedUserSessionSequence(event) } -func (v *View) PutUserSessions(userSession []*model.UserSessionView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserSessions(userSession []*model.UserSessionView, event *models.Event) error { err := view.PutUserSessions(v.Db, userSessionTable, userSession...) if err != nil { return err } - return v.ProcessedUserSessionSequence(sequence, eventTimestamp) + return v.ProcessedUserSessionSequence(event) } -func (v *View) DeleteUserSessions(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserSessions(userID string, event *models.Event) error { err := view.DeleteUserSessions(v.Db, userSessionTable, userID) if err != nil { return nil } - return v.ProcessedUserSessionSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSessionSequence(event) } -func (v *View) GetLatestUserSessionSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userSessionTable) +func (v *View) GetLatestUserSessionSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userSessionTable, aggregateType) } -func (v *View) ProcessedUserSessionSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userSessionTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSessionSequence(event *models.Event) error { + return v.saveCurrentSequence(userSessionTable, event) } func (v *View) UpdateUserSessionSpoolerRunTimestamp() error { diff --git a/internal/authz/repository/eventsourcing/handler/application.go b/internal/authz/repository/eventsourcing/handler/application.go index 4b5773607e..fcb84ee675 100644 --- a/internal/authz/repository/eventsourcing/handler/application.go +++ b/internal/authz/repository/eventsourcing/handler/application.go @@ -3,27 +3,61 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Application struct { - handler -} - const ( applicationTable = "authz.applications" ) +type Application struct { + handler + subscription *eventstore.Subscription +} + +func newApplication(handler handler) *Application { + h := &Application{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *Application) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (a *Application) ViewModel() string { return applicationTable } +func (a *Application) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (a *Application) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := a.view.GetLatestApplicationSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (a *Application) EventQuery() (*models.SearchQuery, error) { - sequence, err := a.view.GetLatestApplicationSequence() + sequence, err := a.view.GetLatestApplicationSequence("") if err != nil { return nil, err } @@ -54,14 +88,14 @@ func (a *Application) Reduce(event *models.Event) (err error) { if err != nil { return err } - return a.view.DeleteApplication(app.ID, event.Sequence, event.CreationDate) + return a.view.DeleteApplication(app.ID, event) default: - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } if err != nil { return err } - return a.view.PutApplication(app, event.CreationDate) + return a.view.PutApplication(app, event) } func (a *Application) OnError(event *models.Event, spoolerError error) error { diff --git a/internal/authz/repository/eventsourcing/handler/handler.go b/internal/authz/repository/eventsourcing/handler/handler.go index 31274d77ab..2d5f816c97 100644 --- a/internal/authz/repository/eventsourcing/handler/handler.go +++ b/internal/authz/repository/eventsourcing/handler/handler.go @@ -23,29 +23,35 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { - IamEvents *iam_events.IAMEventstore + IAMEvents *iam_events.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { return []query.Handler{ - &UserGrant{ - handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, - eventstore: eventstore, - iamID: systemDefaults.IamID, - iamEvents: repos.IamEvents, - }, - &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}}, - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, + newUserGrant( + handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, + repos.IAMEvents, + systemDefaults.IamID), + newApplication( + handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}), + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } diff --git a/internal/authz/repository/eventsourcing/handler/org.go b/internal/authz/repository/eventsourcing/handler/org.go index e487831f02..34cc1a69e7 100644 --- a/internal/authz/repository/eventsourcing/handler/org.go +++ b/internal/authz/repository/eventsourcing/handler/org.go @@ -3,27 +3,62 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "authz.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *Org) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (_ *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (o *Org) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } @@ -53,10 +88,10 @@ func (o *Org) Reduce(event *es_models.Event) error { return err } default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/authz/repository/eventsourcing/handler/user_grant.go b/internal/authz/repository/eventsourcing/handler/user_grant.go index 58cd01d31c..f4a8d7c2dd 100644 --- a/internal/authz/repository/eventsourcing/handler/user_grant.go +++ b/internal/authz/repository/eventsourcing/handler/user_grant.go @@ -11,6 +11,7 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing" @@ -20,22 +21,59 @@ import ( view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model" ) -type UserGrant struct { - handler - eventstore eventstore.Eventstore - iamEvents *iam_events.IAMEventstore - iamID string - iamProjectID string -} - const ( userGrantTable = "authz.user_grants" ) +type UserGrant struct { + handler + iamEvents *iam_events.IAMEventstore + iamID string + iamProjectID string + subscription *eventstore.Subscription +} + +func newUserGrant( + handler handler, + iamEvents *iam_events.IAMEventstore, + iamID string, +) *UserGrant { + h := &UserGrant{ + handler: handler, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *UserGrant) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (u *UserGrant) ViewModel() string { return userGrantTable } +func (_ *UserGrant) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate} +} + +func (u *UserGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { if u.iamProjectID == "" { err := u.setIamProjectID() @@ -43,7 +81,7 @@ func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { return nil, err } } - sequence, err := u.view.GetLatestUserGrantSequence() + sequence, err := u.view.GetLatestUserGrantSequence("") if err != nil { return nil, err } @@ -75,7 +113,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "PROJECT_GRANT", member.GrantID, member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -86,7 +124,7 @@ func (u *UserGrant) processOrg(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "ORG", "", member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -124,16 +162,16 @@ func (u *UserGrant) processIAMMember(event *models.Event, rolePrefix string, suf } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case iam_es_model.IAMMemberRemoved: member.SetData(event) grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID) if err != nil { return err } - return u.view.DeleteUserGrant(grant.ID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(grant.ID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -169,7 +207,7 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case org_es_model.OrgMemberRemoved, proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectGrantMemberRemoved: @@ -179,18 +217,18 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st return err } if errors.IsNotFound(err) { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if roleSuffix != "" { roleKeys = suffixRoles(roleSuffix, roleKeys) } if grant.RoleKeys == nil { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } grant.RoleKeys = mergeExistingRoles(rolePrefix, roleSuffix, grant.RoleKeys, nil) - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } diff --git a/internal/authz/repository/eventsourcing/repository.go b/internal/authz/repository/eventsourcing/repository.go index b39ad5e2e8..e42a152cdb 100644 --- a/internal/authz/repository/eventsourcing/repository.go +++ b/internal/authz/repository/eventsourcing/repository.go @@ -2,6 +2,7 @@ package eventsourcing import ( "context" + es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing" "github.com/caos/zitadel/internal/api/authz" @@ -76,7 +77,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults) (* if err != nil { return nil, err } - repos := handler.EventstoreRepos{IamEvents: iam} + repos := handler.EventstoreRepos{IAMEvents: iam} spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, repos, systemDefaults) return &EsRepository{ diff --git a/internal/authz/repository/eventsourcing/spooler/lock.go b/internal/authz/repository/eventsourcing/spooler/lock.go index 6100f470d3..0397382276 100644 --- a/internal/authz/repository/eventsourcing/spooler/lock.go +++ b/internal/authz/repository/eventsourcing/spooler/lock.go @@ -2,7 +2,6 @@ package spooler import ( "database/sql" - es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) @@ -15,5 +14,5 @@ type locker struct { } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) + return nil } diff --git a/internal/authz/repository/eventsourcing/view/application.go b/internal/authz/repository/eventsourcing/view/application.go index a8e772b58d..07448bf64f 100644 --- a/internal/authz/repository/eventsourcing/view/application.go +++ b/internal/authz/repository/eventsourcing/view/application.go @@ -2,8 +2,8 @@ package view import ( "context" - "time" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" @@ -34,28 +34,28 @@ func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) return view.SearchApplications(v.Db, applicationTable, request) } -func (v *View) PutApplication(project *model.ApplicationView, eventTimestamp time.Time) error { +func (v *View) PutApplication(project *model.ApplicationView, event *models.Event) error { err := view.PutApplication(v.Db, applicationTable, project) if err != nil { return err } - return v.ProcessedApplicationSequence(project.Sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) DeleteApplication(appID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) if err != nil { return nil } - return v.ProcessedApplicationSequence(eventSequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(applicationTable) +func (v *View) GetLatestApplicationSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(applicationTable, aggregateType) } -func (v *View) ProcessedApplicationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(applicationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedApplicationSequence(event *models.Event) error { + return v.saveCurrentSequence(applicationTable, event) } func (v *View) UpdateApplicationSpoolerRunTimestamp() error { diff --git a/internal/authz/repository/eventsourcing/view/org.go b/internal/authz/repository/eventsourcing/view/org.go index f886123b2e..6d27b5b071 100644 --- a/internal/authz/repository/eventsourcing/view/org.go +++ b/internal/authz/repository/eventsourcing/view/org.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/org/model" org_view "github.com/caos/zitadel/internal/org/repository/view" org_model "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -20,12 +20,12 @@ func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, ui return org_view.SearchOrgs(v.Db, orgTable, req) } -func (v *View) PutOrg(org *org_model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *org_model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -36,12 +36,12 @@ func (v *View) ProcessedOrgFailedEvent(failedEvent *repository.FailedEvent) erro return v.saveFailedEvent(failedEvent) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } func (v *View) UpdateOrgSpoolerRunTimestamp() error { diff --git a/internal/authz/repository/eventsourcing/view/sequence.go b/internal/authz/repository/eventsourcing/view/sequence.go index ad628fbf7b..42e6e20bfd 100644 --- a/internal/authz/repository/eventsourcing/view/sequence.go +++ b/internal/authz/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "authz.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/authz/repository/eventsourcing/view/token.go b/internal/authz/repository/eventsourcing/view/token.go index bab80f238a..da411da937 100644 --- a/internal/authz/repository/eventsourcing/view/token.go +++ b/internal/authz/repository/eventsourcing/view/token.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_view "github.com/caos/zitadel/internal/user/repository/view" usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -15,36 +15,36 @@ func (v *View) TokenByID(tokenID string) (*usr_view_model.TokenView, error) { return usr_view.TokenByID(v.Db, tokenTable, tokenID) } -func (v *View) PutToken(token *usr_view_model.TokenView, eventTimestamp time.Time) error { +func (v *View) PutToken(token *usr_view_model.TokenView, event *models.Event) error { err := usr_view.PutToken(v.Db, tokenTable, token) if err != nil { return err } - return v.ProcessedTokenSequence(token.Sequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteToken(tokenID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteToken(tokenID string, event *models.Event) error { err := usr_view.DeleteToken(v.Db, tokenTable, tokenID) if err != nil { return nil } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteSessionTokens(agentID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteSessionTokens(agentID, userID string, event *models.Event) error { err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, userID) if err != nil { return nil } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) GetLatestTokenSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(tokenTable) +func (v *View) GetLatestTokenSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(tokenTable, aggregateType) } -func (v *View) ProcessedTokenSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(tokenTable, eventSequence, eventTimestamp) +func (v *View) ProcessedTokenSequence(event *models.Event) error { + return v.saveCurrentSequence(tokenTable, event) } func (v *View) UpdateTokenSpoolerRunTimestamp() error { diff --git a/internal/authz/repository/eventsourcing/view/user_grant.go b/internal/authz/repository/eventsourcing/view/user_grant.go index 68154dc3c9..119ab9ae23 100644 --- a/internal/authz/repository/eventsourcing/view/user_grant.go +++ b/internal/authz/repository/eventsourcing/view/user_grant.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" "github.com/caos/zitadel/internal/usergrant/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,28 +32,28 @@ func (v *View) SearchUserGrants(request *grant_model.UserGrantSearchRequest) ([] return view.SearchUserGrants(v.Db, userGrantTable, request) } -func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) error { err := view.PutUserGrant(v.Db, userGrantTable, grant) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) DeleteUserGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) if err != nil { return nil } - return v.ProcessedUserGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) GetLatestUserGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userGrantTable) +func (v *View) GetLatestUserGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userGrantTable, aggregateType) } -func (v *View) ProcessedUserGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userGrantTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(userGrantTable, event) } func (v *View) UpdateUserGrantSpoolerRunTimestamp() error { diff --git a/internal/eventstore/eventstore.go b/internal/eventstore/eventstore.go index 138599b856..b4c0e5a745 100644 --- a/internal/eventstore/eventstore.go +++ b/internal/eventstore/eventstore.go @@ -14,6 +14,7 @@ type Eventstore interface { PushAggregates(ctx context.Context, aggregates ...*models.Aggregate) error FilterEvents(ctx context.Context, searchQuery *models.SearchQuery) (events []*models.Event, err error) LatestSequence(ctx context.Context, searchQuery *models.SearchQueryFactory) (uint64, error) + Subscribe(aggregates ...models.AggregateType) *Subscription } var _ Eventstore = (*eventstore)(nil) @@ -42,6 +43,8 @@ func (es *eventstore) PushAggregates(ctx context.Context, aggregates ...*models. if err != nil { return err } + + go notify(aggregates) return nil } diff --git a/internal/eventstore/mock/eventstore.mock.go b/internal/eventstore/mock/eventstore.mock.go index 18fb7a949a..fbe1990856 100644 --- a/internal/eventstore/mock/eventstore.mock.go +++ b/internal/eventstore/mock/eventstore.mock.go @@ -6,6 +6,7 @@ package mock import ( context "context" + eventstore "github.com/caos/zitadel/internal/eventstore" models "github.com/caos/zitadel/internal/eventstore/models" gomock "github.com/golang/mock/gomock" reflect "reflect" @@ -110,3 +111,21 @@ func (mr *MockEventstoreMockRecorder) PushAggregates(arg0 interface{}, arg1 ...i varargs := append([]interface{}{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushAggregates", reflect.TypeOf((*MockEventstore)(nil).PushAggregates), varargs...) } + +// Subscribe mocks base method +func (m *MockEventstore) Subscribe(arg0 ...models.AggregateType) *eventstore.Subscription { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range arg0 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Subscribe", varargs...) + ret0, _ := ret[0].(*eventstore.Subscription) + return ret0 +} + +// Subscribe indicates an expected call of Subscribe +func (mr *MockEventstoreMockRecorder) Subscribe(arg0 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockEventstore)(nil).Subscribe), arg0...) +} diff --git a/internal/eventstore/models/aggregate.go b/internal/eventstore/models/aggregate.go index 66026b05d5..a0a5c88779 100644 --- a/internal/eventstore/models/aggregate.go +++ b/internal/eventstore/models/aggregate.go @@ -27,6 +27,10 @@ type Aggregate struct { Precondition *precondition } +func (a *Aggregate) Type() AggregateType { + return a.typ +} + type precondition struct { Query *SearchQuery Validation func(...*Event) error diff --git a/internal/eventstore/models/search_query.go b/internal/eventstore/models/search_query.go index 3530db46a8..06cbf97f1d 100644 --- a/internal/eventstore/models/search_query.go +++ b/internal/eventstore/models/search_query.go @@ -11,7 +11,8 @@ type SearchQueryFactory struct { desc bool aggregateTypes []AggregateType aggregateIDs []string - eventSequence uint64 + sequenceFrom uint64 + sequenceTo uint64 eventTypes []EventType resourceOwner string } @@ -51,7 +52,11 @@ func FactoryFromSearchQuery(query *SearchQuery) *SearchQueryFactory { factory = factory.AggregateIDs(aggregateIDs...) } case Field_LatestSequence: - factory = factory.SequenceGreater(filter.value.(uint64)) + if filter.operation == Operation_Greater { + factory = factory.SequenceGreater(filter.value.(uint64)) + } else { + factory = factory.SequenceLess(filter.value.(uint64)) + } case Field_ResourceOwner: factory = factory.ResourceOwner(filter.value.(string)) case Field_EventType: @@ -82,7 +87,12 @@ func (factory *SearchQueryFactory) Limit(limit uint64) *SearchQueryFactory { } func (factory *SearchQueryFactory) SequenceGreater(sequence uint64) *SearchQueryFactory { - factory.eventSequence = sequence + factory.sequenceFrom = sequence + return factory +} + +func (factory *SearchQueryFactory) SequenceLess(sequence uint64) *SearchQueryFactory { + factory.sequenceTo = sequence return factory } @@ -128,7 +138,8 @@ func (factory *SearchQueryFactory) Build() (*searchQuery, error) { for _, f := range []func() *Filter{ factory.aggregateIDFilter, - factory.eventSequenceFilter, + factory.sequenceFromFilter, + factory.sequenceToFilter, factory.eventTypeFilter, factory.resourceOwnerFilter, } { @@ -172,15 +183,26 @@ func (factory *SearchQueryFactory) aggregateTypeFilter() *Filter { return NewFilter(Field_AggregateType, factory.aggregateTypes, Operation_In) } -func (factory *SearchQueryFactory) eventSequenceFilter() *Filter { - if factory.eventSequence == 0 { +func (factory *SearchQueryFactory) sequenceFromFilter() *Filter { + if factory.sequenceFrom == 0 { return nil } sortOrder := Operation_Greater if factory.desc { sortOrder = Operation_Less } - return NewFilter(Field_LatestSequence, factory.eventSequence, sortOrder) + return NewFilter(Field_LatestSequence, factory.sequenceFrom, sortOrder) +} + +func (factory *SearchQueryFactory) sequenceToFilter() *Filter { + if factory.sequenceTo == 0 { + return nil + } + sortOrder := Operation_Less + if factory.desc { + sortOrder = Operation_Greater + } + return NewFilter(Field_LatestSequence, factory.sequenceTo, sortOrder) } func (factory *SearchQueryFactory) resourceOwnerFilter() *Filter { diff --git a/internal/eventstore/models/search_query_old.go b/internal/eventstore/models/search_query_old.go index c803fac64b..0d6e1ebc6c 100644 --- a/internal/eventstore/models/search_query_old.go +++ b/internal/eventstore/models/search_query_old.go @@ -58,6 +58,12 @@ func (q *SearchQuery) LatestSequenceFilter(sequence uint64) *SearchQuery { return q.setFilter(NewFilter(Field_LatestSequence, sequence, sortOrder)) } +func (q *SearchQuery) SequenceBetween(from, to uint64) *SearchQuery { + q.setFilter(NewFilter(Field_LatestSequence, from, Operation_Greater)) + q.setFilter(NewFilter(Field_LatestSequence, to, Operation_Less)) + return q +} + func (q *SearchQuery) ResourceOwnerFilter(resourceOwner string) *SearchQuery { return q.setFilter(NewFilter(Field_ResourceOwner, resourceOwner, Operation_Equals)) } diff --git a/internal/eventstore/models/search_query_test.go b/internal/eventstore/models/search_query_test.go index ac9380b4d9..b52f6490cf 100644 --- a/internal/eventstore/models/search_query_test.go +++ b/internal/eventstore/models/search_query_test.go @@ -103,7 +103,7 @@ func TestSearchQueryFactorySetters(t *testing.T) { setters: []func(*SearchQueryFactory) *SearchQueryFactory{testSetSequence(90)}, }, res: &SearchQueryFactory{ - eventSequence: 90, + sequenceFrom: 90, }, }, { diff --git a/internal/eventstore/query/handler.go b/internal/eventstore/query/handler.go index 36bba2547e..07f4f6ce3d 100755 --- a/internal/eventstore/query/handler.go +++ b/internal/eventstore/query/handler.go @@ -1,8 +1,11 @@ package query import ( + "context" "time" + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" ) @@ -14,4 +17,42 @@ type Handler interface { OnSuccess() error MinimumCycleDuration() time.Duration QueryLimit() uint64 + + AggregateTypes() []models.AggregateType + CurrentSequence(*models.Event) (uint64, error) + Eventstore() eventstore.Eventstore +} + +func ReduceEvent(handler Handler, event *models.Event) { + currentSequence, err := handler.CurrentSequence(event) + if err != nil { + logging.Log("HANDL-BmpkC").WithError(err).Warn("unable to get current sequence") + return + } + if event.PreviousSequence > currentSequence { + searchQuery := models.NewSearchQuery(). + AggregateTypeFilter(handler.AggregateTypes()...). + SequenceBetween(currentSequence, event.PreviousSequence) + + events, err := handler.Eventstore().FilterEvents(context.Background(), searchQuery) + if err != nil { + logging.LogWithFields("HANDL-L6YH1", "seq", event.Sequence).Warn("filter failed") + return + } + for _, previousEvent := range events { + //if other process already updated view + //TODO: correct? + if event.PreviousSequence > previousEvent.Sequence { + continue + } + err = handler.Reduce(previousEvent) + logging.LogWithFields("HANDL-V42TI", "seq", previousEvent.Sequence).OnError(err).Warn("reduce failed") + return + } + } else if event.PreviousSequence > 0 && event.PreviousSequence < currentSequence { + logging.LogWithFields("HANDL-w9Bdy", "previousSeq", event.PreviousSequence, "currentSeq", currentSequence).Debug("already processed") + return + } + err = handler.Reduce(event) + logging.LogWithFields("HANDL-wQDL2", "seq", event.Sequence).OnError(err).Warn("reduce failed") } diff --git a/internal/eventstore/spooler/spooler.go b/internal/eventstore/spooler/spooler.go index fb480f7916..723fbe9229 100644 --- a/internal/eventstore/spooler/spooler.go +++ b/internal/eventstore/spooler/spooler.go @@ -167,7 +167,7 @@ func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID s func HandleError(event *models.Event, failedErr error, latestFailedEvent func(sequence uint64) (*repository.FailedEvent, error), processFailedEvent func(*repository.FailedEvent) error, - processSequence func(uint64, time.Time) error, errorCountUntilSkip uint64) error { + processSequence func(*models.Event) error, errorCountUntilSkip uint64) error { failedEvent, err := latestFailedEvent(event.Sequence) if err != nil { return err @@ -179,7 +179,7 @@ func HandleError(event *models.Event, failedErr error, return err } if errorCountUntilSkip <= failedEvent.FailureCount { - return processSequence(event.Sequence, event.CreationDate) + return processSequence(event) } return nil } diff --git a/internal/eventstore/spooler/spooler_test.go b/internal/eventstore/spooler/spooler_test.go index 721ad74e26..13c6737aa0 100644 --- a/internal/eventstore/spooler/spooler_test.go +++ b/internal/eventstore/spooler/spooler_test.go @@ -24,6 +24,18 @@ type testHandler struct { bulkLimit uint64 } +func (h *testHandler) AggregateTypes() []models.AggregateType { + return nil +} + +func (h *testHandler) CurrentSequence(event *models.Event) (uint64, error) { + return 0, nil +} + +func (h *testHandler) Eventstore() eventstore.Eventstore { + return nil +} + func (h *testHandler) ViewModel() string { return h.viewModel } @@ -55,6 +67,8 @@ type eventstoreStub struct { err error } +func (es *eventstoreStub) Subscribe(...models.AggregateType) *eventstore.Subscription { return nil } + func (es *eventstoreStub) Health(ctx context.Context) error { return nil } @@ -432,7 +446,7 @@ func TestHandleError(t *testing.T) { func(*repository.FailedEvent) error { return nil }, - func(uint64, time.Time) error { + func(*models.Event) error { processedSequence = true return nil }, diff --git a/internal/eventstore/subscription.go b/internal/eventstore/subscription.go new file mode 100644 index 0000000000..37aa0dd45c --- /dev/null +++ b/internal/eventstore/subscription.go @@ -0,0 +1,73 @@ +package eventstore + +import ( + "sync" + + "github.com/caos/zitadel/internal/eventstore/models" +) + +var ( + subscriptions map[models.AggregateType][]*Subscription = map[models.AggregateType][]*Subscription{} + subsMutext sync.Mutex +) + +type Subscription struct { + Events chan *models.Event + aggregates []models.AggregateType +} + +func (es *eventstore) Subscribe(aggregates ...models.AggregateType) *Subscription { + events := make(chan *models.Event, 100) + sub := &Subscription{ + Events: events, + aggregates: aggregates, + } + + subsMutext.Lock() + defer subsMutext.Unlock() + + for _, aggregate := range aggregates { + _, ok := subscriptions[aggregate] + if !ok { + subscriptions[aggregate] = make([]*Subscription, 0, 1) + } + subscriptions[aggregate] = append(subscriptions[aggregate], sub) + } + + return sub +} + +func notify(aggregates []*models.Aggregate) { + subsMutext.Lock() + defer subsMutext.Unlock() + for _, aggregate := range aggregates { + subs, ok := subscriptions[aggregate.Type()] + if !ok { + continue + } + for _, sub := range subs { + for _, event := range aggregate.Events { + sub.Events <- event + } + } + } +} + +func (s *Subscription) Unsubscribe() { + subsMutext.Lock() + defer subsMutext.Unlock() + for _, aggregate := range s.aggregates { + subs, ok := subscriptions[aggregate] + if !ok { + continue + } + for i := len(subs) - 1; i >= 0; i-- { + if subs[i] == s { + subs[i] = subs[len(subs)-1] + subs[len(subs)-1] = nil + subs = subs[:len(subs)-1] + } + } + } + close(s.Events) +} diff --git a/internal/iam/repository/eventsourcing/model/login_policy.go b/internal/iam/repository/eventsourcing/model/login_policy.go index 7b28b18b96..2dbfea499a 100644 --- a/internal/iam/repository/eventsourcing/model/login_policy.go +++ b/internal/iam/repository/eventsourcing/model/login_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/org_iam_policy.go b/internal/iam/repository/eventsourcing/model/org_iam_policy.go index 3056750803..1612d84c12 100644 --- a/internal/iam/repository/eventsourcing/model/org_iam_policy.go +++ b/internal/iam/repository/eventsourcing/model/org_iam_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/password_age_policy.go b/internal/iam/repository/eventsourcing/model/password_age_policy.go index 86c65247d9..7d4b96d4d4 100644 --- a/internal/iam/repository/eventsourcing/model/password_age_policy.go +++ b/internal/iam/repository/eventsourcing/model/password_age_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/password_complexity_policy.go b/internal/iam/repository/eventsourcing/model/password_complexity_policy.go index 37dc2db784..148db9f422 100644 --- a/internal/iam/repository/eventsourcing/model/password_complexity_policy.go +++ b/internal/iam/repository/eventsourcing/model/password_complexity_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/password_lockout_policy.go b/internal/iam/repository/eventsourcing/model/password_lockout_policy.go index e4878c82f9..15c78c45ac 100644 --- a/internal/iam/repository/eventsourcing/model/password_lockout_policy.go +++ b/internal/iam/repository/eventsourcing/model/password_lockout_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/management/repository/eventsourcing/eventstore/org.go b/internal/management/repository/eventsourcing/eventstore/org.go index 6bc11af854..74c37908b7 100644 --- a/internal/management/repository/eventsourcing/eventstore/org.go +++ b/internal/management/repository/eventsourcing/eventstore/org.go @@ -108,7 +108,7 @@ func (repo *OrgRepository) GetMyOrgIamPolicy(ctx context.Context) (*iam_model.Or func (repo *OrgRepository) SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.Queries = append(request.Queries, &org_model.OrgDomainSearchQuery{Key: org_model.OrgDomainSearchKeyOrgID, Method: global_model.SearchMethodEquals, Value: authz.GetCtxData(ctx).OrgID}) - sequence, sequenceErr := repo.View.GetLatestOrgDomainSequence() + sequence, sequenceErr := repo.View.GetLatestOrgDomainSequence("") logging.Log("EVENT-SLowp").OnError(sequenceErr).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest org domain sequence") domains, count, err := repo.View.SearchOrgDomains(request) if err != nil { @@ -205,7 +205,7 @@ func (repo *OrgRepository) RemoveMyOrgMember(ctx context.Context, userID string) func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.Queries[len(request.Queries)-1] = &org_model.OrgMemberSearchQuery{Key: org_model.OrgMemberSearchKeyOrgID, Method: global_model.SearchMethodEquals, Value: authz.GetCtxData(ctx).OrgID} - sequence, sequenceErr := repo.View.GetLatestOrgMemberSequence() + sequence, sequenceErr := repo.View.GetLatestOrgMemberSequence("") logging.Log("EVENT-Smu3d").OnError(sequenceErr).Warn("could not read latest org member sequence") members, count, err := repo.View.SearchOrgMembers(request) if err != nil { @@ -292,7 +292,7 @@ func (repo *OrgRepository) SearchIDPConfigs(ctx context.Context, request *iam_mo request.EnsureLimit(repo.SearchLimit) request.AppendMyOrgQuery(authz.GetCtxData(ctx).OrgID, repo.SystemDefaults.IamID) - sequence, sequenceErr := repo.View.GetLatestIDPConfigSequence() + sequence, sequenceErr := repo.View.GetLatestIDPConfigSequence("") logging.Log("EVENT-Dk8si").OnError(sequenceErr).Warn("could not read latest idp config sequence") idps, count, err := repo.View.SearchIDPConfigs(request) if err != nil { @@ -414,7 +414,7 @@ func (repo *OrgRepository) SearchIDPProviders(ctx context.Context, request *iam_ request.AppendAggregateIDQuery(authz.GetCtxData(ctx).OrgID) } request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestIDPProviderSequence() + sequence, sequenceErr := repo.View.GetLatestIDPProviderSequence("") logging.Log("EVENT-Tuiks").OnError(sequenceErr).Warn("could not read latest iam sequence") providers, count, err := repo.View.SearchIDPProviders(request) if err != nil { diff --git a/internal/management/repository/eventsourcing/eventstore/project.go b/internal/management/repository/eventsourcing/eventstore/project.go index 5a9343ff11..2d949a3f95 100644 --- a/internal/management/repository/eventsourcing/eventstore/project.go +++ b/internal/management/repository/eventsourcing/eventstore/project.go @@ -118,7 +118,7 @@ func (repo *ProjectRepo) RemoveProject(ctx context.Context, projectID string) er func (repo *ProjectRepo) SearchProjects(ctx context.Context, request *proj_model.ProjectViewSearchRequest) (*proj_model.ProjectViewSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectSequence() + sequence, sequenceErr := repo.View.GetLatestProjectSequence("") logging.Log("EVENT-Edc56").OnError(sequenceErr).Warn("could not read latest project sequence") permissions := authz.GetRequestPermissionsFromCtx(ctx) @@ -198,7 +198,7 @@ func (repo *ProjectRepo) RemoveProjectMember(ctx context.Context, projectID, use func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj_model.ProjectMemberSearchRequest) (*proj_model.ProjectMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectMemberSequence() + sequence, sequenceErr := repo.View.GetLatestProjectMemberSequence("") logging.Log("EVENT-3dgt6").OnError(sequenceErr).Warn("could not read latest project member sequence") members, count, err := repo.View.SearchProjectMembers(request) if err != nil { @@ -270,7 +270,7 @@ func (repo *ProjectRepo) RemoveProjectRole(ctx context.Context, projectID, key s func (repo *ProjectRepo) SearchProjectRoles(ctx context.Context, projectID string, request *proj_model.ProjectRoleSearchRequest) (*proj_model.ProjectRoleSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.AppendProjectQuery(projectID) - sequence, sequenceErr := repo.View.GetLatestProjectRoleSequence() + sequence, sequenceErr := repo.View.GetLatestProjectRoleSequence("") logging.Log("LSp0d-47suf").OnError(sequenceErr).Warn("could not read latest project role sequence") roles, count, err := repo.View.SearchProjectRoles(request) if err != nil { @@ -366,7 +366,7 @@ func (repo *ProjectRepo) RemoveApplication(ctx context.Context, projectID, appID func (repo *ProjectRepo) SearchApplications(ctx context.Context, request *proj_model.ApplicationSearchRequest) (*proj_model.ApplicationSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestApplicationSequence() + sequence, sequenceErr := repo.View.GetLatestApplicationSequence("") logging.Log("EVENT-SKe8s").OnError(sequenceErr).Warn("could not read latest application sequence") apps, count, err := repo.View.SearchApplications(request) if err != nil { @@ -423,7 +423,7 @@ func (repo *ProjectRepo) ProjectGrantByID(ctx context.Context, grantID string) ( func (repo *ProjectRepo) SearchProjectGrants(ctx context.Context, request *proj_model.ProjectGrantViewSearchRequest) (*proj_model.ProjectGrantViewSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence() + sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence("") logging.Log("EVENT-Skw9f").OnError(sequenceErr).Warn("could not read latest project grant sequence") projects, count, err := repo.View.SearchProjectGrants(request) if err != nil { @@ -444,7 +444,7 @@ func (repo *ProjectRepo) SearchProjectGrants(ctx context.Context, request *proj_ func (repo *ProjectRepo) SearchGrantedProjects(ctx context.Context, request *proj_model.ProjectGrantViewSearchRequest) (*proj_model.ProjectGrantViewSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence() + sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence("") logging.Log("EVENT-Skw9f").OnError(sequenceErr).Warn("could not read latest project grant sequence") permissions := authz.GetRequestPermissionsFromCtx(ctx) @@ -612,7 +612,7 @@ func (repo *ProjectRepo) RemoveProjectGrantMember(ctx context.Context, projectID func (repo *ProjectRepo) SearchProjectGrantMembers(ctx context.Context, request *proj_model.ProjectGrantMemberSearchRequest) (*proj_model.ProjectGrantMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectGrantMemberSequence() + sequence, sequenceErr := repo.View.GetLatestProjectGrantMemberSequence("") logging.Log("EVENT-Du8sk").OnError(sequenceErr).Warn("could not read latest project grant sequence") members, count, err := repo.View.SearchProjectGrantMembers(request) if err != nil { diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index 3d718e2f24..f1b6e62ef7 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -158,7 +158,7 @@ func (repo *UserRepo) RemoveUser(ctx context.Context, id string) error { func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSearchRequest) (*usr_model.UserSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestUserSequence() + sequence, sequenceErr := repo.View.GetLatestUserSequence("") logging.Log("EVENT-Lcn7d").OnError(sequenceErr).Warn("could not read latest user sequence") users, count, err := repo.View.SearchUsers(request) if err != nil { @@ -276,7 +276,7 @@ func (repo *UserRepo) ProfileByID(ctx context.Context, userID string) (*usr_mode func (repo *UserRepo) SearchExternalIDPs(ctx context.Context, request *usr_model.ExternalIDPSearchRequest) (*usr_model.ExternalIDPSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, seqErr := repo.View.GetLatestExternalIDPSequence() + sequence, seqErr := repo.View.GetLatestExternalIDPSequence("") logging.Log("EVENT-Qs7uf").OnError(seqErr).Warn("could not read latest external idp sequence") externalIDPS, count, err := repo.View.SearchExternalIDPs(request) if err != nil { @@ -313,7 +313,7 @@ func (repo *UserRepo) GetMachineKey(ctx context.Context, userID, keyID string) ( func (repo *UserRepo) SearchMachineKeys(ctx context.Context, request *usr_model.MachineKeySearchRequest) (*usr_model.MachineKeySearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, seqErr := repo.View.GetLatestMachineKeySequence() + sequence, seqErr := repo.View.GetLatestMachineKeySequence("") logging.Log("EVENT-Sk8fs").OnError(seqErr).Warn("could not read latest user sequence") keys, count, err := repo.View.SearchMachineKeys(request) if err != nil { @@ -415,7 +415,7 @@ func (repo *UserRepo) ChangeAddress(ctx context.Context, address *usr_model.Addr func (repo *UserRepo) SearchUserMemberships(ctx context.Context, request *usr_model.UserMembershipSearchRequest) (*usr_model.UserMembershipSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestUserMembershipSequence() + sequence, sequenceErr := repo.View.GetLatestUserMembershipSequence("") logging.Log("EVENT-Dn7sf").OnError(sequenceErr).Warn("could not read latest user sequence") result := handleSearchUserMembershipsPermissions(ctx, request, sequence) diff --git a/internal/management/repository/eventsourcing/eventstore/user_grant.go b/internal/management/repository/eventsourcing/eventstore/user_grant.go index 09568b64fc..d2f4f337e9 100644 --- a/internal/management/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/management/repository/eventsourcing/eventstore/user_grant.go @@ -116,7 +116,7 @@ func (repo *UserGrantRepo) BulkRemoveUserGrant(ctx context.Context, grantIDs ... func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestUserGrantSequence() + sequence, sequenceErr := repo.View.GetLatestUserGrantSequence("") logging.Log("EVENT-5Viwf").OnError(sequenceErr).Warn("could not read latest user grant sequence") result := handleSearchUserGrantPermissions(ctx, request, sequence) diff --git a/internal/management/repository/eventsourcing/handler/application.go b/internal/management/repository/eventsourcing/handler/application.go index 8d1490c433..442eae59b3 100644 --- a/internal/management/repository/eventsourcing/handler/application.go +++ b/internal/management/repository/eventsourcing/handler/application.go @@ -5,7 +5,9 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -13,21 +15,57 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Application struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( applicationTable = "management.applications" ) +type Application struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newApplication( + handler handler, + projectEvents *proj_event.ProjectEventstore, +) *Application { + h := &Application{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (a *Application) subscribe() { + a.subscription = a.es.Subscribe(a.AggregateTypes()...) + go func() { + for event := range a.subscription.Events { + query.ReduceEvent(a, event) + } + }() +} + func (a *Application) ViewModel() string { return applicationTable } +func (_ *Application) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (a *Application) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := a.view.GetLatestApplicationSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (a *Application) EventQuery() (*models.SearchQuery, error) { - sequence, err := a.view.GetLatestApplicationSequence() + sequence, err := a.view.GetLatestApplicationSequence("") if err != nil { return nil, err } @@ -65,30 +103,30 @@ func (a *Application) Reduce(event *models.Event) (err error) { if err != nil { return err } - return a.view.DeleteApplication(app.ID, event.Sequence, event.CreationDate) + return a.view.DeleteApplication(app.ID, event) case es_model.ProjectChanged: apps, err := a.view.ApplicationsByProjectID(event.AggregateID) if err != nil { return err } if len(apps) == 0 { - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } for _, app := range apps { if err := app.AppendEvent(event); err != nil { return err } } - return a.view.PutApplications(apps, event.Sequence, event.CreationDate) + return a.view.PutApplications(apps, event) case es_model.ProjectRemoved: return a.view.DeleteApplicationsByProjectID(event.AggregateID) default: - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } if err != nil { return err } - return a.view.PutApplication(app, event.CreationDate) + return a.view.PutApplication(app, event) } func (a *Application) OnError(event *models.Event, spoolerError error) error { diff --git a/internal/management/repository/eventsourcing/handler/handler.go b/internal/management/repository/eventsourcing/handler/handler.go index 531a9db468..56ea997f2f 100644 --- a/internal/management/repository/eventsourcing/handler/handler.go +++ b/internal/management/repository/eventsourcing/handler/handler.go @@ -25,6 +25,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -34,49 +40,76 @@ type EventstoreRepos struct { IamEvents *iam_event.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { return []query.Handler{ - &Project{handler: handler{view, bulkLimit, configs.cycleDuration("Project"), errorCount}, - eventstore: eventstore}, - &ProjectGrant{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrant"), errorCount}, - eventstore: eventstore, projectEvents: repos.ProjectEvents, orgEvents: repos.OrgEvents}, - &ProjectRole{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount}, - projectEvents: repos.ProjectEvents}, - &ProjectMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount}, - userEvents: repos.UserEvents}, - &ProjectGrantMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount}, - userEvents: repos.UserEvents}, - &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, - projectEvents: repos.ProjectEvents}, - &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - eventstore: eventstore, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, iamID: defaults.IamID}, - &UserGrant{handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, - projectEvents: repos.ProjectEvents, userEvents: repos.UserEvents, orgEvents: repos.OrgEvents}, - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, - &OrgMember{handler: handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount}, - userEvents: repos.UserEvents}, - &OrgDomain{handler: handler{view, bulkLimit, configs.cycleDuration("OrgDomain"), errorCount}}, - &UserMembership{handler: handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount}, - orgEvents: repos.OrgEvents, projectEvents: repos.ProjectEvents}, - &MachineKeys{handler: handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount}}, - &IDPConfig{handler: handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount}}, - &LoginPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount}}, - &LabelPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount}}, - &IDPProvider{handler: handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount}, - systemDefaults: defaults, iamEvents: repos.IamEvents, orgEvents: repos.OrgEvents}, - &ExternalIDP{handler: handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount}, - systemDefaults: defaults, iamEvents: repos.IamEvents, orgEvents: repos.OrgEvents}, - &PasswordComplexityPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount}}, - &PasswordAgePolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount}}, - &PasswordLockoutPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount}}, - &OrgIAMPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount}}, + newProject( + handler{view, bulkLimit, configs.cycleDuration("Project"), errorCount, es}), + newProjectGrant( + handler{view, bulkLimit, configs.cycleDuration("ProjectGrant"), errorCount, es}, + repos.ProjectEvents, + repos.OrgEvents), + newProjectRole(handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount, es}, + repos.ProjectEvents), + newProjectMember(handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount, es}, + repos.UserEvents), + newProjectGrantMember(handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount, es}, + repos.UserEvents), + newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}, + repos.ProjectEvents), + newUser(handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IamEvents, + defaults.IamID), + newUserGrant(handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, + repos.ProjectEvents, + repos.UserEvents, + repos.OrgEvents), + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), + newOrgMember( + handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}, + repos.UserEvents), + newOrgDomain( + handler{view, bulkLimit, configs.cycleDuration("OrgDomain"), errorCount, es}), + newUserMembership( + handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}, + repos.OrgEvents, + repos.ProjectEvents), + newMachineKeys( + handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount, es}), + newIDPConfig( + handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), + newLoginPolicy( + handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount, es}), + newLabelPolicy( + handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}), + newIDPProvider( + handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, + + defaults, + repos.IamEvents, + repos.OrgEvents), + newExternalIDP( + handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount, es}, + + defaults, + repos.IamEvents, + repos.OrgEvents), + newPasswordComplexityPolicy( + 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}), + newOrgIAMPolicy( + handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } diff --git a/internal/management/repository/eventsourcing/handler/idp_config.go b/internal/management/repository/eventsourcing/handler/idp_config.go index 07301c71b8..5fa73038e5 100644 --- a/internal/management/repository/eventsourcing/handler/idp_config.go +++ b/internal/management/repository/eventsourcing/handler/idp_config.go @@ -2,8 +2,10 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" @@ -11,29 +13,61 @@ import ( "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type IDPConfig struct { - handler -} - const ( idpConfigTable = "management.idp_configs" ) +type IDPConfig struct { + handler + subscription *eventstore.Subscription +} + +func newIDPConfig(handler handler) *IDPConfig { + h := &IDPConfig{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *IDPConfig) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *IDPConfig) ViewModel() string { return idpConfigTable } -func (m *IDPConfig) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestIDPConfigSequence() +func (_ *IDPConfig) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (m *IDPConfig) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestIDPConfigSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *IDPConfig) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestIDPConfigSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *IDPConfig) Reduce(event *models.Event) (err error) { +func (m *IDPConfig) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.OrgAggregate: err = m.processIdpConfig(iam_model.IDPProviderTypeOrg, event) @@ -43,7 +77,7 @@ func (m *IDPConfig) Reduce(event *models.Event) (err error) { return err } -func (m *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, event *models.Event) (err error) { +func (m *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, event *es_models.Event) (err error) { idp := new(iam_view_model.IDPConfigView) switch event.Type { case model.IDPConfigAdded, @@ -66,17 +100,17 @@ func (m *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, eve if err != nil { return err } - return m.view.DeleteIDPConfig(idp.IDPConfigID, event.Sequence, event.CreationDate) + return m.view.DeleteIDPConfig(idp.IDPConfigID, event) default: - return m.view.ProcessedIDPConfigSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIDPConfigSequence(event) } if err != nil { return err } - return m.view.PutIDPConfig(idp, idp.Sequence, event.CreationDate) + return m.view.PutIDPConfig(idp, event) } -func (i *IDPConfig) OnError(event *models.Event, err error) error { +func (i *IDPConfig) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Nxu8s", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp config handler") return spooler.HandleError(event, err, i.view.GetLatestIDPConfigFailedEvent, i.view.ProcessedIDPConfigFailedEvent, i.view.ProcessedIDPConfigSequence, i.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/idp_providers.go b/internal/management/repository/eventsourcing/handler/idp_providers.go index 9d873eed67..1b5933917b 100644 --- a/internal/management/repository/eventsourcing/handler/idp_providers.go +++ b/internal/management/repository/eventsourcing/handler/idp_providers.go @@ -2,18 +2,23 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" - "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" - org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" + org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" +) + +const ( + idpProviderTable = "management.idp_providers" ) type IDPProvider struct { @@ -21,27 +26,63 @@ type IDPProvider struct { systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - idpProviderTable = "management.idp_providers" -) +func newIDPProvider( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *IDPProvider { + h := &IDPProvider{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *IDPProvider) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (m *IDPProvider) ViewModel() string { return idpProviderTable } -func (m *IDPProvider) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestIDPProviderSequence() +func (_ *IDPProvider) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (m *IDPProvider) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := m.view.GetLatestIDPProviderSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *IDPProvider) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestIDPProviderSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *IDPProvider) Reduce(event *models.Event) (err error) { +func (m *IDPProvider) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate, org_es_model.OrgAggregate: err = m.processIdpProvider(event) @@ -49,7 +90,7 @@ func (m *IDPProvider) Reduce(event *models.Event) (err error) { return err } -func (m *IDPProvider) processIdpProvider(event *models.Event) (err error) { +func (m *IDPProvider) processIdpProvider(event *es_models.Event) (err error) { provider := new(iam_view_model.IDPProviderView) switch event.Type { case model.LoginPolicyIDPProviderAdded, org_es_model.LoginPolicyIDPProviderAdded: @@ -64,7 +105,7 @@ func (m *IDPProvider) processIdpProvider(event *models.Event) (err error) { if err != nil { return err } - return m.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event.Sequence, event.CreationDate) + return m.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event) case model.IDPConfigChanged, org_es_model.IDPConfigChanged: esConfig := new(iam_view_model.IDPConfigView) providerType := iam_model.IDPProviderTypeSystem @@ -88,16 +129,16 @@ func (m *IDPProvider) processIdpProvider(event *models.Event) (err error) { for _, provider := range providers { m.fillConfigData(provider, config) } - return m.view.PutIDPProviders(event.Sequence, event.CreationDate, providers...) + return m.view.PutIDPProviders(event, providers...) case org_es_model.LoginPolicyRemoved: - return m.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event) default: - return m.view.ProcessedIDPProviderSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIDPProviderSequence(event) } if err != nil { return err } - return m.view.PutIDPProvider(provider, provider.Sequence, event.CreationDate) + return m.view.PutIDPProvider(provider, event) } func (m *IDPProvider) fillData(provider *iam_view_model.IDPProviderView) (err error) { @@ -121,7 +162,7 @@ func (m *IDPProvider) fillConfigData(provider *iam_view_model.IDPProviderView, c provider.IDPState = int32(config.State) } -func (m *IDPProvider) OnError(event *models.Event, err error) error { +func (m *IDPProvider) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Msj8c", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp provider handler") return spooler.HandleError(event, err, m.view.GetLatestIDPProviderFailedEvent, m.view.ProcessedIDPProviderFailedEvent, m.view.ProcessedIDPProviderSequence, m.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/label_policy.go b/internal/management/repository/eventsourcing/handler/label_policy.go index 2c47a03c80..2cc3ba833e 100644 --- a/internal/management/repository/eventsourcing/handler/label_policy.go +++ b/internal/management/repository/eventsourcing/handler/label_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type LabelPolicy struct { - handler -} - const ( labelPolicyTable = "management.label_policies" ) +type LabelPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLabelPolicy(handler handler) *LabelPolicy { + h := &LabelPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *LabelPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *LabelPolicy) ViewModel() string { return labelPolicyTable } +func (_ *LabelPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (m *LabelPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestLabelPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *LabelPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestLabelPolicySequence() + sequence, err := m.view.GetLatestLabelPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,12 +86,12 @@ func (m *LabelPolicy) processLabelPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) default: - return m.view.ProcessedLabelPolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedLabelPolicySequence(event) } if err != nil { return err } - return m.view.PutLabelPolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutLabelPolicy(policy, event) } func (m *LabelPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/login_policy.go b/internal/management/repository/eventsourcing/handler/login_policy.go index cb6f06c4d2..cf8994ff1e 100644 --- a/internal/management/repository/eventsourcing/handler/login_policy.go +++ b/internal/management/repository/eventsourcing/handler/login_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type LoginPolicy struct { - handler -} - const ( loginPolicyTable = "management.login_policies" ) +type LoginPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLoginPolicy(handler handler) *LoginPolicy { + h := &LoginPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *LoginPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *LoginPolicy) ViewModel() string { return loginPolicyTable } +func (_ *LoginPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (m *LoginPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestLoginPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *LoginPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestLoginPolicySequence() + sequence, err := m.view.GetLatestLoginPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -57,14 +90,14 @@ func (m *LoginPolicy) processLoginPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.LoginPolicyRemoved: - return m.view.DeleteLoginPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteLoginPolicy(event.AggregateID, event) default: - return m.view.ProcessedLoginPolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedLoginPolicySequence(event) } if err != nil { return err } - return m.view.PutLoginPolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutLoginPolicy(policy, event) } func (m *LoginPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/machine_keys.go b/internal/management/repository/eventsourcing/handler/machine_keys.go index cd6b29b6a8..6758c4f173 100644 --- a/internal/management/repository/eventsourcing/handler/machine_keys.go +++ b/internal/management/repository/eventsourcing/handler/machine_keys.go @@ -4,33 +4,66 @@ import ( "time" "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type MachineKeys struct { - handler -} - const ( machineKeysTable = "management.machine_keys" ) +type MachineKeys struct { + handler + subscription *eventstore.Subscription +} + +func newMachineKeys(handler handler) *MachineKeys { + h := &MachineKeys{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *MachineKeys) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (d *MachineKeys) ViewModel() string { return machineKeysTable } +func (_ *MachineKeys) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate} +} + +func (k *MachineKeys) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := k.view.GetLatestMachineKeySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (d *MachineKeys) EventQuery() (*models.SearchQuery, error) { - sequence, err := d.view.GetLatestMachineKeySequence() + sequence, err := d.view.GetLatestMachineKeySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate). + AggregateTypeFilter(d.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -48,23 +81,23 @@ func (d *MachineKeys) processMachineKeys(event *models.Event) (err error) { case model.MachineKeyAdded: err = key.AppendEvent(event) if key.ExpirationDate.Before(time.Now()) { - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return d.view.ProcessedMachineKeySequence(event) } case model.MachineKeyRemoved: err = key.SetData(event) if err != nil { return err } - return d.view.DeleteMachineKey(key.ID, event.Sequence, event.CreationDate) + return d.view.DeleteMachineKey(key.ID, event) case model.UserRemoved: - return d.view.DeleteMachineKeysByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return d.view.DeleteMachineKeysByUserID(event.AggregateID, event) default: - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return d.view.ProcessedMachineKeySequence(event) } if err != nil { return err } - return d.view.PutMachineKey(key, key.Sequence, event.CreationDate) + return d.view.PutMachineKey(key, event) } func (d *MachineKeys) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/org.go b/internal/management/repository/eventsourcing/handler/org.go index 23155a5c52..a4f6eb211d 100644 --- a/internal/management/repository/eventsourcing/handler/org.go +++ b/internal/management/repository/eventsourcing/handler/org.go @@ -2,28 +2,62 @@ package handler import ( "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "management.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *Org) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (_ *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (o *Org) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } @@ -47,12 +81,12 @@ func (o *Org) Reduce(event *es_models.Event) (err error) { } err = org.AppendEvent(event) default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } if err != nil { return err } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/management/repository/eventsourcing/handler/org_domain.go b/internal/management/repository/eventsourcing/handler/org_domain.go index 9dc6307749..9a7dedb5ee 100644 --- a/internal/management/repository/eventsourcing/handler/org_domain.go +++ b/internal/management/repository/eventsourcing/handler/org_domain.go @@ -2,33 +2,66 @@ package handler import ( "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type OrgDomain struct { - handler -} - const ( orgDomainTable = "management.org_domains" ) +type OrgDomain struct { + handler + subscription *eventstore.Subscription +} + +func newOrgDomain(handler handler) *OrgDomain { + h := &OrgDomain{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *OrgDomain) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (d *OrgDomain) ViewModel() string { return orgDomainTable } +func (_ *OrgDomain) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (p *OrgDomain) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgDomainSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (d *OrgDomain) EventQuery() (*models.SearchQuery, error) { - sequence, err := d.view.GetLatestOrgDomainSequence() + sequence, err := d.view.GetLatestOrgDomainSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate). + AggregateTypeFilter(d.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,7 +105,7 @@ func (d *OrgDomain) processOrgDomain(event *models.Event) (err error) { for _, existingDomain := range existingDomains { existingDomain.Primary = false } - err = d.view.PutOrgDomains(existingDomains, 0, event.CreationDate) + err = d.view.PutOrgDomains(existingDomains, event) if err != nil { return err } @@ -82,14 +115,14 @@ func (d *OrgDomain) processOrgDomain(event *models.Event) (err error) { if err != nil { return err } - return d.view.DeleteOrgDomain(event.AggregateID, domain.Domain, event.Sequence, event.CreationDate) + return d.view.DeleteOrgDomain(event.AggregateID, domain.Domain, event) default: - return d.view.ProcessedOrgDomainSequence(event.Sequence, event.CreationDate) + return d.view.ProcessedOrgDomainSequence(event) } if err != nil { return err } - return d.view.PutOrgDomain(domain, domain.Sequence, event.CreationDate) + return d.view.PutOrgDomain(domain, event) } func (d *OrgDomain) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/org_iam_policy.go b/internal/management/repository/eventsourcing/handler/org_iam_policy.go index fffe08cfec..796652bbb4 100644 --- a/internal/management/repository/eventsourcing/handler/org_iam_policy.go +++ b/internal/management/repository/eventsourcing/handler/org_iam_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type OrgIAMPolicy struct { - handler -} - const ( orgIAMPolicyTable = "management.org_iam_policies" ) +type OrgIAMPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newOrgIAMPolicy(handler handler) *OrgIAMPolicy { + h := &OrgIAMPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *OrgIAMPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *OrgIAMPolicy) ViewModel() string { return orgIAMPolicyTable } +func (_ *OrgIAMPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *OrgIAMPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *OrgIAMPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestOrgIAMPolicySequence() + sequence, err := m.view.GetLatestOrgIAMPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +86,14 @@ func (m *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.OrgIAMPolicyRemoved: - return m.view.DeleteOrgIAMPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteOrgIAMPolicy(event.AggregateID, event) default: - return m.view.ProcessedOrgIAMPolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgIAMPolicySequence(event) } if err != nil { return err } - return m.view.PutOrgIAMPolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutOrgIAMPolicy(policy, event) } func (m *OrgIAMPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/org_member.go b/internal/management/repository/eventsourcing/handler/org_member.go index 26f2b2fb86..cf3e1b6499 100644 --- a/internal/management/repository/eventsourcing/handler/org_member.go +++ b/internal/management/repository/eventsourcing/handler/org_member.go @@ -4,9 +4,10 @@ import ( "context" "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" @@ -15,26 +16,62 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type OrgMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( orgMemberTable = "management.org_members" ) +type OrgMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newOrgMember( + handler handler, + userEvents *usr_event.UserEventstore, +) *OrgMember { + h := &OrgMember{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (m *OrgMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *OrgMember) ViewModel() string { return orgMemberTable } +func (_ *OrgMember) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, usr_es_model.UserAggregate} +} + +func (p *OrgMember) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *OrgMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestOrgMemberSequence() + sequence, err := m.view.GetLatestOrgMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,14 +109,14 @@ func (m *OrgMember) processOrgMember(event *models.Event) (err error) { if err != nil { return err } - return m.view.DeleteOrgMember(event.AggregateID, member.UserID, event.Sequence, event.CreationDate) + return m.view.DeleteOrgMember(event.AggregateID, member.UserID, event) default: - return m.view.ProcessedOrgMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgMemberSequence(event) } if err != nil { return err } - return m.view.PutOrgMember(member, member.Sequence, event.CreationDate) + return m.view.PutOrgMember(member, event) } func (m *OrgMember) processUser(event *models.Event) (err error) { @@ -94,7 +131,7 @@ func (m *OrgMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return m.view.ProcessedOrgMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgMemberSequence(event) } user, err := m.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -103,13 +140,12 @@ func (m *OrgMember) processUser(event *models.Event) (err error) { for _, member := range members { m.fillUserData(member, user) } - return m.view.PutOrgMembers(members, event.Sequence, event.CreationDate) + return m.view.PutOrgMembers(members, event) case usr_es_model.UserRemoved: - return m.view.DeleteOrgMembersByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteOrgMembersByUserID(event.AggregateID, event) default: - return m.view.ProcessedOrgMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgMemberSequence(event) } - return nil } func (m *OrgMember) fillData(member *org_model.OrgMemberView) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/password_age_policy.go b/internal/management/repository/eventsourcing/handler/password_age_policy.go index 535acf03db..eee2e2711c 100644 --- a/internal/management/repository/eventsourcing/handler/password_age_policy.go +++ b/internal/management/repository/eventsourcing/handler/password_age_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordAgePolicy struct { - handler -} - const ( passwordAgePolicyTable = "management.password_age_policies" ) +type PasswordAgePolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordAgePolicy(handler handler) *PasswordAgePolicy { + h := &PasswordAgePolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *PasswordAgePolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *PasswordAgePolicy) ViewModel() string { return passwordAgePolicyTable } -func (m *PasswordAgePolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestPasswordAgePolicySequence() +func (_ *PasswordAgePolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (o *PasswordAgePolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestPasswordAgePolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *PasswordAgePolicy) EventQuery() (*models.SearchQuery, error) { + sequence, err := p.view.GetLatestPasswordAgePolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +86,14 @@ func (m *PasswordAgePolicy) processPasswordAgePolicy(event *models.Event) (err e } err = policy.AppendEvent(event) case model.PasswordAgePolicyRemoved: - return m.view.DeletePasswordAgePolicy(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeletePasswordAgePolicy(event.AggregateID, event) default: - return m.view.ProcessedPasswordAgePolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedPasswordAgePolicySequence(event) } if err != nil { return err } - return m.view.PutPasswordAgePolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutPasswordAgePolicy(policy, event) } func (m *PasswordAgePolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/password_complexity_policy.go b/internal/management/repository/eventsourcing/handler/password_complexity_policy.go index 4ada8b0c95..d2d14c885d 100644 --- a/internal/management/repository/eventsourcing/handler/password_complexity_policy.go +++ b/internal/management/repository/eventsourcing/handler/password_complexity_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordComplexityPolicy struct { - handler -} - const ( passwordComplexityPolicyTable = "management.password_complexity_policies" ) +type PasswordComplexityPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordComplexityPolicy(handler handler) *PasswordComplexityPolicy { + h := &PasswordComplexityPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *PasswordComplexityPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *PasswordComplexityPolicy) ViewModel() string { return passwordComplexityPolicyTable } +func (_ *PasswordComplexityPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordComplexityPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordComplexityPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordComplexityPolicySequence() + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +86,14 @@ func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *models } err = policy.AppendEvent(event) case model.PasswordComplexityPolicyRemoved: - return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordComplexityPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordComplexityPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordComplexityPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordComplexityPolicy(policy, event) } func (p *PasswordComplexityPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/password_lockout_policy.go b/internal/management/repository/eventsourcing/handler/password_lockout_policy.go index c5a66d9528..19e7c9c0b6 100644 --- a/internal/management/repository/eventsourcing/handler/password_lockout_policy.go +++ b/internal/management/repository/eventsourcing/handler/password_lockout_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordLockoutPolicy struct { - handler -} - const ( passwordLockoutPolicyTable = "management.password_lockout_policies" ) +type PasswordLockoutPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy { + h := &PasswordLockoutPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *PasswordLockoutPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *PasswordLockoutPolicy) ViewModel() string { return passwordLockoutPolicyTable } +func (_ *PasswordLockoutPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordLockoutPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordLockoutPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *models.Event } err = policy.AppendEvent(event) case model.PasswordLockoutPolicyRemoved: - return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordLockoutPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordLockoutPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordLockoutPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordLockoutPolicy(policy, event) } func (p *PasswordLockoutPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/project.go b/internal/management/repository/eventsourcing/handler/project.go index 0536de9ae5..51331d44ee 100644 --- a/internal/management/repository/eventsourcing/handler/project.go +++ b/internal/management/repository/eventsourcing/handler/project.go @@ -5,27 +5,59 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Project struct { - handler - eventstore eventstore.Eventstore -} - const ( projectTable = "management.projects" ) +type Project struct { + handler + subscription *eventstore.Subscription +} + +func newProject(handler handler) *Project { + h := &Project{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *Project) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *Project) ViewModel() string { return projectTable } +func (_ *Project) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (p *Project) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *Project) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectSequence() + sequence, err := p.view.GetLatestProjectSequence("") if err != nil { return nil, err } @@ -46,14 +78,14 @@ func (p *Project) Reduce(event *models.Event) (err error) { } err = project.AppendEvent(event) case es_model.ProjectRemoved: - return p.view.DeleteProject(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeleteProject(event.AggregateID, event) default: - return p.view.ProcessedProjectSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectSequence(event) } if err != nil { return err } - return p.view.PutProject(project, event.CreationDate) + return p.view.PutProject(project, event) } func (p *Project) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/project_grant.go b/internal/management/repository/eventsourcing/handler/project_grant.go index 1c7306bb21..b01898bb29 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant.go +++ b/internal/management/repository/eventsourcing/handler/project_grant.go @@ -2,12 +2,12 @@ package handler import ( "context" - "time" "github.com/caos/logging" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" org_model "github.com/caos/zitadel/internal/org/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" @@ -17,23 +17,60 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type ProjectGrant struct { - handler - eventstore eventstore.Eventstore - projectEvents *proj_event.ProjectEventstore - orgEvents *org_event.OrgEventstore -} - const ( grantedProjectTable = "management.project_grants" ) +type ProjectGrant struct { + handler + projectEvents *proj_event.ProjectEventstore + orgEvents *org_event.OrgEventstore + subscription *eventstore.Subscription +} + +func newProjectGrant( + handler handler, + projectEvents *proj_event.ProjectEventstore, + orgEvents *org_event.OrgEventstore, +) *ProjectGrant { + h := &ProjectGrant{ + handler: handler, + projectEvents: projectEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectGrant) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectGrant) ViewModel() string { return grantedProjectTable } +func (_ *ProjectGrant) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (p *ProjectGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectGrant) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectGrantSequence() + sequence, err := p.view.GetLatestProjectGrantSequence("") if err != nil { return nil, err } @@ -48,7 +85,7 @@ func (p *ProjectGrant) Reduce(event *models.Event) (err error) { if err != nil { return err } - return p.updateExistingProjects(project, event.Sequence, event.CreationDate) + return p.updateExistingProjects(project, event) case es_model.ProjectGrantAdded: err = grantedProject.AppendEvent(event) if err != nil { @@ -86,16 +123,16 @@ func (p *ProjectGrant) Reduce(event *models.Event) (err error) { if err != nil { return err } - return p.view.DeleteProjectGrant(grant.GrantID, event.Sequence, event.CreationDate) + return p.view.DeleteProjectGrant(grant.GrantID, event) case es_model.ProjectRemoved: return p.view.DeleteProjectGrantsByProjectID(event.AggregateID) default: - return p.view.ProcessedProjectGrantSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantSequence(event) } if err != nil { return err } - return p.view.PutProjectGrant(grantedProject, event.CreationDate) + return p.view.PutProjectGrant(grantedProject, event) } func (p *ProjectGrant) fillOrgData(grantedProject *view_model.ProjectGrantView, org, resourceOwner *org_model.Org) { @@ -107,7 +144,7 @@ func (p *ProjectGrant) getProject(projectID string) (*proj_model.Project, error) return p.projectEvents.ProjectByID(context.Background(), projectID) } -func (p *ProjectGrant) updateExistingProjects(project *view_model.ProjectView, sequence uint64, eventTimestamp time.Time) error { +func (p *ProjectGrant) updateExistingProjects(project *view_model.ProjectView, event *models.Event) error { projectGrants, err := p.view.ProjectGrantsByProjectID(project.ProjectID) if err != nil { logging.LogWithFields("SPOOL-los03", "id", project.ProjectID).WithError(err).Warn("could not update existing projects") @@ -115,7 +152,7 @@ func (p *ProjectGrant) updateExistingProjects(project *view_model.ProjectView, s for _, existingGrant := range projectGrants { existingGrant.Name = project.Name } - return p.view.PutProjectGrants(projectGrants, sequence, eventTimestamp) + return p.view.PutProjectGrants(projectGrants, event) } func (p *ProjectGrant) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/project_grant_member.go b/internal/management/repository/eventsourcing/handler/project_grant_member.go index fb37af782e..c00eb64763 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant_member.go +++ b/internal/management/repository/eventsourcing/handler/project_grant_member.go @@ -5,8 +5,10 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" @@ -15,26 +17,62 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type ProjectGrantMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( projectGrantMemberTable = "management.project_grant_members" ) +type ProjectGrantMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newProjectGrantMember( + handler handler, + userEvents *usr_event.UserEventstore, +) *ProjectGrantMember { + h := &ProjectGrantMember{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectGrantMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectGrantMember) ViewModel() string { return projectGrantMemberTable } +func (_ *ProjectGrantMember) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{proj_es_model.ProjectAggregate, usr_es_model.UserAggregate} +} + +func (p *ProjectGrantMember) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectGrantMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectGrantMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectGrantMemberSequence() + sequence, err := p.view.GetLatestProjectGrantMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(proj_es_model.ProjectAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,16 +110,16 @@ func (p *ProjectGrantMember) processProjectGrantMember(event *models.Event) (err if err != nil { return err } - return p.view.DeleteProjectGrantMember(member.GrantID, member.UserID, event.Sequence, event.CreationDate) + return p.view.DeleteProjectGrantMember(member.GrantID, member.UserID, event) case proj_es_model.ProjectRemoved: return p.view.DeleteProjectGrantMembersByProjectID(event.AggregateID) default: - return p.view.ProcessedProjectGrantMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantMemberSequence(event) } if err != nil { return err } - return p.view.PutProjectGrantMember(member, member.Sequence, event.CreationDate) + return p.view.PutProjectGrantMember(member, event) } func (p *ProjectGrantMember) processUser(event *models.Event) (err error) { @@ -96,7 +134,7 @@ func (p *ProjectGrantMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return p.view.ProcessedProjectGrantMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantMemberSequence(event) } user, err := p.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -105,11 +143,10 @@ func (p *ProjectGrantMember) processUser(event *models.Event) (err error) { for _, member := range members { p.fillUserData(member, user) } - return p.view.PutProjectGrantMembers(members, event.Sequence, event.CreationDate) + return p.view.PutProjectGrantMembers(members, event) default: - return p.view.ProcessedProjectGrantMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantMemberSequence(event) } - return nil } func (p *ProjectGrantMember) fillData(member *view_model.ProjectGrantMemberView) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/project_member.go b/internal/management/repository/eventsourcing/handler/project_member.go index 7de3603f25..721f42e9d8 100644 --- a/internal/management/repository/eventsourcing/handler/project_member.go +++ b/internal/management/repository/eventsourcing/handler/project_member.go @@ -5,8 +5,10 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" @@ -15,26 +17,62 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type ProjectMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( projectMemberTable = "management.project_members" ) +type ProjectMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newProjectMember( + handler handler, + userEvents *usr_event.UserEventstore, +) *ProjectMember { + h := &ProjectMember{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectMember) ViewModel() string { return projectMemberTable } +func (_ *ProjectMember) AggregateTypes() []models.AggregateType { + return []models.AggregateType{proj_es_model.ProjectAggregate, usr_es_model.UserAggregate} +} + +func (p *ProjectMember) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectMemberSequence() + sequence, err := p.view.GetLatestProjectMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(proj_es_model.ProjectAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,16 +110,16 @@ func (p *ProjectMember) processProjectMember(event *models.Event) (err error) { if err != nil { return err } - return p.view.DeleteProjectMember(event.AggregateID, member.UserID, event.Sequence, event.CreationDate) + return p.view.DeleteProjectMember(event.AggregateID, member.UserID, event) case proj_es_model.ProjectRemoved: return p.view.DeleteProjectMembersByProjectID(event.AggregateID) default: - return p.view.ProcessedProjectMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectMemberSequence(event) } if err != nil { return err } - return p.view.PutProjectMember(member, member.Sequence, event.CreationDate) + return p.view.PutProjectMember(member, event) } func (p *ProjectMember) processUser(event *models.Event) (err error) { @@ -96,7 +134,7 @@ func (p *ProjectMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return p.view.ProcessedProjectMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectMemberSequence(event) } user, err := p.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -105,9 +143,9 @@ func (p *ProjectMember) processUser(event *models.Event) (err error) { for _, member := range members { p.fillUserData(member, user) } - return p.view.PutProjectMembers(members, event.Sequence, event.CreationDate) + return p.view.PutProjectMembers(members, event) default: - return p.view.ProcessedProjectMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectMemberSequence(event) } return nil } diff --git a/internal/management/repository/eventsourcing/handler/project_role.go b/internal/management/repository/eventsourcing/handler/project_role.go index 0dba4b14e8..3d89ba4a9f 100644 --- a/internal/management/repository/eventsourcing/handler/project_role.go +++ b/internal/management/repository/eventsourcing/handler/project_role.go @@ -3,7 +3,9 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -11,21 +13,57 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type ProjectRole struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( projectRoleTable = "management.project_roles" ) +type ProjectRole struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newProjectRole( + handler handler, + projectEvents *proj_event.ProjectEventstore, +) *ProjectRole { + h := &ProjectRole{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectRole) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectRole) ViewModel() string { return projectRoleTable } +func (_ *ProjectRole) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (p *ProjectRole) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectRoleSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectRole) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectRoleSequence() + sequence, err := p.view.GetLatestProjectRoleSequence("") if err != nil { return nil, err } @@ -52,16 +90,16 @@ func (p *ProjectRole) Reduce(event *models.Event) (err error) { if err != nil { return err } - return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event.Sequence, event.CreationDate) + return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event) case es_model.ProjectRemoved: return p.view.DeleteProjectRolesByProjectID(event.AggregateID) default: - return p.view.ProcessedProjectRoleSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectRoleSequence(event) } if err != nil { return err } - return p.view.PutProjectRole(role, event.CreationDate) + return p.view.PutProjectRole(role, event) } func (p *ProjectRole) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/user.go b/internal/management/repository/eventsourcing/handler/user.go index 0721e0fcd3..05497a0260 100644 --- a/internal/management/repository/eventsourcing/handler/user.go +++ b/internal/management/repository/eventsourcing/handler/user.go @@ -2,14 +2,14 @@ package handler import ( "context" - iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/logging" - "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" @@ -17,29 +17,68 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type User struct { - handler - eventstore eventstore.Eventstore - orgEvents *org_events.OrgEventstore - iamEvents *iam_es.IAMEventstore - iamID string -} - const ( userTable = "management.users" ) +type User struct { + handler + orgEvents *org_events.OrgEventstore + iamEvents *iam_es.IAMEventstore + iamID string + subscription *eventstore.Subscription +} + +func newUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + iamID string, +) *User { + h := &User{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (m *User) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (u *User) ViewModel() string { return userTable } +func (_ *User) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (u *User) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *User) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSequence() + sequence, err := u.view.GetLatestUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -115,14 +154,14 @@ func (u *User) ProcessUser(event *models.Event) (err error) { } err = u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUser(event.AggregateID, event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } if err != nil { return err } - return u.view.PutUser(user, user.Sequence, event.CreationDate) + return u.view.PutUser(user, event) } func (u *User) ProcessOrg(event *models.Event) (err error) { @@ -136,7 +175,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } } @@ -159,7 +198,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.SetLoginNames(policy, org.Domains) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { @@ -184,7 +223,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/user_external_idps.go b/internal/management/repository/eventsourcing/handler/user_external_idps.go index de57a8f0f2..059a15c9bd 100644 --- a/internal/management/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/management/repository/eventsourcing/handler/user_external_idps.go @@ -2,21 +2,27 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" + iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" +) - "github.com/caos/zitadel/internal/eventstore/models" - es_models "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" - iam_model "github.com/caos/zitadel/internal/iam/model" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" +const ( + externalIDPTable = "management.user_external_idps" ) type ExternalIDP struct { @@ -24,27 +30,63 @@ type ExternalIDP struct { systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - externalIDPTable = "management.user_external_idps" -) +func newExternalIDP( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *ExternalIDP { + h := &ExternalIDP{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *ExternalIDP) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (i *ExternalIDP) ViewModel() string { return externalIDPTable } -func (i *ExternalIDP) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestExternalIDPSequence() +func (_ *ExternalIDP) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *ExternalIDP) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestExternalIDPSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (i *ExternalIDP) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := i.view.GetLatestExternalIDPSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (i *ExternalIDP) Reduce(event *models.Event) (err error) { +func (i *ExternalIDP) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.UserAggregate: err = i.processUser(event) @@ -54,7 +96,7 @@ func (i *ExternalIDP) Reduce(event *models.Event) (err error) { return err } -func (i *ExternalIDP) processUser(event *models.Event) (err error) { +func (i *ExternalIDP) processUser(event *es_models.Event) (err error) { externalIDP := new(usr_view_model.ExternalIDPView) switch event.Type { case model.HumanExternalIDPAdded: @@ -68,19 +110,19 @@ func (i *ExternalIDP) processUser(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event) case model.UserRemoved: - return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } if err != nil { return err } - return i.view.PutExternalIDP(externalIDP, externalIDP.Sequence, event.CreationDate) + return i.view.PutExternalIDP(externalIDP, event) } -func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { +func (i *ExternalIDP) processIdpConfig(event *es_models.Event) (err error) { switch event.Type { case iam_es_model.IDPConfigChanged, org_es_model.IDPConfigChanged: configView := new(iam_view_model.IDPConfigView) @@ -105,9 +147,9 @@ func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { for _, provider := range exterinalIDPs { i.fillConfigData(provider, config) } - return i.view.PutExternalIDPs(event.Sequence, event.CreationDate, exterinalIDPs...) + return i.view.PutExternalIDPs(event, exterinalIDPs...) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } return nil } @@ -128,7 +170,7 @@ func (i *ExternalIDP) fillConfigData(externalIDP *usr_view_model.ExternalIDPView externalIDP.IDPName = config.Name } -func (i *ExternalIDP) OnError(event *models.Event, err error) error { +func (i *ExternalIDP) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-4Rsu8", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp provider handler") return spooler.HandleError(event, err, i.view.GetLatestExternalIDPFailedEvent, i.view.ProcessedExternalIDPFailedEvent, i.view.ProcessedExternalIDPSequence, i.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/user_grant.go b/internal/management/repository/eventsourcing/handler/user_grant.go index 0ffe6955ac..3c9f0a0f8f 100644 --- a/internal/management/repository/eventsourcing/handler/user_grant.go +++ b/internal/management/repository/eventsourcing/handler/user_grant.go @@ -3,7 +3,12 @@ package handler import ( "context" + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" proj_model "github.com/caos/zitadel/internal/project/model" @@ -13,42 +18,75 @@ import ( usr_events "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" grant_es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model" ) -type UserGrant struct { - handler - eventstore eventstore.Eventstore - projectEvents *proj_event.ProjectEventstore - userEvents *usr_events.UserEventstore - orgEvents *org_events.OrgEventstore -} - const ( userGrantTable = "management.user_grants" ) +type UserGrant struct { + handler + projectEvents *proj_event.ProjectEventstore + userEvents *usr_events.UserEventstore + orgEvents *org_events.OrgEventstore + subscription *eventstore.Subscription +} + +func newUserGrant( + handler handler, + projectEvents *proj_event.ProjectEventstore, + userEvents *usr_events.UserEventstore, + orgEvents *org_events.OrgEventstore, +) *UserGrant { + h := &UserGrant{ + handler: handler, + projectEvents: projectEvents, + userEvents: userEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *UserGrant) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (u *UserGrant) ViewModel() string { return userGrantTable } -func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserGrantSequence() +func (_ *UserGrant) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{grant_es_model.UserGrantAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate} +} + +func (u *UserGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (u *UserGrant) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := u.view.GetLatestUserGrantSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(grant_es_model.UserGrantAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (u *UserGrant) Reduce(event *models.Event) (err error) { +func (u *UserGrant) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case grant_es_model.UserGrantAggregate: err = u.processUserGrant(event) @@ -60,7 +98,7 @@ func (u *UserGrant) Reduce(event *models.Event) (err error) { return err } -func (u *UserGrant) processUserGrant(event *models.Event) (err error) { +func (u *UserGrant) processUserGrant(event *es_models.Event) (err error) { grant := new(view_model.UserGrantView) switch event.Type { case grant_es_model.UserGrantAdded: @@ -79,17 +117,17 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) { } err = grant.AppendEvent(event) case grant_es_model.UserGrantRemoved, grant_es_model.UserGrantCascadeRemoved: - return u.view.DeleteUserGrant(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(event.AggregateID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if err != nil { return err } - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) } -func (u *UserGrant) processUser(event *models.Event) (err error) { +func (u *UserGrant) processUser(event *es_models.Event) (err error) { switch event.Type { case usr_es_model.UserProfileChanged, usr_es_model.UserEmailChanged, @@ -101,7 +139,7 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { return err } if len(grants) == 0 { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } user, err := u.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -110,14 +148,14 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { for _, grant := range grants { u.fillUserData(grant, user) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } return nil } -func (u *UserGrant) processProject(event *models.Event) (err error) { +func (u *UserGrant) processProject(event *es_models.Event) (err error) { switch event.Type { case proj_es_model.ProjectChanged: grants, err := u.view.UserGrantsByProjectID(event.AggregateID) @@ -125,7 +163,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { return err } if len(grants) == 0 { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } project, err := u.projectEvents.ProjectByID(context.Background(), event.AggregateID) if err != nil { @@ -134,9 +172,9 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { for _, grant := range grants { u.fillProjectData(grant, project) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } return nil } @@ -189,7 +227,7 @@ func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model. } } -func (u *UserGrant) OnError(event *models.Event, err error) error { +func (u *UserGrant) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-8is4s", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler") return spooler.HandleError(event, err, u.view.GetLatestUserGrantFailedEvent, u.view.ProcessedUserGrantFailedEvent, u.view.ProcessedUserGrantSequence, u.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/user_membership.go b/internal/management/repository/eventsourcing/handler/user_membership.go index 8537905d47..e6a56fab90 100644 --- a/internal/management/repository/eventsourcing/handler/user_membership.go +++ b/internal/management/repository/eventsourcing/handler/user_membership.go @@ -2,48 +2,87 @@ package handler import ( "context" + + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" - "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore/models" - es_models "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" - org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/model" + "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + userMembershipTable = "management.user_memberships" +) + type UserMembership struct { handler orgEvents *org_event.OrgEventstore projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription } -const ( - userMembershipTable = "management.user_memberships" -) +func newUserMembership( + handler handler, + orgEvents *org_event.OrgEventstore, + projectEvents *proj_event.ProjectEventstore, +) *UserMembership { + h := &UserMembership{ + handler: handler, + orgEvents: orgEvents, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (m *UserMembership) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (m *UserMembership) ViewModel() string { return userMembershipTable } -func (m *UserMembership) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestUserMembershipSequence() +func (_ *UserMembership) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate} +} + +func (u *UserMembership) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserMembershipSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *UserMembership) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestUserMembershipSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *UserMembership) Reduce(event *models.Event) (err error) { +func (m *UserMembership) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case iam_es_model.IAMAggregate: err = m.processIam(event) @@ -57,7 +96,7 @@ func (m *UserMembership) Reduce(event *models.Event) (err error) { return err } -func (m *UserMembership) processIam(event *models.Event) (err error) { +func (m *UserMembership) processIam(event *es_models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { @@ -73,21 +112,21 @@ func (m *UserMembership) processIam(event *models.Event) (err error) { } err = member.AppendEvent(event) case iam_es_model.IAMMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillIamDisplayName(member *usr_es_model.UserMembershipView) { member.DisplayName = member.AggregateID } -func (m *UserMembership) processOrg(event *models.Event) (err error) { +func (m *UserMembership) processOrg(event *es_models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { @@ -103,16 +142,16 @@ func (m *UserMembership) processOrg(event *models.Event) (err error) { } err = member.AppendEvent(event) case org_es_model.OrgMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event) case org_es_model.OrgChanged: return m.updateOrgDisplayName(event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillOrgDisplayName(member *usr_es_model.UserMembershipView) (err error) { @@ -124,7 +163,7 @@ func (m *UserMembership) fillOrgDisplayName(member *usr_es_model.UserMembershipV return nil } -func (m *UserMembership) updateOrgDisplayName(event *models.Event) error { +func (m *UserMembership) updateOrgDisplayName(event *es_models.Event) error { org, err := m.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.AggregateID)) if err != nil { return err @@ -137,10 +176,10 @@ func (m *UserMembership) updateOrgDisplayName(event *models.Event) error { for _, membership := range memberships { membership.DisplayName = org.Name } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } -func (m *UserMembership) processProject(event *models.Event) (err error) { +func (m *UserMembership) processProject(event *es_models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { @@ -156,7 +195,7 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event) case proj_es_model.ProjectGrantMemberChanged: member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant) if err != nil { @@ -164,20 +203,20 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectGrantMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event) case proj_es_model.ProjectChanged: return m.updateProjectDisplayName(event) case proj_es_model.ProjectRemoved: - return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event) case proj_es_model.ProjectGrantRemoved: - return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembershipView) (err error) { @@ -189,7 +228,7 @@ func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembers return nil } -func (m *UserMembership) updateProjectDisplayName(event *models.Event) error { +func (m *UserMembership) updateProjectDisplayName(event *es_models.Event) error { project, err := m.projectEvents.ProjectByID(context.Background(), event.AggregateID) if err != nil { return err @@ -202,19 +241,19 @@ func (m *UserMembership) updateProjectDisplayName(event *models.Event) error { for _, membership := range memberships { membership.DisplayName = project.Name } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } -func (m *UserMembership) processUser(event *models.Event) (err error) { +func (m *UserMembership) processUser(event *es_models.Event) (err error) { switch event.Type { case model.UserRemoved: - return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } } -func (m *UserMembership) OnError(event *models.Event, err error) error { +func (m *UserMembership) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Fwer2", "id", event.AggregateID).WithError(err).Warn("something went wrong in user membership handler") return spooler.HandleError(event, err, m.view.GetLatestUserMembershipFailedEvent, m.view.ProcessedUserMembershipFailedEvent, m.view.ProcessedUserMembershipSequence, m.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/spooler/lock.go b/internal/management/repository/eventsourcing/spooler/lock.go index f24f98f6cc..9dd53a4cc3 100644 --- a/internal/management/repository/eventsourcing/spooler/lock.go +++ b/internal/management/repository/eventsourcing/spooler/lock.go @@ -2,7 +2,6 @@ package spooler import ( "database/sql" - es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) @@ -15,5 +14,5 @@ type locker struct { } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) + return nil } diff --git a/internal/management/repository/eventsourcing/view/application.go b/internal/management/repository/eventsourcing/view/application.go index 46672fc74a..eeb5c344e6 100644 --- a/internal/management/repository/eventsourcing/view/application.go +++ b/internal/management/repository/eventsourcing/view/application.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,40 +24,40 @@ func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) return view.SearchApplications(v.Db, applicationTable, request) } -func (v *View) PutApplication(app *model.ApplicationView, eventTimestamp time.Time) error { +func (v *View) PutApplication(app *model.ApplicationView, event *models.Event) error { err := view.PutApplication(v.Db, applicationTable, app) if err != nil { return err } - return v.ProcessedApplicationSequence(app.Sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) PutApplications(apps []*model.ApplicationView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Event) error { err := view.PutApplications(v.Db, applicationTable, apps...) if err != nil { return err } - return v.ProcessedApplicationSequence(sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) DeleteApplication(appID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) if err != nil { return nil } - return v.ProcessedApplicationSequence(eventSequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } func (v *View) DeleteApplicationsByProjectID(projectID string) error { return view.DeleteApplicationsByProjectID(v.Db, applicationTable, projectID) } -func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(applicationTable) +func (v *View) GetLatestApplicationSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(applicationTable, aggregateType) } -func (v *View) ProcessedApplicationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(applicationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedApplicationSequence(event *models.Event) error { + return v.saveCurrentSequence(applicationTable, event) } func (v *View) UpdateApplicationSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/external_idps.go b/internal/management/repository/eventsourcing/view/external_idps.go index c2944c7a5f..0b8eb3648e 100644 --- a/internal/management/repository/eventsourcing/view/external_idps.go +++ b/internal/management/repository/eventsourcing/view/external_idps.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -33,43 +33,43 @@ func (v *View) SearchExternalIDPs(request *usr_model.ExternalIDPSearchRequest) ( return view.SearchExternalIDPs(v.Db, externalIDPTable, request) } -func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, event *models.Event) error { err := view.PutExternalIDP(v.Db, externalIDPTable, externalIDP) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) PutExternalIDPs(sequence uint64, eventTimestamp time.Time, externalIDPs ...*model.ExternalIDPView) error { +func (v *View) PutExternalIDPs(event *models.Event, externalIDPs ...*model.ExternalIDPView) error { err := view.PutExternalIDPs(v.Db, externalIDPTable, externalIDPs...) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, event *models.Event) error { err := view.DeleteExternalIDP(v.Db, externalIDPTable, externalUserID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDPsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDPsByUserID(userID string, event *models.Event) error { err := view.DeleteExternalIDPsByUserID(v.Db, externalIDPTable, userID) if err != nil { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) GetLatestExternalIDPSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(externalIDPTable) +func (v *View) GetLatestExternalIDPSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(externalIDPTable, aggregateType) } -func (v *View) ProcessedExternalIDPSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(externalIDPTable, eventSequence, eventTimestamp) +func (v *View) ProcessedExternalIDPSequence(event *models.Event) error { + return v.saveCurrentSequence(externalIDPTable, event) } func (v *View) UpdateExternalIDPSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/idp_configs.go b/internal/management/repository/eventsourcing/view/idp_configs.go index a9dfde45da..5770f36b38 100644 --- a/internal/management/repository/eventsourcing/view/idp_configs.go +++ b/internal/management/repository/eventsourcing/view/idp_configs.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -21,28 +21,28 @@ func (v *View) SearchIDPConfigs(request *iam_model.IDPConfigSearchRequest) ([]*i return view.SearchIDPs(v.Db, idpConfigTable, request) } -func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, event *models.Event) error { err := view.PutIDP(v.Db, idpConfigTable, idp) if err != nil { return err } - return v.ProcessedIDPConfigSequence(sequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) DeleteIDPConfig(idpID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPConfig(idpID string, event *models.Event) error { err := view.DeleteIDP(v.Db, idpConfigTable, idpID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPConfigSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) GetLatestIDPConfigSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpConfigTable) +func (v *View) GetLatestIDPConfigSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpConfigTable, aggregateType) } -func (v *View) ProcessedIDPConfigSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpConfigTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPConfigSequence(event *models.Event) error { + return v.saveCurrentSequence(idpConfigTable, event) } func (v *View) UpdateIDPConfigSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/idp_providers.go b/internal/management/repository/eventsourcing/view/idp_providers.go index 207feb621f..a293ec1637 100644 --- a/internal/management/repository/eventsourcing/view/idp_providers.go +++ b/internal/management/repository/eventsourcing/view/idp_providers.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "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" - "time" ) const ( @@ -25,44 +25,44 @@ func (v *View) SearchIDPProviders(request *iam_model.IDPProviderSearchRequest) ( return view.SearchIDPProviders(v.Db, idpProviderTable, request) } -func (v *View) PutIDPProvider(provider *model.IDPProviderView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPProvider(provider *model.IDPProviderView, event *models.Event) error { err := view.PutIDPProvider(v.Db, idpProviderTable, provider) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) PutIDPProviders(sequence uint64, eventTimestamp time.Time, providers ...*model.IDPProviderView) error { +func (v *View) PutIDPProviders(event *models.Event, providers ...*model.IDPProviderView) error { err := view.PutIDPProviders(v.Db, idpProviderTable, providers...) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, event *models.Event) error { err := view.DeleteIDPProvider(v.Db, idpProviderTable, aggregateID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteIDPProvidersByAggregateID(v.Db, idpProviderTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) GetLatestIDPProviderSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpProviderTable) +func (v *View) GetLatestIDPProviderSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpProviderTable, aggregateType) } -func (v *View) ProcessedIDPProviderSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpProviderTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPProviderSequence(event *models.Event) error { + return v.saveCurrentSequence(idpProviderTable, event) } func (v *View) UpdateIDPProviderSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/label_policies.go b/internal/management/repository/eventsourcing/view/label_policies.go index 27b234fc29..05bb9550a4 100644 --- a/internal/management/repository/eventsourcing/view/label_policies.go +++ b/internal/management/repository/eventsourcing/view/label_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LabelPolicyByAggregateID(aggregateID string) (*model.LabelPolicyV return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTable, aggregateID) } -func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error { err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy) if err != nil { return err } - return v.ProcessedLabelPolicySequence(sequence, eventTimestamp) + return v.ProcessedLabelPolicySequence(event) } -func (v *View) DeleteLabelPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLabelPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLabelPolicy(v.Db, labelPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLabelPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLabelPolicySequence(event) } -func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(labelPolicyTable) +func (v *View) GetLatestLabelPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(labelPolicyTable, aggregateType) } -func (v *View) ProcessedLabelPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(labelPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLabelPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(labelPolicyTable, event) } func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/login_policies.go b/internal/management/repository/eventsourcing/view/login_policies.go index c37f4283d6..56e6e80f8f 100644 --- a/internal/management/repository/eventsourcing/view/login_policies.go +++ b/internal/management/repository/eventsourcing/view/login_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LoginPolicyByAggregateID(aggregateID string) (*model.LoginPolicyV return view.GetLoginPolicyByAggregateID(v.Db, loginPolicyTable, aggregateID) } -func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, event *models.Event) error { err := view.PutLoginPolicy(v.Db, loginPolicyTable, policy) if err != nil { return err } - return v.ProcessedLoginPolicySequence(sequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) DeleteLoginPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLoginPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLoginPolicy(v.Db, loginPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLoginPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) GetLatestLoginPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(loginPolicyTable) +func (v *View) GetLatestLoginPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(loginPolicyTable, aggregateType) } -func (v *View) ProcessedLoginPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(loginPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLoginPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(loginPolicyTable, event) } func (v *View) UpdateLoginPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/machine_keys.go b/internal/management/repository/eventsourcing/view/machine_keys.go index b037137c82..da50ed6760 100644 --- a/internal/management/repository/eventsourcing/view/machine_keys.go +++ b/internal/management/repository/eventsourcing/view/machine_keys.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,39 +24,39 @@ func (v *View) SearchMachineKeys(request *usr_model.MachineKeySearchRequest) ([] return view.SearchMachineKeys(v.Db, machineKeyTable, request) } -func (v *View) PutMachineKey(org *model.MachineKeyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutMachineKey(org *model.MachineKeyView, event *models.Event) error { err := view.PutMachineKey(v.Db, machineKeyTable, org) if err != nil { return err } - if sequence != 0 { - return v.ProcessedMachineKeySequence(sequence, eventTimestamp) + if event.Sequence != 0 { + return v.ProcessedMachineKeySequence(event) } return nil } -func (v *View) DeleteMachineKey(keyID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) if err != nil { return nil } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) DeleteMachineKeysByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) if err != nil { return nil } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) GetLatestMachineKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(machineKeyTable) +func (v *View) GetLatestMachineKeySequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(machineKeyTable, aggregateType) } -func (v *View) ProcessedMachineKeySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(machineKeyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedMachineKeySequence(event *models.Event) error { + return v.saveCurrentSequence(machineKeyTable, event) } func (v *View) UpdateMachineKeySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/org.go b/internal/management/repository/eventsourcing/view/org.go index a5de5fdb46..3a53b92031 100644 --- a/internal/management/repository/eventsourcing/view/org.go +++ b/internal/management/repository/eventsourcing/view/org.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" org_view "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -15,12 +15,12 @@ func (v *View) OrgByID(orgID string) (*model.OrgView, error) { return org_view.OrgByID(v.Db, orgTable, orgID) } -func (v *View) PutOrg(org *model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -35,10 +35,10 @@ func (v *View) UpdateOrgSpoolerRunTimestamp() error { return v.updateSpoolerRunSequence(orgTable) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } diff --git a/internal/management/repository/eventsourcing/view/org_domain.go b/internal/management/repository/eventsourcing/view/org_domain.go index 94f3f84c95..f8103674aa 100644 --- a/internal/management/repository/eventsourcing/view/org_domain.go +++ b/internal/management/repository/eventsourcing/view/org_domain.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,39 +28,39 @@ func (v *View) SearchOrgDomains(request *org_model.OrgDomainSearchRequest) ([]*m return view.SearchOrgDomains(v.Db, orgDomainTable, request) } -func (v *View) PutOrgDomain(org *model.OrgDomainView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgDomain(org *model.OrgDomainView, event *models.Event) error { err := view.PutOrgDomain(v.Db, orgDomainTable, org) if err != nil { return err } - if sequence != 0 { - return v.ProcessedOrgDomainSequence(sequence, eventTimestamp) + if event.Sequence != 0 { + return v.ProcessedOrgDomainSequence(event) } return nil } -func (v *View) PutOrgDomains(domains []*model.OrgDomainView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgDomains(domains []*model.OrgDomainView, event *models.Event) error { err := view.PutOrgDomains(v.Db, orgDomainTable, domains...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) DeleteOrgDomain(orgID, domain string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgDomain(orgID, domain string, event *models.Event) error { err := view.DeleteOrgDomain(v.Db, orgDomainTable, orgID, domain) if err != nil { return nil } - return v.ProcessedOrgDomainSequence(eventSequence, eventTimestamp) + return v.ProcessedOrgDomainSequence(event) } -func (v *View) GetLatestOrgDomainSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgDomainTable) +func (v *View) GetLatestOrgDomainSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgDomainTable, aggregateType) } -func (v *View) ProcessedOrgDomainSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgDomainTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgDomainSequence(event *models.Event) error { + return v.saveCurrentSequence(orgDomainTable, event) } func (v *View) UpdateOrgDomainSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/org_iam_policy.go b/internal/management/repository/eventsourcing/view/org_iam_policy.go index db9d113155..90d3de2b9c 100644 --- a/internal/management/repository/eventsourcing/view/org_iam_policy.go +++ b/internal/management/repository/eventsourcing/view/org_iam_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) OrgIAMPolicyByAggregateID(aggregateID string) (*model.OrgIAMPolic return view.GetOrgIAMPolicyByAggregateID(v.Db, orgIAMPolicyTable, aggregateID) } -func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, event *models.Event) error { err := view.PutOrgIAMPolicy(v.Db, orgIAMPolicyTable, policy) if err != nil { return err } - return v.ProcessedOrgIAMPolicySequence(sequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) DeleteOrgIAMPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgIAMPolicy(aggregateID string, event *models.Event) error { err := view.DeleteOrgIAMPolicy(v.Db, orgIAMPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedOrgIAMPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) GetLatestOrgIAMPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(orgIAMPolicyTable) +func (v *View) GetLatestOrgIAMPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(orgIAMPolicyTable, aggregateType) } -func (v *View) ProcessedOrgIAMPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgIAMPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgIAMPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(orgIAMPolicyTable, event) } func (v *View) UpdateOrgIAMPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/org_member.go b/internal/management/repository/eventsourcing/view/org_member.go index d1cb20b3c0..2ea8f79c90 100644 --- a/internal/management/repository/eventsourcing/view/org_member.go +++ b/internal/management/repository/eventsourcing/view/org_member.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,44 +24,44 @@ func (v *View) OrgMembersByUserID(userID string) ([]*model.OrgMemberView, error) return view.OrgMembersByUserID(v.Db, orgMemberTable, userID) } -func (v *View) PutOrgMember(member *model.OrgMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgMember(member *model.OrgMemberView, event *models.Event) error { err := view.PutOrgMember(v.Db, orgMemberTable, member) if err != nil { return err } - return v.ProcessedOrgMemberSequence(sequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) PutOrgMembers(members []*model.OrgMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgMembers(members []*model.OrgMemberView, event *models.Event) error { err := view.PutOrgMembers(v.Db, orgMemberTable, members...) if err != nil { return err } - return v.ProcessedOrgMemberSequence(sequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) DeleteOrgMember(orgID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgMember(orgID, userID string, event *models.Event) error { err := view.DeleteOrgMember(v.Db, orgMemberTable, orgID, userID) if err != nil { return nil } - return v.ProcessedOrgMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) DeleteOrgMembersByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgMembersByUserID(userID string, event *models.Event) error { err := view.DeleteOrgMembersByUserID(v.Db, orgMemberTable, userID) if err != nil { return nil } - return v.ProcessedOrgMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) GetLatestOrgMemberSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgMemberTable) +func (v *View) GetLatestOrgMemberSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgMemberTable, aggregateType) } -func (v *View) ProcessedOrgMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(orgMemberTable, event) } func (v *View) UpdateOrgMemberSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/password_age_policy.go b/internal/management/repository/eventsourcing/view/password_age_policy.go index e37d650072..e1bd56a596 100644 --- a/internal/management/repository/eventsourcing/view/password_age_policy.go +++ b/internal/management/repository/eventsourcing/view/password_age_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordAgePolicyByAggregateID(aggregateID string) (*model.Passwo return view.GetPasswordAgePolicyByAggregateID(v.Db, passwordAgePolicyTable, aggregateID) } -func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, event *models.Event) error { err := view.PutPasswordAgePolicy(v.Db, passwordAgePolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordAgePolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) DeletePasswordAgePolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordAgePolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordAgePolicy(v.Db, passwordAgePolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordAgePolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) GetLatestPasswordAgePolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordAgePolicyTable) +func (v *View) GetLatestPasswordAgePolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordAgePolicyTable, aggregateType) } -func (v *View) ProcessedPasswordAgePolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordAgePolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordAgePolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordAgePolicyTable, event) } func (v *View) UpdatePasswordAgePolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/password_complexity_policy.go b/internal/management/repository/eventsourcing/view/password_complexity_policy.go index 70895d13a9..80d9e2d90d 100644 --- a/internal/management/repository/eventsourcing/view/password_complexity_policy.go +++ b/internal/management/repository/eventsourcing/view/password_complexity_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordComplexityPolicyByAggregateID(aggregateID string) (*model return view.GetPasswordComplexityPolicyByAggregateID(v.Db, passwordComplexityPolicyTable, aggregateID) } -func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, event *models.Event) error { err := view.PutPasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordComplexityPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) DeletePasswordComplexityPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordComplexityPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordComplexityPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) GetLatestPasswordComplexityPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordComplexityPolicyTable) +func (v *View) GetLatestPasswordComplexityPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordComplexityPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordComplexityPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordComplexityPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordComplexityPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordComplexityPolicyTable, event) } func (v *View) UpdatePasswordComplexityPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/password_lockout_policy.go b/internal/management/repository/eventsourcing/view/password_lockout_policy.go index 11e3955720..549f704c75 100644 --- a/internal/management/repository/eventsourcing/view/password_lockout_policy.go +++ b/internal/management/repository/eventsourcing/view/password_lockout_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/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" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordLockoutPolicyByAggregateID(aggregateID string) (*model.Pa return view.GetPasswordLockoutPolicyByAggregateID(v.Db, passwordLockoutPolicyTable, aggregateID) } -func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, sequence uint64, eventTimestamp time.Time) error { +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(sequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) DeletePasswordLockoutPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +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(eventSequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) GetLatestPasswordLockoutPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordLockoutPolicyTable) +func (v *View) GetLatestPasswordLockoutPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordLockoutPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordLockoutPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordLockoutPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordLockoutPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordLockoutPolicyTable, event) } func (v *View) UpdatePasswordLockoutPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project.go b/internal/management/repository/eventsourcing/view/project.go index fede8830ee..8676b0ed2c 100644 --- a/internal/management/repository/eventsourcing/view/project.go +++ b/internal/management/repository/eventsourcing/view/project.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -20,28 +20,28 @@ func (v *View) SearchProjects(request *proj_model.ProjectViewSearchRequest) ([]* return view.SearchProjects(v.Db, projectTable, request) } -func (v *View) PutProject(project *model.ProjectView, eventTimestamp time.Time) error { +func (v *View) PutProject(project *model.ProjectView, event *models.Event) error { err := view.PutProject(v.Db, projectTable, project) if err != nil { return err } - return v.ProcessedProjectSequence(project.Sequence, eventTimestamp) + return v.ProcessedProjectSequence(event) } -func (v *View) DeleteProject(projectID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProject(projectID string, event *models.Event) error { err := view.DeleteProject(v.Db, projectTable, projectID) if err != nil { return nil } - return v.ProcessedProjectSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectSequence(event) } -func (v *View) GetLatestProjectSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectTable) +func (v *View) GetLatestProjectSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectTable, aggregateType) } -func (v *View) ProcessedProjectSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectSequence(event *models.Event) error { + return v.saveCurrentSequence(projectTable, event) } func (v *View) UpdateProjectSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_grant.go b/internal/management/repository/eventsourcing/view/project_grant.go index 64f1f6272e..ab54927a30 100644 --- a/internal/management/repository/eventsourcing/view/project_grant.go +++ b/internal/management/repository/eventsourcing/view/project_grant.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,40 +32,40 @@ func (v *View) SearchProjectGrants(request *proj_model.ProjectGrantViewSearchReq return view.SearchProjectGrants(v.Db, grantedProjectTable, request) } -func (v *View) PutProjectGrant(grant *model.ProjectGrantView, eventTimestamp time.Time) error { +func (v *View) PutProjectGrant(grant *model.ProjectGrantView, event *models.Event) error { err := view.PutProjectGrant(v.Db, grantedProjectTable, grant) if err != nil { return err } - return v.ProcessedProjectGrantSequence(grant.Sequence, eventTimestamp) + return v.ProcessedProjectGrantSequence(event) } -func (v *View) PutProjectGrants(grants []*model.ProjectGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectGrants(grants []*model.ProjectGrantView, event *models.Event) error { err := view.PutProjectGrants(v.Db, grantedProjectTable, grants...) if err != nil { return err } - return v.ProcessedProjectGrantSequence(sequence, eventTimestamp) + return v.ProcessedProjectGrantSequence(event) } -func (v *View) DeleteProjectGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectGrant(grantID string, event *models.Event) error { err := view.DeleteProjectGrant(v.Db, grantedProjectTable, grantID) if err != nil { return err } - return v.ProcessedProjectGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectGrantSequence(event) } func (v *View) DeleteProjectGrantsByProjectID(projectID string) error { return view.DeleteProjectGrantsByProjectID(v.Db, grantedProjectTable, projectID) } -func (v *View) GetLatestProjectGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(grantedProjectTable) +func (v *View) GetLatestProjectGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(grantedProjectTable, aggregateType) } -func (v *View) ProcessedProjectGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(grantedProjectTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(grantedProjectTable, event) } func (v *View) UpdateProjectGrantSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_grant_member.go b/internal/management/repository/eventsourcing/view/project_grant_member.go index b9cf4391ba..83ae463190 100644 --- a/internal/management/repository/eventsourcing/view/project_grant_member.go +++ b/internal/management/repository/eventsourcing/view/project_grant_member.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,40 +28,40 @@ func (v *View) ProjectGrantMembersByUserID(userID string) ([]*model.ProjectGrant return view.ProjectGrantMembersByUserID(v.Db, projectGrantMemberTable, userID) } -func (v *View) PutProjectGrantMember(member *model.ProjectGrantMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectGrantMember(member *model.ProjectGrantMemberView, event *models.Event) error { err := view.PutProjectGrantMember(v.Db, projectGrantMemberTable, member) if err != nil { return err } - return v.ProcessedProjectGrantMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectGrantMemberSequence(event) } -func (v *View) PutProjectGrantMembers(members []*model.ProjectGrantMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectGrantMembers(members []*model.ProjectGrantMemberView, event *models.Event) error { err := view.PutProjectGrantMembers(v.Db, projectGrantMemberTable, members...) if err != nil { return err } - return v.ProcessedProjectGrantMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectGrantMemberSequence(event) } -func (v *View) DeleteProjectGrantMember(grantID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectGrantMember(grantID, userID string, event *models.Event) error { err := view.DeleteProjectGrantMember(v.Db, projectGrantMemberTable, grantID, userID) if err != nil { return nil } - return v.ProcessedProjectGrantMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectGrantMemberSequence(event) } func (v *View) DeleteProjectGrantMembersByProjectID(projectID string) error { return view.DeleteProjectGrantMembersByProjectID(v.Db, projectGrantMemberTable, projectID) } -func (v *View) GetLatestProjectGrantMemberSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectGrantMemberTable) +func (v *View) GetLatestProjectGrantMemberSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectGrantMemberTable, aggregateType) } -func (v *View) ProcessedProjectGrantMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectGrantMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectGrantMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(projectGrantMemberTable, event) } func (v *View) UpdateProjectGrantMemberSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_member.go b/internal/management/repository/eventsourcing/view/project_member.go index c21dee73e2..a867916939 100644 --- a/internal/management/repository/eventsourcing/view/project_member.go +++ b/internal/management/repository/eventsourcing/view/project_member.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,40 +28,40 @@ func (v *View) ProjectMembersByUserID(userID string) ([]*model.ProjectMemberView return view.ProjectMembersByUserID(v.Db, projectMemberTable, userID) } -func (v *View) PutProjectMember(project *model.ProjectMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectMember(project *model.ProjectMemberView, event *models.Event) error { err := view.PutProjectMember(v.Db, projectMemberTable, project) if err != nil { return err } - return v.ProcessedProjectMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectMemberSequence(event) } -func (v *View) PutProjectMembers(project []*model.ProjectMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectMembers(project []*model.ProjectMemberView, event *models.Event) error { err := view.PutProjectMembers(v.Db, projectMemberTable, project...) if err != nil { return err } - return v.ProcessedProjectMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectMemberSequence(event) } -func (v *View) DeleteProjectMember(projectID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectMember(projectID, userID string, event *models.Event) error { err := view.DeleteProjectMember(v.Db, projectMemberTable, projectID, userID) if err != nil { return nil } - return v.ProcessedProjectMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectMemberSequence(event) } func (v *View) DeleteProjectMembersByProjectID(projectID string) error { return view.DeleteProjectMembersByProjectID(v.Db, projectMemberTable, projectID) } -func (v *View) GetLatestProjectMemberSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectMemberTable) +func (v *View) GetLatestProjectMemberSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectMemberTable, aggregateType) } -func (v *View) ProcessedProjectMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(projectMemberTable, event) } func (v *View) UpdateProjectMemberSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_role.go b/internal/management/repository/eventsourcing/view/project_role.go index 6703a93efd..df56939309 100644 --- a/internal/management/repository/eventsourcing/view/project_role.go +++ b/internal/management/repository/eventsourcing/view/project_role.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,32 +32,32 @@ func (v *View) SearchProjectRoles(request *proj_model.ProjectRoleSearchRequest) return view.SearchProjectRoles(v.Db, projectRoleTable, request) } -func (v *View) PutProjectRole(project *model.ProjectRoleView, eventTimestamp time.Time) error { +func (v *View) PutProjectRole(project *model.ProjectRoleView, event *models.Event) error { err := view.PutProjectRole(v.Db, projectRoleTable, project) if err != nil { return err } - return v.ProcessedProjectRoleSequence(project.Sequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } -func (v *View) DeleteProjectRole(projectID, orgID, key string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectRole(projectID, orgID, key string, event *models.Event) error { err := view.DeleteProjectRole(v.Db, projectRoleTable, projectID, orgID, key) if err != nil { return nil } - return v.ProcessedProjectRoleSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } func (v *View) DeleteProjectRolesByProjectID(projectID string) error { return view.DeleteProjectRolesByProjectID(v.Db, projectRoleTable, projectID) } -func (v *View) GetLatestProjectRoleSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectRoleTable) +func (v *View) GetLatestProjectRoleSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectRoleTable, aggregateType) } -func (v *View) ProcessedProjectRoleSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectRoleTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectRoleSequence(event *models.Event) error { + return v.saveCurrentSequence(projectRoleTable, event) } func (v *View) UpdateProjectRoleSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/sequence.go b/internal/management/repository/eventsourcing/view/sequence.go index 82354c98e4..808ff58216 100644 --- a/internal/management/repository/eventsourcing/view/sequence.go +++ b/internal/management/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "management.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/management/repository/eventsourcing/view/user.go b/internal/management/repository/eventsourcing/view/user.go index 69fc14a270..cd7b2a4744 100644 --- a/internal/management/repository/eventsourcing/view/user.go +++ b/internal/management/repository/eventsourcing/view/user.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -40,39 +40,39 @@ func (v *View) UserMFAs(userID string) ([]*usr_model.MultiFactor, error) { return view.UserMFAs(v.Db, userTable, userID) } -func (v *View) PutUsers(user []*model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUsers(user []*model.UserView, event *models.Event) error { err := view.PutUsers(v.Db, userTable, user...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) PutUser(user *model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUser(user *model.UserView, event *models.Event) error { err := view.PutUser(v.Db, userTable, user) if err != nil { return err } - if sequence != 0 { - return v.ProcessedUserSequence(sequence, eventTimestamp) + if event.Sequence != 0 { + return v.ProcessedUserSequence(event) } return nil } -func (v *View) DeleteUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) if err != nil { return nil } - return v.ProcessedUserSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) GetLatestUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userTable) +func (v *View) GetLatestUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userTable, aggregateType) } -func (v *View) ProcessedUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSequence(event *models.Event) error { + return v.saveCurrentSequence(userTable, event) } func (v *View) UpdateUserSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/user_grant.go b/internal/management/repository/eventsourcing/view/user_grant.go index 209fbfe6f4..5f20eaa0f2 100644 --- a/internal/management/repository/eventsourcing/view/user_grant.go +++ b/internal/management/repository/eventsourcing/view/user_grant.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" "github.com/caos/zitadel/internal/usergrant/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -40,36 +40,36 @@ func (v *View) UserGrantsByOrgIDAndProjectID(orgID, projectID string) ([]*model. return view.UserGrantsByOrgIDAndProjectID(v.Db, userGrantTable, orgID, projectID) } -func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) error { err := view.PutUserGrant(v.Db, userGrantTable, grant) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) PutUserGrants(grants []*model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrants(grants []*model.UserGrantView, event *models.Event) error { err := view.PutUserGrants(v.Db, userGrantTable, grants...) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) DeleteUserGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) if err != nil { return nil } - return v.ProcessedUserGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) GetLatestUserGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userGrantTable) +func (v *View) GetLatestUserGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userGrantTable, aggregateType) } -func (v *View) ProcessedUserGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userGrantTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(userGrantTable, event) } func (v *View) UpdateUserGrantSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/user_membership.go b/internal/management/repository/eventsourcing/view/user_membership.go index 4c733dc875..fe80081194 100644 --- a/internal/management/repository/eventsourcing/view/user_membership.go +++ b/internal/management/repository/eventsourcing/view/user_membership.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,60 +24,60 @@ func (v *View) SearchUserMemberships(request *usr_model.UserMembershipSearchRequ return view.SearchUserMemberships(v.Db, userMembershipTable, request) } -func (v *View) PutUserMembership(membership *model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserMembership(membership *model.UserMembershipView, event *models.Event) error { err := view.PutUserMembership(v.Db, userMembershipTable, membership) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, event *models.Event) error { err := view.PutUserMemberships(v.Db, userMembershipTable, memberships...) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, event *models.Event) error { err := view.DeleteUserMembership(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByUserID(userID string, event *models.Event) error { err := view.DeleteUserMembershipsByUserID(v.Db, userMembershipTable, userID) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateIDAndObjectID(v.Db, userMembershipTable, aggregateID, objectID) if err != nil { return nil } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) GetLatestUserMembershipSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userMembershipTable) +func (v *View) GetLatestUserMembershipSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userMembershipTable, aggregateType) } -func (v *View) ProcessedUserMembershipSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userMembershipTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserMembershipSequence(event *models.Event) error { + return v.saveCurrentSequence(userMembershipTable, event) } func (v *View) UpdateUserMembershipSpoolerRunTimestamp() error { diff --git a/internal/notification/repository/eventsourcing/handler/handler.go b/internal/notification/repository/eventsourcing/handler/handler.go index a6db3edb74..aaf553ebdf 100644 --- a/internal/notification/repository/eventsourcing/handler/handler.go +++ b/internal/notification/repository/eventsourcing/handler/handler.go @@ -1,6 +1,9 @@ package handler import ( + "net/http" + "time" + "github.com/caos/logging" sd "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/types" @@ -12,8 +15,6 @@ import ( "github.com/caos/zitadel/internal/notification/repository/eventsourcing/view" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" - "net/http" - "time" ) type Configs map[string]*Config @@ -27,6 +28,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -35,34 +42,33 @@ type EventstoreRepos struct { IAMEvents *iam_es.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler { aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey) if err != nil { logging.Log("HANDL-s90ew").WithError(err).Debug("error create new aes crypto") } return []query.Handler{ - &NotifyUser{ - handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - orgEvents: repos.OrgEvents, - iamEvents: repos.IAMEvents, - iamID: systemDefaults.IamID, - }, - &Notification{ - handler: handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount}, - eventstore: eventstore, - userEvents: repos.UserEvents, - systemDefaults: systemDefaults, - AesCrypto: aesCrypto, - i18n: i18n, - statikDir: dir, - }, + newNotifyUser( + handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IAMEvents, + systemDefaults.IamID, + ), + newNotification( + handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es}, + repos.UserEvents, + systemDefaults, + aesCrypto, + i18n, + dir, + ), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } diff --git a/internal/notification/repository/eventsourcing/handler/notification.go b/internal/notification/repository/eventsourcing/handler/notification.go index 96ff4ae9ce..cebde591bf 100644 --- a/internal/notification/repository/eventsourcing/handler/notification.go +++ b/internal/notification/repository/eventsourcing/handler/notification.go @@ -7,34 +7,22 @@ import ( "time" "github.com/caos/logging" - "github.com/caos/zitadel/internal/api/authz" sd "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/crypto" - "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/i18n" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/notification/types" - "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type Notification struct { - handler - eventstore eventstore.Eventstore - userEvents *usr_event.UserEventstore - systemDefaults sd.SystemDefaults - AesCrypto crypto.EncryptionAlgorithm - i18n *i18n.Translator - statikDir http.FileSystem -} - const ( notificationTable = "notification.notifications" NotifyUserID = "NOTIFICATION" @@ -42,16 +30,69 @@ const ( labelPolicyTableDef = "adminapi.label_policies" ) +type Notification struct { + handler + userEvents *usr_event.UserEventstore + systemDefaults sd.SystemDefaults + AesCrypto crypto.EncryptionAlgorithm + i18n *i18n.Translator + statikDir http.FileSystem + subscription *eventstore.Subscription +} + +func newNotification( + handler handler, + userEvents *usr_event.UserEventstore, + defaults sd.SystemDefaults, + aesCrypto crypto.EncryptionAlgorithm, + translator *i18n.Translator, + statikDir http.FileSystem, +) *Notification { + h := &Notification{ + handler: handler, + userEvents: userEvents, + systemDefaults: defaults, + i18n: translator, + statikDir: statikDir, + AesCrypto: aesCrypto, + } + + h.subscribe() + + return h +} + +func (k *Notification) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (n *Notification) ViewModel() string { return notificationTable } +func (_ *Notification) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate} +} + +func (n *Notification) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := n.view.GetLatestNotificationSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (n *Notification) EventQuery() (*models.SearchQuery, error) { - sequence, err := n.view.GetLatestNotificationSequence() + sequence, err := n.view.GetLatestNotificationSequence("") if err != nil { return nil, err } - return eventsourcing.UserQuery(sequence.CurrentSequence), nil + return usr_event.UserQuery(sequence.CurrentSequence), nil } func (n *Notification) Reduce(event *models.Event) (err error) { @@ -71,12 +112,12 @@ func (n *Notification) Reduce(event *models.Event) (err error) { case es_model.DomainClaimed: err = n.handleDomainClaimed(event) default: - return n.view.ProcessedNotificationSequence(event.Sequence, event.CreationDate) + return n.view.ProcessedNotificationSequence(event) } if err != nil { return err } - return n.view.ProcessedNotificationSequence(event.Sequence, event.CreationDate) + return n.view.ProcessedNotificationSequence(event) } func (n *Notification) handleInitUserCode(event *models.Event) (err error) { @@ -229,12 +270,12 @@ func (n *Notification) checkIfAlreadyHandled(userID string, sequence uint64, eve } func (n *Notification) getUserEvents(userID string, sequence uint64) ([]*models.Event, error) { - query, err := eventsourcing.UserByIDQuery(userID, sequence) + query, err := usr_event.UserByIDQuery(userID, sequence) if err != nil { return nil, err } - return n.eventstore.FilterEvents(context.Background(), query) + return n.es.FilterEvents(context.Background(), query) } func (n *Notification) OnError(event *models.Event, err error) error { @@ -254,7 +295,7 @@ func getSetNotifyContextData(orgID string) context.Context { func (n *Notification) getLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { // read from Org policy, err := n.view.LabelPolicyByAggregateID(authz.GetCtxData(ctx).OrgID, labelPolicyTableOrg) - if errors.IsNotFound(err) { + if caos_errs.IsNotFound(err) { // read from default policy, err = n.view.LabelPolicyByAggregateID(n.systemDefaults.IamID, labelPolicyTableDef) if err != nil { diff --git a/internal/notification/repository/eventsourcing/handler/notify_user.go b/internal/notification/repository/eventsourcing/handler/notify_user.go index b41cd93ce7..4b82a16c0c 100644 --- a/internal/notification/repository/eventsourcing/handler/notify_user.go +++ b/internal/notification/repository/eventsourcing/handler/notify_user.go @@ -2,14 +2,13 @@ package handler import ( "context" - iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/logging" - "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" @@ -17,33 +16,72 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type NotifyUser struct { - handler - eventstore eventstore.Eventstore - orgEvents *org_events.OrgEventstore - iamEvents *iam_es.IAMEventstore - iamID string -} - const ( userTable = "notification.notify_users" ) +type NotifyUser struct { + handler + orgEvents *org_events.OrgEventstore + iamEvents *iam_es.IAMEventstore + iamID string + subscription *eventstore.Subscription +} + +func newNotifyUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + iamID string, +) *NotifyUser { + h := &NotifyUser{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *NotifyUser) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (p *NotifyUser) ViewModel() string { return userTable } -func (p *NotifyUser) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestNotifyUserSequence() +func (_ *NotifyUser) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (p *NotifyUser) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := p.view.GetLatestNotifyUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *NotifyUser) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestNotifyUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (u *NotifyUser) Reduce(event *models.Event) (err error) { +func (u *NotifyUser) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case es_model.UserAggregate: return u.ProcessUser(event) @@ -54,7 +92,7 @@ func (u *NotifyUser) Reduce(event *models.Event) (err error) { } } -func (u *NotifyUser) ProcessUser(event *models.Event) (err error) { +func (u *NotifyUser) ProcessUser(event *es_models.Event) (err error) { user := new(view_model.NotifyUser) switch event.Type { case es_model.UserAdded, @@ -93,17 +131,17 @@ func (u *NotifyUser) ProcessUser(event *models.Event) (err error) { } u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteNotifyUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteNotifyUser(event.AggregateID, event) default: - return u.view.ProcessedNotifyUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedNotifyUserSequence(event) } if err != nil { return err } - return u.view.PutNotifyUser(user, user.Sequence, event.CreationDate) + return u.view.PutNotifyUser(user, event) } -func (u *NotifyUser) ProcessOrg(event *models.Event) (err error) { +func (u *NotifyUser) ProcessOrg(event *es_models.Event) (err error) { switch event.Type { case org_es_model.OrgDomainVerified, org_es_model.OrgDomainRemoved, @@ -114,11 +152,11 @@ func (u *NotifyUser) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedNotifyUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedNotifyUserSequence(event) } } -func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *models.Event) error { +func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *es_models.Event) error { org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner)) if err != nil { return err @@ -136,15 +174,15 @@ func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *models.Event) error { } for _, user := range users { user.SetLoginNames(policy, org.Domains) - err := u.view.PutNotifyUser(user, 0, event.CreationDate) + err := u.view.PutNotifyUser(user, event) if err != nil { return err } } - return u.view.ProcessedNotifyUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedNotifyUserSequence(event) } -func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { +func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error { org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner)) if err != nil { return err @@ -165,7 +203,7 @@ func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *models.Event) erro } for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) - err := u.view.PutNotifyUser(user, 0, event.CreationDate) + err := u.view.PutNotifyUser(user, event) if err != nil { return err } @@ -190,7 +228,7 @@ func (u *NotifyUser) fillLoginNames(user *view_model.NotifyUser) (err error) { return nil } -func (p *NotifyUser) OnError(event *models.Event, err error) error { +func (p *NotifyUser) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-9spwf", "id", event.AggregateID).WithError(err).Warn("something went wrong in notify user handler") return spooler.HandleError(event, err, p.view.GetLatestNotifyUserFailedEvent, p.view.ProcessedNotifyUserFailedEvent, p.view.ProcessedNotifyUserSequence, p.errorCountUntilSkip) } diff --git a/internal/notification/repository/eventsourcing/spooler/lock.go b/internal/notification/repository/eventsourcing/spooler/lock.go index 46325b1495..fe14ec49fc 100644 --- a/internal/notification/repository/eventsourcing/spooler/lock.go +++ b/internal/notification/repository/eventsourcing/spooler/lock.go @@ -2,7 +2,6 @@ package spooler import ( "database/sql" - es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) @@ -15,5 +14,5 @@ type locker struct { } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) + return nil } diff --git a/internal/notification/repository/eventsourcing/view/notification.go b/internal/notification/repository/eventsourcing/view/notification.go index baeb75e162..b14242f47e 100644 --- a/internal/notification/repository/eventsourcing/view/notification.go +++ b/internal/notification/repository/eventsourcing/view/notification.go @@ -1,20 +1,20 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( notificationTable = "notification.notifications" ) -func (v *View) GetLatestNotificationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(notificationTable) +func (v *View) GetLatestNotificationSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(notificationTable, aggregateType) } -func (v *View) ProcessedNotificationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(notificationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedNotificationSequence(event *models.Event) error { + return v.saveCurrentSequence(notificationTable, event) } func (v *View) UpdateNotificationSpoolerRunTimestamp() error { diff --git a/internal/notification/repository/eventsourcing/view/notify_user.go b/internal/notification/repository/eventsourcing/view/notify_user.go index 44d1a1e32b..b7a24899f7 100644 --- a/internal/notification/repository/eventsourcing/view/notify_user.go +++ b/internal/notification/repository/eventsourcing/view/notify_user.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -15,13 +15,13 @@ func (v *View) NotifyUserByID(userID string) (*model.NotifyUser, error) { return view.NotifyUserByID(v.Db, notifyUserTable, userID) } -func (v *View) PutNotifyUser(user *model.NotifyUser, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutNotifyUser(user *model.NotifyUser, event *models.Event) error { err := view.PutNotifyUser(v.Db, notifyUserTable, user) if err != nil { return err } - if sequence != 0 { - return v.ProcessedNotifyUserSequence(sequence, eventTimestamp) + if event.Sequence != 0 { + return v.ProcessedNotifyUserSequence(event) } return nil } @@ -30,20 +30,20 @@ func (v *View) NotifyUsersByOrgID(orgID string) ([]*model.NotifyUser, error) { return view.NotifyUsersByOrgID(v.Db, notifyUserTable, orgID) } -func (v *View) DeleteNotifyUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteNotifyUser(userID string, event *models.Event) error { err := view.DeleteNotifyUser(v.Db, notifyUserTable, userID) if err != nil { return nil } - return v.ProcessedNotifyUserSequence(eventSequence, eventTimestamp) + return v.ProcessedNotifyUserSequence(event) } -func (v *View) GetLatestNotifyUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(notifyUserTable) +func (v *View) GetLatestNotifyUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(notifyUserTable, aggregateType) } -func (v *View) ProcessedNotifyUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(notifyUserTable, eventSequence, eventTimestamp) +func (v *View) ProcessedNotifyUserSequence(event *models.Event) error { + return v.saveCurrentSequence(notifyUserTable, event) } func (v *View) UpdateNotifyUserSpoolerRunTimestamp() error { diff --git a/internal/notification/repository/eventsourcing/view/sequence.go b/internal/notification/repository/eventsourcing/view/sequence.go index f1868e4f44..8d56cc19b9 100644 --- a/internal/notification/repository/eventsourcing/view/sequence.go +++ b/internal/notification/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "notification.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/org/repository/view/org_member_view.go b/internal/org/repository/view/org_member_view.go index ef52864b52..c9035d38dc 100644 --- a/internal/org/repository/view/org_member_view.go +++ b/internal/org/repository/view/org_member_view.go @@ -17,7 +17,7 @@ func OrgMemberByIDs(db *gorm.DB, table, orgID, userID string) (*model.OrgMemberV query := repository.PrepareGetByQuery(table, orgIDQuery, userIDQuery) err := query(db, member) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-DG1qh", "Errors.Org.MemberNotFound") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-gIaTM", "Errors.Org.MemberNotFound") } return member, err } diff --git a/internal/user/repository/view/model/token.go b/internal/user/repository/view/model/token.go index 725132ad52..eb20cf76c9 100644 --- a/internal/user/repository/view/model/token.go +++ b/internal/user/repository/view/model/token.go @@ -2,13 +2,14 @@ package model import ( "encoding/json" + "time" + "github.com/caos/logging" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - "time" "github.com/lib/pq" ) diff --git a/internal/view/model/view.go b/internal/view/model/view.go index 6f766b912b..5cb354a1b7 100644 --- a/internal/view/model/view.go +++ b/internal/view/model/view.go @@ -10,4 +10,5 @@ type View struct { CurrentSequence uint64 EventTimestamp time.Time LastSuccessfulSpoolerRun time.Time + AggregateType string } diff --git a/internal/view/repository/sequence.go b/internal/view/repository/sequence.go index 10023b2b66..056787a5a7 100644 --- a/internal/view/repository/sequence.go +++ b/internal/view/repository/sequence.go @@ -1,11 +1,13 @@ package repository import ( - caos_errs "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/view/model" - "github.com/jinzhu/gorm" "strings" "time" + + caos_errs "github.com/caos/zitadel/internal/errors" + int_model "github.com/caos/zitadel/internal/model" + "github.com/caos/zitadel/internal/view/model" + "github.com/jinzhu/gorm" ) type CurrentSequence struct { @@ -13,6 +15,18 @@ type CurrentSequence struct { CurrentSequence uint64 `gorm:"column:current_sequence"` EventTimestamp time.Time `gorm:"column:event_timestamp"` LastSuccessfulSpoolerRun time.Time `gorm:"column:last_successful_spooler_run"` + AggregateType string `gorm:"column:aggregate_type;primary_key"` +} + +type currentSequenceViewWithSequence struct { + ViewName string `gorm:"column:view_name;primary_key"` + CurrentSequence uint64 `gorm:"column:current_sequence"` + LastSuccessfulSpoolerRun time.Time `gorm:"column:last_successful_spooler_run"` +} + +type currentSequenceView struct { + ViewName string `gorm:"column:view_name;primary_key"` + LastSuccessfulSpoolerRun time.Time `gorm:"column:last_successful_spooler_run"` } type SequenceSearchKey int32 @@ -20,6 +34,7 @@ type SequenceSearchKey int32 const ( SequenceSearchKeyUndefined SequenceSearchKey = iota SequenceSearchKeyViewName + SequenceSearchKeyAggregateType ) type sequenceSearchKey SequenceSearchKey @@ -28,11 +43,30 @@ func (key sequenceSearchKey) ToColumnName() string { switch SequenceSearchKey(key) { case SequenceSearchKeyViewName: return "view_name" + case SequenceSearchKeyAggregateType: + return "aggregate_type" default: return "" } } +type sequenceSearchQuery struct { + key sequenceSearchKey + value string +} + +func (q *sequenceSearchQuery) GetKey() ColumnKey { + return q.key +} + +func (q *sequenceSearchQuery) GetMethod() int_model.SearchMethod { + return int_model.SearchMethodEquals +} + +func (q *sequenceSearchQuery) GetValue() interface{} { + return q.value +} + func CurrentSequenceToModel(sequence *CurrentSequence) *model.View { dbView := strings.Split(sequence.ViewName, ".") return &model.View{ @@ -41,26 +75,44 @@ func CurrentSequenceToModel(sequence *CurrentSequence) *model.View { CurrentSequence: sequence.CurrentSequence, EventTimestamp: sequence.EventTimestamp, LastSuccessfulSpoolerRun: sequence.LastSuccessfulSpoolerRun, + AggregateType: sequence.AggregateType, } } -func SaveCurrentSequence(db *gorm.DB, table, viewName string, sequence uint64, eventTimestamp time.Time) error { - return UpdateCurrentSequence(db, table, &CurrentSequence{viewName, sequence, eventTimestamp, time.Now()}) +func SaveCurrentSequence(db *gorm.DB, table, viewName, aggregateType string, sequence uint64, eventTimestamp time.Time) error { + return UpdateCurrentSequence(db, table, &CurrentSequence{viewName, sequence, eventTimestamp, time.Now(), aggregateType}) } -func UpdateCurrentSequence(db *gorm.DB, table string, currentSequence *CurrentSequence) error { - save := PrepareSave(table) - err := save(db, currentSequence) +func UpdateCurrentSequence(db *gorm.DB, table string, currentSequence *CurrentSequence) (err error) { + var seq interface{} = currentSequence + if currentSequence.AggregateType == "" && currentSequence.CurrentSequence > 0 { + //spooler run + seq = ¤tSequenceView{ViewName: currentSequence.ViewName, LastSuccessfulSpoolerRun: currentSequence.LastSuccessfulSpoolerRun} + } else if currentSequence.AggregateType == "" { + //reset current sequence on view + seq = ¤tSequenceViewWithSequence{ViewName: currentSequence.ViewName, LastSuccessfulSpoolerRun: currentSequence.LastSuccessfulSpoolerRun, CurrentSequence: currentSequence.CurrentSequence} + } + save := PrepareSave(table) + err = save(db, seq) if err != nil { return caos_errs.ThrowInternal(err, "VIEW-5kOhP", "unable to updated processed sequence") } return nil } -func LatestSequence(db *gorm.DB, table, viewName string) (*CurrentSequence, error) { +func LatestSequence(db *gorm.DB, table, viewName, aggregateType string) (*CurrentSequence, error) { + searchQueries := make([]SearchQuery, 0, 2) + searchQueries = append(searchQueries, &sequenceSearchQuery{key: sequenceSearchKey(SequenceSearchKeyViewName), value: viewName}) + if aggregateType != "" { + searchQueries = append(searchQueries, &sequenceSearchQuery{key: sequenceSearchKey(SequenceSearchKeyAggregateType), value: aggregateType}) + } else { + // ensure highest sequence of view + db = db.Order("current_sequence DESC") + } + + query := PrepareGetByQuery(table, searchQueries...) sequence := new(CurrentSequence) - query := PrepareGetByKey(table, sequenceSearchKey(SequenceSearchKeyViewName), viewName) err := query(db, sequence) if err == nil { @@ -89,5 +141,5 @@ func ClearView(db *gorm.DB, truncateView, sequenceTable string) error { if err != nil { return err } - return SaveCurrentSequence(db, sequenceTable, truncateView, 0, time.Now()) + return SaveCurrentSequence(db, sequenceTable, truncateView, "", 0, time.Now()) } diff --git a/migrations/cockroach/V1.26__current_sequence_table.sql b/migrations/cockroach/V1.26__current_sequence_table.sql new file mode 100644 index 0000000000..61bf4bb87a --- /dev/null +++ b/migrations/cockroach/V1.26__current_sequence_table.sql @@ -0,0 +1,21 @@ +ALTER TABLE management.current_sequences ADD COLUMN aggregate_type STRING NOT NULL DEFAULT ''; +ALTER TABLE auth.current_sequences ADD COLUMN aggregate_type STRING NOT NULL DEFAULT ''; +ALTER TABLE authz.current_sequences ADD COLUMN aggregate_type STRING NOT NULL DEFAULT ''; +ALTER TABLE adminapi.current_sequences ADD COLUMN aggregate_type STRING NOT NULL DEFAULT ''; +ALTER TABLE notification.current_sequences ADD COLUMN aggregate_type STRING NOT NULL DEFAULT ''; + +BEGIN; + +ALTER TABLE management.current_sequences DROP CONSTRAINT "primary"; +ALTER TABLE auth.current_sequences DROP CONSTRAINT "primary"; +ALTER TABLE authz.current_sequences DROP CONSTRAINT "primary"; +ALTER TABLE adminapi.current_sequences DROP CONSTRAINT "primary"; +ALTER TABLE notification.current_sequences DROP CONSTRAINT "primary"; + +ALTER TABLE management.current_sequences ADD CONSTRAINT "primary" PRIMARY KEY (view_name, aggregate_type); +ALTER TABLE auth.current_sequences ADD CONSTRAINT "primary" PRIMARY KEY (view_name, aggregate_type); +ALTER TABLE authz.current_sequences ADD CONSTRAINT "primary" PRIMARY KEY (view_name, aggregate_type); +ALTER TABLE adminapi.current_sequences ADD CONSTRAINT "primary" PRIMARY KEY (view_name, aggregate_type); +ALTER TABLE notification.current_sequences ADD CONSTRAINT "primary" PRIMARY KEY (view_name, aggregate_type); + +COMMIT; \ No newline at end of file diff --git a/pkg/grpc/management/mock/management.proto.mock.go b/pkg/grpc/management/mock/management.proto.mock.go index b76d47eaf5..28011d98d1 100644 --- a/pkg/grpc/management/mock/management.proto.mock.go +++ b/pkg/grpc/management/mock/management.proto.mock.go @@ -1836,6 +1836,46 @@ func (mr *MockManagementServiceClientMockRecorder) RemoveLoginPolicy(arg0, arg1 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveLoginPolicy", reflect.TypeOf((*MockManagementServiceClient)(nil).RemoveLoginPolicy), varargs...) } +// RemoveMfaOTP mocks base method +func (m *MockManagementServiceClient) RemoveMfaOTP(arg0 context.Context, arg1 *management.UserID, arg2 ...grpc.CallOption) (*emptypb.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RemoveMfaOTP", varargs...) + ret0, _ := ret[0].(*emptypb.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RemoveMfaOTP indicates an expected call of RemoveMfaOTP +func (mr *MockManagementServiceClientMockRecorder) RemoveMfaOTP(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMfaOTP", reflect.TypeOf((*MockManagementServiceClient)(nil).RemoveMfaOTP), varargs...) +} + +// RemoveMfaU2F mocks base method +func (m *MockManagementServiceClient) RemoveMfaU2F(arg0 context.Context, arg1 *management.WebAuthNTokenID, arg2 ...grpc.CallOption) (*emptypb.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RemoveMfaU2F", varargs...) + ret0, _ := ret[0].(*emptypb.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RemoveMfaU2F indicates an expected call of RemoveMfaU2F +func (mr *MockManagementServiceClientMockRecorder) RemoveMfaU2F(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMfaU2F", reflect.TypeOf((*MockManagementServiceClient)(nil).RemoveMfaU2F), varargs...) +} + // RemoveMultiFactorFromLoginPolicy mocks base method func (m *MockManagementServiceClient) RemoveMultiFactorFromLoginPolicy(arg0 context.Context, arg1 *management.MultiFactor, arg2 ...grpc.CallOption) (*emptypb.Empty, error) { m.ctrl.T.Helper() From 3118a99c1e2d2460b5563789fed8e3aebe6eeee2 Mon Sep 17 00:00:00 2001 From: Silvan Date: Mon, 21 Dec 2020 18:42:34 +0100 Subject: [PATCH 17/22] fix: pubsub (#1122) * start sub * start implement subsciptions * start subscription * implementation for member done * admin done * fix: tests * extend handlers * prepary notification * no errors in adminapi * changed current sequence in all packages * ignore mocks * works * subscriptions as singleton * tests * refactor: rename function scope var * fix: process ALL previous sequences * fix: spooler and pubsub * handler check * fix: process events until all done * fix break on query err * fix: handler * fix: process sequence or return error * check aggregate id * fix: log only in error case * fix tests * fix: handlers * fix: spooler * fix: spooler * fix: tests * fix: continue Co-authored-by: Livio Amstutz --- cmd/zitadel/startup.yaml | 10 ++-- .../eventsourcing/handler/iam_member.go | 4 +- .../handler/user_external_idps.go | 1 - .../repository/eventsourcing/view/user.go | 10 ++-- .../eventsourcing/handler/application.go | 5 +- .../eventsourcing/handler/project_role.go | 6 ++- .../eventsourcing/handler/user_membership.go | 21 ++++---- .../eventsourcing/view/application.go | 4 +- .../auth/repository/eventsourcing/view/key.go | 9 ++-- .../eventsourcing/view/machine_keys.go | 14 +++-- .../eventsourcing/view/project_role.go | 5 +- .../repository/eventsourcing/view/token.go | 17 +++--- .../repository/eventsourcing/view/user.go | 5 +- .../eventsourcing/view/user_grant.go | 5 +- .../eventsourcing/view/user_membership.go | 17 +++--- .../eventsourcing/view/user_session.go | 5 +- .../eventsourcing/view/application.go | 5 +- .../repository/eventsourcing/view/token.go | 9 ++-- .../eventsourcing/view/user_grant.go | 5 +- internal/eventstore/models/object.go | 2 + .../eventstore/models/search_query_old.go | 2 +- internal/eventstore/query/handler.go | 53 +++++++++++++------ internal/eventstore/spooler/spooler.go | 43 ++++++++++----- internal/eventstore/spooler/spooler_test.go | 33 ++++++++---- .../eventsourcing/eventstore/user.go | 12 ++--- .../eventsourcing/handler/project_grant.go | 6 ++- .../handler/project_grant_member.go | 6 ++- .../repository/eventsourcing/handler/user.go | 2 +- .../eventsourcing/handler/user_grant.go | 2 - .../eventsourcing/handler/user_membership.go | 4 +- .../eventsourcing/view/application.go | 5 +- .../eventsourcing/view/machine_keys.go | 14 +++-- .../eventsourcing/view/org_domain.go | 10 ++-- .../eventsourcing/view/org_member.go | 9 ++-- .../repository/eventsourcing/view/project.go | 5 +- .../view/project_grant_member.go | 5 +- .../eventsourcing/view/project_member.go | 5 +- .../eventsourcing/view/project_role.go | 5 +- .../repository/eventsourcing/view/user.go | 10 ++-- .../eventsourcing/view/user_grant.go | 5 +- .../eventsourcing/view/user_membership.go | 17 +++--- .../eventsourcing/handler/notification.go | 2 - .../eventsourcing/view/notify_user.go | 10 ++-- .../notification/templates/templateData.go | 4 +- .../eventsourcing/eventstore_mock_test.go | 2 +- .../eventsourcing/eventstore_test.go | 2 +- .../repository/view/application_view.go | 2 +- .../repository/view/project_role_view.go | 12 ++--- .../repository/eventsourcing/eventstore.go | 3 +- 49 files changed, 256 insertions(+), 193 deletions(-) diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 9f0f011d60..8e92800397 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -49,7 +49,7 @@ AuthZ: Key: $CR_AUTHZ_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 Auth: @@ -98,7 +98,7 @@ Auth: Key: $CR_AUTH_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 KeyConfig: Size: 2048 @@ -142,7 +142,7 @@ Admin: Key: $CR_ADMINAPI_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 Mgmt: @@ -179,7 +179,7 @@ Mgmt: Key: $CR_MANAGEMENT_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 API: @@ -292,6 +292,6 @@ Notification: Key: $CR_NOTIFICATION_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 Handlers: \ No newline at end of file diff --git a/internal/admin/repository/eventsourcing/handler/iam_member.go b/internal/admin/repository/eventsourcing/handler/iam_member.go index 19db852e42..79aafcf055 100644 --- a/internal/admin/repository/eventsourcing/handler/iam_member.go +++ b/internal/admin/repository/eventsourcing/handler/iam_member.go @@ -74,14 +74,14 @@ func (m *IAMMember) EventQuery() (*es_models.SearchQuery, error) { func (m *IAMMember) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: - err = m.processIamMember(event) + err = m.processIAMMember(event) case usr_es_model.UserAggregate: err = m.processUser(event) } return err } -func (m *IAMMember) processIamMember(event *es_models.Event) (err error) { +func (m *IAMMember) processIAMMember(event *es_models.Event) (err error) { member := new(iam_model.IAMMemberView) switch event.Type { case model.IAMMemberAdded: diff --git a/internal/admin/repository/eventsourcing/handler/user_external_idps.go b/internal/admin/repository/eventsourcing/handler/user_external_idps.go index cf4e4d05ce..f7719e601b 100644 --- a/internal/admin/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/admin/repository/eventsourcing/handler/user_external_idps.go @@ -152,7 +152,6 @@ func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { default: return i.view.ProcessedExternalIDPSequence(event) } - return nil } func (i *ExternalIDP) fillData(externalIDP *usr_view_model.ExternalIDPView) error { diff --git a/internal/admin/repository/eventsourcing/view/user.go b/internal/admin/repository/eventsourcing/view/user.go index 6e34155c96..9694b41f78 100644 --- a/internal/admin/repository/eventsourcing/view/user.go +++ b/internal/admin/repository/eventsourcing/view/user.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" @@ -53,16 +54,13 @@ func (v *View) PutUser(user *model.UserView, event *models.Event) error { if err != nil { return err } - if event.Sequence != 0 { - return v.ProcessedUserSequence(event) - } - return nil + return v.ProcessedUserSequence(event) } func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserSequence(event) } diff --git a/internal/auth/repository/eventsourcing/handler/application.go b/internal/auth/repository/eventsourcing/handler/application.go index 2ce66a2c22..5a8d5d41ff 100644 --- a/internal/auth/repository/eventsourcing/handler/application.go +++ b/internal/auth/repository/eventsourcing/handler/application.go @@ -116,7 +116,10 @@ func (a *Application) Reduce(event *models.Event) (err error) { } return a.view.PutApplications(apps, event) case es_model.ProjectRemoved: - return a.view.DeleteApplicationsByProjectID(event.AggregateID) + err = a.view.DeleteApplicationsByProjectID(event.AggregateID) + if err == nil { + return a.view.ProcessedApplicationSequence(event) + } default: return a.view.ProcessedApplicationSequence(event) } diff --git a/internal/auth/repository/eventsourcing/handler/project_role.go b/internal/auth/repository/eventsourcing/handler/project_role.go index 9d7e684c7c..5ee8722bdc 100644 --- a/internal/auth/repository/eventsourcing/handler/project_role.go +++ b/internal/auth/repository/eventsourcing/handler/project_role.go @@ -2,7 +2,6 @@ package handler import ( "github.com/caos/logging" - "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" @@ -93,7 +92,10 @@ func (p *ProjectRole) Reduce(event *es_models.Event) (err error) { } return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event) case model.ProjectRemoved: - return p.view.DeleteProjectRolesByProjectID(event.AggregateID) + err := p.view.DeleteProjectRolesByProjectID(event.AggregateID) + if err == nil { + return p.view.ProcessedProjectRoleSequence(event) + } default: return p.view.ProcessedProjectRoleSequence(event) } diff --git a/internal/auth/repository/eventsourcing/handler/user_membership.go b/internal/auth/repository/eventsourcing/handler/user_membership.go index ddc6fa6055..e1a41eccf5 100644 --- a/internal/auth/repository/eventsourcing/handler/user_membership.go +++ b/internal/auth/repository/eventsourcing/handler/user_membership.go @@ -3,23 +3,20 @@ package handler import ( "context" - "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - org_model "github.com/caos/zitadel/internal/org/model" - org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" - proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" - proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" - "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" + org_model "github.com/caos/zitadel/internal/org/model" + org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" + proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" + proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/model" + "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/view/model" ) @@ -88,7 +85,7 @@ func (m *UserMembership) EventQuery() (*models.SearchQuery, error) { func (m *UserMembership) Reduce(event *models.Event) (err error) { switch event.AggregateType { case iam_es_model.IAMAggregate: - err = m.processIam(event) + err = m.processIAM(event) case org_es_model.OrgAggregate: err = m.processOrg(event) case proj_es_model.ProjectAggregate: @@ -99,7 +96,7 @@ func (m *UserMembership) Reduce(event *models.Event) (err error) { return err } -func (m *UserMembership) processIam(event *models.Event) (err error) { +func (m *UserMembership) processIAM(event *models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { diff --git a/internal/auth/repository/eventsourcing/view/application.go b/internal/auth/repository/eventsourcing/view/application.go index dabe7727cb..5a7f2c6f1b 100644 --- a/internal/auth/repository/eventsourcing/view/application.go +++ b/internal/auth/repository/eventsourcing/view/application.go @@ -46,8 +46,8 @@ func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Even func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedApplicationSequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/key.go b/internal/auth/repository/eventsourcing/view/key.go index 49b65cf38d..f8fca89445 100644 --- a/internal/auth/repository/eventsourcing/view/key.go +++ b/internal/auth/repository/eventsourcing/view/key.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/key/repository/view" @@ -42,16 +43,16 @@ func (v *View) PutKeys(privateKey, publicKey *model.KeyView, event *models.Event func (v *View) DeleteKey(keyID string, private bool, event *models.Event) error { err := view.DeleteKey(v.Db, keyTable, keyID, private) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedKeySequence(event) } func (v *View) DeleteKeyPair(keyID string, event *models.Event) error { err := view.DeleteKeyPair(v.Db, keyTable, keyID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedKeySequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/machine_keys.go b/internal/auth/repository/eventsourcing/view/machine_keys.go index 4aab211292..b5412d7240 100644 --- a/internal/auth/repository/eventsourcing/view/machine_keys.go +++ b/internal/auth/repository/eventsourcing/view/machine_keys.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" @@ -33,24 +34,21 @@ func (v *View) PutMachineKey(key *model.MachineKeyView, event *models.Event) err if err != nil { return err } - if event.Sequence != 0 { - return v.ProcessedMachineKeySequence(event) - } - return nil + return v.ProcessedMachineKeySequence(event) } func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedMachineKeySequence(event) } func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedMachineKeySequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/project_role.go b/internal/auth/repository/eventsourcing/view/project_role.go index 5c2751e154..c5a99bfe4a 100644 --- a/internal/auth/repository/eventsourcing/view/project_role.go +++ b/internal/auth/repository/eventsourcing/view/project_role.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" @@ -42,8 +43,8 @@ func (v *View) PutProjectRole(role *model.ProjectRoleView, event *models.Event) func (v *View) DeleteProjectRole(projectID, orgID, key string, event *models.Event) error { err := view.DeleteProjectRole(v.Db, projectRoleTable, projectID, orgID, key) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedProjectRoleSequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/token.go b/internal/auth/repository/eventsourcing/view/token.go index 67644c0865..105034a364 100644 --- a/internal/auth/repository/eventsourcing/view/token.go +++ b/internal/auth/repository/eventsourcing/view/token.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_view "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" @@ -37,32 +38,32 @@ func (v *View) PutTokens(token []*model.TokenView, event *models.Event) error { func (v *View) DeleteToken(tokenID string, event *models.Event) error { err := usr_view.DeleteToken(v.Db, tokenTable, tokenID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedTokenSequence(event) } func (v *View) DeleteSessionTokens(agentID, userID string, event *models.Event) error { err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedTokenSequence(event) } func (v *View) DeleteUserTokens(userID string, event *models.Event) error { err := usr_view.DeleteUserTokens(v.Db, tokenTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedTokenSequence(event) } func (v *View) DeleteApplicationTokens(event *models.Event, ids ...string) error { err := usr_view.DeleteApplicationTokens(v.Db, tokenTable, ids) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedTokenSequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/user.go b/internal/auth/repository/eventsourcing/view/user.go index f7eaea8505..21de8cef8d 100644 --- a/internal/auth/repository/eventsourcing/view/user.go +++ b/internal/auth/repository/eventsourcing/view/user.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" @@ -70,8 +71,8 @@ func (v *View) PutUsers(users []*model.UserView, event *models.Event) error { func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserSequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/user_grant.go b/internal/auth/repository/eventsourcing/view/user_grant.go index 4121a210ff..d9a52a586e 100644 --- a/internal/auth/repository/eventsourcing/view/user_grant.go +++ b/internal/auth/repository/eventsourcing/view/user_grant.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" @@ -54,8 +55,8 @@ func (v *View) PutUserGrants(grants []*model.UserGrantView, event *models.Event) func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserGrantSequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/user_membership.go b/internal/auth/repository/eventsourcing/view/user_membership.go index d13ec0ec9e..f3d9fae70e 100644 --- a/internal/auth/repository/eventsourcing/view/user_membership.go +++ b/internal/auth/repository/eventsourcing/view/user_membership.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" @@ -46,32 +47,32 @@ func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, e func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, event *models.Event) error { err := view.DeleteUserMembership(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } func (v *View) DeleteUserMembershipsByUserID(userID string, event *models.Event) error { err := view.DeleteUserMembershipsByUserID(v.Db, userMembershipTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateIDAndObjectID(v.Db, userMembershipTable, aggregateID, objectID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } diff --git a/internal/auth/repository/eventsourcing/view/user_session.go b/internal/auth/repository/eventsourcing/view/user_session.go index f0ac1ae3cd..16ce4c7986 100644 --- a/internal/auth/repository/eventsourcing/view/user_session.go +++ b/internal/auth/repository/eventsourcing/view/user_session.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" @@ -45,8 +46,8 @@ func (v *View) PutUserSessions(userSession []*model.UserSessionView, event *mode func (v *View) DeleteUserSessions(userID string, event *models.Event) error { err := view.DeleteUserSessions(v.Db, userSessionTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserSessionSequence(event) } diff --git a/internal/authz/repository/eventsourcing/view/application.go b/internal/authz/repository/eventsourcing/view/application.go index 07448bf64f..84ae564981 100644 --- a/internal/authz/repository/eventsourcing/view/application.go +++ b/internal/authz/repository/eventsourcing/view/application.go @@ -3,6 +3,7 @@ package view import ( "context" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" @@ -44,8 +45,8 @@ func (v *View) PutApplication(project *model.ApplicationView, event *models.Even func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedApplicationSequence(event) } diff --git a/internal/authz/repository/eventsourcing/view/token.go b/internal/authz/repository/eventsourcing/view/token.go index da411da937..d2987a8194 100644 --- a/internal/authz/repository/eventsourcing/view/token.go +++ b/internal/authz/repository/eventsourcing/view/token.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_view "github.com/caos/zitadel/internal/user/repository/view" usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" @@ -25,16 +26,16 @@ func (v *View) PutToken(token *usr_view_model.TokenView, event *models.Event) er func (v *View) DeleteToken(tokenID string, event *models.Event) error { err := usr_view.DeleteToken(v.Db, tokenTable, tokenID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedTokenSequence(event) } func (v *View) DeleteSessionTokens(agentID, userID string, event *models.Event) error { err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedTokenSequence(event) } diff --git a/internal/authz/repository/eventsourcing/view/user_grant.go b/internal/authz/repository/eventsourcing/view/user_grant.go index 119ab9ae23..84de9319ea 100644 --- a/internal/authz/repository/eventsourcing/view/user_grant.go +++ b/internal/authz/repository/eventsourcing/view/user_grant.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" @@ -42,8 +43,8 @@ func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) err func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserGrantSequence(event) } diff --git a/internal/eventstore/models/object.go b/internal/eventstore/models/object.go index 62a6df14de..4444fc02d6 100644 --- a/internal/eventstore/models/object.go +++ b/internal/eventstore/models/object.go @@ -15,6 +15,8 @@ type ObjectRoot struct { func (o *ObjectRoot) AppendEvent(event *Event) { if o.AggregateID == "" { o.AggregateID = event.AggregateID + } else if o.AggregateID != event.AggregateID { + return } if o.ResourceOwner == "" { o.ResourceOwner = event.ResourceOwner diff --git a/internal/eventstore/models/search_query_old.go b/internal/eventstore/models/search_query_old.go index 0d6e1ebc6c..1748b9d4d7 100644 --- a/internal/eventstore/models/search_query_old.go +++ b/internal/eventstore/models/search_query_old.go @@ -70,7 +70,7 @@ func (q *SearchQuery) ResourceOwnerFilter(resourceOwner string) *SearchQuery { func (q *SearchQuery) setFilter(filter *Filter) *SearchQuery { for i, f := range q.Filters { - if f.field == filter.field { + if f.field == filter.field && f.field != Field_LatestSequence { q.Filters[i] = filter return q } diff --git a/internal/eventstore/query/handler.go b/internal/eventstore/query/handler.go index 07f4f6ce3d..84a79d3079 100755 --- a/internal/eventstore/query/handler.go +++ b/internal/eventstore/query/handler.go @@ -9,6 +9,10 @@ import ( "github.com/caos/zitadel/internal/eventstore/models" ) +const ( + eventLimit = 10000 +) + type Handler interface { ViewModel() string EventQuery() (*models.SearchQuery, error) @@ -29,28 +33,47 @@ func ReduceEvent(handler Handler, event *models.Event) { logging.Log("HANDL-BmpkC").WithError(err).Warn("unable to get current sequence") return } - if event.PreviousSequence > currentSequence { - searchQuery := models.NewSearchQuery(). - AggregateTypeFilter(handler.AggregateTypes()...). - SequenceBetween(currentSequence, event.PreviousSequence) - events, err := handler.Eventstore().FilterEvents(context.Background(), searchQuery) + searchQuery := models.NewSearchQuery(). + AggregateTypeFilter(handler.AggregateTypes()...). + SequenceBetween(currentSequence, event.Sequence). + SetLimit(eventLimit) + + unprocessedEvents, err := handler.Eventstore().FilterEvents(context.Background(), searchQuery) + if err != nil { + logging.LogWithFields("HANDL-L6YH1", "seq", event.Sequence).Warn("filter failed") + return + } + + processedSequences := map[models.AggregateType]uint64{} + + for _, unprocessedEvent := range unprocessedEvents { + currentSequence, err := handler.CurrentSequence(unprocessedEvent) if err != nil { - logging.LogWithFields("HANDL-L6YH1", "seq", event.Sequence).Warn("filter failed") + logging.Log("HANDL-BmpkC").WithError(err).Warn("unable to get current sequence") return } - for _, previousEvent := range events { - //if other process already updated view - //TODO: correct? - if event.PreviousSequence > previousEvent.Sequence { - continue + _, ok := processedSequences[unprocessedEvent.AggregateType] + if !ok { + processedSequences[unprocessedEvent.AggregateType] = currentSequence + } + if processedSequences[unprocessedEvent.AggregateType] != currentSequence { + if currentSequence < processedSequences[unprocessedEvent.AggregateType] { + logging.LogWithFields("QUERY-DOYVN", + "processed", processedSequences[unprocessedEvent.AggregateType], + "current", currentSequence, + "view", handler.ViewModel()). + Warn("sequence not matching") } - err = handler.Reduce(previousEvent) - logging.LogWithFields("HANDL-V42TI", "seq", previousEvent.Sequence).OnError(err).Warn("reduce failed") return } - } else if event.PreviousSequence > 0 && event.PreviousSequence < currentSequence { - logging.LogWithFields("HANDL-w9Bdy", "previousSeq", event.PreviousSequence, "currentSeq", currentSequence).Debug("already processed") + + err = handler.Reduce(unprocessedEvent) + logging.LogWithFields("HANDL-V42TI", "seq", unprocessedEvent.Sequence).OnError(err).Warn("reduce failed") + processedSequences[unprocessedEvent.AggregateType] = unprocessedEvent.Sequence + } + if len(unprocessedEvents) == eventLimit { + logging.LogWithFields("QUERY-BSqe9", "seq", event.Sequence).Warn("didnt process event") return } err = handler.Reduce(event) diff --git a/internal/eventstore/spooler/spooler.go b/internal/eventstore/spooler/spooler.go index 723fbe9229..b5c844e482 100644 --- a/internal/eventstore/spooler/spooler.go +++ b/internal/eventstore/spooler/spooler.go @@ -4,6 +4,7 @@ import ( "context" "strconv" "sync" + "time" "github.com/caos/logging" "github.com/caos/zitadel/internal/eventstore" @@ -11,8 +12,6 @@ import ( "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/view/repository" - - "time" ) type Spooler struct { @@ -71,14 +70,26 @@ func (s *spooledHandler) load(workerID string) { hasLocked := s.lock(ctx, errs, workerID) if <-hasLocked { - events, err := s.query(ctx) - if err != nil { - errs <- err - } else { - errs <- s.process(ctx, events, workerID) - logging.Log("SPOOL-0pV8o").WithField("view", s.ViewModel()).WithField("worker", workerID).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("process done") + for { + events, err := s.query(ctx) + if err != nil { + errs <- err + break + } + err = s.process(ctx, events, workerID) + if err != nil { + errs <- err + break + } + if uint64(len(events)) < s.QueryLimit() { + // no more events to process + // stop chan + if ctx.Err() == nil { + errs <- nil + } + break + } } - } <-ctx.Done() } @@ -92,14 +103,19 @@ func (s *spooledHandler) awaitError(cancel func(), errs chan error, workerID str } func (s *spooledHandler) process(ctx context.Context, events []*models.Event, workerID string) error { - for _, event := range events { + for i, event := range events { select { case <-ctx.Done(): logging.LogWithFields("SPOOL-FTKwH", "view", s.ViewModel(), "worker", workerID, "traceID", tracing.TraceIDFromCtx(ctx)).Debug("context canceled") return nil default: if err := s.Reduce(event); err != nil { - return s.OnError(event, err) + err = s.OnError(event, err) + if err == nil { + continue + } + time.Sleep(100 * time.Millisecond) + return s.process(ctx, events[i:], workerID) } } } @@ -167,7 +183,8 @@ func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID s func HandleError(event *models.Event, failedErr error, latestFailedEvent func(sequence uint64) (*repository.FailedEvent, error), processFailedEvent func(*repository.FailedEvent) error, - processSequence func(*models.Event) error, errorCountUntilSkip uint64) error { + processSequence func(*models.Event) error, + errorCountUntilSkip uint64) error { failedEvent, err := latestFailedEvent(event.Sequence) if err != nil { return err @@ -181,7 +198,7 @@ func HandleError(event *models.Event, failedErr error, if errorCountUntilSkip <= failedEvent.FailureCount { return processSequence(event) } - return nil + return failedErr } func HandleSuccess(updateSpoolerRunTimestamp func() error) error { diff --git a/internal/eventstore/spooler/spooler_test.go b/internal/eventstore/spooler/spooler_test.go index 13c6737aa0..8192e4b908 100644 --- a/internal/eventstore/spooler/spooler_test.go +++ b/internal/eventstore/spooler/spooler_test.go @@ -22,6 +22,7 @@ type testHandler struct { queryError error viewModel string bulkLimit uint64 + maxErrCount int } func (h *testHandler) AggregateTypes() []models.AggregateType { @@ -50,6 +51,10 @@ func (h *testHandler) Reduce(*models.Event) error { return h.processError } func (h *testHandler) OnError(event *models.Event, err error) error { + if h.maxErrCount == 2 { + return nil + } + h.maxErrCount++ return err } func (h *testHandler) OnSuccess() error { @@ -93,17 +98,18 @@ func (es *eventstoreStub) LatestSequence(ctx context.Context, in *models.SearchQ func TestSpooler_process(t *testing.T) { type fields struct { - currentHandler query.Handler + currentHandler *testHandler } type args struct { timeout time.Duration events []*models.Event } tests := []struct { - name string - fields fields - args args - wantErr bool + name string + fields fields + args args + wantErr bool + wantRetries int }{ { name: "process all events", @@ -135,7 +141,8 @@ func TestSpooler_process(t *testing.T) { args: args{ events: []*models.Event{{}, {}}, }, - wantErr: true, + wantErr: false, + wantRetries: 2, }, } for _, tt := range tests { @@ -154,6 +161,9 @@ func TestSpooler_process(t *testing.T) { if err := s.process(ctx, tt.args.events, "test"); (err != nil) != tt.wantErr { t.Errorf("Spooler.process() error = %v, wantErr %v", err, tt.wantErr) } + if tt.fields.currentHandler.maxErrCount != tt.wantRetries { + t.Errorf("Spooler.process() wrong retry count got: %d want %d", tt.fields.currentHandler.maxErrCount, tt.wantRetries) + } elapsed := time.Since(start).Round(1 * time.Second) if tt.args.timeout != 0 && elapsed != tt.args.timeout { @@ -222,14 +232,14 @@ func TestSpooler_load(t *testing.T) { { "lock exists", fields{ - currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second}, + currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second, bulkLimit: 10}, locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("lock already exists"), 2000*time.Millisecond), }, }, { "lock fails", fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second}, + currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second, bulkLimit: 10}, locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("fail"), 2000*time.Millisecond), eventstore: &eventstoreStub{events: []*models.Event{{}}}, }, @@ -237,7 +247,7 @@ func TestSpooler_load(t *testing.T) { { "query fails", fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second}, + currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second, bulkLimit: 10}, locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 2000*time.Millisecond), eventstore: &eventstoreStub{err: fmt.Errorf("fail")}, }, @@ -245,8 +255,8 @@ func TestSpooler_load(t *testing.T) { { "process event fails", fields{ - currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 500 * time.Millisecond}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 1000*time.Millisecond), + currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 500 * time.Millisecond, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 1000*time.Millisecond).expectRenew(t, nil, 1000*time.Millisecond), eventstore: &eventstoreStub{events: []*models.Event{{}}}, }, }, @@ -433,6 +443,7 @@ func TestHandleError(t *testing.T) { }, res: res{ shouldProcessSequence: false, + wantErr: true, }, }, } diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index f1b6e62ef7..1c16407b4f 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -3,24 +3,22 @@ package eventstore import ( "context" - es_int "github.com/caos/zitadel/internal/eventstore" - es_models "github.com/caos/zitadel/internal/eventstore/models" - es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" - usr_grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing" - "github.com/caos/logging" - "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" + es_int "github.com/caos/zitadel/internal/eventstore" + es_models "github.com/caos/zitadel/internal/eventstore/models" + es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" global_model "github.com/caos/zitadel/internal/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" usr_model "github.com/caos/zitadel/internal/user/model" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" "github.com/caos/zitadel/internal/user/repository/view/model" + usr_grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing" "github.com/caos/zitadel/internal/view/repository" ) diff --git a/internal/management/repository/eventsourcing/handler/project_grant.go b/internal/management/repository/eventsourcing/handler/project_grant.go index b01898bb29..918eb54f56 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant.go +++ b/internal/management/repository/eventsourcing/handler/project_grant.go @@ -125,7 +125,11 @@ func (p *ProjectGrant) Reduce(event *models.Event) (err error) { } return p.view.DeleteProjectGrant(grant.GrantID, event) case es_model.ProjectRemoved: - return p.view.DeleteProjectGrantsByProjectID(event.AggregateID) + err = p.view.DeleteProjectGrantsByProjectID(event.AggregateID) + if err != nil { + return err + } + return p.view.ProcessedProjectGrantSequence(event) default: return p.view.ProcessedProjectGrantSequence(event) } diff --git a/internal/management/repository/eventsourcing/handler/project_grant_member.go b/internal/management/repository/eventsourcing/handler/project_grant_member.go index c00eb64763..159be6d870 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant_member.go +++ b/internal/management/repository/eventsourcing/handler/project_grant_member.go @@ -112,7 +112,11 @@ func (p *ProjectGrantMember) processProjectGrantMember(event *models.Event) (err } return p.view.DeleteProjectGrantMember(member.GrantID, member.UserID, event) case proj_es_model.ProjectRemoved: - return p.view.DeleteProjectGrantMembersByProjectID(event.AggregateID) + err = p.view.DeleteProjectGrantMembersByProjectID(event.AggregateID) + if err != nil { + return err + } + return p.view.ProcessedProjectGrantMemberSequence(event) default: return p.view.ProcessedProjectGrantMemberSequence(event) } diff --git a/internal/management/repository/eventsourcing/handler/user.go b/internal/management/repository/eventsourcing/handler/user.go index 05497a0260..bf415d1602 100644 --- a/internal/management/repository/eventsourcing/handler/user.go +++ b/internal/management/repository/eventsourcing/handler/user.go @@ -214,7 +214,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { } } if !policy.UserLoginMustBeDomain { - return nil + return u.view.ProcessedUserSequence(event) } users, err := u.view.UsersByOrgID(event.AggregateID) if err != nil { diff --git a/internal/management/repository/eventsourcing/handler/user_grant.go b/internal/management/repository/eventsourcing/handler/user_grant.go index 3c9f0a0f8f..a4f0869783 100644 --- a/internal/management/repository/eventsourcing/handler/user_grant.go +++ b/internal/management/repository/eventsourcing/handler/user_grant.go @@ -152,7 +152,6 @@ func (u *UserGrant) processUser(event *es_models.Event) (err error) { default: return u.view.ProcessedUserGrantSequence(event) } - return nil } func (u *UserGrant) processProject(event *es_models.Event) (err error) { @@ -176,7 +175,6 @@ func (u *UserGrant) processProject(event *es_models.Event) (err error) { default: return u.view.ProcessedUserGrantSequence(event) } - return nil } func (u *UserGrant) fillData(grant *view_model.UserGrantView, resourceOwner string) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/user_membership.go b/internal/management/repository/eventsourcing/handler/user_membership.go index e6a56fab90..9358acc4e2 100644 --- a/internal/management/repository/eventsourcing/handler/user_membership.go +++ b/internal/management/repository/eventsourcing/handler/user_membership.go @@ -85,7 +85,7 @@ func (m *UserMembership) EventQuery() (*es_models.SearchQuery, error) { func (m *UserMembership) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case iam_es_model.IAMAggregate: - err = m.processIam(event) + err = m.processIAM(event) case org_es_model.OrgAggregate: err = m.processOrg(event) case proj_es_model.ProjectAggregate: @@ -96,7 +96,7 @@ func (m *UserMembership) Reduce(event *es_models.Event) (err error) { return err } -func (m *UserMembership) processIam(event *es_models.Event) (err error) { +func (m *UserMembership) processIAM(event *es_models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { diff --git a/internal/management/repository/eventsourcing/view/application.go b/internal/management/repository/eventsourcing/view/application.go index eeb5c344e6..c9b4a6fbd0 100644 --- a/internal/management/repository/eventsourcing/view/application.go +++ b/internal/management/repository/eventsourcing/view/application.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" @@ -26,7 +27,7 @@ func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) func (v *View) PutApplication(app *model.ApplicationView, event *models.Event) error { err := view.PutApplication(v.Db, applicationTable, app) - if err != nil { + if err != nil && !errors.IsNotFound(err) { return err } return v.ProcessedApplicationSequence(event) @@ -43,7 +44,7 @@ func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Even func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) if err != nil { - return nil + return err } return v.ProcessedApplicationSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/machine_keys.go b/internal/management/repository/eventsourcing/view/machine_keys.go index da50ed6760..c861cfd58c 100644 --- a/internal/management/repository/eventsourcing/view/machine_keys.go +++ b/internal/management/repository/eventsourcing/view/machine_keys.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" @@ -29,24 +30,21 @@ func (v *View) PutMachineKey(org *model.MachineKeyView, event *models.Event) err if err != nil { return err } - if event.Sequence != 0 { - return v.ProcessedMachineKeySequence(event) - } - return nil + return v.ProcessedMachineKeySequence(event) } func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedMachineKeySequence(event) } func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedMachineKeySequence(event) } diff --git a/internal/management/repository/eventsourcing/view/org_domain.go b/internal/management/repository/eventsourcing/view/org_domain.go index f8103674aa..45ee4ad912 100644 --- a/internal/management/repository/eventsourcing/view/org_domain.go +++ b/internal/management/repository/eventsourcing/view/org_domain.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/org/repository/view" @@ -33,10 +34,7 @@ func (v *View) PutOrgDomain(org *model.OrgDomainView, event *models.Event) error if err != nil { return err } - if event.Sequence != 0 { - return v.ProcessedOrgDomainSequence(event) - } - return nil + return v.ProcessedOrgDomainSequence(event) } func (v *View) PutOrgDomains(domains []*model.OrgDomainView, event *models.Event) error { @@ -49,8 +47,8 @@ func (v *View) PutOrgDomains(domains []*model.OrgDomainView, event *models.Event func (v *View) DeleteOrgDomain(orgID, domain string, event *models.Event) error { err := view.DeleteOrgDomain(v.Db, orgDomainTable, orgID, domain) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedOrgDomainSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/org_member.go b/internal/management/repository/eventsourcing/view/org_member.go index 2ea8f79c90..ee6deac739 100644 --- a/internal/management/repository/eventsourcing/view/org_member.go +++ b/internal/management/repository/eventsourcing/view/org_member.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/org/repository/view" @@ -42,16 +43,16 @@ func (v *View) PutOrgMembers(members []*model.OrgMemberView, event *models.Event func (v *View) DeleteOrgMember(orgID, userID string, event *models.Event) error { err := view.DeleteOrgMember(v.Db, orgMemberTable, orgID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedOrgMemberSequence(event) } func (v *View) DeleteOrgMembersByUserID(userID string, event *models.Event) error { err := view.DeleteOrgMembersByUserID(v.Db, orgMemberTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedOrgMemberSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/project.go b/internal/management/repository/eventsourcing/view/project.go index 8676b0ed2c..f5ac609ddc 100644 --- a/internal/management/repository/eventsourcing/view/project.go +++ b/internal/management/repository/eventsourcing/view/project.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" @@ -30,8 +31,8 @@ func (v *View) PutProject(project *model.ProjectView, event *models.Event) error func (v *View) DeleteProject(projectID string, event *models.Event) error { err := view.DeleteProject(v.Db, projectTable, projectID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedProjectSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/project_grant_member.go b/internal/management/repository/eventsourcing/view/project_grant_member.go index 83ae463190..7634cefbf5 100644 --- a/internal/management/repository/eventsourcing/view/project_grant_member.go +++ b/internal/management/repository/eventsourcing/view/project_grant_member.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" @@ -46,8 +47,8 @@ func (v *View) PutProjectGrantMembers(members []*model.ProjectGrantMemberView, e func (v *View) DeleteProjectGrantMember(grantID, userID string, event *models.Event) error { err := view.DeleteProjectGrantMember(v.Db, projectGrantMemberTable, grantID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedProjectGrantMemberSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/project_member.go b/internal/management/repository/eventsourcing/view/project_member.go index a867916939..09f3b31518 100644 --- a/internal/management/repository/eventsourcing/view/project_member.go +++ b/internal/management/repository/eventsourcing/view/project_member.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" @@ -46,8 +47,8 @@ func (v *View) PutProjectMembers(project []*model.ProjectMemberView, event *mode func (v *View) DeleteProjectMember(projectID, userID string, event *models.Event) error { err := view.DeleteProjectMember(v.Db, projectMemberTable, projectID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedProjectMemberSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/project_role.go b/internal/management/repository/eventsourcing/view/project_role.go index df56939309..5b251fa24b 100644 --- a/internal/management/repository/eventsourcing/view/project_role.go +++ b/internal/management/repository/eventsourcing/view/project_role.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" @@ -42,8 +43,8 @@ func (v *View) PutProjectRole(project *model.ProjectRoleView, event *models.Even func (v *View) DeleteProjectRole(projectID, orgID, key string, event *models.Event) error { err := view.DeleteProjectRole(v.Db, projectRoleTable, projectID, orgID, key) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedProjectRoleSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/user.go b/internal/management/repository/eventsourcing/view/user.go index cd7b2a4744..d589b546f7 100644 --- a/internal/management/repository/eventsourcing/view/user.go +++ b/internal/management/repository/eventsourcing/view/user.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" @@ -53,16 +54,13 @@ func (v *View) PutUser(user *model.UserView, event *models.Event) error { if err != nil { return err } - if event.Sequence != 0 { - return v.ProcessedUserSequence(event) - } - return nil + return v.ProcessedUserSequence(event) } func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/user_grant.go b/internal/management/repository/eventsourcing/view/user_grant.go index 5f20eaa0f2..15dd3220d2 100644 --- a/internal/management/repository/eventsourcing/view/user_grant.go +++ b/internal/management/repository/eventsourcing/view/user_grant.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" @@ -58,8 +59,8 @@ func (v *View) PutUserGrants(grants []*model.UserGrantView, event *models.Event) func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserGrantSequence(event) } diff --git a/internal/management/repository/eventsourcing/view/user_membership.go b/internal/management/repository/eventsourcing/view/user_membership.go index fe80081194..90f64906a7 100644 --- a/internal/management/repository/eventsourcing/view/user_membership.go +++ b/internal/management/repository/eventsourcing/view/user_membership.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" @@ -42,32 +43,32 @@ func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, e func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, event *models.Event) error { err := view.DeleteUserMembership(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } func (v *View) DeleteUserMembershipsByUserID(userID string, event *models.Event) error { err := view.DeleteUserMembershipsByUserID(v.Db, userMembershipTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateIDAndObjectID(v.Db, userMembershipTable, aggregateID, objectID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedUserMembershipSequence(event) } diff --git a/internal/notification/repository/eventsourcing/handler/notification.go b/internal/notification/repository/eventsourcing/handler/notification.go index cebde591bf..0d37914e3a 100644 --- a/internal/notification/repository/eventsourcing/handler/notification.go +++ b/internal/notification/repository/eventsourcing/handler/notification.go @@ -111,8 +111,6 @@ func (n *Notification) Reduce(event *models.Event) (err error) { err = n.handlePasswordCode(event) case es_model.DomainClaimed: err = n.handleDomainClaimed(event) - default: - return n.view.ProcessedNotificationSequence(event) } if err != nil { return err diff --git a/internal/notification/repository/eventsourcing/view/notify_user.go b/internal/notification/repository/eventsourcing/view/notify_user.go index b7a24899f7..f81c986d0a 100644 --- a/internal/notification/repository/eventsourcing/view/notify_user.go +++ b/internal/notification/repository/eventsourcing/view/notify_user.go @@ -1,6 +1,7 @@ package view import ( + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" @@ -20,10 +21,7 @@ func (v *View) PutNotifyUser(user *model.NotifyUser, event *models.Event) error if err != nil { return err } - if event.Sequence != 0 { - return v.ProcessedNotifyUserSequence(event) - } - return nil + return v.ProcessedNotifyUserSequence(event) } func (v *View) NotifyUsersByOrgID(orgID string) ([]*model.NotifyUser, error) { @@ -32,8 +30,8 @@ func (v *View) NotifyUsersByOrgID(orgID string) ([]*model.NotifyUser, error) { func (v *View) DeleteNotifyUser(userID string, event *models.Event) error { err := view.DeleteNotifyUser(v.Db, notifyUserTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } return v.ProcessedNotifyUserSequence(event) } diff --git a/internal/notification/templates/templateData.go b/internal/notification/templates/templateData.go index ee7b910709..676af69d4b 100644 --- a/internal/notification/templates/templateData.go +++ b/internal/notification/templates/templateData.go @@ -24,6 +24,8 @@ func (data *TemplateData) Translate(i18n *i18n.Translator, args map[string]inter data.Subject = i18n.Localize(data.Subject, nil, langs...) data.Greeting = i18n.Localize(data.Greeting, args, langs...) data.Text = html.UnescapeString(i18n.Localize(data.Text, args, langs...)) - data.Href = i18n.Localize(data.Href, nil, langs...) + if data.Href != "" { + data.Href = i18n.Localize(data.Href, nil, langs...) + } data.ButtonText = i18n.Localize(data.ButtonText, nil, langs...) } diff --git a/internal/org/repository/eventsourcing/eventstore_mock_test.go b/internal/org/repository/eventsourcing/eventstore_mock_test.go index 6d7d175317..f36f48d7f9 100644 --- a/internal/org/repository/eventsourcing/eventstore_mock_test.go +++ b/internal/org/repository/eventsourcing/eventstore_mock_test.go @@ -42,7 +42,7 @@ func GetMockChangesOrgOK(ctrl *gomock.Controller) *OrgEventstore { } events := []*es_models.Event{ - {AggregateID: "AggregateIDApp", Sequence: 1, AggregateType: repo_model.OrgAggregate, Data: data}, + {AggregateID: "AggregateID", Sequence: 1, AggregateType: repo_model.OrgAggregate, Data: data}, } mockEs := mock.NewMockEventstore(ctrl) mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) diff --git a/internal/org/repository/eventsourcing/eventstore_test.go b/internal/org/repository/eventsourcing/eventstore_test.go index 237a55f63e..e4b5f76111 100644 --- a/internal/org/repository/eventsourcing/eventstore_test.go +++ b/internal/org/repository/eventsourcing/eventstore_test.go @@ -179,7 +179,7 @@ func TestOrgEventstore_OrgByID(t *testing.T) { { name: "new events found and added success", fields: fields{Eventstore: newTestEventstore(t).expectFilterEvents([]*es_models.Event{ - {Sequence: 6}, + {Sequence: 6, AggregateID: "hodor-org"}, }, nil)}, args: args{ ctx: authz.NewMockContext("user", "org"), diff --git a/internal/project/repository/view/application_view.go b/internal/project/repository/view/application_view.go index 414d7dfcf7..2abfd9f47d 100644 --- a/internal/project/repository/view/application_view.go +++ b/internal/project/repository/view/application_view.go @@ -24,7 +24,7 @@ func ApplicationByID(db *gorm.DB, table, projectID, appID string) (*model.Applic func ApplicationsByProjectID(db *gorm.DB, table, projectID string) ([]*model.ApplicationView, error) { applications := make([]*model.ApplicationView, 0) queries := []*proj_model.ApplicationSearchQuery{ - &proj_model.ApplicationSearchQuery{Key: proj_model.AppSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.AppSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ApplicationSearchRequest{Queries: queries}) _, err := query(db, &applications) diff --git a/internal/project/repository/view/project_role_view.go b/internal/project/repository/view/project_role_view.go index 12248b6640..4065439d46 100644 --- a/internal/project/repository/view/project_role_view.go +++ b/internal/project/repository/view/project_role_view.go @@ -26,7 +26,7 @@ func ProjectRoleByIDs(db *gorm.DB, table, projectID, orgID, key string) (*model. func ProjectRolesByProjectID(db *gorm.DB, table, projectID string) ([]*model.ProjectRoleView, error) { roles := make([]*model.ProjectRoleView, 0) queries := []*proj_model.ProjectRoleSearchQuery{ - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectRoleSearchRequest{Queries: queries}) _, err := query(db, &roles) @@ -39,9 +39,9 @@ func ProjectRolesByProjectID(db *gorm.DB, table, projectID string) ([]*model.Pro func ResourceOwnerProjectRolesByKey(db *gorm.DB, table, projectID, resourceOwner, key string) ([]*model.ProjectRoleView, error) { roles := make([]*model.ProjectRoleView, 0) queries := []*proj_model.ProjectRoleSearchQuery{ - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyKey, Value: key, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyKey, Value: key, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectRoleSearchRequest{Queries: queries}) _, err := query(db, &roles) @@ -54,8 +54,8 @@ func ResourceOwnerProjectRolesByKey(db *gorm.DB, table, projectID, resourceOwner func ResourceOwnerProjectRoles(db *gorm.DB, table, projectID, resourceOwner string) ([]*model.ProjectRoleView, error) { roles := make([]*model.ProjectRoleView, 0) queries := []*proj_model.ProjectRoleSearchQuery{ - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectRoleSearchRequest{Queries: queries}) _, err := query(db, &roles) diff --git a/internal/user/repository/eventsourcing/eventstore.go b/internal/user/repository/eventsourcing/eventstore.go index acbf78048b..12688f855d 100644 --- a/internal/user/repository/eventsourcing/eventstore.go +++ b/internal/user/repository/eventsourcing/eventstore.go @@ -1624,11 +1624,10 @@ func (es *UserEventstore) AddMachineKey(ctx context.Context, key *usr_model.Mach return nil, errors.ThrowPreconditionFailed(nil, "EVENT-5ROh4", "Errors.User.NotMachine") } - id, err := es.idGenerator.Next() + key.KeyID, err = es.idGenerator.Next() if err != nil { return nil, err } - key.KeyID = id if key.ExpirationDate.IsZero() { key.ExpirationDate, err = time.Parse(yearLayout, defaultExpirationDate) From 6a05527f1823444221c75700a512600d8e5ab004 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 21 Dec 2020 21:25:32 +0100 Subject: [PATCH 18/22] fix: update oidc lib (#1130) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1dfca8afe9..ec1eca6f62 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/allegro/bigcache v1.2.1 github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc github.com/caos/logging v0.0.2 - github.com/caos/oidc v0.13.1 + github.com/caos/oidc v0.13.2 github.com/cockroachdb/cockroach-go/v2 v2.1.0 github.com/duo-labs/webauthn v0.0.0-20200714211715-1daaee874e43 github.com/envoyproxy/protoc-gen-validate v0.4.1 diff --git a/go.sum b/go.sum index 3671fe0905..78384de1fa 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,8 @@ github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo= github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo= github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0= github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0= -github.com/caos/oidc v0.13.1 h1:8890xd3XJpev5xuU4Dn2l49c1lCA3Qd1xu4KsPSVo38= -github.com/caos/oidc v0.13.1/go.mod h1:dLvfYUiAt9ORfl77L/KkcWuR/N0ll8Ry1nD2ERsamDY= +github.com/caos/oidc v0.13.2 h1:52oP3KB1UrZuwraBTLuwM9ItRIhJQMYOm1J5uQ0sYXw= +github.com/caos/oidc v0.13.2/go.mod h1:dLvfYUiAt9ORfl77L/KkcWuR/N0ll8Ry1nD2ERsamDY= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= From 273b7487b3903f26823b3849e0aa1e31c23d7da2 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Tue, 22 Dec 2020 10:41:22 +0100 Subject: [PATCH 19/22] fix: don't fail if OIDCClientSecretCheckSucceeded event can not be pushed (#1131) --- .../project/repository/eventsourcing/eventstore.go | 11 ++++++----- internal/static/i18n/de.yaml | 1 + internal/static/i18n/en.yaml | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/project/repository/eventsourcing/eventstore.go b/internal/project/repository/eventsourcing/eventstore.go index 8967c81389..81d60dbcf8 100644 --- a/internal/project/repository/eventsourcing/eventstore.go +++ b/internal/project/repository/eventsourcing/eventstore.go @@ -811,12 +811,13 @@ func (es *ProjectEventstore) VerifyOIDCClientSecret(ctx context.Context, project err = crypto.CompareHash(app.OIDCConfig.ClientSecret, []byte(secret), es.passwordAlg) spanHash.EndWithError(err) if err == nil { - return es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckSucceededAggregate) + err = es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckSucceededAggregate) + logging.Log("EVENT-AE1vf").OnError(err).Warn("could not push event OIDCClientSecretCheckSucceeded") + return nil } - if err := es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckFailedAggregate); err != nil { - return err - } - return caos_errs.ThrowInvalidArgument(nil, "EVENT-wg24q", "Errors.Internal") + err = es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckFailedAggregate) + logging.Log("EVENT-GD1gh").OnError(err).Warn("could not push event OIDCClientSecretCheckFailed") + return caos_errs.ThrowInvalidArgument(nil, "EVENT-wg24q", "Errors.Project.OIDCSecretInvalid") } func (es *ProjectEventstore) setOIDCClientSecretCheckResult(ctx context.Context, project *proj_model.Project, appID string, check func(*es_models.AggregateCreator, *model.Project, string) es_sdk.AggregateFunc) error { diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 2c065366e0..46a32e4fee 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -165,6 +165,7 @@ Errors: GrantNotExists: Projekt Grant existiert nicht GrantHasNotExistingRole: Eine der Rollen existiert nicht auf dem Projekt UserIDMisisng: User ID fehlt + OIDCSecretInvalid: Client Secret ist ungültig IAM: MemberInvalid: Member ist ungültig MemberAlreadyExisting: Member existiert bereits diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 4654927508..f1f9c5781f 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -165,6 +165,7 @@ Errors: GrantNotExists: Project grant doesn't exist GrantHasNotExistingRole: One role doesn't exist on project UserIDMisisng: User ID missing + OIDCSecretInvalid: Client Secret is invalid IAM: MemberInvalid: Member is invalid MemberAlreadyExisting: Member already exists From f96838cf62515d1be2570f802c046279926faf95 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Tue, 22 Dec 2020 12:12:05 +0100 Subject: [PATCH 20/22] fix: org name overwrite (#1133) * fix: org name overwrite * import --- .../ui/login/handler/register_org_handler.go | 20 +++++++++---------- .../login/static/templates/register_org.html | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/ui/login/handler/register_org_handler.go b/internal/ui/login/handler/register_org_handler.go index c1a4b8dc6e..ea2f7a739d 100644 --- a/internal/ui/login/handler/register_org_handler.go +++ b/internal/ui/login/handler/register_org_handler.go @@ -1,9 +1,9 @@ package handler import ( - auth_model "github.com/caos/zitadel/internal/auth/model" "net/http" + auth_model "github.com/caos/zitadel/internal/auth/model" "github.com/caos/zitadel/internal/auth_request/model" caos_errs "github.com/caos/zitadel/internal/errors" org_model "github.com/caos/zitadel/internal/org/model" @@ -15,14 +15,14 @@ const ( ) type registerOrgFormData struct { - OrgName string `schema:"orgname"` - Email string `schema:"email"` - Username string `schema:"username"` - Firstname string `schema:"firstname"` - Lastname string `schema:"lastname"` - Password string `schema:"register-password"` - Password2 string `schema:"register-password-confirmation"` - TermsConfirm bool `schema:"terms-confirm"` + RegisterOrgName string `schema:"orgname"` + Email string `schema:"email"` + Username string `schema:"username"` + Firstname string `schema:"firstname"` + Lastname string `schema:"lastname"` + Password string `schema:"register-password"` + Password2 string `schema:"register-password-confirmation"` + TermsConfirm bool `schema:"terms-confirm"` } type registerOrgData struct { @@ -140,6 +140,6 @@ func (d registerOrgFormData) toUserModel() *usr_model.User { func (d registerOrgFormData) toOrgModel() *org_model.Org { return &org_model.Org{ - Name: d.OrgName, + Name: d.RegisterOrgName, } } diff --git a/internal/ui/login/static/templates/register_org.html b/internal/ui/login/static/templates/register_org.html index fc0aeea156..ed8037bea1 100644 --- a/internal/ui/login/static/templates/register_org.html +++ b/internal/ui/login/static/templates/register_org.html @@ -15,7 +15,7 @@
- +
{{if .UserLoginMustBeDomain}} From a6c4702b8ed651beb4d37dacb02b81cb1e3f898d Mon Sep 17 00:00:00 2001 From: Silvan Date: Tue, 22 Dec 2020 12:27:55 +0100 Subject: [PATCH 21/22] fix: lock again (#1132) * start sub * start implement subsciptions * start subscription * implementation for member done * admin done * fix: tests * extend handlers * prepary notification * no errors in adminapi * changed current sequence in all packages * ignore mocks * works * subscriptions as singleton * tests * refactor: rename function scope var * fix: process ALL previous sequences * fix: spooler and pubsub * handler check * fix: process events until all done * fix break on query err * fix: handler * fix: process sequence or return error * check aggregate id * fix: log only in error case * fix tests * fix: handlers * fix: spooler * fix: spooler * fix: tests * fix: continue * fix: locker duration * fix: variable lock duration * fix: test * fix: test * fix: test min max time Co-authored-by: Livio Amstutz --- .../eventsourcing/handler/handler.go | 4 +++ .../repository/eventsourcing/spooler/lock.go | 14 ++------ .../eventsourcing/handler/handler.go | 4 +++ .../repository/eventsourcing/spooler/lock.go | 7 +++- .../eventsourcing/handler/handler.go | 9 +++-- .../repository/eventsourcing/spooler/lock.go | 3 +- internal/eventstore/query/handler.go | 1 + internal/eventstore/spooler/spooler.go | 4 +-- internal/eventstore/spooler/spooler_test.go | 34 +++++++++++++------ .../eventsourcing/handler/handler.go | 4 +++ .../repository/eventsourcing/spooler/lock.go | 3 +- .../eventsourcing/handler/handler.go | 4 +++ .../repository/eventsourcing/spooler/lock.go | 3 +- 13 files changed, 63 insertions(+), 31 deletions(-) diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go index 60dcb9dac1..e6fdc3e531 100644 --- a/internal/admin/repository/eventsourcing/handler/handler.go +++ b/internal/admin/repository/eventsourcing/handler/handler.go @@ -95,6 +95,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/admin/repository/eventsourcing/spooler/lock.go b/internal/admin/repository/eventsourcing/spooler/lock.go index 0c04a3bf07..f07b190c1c 100644 --- a/internal/admin/repository/eventsourcing/spooler/lock.go +++ b/internal/admin/repository/eventsourcing/spooler/lock.go @@ -2,26 +2,18 @@ package spooler import ( "database/sql" + es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) const ( - lockTable = "adminapi.locks" - lockedUntilKey = "locked_until" - lockerIDKey = "locker_id" - objectTypeKey = "object_type" + lockTable = "adminapi.locks" ) type locker struct { dbClient *sql.DB } -type lock struct { - LockerID string `gorm:"column:locker_id;primary_key"` - LockedUntil time.Time `gorm:"column:locked_until"` - ViewName string `gorm:"column:object_type;primary_key"` -} - func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return nil + return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) } diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index 0235304ce7..c5825c5abe 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -108,6 +108,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/auth/repository/eventsourcing/spooler/lock.go b/internal/auth/repository/eventsourcing/spooler/lock.go index 5fdbc5f0ae..f24f98f6cc 100644 --- a/internal/auth/repository/eventsourcing/spooler/lock.go +++ b/internal/auth/repository/eventsourcing/spooler/lock.go @@ -2,13 +2,18 @@ package spooler import ( "database/sql" + es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) +const ( + lockTable = "management.locks" +) + type locker struct { dbClient *sql.DB } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return nil + return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) } diff --git a/internal/authz/repository/eventsourcing/handler/handler.go b/internal/authz/repository/eventsourcing/handler/handler.go index 2d5f816c97..ee21d1b020 100644 --- a/internal/authz/repository/eventsourcing/handler/handler.go +++ b/internal/authz/repository/eventsourcing/handler/handler.go @@ -3,13 +3,12 @@ package handler import ( "time" + "github.com/caos/zitadel/internal/authz/repository/eventsourcing/view" sd "github.com/caos/zitadel/internal/config/systemdefaults" + "github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/query" iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - - "github.com/caos/zitadel/internal/authz/repository/eventsourcing/view" - "github.com/caos/zitadel/internal/config/types" ) type Configs map[string]*Config @@ -60,6 +59,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/authz/repository/eventsourcing/spooler/lock.go b/internal/authz/repository/eventsourcing/spooler/lock.go index 0397382276..6100f470d3 100644 --- a/internal/authz/repository/eventsourcing/spooler/lock.go +++ b/internal/authz/repository/eventsourcing/spooler/lock.go @@ -2,6 +2,7 @@ package spooler import ( "database/sql" + es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) @@ -14,5 +15,5 @@ type locker struct { } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return nil + return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) } diff --git a/internal/eventstore/query/handler.go b/internal/eventstore/query/handler.go index 84a79d3079..622a69156e 100755 --- a/internal/eventstore/query/handler.go +++ b/internal/eventstore/query/handler.go @@ -20,6 +20,7 @@ type Handler interface { OnError(event *models.Event, err error) error OnSuccess() error MinimumCycleDuration() time.Duration + LockDuration() time.Duration QueryLimit() uint64 AggregateTypes() []models.AggregateType diff --git a/internal/eventstore/spooler/spooler.go b/internal/eventstore/spooler/spooler.go index b5c844e482..40d124a6ed 100644 --- a/internal/eventstore/spooler/spooler.go +++ b/internal/eventstore/spooler/spooler.go @@ -160,12 +160,12 @@ func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID s case <-ctx.Done(): return case <-renewTimer: - err := s.locker.Renew(workerID, s.ViewModel(), s.MinimumCycleDuration()*2) + err := s.locker.Renew(workerID, s.ViewModel(), s.LockDuration()) firstLock.Do(func() { locked <- err == nil }) if err == nil { - renewTimer = time.After(s.MinimumCycleDuration()) + renewTimer = time.After(s.LockDuration()) continue } diff --git a/internal/eventstore/spooler/spooler_test.go b/internal/eventstore/spooler/spooler_test.go index 8192e4b908..5b02977ea1 100644 --- a/internal/eventstore/spooler/spooler_test.go +++ b/internal/eventstore/spooler/spooler_test.go @@ -40,16 +40,19 @@ func (h *testHandler) Eventstore() eventstore.Eventstore { func (h *testHandler) ViewModel() string { return h.viewModel } + func (h *testHandler) EventQuery() (*models.SearchQuery, error) { if h.queryError != nil { return nil, h.queryError } return &models.SearchQuery{}, nil } + func (h *testHandler) Reduce(*models.Event) error { <-time.After(h.processSleep) return h.processError } + func (h *testHandler) OnError(event *models.Event, err error) error { if h.maxErrCount == 2 { return nil @@ -57,12 +60,19 @@ func (h *testHandler) OnError(event *models.Event, err error) error { h.maxErrCount++ return err } + func (h *testHandler) OnSuccess() error { return nil } + func (h *testHandler) MinimumCycleDuration() time.Duration { return h.cycleDuration } + +func (h *testHandler) LockDuration() time.Duration { + return h.cycleDuration / 2 +} + func (h *testHandler) QueryLimit() uint64 { return h.bulkLimit } @@ -232,31 +242,31 @@ func TestSpooler_load(t *testing.T) { { "lock exists", fields{ - currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("lock already exists"), 2000*time.Millisecond), + currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView1", cycleDuration: 1 * time.Second, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView1").expectRenew(t, fmt.Errorf("lock already exists"), 500*time.Millisecond), }, }, { "lock fails", fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("fail"), 2000*time.Millisecond), + currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView2", cycleDuration: 1 * time.Second, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView2").expectRenew(t, fmt.Errorf("fail"), 500*time.Millisecond), eventstore: &eventstoreStub{events: []*models.Event{{}}}, }, }, { "query fails", fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 2000*time.Millisecond), + currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView3", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView3").expectRenew(t, nil, 500*time.Millisecond), eventstore: &eventstoreStub{err: fmt.Errorf("fail")}, }, }, { "process event fails", fields{ - currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 500 * time.Millisecond, bulkLimit: 10}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 1000*time.Millisecond).expectRenew(t, nil, 1000*time.Millisecond), + currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView4", cycleDuration: 500 * time.Millisecond, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView4").expectRenew(t, nil, 250*time.Millisecond), eventstore: &eventstoreStub{events: []*models.Event{{}}}, }, }, @@ -292,7 +302,7 @@ func TestSpooler_lock(t *testing.T) { "renew correct", fields{ currentHandler: &testHandler{cycleDuration: 1 * time.Second, viewModel: "testView"}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 2000*time.Millisecond), + locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 500*time.Millisecond), expectsErr: false, }, args{ @@ -303,7 +313,7 @@ func TestSpooler_lock(t *testing.T) { "renew fails", fields{ currentHandler: &testHandler{cycleDuration: 900 * time.Millisecond, viewModel: "testView"}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("renew failed"), 1800*time.Millisecond), + locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("renew failed"), 450*time.Millisecond), expectsErr: true, }, args{ @@ -357,13 +367,15 @@ func newTestLocker(t *testing.T, lockerID, viewName string) *testLocker { } func (l *testLocker) expectRenew(t *testing.T, err error, waitTime time.Duration) *testLocker { + t.Helper() l.mock.EXPECT().Renew(gomock.Any(), l.viewName, gomock.Any()).DoAndReturn( func(_, _ string, gotten time.Duration) error { + t.Helper() if waitTime-gotten != 0 { t.Errorf("expected waittime %v got %v", waitTime, gotten) } return err - }).Times(1) + }).MinTimes(1).MaxTimes(3) return l } diff --git a/internal/management/repository/eventsourcing/handler/handler.go b/internal/management/repository/eventsourcing/handler/handler.go index 56ea997f2f..addd360caf 100644 --- a/internal/management/repository/eventsourcing/handler/handler.go +++ b/internal/management/repository/eventsourcing/handler/handler.go @@ -118,6 +118,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/management/repository/eventsourcing/spooler/lock.go b/internal/management/repository/eventsourcing/spooler/lock.go index 9dd53a4cc3..f24f98f6cc 100644 --- a/internal/management/repository/eventsourcing/spooler/lock.go +++ b/internal/management/repository/eventsourcing/spooler/lock.go @@ -2,6 +2,7 @@ package spooler import ( "database/sql" + es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) @@ -14,5 +15,5 @@ type locker struct { } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return nil + return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) } diff --git a/internal/notification/repository/eventsourcing/handler/handler.go b/internal/notification/repository/eventsourcing/handler/handler.go index aaf553ebdf..eb8035ed88 100644 --- a/internal/notification/repository/eventsourcing/handler/handler.go +++ b/internal/notification/repository/eventsourcing/handler/handler.go @@ -77,6 +77,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/notification/repository/eventsourcing/spooler/lock.go b/internal/notification/repository/eventsourcing/spooler/lock.go index fe14ec49fc..46325b1495 100644 --- a/internal/notification/repository/eventsourcing/spooler/lock.go +++ b/internal/notification/repository/eventsourcing/spooler/lock.go @@ -2,6 +2,7 @@ package spooler import ( "database/sql" + es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" ) @@ -14,5 +15,5 @@ type locker struct { } func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { - return nil + return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) } From 28c8274eab7e9c8db41a6d1f3e5876b608060506 Mon Sep 17 00:00:00 2001 From: Silvan Date: Thu, 24 Dec 2020 13:01:23 +0100 Subject: [PATCH 22/22] fix(auth): spooler locks correct database (#1134) --- internal/admin/repository/eventsourcing/handler/handler.go | 6 ------ internal/auth/repository/eventsourcing/spooler/lock.go | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go index e6fdc3e531..ed0e60f6ed 100644 --- a/internal/admin/repository/eventsourcing/handler/handler.go +++ b/internal/admin/repository/eventsourcing/handler/handler.go @@ -77,12 +77,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es } } -func subscribe(es eventstore.Eventstore, handlers []query.Handler) { - for _, handler := range handlers { - es.Subscribe(handler.AggregateTypes()...) - } -} - func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { diff --git a/internal/auth/repository/eventsourcing/spooler/lock.go b/internal/auth/repository/eventsourcing/spooler/lock.go index f24f98f6cc..5b69f3ae0c 100644 --- a/internal/auth/repository/eventsourcing/spooler/lock.go +++ b/internal/auth/repository/eventsourcing/spooler/lock.go @@ -2,12 +2,13 @@ package spooler import ( "database/sql" - es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" + + es_locker "github.com/caos/zitadel/internal/eventstore/locker" ) const ( - lockTable = "management.locks" + lockTable = "auth.locks" ) type locker struct {