feat(console): Google, Generic OIDC, Generic JWT - Identity provider templates (#5265)

implement Google, Generic OIDC, Generic JWT provider templates in console
This commit is contained in:
Max Peintner
2023-03-06 10:20:31 +00:00
committed by GitHub
parent 2efa305e10
commit 2ee381f414
55 changed files with 2217 additions and 1880 deletions
@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderJWTComponent } from './provider-jwt.component';
const routes: Routes = [
{
path: '',
component: ProviderJWTComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderJWTCreateRoutingModule {}
@@ -0,0 +1,60 @@
<cnsl-create-layout
title="{{ id ? ('IDP.DETAIL.TITLE' | translate) : ('IDP.CREATE.TITLE' | translate) }}"
(closed)="close()"
>
<div class="jwt-create-content">
<div class="title-row">
<mat-icon class="idp-logo" svgIcon="mdi_jwt" alt="jwt" />
<h1>{{ 'IDP.CREATE.JWT.TITLE' | translate }}</h1>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<p class="desc cnsl-secondary-text">{{ 'IDP.CREATE.JWT.DESCRIPTION' | translate }}</p>
<form [formGroup]="form" (ngSubmit)="submitForm()">
<div class="jwt-content">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.JWT.HEADERNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="headerName" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
<input cnslInput formControlName="issuer" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.JWT.JWTENDPOINT' | translate }}</cnsl-label>
<input cnslInput formControlName="jwtEndpoint" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }}</cnsl-label>
<input cnslInput formControlName="keysEndpoint" />
</cnsl-form-field>
</div>
<div class="optional-h-wrapper">
<h2>{{ 'IDP.OPTIONAL' | translate }}</h2>
<button (click)="showOptional = !showOptional" type="button" mat-icon-button>
<mat-icon *ngIf="showOptional">keyboard_arrow_up</mat-icon
><mat-icon *ngIf="!showOptional">keyboard_arrow_down</mat-icon>
</button>
</div>
<div *ngIf="showOptional">
<cnsl-provider-options
[initialOptions]="provider?.config?.options"
(optionsChanged)="options = $event"
></cnsl-provider-options>
</div>
<div class="jwt-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>
</div>
</form>
</div>
</cnsl-create-layout>
@@ -0,0 +1,85 @@
.desc {
font-size: 14px;
}
.jwt-create-content {
.title-row {
display: flex;
align-items: center;
.idp-logo {
height: 36px;
width: 36px;
margin-right: 1rem;
flex-shrink: 0;
}
h1 {
margin: 0 1rem 0 0;
}
}
.formfield {
display: block;
max-width: 400px;
.mat-chip-input {
width: 100%;
margin: 0;
}
.chip {
border-radius: 0.5rem;
height: 40px;
}
@media only screen and (max-width: 450px) {
max-width: none;
}
}
.jwt-content {
.desc {
margin-bottom: 1rem;
}
.scope-card {
max-width: 400px;
display: block;
.idp-scopes {
.flex-line {
display: flex;
align-items: flex-start;
.formfield {
flex: 1;
}
.scope-add-button {
margin-top: 1.75rem;
}
}
}
}
}
}
.jwt-create-actions {
display: flex;
margin-top: 1rem;
button[mat-raised-button] {
border-radius: 0.5rem;
padding: 0.5rem 4rem;
}
}
.optional-h-wrapper {
display: flex;
align-items: center;
h2 {
margin-right: 0.25rem;
}
}
@@ -0,0 +1,24 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ProviderJWTComponent } from './provider-jwt.component';
describe('ProviderJWTComponent', () => {
let component: ProviderJWTComponent;
let fixture: ComponentFixture<ProviderJWTComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ProviderJWTComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProviderJWTComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,245 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common';
import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { take } from 'rxjs/operators';
import {
AddJWTProviderRequest as AdminAddJWTProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
UpdateJWTProviderRequest as AdminUpdateJWTProviderRequest,
} from 'src/app/proto/generated/zitadel/admin_pb';
import { Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb';
import {
AddJWTProviderRequest as MgmtAddJWTProviderRequest,
GetProviderByIDRequest as MgmtGetProviderByIDRequest,
UpdateJWTProviderRequest as MgmtUpdateJWTProviderRequest,
} from 'src/app/proto/generated/zitadel/management_pb';
import { AdminService } from 'src/app/services/admin.service';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
@Component({
selector: 'cnsl-provider-jwt',
templateUrl: './provider-jwt.component.html',
styleUrls: ['./provider-jwt.component.scss'],
})
export class ProviderJWTComponent {
public showOptional: boolean = false;
public options: Options = new Options();
public id: string | null = '';
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public form!: UntypedFormGroup;
public loading: boolean = false;
public provider?: Provider.AsObject;
constructor(
private router: Router,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
private _location: Location,
breadcrumbService: BreadcrumbService,
) {
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;
console.log(data.serviceType);
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>);
const bread: Breadcrumb = {
type: BreadcrumbType.ORG,
routerLink: ['/org'],
};
breadcrumbService.setBreadcrumb([bread]);
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
const iamBread = new Breadcrumb({
type: BreadcrumbType.ORG,
name: 'Instance',
routerLink: ['/instance'],
});
breadcrumbService.setBreadcrumb([iamBread]);
break;
}
this.id = this.route.snapshot.paramMap.get('id');
if (this.id) {
this.getData(this.id);
}
});
this.form = new UntypedFormGroup({
name: new UntypedFormControl('', [Validators.required]),
headerName: new UntypedFormControl('', [Validators.required]),
issuer: new UntypedFormControl('', [Validators.required]),
jwtEndpoint: new UntypedFormControl('', [Validators.required]),
keysEndpoint: new UntypedFormControl('', [Validators.required]),
});
}
private getData(id: string): void {
const req =
this.serviceType === PolicyComponentServiceType.ADMIN
? new AdminGetProviderByIDRequest()
: new MgmtGetProviderByIDRequest();
req.setId(id);
this.service
.getProviderByID(req)
.then((resp) => {
this.provider = resp.idp;
this.loading = false;
if (this.provider?.config?.jwt) {
this.form.patchValue(this.provider.config.jwt);
this.name?.setValue(this.provider.name);
}
})
.catch((error) => {
this.toast.showError(error);
this.loading = false;
});
}
public submitForm(): void {
this.provider ? this.updateJWTProvider() : this.addJWTProvider();
}
public addJWTProvider(): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
const req = new MgmtAddJWTProviderRequest();
req.setName(this.name?.value);
req.setHeaderName(this.headerName?.value);
req.setIssuer(this.issuer?.value);
req.setJwtEndpoint(this.jwtEndpoint?.value);
req.setKeysEndpoint(this.keysEndpoint?.value);
req.setProviderOptions(this.options);
this.loading = true;
(this.service as ManagementService)
.addJWTProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.router.navigate(['/org-settings'], { queryParams: { id: 'idp' } });
}, 2000);
})
.catch((error) => {
this.toast.showError(error);
this.loading = false;
});
} else if (PolicyComponentServiceType.ADMIN) {
const req = new AdminAddJWTProviderRequest();
req.setName(this.name?.value);
req.setHeaderName(this.headerName?.value);
req.setIssuer(this.issuer?.value);
req.setJwtEndpoint(this.jwtEndpoint?.value);
req.setKeysEndpoint(this.keysEndpoint?.value);
req.setProviderOptions(this.options);
this.loading = true;
(this.service as AdminService)
.addJWTProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.router.navigate(['/settings'], { queryParams: { id: 'idp' } });
}, 2000);
})
.catch((error) => {
this.toast.showError(error);
this.loading = false;
});
}
}
public updateJWTProvider(): void {
if (this.provider) {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
const req = new MgmtUpdateJWTProviderRequest();
req.setId(this.provider.id);
req.setName(this.name?.value);
req.setHeaderName(this.headerName?.value);
req.setIssuer(this.issuer?.value);
req.setJwtEndpoint(this.jwtEndpoint?.value);
req.setKeysEndpoint(this.keysEndpoint?.value);
req.setProviderOptions(this.options);
this.loading = true;
(this.service as ManagementService)
.updateJWTProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.router.navigate(['/org-settings'], { queryParams: { id: 'idp' } });
}, 2000);
})
.catch((error) => {
this.toast.showError(error);
this.loading = false;
});
} else if (PolicyComponentServiceType.ADMIN) {
const req = new AdminUpdateJWTProviderRequest();
req.setId(this.provider.id);
req.setName(this.name?.value);
req.setHeaderName(this.headerName?.value);
req.setIssuer(this.issuer?.value);
req.setJwtEndpoint(this.jwtEndpoint?.value);
req.setKeysEndpoint(this.keysEndpoint?.value);
req.setProviderOptions(this.options);
this.loading = true;
(this.service as AdminService)
.updateJWTProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.router.navigate(['/settings'], { queryParams: { id: 'idp' } });
}, 2000);
})
.catch((error) => {
this.toast.showError(error);
this.loading = false;
});
}
}
}
public close(): void {
this._location.back();
}
public get name(): AbstractControl | null {
return this.form.get('name');
}
public get headerName(): AbstractControl | null {
return this.form.get('headerName');
}
public get issuer(): AbstractControl | null {
return this.form.get('issuer');
}
public get jwtEndpoint(): AbstractControl | null {
return this.form.get('jwtEndpoint');
}
public get keysEndpoint(): AbstractControl | null {
return this.form.get('keysEndpoint');
}
}
@@ -0,0 +1,43 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
import { MatLegacyChipsModule as MatChipsModule } from '@angular/material/legacy-chips';
import { MatLegacyProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { CardModule } from '../../card/card.module';
import { CreateLayoutModule } from '../../create-layout/create-layout.module';
import { InfoSectionModule } from '../../info-section/info-section.module';
import { ProviderOptionsModule } from '../../provider-options/provider-options.module';
import { ProviderJWTCreateRoutingModule } from './provider-jwt-routing.module';
import { ProviderJWTComponent } from './provider-jwt.component';
@NgModule({
declarations: [ProviderJWTComponent],
imports: [
ProviderJWTCreateRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProviderJWTModule {}