mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-11 00:25:12 +00:00
feat(console): guide users when configuring IDPs (#7572)
* feat(console): show external idp remote config * reuse copy-row * finish google and saml sp * finish apps * add next steps modal * rollout * activate * fix saml urls * fix saml provider * complete providers * translate * update google docs * update entra id oidc docs * update entra id saml docs * update github docs * update gitlab docs * update apple docs * update okta oidc docs * update okta saml docs * update keycloak docs * update mocksaml * cleanup * lint * lint * fix overriden classes * encapsulate styles * fix icon classes --------- Co-authored-by: peintnermax <[email protected]>
This commit is contained in:
co-authored by
peintnermax
parent
84644214d7
commit
d26391a642
@@ -5,7 +5,7 @@ import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { BehaviorSubject, firstValueFrom, Observable, Subject } from 'rxjs';
|
||||
import {
|
||||
ListProvidersRequest as AdminListProvidersRequest,
|
||||
ListProvidersResponse as AdminListProvidersResponse,
|
||||
@@ -36,6 +36,8 @@ 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 { first } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-idp-table',
|
||||
@@ -70,6 +72,7 @@ export class IdpTableComponent implements OnInit, OnDestroy {
|
||||
private toast: ToastService,
|
||||
private dialog: MatDialog,
|
||||
private router: Router,
|
||||
private activateIdpSvc: ActivateIdpService,
|
||||
) {
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
@@ -298,93 +301,15 @@ export class IdpTableComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private addLoginPolicy(): Promise<AddCustomLoginPolicyResponse.AsObject> {
|
||||
const mgmtreq = new AddCustomLoginPolicyRequest();
|
||||
mgmtreq.setAllowExternalIdp(this.loginPolicy.allowExternalIdp);
|
||||
mgmtreq.setAllowRegister(this.loginPolicy.allowRegister);
|
||||
mgmtreq.setAllowUsernamePassword(this.loginPolicy.allowUsernamePassword);
|
||||
mgmtreq.setForceMfa(this.loginPolicy.forceMfa);
|
||||
mgmtreq.setPasswordlessType(this.loginPolicy.passwordlessType);
|
||||
mgmtreq.setHidePasswordReset(this.loginPolicy.hidePasswordReset);
|
||||
mgmtreq.setMultiFactorsList(this.loginPolicy.multiFactorsList);
|
||||
mgmtreq.setSecondFactorsList(this.loginPolicy.secondFactorsList);
|
||||
|
||||
const pcl = new Duration()
|
||||
.setSeconds(this.loginPolicy.passwordCheckLifetime?.seconds ?? 0)
|
||||
.setNanos(this.loginPolicy.passwordCheckLifetime?.nanos ?? 0);
|
||||
mgmtreq.setPasswordCheckLifetime(pcl);
|
||||
|
||||
const elcl = new Duration()
|
||||
.setSeconds(this.loginPolicy.externalLoginCheckLifetime?.seconds ?? 0)
|
||||
.setNanos(this.loginPolicy.externalLoginCheckLifetime?.nanos ?? 0);
|
||||
mgmtreq.setExternalLoginCheckLifetime(elcl);
|
||||
|
||||
const misl = new Duration()
|
||||
.setSeconds(this.loginPolicy.mfaInitSkipLifetime?.seconds ?? 0)
|
||||
.setNanos(this.loginPolicy.mfaInitSkipLifetime?.nanos ?? 0);
|
||||
mgmtreq.setMfaInitSkipLifetime(misl);
|
||||
|
||||
const sfcl = new Duration()
|
||||
.setSeconds(this.loginPolicy.secondFactorCheckLifetime?.seconds ?? 0)
|
||||
.setNanos(this.loginPolicy.secondFactorCheckLifetime?.nanos ?? 0);
|
||||
mgmtreq.setSecondFactorCheckLifetime(sfcl);
|
||||
|
||||
const mficl = new Duration()
|
||||
.setSeconds(this.loginPolicy.multiFactorCheckLifetime?.seconds ?? 0)
|
||||
.setNanos(this.loginPolicy.multiFactorCheckLifetime?.nanos ?? 0);
|
||||
mgmtreq.setMultiFactorCheckLifetime(mficl);
|
||||
|
||||
mgmtreq.setAllowDomainDiscovery(this.loginPolicy.allowDomainDiscovery);
|
||||
mgmtreq.setIgnoreUnknownUsernames(this.loginPolicy.ignoreUnknownUsernames);
|
||||
mgmtreq.setDefaultRedirectUri(this.loginPolicy.defaultRedirectUri);
|
||||
|
||||
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
|
||||
}
|
||||
|
||||
public addIdp(idp: Provider.AsObject): Promise<any> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if (this.isDefault) {
|
||||
return this.addLoginPolicy()
|
||||
.then(() => {
|
||||
this.loginPolicy.isDefault = false;
|
||||
return (this.service as ManagementService).addIDPToLoginPolicy(idp.id, idp.owner).then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.ADDED', true);
|
||||
|
||||
setTimeout(() => {
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
return (this.service as ManagementService)
|
||||
.addIDPToLoginPolicy(idp.id, idp.owner)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.ADDED', true);
|
||||
setTimeout(() => {
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService)
|
||||
.addIDPToLoginPolicy(idp.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.ADDED', true);
|
||||
setTimeout(() => {
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
return firstValueFrom(this.activateIdpSvc.activateIdp(this.service, idp.id, idp.owner, this.loginPolicy))
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.ADDED', true);
|
||||
setTimeout(() => {
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch(this.toast.showError);
|
||||
}
|
||||
|
||||
public removeIdp(idp: Provider.AsObject): void {
|
||||
@@ -403,7 +328,8 @@ export class IdpTableComponent implements OnInit, OnDestroy {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if (this.isDefault) {
|
||||
this.addLoginPolicy()
|
||||
this.activateIdpSvc
|
||||
.addLoginPolicy(this.service as ManagementService, this.loginPolicy)
|
||||
.then(() => {
|
||||
this.loginPolicy.isDefault = false;
|
||||
return (this.service as ManagementService)
|
||||
|
||||
@@ -7,9 +7,29 @@
|
||||
<img class="idp-logo apple dark" src="./assets/images/idp/apple-dark.svg" alt="apple" />
|
||||
<img class="idp-logo apple light" src="./assets/images/idp/apple.svg" alt="apple" />
|
||||
<h1>{{ 'IDP.CREATE.APPLE.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'Apple' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'Apple' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/apple#apple-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.APPLE.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -131,8 +151,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, take } from 'rxjs';
|
||||
import {
|
||||
AddAppleProviderRequest as AdminAddAppleProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
const MAX_ALLOWED_SIZE = 5 * 1024;
|
||||
|
||||
@@ -34,8 +35,11 @@ const MAX_ALLOWED_SIZE = 5 * 1024;
|
||||
export class ProviderAppleComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
@@ -47,6 +51,25 @@ export class ProviderAppleComponent {
|
||||
public provider?: Provider.AsObject;
|
||||
public updatePrivateKey: boolean = false;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/apple#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -54,6 +77,7 @@ export class ProviderAppleComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl('', []),
|
||||
@@ -115,6 +139,10 @@ export class ProviderAppleComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -138,7 +166,7 @@ export class ProviderAppleComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateAppleProvider() : this.addAppleProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateAppleProvider() : this.addAppleProvider();
|
||||
}
|
||||
|
||||
public addAppleProvider(): void {
|
||||
@@ -158,11 +186,9 @@ export class ProviderAppleComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addAppleProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -171,10 +197,10 @@ export class ProviderAppleComponent {
|
||||
}
|
||||
|
||||
public updateAppleProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
const req = new MgmtUpdateAppleProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setTeamId(this.teamId?.value);
|
||||
@@ -201,7 +227,7 @@ export class ProviderAppleComponent {
|
||||
});
|
||||
} else if (PolicyComponentServiceType.ADMIN) {
|
||||
const req = new AdminUpdateAppleProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setTeamId(this.teamId?.value);
|
||||
|
||||
+23
-2
@@ -6,9 +6,29 @@
|
||||
<div class="title-row">
|
||||
<img class="idp-logo" src="./assets/images/idp/ms.svg" alt="microsoft" />
|
||||
<h1>{{ 'IDP.CREATE.AZUREAD.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'Microsoft' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'Microsoft' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/azure-ad-oidc#entra-id-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.AZUREAD.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -104,8 +124,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+36
-10
@@ -4,13 +4,13 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddAzureADProviderRequest as AdminAddAzureADProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
UpdateAzureADProviderRequest as AdminUpdateAzureADProviderRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { AzureADTenant, AzureADTenantType, Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb';
|
||||
import { AzureADTenant, AzureADTenantType, IDPOwnerType, Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb';
|
||||
import {
|
||||
AddAzureADProviderRequest as MgmtAddAzureADProviderRequest,
|
||||
GetProviderByIDRequest as MgmtGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-azure-ad',
|
||||
@@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
|
||||
export class ProviderAzureADComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
@@ -53,6 +57,25 @@ export class ProviderAzureADComponent {
|
||||
AzureADTenantType.AZURE_AD_TENANT_TYPE_CONSUMERS,
|
||||
];
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/azure-ad-oidc#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -60,6 +83,7 @@ export class ProviderAzureADComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl('', []),
|
||||
@@ -122,6 +146,10 @@ export class ProviderAzureADComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -166,7 +194,7 @@ export class ProviderAzureADComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateAzureADProvider() : this.addAzureADProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateAzureADProvider() : this.addAzureADProvider();
|
||||
}
|
||||
|
||||
public addAzureADProvider(): void {
|
||||
@@ -194,11 +222,9 @@ export class ProviderAzureADComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addAzureADProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -207,13 +233,13 @@ export class ProviderAzureADComponent {
|
||||
}
|
||||
|
||||
public updateAzureADProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateAzureADProviderRequest()
|
||||
: new AdminUpdateAzureADProviderRequest();
|
||||
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setEmailVerified(this.emailVerified?.value);
|
||||
|
||||
+23
-3
@@ -6,11 +6,30 @@
|
||||
<div class="title-row">
|
||||
<img class="idp-logo dark" src="./assets/images/idp/github-dark.svg" alt="github" />
|
||||
<img class="idp-logo light" src="./assets/images/idp/github.svg" alt="github" />
|
||||
|
||||
<h1>{{ 'IDP.CREATE.GITHUBES.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitHub' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitHub' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/github#github-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.GITHUBES.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="submitForm()">
|
||||
@@ -103,8 +122,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+35
-9
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddGitHubEnterpriseServerProviderRequest as AdminAddGitHubEnterpriseServerProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-github-es',
|
||||
@@ -33,9 +34,12 @@ export class ProviderGithubESComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
public updateClientSecret: boolean = false;
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
public form!: UntypedFormGroup;
|
||||
@@ -44,6 +48,25 @@ export class ProviderGithubESComponent {
|
||||
|
||||
public provider?: Provider.AsObject;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/github#optional-add-zitadel-action-to-autofill-userdata`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/github#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -51,6 +74,7 @@ export class ProviderGithubESComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new UntypedFormGroup({
|
||||
name: new UntypedFormControl('', [requiredValidator]),
|
||||
@@ -113,6 +137,10 @@ export class ProviderGithubESComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
this.loading = true;
|
||||
const req =
|
||||
@@ -137,7 +165,7 @@ export class ProviderGithubESComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
|
||||
}
|
||||
|
||||
public addGenericOAuthProvider(): void {
|
||||
@@ -158,11 +186,9 @@ export class ProviderGithubESComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addGitHubEnterpriseServerProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -171,12 +197,12 @@ export class ProviderGithubESComponent {
|
||||
}
|
||||
|
||||
public updateGenericOAuthProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateGitHubEnterpriseServerProviderRequest()
|
||||
: new AdminUpdateGitHubEnterpriseServerProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setAuthorizationEndpoint(this.authorizationEndpoint?.value);
|
||||
req.setTokenEndpoint(this.tokenEndpoint?.value);
|
||||
|
||||
@@ -7,9 +7,29 @@
|
||||
<img class="idp-logo dark" src="./assets/images/idp/github-dark.svg" alt="github" />
|
||||
<img class="idp-logo light" src="./assets/images/idp/github.svg" alt="github" />
|
||||
<h1>{{ 'IDP.CREATE.GITHUB.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitHub' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitHub' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/github#github-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.GITHUB.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -90,8 +110,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddGitHubProviderRequest as AdminAddGithubProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-github',
|
||||
@@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
|
||||
export class ProviderGithubComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
@@ -45,6 +49,25 @@ export class ProviderGithubComponent {
|
||||
public provider?: Provider.AsObject;
|
||||
public updateClientSecret: boolean = false;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/github#optional-add-zitadel-action-to-autofill-userdata`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/github#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -52,6 +75,7 @@ export class ProviderGithubComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl('', []),
|
||||
@@ -111,6 +135,10 @@ export class ProviderGithubComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -134,7 +162,7 @@ export class ProviderGithubComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateGithubProvider() : this.addGithubProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateGithubProvider() : this.addGithubProvider();
|
||||
}
|
||||
|
||||
public addGithubProvider(): void {
|
||||
@@ -152,11 +180,9 @@ export class ProviderGithubComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addGitHubProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -165,12 +191,12 @@ export class ProviderGithubComponent {
|
||||
}
|
||||
|
||||
public updateGithubProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateGithubProviderRequest()
|
||||
: new AdminUpdateGithubProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
|
||||
+23
-2
@@ -6,9 +6,29 @@
|
||||
<div class="title-row">
|
||||
<img class="idp-logo" src="./assets/images/idp/gitlab.svg" alt="gitlab" />
|
||||
<h1>{{ 'IDP.CREATE.GITLABSELFHOSTED.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitLab' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitLab' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#gitlab-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.GITLABSELFHOSTED.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -93,8 +113,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+35
-9
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddGitLabSelfHostedProviderRequest as AdminAddGitLabSelfHostedProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-gitlab-self-hosted',
|
||||
@@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
|
||||
export class ProviderGitlabSelfHostedComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
@@ -45,6 +49,25 @@ export class ProviderGitlabSelfHostedComponent {
|
||||
public provider?: Provider.AsObject;
|
||||
public updateClientSecret: boolean = false;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#optional-add-zitadel-action-to-autofill-userdata`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -52,6 +75,7 @@ export class ProviderGitlabSelfHostedComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl('', [requiredValidator]),
|
||||
@@ -112,6 +136,10 @@ export class ProviderGitlabSelfHostedComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -135,7 +163,7 @@ export class ProviderGitlabSelfHostedComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateGitlabSelfHostedProvider() : this.addGitlabSelfHostedProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateGitlabSelfHostedProvider() : this.addGitlabSelfHostedProvider();
|
||||
}
|
||||
|
||||
public addGitlabSelfHostedProvider(): void {
|
||||
@@ -154,11 +182,9 @@ export class ProviderGitlabSelfHostedComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addGitLabSelfHostedProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -167,12 +193,12 @@ export class ProviderGitlabSelfHostedComponent {
|
||||
}
|
||||
|
||||
public updateGitlabSelfHostedProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateGitLabSelfHostedProviderRequest()
|
||||
: new AdminUpdateGitLabSelfHostedProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setIssuer(this.issuer?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
|
||||
@@ -6,9 +6,29 @@
|
||||
<div class="title-row">
|
||||
<img class="idp-logo" src="./assets/images/idp/gitlab.svg" alt="gitlab" />
|
||||
<h1>{{ 'IDP.CREATE.GITLAB.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'GitLab' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'GitLab' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#gitlab-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.GITLAB.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -89,8 +109,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddGitLabProviderRequest as AdminAddGitLabProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-gitlab',
|
||||
@@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
|
||||
export class ProviderGitlabComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
@@ -45,6 +49,25 @@ export class ProviderGitlabComponent {
|
||||
public provider?: Provider.AsObject;
|
||||
public updateClientSecret: boolean = false;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#optional-add-zitadel-action-to-autofill-userdata`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/gitlab#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -52,6 +75,7 @@ export class ProviderGitlabComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl('', []),
|
||||
@@ -111,6 +135,10 @@ export class ProviderGitlabComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -134,7 +162,7 @@ export class ProviderGitlabComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateGitlabProvider() : this.addGitlabProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateGitlabProvider() : this.addGitlabProvider();
|
||||
}
|
||||
|
||||
public addGitlabProvider(): void {
|
||||
@@ -152,11 +180,9 @@ export class ProviderGitlabComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addGitLabProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -165,12 +191,12 @@ export class ProviderGitlabComponent {
|
||||
}
|
||||
|
||||
public updateGitlabProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateGitLabProviderRequest()
|
||||
: new AdminUpdateGitLabProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
|
||||
@@ -6,9 +6,29 @@
|
||||
<div class="title-row">
|
||||
<img class="idp-logo" src="./assets/images/idp/google.png" alt="google" />
|
||||
<h1>{{ 'IDP.CREATE.GOOGLE.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'Google' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'Google' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/google#google-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.GOOGLE.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -88,8 +108,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddGoogleProviderRequest as AdminAddGoogleProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-google',
|
||||
@@ -32,8 +33,11 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type
|
||||
export class ProviderGoogleComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
@@ -45,6 +49,25 @@ export class ProviderGoogleComponent {
|
||||
public provider?: Provider.AsObject;
|
||||
public updateClientSecret: boolean = false;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/google#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -52,6 +75,7 @@ export class ProviderGoogleComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl('', []),
|
||||
@@ -59,7 +83,6 @@ export class ProviderGoogleComponent {
|
||||
clientSecret: new FormControl('', [requiredValidator]),
|
||||
scopesList: new FormControl(['openid', 'profile', 'email'], []),
|
||||
});
|
||||
|
||||
this.authService
|
||||
.isAllowed(
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -102,7 +125,6 @@ export class ProviderGoogleComponent {
|
||||
this.breadcrumbService.setBreadcrumb([iamBread]);
|
||||
break;
|
||||
}
|
||||
|
||||
this.id = this.route.snapshot.paramMap.get('id');
|
||||
if (this.id) {
|
||||
this.clientSecret?.setValidators([]);
|
||||
@@ -111,6 +133,10 @@ export class ProviderGoogleComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -134,7 +160,7 @@ export class ProviderGoogleComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateGoogleProvider() : this.addGoogleProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateGoogleProvider() : this.addGoogleProvider();
|
||||
}
|
||||
|
||||
public addGoogleProvider(): void {
|
||||
@@ -152,11 +178,9 @@ export class ProviderGoogleComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addGoogleProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -165,10 +189,10 @@ export class ProviderGoogleComponent {
|
||||
}
|
||||
|
||||
public updateGoogleProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
const req = new MgmtUpdateGoogleProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
@@ -193,7 +217,7 @@ export class ProviderGoogleComponent {
|
||||
});
|
||||
} else if (PolicyComponentServiceType.ADMIN) {
|
||||
const req = new AdminUpdateGoogleProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setScopesList(this.scopesList?.value);
|
||||
|
||||
@@ -6,9 +6,28 @@
|
||||
<div class="title-row">
|
||||
<mat-icon class="idp-logo" svgIcon="mdi_jwt" alt="jwt" />
|
||||
<h1>{{ 'IDP.CREATE.JWT.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.JWT.TITLE' | translate"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.JWT.DESCRIPTION' | translate"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/jwt-idp"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.JWT.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="submitForm()">
|
||||
@@ -57,8 +76,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -23,6 +23,8 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-jwt',
|
||||
@@ -32,8 +34,11 @@ export class ProviderJWTComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
|
||||
@@ -42,6 +47,24 @@ export class ProviderJWTComponent {
|
||||
|
||||
public provider?: Provider.AsObject;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/okta-oidc#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -49,6 +72,7 @@ export class ProviderJWTComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.route.data.pipe(take(1)).subscribe((data) => {
|
||||
this.serviceType = data['serviceType'];
|
||||
@@ -108,6 +132,10 @@ export class ProviderJWTComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -131,7 +159,7 @@ export class ProviderJWTComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateJWTProvider() : this.addJWTProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateJWTProvider() : this.addJWTProvider();
|
||||
}
|
||||
|
||||
public addJWTProvider(): void {
|
||||
@@ -149,11 +177,9 @@ export class ProviderJWTComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addJWTProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -162,12 +188,12 @@ export class ProviderJWTComponent {
|
||||
}
|
||||
|
||||
public updateJWTProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateJWTProviderRequest()
|
||||
: new AdminUpdateJWTProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setHeaderName(this.headerName?.value);
|
||||
req.setIssuer(this.issuer?.value);
|
||||
|
||||
@@ -6,9 +6,27 @@
|
||||
<div class="title-row">
|
||||
<i class="idp-icon las la-building"></i>
|
||||
<h1>{{ 'IDP.CREATE.LDAP.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.LDAP.TITLE' | translate: { provider: 'Google' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.LDAP.DESCRIPTION' | translate: { provider: 'Google' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/ldap"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.LDAP.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -118,8 +136,9 @@
|
||||
[disabled]="!form.valid || !attributes.toObject().idAttribute || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddLDAPProviderRequest as AdminAddLDAPProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -23,6 +23,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { minArrayLengthValidator, requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-ldap',
|
||||
@@ -33,8 +34,11 @@ export class ProviderLDAPComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
public attributes: LDAPAttributes = new LDAPAttributes();
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
public form!: FormGroup;
|
||||
@@ -43,6 +47,20 @@ export class ProviderLDAPComponent {
|
||||
|
||||
public provider?: Provider.AsObject;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/google#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -50,6 +68,7 @@ export class ProviderLDAPComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl('', [requiredValidator]),
|
||||
@@ -116,6 +135,10 @@ export class ProviderLDAPComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.ADMIN
|
||||
@@ -179,11 +202,9 @@ export class ProviderLDAPComponent {
|
||||
this.loading = true;
|
||||
(this.service as ManagementService)
|
||||
.addLDAPProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<div class="cnsl-provider-next">
|
||||
<cnsl-card
|
||||
[title]="'DESCRIPTIONS.SETTINGS.IDPS.NEXT' | translate"
|
||||
[expanded]="!!expanded"
|
||||
*ngIf="configureProvider || autofillLink || activateLink"
|
||||
>
|
||||
<cnsl-info-section *ngIf="activateLink">
|
||||
<div class="title-row">
|
||||
<div class="left">
|
||||
<h2 class="title">{{ 'DESCRIPTIONS.SETTINGS.IDPS.ACTIVATE.TITLE' | translate }}</h2>
|
||||
<div>
|
||||
<a
|
||||
mat-icon-button
|
||||
*ngIf="activateLink"
|
||||
card-actions
|
||||
mat-icon-button
|
||||
[href]="activateLink"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<mat-icon class="next-icon">info_outline</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button color="primary" mat-raised-button class="continue-button" (click)="activate.emit()">
|
||||
{{ 'ACTIONS.ACTIVATE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="cnsl-secondary-text description">{{ 'DESCRIPTIONS.SETTINGS.IDPS.ACTIVATE.DESCRIPTION' | translate }}</p>
|
||||
</cnsl-info-section>
|
||||
<div class="section" *ngIf="configureProvider">
|
||||
<div class="title-row">
|
||||
<div class="left">
|
||||
<h2 class="title">{{ configureTitle }}</h2>
|
||||
<a
|
||||
mat-icon-button
|
||||
*ngIf="configureLink"
|
||||
card-actions
|
||||
mat-icon-button
|
||||
[href]="configureLink"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<mat-icon class="next-icon">info_outline</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="cnsl-secondary-text description">{{ configureDescription }}</p>
|
||||
<cnsl-copy-row *ngFor="let row of copyUrls" [label]="row.label" [value]="row.url" labelMinWidth="200px">
|
||||
<a *ngIf="row.downloadable" class="download-button" mat-stroked-button [href]="row.url" download>{{
|
||||
'ACTIONS.DOWNLOAD' | translate
|
||||
}}</a>
|
||||
</cnsl-copy-row>
|
||||
</div>
|
||||
<div class="section" *ngIf="autofillLink">
|
||||
<div class="title-row">
|
||||
<div class="left">
|
||||
<h2 class="title">{{ 'DESCRIPTIONS.SETTINGS.IDPS.AUTOFILL.TITLE' | translate }}</h2>
|
||||
<a
|
||||
mat-icon-button
|
||||
*ngIf="autofillLink"
|
||||
card-actions
|
||||
mat-icon-button
|
||||
[href]="autofillLink"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<mat-icon class="next-icon">info_outline</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="cnsl-secondary-text description">{{ 'DESCRIPTIONS.SETTINGS.IDPS.AUTOFILL.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
.cnsl-provider-next {
|
||||
.section {
|
||||
background-color: #8795a110;
|
||||
border: 1px solid #8795a160;
|
||||
border-radius: 4px;
|
||||
padding: 0.25rem 1rem;
|
||||
margin: 0.25rem 0rem;
|
||||
.box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
.right {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.next-icon {
|
||||
font-size: 1.2rem;
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-block-start: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.download-button {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin provider-next-theme($theme) {
|
||||
$foreground: map-get($theme, foreground);
|
||||
$button-text-color: map-get($foreground, text);
|
||||
$button-disabled-text-color: map-get($foreground, disabled-button);
|
||||
.cnsl-provider-next {
|
||||
.section {
|
||||
color: $button-text-color;
|
||||
|
||||
&[disabled] {
|
||||
color: $button-disabled-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
export interface CopyUrl {
|
||||
label: string;
|
||||
url: string;
|
||||
downloadable?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-next',
|
||||
templateUrl: './provider-next.component.html',
|
||||
styleUrls: ['./provider-next.component.scss'],
|
||||
})
|
||||
export class ProviderNextComponent {
|
||||
@Input() copyUrls?: CopyUrl[] | null;
|
||||
@Input() autofillLink?: string | null;
|
||||
@Input() activateLink?: string | null;
|
||||
@Input() configureProvider?: boolean | null;
|
||||
@Input() configureTitle?: string;
|
||||
@Input() configureDescription?: string;
|
||||
@Input() configureLink?: string;
|
||||
@Input() expanded?: boolean;
|
||||
@Output() activate = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { Injectable, Injector, Type } from '@angular/core';
|
||||
import { BehaviorSubject, combineLatestWith, from, Observable, of, shareReplay, switchMap, take } from 'rxjs';
|
||||
import { filter, map, tap } from 'rxjs/operators';
|
||||
import { EnvironmentService } from '../../../services/environment.service';
|
||||
import { CopyUrl } from './provider-next.component';
|
||||
import { ManagementService } from '../../../services/mgmt.service';
|
||||
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 { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ProviderNextService {
|
||||
constructor(
|
||||
private env: EnvironmentService,
|
||||
private toast: ToastService,
|
||||
private addIdpSvc: ActivateIdpService,
|
||||
private injector: Injector,
|
||||
) {}
|
||||
|
||||
service(routeData: Observable<Data>): Observable<ManagementService | AdminService> {
|
||||
return routeData.pipe(
|
||||
map((data) => {
|
||||
switch (data['serviceType']) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.injector.get(ManagementService as Type<ManagementService>);
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.injector.get(AdminService as Type<AdminService>);
|
||||
default:
|
||||
throw new Error('Unknown Service Type');
|
||||
}
|
||||
}),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
id(paramMap: Observable<ParamMap>, justCreated$: Observable<string>): Observable<string | null> {
|
||||
return paramMap.pipe(
|
||||
// The ID observable should also emit when the IDP was just created
|
||||
combineLatestWith(justCreated$),
|
||||
map(([params, created]) => (created ? created : params.get('id'))),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
exists(id$: Observable<string | null>): Observable<boolean> {
|
||||
return id$.pipe(
|
||||
map((id) => !!id),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
autofillLink(id$: Observable<string | null>, link: string): Observable<string> {
|
||||
return id$.pipe(
|
||||
filter((id) => !!id),
|
||||
map(() => link),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
activateLink(
|
||||
id$: Observable<string | null>,
|
||||
justActivated$: Observable<boolean>,
|
||||
link: string,
|
||||
service$: Observable<ManagementService | AdminService>,
|
||||
): Observable<string> {
|
||||
return id$.pipe(
|
||||
combineLatestWith(justActivated$, service$),
|
||||
// Because we also want to emit when the IDP is not active, we return an empty string if the IDP does not exist
|
||||
switchMap(([id, activated, service]) =>
|
||||
(!id || activated
|
||||
? of(false)
|
||||
: from(service.getLoginPolicy()).pipe(map((policy) => !policy.policy?.idpsList.find((idp) => idp.idpId === id)))
|
||||
).pipe(map((show) => (!show ? '' : link))),
|
||||
),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
callbackUrls(): Observable<CopyUrl[]> {
|
||||
return this.env.env.pipe(
|
||||
map((env) => [
|
||||
{
|
||||
label: 'ZITADEL Callback URL',
|
||||
url: `${env.issuer}/ui/login/login/externalidp/callback`,
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
expandWhatNow(
|
||||
id$: Observable<string | null>,
|
||||
activateLink$: Observable<string>,
|
||||
justCreated$: Observable<string>,
|
||||
): Observable<boolean> {
|
||||
return id$.pipe(
|
||||
combineLatestWith(activateLink$, justCreated$),
|
||||
map(([id, activateLink, created]) => !id || activateLink || created),
|
||||
map((expand) => !!expand),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
activate(
|
||||
id$: Observable<string | null>,
|
||||
emitActivated$: BehaviorSubject<boolean>,
|
||||
service$: Observable<ManagementService | AdminService>,
|
||||
): void {
|
||||
id$
|
||||
.pipe(
|
||||
combineLatestWith(service$),
|
||||
take(1),
|
||||
switchMap(([id, service]) => {
|
||||
if (!id) {
|
||||
throw new Error('No ID');
|
||||
}
|
||||
return this.addIdpSvc.activateIdp(
|
||||
service,
|
||||
id,
|
||||
service instanceof AdminService ? IDPOwnerType.IDP_OWNER_TYPE_SYSTEM : IDPOwnerType.IDP_OWNER_TYPE_ORG,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.toast.showInfo('POLICY.LOGIN_POLICY.PROVIDER_ADDED', true);
|
||||
emitActivated$.next(true);
|
||||
},
|
||||
error: (error) => this.toast.showError(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,25 @@
|
||||
<div class="identity-provider-create-content">
|
||||
<div class="title-row">
|
||||
<img class="idp-logo" src="./assets/images/idp/oauth.svg" alt="oauth" />
|
||||
|
||||
<h1>{{ 'IDP.CREATE.OAUTH.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.OAUTH.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="submitForm()">
|
||||
@@ -107,8 +121,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddGenericOAuthProviderRequest as AdminAddGenericOAuthProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -24,6 +24,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-oauth',
|
||||
@@ -33,9 +34,12 @@ export class ProviderOAuthComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
public updateClientSecret: boolean = false;
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
public form!: UntypedFormGroup;
|
||||
@@ -44,6 +48,24 @@ export class ProviderOAuthComponent {
|
||||
|
||||
public provider?: Provider.AsObject;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/okta-oidc#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@@ -51,6 +73,7 @@ export class ProviderOAuthComponent {
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new UntypedFormGroup({
|
||||
name: new UntypedFormControl('', [requiredValidator]),
|
||||
@@ -114,6 +137,10 @@ export class ProviderOAuthComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
this.loading = true;
|
||||
const req =
|
||||
@@ -138,7 +165,7 @@ export class ProviderOAuthComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateGenericOAuthProvider() : this.addGenericOAuthProvider();
|
||||
}
|
||||
|
||||
public addGenericOAuthProvider(): void {
|
||||
@@ -160,11 +187,9 @@ export class ProviderOAuthComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addGenericOAuthProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -173,12 +198,12 @@ export class ProviderOAuthComponent {
|
||||
}
|
||||
|
||||
public updateGenericOAuthProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateGenericOAuthProviderRequest()
|
||||
: new AdminUpdateGenericOAuthProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setAuthorizationEndpoint(this.authorizationEndpoint?.value);
|
||||
req.setIdAttribute(this.idAttribute?.value);
|
||||
|
||||
@@ -6,9 +6,29 @@
|
||||
<div class="title-row">
|
||||
<mat-icon class="idp-logo" svgIcon="mdi_openid" alt="openid" />
|
||||
<h1>{{ 'IDP.CREATE.OIDC.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="(exists$ | async) === false"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.TITLE' | translate: { provider: 'OIDC' }"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.CALLBACK.DESCRIPTION' | translate: { provider: 'OIDC' }"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/google#google-configuration"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.OIDC.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<form [formGroup]="form" (ngSubmit)="submitForm()">
|
||||
@@ -95,8 +115,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component, Injector, Type } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, take } from 'rxjs';
|
||||
import {
|
||||
AddGenericOIDCProviderRequest as AdminAddGenericOIDCProviderRequest,
|
||||
GetProviderByIDRequest as AdminGetProviderByIDRequest,
|
||||
@@ -23,6 +23,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { requiredValidator } from '../../form-field/validators/validators';
|
||||
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-oidc',
|
||||
@@ -33,9 +34,12 @@ export class ProviderOIDCComponent {
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
public updateClientSecret: boolean = false;
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
public form!: UntypedFormGroup;
|
||||
@@ -44,12 +48,32 @@ export class ProviderOIDCComponent {
|
||||
|
||||
public provider?: Provider.AsObject;
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/additional-information`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/okta-oidc#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.nextSvc.callbackUrls();
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
private _location: Location,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this.form = new UntypedFormGroup({
|
||||
name: new UntypedFormControl('', [requiredValidator]),
|
||||
@@ -94,6 +118,10 @@ export class ProviderOIDCComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
this.loading = true;
|
||||
const req =
|
||||
@@ -118,7 +146,7 @@ export class ProviderOIDCComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateGenericOIDCProvider() : this.addGenericOIDCProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateGenericOIDCProvider() : this.addGenericOIDCProvider();
|
||||
}
|
||||
|
||||
public addGenericOIDCProvider(): void {
|
||||
@@ -138,11 +166,9 @@ export class ProviderOIDCComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addGenericOIDCProvider(req)
|
||||
.then((idp) => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -151,12 +177,12 @@ export class ProviderOIDCComponent {
|
||||
}
|
||||
|
||||
public updateGenericOIDCProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateGenericOIDCProviderRequest()
|
||||
: new AdminUpdateGenericOIDCProviderRequest();
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
req.setClientId(this.clientId?.value);
|
||||
req.setClientSecret(this.clientSecret?.value);
|
||||
|
||||
@@ -6,9 +6,29 @@
|
||||
<div class="title-row">
|
||||
<img class="idp-logo" src="./assets/images/idp/saml-icon.svg" alt="saml" />
|
||||
<h1>{{ 'IDP.CREATE.SAML.TITLE' | translate }}</h1>
|
||||
<ng-container *ngIf="exists$ | async">
|
||||
<div
|
||||
*ngIf="{ isNotActive: (activateLink$ | async) } as idp"
|
||||
class="cnsl-state-dot"
|
||||
[matTooltip]="(idp.isNotActive ? 'IDP.STATES.2' : 'IDP.STATES.1') | translate"
|
||||
[ngClass]="{ active: !idp.isNotActive, inactive: !!idp.isNotActive }"
|
||||
></div>
|
||||
</ng-container>
|
||||
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-next
|
||||
[configureProvider]="exists$ | async"
|
||||
[configureTitle]="'DESCRIPTIONS.SETTINGS.IDPS.SAML.TITLE' | translate"
|
||||
[configureDescription]="'DESCRIPTIONS.SETTINGS.IDPS.SAML.DESCRIPTION' | translate"
|
||||
configureLink="https://zitadel.com/docs/guides/integrate/identity-providers/mocksaml"
|
||||
[autofillLink]="autofillLink$ | async"
|
||||
[activateLink]="activateLink$ | async"
|
||||
[copyUrls]="copyUrls$ | async"
|
||||
[expanded]="!!(expandWhatNow$ | async)"
|
||||
(activate)="activate()"
|
||||
></cnsl-provider-next>
|
||||
|
||||
<p class="identity-provider-desc cnsl-secondary-text">
|
||||
{{ !provider ? ('IDP.CREATE.SAML.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
|
||||
</p>
|
||||
@@ -55,7 +75,6 @@
|
||||
(optionsChanged)="options = $event"
|
||||
></cnsl-provider-options>
|
||||
</div>
|
||||
|
||||
<div class="identity-provider-create-actions">
|
||||
<button
|
||||
color="primary"
|
||||
@@ -64,8 +83,9 @@
|
||||
[disabled]="form.invalid || form.disabled"
|
||||
type="submit"
|
||||
>
|
||||
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
|
||||
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
|
||||
<span *ngIf="{ exists: (exists$ | async) } as idp">{{
|
||||
(!!idp.exists ? 'ACTIONS.SAVE' : 'ACTIONS.CREATE') | translate
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ManagementService } from '../../../services/mgmt.service';
|
||||
import { AdminService } from '../../../services/admin.service';
|
||||
import { ToastService } from '../../../services/toast.service';
|
||||
import { GrpcAuthService } from '../../../services/grpc-auth.service';
|
||||
import { take } from 'rxjs';
|
||||
import { BehaviorSubject, shareReplay, switchMap, take } from 'rxjs';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from '../../../services/breadcrumb.service';
|
||||
import { atLeastOneIsFilled, requiredValidator } from '../../form-field/validators/validators';
|
||||
@@ -21,6 +21,9 @@ import {
|
||||
GetProviderByIDRequest as MgmtGetProviderByIDRequest,
|
||||
UpdateSAMLProviderRequest as MgmtUpdateSAMLProviderRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Environment, EnvironmentService } from '../../../services/environment.service';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { ProviderNextService } from '../provider-next/provider-next.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-provider-saml-sp',
|
||||
@@ -28,17 +31,67 @@ import {
|
||||
styleUrls: ['./provider-saml-sp.component.scss'],
|
||||
})
|
||||
export class ProviderSamlSpComponent {
|
||||
// DEPRECATED: use id$ instead
|
||||
public id: string | null = '';
|
||||
public loading: boolean = false;
|
||||
public provider?: Provider.AsObject;
|
||||
public form!: FormGroup;
|
||||
public showOptional: boolean = false;
|
||||
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
|
||||
// DEPRECATED: assert service$ instead
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
|
||||
bindingValues: string[] = Object.keys(SAMLBinding);
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private service$ = this.nextSvc.service(this.route.data);
|
||||
private id$ = this.nextSvc.id(this.route.paramMap, this.justCreated$);
|
||||
public exists$ = this.nextSvc.exists(this.id$);
|
||||
public autofillLink$ = this.nextSvc.autofillLink(
|
||||
this.id$,
|
||||
`https://zitadel.com/docs/guides/integrate/identity-providers/mocksaml#optional-add-zitadel-action-to-autofill-userdata`,
|
||||
);
|
||||
public activateLink$ = this.nextSvc.activateLink(
|
||||
this.id$,
|
||||
this.justActivated$,
|
||||
'https://zitadel.com/docs/guides/integrate/identity-providers/mocksaml#activate-idp',
|
||||
this.service$,
|
||||
);
|
||||
public expandWhatNow$ = this.nextSvc.expandWhatNow(this.id$, this.activateLink$, this.justCreated$);
|
||||
public copyUrls$ = this.id$.pipe(
|
||||
filter((id) => !!id),
|
||||
switchMap((id) =>
|
||||
this.envSvc.env.pipe(
|
||||
map((environment: Environment) => {
|
||||
const idpBase = `${environment.issuer}/idps/${id}/saml`;
|
||||
return [
|
||||
{
|
||||
label: 'ZITADEL Metadata',
|
||||
url: `${idpBase}/metadata`,
|
||||
downloadable: true,
|
||||
},
|
||||
{
|
||||
label: 'ZITADEL ACS Login Form',
|
||||
url: `${environment.issuer}/ui/login/login/externalidp/saml/acs`,
|
||||
},
|
||||
{
|
||||
label: 'ZITADEL ACS Intent API',
|
||||
url: `${idpBase}/acs`,
|
||||
},
|
||||
{
|
||||
label: 'ZITADEL Single Logout',
|
||||
url: `${idpBase}/slo`,
|
||||
},
|
||||
];
|
||||
}),
|
||||
),
|
||||
),
|
||||
shareReplay(1),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private _location: Location,
|
||||
private toast: ToastService,
|
||||
@@ -46,6 +99,8 @@ export class ProviderSamlSpComponent {
|
||||
private route: ActivatedRoute,
|
||||
private injector: Injector,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private envSvc: EnvironmentService,
|
||||
private nextSvc: ProviderNextService,
|
||||
) {
|
||||
this._buildBreadcrumbs();
|
||||
this._initializeForm();
|
||||
@@ -117,14 +172,18 @@ export class ProviderSamlSpComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this.nextSvc.activate(this.id$, this.justActivated$, this.service$);
|
||||
}
|
||||
|
||||
public updateSAMLProvider(): void {
|
||||
if (this.provider) {
|
||||
if (this.provider || this.justCreated$.value) {
|
||||
const req =
|
||||
this.serviceType === PolicyComponentServiceType.MGMT
|
||||
? new MgmtUpdateSAMLProviderRequest()
|
||||
: new AdminUpdateSAMLProviderRequest();
|
||||
|
||||
req.setId(this.provider.id);
|
||||
req.setId(this.provider?.id || this.justCreated$.value);
|
||||
req.setName(this.name?.value);
|
||||
if (this.metadataXml?.value) {
|
||||
req.setMetadataXml(this.metadataXml?.value);
|
||||
@@ -170,11 +229,9 @@ export class ProviderSamlSpComponent {
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addSAMLProvider(req)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
this.close();
|
||||
}, 2000);
|
||||
.then((addedIDP) => {
|
||||
this.justCreated$.next(addedIDP.id);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -183,7 +240,7 @@ export class ProviderSamlSpComponent {
|
||||
}
|
||||
|
||||
public submitForm(): void {
|
||||
this.provider ? this.updateSAMLProvider() : this.addSAMLProvider();
|
||||
this.provider || this.justCreated$.value ? this.updateSAMLProvider() : this.addSAMLProvider();
|
||||
}
|
||||
|
||||
private getData(id: string): void {
|
||||
|
||||
@@ -30,6 +30,9 @@ import { ProviderOAuthComponent } from './provider-oauth/provider-oauth.componen
|
||||
import { ProviderOIDCComponent } from './provider-oidc/provider-oidc.component';
|
||||
import { ProvidersRoutingModule } from './providers-routing.module';
|
||||
import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.component';
|
||||
import { CopyRowComponent } from '../../components/copy-row/copy-row.component';
|
||||
import { ProviderNextComponent } from './provider-next/provider-next.component';
|
||||
import { ProviderNextService } from './provider-next/provider-next.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -47,6 +50,7 @@ import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.com
|
||||
ProviderLDAPComponent,
|
||||
ProviderAppleComponent,
|
||||
ProviderSamlSpComponent,
|
||||
ProviderNextComponent,
|
||||
],
|
||||
imports: [
|
||||
ProvidersRoutingModule,
|
||||
@@ -67,6 +71,8 @@ import { ProviderSamlSpComponent } from './provider-saml-sp/provider-saml-sp.com
|
||||
TranslateModule,
|
||||
ProviderOptionsModule,
|
||||
MatProgressSpinnerModule,
|
||||
CopyRowComponent,
|
||||
],
|
||||
providers: [ProviderNextService],
|
||||
})
|
||||
export default class ProvidersModule {}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$button-text-color: map-get($foreground, text);
|
||||
$button-disabled-text-color: map-get($foreground, disabled-button);
|
||||
|
||||
.identity-provider-desc {
|
||||
font-size: 14px;
|
||||
@@ -192,3 +194,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cnsl-state-dot {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.active {
|
||||
background-color: var(--success);
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
background-color: var(--warn);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user