fix(console): keep default idps active (#7663)

* fix(console): keep default idps active

* rename var

* rename var

* use Promise.all()

* lint

---------

Co-authored-by: Max Peintner <max@caos.ch>
This commit is contained in:
Elio Bischof 2024-04-11 10:29:25 +02:00 committed by GitHub
parent d229da6af7
commit 2de66dcf95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 137 additions and 157 deletions

View File

@ -36,7 +36,7 @@ import { ContextChangedWorkflowOverlays } from 'src/app/services/overlay/workflo
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
import { ActivateIdpService } from '../../services/activate-idp.service';
import { LoginPolicyService } from '../../services/login-policy.service';
import { first } from 'rxjs/operators';
@Component({
@ -72,7 +72,7 @@ export class IdpTableComponent implements OnInit, OnDestroy {
private toast: ToastService,
private dialog: MatDialog,
private router: Router,
private activateIdpSvc: ActivateIdpService,
private loginPolicySvc: LoginPolicyService,
) {
this.selection.changed.subscribe(() => {
this.changedSelection.emit(this.selection.selected);
@ -302,7 +302,7 @@ export class IdpTableComponent implements OnInit, OnDestroy {
}
public addIdp(idp: Provider.AsObject): Promise<any> {
return firstValueFrom(this.activateIdpSvc.activateIdp(this.service, idp.id, idp.owner, this.loginPolicy))
return firstValueFrom(this.loginPolicySvc.activateIdp(this.service, idp.id, idp.owner, this.loginPolicy))
.then(() => {
this.toast.showInfo('IDP.TOAST.ADDED', true);
setTimeout(() => {
@ -328,8 +328,8 @@ export class IdpTableComponent implements OnInit, OnDestroy {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
if (this.isDefault) {
this.activateIdpSvc
.addLoginPolicy(this.service as ManagementService, this.loginPolicy)
this.loginPolicySvc
.createCustomLoginPolicy(this.service as ManagementService, this.loginPolicy)
.then(() => {
this.loginPolicy.isDefault = false;
return (this.service as ManagementService)

View File

@ -26,6 +26,7 @@ import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { LoginMethodComponentType } from './factor-table/factor-table.component';
import { catchError, map, takeUntil } from 'rxjs/operators';
import { error } from 'console';
import { LoginPolicyService } from '../../../services/login-policy.service';
const minValueValidator = (minValue: number) => (control: AbstractControl) => {
const value = control.value;
@ -71,6 +72,7 @@ export class LoginPolicyComponent implements OnInit, OnDestroy {
private fb: UntypedFormBuilder,
private authService: GrpcAuthService,
private dialog: MatDialog,
private loginPolicySvc: LoginPolicyService,
) {}
ngOnDestroy(): void {
@ -172,45 +174,13 @@ export class LoginPolicyComponent implements OnInit, OnDestroy {
}
private async updateData(): Promise<any> {
const calls: Observable<any>[] = [];
const calls: Promise<any>[] = [];
if (this.loginData) {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
if (this.isDefault) {
const mgmtreq = new AddCustomLoginPolicyRequest();
mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
mgmtreq.setAllowRegister(this.loginData.allowRegister);
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
mgmtreq.setForceMfa(this.loginData.forceMfa);
mgmtreq.setForceMfaLocalOnly(this.loginData.forceMfaLocalOnly);
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
mgmtreq.setMultiFactorsList(this.loginData.multiFactorsList);
mgmtreq.setSecondFactorsList(this.loginData.secondFactorsList);
mgmtreq.setDisableLoginWithEmail(this.loginData.disableLoginWithEmail);
mgmtreq.setDisableLoginWithPhone(this.loginData.disableLoginWithPhone);
const pcl = new Duration().setSeconds((this.passwordCheckLifetime?.value ?? 0) * 60 * 60);
mgmtreq.setPasswordCheckLifetime(pcl);
const elcl = new Duration().setSeconds((this.externalLoginCheckLifetime?.value ?? 0) * 60 * 60);
mgmtreq.setExternalLoginCheckLifetime(elcl);
const misl = new Duration().setSeconds((this.mfaInitSkipLifetime?.value ?? 0) * 60 * 60);
mgmtreq.setMfaInitSkipLifetime(misl);
const sfcl = new Duration().setSeconds((this.secondFactorCheckLifetime?.value ?? 0) * 60 * 60);
mgmtreq.setSecondFactorCheckLifetime(sfcl);
const mficl = new Duration().setSeconds((this.multiFactorCheckLifetime?.value ?? 0) * 60 * 60);
mgmtreq.setMultiFactorCheckLifetime(mficl);
mgmtreq.setAllowDomainDiscovery(this.loginData.allowDomainDiscovery);
mgmtreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
calls.push(from((this.service as ManagementService).addCustomLoginPolicy(mgmtreq)));
calls.push(this.loginPolicySvc.createCustomLoginPolicy(this.service as ManagementService, this.loginData));
break;
} else {
const mgmtreq = new UpdateCustomLoginPolicyRequest();
@ -243,7 +213,7 @@ export class LoginPolicyComponent implements OnInit, OnDestroy {
mgmtreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
calls.push(from((this.service as ManagementService).updateCustomLoginPolicy(mgmtreq)));
calls.push((this.service as ManagementService).updateCustomLoginPolicy(mgmtreq));
break;
}
case PolicyComponentServiceType.ADMIN:
@ -276,21 +246,19 @@ export class LoginPolicyComponent implements OnInit, OnDestroy {
adminreq.setIgnoreUnknownUsernames(this.loginData.ignoreUnknownUsernames);
adminreq.setDefaultRedirectUri(this.loginData.defaultRedirectUri);
calls.push(from((this.service as AdminService).setRestrictions(!this.allowOrgRegistration)));
calls.push(from((this.service as AdminService).updateLoginPolicy(adminreq)));
calls.push((this.service as AdminService).setRestrictions(!this.allowOrgRegistration));
calls.push((this.service as AdminService).updateLoginPolicy(adminreq));
break;
}
} else {
calls.push(from(Promise.reject()));
calls.push(Promise.reject());
}
return firstValueFrom(
forkJoin(calls).pipe(
catchError((error, caught) => {
// We just ignore the policy not changed error!
return (error as { message: string }).message.includes('INSTANCE-5M9vdd') ? of(true) : caught;
}),
),
);
return Promise.all(calls).catch((err) => {
if (err?.message?.includes('INSTANCE-5M9vdd')) {
return true;
}
throw err;
});
}
public savePolicy(): void {

View File

@ -8,7 +8,7 @@ import { AdminService } from '../../../services/admin.service';
import { IDPOwnerType } from '../../../proto/generated/zitadel/idp_pb';
import { ToastService } from '../../../services/toast.service';
import { Data, ParamMap } from '@angular/router';
import { ActivateIdpService } from '../../../services/activate-idp.service';
import { LoginPolicyService } from '../../../services/login-policy.service';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
@Injectable({
@ -18,7 +18,7 @@ export class ProviderNextService {
constructor(
private env: EnvironmentService,
private toast: ToastService,
private addIdpSvc: ActivateIdpService,
private loginPolicySvc: LoginPolicyService,
private injector: Injector,
) {}
@ -117,7 +117,7 @@ export class ProviderNextService {
if (!id) {
throw new Error('No ID');
}
return this.addIdpSvc.activateIdp(
return this.loginPolicySvc.activateIdp(
service,
id,
service instanceof AdminService ? IDPOwnerType.IDP_OWNER_TYPE_SYSTEM : IDPOwnerType.IDP_OWNER_TYPE_ORG,

View File

@ -1,103 +0,0 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, combineLatest, from, map, Observable, of, switchMap } from 'rxjs';
import { ManagementService } from './mgmt.service';
import { AddCustomLoginPolicyRequest, AddCustomLoginPolicyResponse } from '../proto/generated/zitadel/management_pb';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { IDPOwnerType, Provider } from '../proto/generated/zitadel/idp_pb';
import { AdminService } from './admin.service';
import { catchError } from 'rxjs/operators';
import { LoginPolicy } from '../proto/generated/zitadel/policy_pb';
@Injectable({
providedIn: 'root',
})
export class ActivateIdpService {
constructor() {}
public activateIdp(
service: AdminService | ManagementService,
id: string,
owner?: IDPOwnerType,
policy?: LoginPolicy.AsObject,
): Observable<any> {
const isAdmin = service instanceof AdminService;
if (isAdmin) {
service.addIDPToLoginPolicy(id);
}
if (!isAdmin && owner !== IDPOwnerType.IDP_OWNER_TYPE_SYSTEM && owner !== IDPOwnerType.IDP_OWNER_TYPE_ORG) {
throw new Error('Must specify owner for management service');
}
return from(service.addIDPToLoginPolicy(id!, owner!)).pipe(
catchError((error) => {
if (isAdmin || error.code != 5) {
throw error;
}
// No org policy was found, so we create a new one
return from(policy ? of(policy) : from(service.getLoginPolicy()).pipe(map((policy) => policy.policy))).pipe(
switchMap((policy) => {
if (!policy?.isDefault) {
// There is already an org policy
throw error;
}
return from(this.addLoginPolicy(service, policy)).pipe(
switchMap(() => from(service.addIDPToLoginPolicy(id!, owner!))),
);
}),
);
}),
);
}
public addLoginPolicy(
service: ManagementService,
policy: LoginPolicy.AsObject,
): Promise<AddCustomLoginPolicyResponse.AsObject> {
const mgmtreq = new AddCustomLoginPolicyRequest();
mgmtreq.setAllowExternalIdp(policy.allowExternalIdp);
mgmtreq.setAllowRegister(policy.allowRegister);
mgmtreq.setAllowUsernamePassword(policy.allowUsernamePassword);
mgmtreq.setForceMfa(policy.forceMfa);
mgmtreq.setPasswordlessType(policy.passwordlessType);
mgmtreq.setHidePasswordReset(policy.hidePasswordReset);
mgmtreq.setMultiFactorsList(policy.multiFactorsList);
mgmtreq.setSecondFactorsList(policy.secondFactorsList);
const pcl = new Duration()
.setSeconds(policy.passwordCheckLifetime?.seconds ?? 0)
.setNanos(policy.passwordCheckLifetime?.nanos ?? 0);
mgmtreq.setPasswordCheckLifetime(pcl);
const elcl = new Duration()
.setSeconds(policy.externalLoginCheckLifetime?.seconds ?? 0)
.setNanos(policy.externalLoginCheckLifetime?.nanos ?? 0);
mgmtreq.setExternalLoginCheckLifetime(elcl);
const misl = new Duration()
.setSeconds(policy.mfaInitSkipLifetime?.seconds ?? 0)
.setNanos(policy.mfaInitSkipLifetime?.nanos ?? 0);
mgmtreq.setMfaInitSkipLifetime(misl);
const sfcl = new Duration()
.setSeconds(policy.secondFactorCheckLifetime?.seconds ?? 0)
.setNanos(policy.secondFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setSecondFactorCheckLifetime(sfcl);
const mficl = new Duration()
.setSeconds(policy.multiFactorCheckLifetime?.seconds ?? 0)
.setNanos(policy.multiFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setMultiFactorCheckLifetime(mficl);
mgmtreq.setAllowDomainDiscovery(policy.allowDomainDiscovery);
mgmtreq.setIgnoreUnknownUsernames(policy.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(policy.defaultRedirectUri);
mgmtreq.setDisableLoginWithEmail(policy.disableLoginWithEmail);
mgmtreq.setDisableLoginWithPhone(policy.disableLoginWithPhone);
mgmtreq.setForceMfaLocalOnly(policy.forceMfaLocalOnly);
return service.addCustomLoginPolicy(mgmtreq);
}
}

View File

@ -0,0 +1,115 @@
import { Injectable } from '@angular/core';
import { from, map, Observable, of, switchMap } from 'rxjs';
import { ManagementService } from './mgmt.service';
import { AddCustomLoginPolicyRequest, AddCustomLoginPolicyResponse } from '../proto/generated/zitadel/management_pb';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { IDPOwnerType } from '../proto/generated/zitadel/idp_pb';
import { AdminService } from './admin.service';
import { catchError } from 'rxjs/operators';
import { LoginPolicy } from '../proto/generated/zitadel/policy_pb';
@Injectable({
providedIn: 'root',
})
export class LoginPolicyService {
constructor() {}
public activateIdp(
service: AdminService | ManagementService,
id: string,
owner?: IDPOwnerType,
policy?: LoginPolicy.AsObject,
): Observable<any> {
const isAdmin = service instanceof AdminService;
if (isAdmin) {
service.addIDPToLoginPolicy(id);
}
if (!isAdmin && owner !== IDPOwnerType.IDP_OWNER_TYPE_SYSTEM && owner !== IDPOwnerType.IDP_OWNER_TYPE_ORG) {
throw new Error('Must specify owner for management service');
}
return from(service.addIDPToLoginPolicy(id!, owner!)).pipe(
catchError((error) => {
if (isAdmin || error.code != 5) {
throw error;
}
// No org policy was found, so we create a new one
return from(policy ? of(policy) : from(service.getLoginPolicy()).pipe(map((policy) => policy.policy))).pipe(
switchMap((policy) => {
if (!policy?.isDefault) {
// There is already an org policy
throw error;
}
return from(this.createCustomLoginPolicy(service, policy, id));
}),
);
}),
);
}
public createCustomLoginPolicy(
service: ManagementService,
fromDefaultPolicy: LoginPolicy.AsObject,
activateOrgIdp?: string,
): Promise<AddCustomLoginPolicyResponse.AsObject> {
const mgmtreq = new AddCustomLoginPolicyRequest();
mgmtreq.setAllowExternalIdp(fromDefaultPolicy.allowExternalIdp);
mgmtreq.setAllowRegister(fromDefaultPolicy.allowRegister);
mgmtreq.setAllowUsernamePassword(fromDefaultPolicy.allowUsernamePassword);
mgmtreq.setForceMfa(fromDefaultPolicy.forceMfa);
mgmtreq.setPasswordlessType(fromDefaultPolicy.passwordlessType);
mgmtreq.setHidePasswordReset(fromDefaultPolicy.hidePasswordReset);
mgmtreq.setMultiFactorsList(fromDefaultPolicy.multiFactorsList);
mgmtreq.setSecondFactorsList(fromDefaultPolicy.secondFactorsList);
const pcl = new Duration()
.setSeconds(fromDefaultPolicy.passwordCheckLifetime?.seconds ?? 0)
.setNanos(fromDefaultPolicy.passwordCheckLifetime?.nanos ?? 0);
mgmtreq.setPasswordCheckLifetime(pcl);
const elcl = new Duration()
.setSeconds(fromDefaultPolicy.externalLoginCheckLifetime?.seconds ?? 0)
.setNanos(fromDefaultPolicy.externalLoginCheckLifetime?.nanos ?? 0);
mgmtreq.setExternalLoginCheckLifetime(elcl);
const misl = new Duration()
.setSeconds(fromDefaultPolicy.mfaInitSkipLifetime?.seconds ?? 0)
.setNanos(fromDefaultPolicy.mfaInitSkipLifetime?.nanos ?? 0);
mgmtreq.setMfaInitSkipLifetime(misl);
const sfcl = new Duration()
.setSeconds(fromDefaultPolicy.secondFactorCheckLifetime?.seconds ?? 0)
.setNanos(fromDefaultPolicy.secondFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setSecondFactorCheckLifetime(sfcl);
const mficl = new Duration()
.setSeconds(fromDefaultPolicy.multiFactorCheckLifetime?.seconds ?? 0)
.setNanos(fromDefaultPolicy.multiFactorCheckLifetime?.nanos ?? 0);
mgmtreq.setMultiFactorCheckLifetime(mficl);
mgmtreq.setAllowDomainDiscovery(fromDefaultPolicy.allowDomainDiscovery);
mgmtreq.setIgnoreUnknownUsernames(fromDefaultPolicy.ignoreUnknownUsernames);
mgmtreq.setDefaultRedirectUri(fromDefaultPolicy.defaultRedirectUri);
mgmtreq.setDisableLoginWithEmail(fromDefaultPolicy.disableLoginWithEmail);
mgmtreq.setDisableLoginWithPhone(fromDefaultPolicy.disableLoginWithPhone);
mgmtreq.setForceMfaLocalOnly(fromDefaultPolicy.forceMfaLocalOnly);
mgmtreq.setIdpsList(
fromDefaultPolicy.idpsList.map((idp) => addIdpMessage(idp.idpId, IDPOwnerType.IDP_OWNER_TYPE_SYSTEM)),
);
if (activateOrgIdp) {
mgmtreq.addIdps(addIdpMessage(activateOrgIdp, IDPOwnerType.IDP_OWNER_TYPE_ORG));
}
return service.addCustomLoginPolicy(mgmtreq);
}
}
function addIdpMessage(id: string, owner: IDPOwnerType): AddCustomLoginPolicyRequest.IDP {
const addIdp = new AddCustomLoginPolicyRequest.IDP();
addIdp.setIdpId(id);
addIdp.setOwnertype(owner);
return addIdp;
}