mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 16:47:23 +00:00
Merge branch 'master' into new-eventstore
# Conflicts: # go.mod # internal/admin/repository/eventsourcing/eventstore/iam.go # internal/authz/repository/eventsourcing/repository.go # internal/eventstore/eventstore.go # internal/setup/config.go # pkg/grpc/management/mock/management.proto.mock.go
This commit is contained in:
commit
5b84c9b619
1
.gitignore
vendored
1
.gitignore
vendored
@ -44,4 +44,5 @@ tmp/
|
||||
console/src/app/proto/generated/
|
||||
|
||||
pkg/grpc/*/*.pb.*
|
||||
pkg/grpc/*/mock/*.mock.go
|
||||
pkg/grpc/*/*.swagger.json
|
2
LICENSE
2
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.
|
||||
|
@ -67,7 +67,7 @@ export ZITADEL_SHORT_CACHE_SHARED_MAXAGE=15m
|
||||
export ZITADEL_CONSOLE_ENV_DIR=../../console/src/assets/
|
||||
|
||||
#Org
|
||||
export ZITADEL_DEFAULT_DOMAIN=zitadel.ch
|
||||
export ZITADEL_DEFAULT_DOMAIN=localhost
|
||||
|
||||
|
||||
#Setup
|
||||
|
@ -98,4 +98,6 @@ SetUp:
|
||||
Step7:
|
||||
DefaultSecondFactor: 1 #SecondFactorTypeOTP
|
||||
Step8:
|
||||
DefaultSecondFactor: 2 #SecondFactorTypeU2F
|
||||
DefaultSecondFactor: 2 #SecondFactorTypeU2F
|
||||
Step9:
|
||||
Passwordless: true
|
@ -49,11 +49,11 @@ AuthZ:
|
||||
Key: $CR_AUTHZ_KEY
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1
|
||||
BulkLimit: 100
|
||||
BulkLimit: 10000
|
||||
FailureCountUntilSkip: 5
|
||||
|
||||
Auth:
|
||||
SearchLimit: 100
|
||||
SearchLimit: 1000
|
||||
Domain: $ZITADEL_DEFAULT_DOMAIN
|
||||
Eventstore:
|
||||
ServiceName: 'authAPI'
|
||||
@ -98,7 +98,7 @@ Auth:
|
||||
Key: $CR_AUTH_KEY
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1
|
||||
BulkLimit: 100
|
||||
BulkLimit: 10000
|
||||
FailureCountUntilSkip: 5
|
||||
KeyConfig:
|
||||
Size: 2048
|
||||
@ -109,7 +109,7 @@ Auth:
|
||||
SigningKeyRotation: 10s
|
||||
|
||||
Admin:
|
||||
SearchLimit: 100
|
||||
SearchLimit: 1000
|
||||
Domain: $ZITADEL_DEFAULT_DOMAIN
|
||||
Eventstore:
|
||||
ServiceName: 'Admin'
|
||||
@ -142,11 +142,11 @@ Admin:
|
||||
Key: $CR_ADMINAPI_KEY
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1
|
||||
BulkLimit: 100
|
||||
BulkLimit: 10000
|
||||
FailureCountUntilSkip: 5
|
||||
|
||||
Mgmt:
|
||||
SearchLimit: 100
|
||||
SearchLimit: 1000
|
||||
Domain: $ZITADEL_DEFAULT_DOMAIN
|
||||
Eventstore:
|
||||
ServiceName: 'ManagementAPI'
|
||||
@ -179,7 +179,7 @@ Mgmt:
|
||||
Key: $CR_MANAGEMENT_KEY
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1
|
||||
BulkLimit: 100
|
||||
BulkLimit: 10000
|
||||
FailureCountUntilSkip: 5
|
||||
|
||||
API:
|
||||
@ -292,10 +292,6 @@ Notification:
|
||||
Key: $CR_NOTIFICATION_KEY
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1
|
||||
BulkLimit: 100
|
||||
BulkLimit: 10000
|
||||
FailureCountUntilSkip: 5
|
||||
Handlers:
|
||||
Notification:
|
||||
MinimumCycleDuration: 5s
|
||||
User:
|
||||
MinimumCycleDuration: 5s
|
||||
Handlers:
|
@ -126,7 +126,7 @@ SystemDefaults:
|
||||
Text: 'DomainClaimed.Text'
|
||||
ButtonText: 'DomainClaimed.ButtonText'
|
||||
WebAuthN:
|
||||
ID: $ZITADEL_COOKIE_DOMAIN
|
||||
ID: $ZITADEL_DEFAULT_DOMAIN
|
||||
OriginLogin: $ZITADEL_ACCOUNTS
|
||||
OriginConsole: $ZITADEL_CONSOLE
|
||||
DisplayName: ZITADEL
|
779
console/package-lock.json
generated
779
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -40,17 +40,17 @@
|
||||
"rxjs": "~6.6.3",
|
||||
"ts-protoc-gen": "^0.13.0",
|
||||
"tslib": "^2.0.0",
|
||||
"uuid": "^8.3.1",
|
||||
"uuid": "^8.3.2",
|
||||
"zone.js": "~0.11.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.1100.3",
|
||||
"@angular/cli": "~11.0.3",
|
||||
"@angular-devkit/build-angular": "~0.1100.4",
|
||||
"@angular/cli": "~11.0.4",
|
||||
"@angular/compiler-cli": "~11.0.0",
|
||||
"@types/jasmine": "~3.6.2",
|
||||
"@angular/language-service": "~11.0.3",
|
||||
"@angular/language-service": "~11.0.4",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^14.14.10",
|
||||
"@types/node": "^14.14.13",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-spec-reporter": "~6.0.0",
|
||||
@ -64,7 +64,7 @@
|
||||
"stylelint": "^13.8.0",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"stylelint-scss": "^3.18.0",
|
||||
"ts-node": "~9.1.0",
|
||||
"ts-node": "~9.1.1",
|
||||
"tslint": "~6.1.3",
|
||||
"typescript": "^4.0.5"
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
||||
this.init();
|
||||
if (this.refresh) {
|
||||
this.refresh.pipe(takeUntil(this.destroyed$), debounceTime(2000)).subscribe(() => {
|
||||
console.log('asdf');
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
<h1 mat-dialog-title class="title"><span>{{data.title | translate}}</span></h1>
|
||||
<div mat-dialog-content>
|
||||
<p class="desc">{{data.desc | translate}}</p>
|
||||
|
||||
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||
<cnsl-label>{{'MFA.TYPE' | translate}}</cnsl-label>
|
||||
<mat-select [(ngModel)]="newMfaType">
|
||||
<mat-option *ngFor="let mfa of availableMfaTypes" [value]="mfa">
|
||||
{{(data.componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-button (click)="closeDialog()"><span>{{'ACTIONS.CLOSE' | translate}}</span></button>
|
||||
<button [disabled]="newMfaType == undefined" mat-raised-button class="ok-button" color="primary"
|
||||
(click)="closeDialogWithCode()"><span>{{'ACTIONS.OK' | translate}}</span>
|
||||
</button>
|
||||
</div>
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<AdminMultiFactorType | MgmtMultiFactorType> = [];
|
||||
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>,
|
||||
@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);
|
||||
}
|
||||
}
|
15
console/src/app/modules/mfa-table/mfa-table.component.html
Normal file
15
console/src/app/modules/mfa-table/mfa-table.component.html
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="sp_wrapper">
|
||||
<mat-spinner diameter="30" *ngIf="loading$ | async"></mat-spinner>
|
||||
</div>
|
||||
<div class="mfa-list">
|
||||
<div class="mfa" *ngFor="let mfa of mfas">
|
||||
<button [disabled]="disabled" mat-icon-button (click)="removeMfa(mfa)" class="rm">
|
||||
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">
|
||||
remove_circle</mat-icon>
|
||||
</button>
|
||||
{{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||
</div>
|
||||
<div class="mfa" (click)="addMfa()">
|
||||
<mat-icon>add</mat-icon>
|
||||
</div>
|
||||
</div>
|
42
console/src/app/modules/mfa-table/mfa-table.component.scss
Normal file
42
console/src/app/modules/mfa-table/mfa-table.component.scss
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<MfaTableComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MfaTableComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MfaTableComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
232
console/src/app/modules/mfa-table/mfa-table.component.ts
Normal file
232
console/src/app/modules/mfa-table/mfa-table.component.ts
Normal file
@ -0,0 +1,232 @@
|
||||
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<AdminMultiFactorType | MgmtMultiFactorType | MgmtSecondFactorType | AdminSecondFactorType> = [];
|
||||
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = 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_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',
|
||||
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<void> {
|
||||
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);
|
||||
}
|
||||
}
|
44
console/src/app/modules/mfa-table/mfa-table.module.ts
Normal file
44
console/src/app/modules/mfa-table/mfa-table.module.ts
Normal file
@ -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 { }
|
@ -44,8 +44,46 @@
|
||||
</mat-slide-toggle>
|
||||
<p> {{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}} </p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
|
||||
[(ngModel)]="loginData.forceMfa">
|
||||
{{'POLICY.DATA.FORCEMFA' | translate}}
|
||||
</mat-slide-toggle>
|
||||
<p> {{'POLICY.DATA.FORCEMFA_DESC' | translate}} </p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||
<cnsl-label>{{'LOGINPOLICY.PASSWORDLESS' | translate}}</cnsl-label>
|
||||
<mat-select [(ngModel)]="loginData.passwordlessType">
|
||||
<mat-option *ngFor="let pt of passwordlessTypes" [value]="pt">
|
||||
{{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button [disabled]="disabled" class="save-button" (click)="savePolicy()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<ng-container *ngIf="!isDefault">
|
||||
<h3 class="subheader">{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}</h3>
|
||||
<p class="subdesc">{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}</p>
|
||||
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||
[componentType]="LoginMethodComponentType.MultiFactor"
|
||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'iam.policy.write' : ''] | hasRole | async) == false">
|
||||
</app-mfa-table>
|
||||
|
||||
<h3 class="subheader">{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}</h3>
|
||||
<p class="subdesc">{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}</p>
|
||||
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||
[componentType]="LoginMethodComponentType.SecondFactor"
|
||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'iam.policy.write' : ''] | hasRole | async) == false">
|
||||
</app-mfa-table>
|
||||
</ng-container>
|
||||
|
||||
<h3 class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</h3>
|
||||
|
||||
<div class="idps">
|
||||
@ -63,9 +101,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button [disabled]="disabled" class="save-button" (click)="savePolicy()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['org.idp.read']">
|
||||
<h2>{{ 'IDP.LIST.TITLE' | translate }}</h2>
|
||||
<p>{{ 'IDP.LIST.DESCRIPTION' | translate }}</p>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -3,18 +3,23 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { LoginMethodComponentType } from 'src/app/modules/mfa-table/mfa-table.component';
|
||||
import {
|
||||
DefaultLoginPolicy,
|
||||
DefaultLoginPolicyRequest,
|
||||
DefaultLoginPolicyView,
|
||||
IdpProviderView as AdminIdpProviderView,
|
||||
IdpView as AdminIdpView,
|
||||
PasswordlessType as AdminPasswordlessType,
|
||||
} from 'src/app/proto/generated/admin_pb';
|
||||
import {
|
||||
IdpProviderType,
|
||||
IdpProviderView as MgmtIdpProviderView,
|
||||
IdpView as MgmtIdpView,
|
||||
LoginPolicy,
|
||||
LoginPolicyRequest,
|
||||
LoginPolicyView,
|
||||
PasswordlessType as MgmtPasswordlessType,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
@ -29,6 +34,8 @@ 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 passwordlessTypes: Array<AdminPasswordlessType | MgmtPasswordlessType> = [];
|
||||
public loginData!: LoginPolicyView.AsObject | DefaultLoginPolicyView.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
@ -50,9 +57,13 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.passwordlessTypes = [MgmtPasswordlessType.PASSWORDLESSTYPE_ALLOWED,
|
||||
MgmtPasswordlessType.PASSWORDLESSTYPE_NOT_ALLOWED];
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
this.passwordlessTypes = [AdminPasswordlessType.PASSWORDLESSTYPE_ALLOWED,
|
||||
AdminPasswordlessType.PASSWORDLESSTYPE_NOT_ALLOWED];
|
||||
break;
|
||||
}
|
||||
|
||||
@ -108,20 +119,28 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
Promise<LoginPolicy | DefaultLoginPolicy> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
const mgmtreq = new LoginPolicy();
|
||||
const mgmtreq = new LoginPolicyRequest();
|
||||
mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
||||
mgmtreq.setAllowRegister(this.loginData.allowRegister);
|
||||
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
// console.log(mgmtreq.toObject());
|
||||
if ((this.loginData as LoginPolicyView.AsObject).pb_default) {
|
||||
return (this.service as ManagementService).CreateLoginPolicy(mgmtreq);
|
||||
} else {
|
||||
return (this.service as ManagementService).UpdateLoginPolicy(mgmtreq);
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
const adminreq = new DefaultLoginPolicy();
|
||||
const adminreq = new DefaultLoginPolicyRequest();
|
||||
adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
||||
adminreq.setAllowRegister(this.loginData.allowRegister);
|
||||
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||
adminreq.setForceMfa(this.loginData.forceMfa);
|
||||
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
|
||||
// console.log(adminreq.toObject());
|
||||
|
||||
return (this.service as AdminService).UpdateDefaultLoginPolicy(adminreq);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@ -12,6 +13,7 @@ import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { MfaTableModule } from 'src/app/modules/mfa-table/mfa-table.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
|
||||
@ -36,7 +38,9 @@ import { LoginPolicyComponent } from './login-policy.component';
|
||||
DetailLayoutModule,
|
||||
AddIdpDialogModule,
|
||||
IdpTableModule,
|
||||
MfaTableModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
],
|
||||
})
|
||||
export class LoginPolicyModule { }
|
||||
|
@ -127,6 +127,22 @@
|
||||
<span>{{'APP.OIDC.IDTOKENROLEASSERTION_DESCRIPTION' | translate}}</span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<mat-checkbox class="full-width" style="margin-bottom: .5rem"
|
||||
formControlName="idTokenUserinfoAssertion" color="primary">
|
||||
{{'APP.OIDC.IDTOKENUSERINFOASSERTION' | translate}}</mat-checkbox>
|
||||
<cnsl-info-section class="full-width desc">
|
||||
<span>{{'APP.OIDC.IDTOKENUSERINFOASSERTION_DESCRIPTION' | translate}}</span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<p class="clockskew-title">ClockSkew</p>
|
||||
<mat-slider color="primary" formControlName="clockSkewSeconds" class="clockskew-slider" thumbLabel
|
||||
[displayWith]="formatClockSkewLabel" tickInterval=".1" min="0" [step]="1" max="5">
|
||||
</mat-slider>
|
||||
<cnsl-info-section class="full-width desc">
|
||||
<span>{{'APP.OIDC.CLOCKSKEW' | translate}}</span>
|
||||
</cnsl-info-section>
|
||||
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<p class="full-width section-title">{{'APP.OIDC.REDIRECTSECTIONTITLE' | translate}}</p>
|
||||
|
@ -128,6 +128,17 @@
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.clockskew-title {
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
margin: 1rem .5rem 0 .5rem;
|
||||
}
|
||||
|
||||
.clockskew-slider {
|
||||
width: 100%;
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: var(--grey);
|
||||
font-size: 14px;
|
||||
|
@ -6,6 +6,7 @@ import { MatButtonToggleChange } from '@angular/material/button-toggle';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
@ -15,6 +16,7 @@ import {
|
||||
OIDCApplicationType,
|
||||
OIDCAuthMethodType,
|
||||
OIDCConfig,
|
||||
OIDCConfigUpdate,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
OIDCTokenType,
|
||||
@ -116,9 +118,15 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
accessTokenType: [{ value: '', disabled: true }],
|
||||
accessTokenRoleAssertion: [{ value: false, disabled: true }],
|
||||
idTokenRoleAssertion: [{ value: false, disabled: true }],
|
||||
idTokenUserinfoAssertion: [{ value: false, disabled: true }],
|
||||
clockSkewSeconds: [{ value: 0, disabled: true }],
|
||||
});
|
||||
}
|
||||
|
||||
public formatClockSkewLabel(seconds: number): string {
|
||||
return seconds + 's';
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.subscription = this.route.params.subscribe(params => this.getData(params));
|
||||
}
|
||||
@ -132,11 +140,12 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
this.mgmtService.GetIam().then(iam => {
|
||||
this.isZitadel = iam.toObject().iamProjectId === this.projectId;
|
||||
});
|
||||
this.authService.isAllowed(['project.app.write$', 'project.app.write:' + id]).pipe(take(1)).subscribe((allowed) => {
|
||||
this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid]).pipe(take(1)).subscribe((allowed) => {
|
||||
this.canWrite = allowed;
|
||||
this.mgmtService.GetApplicationById(projectid, id).then(app => {
|
||||
this.app = app.toObject();
|
||||
this.appNameForm.patchValue(this.app);
|
||||
console.log(this.app);
|
||||
if (allowed) {
|
||||
this.appNameForm.enable();
|
||||
this.appForm.enable();
|
||||
@ -150,6 +159,11 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
|
||||
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
|
||||
}
|
||||
if (this.app.oidcConfig?.clockSkew) {
|
||||
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
|
||||
console.log(inSecs);
|
||||
this.appForm.controls['clockSkewSeconds'].setValue(inSecs);
|
||||
}
|
||||
if (this.app.oidcConfig) {
|
||||
this.appForm.patchValue(this.app.oidcConfig);
|
||||
}
|
||||
@ -159,8 +173,6 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
this.errorMessage = error.message;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.docs = (await this.mgmtService.GetZitadelDocs()).toObject();
|
||||
}
|
||||
|
||||
@ -247,9 +259,32 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
this.app.oidcConfig.accessTokenType = this.accessTokenType?.value;
|
||||
this.app.oidcConfig.accessTokenRoleAssertion = this.accessTokenRoleAssertion?.value;
|
||||
this.app.oidcConfig.idTokenRoleAssertion = this.idTokenRoleAssertion?.value;
|
||||
this.app.oidcConfig.idTokenUserinfoAssertion = this.idTokenUserinfoAssertion?.value;
|
||||
|
||||
|
||||
const req = new OIDCConfigUpdate();
|
||||
req.setProjectId(this.projectId);
|
||||
req.setApplicationId(this.app.id);
|
||||
req.setRedirectUrisList(this.app.oidcConfig.redirectUrisList);
|
||||
req.setResponseTypesList(this.app.oidcConfig.responseTypesList);
|
||||
req.setAuthMethodType(this.app.oidcConfig.authMethodType);
|
||||
req.setPostLogoutRedirectUrisList(this.app.oidcConfig.postLogoutRedirectUrisList);
|
||||
req.setGrantTypesList(this.app.oidcConfig.grantTypesList);
|
||||
req.setApplicationType(this.app.oidcConfig.applicationType);
|
||||
req.setDevMode(this.app.oidcConfig.devMode);
|
||||
req.setAccessTokenType(this.app.oidcConfig.accessTokenType);
|
||||
req.setAccessTokenRoleAssertion(this.app.oidcConfig.accessTokenRoleAssertion);
|
||||
req.setIdTokenRoleAssertion(this.app.oidcConfig.idTokenRoleAssertion);
|
||||
req.setIdTokenUserinfoAssertion(this.app.oidcConfig.idTokenUserinfoAssertion);
|
||||
if (this.clockSkewSeconds?.value) {
|
||||
const dur = new Duration();
|
||||
dur.setSeconds(Math.floor(this.clockSkewSeconds?.value));
|
||||
dur.setNanos((Math.floor(this.clockSkewSeconds?.value % 1) * 10000));
|
||||
req.setClockSkew(dur);
|
||||
}
|
||||
console.log(req.toObject());
|
||||
this.mgmtService
|
||||
.UpdateOIDCAppConfig(this.projectId, this.app.id, this.app.oidcConfig)
|
||||
.UpdateOIDCAppConfig(req)
|
||||
.then(() => {
|
||||
this.toast.showInfo('APP.TOAST.OIDCUPDATED', true);
|
||||
})
|
||||
@ -319,4 +354,12 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public get accessTokenRoleAssertion(): AbstractControl | null {
|
||||
return this.appForm.get('accessTokenRoleAssertion');
|
||||
}
|
||||
|
||||
public get idTokenUserinfoAssertion(): AbstractControl | null {
|
||||
return this.appForm.get('idTokenUserinfoAssertion');
|
||||
}
|
||||
|
||||
public get clockSkewSeconds(): AbstractControl | null {
|
||||
return this.appForm.get('clockSkewSeconds');
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@ -61,6 +62,7 @@ import { AppsRoutingModule } from './apps-routing.module';
|
||||
MatSlideToggleModule,
|
||||
InputModule,
|
||||
MetaLayoutModule,
|
||||
MatSliderModule,
|
||||
ChangesModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
|
@ -0,0 +1,50 @@
|
||||
<app-card title="{{'USER.PASSWORDLESS.TITLE' | translate}}"
|
||||
description="{{'USER.PASSWORDLESS.DESCRIPTION' | translate}}">
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getPasswordless()"
|
||||
[dataSize]="dataSource?.data?.length">
|
||||
<table class="table" mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.NAME' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span *ngIf="mfa?.name" class="centered">
|
||||
{{ mfa?.name }}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||
{{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}}
|
||||
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||
class="verified las la-check-circle"></i>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLEACTIONS' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||
(click)="deletePasswordless(mfa.id)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</app-refresh-table>
|
||||
<div class="add-row">
|
||||
<button class="button" (click)="addPasswordless()" mat-stroked-button color="primary"
|
||||
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
||||
<i class="las la-fingerprint"></i>
|
||||
{{'USER.PASSWORDLESS.U2F' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="loading$ | async">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
</app-card>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<AuthPasswordlessComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AuthPasswordlessComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AuthPasswordlessComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,121 @@
|
||||
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<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@ViewChild(MatTable) public table!: MatTable<WebAuthNToken.AsObject>;
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
public dataSource!: MatTableDataSource<WebAuthNToken.AsObject>;
|
||||
|
||||
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);
|
||||
if (credOptions.publicKey.excludeCredentials) {
|
||||
credOptions.publicKey.excludeCredentials.map(cred => {
|
||||
cred.id = _base64ToArrayBuffer(cred.id as any);
|
||||
return cred;
|
||||
});
|
||||
}
|
||||
console.log(credOptions);
|
||||
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);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -51,6 +51,8 @@
|
||||
</app-contact>
|
||||
</app-card>
|
||||
|
||||
<app-auth-passwordless *ngIf="user" #mfaComponent></app-auth-passwordless>
|
||||
|
||||
<app-auth-user-mfa *ngIf="user" #mfaComponent></app-auth-user-mfa>
|
||||
|
||||
<app-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
|
||||
|
@ -1,11 +1,19 @@
|
||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getMFAs()" [dataSize]="dataSource?.data?.length">
|
||||
<table class="table" mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="attr">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.ATTRIBUTE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span *ngIf="mfa?.attribute" class="centered">
|
||||
{{ mfa?.attribute }}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||
@ -20,7 +28,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||
(click)="deleteMFA(mfa.type)">
|
||||
(click)="deleteMFA(mfa.type, mfa.id)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
@ -35,6 +43,11 @@
|
||||
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
||||
<mat-icon class="icon" svgIcon="mdi_radar"></mat-icon>{{'USER.MFA.OTP' | translate}}
|
||||
</button>
|
||||
<button class="button" (click)="addU2F()" mat-stroked-button color="primary"
|
||||
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
||||
<i class="las la-fingerprint"></i>
|
||||
{{'USER.MFA.U2F' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="loading$ | async">
|
||||
|
@ -2,6 +2,7 @@
|
||||
.add-row {
|
||||
display: flex;
|
||||
margin: -.5rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.button {
|
||||
margin: .5rem;
|
||||
|
@ -4,11 +4,23 @@ 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 { _base64ToArrayBuffer } from '../../u2f-util';
|
||||
import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component';
|
||||
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-user-mfa',
|
||||
@ -16,7 +28,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<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@ -29,10 +41,13 @@ 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 +65,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
dialogRef.afterClosed().subscribe((code) => {
|
||||
if (code) {
|
||||
this.service.VerifyMfaOTP(code).then(() => {
|
||||
this.getOTP();
|
||||
this.getMFAs();
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -59,7 +74,44 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
public getOTP(): 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);
|
||||
if (credOptions.publicKey.excludeCredentials) {
|
||||
credOptions.publicKey.excludeCredentials.map(cred => {
|
||||
cred.id = _base64ToArrayBuffer(cred.id as any);
|
||||
return cred;
|
||||
});
|
||||
}
|
||||
console.log(credOptions);
|
||||
const dialogRef = this.dialog.open(DialogU2FComponent, {
|
||||
width: '400px',
|
||||
data: {
|
||||
credOptions,
|
||||
type: U2FComponentDestination.MFA,
|
||||
},
|
||||
});
|
||||
|
||||
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 +125,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 +146,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 +166,6 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<h1 mat-dialog-title>{{'USER.MFA.OTP_DIALOG_TITLE' | translate}}</h1>
|
||||
<div mat-dialog-content>
|
||||
<p translate>{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}</p>
|
||||
<p>{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}</p>
|
||||
<div class="qrcode-wrapper">
|
||||
<qrcode *ngIf="data" class="qrcode" [qrdata]="data" [width]="150" [errorCorrectionLevel]="'M'"></qrcode>
|
||||
</div>
|
||||
@ -15,4 +15,4 @@
|
||||
<button mat-raised-button class="ok-button" color="primary" (click)="closeDialogWithCode()"><span
|
||||
translate>ACTIONS.OK</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,19 @@
|
||||
<h1 mat-dialog-title>{{'USER.MFA.U2F_DIALOG_TITLE' | translate}}</h1>
|
||||
<div mat-dialog-content>
|
||||
<p>{{'USER.MFA.U2F_DIALOG_DESCRIPTION' | translate}}</p>
|
||||
|
||||
<cnsl-form-field class="form-field" label="Name" required="true">
|
||||
<cnsl-label>{{'USER.MFA.U2F_NAME' | translate}}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="name" required (keydown.enter)="name ? closeDialogWithCode() : null" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<mat-spinner diameter="30" *ngIf="loading"></mat-spinner>
|
||||
|
||||
<p class="error">{{error}}</p>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-button (click)="closeDialog()">{{'ACTIONS.CLOSE' | translate}}</button>
|
||||
<button cdkFocusInitial [disabled]="!name" mat-raised-button class="ok-button" color="primary"
|
||||
(click)="closeDialogWithCode()">{{'ACTIONS.VERIFY' | translate}}
|
||||
</button>
|
||||
</div>
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
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, '');
|
||||
}
|
||||
|
||||
export enum U2FComponentDestination {
|
||||
MFA = 'mfa',
|
||||
PASSWORDLESS = 'passwordless',
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog-u2f',
|
||||
templateUrl: './dialog-u2f.component.html',
|
||||
styleUrls: ['./dialog-u2f.component.scss'],
|
||||
})
|
||||
export class DialogU2FComponent {
|
||||
private type!: U2FComponentDestination;
|
||||
public name: string = '';
|
||||
public error: string = '';
|
||||
public loading: boolean = false;
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<DialogU2FComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { credOptions: any; type: U2FComponentDestination; },
|
||||
private service: GrpcAuthService, private translate: TranslateService, private toast: ToastService) {
|
||||
this.type = data.type;
|
||||
}
|
||||
|
||||
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);
|
||||
if (this.type === U2FComponentDestination.MFA) {
|
||||
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 if (this.type === U2FComponentDestination.PASSWORDLESS) {
|
||||
this.service.verifyMyPasswordless(base64, this.name).then(() => {
|
||||
this.translate.get('USER.PASSWORDLESS.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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<div mat-dialog-content>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{data.labelKey | translate }}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="value" />
|
||||
<input cnslInput [(ngModel)]="value" (keydown.enter)="value ? closeDialogWithValue(value) : null" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
|
10
console/src/app/pages/users/user-detail/u2f-util.ts
Normal file
10
console/src/app/pages/users/user-detail/u2f-util.ts
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
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;
|
||||
}
|
@ -29,10 +29,12 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
||||
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 { AuthPasswordlessComponent } from './auth-user-detail/auth-passwordless/auth-passwordless.component';
|
||||
import { AuthUserDetailComponent } from './auth-user-detail/auth-user-detail.component';
|
||||
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';
|
||||
@ -46,6 +48,7 @@ import { ShowKeyDialogModule } from './machine-keys/show-key-dialog/show-key-dia
|
||||
import { MembershipsComponent } from './memberships/memberships.component';
|
||||
import { PasswordComponent } from './password/password.component';
|
||||
import { UserDetailRoutingModule } from './user-detail-routing.module';
|
||||
import { PasswordlessComponent } from './user-detail/passwordless/passwordless.component';
|
||||
import { UserDetailComponent } from './user-detail/user-detail.component';
|
||||
import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
|
||||
@ -56,7 +59,9 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
DialogOtpComponent,
|
||||
EditDialogComponent,
|
||||
AuthUserMfaComponent,
|
||||
AuthPasswordlessComponent,
|
||||
UserMfaComponent,
|
||||
PasswordlessComponent,
|
||||
ThemeSettingComponent,
|
||||
PasswordComponent,
|
||||
CodeDialogComponent,
|
||||
@ -65,6 +70,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
ExternalIdpsComponent,
|
||||
ContactComponent,
|
||||
ResendEmailDialogComponent,
|
||||
DialogU2FComponent,
|
||||
],
|
||||
imports: [
|
||||
UserDetailRoutingModule,
|
||||
|
@ -0,0 +1,43 @@
|
||||
<app-card title="{{'USER.PASSWORDLESS.TITLE' | translate}}"
|
||||
description="{{'USER.PASSWORDLESS.DESCRIPTION' | translate}}">
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getPasswordless()"
|
||||
[dataSize]="dataSource?.data?.length">
|
||||
<table class="table" mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.NAME' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span *ngIf="mfa?.name" class="centered">
|
||||
{{ mfa?.name }}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||
{{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}}
|
||||
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||
class="verified las la-check-circle"></i>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLEACTIONS' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||
(click)="deletePasswordless(mfa.id)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</app-refresh-table>
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="loading$ | async">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
</app-card>
|
@ -0,0 +1,26 @@
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<AuthPasswordlessComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AuthPasswordlessComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AuthPasswordlessComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,84 @@
|
||||
import { Component, Input, 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, WebAuthNToken } from 'src/app/proto/generated/auth_pb';
|
||||
import { UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
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-passwordless',
|
||||
templateUrl: './passwordless.component.html',
|
||||
styleUrls: ['./passwordless.component.scss'],
|
||||
})
|
||||
export class PasswordlessComponent implements OnInit, OnDestroy {
|
||||
@Input() private user!: UserView.AsObject;
|
||||
public displayedColumns: string[] = ['name', 'state', 'actions'];
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@ViewChild(MatTable) public table!: MatTable<WebAuthNToken.AsObject>;
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
public dataSource!: MatTableDataSource<WebAuthNToken.AsObject>;
|
||||
|
||||
public MFAState: any = MFAState;
|
||||
public error: string = '';
|
||||
|
||||
constructor(private service: ManagementService,
|
||||
private toast: ToastService,
|
||||
private dialog: MatDialog) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.getPasswordless();
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.loadingSubject.complete();
|
||||
}
|
||||
|
||||
public getPasswordless(): void {
|
||||
this.service.GetPasswordless(this.user.id).then(passwordless => {
|
||||
console.log(passwordless.toObject().tokensList);
|
||||
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.RemovePasswordless(id, this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PASSWORDLESSREMOVED', true);
|
||||
this.getPasswordless();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -84,6 +84,8 @@
|
||||
</app-contact>
|
||||
</app-card>
|
||||
|
||||
<app-passwordless *ngIf="user && user.human" [user]="user"></app-passwordless>
|
||||
|
||||
<app-user-mfa *ngIf="user && user.human" [user]="user"></app-user-mfa>
|
||||
|
||||
<app-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
|
||||
|
@ -1,11 +1,19 @@
|
||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getMFAs()" [dataSize]="dataSource?.data?.length">
|
||||
<table class="table" mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="attr">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.ATTRIBUTE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span *ngIf="mfa?.attribute" class="centered">
|
||||
{{ mfa.attribute }}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
@ -21,7 +29,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||
(click)="deleteMFA(mfa.type)">
|
||||
(click)="deleteMFA(mfa.type, mfa.id)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -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<UserMultiFactor.AsObject[]> = new BehaviorSubject<UserMultiFactor.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(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,8 +45,9 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
||||
this.loadingSubject.complete();
|
||||
}
|
||||
|
||||
public getOTP(): void {
|
||||
public getMFAs(): void {
|
||||
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
||||
console.log(mfas.toObject().mfasList);
|
||||
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||
this.dataSource.sort = this.sort;
|
||||
}).catch(error => {
|
||||
@ -54,13 +55,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 +77,20 @@ 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) {
|
||||
console.log(this.user.id, id);
|
||||
this.mgmtUserService.RemoveMfaU2F(this.user.id, 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);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="userResult?.totalResult"
|
||||
[timestamp]="userResult?.viewTimestamp" [selection]="selection"
|
||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes">
|
||||
<cnsl-form-field @appearfade *ngIf="userSearchKey != undefined" actions class="filtername">
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
DefaultLabelPolicyUpdate,
|
||||
DefaultLabelPolicyView,
|
||||
DefaultLoginPolicy,
|
||||
DefaultLoginPolicyRequest,
|
||||
DefaultLoginPolicyView,
|
||||
DefaultPasswordAgePolicyRequest,
|
||||
DefaultPasswordAgePolicyView,
|
||||
@ -36,6 +37,8 @@ import {
|
||||
IdpSearchRequest,
|
||||
IdpSearchResponse,
|
||||
IdpView,
|
||||
MultiFactor,
|
||||
MultiFactorsResult,
|
||||
OidcIdpConfig,
|
||||
OidcIdpConfigCreate,
|
||||
OidcIdpConfigUpdate,
|
||||
@ -46,6 +49,8 @@ import {
|
||||
OrgSetUpRequest,
|
||||
OrgSetUpResponse,
|
||||
RemoveIamMemberRequest,
|
||||
SecondFactor,
|
||||
SecondFactorsResult,
|
||||
ViewID,
|
||||
Views,
|
||||
} from '../proto/generated/admin_pb';
|
||||
@ -73,6 +78,32 @@ export class AdminService {
|
||||
return this.grpcService.admin.setUpOrg(req);
|
||||
}
|
||||
|
||||
public getDefaultLoginPolicyMultiFactors(): Promise<MultiFactorsResult> {
|
||||
const req = new Empty();
|
||||
return this.grpcService.admin.getDefaultLoginPolicyMultiFactors(req);
|
||||
}
|
||||
|
||||
public addMultiFactorToDefaultLoginPolicy(req: MultiFactor): Promise<MultiFactor> {
|
||||
return this.grpcService.admin.addMultiFactorToDefaultLoginPolicy(req);
|
||||
}
|
||||
|
||||
public RemoveMultiFactorFromDefaultLoginPolicy(req: MultiFactor): Promise<Empty> {
|
||||
return this.grpcService.admin.removeMultiFactorFromDefaultLoginPolicy(req);
|
||||
}
|
||||
|
||||
public GetDefaultLoginPolicySecondFactors(): Promise<SecondFactorsResult> {
|
||||
const req = new Empty();
|
||||
return this.grpcService.admin.getDefaultLoginPolicySecondFactors(req);
|
||||
}
|
||||
|
||||
public AddSecondFactorToDefaultLoginPolicy(req: SecondFactor): Promise<SecondFactor> {
|
||||
return this.grpcService.admin.addSecondFactorToDefaultLoginPolicy(req);
|
||||
}
|
||||
|
||||
public RemoveSecondFactorFromDefaultLoginPolicy(req: SecondFactor): Promise<Empty> {
|
||||
return this.grpcService.admin.removeSecondFactorFromDefaultLoginPolicy(req);
|
||||
}
|
||||
|
||||
public GetIamMemberRoles(): Promise<IamMemberRoles> {
|
||||
const req = new Empty();
|
||||
return this.grpcService.admin.getIamMemberRoles(req);
|
||||
@ -184,7 +215,7 @@ export class AdminService {
|
||||
return this.grpcService.admin.getDefaultLoginPolicy(req);
|
||||
}
|
||||
|
||||
public UpdateDefaultLoginPolicy(req: DefaultLoginPolicy): Promise<DefaultLoginPolicy> {
|
||||
public UpdateDefaultLoginPolicy(req: DefaultLoginPolicyRequest): Promise<DefaultLoginPolicy> {
|
||||
return this.grpcService.admin.updateDefaultLoginPolicy(req);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,10 @@ import {
|
||||
UserView,
|
||||
VerifyMfaOtp,
|
||||
VerifyUserPhoneRequest,
|
||||
VerifyWebAuthN,
|
||||
WebAuthNResponse,
|
||||
WebAuthNTokenID,
|
||||
WebAuthNTokens,
|
||||
} from '../proto/generated/auth_pb';
|
||||
import { GrpcService } from './grpc.service';
|
||||
import { StorageKey, StorageService } from './storage.service';
|
||||
@ -328,6 +332,56 @@ export class GrpcAuthService {
|
||||
);
|
||||
}
|
||||
|
||||
public AddMyMfaU2F(): Promise<WebAuthNResponse> {
|
||||
return this.grpcService.auth.addMyMfaU2F(
|
||||
new Empty(),
|
||||
);
|
||||
}
|
||||
|
||||
public RemoveMyMfaU2F(id: string): Promise<Empty> {
|
||||
const req = new WebAuthNTokenID();
|
||||
req.setId(id);
|
||||
return this.grpcService.auth.removeMyMfaU2F(req);
|
||||
}
|
||||
|
||||
public VerifyMyMfaU2F(credential: string, tokenname: string): Promise<Empty> {
|
||||
const req = new VerifyWebAuthN();
|
||||
req.setPublicKeyCredential(credential);
|
||||
req.setTokenName(tokenname);
|
||||
|
||||
return this.grpcService.auth.verifyMyMfaU2F(
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
public GetMyPasswordless(): Promise<WebAuthNTokens> {
|
||||
return this.grpcService.auth.getMyPasswordless(
|
||||
new Empty(),
|
||||
);
|
||||
}
|
||||
|
||||
public AddMyPasswordless(): Promise<WebAuthNResponse> {
|
||||
return this.grpcService.auth.addMyPasswordless(
|
||||
new Empty(),
|
||||
);
|
||||
}
|
||||
|
||||
public RemoveMyPasswordless(id: string): Promise<Empty> {
|
||||
const req = new WebAuthNTokenID();
|
||||
req.setId(id);
|
||||
return this.grpcService.auth.removeMyPasswordless(req);
|
||||
}
|
||||
|
||||
public verifyMyPasswordless(credential: string, tokenname: string): Promise<Empty> {
|
||||
const req = new VerifyWebAuthN();
|
||||
req.setPublicKeyCredential(credential);
|
||||
req.setTokenName(tokenname);
|
||||
|
||||
return this.grpcService.auth.verifyMyPasswordless(
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
public RemoveMfaOTP(): Promise<Empty> {
|
||||
return this.grpcService.auth.removeMfaOTP(
|
||||
new Empty(),
|
||||
|
@ -3,6 +3,7 @@ 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,
|
||||
AddMachineKeyResponse,
|
||||
@ -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,8 @@ import {
|
||||
UserSearchResponse,
|
||||
UserView,
|
||||
ValidateOrgDomainRequest,
|
||||
WebAuthNTokenID,
|
||||
WebAuthNTokens,
|
||||
ZitadelDocs,
|
||||
} from '../proto/generated/management_pb';
|
||||
import { GrpcService } from './grpc.service';
|
||||
@ -185,6 +191,45 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.searchIdps(req);
|
||||
}
|
||||
|
||||
public GetPasswordless(userId: string): Promise<WebAuthNTokens> {
|
||||
const req = new UserID();
|
||||
req.setId(userId);
|
||||
return this.grpcService.mgmt.getPasswordless(req);
|
||||
}
|
||||
|
||||
public RemovePasswordless(id: string, userId: string): Promise<Empty> {
|
||||
const req = new WebAuthNTokenID();
|
||||
req.setId(id);
|
||||
req.setUserId(userId);
|
||||
return this.grpcService.mgmt.removePasswordless(req);
|
||||
}
|
||||
|
||||
public GetLoginPolicyMultiFactors(): Promise<MultiFactorsResult> {
|
||||
const req = new Empty();
|
||||
return this.grpcService.mgmt.getLoginPolicyMultiFactors(req);
|
||||
}
|
||||
|
||||
public AddMultiFactorToLoginPolicy(req: MultiFactor): Promise<MultiFactor> {
|
||||
return this.grpcService.mgmt.addMultiFactorToLoginPolicy(req);
|
||||
}
|
||||
|
||||
public RemoveMultiFactorFromLoginPolicy(req: MultiFactor): Promise<Empty> {
|
||||
return this.grpcService.mgmt.removeMultiFactorFromLoginPolicy(req);
|
||||
}
|
||||
|
||||
public GetLoginPolicySecondFactors(): Promise<SecondFactorsResult> {
|
||||
const req = new Empty();
|
||||
return this.grpcService.mgmt.getLoginPolicySecondFactors(req);
|
||||
}
|
||||
|
||||
public AddSecondFactorToLoginPolicy(req: SecondFactor): Promise<SecondFactor> {
|
||||
return this.grpcService.mgmt.addSecondFactorToLoginPolicy(req);
|
||||
}
|
||||
|
||||
public RemoveSecondFactorFromLoginPolicy(req: SecondFactor): Promise<Empty> {
|
||||
return this.grpcService.mgmt.removeSecondFactorFromLoginPolicy(req);
|
||||
}
|
||||
|
||||
public GetLoginPolicy(): Promise<LoginPolicyView> {
|
||||
const req = new Empty();
|
||||
return this.grpcService.mgmt.getLoginPolicy(req);
|
||||
@ -683,6 +728,13 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.removeMfaOTP(req);
|
||||
}
|
||||
|
||||
public RemoveMfaU2F(userid: string, id: string): Promise<Empty> {
|
||||
const req = new WebAuthNTokenID();
|
||||
req.setId(id);
|
||||
req.setUserId(userid);
|
||||
return this.grpcService.mgmt.removeMfaU2F(req);
|
||||
}
|
||||
|
||||
public SaveUserProfile(
|
||||
id: string,
|
||||
firstName?: string,
|
||||
@ -1307,21 +1359,7 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.updateApplication(req);
|
||||
}
|
||||
|
||||
public UpdateOIDCAppConfig(projectId: string,
|
||||
appId: string, oidcConfig: OIDCConfig.AsObject): Promise<OIDCConfig> {
|
||||
const req = new OIDCConfigUpdate();
|
||||
req.setProjectId(projectId);
|
||||
req.setApplicationId(appId);
|
||||
req.setRedirectUrisList(oidcConfig.redirectUrisList);
|
||||
req.setResponseTypesList(oidcConfig.responseTypesList);
|
||||
req.setAuthMethodType(oidcConfig.authMethodType);
|
||||
req.setPostLogoutRedirectUrisList(oidcConfig.postLogoutRedirectUrisList);
|
||||
req.setGrantTypesList(oidcConfig.grantTypesList);
|
||||
req.setApplicationType(oidcConfig.applicationType);
|
||||
req.setDevMode(oidcConfig.devMode);
|
||||
req.setAccessTokenType(oidcConfig.accessTokenType);
|
||||
req.setAccessTokenRoleAssertion(oidcConfig.accessTokenRoleAssertion);
|
||||
req.setIdTokenRoleAssertion(oidcConfig.idTokenRoleAssertion);
|
||||
public UpdateOIDCAppConfig(req: OIDCConfigUpdate): Promise<OIDCConfig> {
|
||||
return this.grpcService.mgmt.updateApplicationOIDCConfig(req);
|
||||
}
|
||||
}
|
||||
|
@ -152,16 +152,20 @@
|
||||
},
|
||||
"EMPTY":"Keine Einträge"
|
||||
},
|
||||
"MFA": {
|
||||
"PASSWORDLESS": {
|
||||
"TABLETYPE":"Typ",
|
||||
"TABLESTATE":"Status",
|
||||
"NAME":"Name",
|
||||
"TABLEACTIONS":"Aktionen",
|
||||
"TITLE": "Multifaktor-Authentisierung",
|
||||
"DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um Dein Konto optimal zu schützen.",
|
||||
"TITLE": "Passwortlose Authentifizierungsmethoden",
|
||||
"DESCRIPTION": "Füge WebAuthn kompatible Authentifikatoren hinzu um dich passwortlos anzumelden.",
|
||||
"MANAGE_DESCRIPTION": "Verwalte die Multifaktor-Merkmale Deiner Benutzer.",
|
||||
"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":"Authentifikator hinzufügen",
|
||||
"U2F_DIALOG_TITLE": "Authentifikator hinzufügen",
|
||||
"U2F_DIALOG_DESCRIPTION": "Gib einen Namen für den von dir verwendeten Login an.",
|
||||
"U2F_SUCCESS":"Passwordless erfolgreich erstellt!",
|
||||
"U2F_ERROR":"Ein Fehler ist aufgetreten!",
|
||||
"U2F_NAME":"Authentifikator Name",
|
||||
"TYPE": {
|
||||
"0":"Keine MFA definiert",
|
||||
"1":"OTP",
|
||||
@ -174,8 +178,41 @@
|
||||
"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?"
|
||||
"DELETE_TITLE":"Passwordless entfernen",
|
||||
"DELETE_DESCRIPTION":"Sie sind im Begriff eine Passwortlose Authentifizierungsmethode zu entfernen. Sind sie sicher?"
|
||||
}
|
||||
},
|
||||
"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.",
|
||||
"MANAGE_DESCRIPTION": "Verwalte die Multifaktor-Merkmale Deiner Benutzer.",
|
||||
"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",
|
||||
"2":"U2F"
|
||||
},
|
||||
"STATE": {
|
||||
"0": "Kein Status",
|
||||
"1": "Nicht bereit",
|
||||
"2": "Bereit",
|
||||
"3": "Gelöscht"
|
||||
},
|
||||
"DIALOG": {
|
||||
"MFA_DELETE_TITLE":"Zweiten Faktor entfernen",
|
||||
"MFA_DELETE_DESCRIPTION":"Sie sind im Begriff eine Zweitfaktormethode zu entfernen. Sind sie sicher?"
|
||||
}
|
||||
},
|
||||
"EXTERNALIDP": {
|
||||
@ -345,6 +382,8 @@
|
||||
"PHONEVERIFICATIONSENT":"Bestätigungscode per Telefonnummer gesendet.",
|
||||
"EMAILVERIFICATIONSENT":"Bestätigungscode per E-Mail gesendet.",
|
||||
"OTPREMOVED":"OTP entfernt.",
|
||||
"U2FREMOVED":"U2F entfernt.",
|
||||
"PASSWORDLESSREMOVED":"Passwordless entfernt.",
|
||||
"INITIALPASSWORDSET":"Initiales Passwort gesetzt.",
|
||||
"PASSWORDNOTIFICATIONSENT":"Passwortänderung mittgeteilt.",
|
||||
"PASSWORDCHANGED":"Passwort geändert.",
|
||||
@ -551,7 +590,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 +804,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 +867,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",
|
||||
@ -836,6 +908,11 @@
|
||||
"TITLE":"Identity Provider hinzufügen",
|
||||
"DESCRIPTION":"Sie können vordefinierte oder selbsterstellten Provider auswählen",
|
||||
"SELECTIDPS":"Identity Provider"
|
||||
},
|
||||
"PASSWORDLESS":"Passwordloser Login",
|
||||
"PASSWORDLESSTYPE": {
|
||||
"0":"Nicht erlaubt",
|
||||
"1":"Erlaubt"
|
||||
}
|
||||
},
|
||||
"APP": {
|
||||
@ -910,8 +987,11 @@
|
||||
"OVERVIEWTITLE":"Deine Konfiguration ist bereit. Du kannst sie hier nochmals prüfen.",
|
||||
"ACCESSTOKENROLEASSERTION":"Benutzerrollen dem Access Token hinzufügen",
|
||||
"ACCESSTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Access Token die Rollen des Authentifizierten Benutzers hinzugefügt.",
|
||||
"IDTOKENROLEASSERTION":"Benutzerrollen dem Id Token hinzufügen",
|
||||
"IDTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Id Token die Rollen des Authentifizierten Benutzers hinzugefügt."
|
||||
"IDTOKENROLEASSERTION":"Benutzerrollen im ID Token",
|
||||
"IDTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Id Token die Rollen des Authentifizierten Benutzers hinzugefügt.",
|
||||
"IDTOKENUSERINFOASSERTION":"User Info im ID Token",
|
||||
"IDTOKENUSERINFOASSERTION_DESCRIPTION":"Ermöglich OIDC clients claims von profile, email, phone und address direkt vom ID Token zu beziehen.",
|
||||
"CLOCKSKEW":"ermöglicht Clients, den Taktversatz von OP und Client zu verarbeiten. Die Dauer (0-5s) wird der exp addiert und von iats, auth_time und nbf abgezogen."
|
||||
},
|
||||
"TOAST": {
|
||||
"REACTIVATED":"Anwendung reaktiviert.",
|
||||
|
@ -152,16 +152,20 @@
|
||||
},
|
||||
"EMPTY":"No entries"
|
||||
},
|
||||
"MFA": {
|
||||
"PASSWORDLESS": {
|
||||
"TABLETYPE":"Type",
|
||||
"TABLESTATE":"Status",
|
||||
"NAME":"Name",
|
||||
"TABLEACTIONS":"Actions",
|
||||
"TITLE": "Multifactor Authentication",
|
||||
"DESCRIPTION": "Add a second factor to ensure optimal security for your account.",
|
||||
"TITLE": "Passwordless Authentication",
|
||||
"DESCRIPTION": "Add WebAuthn based Authentication Methods to log onto ZITADEL passwordless.",
|
||||
"MANAGE_DESCRIPTION": "Manage the second factor methods of your users.",
|
||||
"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 authenticator",
|
||||
"U2F_DIALOG_TITLE": "Verify authenticator",
|
||||
"U2F_DIALOG_DESCRIPTION": "Enter a name for your used passwordless Login",
|
||||
"U2F_SUCCESS":"Passwordless Auth created successfully!",
|
||||
"U2F_ERROR":"An error during U2F setup occurred!",
|
||||
"U2F_NAME":"Authenticator Name",
|
||||
"TYPE": {
|
||||
"0": "No MFA defined",
|
||||
"1": "OTP",
|
||||
@ -174,8 +178,41 @@
|
||||
"3": "Deleted"
|
||||
},
|
||||
"DIALOG": {
|
||||
"OTP_DELETE_TITLE":"Remove OTP Factor",
|
||||
"OTP_DELETE_DESCRIPTION":"You are about to delete OTP as second factor. Are you sure?"
|
||||
"DELETE_TITLE":"Remove Passwordless Authentication Method",
|
||||
"DELETE_DESCRIPTION":"You are about to delete a passwordless Authentication method. Are you sure?"
|
||||
}
|
||||
},
|
||||
"MFA": {
|
||||
"TABLETYPE":"Type",
|
||||
"TABLESTATE":"Status",
|
||||
"ATTRIBUTE":"Attribut",
|
||||
"TABLEACTIONS":"Actions",
|
||||
"TITLE": "Multifactor Authentication",
|
||||
"DESCRIPTION": "Add a second factor to ensure optimal security for your account.",
|
||||
"MANAGE_DESCRIPTION": "Manage the second factor methods of your users.",
|
||||
"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",
|
||||
"2": "U2F"
|
||||
},
|
||||
"STATE": {
|
||||
"0": "No State",
|
||||
"1": "Not Ready",
|
||||
"2": "Ready",
|
||||
"3": "Deleted"
|
||||
},
|
||||
"DIALOG": {
|
||||
"MFA_DELETE_TITLE":"Remove Secondfactor",
|
||||
"MFA_DELETE_DESCRIPTION":"You are about to delete a second factor. Are you sure?"
|
||||
}
|
||||
},
|
||||
"EXTERNALIDP": {
|
||||
@ -345,6 +382,8 @@
|
||||
"PHONEVERIFICATIONSENT":"Phone verification code sent.",
|
||||
"EMAILVERIFICATIONSENT":"E-mail verification code sent.",
|
||||
"OTPREMOVED":"OTP removed.",
|
||||
"U2FREMOVED":"U2F removed.",
|
||||
"PASSWORDLESSREMOVED":"Passwordless removed.",
|
||||
"INITIALPASSWORDSET":"Initial password set.",
|
||||
"PASSWORDNOTIFICATIONSENT":"Password change notification sent.",
|
||||
"PASSWORDCHANGED":"Password changed successfully.",
|
||||
@ -551,7 +590,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 +867,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",
|
||||
@ -836,6 +908,11 @@
|
||||
"TITLE":"Add Identity Provider",
|
||||
"DESCRIPTION":"You can select predefined or selfcreated providers for authentication.",
|
||||
"SELECTIDPS":"Identity providers"
|
||||
},
|
||||
"PASSWORDLESS":"Passwordless Login",
|
||||
"PASSWORDLESSTYPE": {
|
||||
"0":"Not allowed",
|
||||
"1":"Allowed"
|
||||
}
|
||||
},
|
||||
"APP": {
|
||||
@ -910,8 +987,11 @@
|
||||
"OVERVIEWTITLE":"You are now done. Review your configuration.",
|
||||
"ACCESSTOKENROLEASSERTION":"Add user roles to the access token",
|
||||
"ACCESSTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the access token.",
|
||||
"IDTOKENROLEASSERTION":"Add user roles to the id token",
|
||||
"IDTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the id token."
|
||||
"IDTOKENROLEASSERTION":"User roles inside ID Token",
|
||||
"IDTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the ID token.",
|
||||
"IDTOKENUSERINFOASSERTION":"User Info inside ID Token",
|
||||
"IDTOKENUSERINFOASSERTION_DESCRIPTION":"Enables clients to retrieve profile, email, phone and address claims from ID token.",
|
||||
"CLOCKSKEW":"Enables clients to handle clock skew of OP and client. The duration (0-5s) will be added to exp claim and subtracted from iats, auth_time and nbf."
|
||||
},
|
||||
"TOAST": {
|
||||
"REACTIVATED":"Application reactivated.",
|
||||
|
9
go.mod
9
go.mod
@ -15,8 +15,8 @@ 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/cockroachdb/cockroach-go/v2 v2.0.8
|
||||
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
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
@ -40,9 +40,8 @@ require (
|
||||
github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8 // indirect
|
||||
github.com/kevinburke/twilio-go v0.0.0-20200810163702-320748330fac
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lib/pq v1.8.0
|
||||
github.com/lib/pq v1.9.0
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.1.1
|
||||
@ -69,7 +68,7 @@ require (
|
||||
golang.org/x/tools v0.0.0-20201103235415-b653051172e4
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20201103154000-415bd0cd5df6
|
||||
google.golang.org/grpc v1.33.1
|
||||
google.golang.org/grpc v1.34.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/square/go-jose.v2 v2.5.1
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
|
55
go.sum
55
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=
|
||||
@ -132,10 +132,11 @@ github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 h1:Puu1hUwfps3+1C
|
||||
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.0.8 h1:50C/7ptrrfdxDccCjDU0xsdeBca+S0/AYW4Mo8RyzFE=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.0.8/go.mod h1:nkf7rUmgPdawp3EwRjXIumihI2AYg9usGNWbJ2hsJqI=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.1.0 h1:zicZlBhWZu6wfK7Ezg4Owdc3HamLpRdBllPTT9tb+2k=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.1.0/go.mod h1:ilhrLnPDDwGHL+iK2UxQhp1UzUhst8sfItSAgCYwAyg=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@ -169,6 +170,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.4.1 h1:7dLaJvASGRD7X49jSCSXXHwKPm0ZN9r9kJD+p+vS7dM=
|
||||
@ -199,7 +201,6 @@ github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80n
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
@ -378,8 +379,12 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
||||
github.com/jackc/pgconn v1.5.0 h1:oFSOilzIZkyg787M1fEmyMfOUUvwj0daqYMfaWwNL4o=
|
||||
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||
github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA=
|
||||
github.com/jackc/pgconn v1.7.2/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
|
||||
@ -394,31 +399,41 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.1 h1:Rdjp4NFjwHnEslx2b66FfCI2S0LhO4itac3hXz6WX9M=
|
||||
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.3.0 h1:l8JvKrby3RI7Kg3bYEeU9TA4vqC38QDpFCfcrC7KuN0=
|
||||
github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik=
|
||||
github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
|
||||
github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
||||
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
||||
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||
github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||
github.com/jackc/pgtype v1.6.1/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.6.0 h1:Fh0O9GdlG4gYpjpwOqjdEodJUQM9jzN3Hdv7PN0xmm0=
|
||||
github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg=
|
||||
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
|
||||
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
||||
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||
github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms=
|
||||
github.com/jackc/pgx/v4 v4.9.2/go.mod h1:Jt/xJDqjUDUOMSv8VMWPQlCObVgF2XOgqKsW8S4ROYA=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.0 h1:musOWczZC/rSbqut475Vfcczg7jJsdUQf0D6oKPLgNU=
|
||||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
|
||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
@ -466,9 +481,10 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU=
|
||||
@ -476,6 +492,7 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@ -489,7 +506,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
@ -611,6 +627,7 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea h1:jaXWVFZ98/ihXniiDzqNXQgMSgklX4kjfDWZTE3ZtdU=
|
||||
github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@ -699,9 +716,11 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
@ -718,6 +737,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@ -1076,6 +1096,8 @@ google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@ -1123,6 +1145,9 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.0.5/go.mod h1:qrD92UurYzNctBMVCJ8C3VQEjffEuphycXtxOudXNCA=
|
||||
gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -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
|
||||
|
@ -69,7 +69,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 {
|
||||
@ -194,7 +194,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 {
|
||||
@ -298,7 +298,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 {
|
||||
|
@ -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 {
|
||||
|
@ -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,49 @@ 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 (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
|
||||
}
|
||||
@ -66,6 +89,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
|
||||
}
|
||||
|
@ -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,40 +15,73 @@ 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)
|
||||
err = m.processIAMMember(event)
|
||||
case usr_es_model.UserAggregate:
|
||||
err = m.processUser(event)
|
||||
}
|
||||
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,16 +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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
@ -121,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
|
||||
@ -133,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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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,11 +148,10 @@ 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
|
||||
}
|
||||
|
||||
func (i *ExternalIDP) fillData(externalIDP *usr_view_model.ExternalIDPView) error {
|
||||
|
@ -2,28 +2,18 @@ package spooler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
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 es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -1,11 +1,12 @@
|
||||
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"
|
||||
"github.com/caos/zitadel/internal/view/repository"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -40,39 +41,36 @@ 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)
|
||||
}
|
||||
return nil
|
||||
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
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
|
@ -14,33 +14,39 @@ const (
|
||||
authenticated = "authenticated"
|
||||
)
|
||||
|
||||
func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID string, verifier *TokenVerifier, authConfig Config, requiredAuthOption Option, method string) (_ context.Context, err error) {
|
||||
func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID string, verifier *TokenVerifier, authConfig Config, requiredAuthOption Option, method string) (ctxSetter func(context.Context) context.Context, err error) {
|
||||
ctx, span := tracing.NewServerInterceptorSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
ctx, err = VerifyTokenAndWriteCtxData(ctx, token, orgID, verifier, method)
|
||||
ctxData, err := VerifyTokenAndCreateCtxData(ctx, token, orgID, verifier, method)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var perms []string
|
||||
if requiredAuthOption.Permission == authenticated {
|
||||
return ctx, nil
|
||||
return func(parent context.Context) context.Context {
|
||||
return context.WithValue(parent, dataKey, ctxData)
|
||||
}, nil
|
||||
}
|
||||
|
||||
ctx, perms, err = getUserMethodPermissions(ctx, verifier, requiredAuthOption.Permission, authConfig)
|
||||
requestedPermissions, allPermissions, err := getUserMethodPermissions(ctx, verifier, requiredAuthOption.Permission, authConfig, ctxData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, userPermissionSpan := tracing.NewNamedSpan(ctx, "checkUserPermissions")
|
||||
err = checkUserPermissions(req, perms, requiredAuthOption)
|
||||
err = checkUserPermissions(req, requestedPermissions, requiredAuthOption)
|
||||
userPermissionSpan.EndWithError(err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
return func(parent context.Context) context.Context {
|
||||
parent = context.WithValue(parent, dataKey, ctxData)
|
||||
parent = context.WithValue(parent, allPermissionsKey, allPermissions)
|
||||
parent = context.WithValue(parent, requestPermissionsKey, requestedPermissions)
|
||||
return parent
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkUserPermissions(req interface{}, userPerms []string, authOpt Option) error {
|
||||
|
@ -36,29 +36,36 @@ type Grant struct {
|
||||
Roles []string
|
||||
}
|
||||
|
||||
func VerifyTokenAndWriteCtxData(ctx context.Context, token, orgID string, t *TokenVerifier, method string) (_ context.Context, err error) {
|
||||
func VerifyTokenAndCreateCtxData(ctx context.Context, token, orgID string, t *TokenVerifier, method string) (_ CtxData, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if orgID != "" {
|
||||
err = t.ExistsOrg(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPermissionDenied(nil, "AUTH-Bs7Ds", "Organisation doesn't exist")
|
||||
return CtxData{}, errors.ThrowPermissionDenied(nil, "AUTH-Bs7Ds", "Organisation doesn't exist")
|
||||
}
|
||||
}
|
||||
|
||||
userID, clientID, agentID, prefLang, err := verifyAccessToken(ctx, token, t, method)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return CtxData{}, err
|
||||
}
|
||||
projectID, origins, err := t.ProjectIDAndOriginsByClientID(ctx, clientID)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPermissionDenied(err, "AUTH-GHpw2", "could not read projectid by clientid")
|
||||
return CtxData{}, errors.ThrowPermissionDenied(err, "AUTH-GHpw2", "could not read projectid by clientid")
|
||||
}
|
||||
if err := checkOrigin(ctx, origins); err != nil {
|
||||
return nil, err
|
||||
return CtxData{}, err
|
||||
}
|
||||
return context.WithValue(ctx, dataKey, CtxData{UserID: userID, OrgID: orgID, ProjectID: projectID, AgentID: agentID, PreferredLanguage: prefLang}), nil
|
||||
return CtxData{
|
||||
UserID: userID,
|
||||
OrgID: orgID,
|
||||
ProjectID: projectID,
|
||||
AgentID: agentID,
|
||||
PreferredLanguage: prefLang,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func SetCtxData(ctx context.Context, ctxData CtxData) context.Context {
|
||||
|
@ -7,29 +7,29 @@ import (
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func getUserMethodPermissions(ctx context.Context, t *TokenVerifier, requiredPerm string, authConfig Config) (_ context.Context, _ []string, err error) {
|
||||
func getUserMethodPermissions(ctx context.Context, t *TokenVerifier, requiredPerm string, authConfig Config, ctxData CtxData) (requestedPermissions, allPermissions []string, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
ctxData := GetCtxData(ctx)
|
||||
if ctxData.IsZero() {
|
||||
return nil, nil, errors.ThrowUnauthenticated(nil, "AUTH-rKLWEH", "context missing")
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, dataKey, ctxData)
|
||||
grant, err := t.ResolveGrant(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if grant == nil {
|
||||
return context.WithValue(ctx, requestPermissionsKey, []string{}), []string{}, nil
|
||||
return requestedPermissions, nil, nil
|
||||
}
|
||||
requestPermissions, allPermissions := mapGrantToPermissions(requiredPerm, grant, authConfig)
|
||||
ctx = context.WithValue(ctx, allPermissionsKey, allPermissions)
|
||||
return context.WithValue(ctx, requestPermissionsKey, requestPermissions), requestPermissions, nil
|
||||
requestedPermissions, allPermissions = mapGrantToPermissions(requiredPerm, grant, authConfig)
|
||||
return requestedPermissions, allPermissions, nil
|
||||
}
|
||||
|
||||
func mapGrantToPermissions(requiredPerm string, grant *Grant, authConfig Config) ([]string, []string) {
|
||||
requestPermissions := make([]string, 0)
|
||||
allPermissions := make([]string, 0)
|
||||
func mapGrantToPermissions(requiredPerm string, grant *Grant, authConfig Config) (requestPermissions, allPermissions []string) {
|
||||
requestPermissions = make([]string, 0)
|
||||
allPermissions = make([]string, 0)
|
||||
for _, role := range grant.Roles {
|
||||
requestPermissions, allPermissions = mapRoleToPerm(requiredPerm, role, authConfig, requestPermissions, allPermissions)
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func equalStringArray(a, b []string) bool {
|
||||
|
||||
func Test_GetUserMethodPermissions(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
ctxData CtxData
|
||||
verifier *TokenVerifier
|
||||
requiredPerm string
|
||||
authConfig Config
|
||||
@ -64,7 +64,7 @@ func Test_GetUserMethodPermissions(t *testing.T) {
|
||||
{
|
||||
name: "Empty Context",
|
||||
args: args{
|
||||
ctx: getTestCtx("", ""),
|
||||
ctxData: CtxData{},
|
||||
verifier: Start(&testVerifier{grant: &Grant{
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
}}),
|
||||
@ -89,7 +89,7 @@ func Test_GetUserMethodPermissions(t *testing.T) {
|
||||
{
|
||||
name: "No Grants",
|
||||
args: args{
|
||||
ctx: getTestCtx("", ""),
|
||||
ctxData: CtxData{},
|
||||
verifier: Start(&testVerifier{grant: &Grant{}}),
|
||||
requiredPerm: "project.read",
|
||||
authConfig: Config{
|
||||
@ -110,9 +110,9 @@ func Test_GetUserMethodPermissions(t *testing.T) {
|
||||
{
|
||||
name: "Get Permissions",
|
||||
args: args{
|
||||
ctx: getTestCtx("userID", "orgID"),
|
||||
ctxData: CtxData{UserID: "userID", OrgID: "orgID"},
|
||||
verifier: Start(&testVerifier{grant: &Grant{
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
}}),
|
||||
requiredPerm: "project.read",
|
||||
authConfig: Config{
|
||||
@ -133,7 +133,7 @@ func Test_GetUserMethodPermissions(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, perms, err := getUserMethodPermissions(tt.args.ctx, tt.args.verifier, tt.args.requiredPerm, tt.args.authConfig)
|
||||
_, perms, err := getUserMethodPermissions(context.Background(), tt.args.verifier, tt.args.requiredPerm, tt.args.authConfig, tt.args.ctxData)
|
||||
|
||||
if tt.wantErr && err == nil {
|
||||
t.Errorf("got wrong result, should get err: actual: %v ", err)
|
||||
|
@ -28,8 +28,8 @@ func failedEventsFromModel(failedEvents []*view_model.FailedEvent) []*admin.Fail
|
||||
func viewFromModel(view *view_model.View) *admin.View {
|
||||
eventTimestamp, err := ptypes.TimestampProto(view.EventTimestamp)
|
||||
logging.Log("GRPC-KSo03").OnError(err).Debug("unable to parse timestamp")
|
||||
lastSpool, err := ptypes.TimestampProto(view.EventTimestamp)
|
||||
logging.Log("GRPC-KSo03").OnError(err).Debug("unable to parse timestamp")
|
||||
lastSpool, err := ptypes.TimestampProto(view.LastSuccessfulSpoolerRun)
|
||||
logging.Log("GRPC-0oP87").OnError(err).Debug("unable to parse timestamp")
|
||||
|
||||
return &admin.View{
|
||||
Database: view.Database,
|
||||
|
@ -2,6 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
|
||||
"github.com/caos/zitadel/pkg/grpc/auth"
|
||||
@ -162,6 +163,9 @@ func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Emp
|
||||
|
||||
func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
|
||||
u2f, err := s.repo.AddMyMFAU2F(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return verifyWebAuthNFromModel(u2f), err
|
||||
}
|
||||
|
||||
@ -175,8 +179,19 @@ func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) (
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *Server) GetMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNTokens, err error) {
|
||||
tokens, err := s.repo.GetMyPasswordless(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return webAuthNTokensFromModel(tokens), err
|
||||
}
|
||||
|
||||
func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
|
||||
u2f, err := s.repo.AddMyPasswordless(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return verifyWebAuthNFromModel(u2f), err
|
||||
}
|
||||
|
||||
|
@ -436,3 +436,19 @@ func verifyWebAuthNFromModel(u2f *usr_model.WebAuthNToken) *auth.WebAuthNRespons
|
||||
State: mfaStateFromModel(u2f.State),
|
||||
}
|
||||
}
|
||||
|
||||
func webAuthNTokensFromModel(tokens []*usr_model.WebAuthNToken) *auth.WebAuthNTokens {
|
||||
result := make([]*auth.WebAuthNToken, len(tokens))
|
||||
for i, token := range tokens {
|
||||
result[i] = webAuthNTokenFromModel(token)
|
||||
}
|
||||
return &auth.WebAuthNTokens{Tokens: result}
|
||||
}
|
||||
|
||||
func webAuthNTokenFromModel(token *usr_model.WebAuthNToken) *auth.WebAuthNToken {
|
||||
return &auth.WebAuthNToken{
|
||||
Id: token.WebAuthNTokenID,
|
||||
Name: token.WebAuthNTokenName,
|
||||
State: mfaStateFromModel(token.State),
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,11 @@ package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/pkg/grpc/management"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
)
|
||||
|
||||
func (s *Server) GetUserByID(ctx context.Context, id *management.UserID) (*management.UserView, error) {
|
||||
@ -226,6 +228,24 @@ 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) GetPasswordless(ctx context.Context, userID *management.UserID) (_ *management.WebAuthNTokens, err error) {
|
||||
tokens, err := s.user.GetPasswordless(ctx, userID.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return webAuthNTokensFromModel(tokens), err
|
||||
}
|
||||
|
||||
func (s *Server) RemovePasswordless(ctx context.Context, id *management.WebAuthNTokenID) (*empty.Empty, error) {
|
||||
err := s.user.RemovePasswordless(ctx, id.UserId, id.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)
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,3 +629,19 @@ func userChangesToMgtAPI(changes *usr_model.UserChanges) (_ []*management.Change
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func webAuthNTokensFromModel(tokens []*usr_model.WebAuthNToken) *management.WebAuthNTokens {
|
||||
result := make([]*management.WebAuthNToken, len(tokens))
|
||||
for i, token := range tokens {
|
||||
result[i] = webAuthNTokenFromModel(token)
|
||||
}
|
||||
return &management.WebAuthNTokens{Tokens: result}
|
||||
}
|
||||
|
||||
func webAuthNTokenFromModel(token *usr_model.WebAuthNToken) *management.WebAuthNToken {
|
||||
return &management.WebAuthNToken{
|
||||
Id: token.WebAuthNTokenID,
|
||||
Name: token.WebAuthNTokenName,
|
||||
State: mfaStateFromModel(token.State),
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,20 @@ func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
ctx, span := tracing.NewServerInterceptorSpan(ctx)
|
||||
authCtx, span := tracing.NewServerInterceptorSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
authToken := grpc_util.GetAuthorizationHeader(ctx)
|
||||
authToken := grpc_util.GetAuthorizationHeader(authCtx)
|
||||
if authToken == "" {
|
||||
return nil, status.Error(codes.Unauthenticated, "auth header missing")
|
||||
}
|
||||
|
||||
orgID := grpc_util.GetHeader(ctx, http.ZitadelOrgID)
|
||||
orgID := grpc_util.GetHeader(authCtx, http.ZitadelOrgID)
|
||||
|
||||
ctx, err = authz.CheckUserAuthorization(ctx, req, authToken, orgID, verifier, authConfig, authOpt, info.FullMethod)
|
||||
ctxSetter, err := authz.CheckUserAuthorization(authCtx, req, authToken, orgID, verifier, authConfig, authOpt, info.FullMethod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
span.End()
|
||||
return handler(ctx, req)
|
||||
return handler(ctxSetter(ctx), req)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
@ -667,24 +643,28 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user
|
||||
return &model.InitUserStep{PasswordSet: user.PasswordSet}
|
||||
}
|
||||
|
||||
if user.IsPasswordlessReady() {
|
||||
if !checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) {
|
||||
return &model.PasswordlessStep{}
|
||||
var step model.NextStep
|
||||
if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
|
||||
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) {
|
||||
@ -753,6 +733,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)
|
||||
@ -824,9 +819,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
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
}},
|
||||
@ -386,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,
|
||||
},
|
||||
@ -431,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,
|
||||
},
|
||||
@ -456,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{}},
|
||||
@ -470,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,
|
||||
},
|
||||
@ -534,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,
|
||||
},
|
||||
@ -566,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{
|
||||
@ -843,6 +917,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},
|
||||
|
@ -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 {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user