mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-03 06:22:12 +00:00
feat: directly specify factors/idps on addCustomLoginPolicy and return on LoginPolicy responses (#3711)
* feat: directly specify factors on addCustomLoginPolicy and return on LoginPolicy responses * fix proto * update login policy * feat: directly specify idp on addCustomLoginPolicy and return on LoginPolicy responses * fix: tests Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-policy-mfa-list">
|
<div class="login-policy-mfa-list">
|
||||||
<div class="mfa" *ngFor="let mfa of mfas">
|
<div class="mfa" *ngFor="let mfa of list">
|
||||||
<span>
|
<span>
|
||||||
{{
|
{{
|
||||||
(componentType === LoginMethodComponentType.SecondFactor
|
(componentType === LoginMethodComponentType.SecondFactor
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@@ -34,14 +34,15 @@ export enum LoginMethodComponentType {
|
|||||||
templateUrl: './factor-table.component.html',
|
templateUrl: './factor-table.component.html',
|
||||||
styleUrls: ['./factor-table.component.scss'],
|
styleUrls: ['./factor-table.component.scss'],
|
||||||
})
|
})
|
||||||
export class FactorTableComponent implements OnInit {
|
export class FactorTableComponent {
|
||||||
public LoginMethodComponentType: any = LoginMethodComponentType;
|
public LoginMethodComponentType: any = LoginMethodComponentType;
|
||||||
@Input() componentType!: LoginMethodComponentType;
|
@Input() componentType!: LoginMethodComponentType;
|
||||||
@Input() public serviceType!: PolicyComponentServiceType;
|
@Input() public serviceType!: PolicyComponentServiceType;
|
||||||
@Input() service!: AdminService | ManagementService;
|
@Input() service!: AdminService | ManagementService;
|
||||||
@Input() disabled: boolean = false;
|
@Input() disabled: boolean = false;
|
||||||
|
@Input() list: Array<MultiFactorType | SecondFactorType> = [];
|
||||||
|
@Output() listChanged: EventEmitter<void> = new EventEmitter();
|
||||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||||
public mfas: Array<MultiFactorType | SecondFactorType> = [];
|
|
||||||
|
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
@@ -50,10 +51,6 @@ export class FactorTableComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) {}
|
constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) {}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
this.getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeMfa(type: MultiFactorType | SecondFactorType): void {
|
public removeMfa(type: MultiFactorType | SecondFactorType): void {
|
||||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
@@ -73,14 +70,14 @@ export class FactorTableComponent implements OnInit {
|
|||||||
req.setType(type as MultiFactorType);
|
req.setType(type as MultiFactorType);
|
||||||
(this.service as ManagementService).removeMultiFactorFromLoginPolicy(req).then(() => {
|
(this.service as ManagementService).removeMultiFactorFromLoginPolicy(req).then(() => {
|
||||||
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
this.refreshPageAfterTimout(2000);
|
this.listChanged.emit();
|
||||||
});
|
});
|
||||||
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
const req = new MgmtRemoveSecondFactorFromLoginPolicyRequest();
|
const req = new MgmtRemoveSecondFactorFromLoginPolicyRequest();
|
||||||
req.setType(type as SecondFactorType);
|
req.setType(type as SecondFactorType);
|
||||||
(this.service as ManagementService).removeSecondFactorFromLoginPolicy(req).then(() => {
|
(this.service as ManagementService).removeSecondFactorFromLoginPolicy(req).then(() => {
|
||||||
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
this.refreshPageAfterTimout(2000);
|
this.listChanged.emit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||||
@@ -89,14 +86,14 @@ export class FactorTableComponent implements OnInit {
|
|||||||
req.setType(type as MultiFactorType);
|
req.setType(type as MultiFactorType);
|
||||||
(this.service as AdminService).removeMultiFactorFromLoginPolicy(req).then(() => {
|
(this.service as AdminService).removeMultiFactorFromLoginPolicy(req).then(() => {
|
||||||
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
this.refreshPageAfterTimout(2000);
|
this.listChanged.emit();
|
||||||
});
|
});
|
||||||
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
||||||
const req = new AdminRemoveSecondFactorFromLoginPolicyRequest();
|
const req = new AdminRemoveSecondFactorFromLoginPolicyRequest();
|
||||||
req.setType(type as SecondFactorType);
|
req.setType(type as SecondFactorType);
|
||||||
(this.service as AdminService).removeSecondFactorFromLoginPolicy(req).then(() => {
|
(this.service as AdminService).removeSecondFactorFromLoginPolicy(req).then(() => {
|
||||||
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
this.toast.showInfo('MFA.TOAST.DELETED', true);
|
||||||
this.refreshPageAfterTimout(2000);
|
this.listChanged.emit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +121,8 @@ export class FactorTableComponent implements OnInit {
|
|||||||
(this.service as ManagementService)
|
(this.service as ManagementService)
|
||||||
.addMultiFactorToLoginPolicy(req)
|
.addMultiFactorToLoginPolicy(req)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.refreshPageAfterTimout(2000);
|
this.toast.showInfo('MFA.TOAST.ADDED', true);
|
||||||
|
this.listChanged.emit();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
@@ -135,7 +133,8 @@ export class FactorTableComponent implements OnInit {
|
|||||||
(this.service as ManagementService)
|
(this.service as ManagementService)
|
||||||
.addSecondFactorToLoginPolicy(req)
|
.addSecondFactorToLoginPolicy(req)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.refreshPageAfterTimout(2000);
|
this.toast.showInfo('MFA.TOAST.ADDED', true);
|
||||||
|
this.listChanged.emit();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
@@ -148,7 +147,8 @@ export class FactorTableComponent implements OnInit {
|
|||||||
(this.service as AdminService)
|
(this.service as AdminService)
|
||||||
.addMultiFactorToLoginPolicy(req)
|
.addMultiFactorToLoginPolicy(req)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.refreshPageAfterTimout(2000);
|
this.toast.showInfo('MFA.TOAST.ADDED', true);
|
||||||
|
this.listChanged.emit();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
@@ -159,7 +159,8 @@ export class FactorTableComponent implements OnInit {
|
|||||||
(this.service as AdminService)
|
(this.service as AdminService)
|
||||||
.addSecondFactorToLoginPolicy(req)
|
.addSecondFactorToLoginPolicy(req)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.refreshPageAfterTimout(2000);
|
this.toast.showInfo('MFA.TOAST.ADDED', true);
|
||||||
|
this.listChanged.emit();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
@@ -170,66 +171,6 @@ export class FactorTableComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getData(): Promise<void> {
|
|
||||||
this.loadingSubject.next(true);
|
|
||||||
|
|
||||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
|
||||||
if (this.componentType === LoginMethodComponentType.MultiFactor) {
|
|
||||||
(this.service as ManagementService)
|
|
||||||
.listLoginPolicyMultiFactors()
|
|
||||||
.then((resp) => {
|
|
||||||
this.mfas = resp.resultList;
|
|
||||||
this.loadingSubject.next(false);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
this.loadingSubject.next(false);
|
|
||||||
});
|
|
||||||
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
|
||||||
(this.service as ManagementService)
|
|
||||||
.listLoginPolicySecondFactors()
|
|
||||||
.then((resp) => {
|
|
||||||
this.mfas = resp.resultList;
|
|
||||||
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)
|
|
||||||
.listLoginPolicyMultiFactors()
|
|
||||||
.then((resp) => {
|
|
||||||
this.mfas = resp.resultList;
|
|
||||||
this.loadingSubject.next(false);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
this.loadingSubject.next(false);
|
|
||||||
});
|
|
||||||
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
|
|
||||||
(this.service as AdminService)
|
|
||||||
.listLoginPolicySecondFactors()
|
|
||||||
.then((resp) => {
|
|
||||||
this.mfas = resp.resultList;
|
|
||||||
this.loadingSubject.next(false);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
this.loadingSubject.next(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public refreshPageAfterTimout(to: number): void {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.getData();
|
|
||||||
}, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get availableSelection(): Array<MultiFactorType | SecondFactorType> {
|
public get availableSelection(): Array<MultiFactorType | SecondFactorType> {
|
||||||
const allTypes: MultiFactorType[] | SecondFactorType[] =
|
const allTypes: MultiFactorType[] | SecondFactorType[] =
|
||||||
this.componentType === LoginMethodComponentType.MultiFactor
|
this.componentType === LoginMethodComponentType.MultiFactor
|
||||||
@@ -238,7 +179,7 @@ export class FactorTableComponent implements OnInit {
|
|||||||
? [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP]
|
? [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const filtered = (allTypes as Array<MultiFactorType | SecondFactorType>).filter((type) => !this.mfas.includes(type));
|
const filtered = (allTypes as Array<MultiFactorType | SecondFactorType>).filter((type) => !this.list.includes(type));
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
[service]="service"
|
[service]="service"
|
||||||
[serviceType]="serviceType"
|
[serviceType]="serviceType"
|
||||||
[componentType]="LoginMethodComponentType.MultiFactor"
|
[componentType]="LoginMethodComponentType.MultiFactor"
|
||||||
|
[list]="loginData.multiFactorsList"
|
||||||
|
(listChanged)="fetchData()"
|
||||||
[disabled]="
|
[disabled]="
|
||||||
loginData?.passwordlessType === PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED ||
|
loginData?.passwordlessType === PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED ||
|
||||||
([
|
([
|
||||||
@@ -103,6 +105,8 @@
|
|||||||
[service]="service"
|
[service]="service"
|
||||||
[serviceType]="serviceType"
|
[serviceType]="serviceType"
|
||||||
[componentType]="LoginMethodComponentType.SecondFactor"
|
[componentType]="LoginMethodComponentType.SecondFactor"
|
||||||
|
[list]="loginData.secondFactorsList"
|
||||||
|
(listChanged)="fetchData()"
|
||||||
[disabled]="
|
[disabled]="
|
||||||
([
|
([
|
||||||
serviceType === PolicyComponentServiceType.ADMIN
|
serviceType === PolicyComponentServiceType.ADMIN
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class LoginPolicyComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchData(): void {
|
public fetchData(): void {
|
||||||
this.getData()
|
this.getData()
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.policy) {
|
if (resp.policy) {
|
||||||
@@ -147,6 +147,8 @@ export class LoginPolicyComponent implements OnInit {
|
|||||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||||
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||||
|
mgmtreq.setMultiFactorsList(this.loginData.multiFactorsList);
|
||||||
|
mgmtreq.setSecondFactorsList(this.loginData.secondFactorsList);
|
||||||
|
|
||||||
const pcl = new Duration().setSeconds((this.passwordCheckLifetime?.value ?? 240) * 60 * 60);
|
const pcl = new Duration().setSeconds((this.passwordCheckLifetime?.value ?? 240) * 60 * 60);
|
||||||
mgmtreq.setPasswordCheckLifetime(pcl);
|
mgmtreq.setPasswordCheckLifetime(pcl);
|
||||||
|
|||||||
@@ -3046,6 +3046,21 @@ This is an empty request
|
|||||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||||
| second_factor_check_lifetime | google.protobuf.Duration | - | |
|
| second_factor_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| multi_factor_check_lifetime | google.protobuf.Duration | - | |
|
| multi_factor_check_lifetime | google.protobuf.Duration | - | |
|
||||||
|
| second_factors | repeated zitadel.policy.v1.SecondFactorType | - | |
|
||||||
|
| multi_factors | repeated zitadel.policy.v1.MultiFactorType | - | |
|
||||||
|
| idps | repeated AddCustomLoginPolicyRequest.IDP | - | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### AddCustomLoginPolicyRequest.IDP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
| Field | Type | Description | Validation |
|
||||||
|
| ----- | ---- | ----------- | ----------- |
|
||||||
|
| idp_id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||||
|
| ownerType | zitadel.idp.v1.IDPOwnerType | - | enum.defined_only: true<br /> enum.not_in: [0]<br /> |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ title: zitadel/policy.proto
|
|||||||
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
| mfa_init_skip_lifetime | google.protobuf.Duration | - | |
|
||||||
| second_factor_check_lifetime | google.protobuf.Duration | - | |
|
| second_factor_check_lifetime | google.protobuf.Duration | - | |
|
||||||
| multi_factor_check_lifetime | google.protobuf.Duration | - | |
|
| multi_factor_check_lifetime | google.protobuf.Duration | - | |
|
||||||
|
| second_factors | repeated SecondFactorType | - | |
|
||||||
|
| multi_factors | repeated MultiFactorType | - | |
|
||||||
|
| idps | repeated zitadel.idp.v1.IDPLoginPolicyLink | - | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package management
|
package management
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
|
||||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||||
policy_grpc "github.com/zitadel/zitadel/internal/api/grpc/policy"
|
policy_grpc "github.com/zitadel/zitadel/internal/api/grpc/policy"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
@@ -23,8 +24,21 @@ func addLoginPolicyToDomain(p *mgmt_pb.AddCustomLoginPolicyRequest) *domain.Logi
|
|||||||
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
MFAInitSkipLifetime: p.MfaInitSkipLifetime.AsDuration(),
|
||||||
SecondFactorCheckLifetime: p.SecondFactorCheckLifetime.AsDuration(),
|
SecondFactorCheckLifetime: p.SecondFactorCheckLifetime.AsDuration(),
|
||||||
MultiFactorCheckLifetime: p.MultiFactorCheckLifetime.AsDuration(),
|
MultiFactorCheckLifetime: p.MultiFactorCheckLifetime.AsDuration(),
|
||||||
|
SecondFactors: policy_grpc.SecondFactorsTypesToDomain(p.SecondFactors),
|
||||||
|
MultiFactors: policy_grpc.MultiFactorsTypesToDomain(p.MultiFactors),
|
||||||
|
IDPProviders: addLoginPolicyIDPsToDomain(p.Idps),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func addLoginPolicyIDPsToDomain(idps []*mgmt_pb.AddCustomLoginPolicyRequest_IDP) []*domain.IDPProvider {
|
||||||
|
providers := make([]*domain.IDPProvider, len(idps))
|
||||||
|
for i, idp := range idps {
|
||||||
|
providers[i] = &domain.IDPProvider{
|
||||||
|
Type: idp_grpc.IDPProviderTypeFromPb(idp.OwnerType),
|
||||||
|
IDPConfigID: idp.IdpId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|
||||||
func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domain.LoginPolicy {
|
func updateLoginPolicyToDomain(p *mgmt_pb.UpdateCustomLoginPolicyRequest) *domain.LoginPolicy {
|
||||||
return &domain.LoginPolicy{
|
return &domain.LoginPolicy{
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ import (
|
|||||||
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func SecondFactorsTypesToDomain(secondFactorTypes []policy_pb.SecondFactorType) []domain.SecondFactorType {
|
||||||
|
types := make([]domain.SecondFactorType, len(secondFactorTypes))
|
||||||
|
for i, factorType := range secondFactorTypes {
|
||||||
|
types[i] = SecondFactorTypeToDomain(factorType)
|
||||||
|
}
|
||||||
|
return types
|
||||||
|
}
|
||||||
|
|
||||||
func SecondFactorTypeToDomain(secondFactorType policy_pb.SecondFactorType) domain.SecondFactorType {
|
func SecondFactorTypeToDomain(secondFactorType policy_pb.SecondFactorType) domain.SecondFactorType {
|
||||||
switch secondFactorType {
|
switch secondFactorType {
|
||||||
case policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP:
|
case policy_pb.SecondFactorType_SECOND_FACTOR_TYPE_OTP:
|
||||||
@@ -35,6 +43,23 @@ func ModelSecondFactorTypeToPb(secondFactorType domain.SecondFactorType) policy_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MultiFactorsTypesToDomain(multiFactorTypes []policy_pb.MultiFactorType) []domain.MultiFactorType {
|
||||||
|
types := make([]domain.MultiFactorType, len(multiFactorTypes))
|
||||||
|
for i, factorType := range multiFactorTypes {
|
||||||
|
types[i] = MultiFactorTypeToDomain(factorType)
|
||||||
|
}
|
||||||
|
return types
|
||||||
|
}
|
||||||
|
|
||||||
|
func MultiFactorTypeToDomain(multiFactorType policy_pb.MultiFactorType) domain.MultiFactorType {
|
||||||
|
switch multiFactorType {
|
||||||
|
case policy_pb.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION:
|
||||||
|
return domain.MultiFactorTypeU2FWithPIN
|
||||||
|
default:
|
||||||
|
return domain.MultiFactorTypeUnspecified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ModelMultiFactorTypesToPb(types []domain.MultiFactorType) []policy_pb.MultiFactorType {
|
func ModelMultiFactorTypesToPb(types []domain.MultiFactorType) []policy_pb.MultiFactorType {
|
||||||
t := make([]policy_pb.MultiFactorType, len(types))
|
t := make([]policy_pb.MultiFactorType, len(types))
|
||||||
for i, typ := range types {
|
for i, typ := range types {
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
|
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/object"
|
"github.com/zitadel/zitadel/pkg/grpc/object"
|
||||||
@@ -26,6 +27,9 @@ func ModelLoginPolicyToPb(policy *query.LoginPolicy) *policy_pb.LoginPolicy {
|
|||||||
MfaInitSkipLifetime: durationpb.New(policy.MFAInitSkipLifetime),
|
MfaInitSkipLifetime: durationpb.New(policy.MFAInitSkipLifetime),
|
||||||
SecondFactorCheckLifetime: durationpb.New(policy.SecondFactorCheckLifetime),
|
SecondFactorCheckLifetime: durationpb.New(policy.SecondFactorCheckLifetime),
|
||||||
MultiFactorCheckLifetime: durationpb.New(policy.MultiFactorCheckLifetime),
|
MultiFactorCheckLifetime: durationpb.New(policy.MultiFactorCheckLifetime),
|
||||||
|
SecondFactors: ModelSecondFactorTypesToPb(policy.SecondFactors),
|
||||||
|
MultiFactors: ModelMultiFactorTypesToPb(policy.MultiFactors),
|
||||||
|
Idps: idp_grpc.IDPLoginPolicyLinksToPb(policy.IDPLinks),
|
||||||
Details: &object.ObjectDetails{
|
Details: &object.ObjectDetails{
|
||||||
Sequence: policy.Sequence,
|
Sequence: policy.Sequence,
|
||||||
CreationDate: timestamppb.New(policy.CreationDate),
|
CreationDate: timestamppb.New(policy.CreationDate),
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package policy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
|
||||||
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
|
||||||
)
|
|
||||||
|
|
||||||
func MultiFactorTypeToDomain(multiFactorType policy_pb.MultiFactorType) domain.MultiFactorType {
|
|
||||||
switch multiFactorType {
|
|
||||||
case policy_pb.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION:
|
|
||||||
return domain.MultiFactorTypeU2FWithPIN
|
|
||||||
default:
|
|
||||||
return domain.MultiFactorTypeUnspecified
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,8 +29,7 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
|||||||
}
|
}
|
||||||
|
|
||||||
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
|
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
|
||||||
pushedEvents, err := c.eventstore.Push(
|
cmds := []eventstore.Command{
|
||||||
ctx,
|
|
||||||
org.NewLoginPolicyAddedEvent(
|
org.NewLoginPolicyAddedEvent(
|
||||||
ctx,
|
ctx,
|
||||||
orgAgg,
|
orgAgg,
|
||||||
@@ -46,7 +45,32 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
|||||||
policy.ExternalLoginCheckLifetime,
|
policy.ExternalLoginCheckLifetime,
|
||||||
policy.MFAInitSkipLifetime,
|
policy.MFAInitSkipLifetime,
|
||||||
policy.SecondFactorCheckLifetime,
|
policy.SecondFactorCheckLifetime,
|
||||||
policy.MultiFactorCheckLifetime))
|
policy.MultiFactorCheckLifetime),
|
||||||
|
}
|
||||||
|
for _, factor := range policy.SecondFactors {
|
||||||
|
if !factor.Valid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-SFeea", "Errors.Org.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
cmds = append(cmds, org.NewLoginPolicySecondFactorAddedEvent(ctx, orgAgg, factor))
|
||||||
|
}
|
||||||
|
for _, factor := range policy.MultiFactors {
|
||||||
|
if !factor.Valid() {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-WSfrg", "Errors.Org.LoginPolicy.MFA.Unspecified")
|
||||||
|
}
|
||||||
|
cmds = append(cmds, org.NewLoginPolicyMultiFactorAddedEvent(ctx, orgAgg, factor))
|
||||||
|
}
|
||||||
|
for _, provider := range policy.IDPProviders {
|
||||||
|
if provider.Type == domain.IdentityProviderTypeOrg {
|
||||||
|
_, err = c.getOrgIDPConfigByID(ctx, provider.IDPConfigID, resourceOwner)
|
||||||
|
} else {
|
||||||
|
_, err = c.getInstanceIDPConfigByID(ctx, provider.IDPConfigID)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowPreconditionFailed(err, "Org-FEd32", "Errors.IDPConfig.NotExisting")
|
||||||
|
}
|
||||||
|
cmds = append(cmds, org.NewIdentityProviderAddedEvent(ctx, orgAgg, provider.IDPConfigID, provider.Type))
|
||||||
|
}
|
||||||
|
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||||
"github.com/zitadel/zitadel/internal/repository/org"
|
"github.com/zitadel/zitadel/internal/repository/org"
|
||||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||||
"github.com/zitadel/zitadel/internal/repository/user"
|
"github.com/zitadel/zitadel/internal/repository/user"
|
||||||
@@ -183,6 +184,257 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "add policy with invalid factors, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LoginPolicy{
|
||||||
|
AllowRegister: true,
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
AllowExternalIDP: true,
|
||||||
|
ForceMFA: true,
|
||||||
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
|
SecondFactorCheckLifetime: time.Hour * 4,
|
||||||
|
MultiFactorCheckLifetime: time.Hour * 5,
|
||||||
|
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeUnspecified},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsErrorInvalidArgument,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy factors,ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1").Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
|
time.Hour*1,
|
||||||
|
time.Hour*2,
|
||||||
|
time.Hour*3,
|
||||||
|
time.Hour*4,
|
||||||
|
time.Hour*5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLoginPolicySecondFactorAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1").Aggregate,
|
||||||
|
domain.SecondFactorTypeOTP,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLoginPolicyMultiFactorAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1").Aggregate,
|
||||||
|
domain.MultiFactorTypeU2FWithPIN,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LoginPolicy{
|
||||||
|
AllowRegister: true,
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
AllowExternalIDP: true,
|
||||||
|
ForceMFA: true,
|
||||||
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
|
SecondFactorCheckLifetime: time.Hour * 4,
|
||||||
|
MultiFactorCheckLifetime: time.Hour * 5,
|
||||||
|
SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
|
||||||
|
MultiFactors: []domain.MultiFactorType{domain.MultiFactorTypeU2FWithPIN},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.LoginPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
AllowRegister: true,
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
AllowExternalIDP: true,
|
||||||
|
ForceMFA: true,
|
||||||
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
|
SecondFactorCheckLifetime: time.Hour * 4,
|
||||||
|
MultiFactorCheckLifetime: time.Hour * 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy with unknown idp, invalid argument error",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectFilter(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LoginPolicy{
|
||||||
|
AllowRegister: true,
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
AllowExternalIDP: true,
|
||||||
|
ForceMFA: true,
|
||||||
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
|
SecondFactorCheckLifetime: time.Hour * 4,
|
||||||
|
MultiFactorCheckLifetime: time.Hour * 5,
|
||||||
|
IDPProviders: []*domain.IDPProvider{
|
||||||
|
{
|
||||||
|
Type: domain.IdentityProviderTypeSystem,
|
||||||
|
IDPConfigID: "invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
err: caos_errs.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add policy idp, ok",
|
||||||
|
fields: fields{
|
||||||
|
eventstore: eventstoreExpect(
|
||||||
|
t,
|
||||||
|
expectFilter(),
|
||||||
|
expectFilter(
|
||||||
|
eventFromEventPusher(
|
||||||
|
instance.NewIDPConfigAddedEvent(context.Background(),
|
||||||
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||||
|
"config1",
|
||||||
|
"name1",
|
||||||
|
domain.IDPConfigTypeOIDC,
|
||||||
|
domain.IDPConfigStylingTypeGoogle,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expectPush(
|
||||||
|
[]*repository.Event{
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1").Aggregate,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
domain.PasswordlessTypeAllowed,
|
||||||
|
"https://example.com/redirect",
|
||||||
|
time.Hour*1,
|
||||||
|
time.Hour*2,
|
||||||
|
time.Hour*3,
|
||||||
|
time.Hour*4,
|
||||||
|
time.Hour*5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventFromEventPusher(
|
||||||
|
org.NewIdentityProviderAddedEvent(context.Background(),
|
||||||
|
&org.NewAggregate("org1").Aggregate,
|
||||||
|
"config1",
|
||||||
|
domain.IdentityProviderTypeSystem,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
orgID: "org1",
|
||||||
|
policy: &domain.LoginPolicy{
|
||||||
|
AllowRegister: true,
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
AllowExternalIDP: true,
|
||||||
|
ForceMFA: true,
|
||||||
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
|
SecondFactorCheckLifetime: time.Hour * 4,
|
||||||
|
MultiFactorCheckLifetime: time.Hour * 5,
|
||||||
|
IDPProviders: []*domain.IDPProvider{
|
||||||
|
{
|
||||||
|
Type: domain.IdentityProviderTypeSystem,
|
||||||
|
IDPConfigID: "config1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
want: &domain.LoginPolicy{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: "org1",
|
||||||
|
ResourceOwner: "org1",
|
||||||
|
},
|
||||||
|
AllowRegister: true,
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
AllowExternalIDP: true,
|
||||||
|
ForceMFA: true,
|
||||||
|
HidePasswordReset: true,
|
||||||
|
IgnoreUnknownUsernames: true,
|
||||||
|
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||||
|
DefaultRedirectURI: "https://example.com/redirect",
|
||||||
|
PasswordCheckLifetime: time.Hour * 1,
|
||||||
|
ExternalLoginCheckLifetime: time.Hour * 2,
|
||||||
|
MFAInitSkipLifetime: time.Hour * 3,
|
||||||
|
SecondFactorCheckLifetime: time.Hour * 4,
|
||||||
|
MultiFactorCheckLifetime: time.Hour * 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ type LoginPolicy struct {
|
|||||||
MFAInitSkipLifetime time.Duration
|
MFAInitSkipLifetime time.Duration
|
||||||
SecondFactorCheckLifetime time.Duration
|
SecondFactorCheckLifetime time.Duration
|
||||||
MultiFactorCheckLifetime time.Duration
|
MultiFactorCheckLifetime time.Duration
|
||||||
|
IDPLinks []*IDPLoginPolicyLink
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecondFactors struct {
|
type SecondFactors struct {
|
||||||
@@ -160,8 +161,11 @@ func (q *Queries) LoginPolicyByID(ctx context.Context, orgID string) (*LoginPoli
|
|||||||
return nil, errors.ThrowInternal(err, "QUERY-scVHo", "Errors.Query.SQLStatement")
|
return nil, errors.ThrowInternal(err, "QUERY-scVHo", "Errors.Query.SQLStatement")
|
||||||
}
|
}
|
||||||
|
|
||||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||||
return scan(row)
|
if err != nil {
|
||||||
|
return nil, errors.ThrowInternal(err, "QUERY-SWgr3", "Errors.Internal")
|
||||||
|
}
|
||||||
|
return scan(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) DefaultLoginPolicy(ctx context.Context) (*LoginPolicy, error) {
|
func (q *Queries) DefaultLoginPolicy(ctx context.Context) (*LoginPolicy, error) {
|
||||||
@@ -174,8 +178,11 @@ func (q *Queries) DefaultLoginPolicy(ctx context.Context) (*LoginPolicy, error)
|
|||||||
return nil, errors.ThrowInternal(err, "QUERY-t4TBK", "Errors.Query.SQLStatement")
|
return nil, errors.ThrowInternal(err, "QUERY-t4TBK", "Errors.Query.SQLStatement")
|
||||||
}
|
}
|
||||||
|
|
||||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||||
return scan(row)
|
if err != nil {
|
||||||
|
return nil, errors.ThrowInternal(err, "QUERY-SArt2", "Errors.Internal")
|
||||||
|
}
|
||||||
|
return scan(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (*SecondFactors, error) {
|
func (q *Queries) SecondFactorsByOrg(ctx context.Context, orgID string) (*SecondFactors, error) {
|
||||||
@@ -278,7 +285,7 @@ func (q *Queries) DefaultMultiFactors(ctx context.Context) (*MultiFactors, error
|
|||||||
return factors, err
|
return factors, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy, error)) {
|
func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Rows) (*LoginPolicy, error)) {
|
||||||
return sq.Select(
|
return sq.Select(
|
||||||
LoginPolicyColumnOrgID.identifier(),
|
LoginPolicyColumnOrgID.identifier(),
|
||||||
LoginPolicyColumnCreationDate.identifier(),
|
LoginPolicyColumnCreationDate.identifier(),
|
||||||
@@ -300,13 +307,26 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
|||||||
LoginPolicyColumnMFAInitSkipLifetime.identifier(),
|
LoginPolicyColumnMFAInitSkipLifetime.identifier(),
|
||||||
LoginPolicyColumnSecondFactorCheckLifetime.identifier(),
|
LoginPolicyColumnSecondFactorCheckLifetime.identifier(),
|
||||||
LoginPolicyColumnMultiFacotrCheckLifetime.identifier(),
|
LoginPolicyColumnMultiFacotrCheckLifetime.identifier(),
|
||||||
).From(loginPolicyTable.identifier()).PlaceholderFormat(sq.Dollar),
|
IDPLoginPolicyLinkIDPIDCol.identifier(),
|
||||||
func(row *sql.Row) (*LoginPolicy, error) {
|
IDPNameCol.identifier(),
|
||||||
|
IDPTypeCol.identifier(),
|
||||||
|
).From(loginPolicyTable.identifier()).
|
||||||
|
LeftJoin(join(IDPLoginPolicyLinkIDPIDCol, LoginPolicyColumnOrgID)).
|
||||||
|
LeftJoin(join(IDPIDCol, IDPLoginPolicyLinkIDPIDCol)).
|
||||||
|
PlaceholderFormat(sq.Dollar),
|
||||||
|
func(rows *sql.Rows) (*LoginPolicy, error) {
|
||||||
p := new(LoginPolicy)
|
p := new(LoginPolicy)
|
||||||
secondFactors := pq.Int32Array{}
|
secondFactors := pq.Int32Array{}
|
||||||
multiFactors := pq.Int32Array{}
|
multiFactors := pq.Int32Array{}
|
||||||
defaultRedirectURI := sql.NullString{}
|
defaultRedirectURI := sql.NullString{}
|
||||||
err := row.Scan(
|
links := make([]*IDPLoginPolicyLink, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
var (
|
||||||
|
idpID = sql.NullString{}
|
||||||
|
idpName = sql.NullString{}
|
||||||
|
idpType = sql.NullInt16{}
|
||||||
|
)
|
||||||
|
err := rows.Scan(
|
||||||
&p.OrgID,
|
&p.OrgID,
|
||||||
&p.CreationDate,
|
&p.CreationDate,
|
||||||
&p.ChangeDate,
|
&p.ChangeDate,
|
||||||
@@ -327,13 +347,30 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
|||||||
&p.MFAInitSkipLifetime,
|
&p.MFAInitSkipLifetime,
|
||||||
&p.SecondFactorCheckLifetime,
|
&p.SecondFactorCheckLifetime,
|
||||||
&p.MultiFactorCheckLifetime,
|
&p.MultiFactorCheckLifetime,
|
||||||
|
&idpID,
|
||||||
|
&idpName,
|
||||||
|
&idpType,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errs.Is(err, sql.ErrNoRows) {
|
|
||||||
return nil, errors.ThrowNotFound(err, "QUERY-QsUBJ", "Errors.LoginPolicy.NotFound")
|
|
||||||
}
|
|
||||||
return nil, errors.ThrowInternal(err, "QUERY-YcC53", "Errors.Internal")
|
return nil, errors.ThrowInternal(err, "QUERY-YcC53", "Errors.Internal")
|
||||||
}
|
}
|
||||||
|
var link IDPLoginPolicyLink
|
||||||
|
if idpID.Valid {
|
||||||
|
link = IDPLoginPolicyLink{IDPID: idpID.String}
|
||||||
|
|
||||||
|
link.IDPName = idpName.String
|
||||||
|
//IDPType 0 is oidc so we have to set unspecified manually
|
||||||
|
if idpType.Valid {
|
||||||
|
link.IDPType = domain.IDPConfigType(idpType.Int16)
|
||||||
|
} else {
|
||||||
|
link.IDPType = domain.IDPConfigTypeUnspecified
|
||||||
|
}
|
||||||
|
links = append(links, &link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.OrgID == "" {
|
||||||
|
return nil, errors.ThrowNotFound(nil, "QUERY-QsUBJ", "Errors.LoginPolicy.NotFound")
|
||||||
|
}
|
||||||
p.DefaultRedirectURI = defaultRedirectURI.String
|
p.DefaultRedirectURI = defaultRedirectURI.String
|
||||||
p.MultiFactors = make([]domain.MultiFactorType, len(multiFactors))
|
p.MultiFactors = make([]domain.MultiFactorType, len(multiFactors))
|
||||||
for i, mfa := range multiFactors {
|
for i, mfa := range multiFactors {
|
||||||
@@ -343,6 +380,7 @@ func prepareLoginPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LoginPolicy,
|
|||||||
for i, mfa := range secondFactors {
|
for i, mfa := range secondFactors {
|
||||||
p.SecondFactors[i] = domain.SecondFactorType(mfa)
|
p.SecondFactors[i] = domain.SecondFactorType(mfa)
|
||||||
}
|
}
|
||||||
|
p.IDPLinks = links
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,15 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
` projections.login_policies.external_login_check_lifetime,`+
|
` projections.login_policies.external_login_check_lifetime,`+
|
||||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||||
` projections.login_policies.second_factor_check_lifetime,`+
|
` projections.login_policies.second_factor_check_lifetime,`+
|
||||||
` projections.login_policies.multi_factor_check_lifetime`+
|
` projections.login_policies.multi_factor_check_lifetime,`+
|
||||||
` FROM projections.login_policies`),
|
` projections.idp_login_policy_links.idp_id,`+
|
||||||
|
` projections.idps.name,`+
|
||||||
|
` projections.idps.type`+
|
||||||
|
` FROM projections.login_policies`+
|
||||||
|
` LEFT JOIN projections.idp_login_policy_links ON `+
|
||||||
|
` projections.login_policies.aggregate_id = projections.idp_login_policy_links.idp_id`+
|
||||||
|
` LEFT JOIN projections.idps ON`+
|
||||||
|
` projections.idp_login_policy_links.idp_id = projections.idps.id`),
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
@@ -88,8 +95,15 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
` projections.login_policies.external_login_check_lifetime,`+
|
` projections.login_policies.external_login_check_lifetime,`+
|
||||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||||
` projections.login_policies.second_factor_check_lifetime,`+
|
` projections.login_policies.second_factor_check_lifetime,`+
|
||||||
` projections.login_policies.multi_factor_check_lifetime`+
|
` projections.login_policies.multi_factor_check_lifetime,`+
|
||||||
` FROM projections.login_policies`),
|
` projections.idp_login_policy_links.idp_id,`+
|
||||||
|
` projections.idps.name,`+
|
||||||
|
` projections.idps.type`+
|
||||||
|
` FROM projections.login_policies`+
|
||||||
|
` LEFT JOIN projections.idp_login_policy_links ON `+
|
||||||
|
` projections.login_policies.aggregate_id = projections.idp_login_policy_links.idp_id`+
|
||||||
|
` LEFT JOIN projections.idps ON`+
|
||||||
|
` projections.idp_login_policy_links.idp_id = projections.idps.id`),
|
||||||
[]string{
|
[]string{
|
||||||
"aggregate_id",
|
"aggregate_id",
|
||||||
"creation_date",
|
"creation_date",
|
||||||
@@ -111,6 +125,9 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
"mfa_init_skip_lifetime",
|
"mfa_init_skip_lifetime",
|
||||||
"second_factor_check_lifetime",
|
"second_factor_check_lifetime",
|
||||||
"multi_factor_check_lifetime",
|
"multi_factor_check_lifetime",
|
||||||
|
"idp_id",
|
||||||
|
"name",
|
||||||
|
"type",
|
||||||
},
|
},
|
||||||
[]driver.Value{
|
[]driver.Value{
|
||||||
"ro",
|
"ro",
|
||||||
@@ -133,6 +150,9 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
time.Hour * 2,
|
time.Hour * 2,
|
||||||
time.Hour * 2,
|
time.Hour * 2,
|
||||||
time.Hour * 2,
|
time.Hour * 2,
|
||||||
|
"config1",
|
||||||
|
"IDP",
|
||||||
|
domain.IDPConfigTypeJWT,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -157,6 +177,13 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
MFAInitSkipLifetime: time.Hour * 2,
|
MFAInitSkipLifetime: time.Hour * 2,
|
||||||
SecondFactorCheckLifetime: time.Hour * 2,
|
SecondFactorCheckLifetime: time.Hour * 2,
|
||||||
MultiFactorCheckLifetime: time.Hour * 2,
|
MultiFactorCheckLifetime: time.Hour * 2,
|
||||||
|
IDPLinks: []*IDPLoginPolicyLink{
|
||||||
|
{
|
||||||
|
IDPID: "config1",
|
||||||
|
IDPName: "IDP",
|
||||||
|
IDPType: domain.IDPConfigTypeJWT,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -183,8 +210,15 @@ func Test_LoginPolicyPrepares(t *testing.T) {
|
|||||||
` projections.login_policies.external_login_check_lifetime,`+
|
` projections.login_policies.external_login_check_lifetime,`+
|
||||||
` projections.login_policies.mfa_init_skip_lifetime,`+
|
` projections.login_policies.mfa_init_skip_lifetime,`+
|
||||||
` projections.login_policies.second_factor_check_lifetime,`+
|
` projections.login_policies.second_factor_check_lifetime,`+
|
||||||
` projections.login_policies.multi_factor_check_lifetime`+
|
` projections.login_policies.multi_factor_check_lifetime,`+
|
||||||
` FROM projections.login_policies`),
|
` projections.idp_login_policy_links.idp_id,`+
|
||||||
|
` projections.idps.name,`+
|
||||||
|
` projections.idps.type`+
|
||||||
|
` FROM projections.login_policies`+
|
||||||
|
` LEFT JOIN projections.idp_login_policy_links ON `+
|
||||||
|
` projections.login_policies.aggregate_id = projections.idp_login_policy_links.idp_id`+
|
||||||
|
` LEFT JOIN projections.idps ON`+
|
||||||
|
` projections.idp_login_policy_links.idp_id = projections.idps.id`),
|
||||||
sql.ErrConnDone,
|
sql.ErrConnDone,
|
||||||
),
|
),
|
||||||
err: func(err error) (error, bool) {
|
err: func(err error) (error, bool) {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func NewAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
|
|||||||
crdb.NewColumn(AppAPIConfigColumnClientSecret, crdb.ColumnTypeJSONB, crdb.Nullable()),
|
crdb.NewColumn(AppAPIConfigColumnClientSecret, crdb.ColumnTypeJSONB, crdb.Nullable()),
|
||||||
crdb.NewColumn(AppAPIConfigColumnAuthMethod, crdb.ColumnTypeEnum),
|
crdb.NewColumn(AppAPIConfigColumnAuthMethod, crdb.ColumnTypeEnum),
|
||||||
},
|
},
|
||||||
crdb.NewPrimaryKey(AppAPIConfigColumnAppID),
|
crdb.NewPrimaryKey(AppAPIConfigColumnAppID, AppAPIConfigColumnInstanceID),
|
||||||
appAPITableSuffix,
|
appAPITableSuffix,
|
||||||
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_api_ref_apps")),
|
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_api_ref_apps")),
|
||||||
crdb.WithIndex(crdb.NewIndex("client_id_idx", []string{AppAPIConfigColumnClientID})),
|
crdb.WithIndex(crdb.NewIndex("client_id_idx", []string{AppAPIConfigColumnClientID})),
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
|
|||||||
crdb.NewColumn(IDPStylingTypeCol, crdb.ColumnTypeEnum),
|
crdb.NewColumn(IDPStylingTypeCol, crdb.ColumnTypeEnum),
|
||||||
crdb.NewColumn(IDPOwnerTypeCol, crdb.ColumnTypeEnum),
|
crdb.NewColumn(IDPOwnerTypeCol, crdb.ColumnTypeEnum),
|
||||||
crdb.NewColumn(IDPAutoRegisterCol, crdb.ColumnTypeBool, crdb.Default(false)),
|
crdb.NewColumn(IDPAutoRegisterCol, crdb.ColumnTypeBool, crdb.Default(false)),
|
||||||
crdb.NewColumn(IDPTypeCol, crdb.ColumnTypeEnum),
|
crdb.NewColumn(IDPTypeCol, crdb.ColumnTypeEnum, crdb.Nullable()),
|
||||||
},
|
},
|
||||||
crdb.NewPrimaryKey(IDPIDCol, IDPInstanceIDCol),
|
crdb.NewPrimaryKey(IDPIDCol, IDPInstanceIDCol),
|
||||||
crdb.WithIndex(crdb.NewIndex("ro_idx", []string{IDPResourceOwnerCol})),
|
crdb.WithIndex(crdb.NewIndex("ro_idx", []string{IDPResourceOwnerCol})),
|
||||||
@@ -92,9 +92,9 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
|
|||||||
crdb.NewColumn(OIDCConfigDisplayNameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()),
|
crdb.NewColumn(OIDCConfigDisplayNameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()),
|
||||||
crdb.NewColumn(OIDCConfigUsernameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()),
|
crdb.NewColumn(OIDCConfigUsernameMappingCol, crdb.ColumnTypeEnum, crdb.Nullable()),
|
||||||
crdb.NewColumn(OIDCConfigAuthorizationEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
|
crdb.NewColumn(OIDCConfigAuthorizationEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||||
crdb.NewColumn(OIDCConfigTokenEndpointCol, crdb.ColumnTypeEnum, crdb.Nullable()),
|
crdb.NewColumn(OIDCConfigTokenEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||||
},
|
},
|
||||||
crdb.NewPrimaryKey(OIDCConfigIDPIDCol),
|
crdb.NewPrimaryKey(OIDCConfigIDPIDCol, OIDCConfigInstanceIDCol),
|
||||||
IDPOIDCSuffix,
|
IDPOIDCSuffix,
|
||||||
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_oidc_ref_idp")),
|
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_oidc_ref_idp")),
|
||||||
),
|
),
|
||||||
@@ -106,7 +106,7 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *
|
|||||||
crdb.NewColumn(JWTConfigHeaderNameCol, crdb.ColumnTypeText, crdb.Nullable()),
|
crdb.NewColumn(JWTConfigHeaderNameCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||||
crdb.NewColumn(JWTConfigEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
|
crdb.NewColumn(JWTConfigEndpointCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||||
},
|
},
|
||||||
crdb.NewPrimaryKey(JWTConfigIDPIDCol),
|
crdb.NewPrimaryKey(JWTConfigIDPIDCol, JWTConfigInstanceIDCol),
|
||||||
IDPJWTSuffix,
|
IDPJWTSuffix,
|
||||||
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_jwt_ref_idp")),
|
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_jwt_ref_idp")),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func NewSMSConfigProjection(ctx context.Context, config crdb.StatementHandlerCon
|
|||||||
crdb.NewColumn(SMSTwilioConfigColumnSenderNumber, crdb.ColumnTypeText),
|
crdb.NewColumn(SMSTwilioConfigColumnSenderNumber, crdb.ColumnTypeText),
|
||||||
crdb.NewColumn(SMSTwilioConfigColumnToken, crdb.ColumnTypeJSONB),
|
crdb.NewColumn(SMSTwilioConfigColumnToken, crdb.ColumnTypeJSONB),
|
||||||
},
|
},
|
||||||
crdb.NewPrimaryKey(SMSTwilioConfigColumnSMSID),
|
crdb.NewPrimaryKey(SMSTwilioConfigColumnSMSID, SMSTwilioColumnInstanceID),
|
||||||
smsTwilioTableSuffix,
|
smsTwilioTableSuffix,
|
||||||
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_twilio_ref_sms")),
|
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_twilio_ref_sms")),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4337,6 +4337,11 @@ message GetDefaultLoginPolicyResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message AddCustomLoginPolicyRequest {
|
message AddCustomLoginPolicyRequest {
|
||||||
|
message IDP {
|
||||||
|
string idp_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||||
|
zitadel.idp.v1.IDPOwnerType ownerType = 2 [(validate.rules).enum = {defined_only: true, not_in: [0]}];
|
||||||
|
}
|
||||||
|
|
||||||
bool allow_username_password = 1;
|
bool allow_username_password = 1;
|
||||||
bool allow_register = 2;
|
bool allow_register = 2;
|
||||||
bool allow_external_idp = 3;
|
bool allow_external_idp = 3;
|
||||||
@@ -4358,6 +4363,9 @@ message AddCustomLoginPolicyRequest {
|
|||||||
google.protobuf.Duration mfa_init_skip_lifetime = 11;
|
google.protobuf.Duration mfa_init_skip_lifetime = 11;
|
||||||
google.protobuf.Duration second_factor_check_lifetime = 12;
|
google.protobuf.Duration second_factor_check_lifetime = 12;
|
||||||
google.protobuf.Duration multi_factor_check_lifetime = 13;
|
google.protobuf.Duration multi_factor_check_lifetime = 13;
|
||||||
|
repeated zitadel.policy.v1.SecondFactorType second_factors = 14;
|
||||||
|
repeated zitadel.policy.v1.MultiFactorType multi_factors = 15;
|
||||||
|
repeated IDP idps = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddCustomLoginPolicyResponse {
|
message AddCustomLoginPolicyResponse {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
import "zitadel/object.proto";
|
import "zitadel/object.proto";
|
||||||
|
import "zitadel/idp.proto";
|
||||||
import "google/protobuf/duration.proto";
|
import "google/protobuf/duration.proto";
|
||||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||||
|
|
||||||
@@ -170,7 +171,9 @@ message LoginPolicy {
|
|||||||
google.protobuf.Duration mfa_init_skip_lifetime = 13;
|
google.protobuf.Duration mfa_init_skip_lifetime = 13;
|
||||||
google.protobuf.Duration second_factor_check_lifetime = 14;
|
google.protobuf.Duration second_factor_check_lifetime = 14;
|
||||||
google.protobuf.Duration multi_factor_check_lifetime = 15;
|
google.protobuf.Duration multi_factor_check_lifetime = 15;
|
||||||
|
repeated SecondFactorType second_factors = 16;
|
||||||
|
repeated MultiFactorType multi_factors = 17;
|
||||||
|
repeated zitadel.idp.v1.IDPLoginPolicyLink idps = 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SecondFactorType {
|
enum SecondFactorType {
|
||||||
|
|||||||
Reference in New Issue
Block a user