diff --git a/console/src/app/modules/idp-table/idp-table.component.ts b/console/src/app/modules/idp-table/idp-table.component.ts index 1bbb211b27..6a5c242ff7 100644 --- a/console/src/app/modules/idp-table/idp-table.component.ts +++ b/console/src/app/modules/idp-table/idp-table.component.ts @@ -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 { - 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) diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.ts b/console/src/app/modules/policies/login-policy/login-policy.component.ts index 1dd0c77311..b4e4557f00 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.component.ts @@ -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 { - const calls: Observable[] = []; + const calls: Promise[] = []; 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 { diff --git a/console/src/app/modules/providers/provider-next/provider-next.service.ts b/console/src/app/modules/providers/provider-next/provider-next.service.ts index d2c268353a..245571e699 100644 --- a/console/src/app/modules/providers/provider-next/provider-next.service.ts +++ b/console/src/app/modules/providers/provider-next/provider-next.service.ts @@ -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, diff --git a/console/src/app/services/activate-idp.service.ts b/console/src/app/services/activate-idp.service.ts deleted file mode 100644 index 8c49925db2..0000000000 --- a/console/src/app/services/activate-idp.service.ts +++ /dev/null @@ -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 { - 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 { - 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); - } -} diff --git a/console/src/app/services/login-policy.service.ts b/console/src/app/services/login-policy.service.ts new file mode 100644 index 0000000000..333d5170f0 --- /dev/null +++ b/console/src/app/services/login-policy.service.ts @@ -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 { + 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 { + 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; +}