diff --git a/console/src/app/modules/idp-create/idp-create.component.html b/console/src/app/modules/idp-create/idp-create.component.html deleted file mode 100644 index 6494ee49a2..0000000000 --- a/console/src/app/modules/idp-create/idp-create.component.html +++ /dev/null @@ -1,174 +0,0 @@ - -
-

{{ 'IDP.CREATE.TITLE' | translate }}

- - - - -

{{ 'IDP.CREATE.DESCRIPTION' | translate }}

- - - -
- -
-
- - -

{{ 'IDP.OIDC.DESCRIPTION' | translate }}

- -
-
- - {{ 'IDP.NAME' | translate }} - - - - {{ 'IDP.ISSUER' | translate }} - - -
- - -
-

{{ 'IDP.AUTOREGISTER_DESC' | translate }}

- - {{ 'IDP.AUTOREGISTER' | translate }} - -
-
- -
- - {{ 'IDP.CLIENTID' | translate }} - - - - {{ 'IDP.CLIENTSECRET' | translate }} - - -
-
- -
-
- - {{ 'IDP.SCOPESLIST' | translate }} - - - - -
- - - - - {{ scope }} cancel - - - -
-
-
-
- - {{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }} - - - {{ 'IDP.MAPPINGFIELD.' + field | translate }} - - - - - {{ 'IDP.USERNAMEMAPPING' | translate }} - - - {{ 'IDP.MAPPINGFIELD.' + field | translate }} - - - -
- -
- - -
-
-
- - -

{{ 'IDP.JWT.DESCRIPTION' | translate }}

- -
-
- - {{ 'IDP.NAME' | translate }} - - - - {{ 'IDP.JWT.HEADERNAME' | translate }} - - - - {{ 'IDP.ISSUER' | translate }} - - -
- - -
-

{{ 'IDP.AUTOREGISTER_DESC' | translate }}

- - {{ 'IDP.AUTOREGISTER' | translate }} - -
-
- -
- - {{ 'IDP.JWT.JWTENDPOINT' | translate }} - - - - {{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }} - - -
- -
- - -
-
-
-
-
diff --git a/console/src/app/modules/idp-create/idp-create.component.ts b/console/src/app/modules/idp-create/idp-create.component.ts deleted file mode 100644 index 4f5a24e77a..0000000000 --- a/console/src/app/modules/idp-create/idp-create.component.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; -import { Location } from '@angular/common'; -import { Component, Injector, OnDestroy, OnInit, Type } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; -import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; -import { ActivatedRoute, Params, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; -import { take } from 'rxjs/operators'; -import { AddJWTIDPRequest, AddOIDCIDPRequest } from 'src/app/proto/generated/zitadel/admin_pb'; -import { OIDCMappingField } from 'src/app/proto/generated/zitadel/idp_pb'; -import { AddOrgJWTIDPRequest, AddOrgOIDCIDPRequest } 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'; -import { JWT, OIDC, RadioItemIdpType } from './idptypes'; - -@Component({ - selector: 'cnsl-idp-create', - templateUrl: './idp-create.component.html', - styleUrls: ['./idp-create.component.scss'], -}) -export class IdpCreateComponent implements OnInit, OnDestroy { - public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; - private service!: ManagementService | AdminService; - public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; - public mappingFields: OIDCMappingField[] = []; - - private subscription?: Subscription; - public projectId: string = ''; - - public oidcFormGroup!: UntypedFormGroup; - public jwtFormGroup!: UntypedFormGroup; - - public createSteps: number = 2; - public currentCreateStep: number = 1; - public loading: boolean = false; - - public idpTypes: RadioItemIdpType[] = [OIDC, JWT]; - - OIDC: any = OIDC; - JWT: any = JWT; - - public idpType!: RadioItemIdpType; - - constructor( - private router: Router, - private route: ActivatedRoute, - private toast: ToastService, - private injector: Injector, - private _location: Location, - breadcrumbService: BreadcrumbService, - ) { - this.oidcFormGroup = new UntypedFormGroup({ - name: new UntypedFormControl('', [Validators.required]), - clientId: new UntypedFormControl('', [Validators.required]), - clientSecret: new UntypedFormControl('', [Validators.required]), - issuer: new UntypedFormControl('', [Validators.required]), - scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []), - idpDisplayNameMapping: new UntypedFormControl(0), - usernameMapping: new UntypedFormControl(0), - autoRegister: new UntypedFormControl(false), - }); - - this.jwtFormGroup = new UntypedFormGroup({ - jwtName: new UntypedFormControl('', [Validators.required]), - jwtHeaderName: new UntypedFormControl('', [Validators.required]), - jwtIssuer: new UntypedFormControl('', [Validators.required]), - jwtEndpoint: new UntypedFormControl('', [Validators.required]), - jwtKeysEndpoint: new UntypedFormControl('', [Validators.required]), - jwtStylingType: new UntypedFormControl(0), - jwtAutoRegister: new UntypedFormControl(false), - }); - - this.route.data.pipe(take(1)).subscribe((data) => { - this.serviceType = data.serviceType; - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - this.service = this.injector.get(ManagementService as Type); - this.mappingFields = [ - OIDCMappingField.OIDC_MAPPING_FIELD_PREFERRED_USERNAME, - OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL, - ]; - const bread: Breadcrumb = { - type: BreadcrumbType.ORG, - routerLink: ['/org'], - }; - - breadcrumbService.setBreadcrumb([bread]); - break; - case PolicyComponentServiceType.ADMIN: - this.service = this.injector.get(AdminService as Type); - this.mappingFields = [ - OIDCMappingField.OIDC_MAPPING_FIELD_PREFERRED_USERNAME, - OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL, - ]; - - const iamBread = new Breadcrumb({ - type: BreadcrumbType.ORG, - name: 'Instance', - routerLink: ['/instance'], - }); - breadcrumbService.setBreadcrumb([iamBread]); - break; - } - }); - } - - public ngOnInit(): void { - this.subscription = this.route.params.subscribe((params) => this.getData(params)); - } - - public ngOnDestroy(): void { - this.subscription?.unsubscribe(); - } - - private getData({ projectid }: Params): void { - this.projectId = projectid; - } - - public addOIDCIdp(): void { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - const req = new AddOrgOIDCIDPRequest(); - - req.setName(this.name?.value); - req.setClientId(this.clientId?.value); - req.setClientSecret(this.clientSecret?.value); - req.setIssuer(this.issuer?.value); - req.setScopesList(this.scopesList?.value); - req.setDisplayNameMapping(this.idpDisplayNameMapping?.value); - req.setUsernameMapping(this.usernameMapping?.value); - req.setAutoRegister(this.autoRegister?.value); - - this.loading = true; - (this.service as ManagementService) - .addOrgOIDCIDP(req) - .then((idp) => { - setTimeout(() => { - this.loading = false; - this.router.navigate( - [ - this.serviceType === PolicyComponentServiceType.MGMT - ? '/org-settings' - : this.serviceType === PolicyComponentServiceType.ADMIN - ? '/settings' - : '', - ], - { queryParams: { id: 'idp' } }, - ); - }, 2000); - }) - .catch((error) => { - this.toast.showError(error); - }); - } else if (PolicyComponentServiceType.ADMIN) { - const req = new AddOIDCIDPRequest(); - req.setName(this.name?.value); - req.setClientId(this.clientId?.value); - req.setClientSecret(this.clientSecret?.value); - req.setIssuer(this.issuer?.value); - req.setScopesList(this.scopesList?.value); - req.setDisplayNameMapping(this.idpDisplayNameMapping?.value); - req.setUsernameMapping(this.usernameMapping?.value); - req.setAutoRegister(this.autoRegister?.value); - - this.loading = true; - (this.service as AdminService) - .addOIDCIDP(req) - .then((idp) => { - setTimeout(() => { - this.loading = false; - this.router.navigate( - [ - this.serviceType === PolicyComponentServiceType.MGMT - ? '/org-settings' - : this.serviceType === PolicyComponentServiceType.ADMIN - ? '/settings' - : '', - ], - { queryParams: { id: 'idp' } }, - ); - }, 2000); - }) - .catch((error) => { - this.toast.showError(error); - }); - } - } - - public addJWTIdp(): void { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - const req = new AddOrgJWTIDPRequest(); - - req.setName(this.jwtName?.value); - req.setHeaderName(this.jwtHeaderName?.value); - req.setIssuer(this.jwtIssuer?.value); - req.setJwtEndpoint(this.jwtEndpoint?.value); - req.setKeysEndpoint(this.jwtKeysEndpoint?.value); - req.setAutoRegister(this.jwtAutoRegister?.value); - req.setStylingType(this.jwtStylingType?.value); - - this.loading = true; - (this.service as ManagementService) - .addOrgJWTIDP(req) - .then((idp) => { - setTimeout(() => { - this.loading = false; - this.router.navigate([ - this.serviceType === PolicyComponentServiceType.MGMT - ? 'org' - : this.serviceType === PolicyComponentServiceType.ADMIN - ? 'iam' - : '', - 'policy', - 'login', - ]); - }, 2000); - }) - .catch((error) => { - this.toast.showError(error); - }); - } else if (PolicyComponentServiceType.ADMIN) { - const req = new AddJWTIDPRequest(); - - req.setName(this.jwtName?.value); - req.setHeaderName(this.jwtHeaderName?.value); - req.setIssuer(this.jwtIssuer?.value); - req.setJwtEndpoint(this.jwtEndpoint?.value); - req.setKeysEndpoint(this.jwtKeysEndpoint?.value); - req.setAutoRegister(this.jwtAutoRegister?.value); - req.setStylingType(this.jwtStylingType?.value); - - this.loading = true; - (this.service as AdminService) - .addJWTIDP(req) - .then((idp) => { - setTimeout(() => { - this.loading = false; - this.router.navigate([ - this.serviceType === PolicyComponentServiceType.MGMT - ? 'org' - : this.serviceType === PolicyComponentServiceType.ADMIN - ? 'iam' - : '', - 'policy', - 'login', - ]); - }, 2000); - }) - .catch((error) => { - this.toast.showError(error); - }); - } - } - - public close(): void { - this._location.back(); - } - - public addScope(event: MatChipInputEvent): void { - const input = event.chipInput?.inputElement; - const value = event.value.trim(); - - if (value !== '') { - if (this.scopesList?.value) { - this.scopesList.value.push(value); - if (input) { - input.value = ''; - } - } - } - } - - public removeScope(uri: string): void { - if (this.scopesList?.value) { - const index = this.scopesList.value.indexOf(uri); - - if (index !== undefined && index >= 0) { - this.scopesList.value.splice(index, 1); - } - } - } - - public get name(): AbstractControl | null { - return this.oidcFormGroup.get('name'); - } - - public get clientId(): AbstractControl | null { - return this.oidcFormGroup.get('clientId'); - } - - public get clientSecret(): AbstractControl | null { - return this.oidcFormGroup.get('clientSecret'); - } - - public get issuer(): AbstractControl | null { - return this.oidcFormGroup.get('issuer'); - } - - public get scopesList(): AbstractControl | null { - return this.oidcFormGroup.get('scopesList'); - } - - public get autoRegister(): AbstractControl | null { - return this.oidcFormGroup.get('autoRegister'); - } - - public get idpDisplayNameMapping(): AbstractControl | null { - return this.oidcFormGroup.get('idpDisplayNameMapping'); - } - - public get usernameMapping(): AbstractControl | null { - return this.oidcFormGroup.get('usernameMapping'); - } - - public get jwtName(): AbstractControl | null { - return this.jwtFormGroup.get('jwtName'); - } - - public get jwtHeaderName(): AbstractControl | null { - return this.jwtFormGroup.get('jwtHeaderName'); - } - - public get jwtIssuer(): AbstractControl | null { - return this.jwtFormGroup.get('jwtIssuer'); - } - - public get jwtEndpoint(): AbstractControl | null { - return this.jwtFormGroup.get('jwtEndpoint'); - } - - public get jwtKeysEndpoint(): AbstractControl | null { - return this.jwtFormGroup.get('jwtKeysEndpoint'); - } - - public get jwtStylingType(): AbstractControl | null { - return this.jwtFormGroup.get('jwtStylingType'); - } - - public get jwtAutoRegister(): AbstractControl | null { - return this.jwtFormGroup.get('jwtAutoRegister'); - } -} diff --git a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.html b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.html deleted file mode 100644 index d88da9ba3b..0000000000 --- a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - - -
diff --git a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss deleted file mode 100644 index bdec242d00..0000000000 --- a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss +++ /dev/null @@ -1,60 +0,0 @@ -@use '@angular/material' as mat; - -.idp-radio-button-wrapper { - display: flex; - flex-direction: column; - flex-wrap: wrap; - margin: 0 -0.5rem; - max-width: 500px; -} - -@mixin idp-type-radio-theme($theme) { - $primary: map-get($theme, primary); - $primary-color: mat.get-color-from-palette($primary, 500); - $primary-color-light: mat.get-color-from-palette($primary, 300); - $primary-color-dark: mat.get-color-from-palette($primary, 700); - $primary-color-contrast: mat.get-color-from-palette($primary, default-contrast); - $is-dark-theme: map-get($theme, is-dark); - $back: map-get($theme, background); - $fg: map-get($theme, foreground); - $cardback: map-get($back, cards); - $text: map-get($fg, text); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - - input[type='radio'].idp { - appearance: none; - display: none; - box-sizing: border-box; - } - - .cnsl-idp-type-radio-button { - margin: 0.5rem; - border-radius: 0.5rem; - display: flex; - flex-direction: column; - cursor: pointer; - position: relative; - background-color: $cardback; - border: 1px solid $border-color; - box-sizing: border-box; - - .cnsl-idp-type-radio-header { - display: flex; - align-items: center; - padding: 0 1rem; - - span { - margin: 1rem 0; - font-size: 1.1rem; - } - - .fill-space { - flex: 1; - } - } - } - - input.idp:checked + label { - border: 1px solid if($is-dark-theme, $primary-color-dark, $primary-color-light) !important; - } -} diff --git a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.ts b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.ts deleted file mode 100644 index b876e37da6..0000000000 --- a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; - -import { OIDC, RadioItemIdpType } from '../idptypes'; - -@Component({ - selector: 'cnsl-idp-type-radio', - templateUrl: './idp-type-radio.component.html', - styleUrls: ['./idp-type-radio.component.scss'], -}) -export class IdpTypeRadioComponent { - @Input() selected: RadioItemIdpType = OIDC; - @Input() types!: RadioItemIdpType[]; - @Output() selectedType: EventEmitter = new EventEmitter(); - - public emitChange(): void { - this.selectedType.emit(this.selected); - } -} diff --git a/console/src/app/modules/idp-create/idptypes.ts b/console/src/app/modules/idp-create/idptypes.ts deleted file mode 100644 index 69a0ef0842..0000000000 --- a/console/src/app/modules/idp-create/idptypes.ts +++ /dev/null @@ -1,22 +0,0 @@ -export enum IdpCreateType { - OIDC = 'OIDC', - JWT = 'JWT', -} - -export interface RadioItemIdpType { - createType: IdpCreateType; - titleI18nKey: string; - mdi?: string; -} - -export const OIDC = { - titleI18nKey: 'IDP.OIDC.TITLE', - mdi: 'mdi_openid', - createType: IdpCreateType.OIDC, -}; - -export const JWT = { - titleI18nKey: 'IDP.JWT.TITLE', - mdi: 'mdi_jwt', - createType: IdpCreateType.JWT, -}; diff --git a/console/src/app/modules/idp-table/idp-table.component.html b/console/src/app/modules/idp-table/idp-table.component.html index 640da10121..7ffdeeca57 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -7,100 +7,8 @@ [selection]="selection" [hideRefresh]="true" > -
- - - - - add{{ 'ACTIONS.NEW' | translate }} - -
-
- - - - - @@ -150,21 +71,21 @@ - - - diff --git a/console/src/app/modules/idp-table/idp-table.component.scss b/console/src/app/modules/idp-table/idp-table.component.scss index 85a9aba067..690c0a1f2d 100644 --- a/console/src/app/modules/idp-table/idp-table.component.scss +++ b/console/src/app/modules/idp-table/idp-table.component.scss @@ -1,3 +1,44 @@ +@mixin idp-table-theme($theme) { + $primary: map-get($theme, primary); + $warn: map-get($theme, warn); + $background: map-get($theme, background); + $accent: map-get($theme, accent); + $primary-color: mat.get-color-from-palette($primary, 500); + + $warn-color: mat.get-color-from-palette($warn, 500); + $accent-color: mat.get-color-from-palette($accent, 500); + $foreground: map-get($theme, foreground); + $is-dark-theme: map-get($theme, is-dark); + $back: map-get($background, background); + + .idp-table-provider-type { + display: flex; + align-items: center; + + .idp-logo { + margin-right: 1rem; + height: 28px; + width: 28px; + flex-shrink: 0; + + &.dark { + display: if($is-dark-theme, block, none); + } + + &.light { + display: if($is-dark-theme, none, block); + } + } + + .idp-icon { + font-size: 1.75rem; + height: 1.75rem; + width: 1.75rem; + margin-right: 1rem; + flex-shrink: 0; + } + } +} .idp-margin-right { margin-right: 0.5rem; } diff --git a/console/src/app/modules/idp-table/idp-table.component.ts b/console/src/app/modules/idp-table/idp-table.component.ts index 2ab19300bf..5acf81ce2c 100644 --- a/console/src/app/modules/idp-table/idp-table.component.ts +++ b/console/src/app/modules/idp-table/idp-table.component.ts @@ -6,13 +6,26 @@ import { RouterLink } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; import { BehaviorSubject, Observable } from 'rxjs'; -import { ListIDPsResponse } from 'src/app/proto/generated/zitadel/admin_pb'; -import { IDP, IDPLoginPolicyLink, IDPOwnerType, IDPState, IDPStylingType } from 'src/app/proto/generated/zitadel/idp_pb'; +import { + ListProvidersRequest as AdminListProvidersRequest, + ListProvidersResponse as AdminListProvidersResponse, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { + IDP, + IDPLoginPolicyLink, + IDPOwnerType, + IDPState, + IDPStylingType, + Provider, + ProviderType, +} from 'src/app/proto/generated/zitadel/idp_pb'; import { AddCustomLoginPolicyRequest, AddCustomLoginPolicyResponse, - ListOrgIDPsResponse, + ListProvidersRequest as MgmtListProvidersRequest, + ListProvidersResponse as MgmtListProvidersResponse, } from 'src/app/proto/generated/zitadel/management_pb'; +import { ListQuery } from 'src/app/proto/generated/zitadel/object_pb'; import { LoginPolicy } from 'src/app/proto/generated/zitadel/policy_pb'; import { AdminService } from 'src/app/services/admin.service'; import { ManagementService } from 'src/app/services/mgmt.service'; @@ -31,16 +44,17 @@ export class IdpTableComponent implements OnInit { @Input() public serviceType!: PolicyComponentServiceType; @Input() service!: AdminService | ManagementService; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; - public dataSource: MatTableDataSource = new MatTableDataSource(); - public selection: SelectionModel = new SelectionModel(true, []); - public idpResult?: ListIDPsResponse.AsObject | ListOrgIDPsResponse.AsObject; + public dataSource: MatTableDataSource = new MatTableDataSource(); + public selection: SelectionModel = new SelectionModel(true, []); + public idpResult?: MgmtListProvidersResponse.AsObject | AdminListProvidersResponse.AsObject; private loadingSubject: BehaviorSubject = new BehaviorSubject(false); public loading$: Observable = this.loadingSubject.asObservable(); public PolicyComponentServiceType: any = PolicyComponentServiceType; public IDPOwnerType: any = IDPOwnerType; public IDPState: any = IDPState; - public displayedColumns: string[] = ['availability', 'name', 'type', 'creationDate', 'changeDate', 'state', 'actions']; - @Output() public changedSelection: EventEmitter> = new EventEmitter(); + public ProviderType: any = ProviderType; + public displayedColumns: string[] = ['availability', 'name', 'type', 'creationDate', 'changeDate', 'actions']; + @Output() public changedSelection: EventEmitter> = new EventEmitter(); public idps: IDPLoginPolicyLink.AsObject[] = []; public IDPStylingType: any = IDPStylingType; @@ -59,7 +73,7 @@ export class IdpTableComponent implements OnInit { }); if (this.serviceType === PolicyComponentServiceType.MGMT) { - this.displayedColumns = ['availability', 'name', 'type', 'owner', 'creationDate', 'changeDate', 'state', 'actions']; + this.displayedColumns = ['availability', 'name', 'type', 'owner', 'creationDate', 'changeDate', 'actions']; } } @@ -161,8 +175,13 @@ export class IdpTableComponent implements OnInit { this.loadingSubject.next(true); if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtListProvidersRequest(); + const lq = new ListQuery(); + lq.setOffset(offset); + lq.setLimit(limit); + req.setQuery(lq); (this.service as ManagementService) - .listOrgIDPs(limit, offset) + .listProviders(req) .then((resp) => { this.idpResult = resp; this.dataSource.data = resp.resultList; @@ -173,8 +192,13 @@ export class IdpTableComponent implements OnInit { this.loadingSubject.next(false); }); } else { + const req = new AdminListProvidersRequest(); + const lq = new ListQuery(); + lq.setOffset(offset); + lq.setLimit(limit); + req.setQuery(lq); (this.service as AdminService) - .listIDPs(limit, offset) + .listProviders(req) .then((resp) => { this.idpResult = resp; this.dataSource.data = resp.resultList; @@ -199,19 +223,15 @@ export class IdpTableComponent implements OnInit { } } - public routerLinkForRow(row: IDP.AsObject): any { + public routerLinkForRow(row: Provider.AsObject): any { if (row.id) { - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - switch (row.owner) { - case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM: - return ['/instance', 'idp', row.id]; - case IDPOwnerType.IDP_OWNER_TYPE_ORG: - return ['/org', 'idp', row.id]; - } - break; - case PolicyComponentServiceType.ADMIN: - return ['/instance', 'idp', row.id]; + switch (row.type) { + case ProviderType.PROVIDER_TYPE_OIDC: + return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'oidc', row.id]; + case ProviderType.PROVIDER_TYPE_JWT: + return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'jwt', row.id]; + case ProviderType.PROVIDER_TYPE_GOOGLE: + return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'google', row.id]; } } } @@ -278,7 +298,7 @@ export class IdpTableComponent implements OnInit { return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq); } - public addIdp(idp: IDP.AsObject | IDP.AsObject): Promise { + public addIdp(idp: Provider.AsObject): Promise { switch (this.serviceType) { case PolicyComponentServiceType.MGMT: if (this.isDefault) { @@ -307,13 +327,15 @@ export class IdpTableComponent implements OnInit { .addIDPToLoginPolicy(idp.id, idp.owner) .then(() => { this.toast.showInfo('IDP.TOAST.ADDED', true); - this.getIdps() - .then((resp) => { - this.idps = resp; - }) - .catch((error) => { - this.toast.showError(error); - }); + setTimeout(() => { + this.getIdps() + .then((resp) => { + this.idps = resp; + }) + .catch((error) => { + this.toast.showError(error); + }); + }, 2000); }) .catch((error) => { this.toast.showError(error); @@ -324,9 +346,15 @@ export class IdpTableComponent implements OnInit { .addIDPToLoginPolicy(idp.id) .then(() => { this.toast.showInfo('IDP.TOAST.ADDED', true); - this.getIdps().then((resp) => { - this.idps = resp; - }); + setTimeout(() => { + this.getIdps() + .then((resp) => { + this.idps = resp; + }) + .catch((error) => { + this.toast.showError(error); + }); + }, 2000); }) .catch((error) => { this.toast.showError(error); @@ -334,7 +362,7 @@ export class IdpTableComponent implements OnInit { } } - public removeIdp(idp: IDP.AsObject): Promise { + public removeIdp(idp: Provider.AsObject): Promise { switch (this.serviceType) { case PolicyComponentServiceType.MGMT: if (this.isDefault) { @@ -367,9 +395,15 @@ export class IdpTableComponent implements OnInit { .removeIDPFromLoginPolicy(idp.id) .then(() => { this.toast.showInfo('IDP.TOAST.REMOVED', true); - this.getIdps().then((resp) => { - this.idps = resp; - }); + setTimeout(() => { + this.getIdps() + .then((resp) => { + this.idps = resp; + }) + .catch((error) => { + this.toast.showError(error); + }); + }, 2000); }) .catch((error) => { this.toast.showError(error); @@ -380,9 +414,15 @@ export class IdpTableComponent implements OnInit { .removeIDPFromLoginPolicy(idp.id) .then(() => { this.toast.showInfo('IDP.TOAST.REMOVED', true); - this.getIdps().then((resp) => { - this.idps = resp; - }); + setTimeout(() => { + this.getIdps() + .then((resp) => { + this.idps = resp; + }) + .catch((error) => { + this.toast.showError(error); + }); + }, 2000); }) .catch((error) => { this.toast.showError(error); @@ -390,7 +430,7 @@ export class IdpTableComponent implements OnInit { } } - public isEnabled(idp: IDP.AsObject): boolean { + public isEnabled(idp: Provider.AsObject): boolean { return this.idps.findIndex((i) => i.idpId === idp.id) > -1; } diff --git a/console/src/app/modules/idp/idp.component.html b/console/src/app/modules/idp/idp.component.html deleted file mode 100644 index aa3b93ed4a..0000000000 --- a/console/src/app/modules/idp/idp.component.html +++ /dev/null @@ -1,225 +0,0 @@ - - - - - - - - - -
-
-

{{ 'IDP.OIDC.GENERAL' | translate }}

- -
- -
- - {{ 'IDP.NAME' | translate }} - - - - - {{ 'IDP.STYLE' | translate }} - - - {{ 'IDP.STYLEFIELD.' + field | translate }} - - - - - -
-

{{ 'IDP.AUTOREGISTER_DESC' | translate }}

- - {{ 'IDP.AUTOREGISTER' | translate }} - -
-
-
-
- -
- -
- - - -

{{ 'IDP.OIDC.TITLE' | translate }}

-

{{ 'IDP.OIDC.DESCRIPTION' | translate }}

- -
- -
- - {{ 'IDP.ISSUER' | translate }} - - - - - {{ 'IDP.CLIENTID' | translate }} - - - - - Update Client Secret - - - - {{ 'IDP.CLIENTSECRET' | translate }} - - - - -
-
- - {{ 'IDP.SCOPESLIST' | translate }} - - - - -
- - - - - {{ scope }} cancel - - - -
-
- - - {{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }} - - - {{ 'IDP.MAPPINGFIELD.' + field | translate }} - - - - - - {{ 'IDP.USERNAMEMAPPING' | translate }} - - - {{ 'IDP.MAPPINGFIELD.' + field | translate }} - - - -
-
- -
- -
- -
- - -

{{ 'IDP.JWT.TITLE' | translate }}

-

{{ 'IDP.JWT.DESCRIPTION' | translate }}

- -
- -
- - {{ 'IDP.ISSUER' | translate }} - - - - - {{ 'IDP.JWT.HEADERNAME' | translate }} - - - - - {{ 'IDP.JWT.JWTENDPOINT' | translate }} - - - - - {{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }} - - -
-
- -
- -
- -
-
-
diff --git a/console/src/app/modules/idp/idp.component.scss b/console/src/app/modules/idp/idp.component.scss deleted file mode 100644 index 1856affae6..0000000000 --- a/console/src/app/modules/idp/idp.component.scss +++ /dev/null @@ -1,85 +0,0 @@ -.idp-wrapper { - margin-top: 2rem; - - .idp-desc { - font-size: 14px; - } - - .idp-content { - .idp-desc { - margin-bottom: 1rem; - } - - .idp-formfield { - display: block; - max-width: 400px; - - .chip { - border-radius: 0.5rem; - height: 40px; - } - - .mat-chip-input { - width: 100%; - margin: 0; - } - - @media only screen and (max-width: 450px) { - max-width: none; - } - } - - .auto-reg-info { - margin: 0 0 1rem 0; - display: block; - width: 100%; - max-width: 400px; - - .auto-reg-desc { - margin: 0 0 1rem 0; - } - } - - .scope-card { - max-width: 400px; - display: block; - - .scope-card-content { - display: block; - - .flex-line { - display: flex; - align-items: flex-end; - width: 100%; - - .idp-formfield { - flex: 1; - - input { - margin: 0; - } - } - - button { - margin-bottom: 12px; - } - } - } - } - } - - .btn-wrapper { - display: flex; - margin-top: 1rem; - - .continue-button { - margin-bottom: 2rem; - display: block; - - @media only screen and (max-width: 450px) { - margin-top: 1rem; - margin-bottom: 2rem; - } - } - } -} diff --git a/console/src/app/modules/idp/idp.component.ts b/console/src/app/modules/idp/idp.component.ts deleted file mode 100644 index f018afc9ce..0000000000 --- a/console/src/app/modules/idp/idp.component.ts +++ /dev/null @@ -1,472 +0,0 @@ -import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; -import { Location } from '@angular/common'; -import { Component, Injector, OnDestroy, Type } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; -import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; -import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; -import { ActivatedRoute, Router } from '@angular/router'; -import { Observable, Subject } from 'rxjs'; -import { take } from 'rxjs/operators'; -import { - UpdateIDPJWTConfigRequest, - UpdateIDPOIDCConfigRequest, - UpdateIDPRequest, -} from 'src/app/proto/generated/zitadel/admin_pb'; -import { IDP, IDPState, IDPStylingType, OIDCMappingField } from 'src/app/proto/generated/zitadel/idp_pb'; -import { - UpdateOrgIDPJWTConfigRequest, - UpdateOrgIDPOIDCConfigRequest, - UpdateOrgIDPRequest, -} 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 { GrpcAuthService } from 'src/app/services/grpc-auth.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'; -import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; - -@Component({ - selector: 'cnsl-idp', - templateUrl: './idp.component.html', - styleUrls: ['./idp.component.scss'], -}) -export class IdpComponent implements OnDestroy { - public mappingFields: OIDCMappingField[] = []; - public styleFields: IDPStylingType[] = []; - - public showIdSecretSection: boolean = false; - public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; - private service!: ManagementService | AdminService; - public PolicyComponentServiceType: any = PolicyComponentServiceType; - public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; - - public idp?: IDP.AsObject; - private destroy$: Subject = new Subject(); - public projectId: string = ''; - - public idpForm!: UntypedFormGroup; - public oidcConfigForm!: UntypedFormGroup; - public jwtConfigForm!: UntypedFormGroup; - - IDPState: any = IDPState; - - public canWrite: Observable = this.authService.isAllowed([ - this.serviceType === PolicyComponentServiceType.ADMIN - ? 'iam.idp.write' - : this.serviceType === PolicyComponentServiceType.MGMT - ? 'org.idp.write' - : '', - ]); - - constructor( - private toast: ToastService, - private injector: Injector, - private route: ActivatedRoute, - private router: Router, - private _location: Location, - private authService: GrpcAuthService, - private dialog: MatDialog, - breadcrumbService: BreadcrumbService, - ) { - this.idpForm = new UntypedFormGroup({ - id: new UntypedFormControl({ disabled: true, value: '' }, [Validators.required]), - name: new UntypedFormControl('', [Validators.required]), - stylingType: new UntypedFormControl('', [Validators.required]), - autoRegister: new UntypedFormControl(false, [Validators.required]), - }); - - this.oidcConfigForm = new UntypedFormGroup({ - clientId: new UntypedFormControl('', [Validators.required]), - clientSecret: new UntypedFormControl(''), - issuer: new UntypedFormControl('', [Validators.required]), - scopesList: new UntypedFormControl([], []), - displayNameMapping: new UntypedFormControl(0), - usernameMapping: new UntypedFormControl(0), - }); - - this.jwtConfigForm = new UntypedFormGroup({ - jwtEndpoint: new UntypedFormControl('', [Validators.required]), - issuer: new UntypedFormControl('', [Validators.required]), - keysEndpoint: new UntypedFormControl('', [Validators.required]), - headerName: new UntypedFormControl('', [Validators.required]), - }); - - const serviceType = this.route.snapshot.data.serviceType; - if (serviceType !== undefined) { - this.serviceType = serviceType; - - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - this.service = this.injector.get(ManagementService as Type); - - const bread: Breadcrumb = { - type: BreadcrumbType.ORG, - routerLink: ['/org'], - }; - breadcrumbService.setBreadcrumb([bread]); - break; - case PolicyComponentServiceType.ADMIN: - this.service = this.injector.get(AdminService as Type); - - const iamBread = new Breadcrumb({ - type: BreadcrumbType.INSTANCE, - name: 'Instance', - routerLink: ['/instance'], - }); - breadcrumbService.setBreadcrumb([iamBread]); - break; - } - - this.mappingFields = [ - OIDCMappingField.OIDC_MAPPING_FIELD_PREFERRED_USERNAME, - OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL, - ]; - this.styleFields = [IDPStylingType.STYLING_TYPE_UNSPECIFIED, IDPStylingType.STYLING_TYPE_GOOGLE]; - } - - const idpId = this.route.snapshot.paramMap.get('id'); - if (idpId) { - this.checkWrite(); - - if (this.serviceType === PolicyComponentServiceType.MGMT) { - (this.service as ManagementService).getOrgIDPByID(idpId).then((resp) => { - if (resp.idp) { - this.idp = resp.idp; - this.idpForm.patchValue(this.idp); - if (this.idp.oidcConfig) { - this.oidcConfigForm.patchValue(this.idp.oidcConfig); - } else if (this.idp.jwtConfig) { - this.jwtConfigForm.patchValue(this.idp.jwtConfig); - this.jwtIssuer?.setValue(this.idp.jwtConfig.issuer); - } - } - }); - } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { - (this.service as AdminService).getIDPByID(idpId).then((resp) => { - if (resp.idp) { - this.idp = resp.idp; - this.idpForm.patchValue(this.idp); - if (this.idp.oidcConfig) { - this.oidcConfigForm.patchValue(this.idp.oidcConfig); - } else if (this.idp.jwtConfig) { - this.jwtConfigForm.patchValue(this.idp.jwtConfig); - this.jwtIssuer?.setValue(this.idp.jwtConfig.issuer); - } - } - }); - } - } - } - - public checkWrite(): void { - this.canWrite.pipe(take(1)).subscribe((canWrite) => { - if (canWrite) { - this.idpForm.enable(); - this.oidcConfigForm.enable(); - } else { - this.idpForm.disable(); - this.oidcConfigForm.disable(); - } - }); - } - - public ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - public deleteIdp(): void { - const dialogRef = this.dialog.open(WarnDialogComponent, { - data: { - confirmKey: 'ACTIONS.DELETE', - cancelKey: 'ACTIONS.CANCEL', - titleKey: 'IDP.DELETE_TITLE', - descriptionKey: 'IDP.DELETE_DESCRIPTION', - }, - width: '400px', - }); - - dialogRef.afterClosed().subscribe((resp) => { - if (this.idp && resp) { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - (this.service as ManagementService) - .removeOrgIDP(this.idp.id) - .then(() => { - this.toast.showInfo('IDP.TOAST.DELETED', true); - this.router.navigate(this.backroutes); - }) - .catch((error: any) => { - this.toast.showError(error); - }); - } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { - (this.service as AdminService) - .removeIDP(this.idp.id) - .then(() => { - this.toast.showInfo('IDP.TOAST.DELETED', true); - this.router.navigate(this.backroutes); - }) - .catch((error: any) => { - this.toast.showError(error); - }); - } - } - }); - } - - public changeState(state: IDPState): void { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - if (state === IDPState.IDP_STATE_ACTIVE && this.idp) { - (this.service as ManagementService) - .reactivateOrgIDP(this.idp.id) - .then(() => { - this.idp!.state = state; - this.toast.showInfo('IDP.TOAST.REACTIVATED', true); - }) - .catch((error: any) => { - this.toast.showError(error); - }); - } else if (state === IDPState.IDP_STATE_INACTIVE && this.idp) { - (this.service as ManagementService) - .deactivateOrgIDP(this.idp.id) - .then(() => { - this.idp!.state = state; - this.toast.showInfo('IDP.TOAST.DEACTIVATED', true); - }) - .catch((error: any) => { - this.toast.showError(error); - }); - } - } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { - if (state === IDPState.IDP_STATE_ACTIVE && this.idp) { - (this.service as AdminService) - .reactivateIDP(this.idp.id) - .then(() => { - this.idp!.state = state; - this.toast.showInfo('IDP.TOAST.REACTIVATED', true); - }) - .catch((error: any) => { - this.toast.showError(error); - }); - } else if (state === IDPState.IDP_STATE_INACTIVE && this.idp) { - (this.service as AdminService) - .deactivateIDP(this.idp.id) - .then(() => { - this.idp!.state = state; - this.toast.showInfo('IDP.TOAST.DEACTIVATED', true); - }) - .catch((error: any) => { - this.toast.showError(error); - }); - } - } - } - - public updateIdp(): void { - if (this.serviceType === PolicyComponentServiceType.MGMT && this.idp) { - const req = new UpdateOrgIDPRequest(); - - req.setIdpId(this.idp.id); - req.setName(this.name?.value); - req.setStylingType(this.stylingType?.value); - req.setAutoRegister(this.autoRegister?.value); - - (this.service as ManagementService) - .updateOrgIDP(req) - .then(() => { - this.toast.showInfo('IDP.TOAST.SAVED', true); - }) - .catch((error) => { - this.toast.showError(error); - }); - } else if (this.serviceType === PolicyComponentServiceType.ADMIN && this.idp) { - const req = new UpdateIDPRequest(); - - req.setIdpId(this.idp.id); - req.setName(this.name?.value); - req.setStylingType(this.stylingType?.value); - req.setAutoRegister(this.autoRegister?.value); - - (this.service as AdminService) - .updateIDP(req) - .then(() => { - this.toast.showInfo('IDP.TOAST.SAVED', true); - }) - .catch((error) => { - this.toast.showError(error); - }); - } - } - - public updateOidcConfig(): void { - if (this.serviceType === PolicyComponentServiceType.MGMT && this.idp) { - const req = new UpdateOrgIDPOIDCConfigRequest(); - - req.setIdpId(this.idp.id); - req.setClientId(this.clientId?.value); - req.setClientSecret(this.clientSecret?.value); - req.setIssuer(this.issuer?.value); - req.setScopesList(this.scopesList?.value); - req.setUsernameMapping(this.usernameMapping?.value); - req.setDisplayNameMapping(this.displayNameMapping?.value); - - (this.service as ManagementService) - .updateOrgIDPOIDCConfig(req) - .then((oidcConfig) => { - this.toast.showInfo('IDP.TOAST.SAVED', true); - }) - .catch((error) => { - this.toast.showError(error); - }); - } else if (this.serviceType === PolicyComponentServiceType.ADMIN && this.idp) { - const req = new UpdateIDPOIDCConfigRequest(); - - req.setIdpId(this.idp.id); - req.setClientId(this.clientId?.value); - req.setClientSecret(this.clientSecret?.value); - req.setIssuer(this.issuer?.value); - req.setScopesList(this.scopesList?.value); - req.setUsernameMapping(this.usernameMapping?.value); - req.setDisplayNameMapping(this.displayNameMapping?.value); - - (this.service as AdminService) - .updateIDPOIDCConfig(req) - .then((oidcConfig) => { - this.toast.showInfo('IDP.TOAST.SAVED', true); - }) - .catch((error) => { - this.toast.showError(error); - }); - } - } - - public updateJwtConfig(): void { - if (this.serviceType === PolicyComponentServiceType.MGMT && this.idp) { - const req = new UpdateOrgIDPJWTConfigRequest(); - - req.setIdpId(this.idp.id); - req.setIssuer(this.jwtIssuer?.value); - req.setHeaderName(this.headerName?.value); - req.setJwtEndpoint(this.jwtEndpoint?.value); - req.setKeysEndpoint(this.keyEndpoint?.value); - - (this.service as ManagementService) - .updateOrgIDPJWTConfig(req) - .then((jwtConfig) => { - this.toast.showInfo('IDP.TOAST.SAVED', true); - // this.router.navigate(['idp', ]); - }) - .catch((error) => { - this.toast.showError(error); - }); - } else if (this.serviceType === PolicyComponentServiceType.ADMIN && this.idp) { - const req = new UpdateIDPJWTConfigRequest(); - - req.setIdpId(this.idp.id); - req.setIssuer(this.jwtIssuer?.value); - req.setHeaderName(this.headerName?.value); - req.setJwtEndpoint(this.jwtEndpoint?.value); - req.setKeysEndpoint(this.keyEndpoint?.value); - - (this.service as AdminService) - .updateIDPJWTConfig(req) - .then((jwtConfig) => { - this.toast.showInfo('IDP.TOAST.SAVED', true); - // this.router.navigate(['idp', ]); - }) - .catch((error) => { - this.toast.showError(error); - }); - } - } - - public close(): void { - this._location.back(); - } - - public addScope(event: MatChipInputEvent): void { - const input = event.chipInput?.inputElement; - const value = event.value.trim(); - - if (value !== '') { - if (this.scopesList?.value) { - this.scopesList.value.push(value); - if (input) { - input.value = ''; - } - } - } - } - - public removeScope(uri: string): void { - if (this.scopesList?.value) { - const index = this.scopesList?.value.indexOf(uri); - - if (index !== undefined && index >= 0) { - this.scopesList?.value.splice(index, 1); - } - } - } - - public get backroutes(): string[] { - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - return ['/org', 'policy', 'login']; - case PolicyComponentServiceType.ADMIN: - return ['/instance', 'policy', 'login']; - } - } - - public get name(): AbstractControl | null { - return this.idpForm.get('name'); - } - - public get stylingType(): AbstractControl | null { - return this.idpForm.get('stylingType'); - } - - public get autoRegister(): AbstractControl | null { - return this.idpForm.get('autoRegister'); - } - - public get clientId(): AbstractControl | null { - return this.oidcConfigForm.get('clientId'); - } - - public get clientSecret(): AbstractControl | null { - return this.oidcConfigForm.get('clientSecret'); - } - - public get issuer(): AbstractControl | null { - return this.oidcConfigForm.get('issuer'); - } - - public get scopesList(): AbstractControl | null { - return this.oidcConfigForm.get('scopesList'); - } - - public get displayNameMapping(): AbstractControl | null { - return this.oidcConfigForm.get('displayNameMapping'); - } - - public get usernameMapping(): AbstractControl | null { - return this.oidcConfigForm.get('usernameMapping'); - } - - public get jwtIssuer(): AbstractControl | null { - return this.jwtConfigForm.get('issuer'); - } - - public get jwtEndpoint(): AbstractControl | null { - return this.jwtConfigForm.get('jwtEndpoint'); - } - - public get keyEndpoint(): AbstractControl | null { - return this.jwtConfigForm.get('keysEndpoint'); - } - - public get headerName(): AbstractControl | null { - return this.jwtConfigForm.get('headerName'); - } -} diff --git a/console/src/app/modules/policies/idp-settings/idp-settings.component.html b/console/src/app/modules/policies/idp-settings/idp-settings.component.html index 3290a959d8..bff6b403ba 100644 --- a/console/src/app/modules/policies/idp-settings/idp-settings.component.html +++ b/console/src/app/modules/policies/idp-settings/idp-settings.component.html @@ -3,3 +3,86 @@
+ +

{{ 'IDP.CREATE.TITLE' | translate }}

+

{{ 'IDP.CREATE.DESCRIPTION' | translate }}

+ +
+ + +
+ Google +
+
+ +
+ {{ 'ACTIONS.COMINGSOON' | translate }} + +
+ Microsoft +
+
+ +
+ {{ 'ACTIONS.COMINGSOON' | translate }} + + + +
+ GitHub +
+
+ +
+ {{ 'ACTIONS.COMINGSOON' | translate }} + +
+ GitLab +
+
+ + +
+ +
+
+ Generic OIDC +
+
+ + +
+ +
+
+ Generic JWT +
+
+
diff --git a/console/src/app/modules/policies/idp-settings/idp-settings.component.scss b/console/src/app/modules/policies/idp-settings/idp-settings.component.scss index fec6398999..25e055dfe4 100644 --- a/console/src/app/modules/policies/idp-settings/idp-settings.component.scss +++ b/console/src/app/modules/policies/idp-settings/idp-settings.component.scss @@ -1,3 +1,109 @@ -.cnsl-idp-table-wrapper { - display: block; +@use '@angular/material' as mat; + +@mixin idp-settings-theme($theme) { + $primary: map-get($theme, primary); + $primary-color: mat.get-color-from-palette($primary, 500); + $is-dark-theme: map-get($theme, is-dark); + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + + .cnsl-idp-table-wrapper { + display: block; + } + + .new-idp-wrapper { + display: grid; + row-gap: 1.5rem; + column-gap: 1.5rem; + box-sizing: border-box; + width: 100%; + grid-template-columns: 1fr; + + @media only screen and (min-width: 700px) { + grid-template-columns: 1fr 1fr; + } + + @media only screen and (min-width: 1000px) { + grid-template-columns: 1fr 1fr 1fr; + } + + @media only screen and (min-width: 1300px) { + grid-template-columns: 1fr 1fr 1fr 1fr; + } + + .item { + position: relative; + z-index: 100; + display: flex; + text-decoration: none; + cursor: pointer; + padding-top: 1rem; + padding-right: 1rem; + padding-bottom: 1rem; + padding-left: 1rem; + border-radius: 0.5rem; + box-sizing: border-box; + transition: box-shadow 0.1s ease-in; + align-items: center; + color: map-get($foreground, text); + + .coming-soon-label { + position: absolute; + top: 0; + right: 1rem; + transform: translateY(-50%); + width: fit-content; + } + + &:hover { + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12); + } + + .idp-logo { + margin-right: 1rem; + height: 36px; + width: 36px; + + &.dark { + display: if($is-dark-theme, block, none); + } + + &.light { + display: if($is-dark-theme, none, block); + } + } + + .idp-icon { + margin-right: 1rem; + display: flex; + justify-content: center; + align-items: center; + height: 36px; + width: 36px; + + .icon { + font-size: 2.25rem; + height: 2.25rem; + width: 2.25rem; + } + } + + .text-container { + display: flex; + flex-direction: column; + + .title { + } + } + + &.coming-soon { + filter: grayscale(1); + cursor: not-allowed; + + &:hover { + box-shadow: none; + } + } + } + } } diff --git a/console/src/app/modules/policies/idp-settings/idp-settings.component.ts b/console/src/app/modules/policies/idp-settings/idp-settings.component.ts index a3ec92e3e0..cb17288612 100644 --- a/console/src/app/modules/policies/idp-settings/idp-settings.component.ts +++ b/console/src/app/modules/policies/idp-settings/idp-settings.component.ts @@ -26,4 +26,6 @@ export class IdpSettingsComponent implements OnInit { break; } } + + public createGoogle() {} } diff --git a/console/src/app/modules/policies/idp-settings/idp-settings.module.ts b/console/src/app/modules/policies/idp-settings/idp-settings.module.ts index fe98a30da5..79264fbe3c 100644 --- a/console/src/app/modules/policies/idp-settings/idp-settings.module.ts +++ b/console/src/app/modules/policies/idp-settings/idp-settings.module.ts @@ -1,7 +1,11 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { MatIconModule } from '@angular/material/icon'; +import { MatLegacyButtonModule } from '@angular/material/legacy-button'; import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner'; +import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; +import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { CardModule } from '../../card/card.module'; import { IdpTableModule } from '../../idp-table/idp-table.module'; @@ -9,7 +13,17 @@ import { IdpSettingsComponent } from './idp-settings.component'; @NgModule({ declarations: [IdpSettingsComponent], - imports: [CommonModule, CardModule, IdpTableModule, MatProgressSpinnerModule, TranslateModule], + imports: [ + CommonModule, + MatLegacyButtonModule, + CardModule, + MatIconModule, + IdpTableModule, + RouterModule, + HasRolePipeModule, + MatProgressSpinnerModule, + TranslateModule, + ], exports: [IdpSettingsComponent], }) export class IdpSettingsModule {} diff --git a/console/src/app/modules/provider-options/provider-options.component.html b/console/src/app/modules/provider-options/provider-options.component.html new file mode 100644 index 0000000000..4991eee7ae --- /dev/null +++ b/console/src/app/modules/provider-options/provider-options.component.html @@ -0,0 +1,26 @@ +
+ +
+

{{ 'IDP.OPTIONS.ISAUTOCREATION_DESC' | translate }}

+ {{ 'IDP.OPTIONS.ISAUTOCREATION' | translate }} +
+
+ +
+

{{ 'IDP.OPTIONS.ISAUTOUPDATE_DESC' | translate }}

+ {{ 'IDP.OPTIONS.ISAUTOUPDATE' | translate }} +
+
+ +
+

{{ 'IDP.OPTIONS.ISCREATIONALLOWED_DESC' | translate }}

+ {{ 'IDP.OPTIONS.ISCREATIONALLOWED' | translate }} +
+
+ +
+

{{ 'IDP.OPTIONS.ISLINKINGALLOWED_DESC' | translate }}

+ {{ 'IDP.OPTIONS.ISLINKINGALLOWED' | translate }} +
+
+ diff --git a/console/src/app/modules/provider-options/provider-options.component.scss b/console/src/app/modules/provider-options/provider-options.component.scss new file mode 100644 index 0000000000..5363ea02da --- /dev/null +++ b/console/src/app/modules/provider-options/provider-options.component.scss @@ -0,0 +1,11 @@ +.option-form { + display: grid; + grid-template-columns: 1fr; + max-width: 400px; + padding-bottom: 1rem; + + .checkbox-desc { + margin-top: 0; + margin-bottom: 0.5rem; + } +} diff --git a/console/src/app/modules/provider-options/provider-options.component.spec.ts b/console/src/app/modules/provider-options/provider-options.component.spec.ts new file mode 100644 index 0000000000..76ec0e2081 --- /dev/null +++ b/console/src/app/modules/provider-options/provider-options.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProviderOptionsComponent } from './provider-options.component'; + +describe('ProviderOptionsComponent', () => { + let component: ProviderOptionsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ProviderOptionsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ProviderOptionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/provider-options/provider-options.component.ts b/console/src/app/modules/provider-options/provider-options.component.ts new file mode 100644 index 0000000000..63a75a1dc5 --- /dev/null +++ b/console/src/app/modules/provider-options/provider-options.component.ts @@ -0,0 +1,45 @@ +import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; +import { Subject, takeUntil } from 'rxjs'; +import { Options } from 'src/app/proto/generated/zitadel/idp_pb'; + +@Component({ + selector: 'cnsl-provider-options', + templateUrl: './provider-options.component.html', + styleUrls: ['./provider-options.component.scss'], +}) +export class ProviderOptionsComponent implements OnChanges, OnDestroy { + @Input() public initialOptions?: Options.AsObject; + @Output() public optionsChanged: EventEmitter = new EventEmitter(); + private destroy$: Subject = new Subject(); + public form: FormGroup = new FormGroup({ + isAutoCreation: new FormControl(false, []), + isAutoUpdate: new FormControl(false, []), + isCreationAllowed: new FormControl(true, []), + isLinkingAllowed: new FormControl(true, []), + }); + + constructor() { + this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => { + if (value) { + const opt = new Options(); + opt.setIsAutoCreation(value.isAutoCreation); + opt.setIsAutoUpdate(value.isAutoUpdate); + opt.setIsCreationAllowed(value.isCreationAllowed); + opt.setIsLinkingAllowed(value.isLinkingAllowed); + this.optionsChanged.emit(opt); + } + }); + } + + ngOnChanges(): void { + if (this.initialOptions) { + this.form.patchValue(this.initialOptions); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/console/src/app/modules/provider-options/provider-options.module.ts b/console/src/app/modules/provider-options/provider-options.module.ts new file mode 100644 index 0000000000..5060a0fea3 --- /dev/null +++ b/console/src/app/modules/provider-options/provider-options.module.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox'; +import { TranslateModule } from '@ngx-translate/core'; +import { InfoSectionModule } from '../info-section/info-section.module'; +import { ProviderOptionsComponent } from './provider-options.component'; + +@NgModule({ + declarations: [ProviderOptionsComponent], + imports: [CommonModule, MatCheckboxModule, FormsModule, InfoSectionModule, ReactiveFormsModule, TranslateModule], + exports: [ProviderOptionsComponent], +}) +export class ProviderOptionsModule {} diff --git a/console/src/app/modules/providers/provider-google/provider-google-routing.module.ts b/console/src/app/modules/providers/provider-google/provider-google-routing.module.ts new file mode 100644 index 0000000000..aa03e3bb98 --- /dev/null +++ b/console/src/app/modules/providers/provider-google/provider-google-routing.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { ProviderGoogleComponent } from './provider-google.component'; + +const routes: Routes = [ + { + path: '', + component: ProviderGoogleComponent, + data: { animation: 'DetailPage' }, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class ProviderGoogleRoutingModule {} diff --git a/console/src/app/modules/providers/provider-google/provider-google.component.html b/console/src/app/modules/providers/provider-google/provider-google.component.html new file mode 100644 index 0000000000..d49717dccf --- /dev/null +++ b/console/src/app/modules/providers/provider-google/provider-google.component.html @@ -0,0 +1,93 @@ + +
+
+ +

{{ 'IDP.CREATE.GOOGLE.TITLE' | translate }}

+ +
+ +

+ {{ !provider ? ('IDP.CREATE.GOOGLE.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }} +

+ +
+
+ + {{ 'IDP.CLIENTID' | translate }} + + + + {{ + 'IDP.UPDATECLIENTSECRET' | translate + }} + + {{ 'IDP.CLIENTSECRET' | translate }} + + + +
+

{{ 'IDP.OPTIONAL' | translate }}

+ +
+
+
+
+ + {{ 'IDP.SCOPESLIST' | translate }} + + + + +
+ + + + + {{ scope }} cancel + + + +
+ + + {{ 'IDP.NAME' | translate }} + + {{ 'IDP.NAMEHINT' | translate }} + + + +
+
+ +
+ +
+ +
+
diff --git a/console/src/app/modules/providers/provider-google/provider-google.component.scss b/console/src/app/modules/providers/provider-google/provider-google.component.scss new file mode 100644 index 0000000000..ce8b8dc9d1 --- /dev/null +++ b/console/src/app/modules/providers/provider-google/provider-google.component.scss @@ -0,0 +1,87 @@ +.desc { + font-size: 14px; +} + +.google-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; + + .name-hint { + font-size: 12px; + } + + .mat-chip-input { + width: 100%; + margin: 0; + } + + .chip { + border-radius: 0.5rem; + height: 40px; + } + + @media only screen and (max-width: 450px) { + max-width: none; + } + } + + .google-content { + .desc { + margin-bottom: 1rem; + } + + .idp-scopes { + padding-bottom: 0.5rem; + + .flex-line { + display: flex; + align-items: flex-start; + max-width: 400px; + + .formfield { + flex: 1; + } + + .scope-add-button { + margin-top: 1.75rem; + } + } + } + } +} + +.google-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; + } +} diff --git a/console/src/app/modules/providers/provider-google/provider-google.component.spec.ts b/console/src/app/modules/providers/provider-google/provider-google.component.spec.ts new file mode 100644 index 0000000000..3b6fdadce3 --- /dev/null +++ b/console/src/app/modules/providers/provider-google/provider-google.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { ProviderGoogleComponent } from './provider-google.component'; + +describe('ProviderGoogleComponent', () => { + let component: ProviderGoogleComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ProviderGoogleComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ProviderGoogleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/providers/provider-google/provider-google.component.ts b/console/src/app/modules/providers/provider-google/provider-google.component.ts new file mode 100644 index 0000000000..b24c38159d --- /dev/null +++ b/console/src/app/modules/providers/provider-google/provider-google.component.ts @@ -0,0 +1,268 @@ +import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; +import { Location } from '@angular/common'; +import { Component, Injector, Type } from '@angular/core'; +import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; +import { ActivatedRoute, Router } from '@angular/router'; +import { take } from 'rxjs'; +import { + AddGoogleProviderRequest as AdminAddGoogleProviderRequest, + GetProviderByIDRequest as AdminGetProviderByIDRequest, + UpdateGoogleProviderRequest as AdminUpdateGoogleProviderRequest, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb'; +import { + AddGoogleProviderRequest as MgmtAddGoogleProviderRequest, + GetProviderByIDRequest as MgmtGetProviderByIDRequest, + UpdateGoogleProviderRequest as MgmtUpdateGoogleProviderRequest, +} 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-google', + templateUrl: './provider-google.component.html', + styleUrls: ['./provider-google.component.scss'], +}) +export class ProviderGoogleComponent { + 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!: FormGroup; + + public loading: boolean = false; + + public provider?: Provider.AsObject; + public updateClientSecret: boolean = false; + + constructor( + private router: Router, + private route: ActivatedRoute, + private toast: ToastService, + private injector: Injector, + private _location: Location, + private breadcrumbService: BreadcrumbService, + ) { + this.form = new FormGroup({ + name: new FormControl('', []), + clientId: new FormControl('', [Validators.required]), + clientSecret: new FormControl('', [Validators.required]), + scopesList: new FormControl(['openid', 'profile', 'email'], []), + }); + + this.route.data.pipe(take(1)).subscribe((data) => { + this.serviceType = data.serviceType; + + switch (this.serviceType) { + case PolicyComponentServiceType.MGMT: + this.service = this.injector.get(ManagementService as Type); + + const bread: Breadcrumb = { + type: BreadcrumbType.ORG, + routerLink: ['/org'], + }; + + this.breadcrumbService.setBreadcrumb([bread]); + break; + case PolicyComponentServiceType.ADMIN: + this.service = this.injector.get(AdminService as Type); + + const iamBread = new Breadcrumb({ + type: BreadcrumbType.ORG, + name: 'Instance', + routerLink: ['/instance'], + }); + this.breadcrumbService.setBreadcrumb([iamBread]); + break; + } + + this.id = this.route.snapshot.paramMap.get('id'); + if (this.id) { + this.clientSecret?.setValidators([]); + this.getData(this.id); + } + }); + } + + 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?.google) { + this.form.patchValue(this.provider.config.google); + this.name?.setValue(this.provider.name); + } + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } + + public submitForm(): void { + this.provider ? this.updateGoogleProvider() : this.addGoogleProvider(); + } + + public addGoogleProvider(): void { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtAddGoogleProviderRequest(); + + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setClientSecret(this.clientSecret?.value); + req.setScopesList(this.scopesList?.value); + req.setProviderOptions(this.options); + + this.loading = true; + (this.service as ManagementService) + .addGoogleProvider(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 AdminAddGoogleProviderRequest(); + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setClientSecret(this.clientSecret?.value); + req.setScopesList(this.scopesList?.value); + req.setProviderOptions(this.options); + + this.loading = true; + (this.service as AdminService) + .addGoogleProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.router.navigate(['/settings'], { queryParams: { id: 'idp' } }); + }, 2000); + }) + .catch((error) => { + this.loading = false; + this.toast.showError(error); + }); + } + } + + public updateGoogleProvider(): void { + if (this.provider) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtUpdateGoogleProviderRequest(); + req.setId(this.provider.id); + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setScopesList(this.scopesList?.value); + req.setProviderOptions(this.options); + + if (this.updateClientSecret) { + req.setClientSecret(this.clientSecret?.value); + } + + this.loading = true; + (this.service as ManagementService) + .updateGoogleProvider(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 AdminUpdateGoogleProviderRequest(); + req.setId(this.provider.id); + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setScopesList(this.scopesList?.value); + req.setProviderOptions(this.options); + + if (this.updateClientSecret) { + req.setClientSecret(this.clientSecret?.value); + } + + this.loading = true; + (this.service as AdminService) + .updateGoogleProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.router.navigate(['/settings'], { queryParams: { id: 'idp' } }); + }, 2000); + }) + .catch((error) => { + this.loading = false; + this.toast.showError(error); + }); + } + } + } + + public close(): void { + this._location.back(); + } + + public addScope(event: MatChipInputEvent): void { + const input = event.chipInput?.inputElement; + const value = event.value.trim(); + + if (value !== '') { + if (this.scopesList?.value) { + this.scopesList.value.push(value); + if (input) { + input.value = ''; + } + } + } + } + + public removeScope(uri: string): void { + if (this.scopesList?.value) { + const index = this.scopesList.value.indexOf(uri); + + if (index !== undefined && index >= 0) { + this.scopesList.value.splice(index, 1); + } + } + } + + public get name(): AbstractControl | null { + return this.form.get('name'); + } + + public get clientId(): AbstractControl | null { + return this.form.get('clientId'); + } + + public get clientSecret(): AbstractControl | null { + return this.form.get('clientSecret'); + } + + public get scopesList(): AbstractControl | null { + return this.form.get('scopesList'); + } +} diff --git a/console/src/app/modules/idp/idp.module.ts b/console/src/app/modules/providers/provider-google/provider-google.module.ts similarity index 52% rename from console/src/app/modules/idp/idp.module.ts rename to console/src/app/modules/providers/provider-google/provider-google.module.ts index 4bb50d7f16..e0af1a8030 100644 --- a/console/src/app/modules/idp/idp.module.ts +++ b/console/src/app/modules/providers/provider-google/provider-google.module.ts @@ -5,47 +5,39 @@ 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 { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu'; +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 { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; -import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module'; import { InputModule } from 'src/app/modules/input/input.module'; -import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; -import { CardModule } from '../card/card.module'; -import { InfoRowModule } from '../info-row/info-row.module'; -import { InfoSectionModule } from '../info-section/info-section.module'; -import { TopViewModule } from '../top-view/top-view.module'; -import { WarnDialogModule } from '../warn-dialog/warn-dialog.module'; -import { IdpRoutingModule } from './idp-routing.module'; -import { IdpComponent } from './idp.component'; +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 { ProviderGoogleRoutingModule } from './provider-google-routing.module'; +import { ProviderGoogleComponent } from './provider-google.component'; @NgModule({ - declarations: [IdpComponent], + declarations: [ProviderGoogleComponent], imports: [ + ProviderGoogleRoutingModule, CommonModule, - IdpRoutingModule, FormsModule, ReactiveFormsModule, + CreateLayoutModule, + InfoSectionModule, InputModule, MatButtonModule, - WarnDialogModule, - MatIconModule, - InfoSectionModule, - MatMenuModule, - TopViewModule, - MatTooltipModule, MatSelectModule, - CardModule, - TranslateModule, - MatCheckboxModule, - InfoRowModule, + MatIconModule, MatChipsModule, - HasRoleModule, - HasRolePipeModule, - DetailLayoutModule, + CardModule, + MatCheckboxModule, + MatTooltipModule, + TranslateModule, + ProviderOptionsModule, + MatLegacyProgressSpinnerModule, ], }) -export default class IdpModule {} +export default class ProviderGoogleModule {} diff --git a/console/src/app/modules/idp/idp-routing.module.ts b/console/src/app/modules/providers/provider-jwt/provider-jwt-routing.module.ts similarity index 65% rename from console/src/app/modules/idp/idp-routing.module.ts rename to console/src/app/modules/providers/provider-jwt/provider-jwt-routing.module.ts index a3b2f62e01..35f580044b 100644 --- a/console/src/app/modules/idp/idp-routing.module.ts +++ b/console/src/app/modules/providers/provider-jwt/provider-jwt-routing.module.ts @@ -1,12 +1,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; - -import { IdpComponent } from './idp.component'; +import { ProviderJWTComponent } from './provider-jwt.component'; const routes: Routes = [ { path: '', - component: IdpComponent, + component: ProviderJWTComponent, data: { animation: 'DetailPage' }, }, ]; @@ -15,4 +14,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) -export class IdpRoutingModule {} +export class ProviderJWTCreateRoutingModule {} diff --git a/console/src/app/modules/providers/provider-jwt/provider-jwt.component.html b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.html new file mode 100644 index 0000000000..c8d9cc99eb --- /dev/null +++ b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.html @@ -0,0 +1,60 @@ + +
+
+
+ +

{{ 'IDP.CREATE.JWT.DESCRIPTION' | translate }}

+ +
+
+ + {{ 'IDP.NAME' | translate }} + + + + {{ 'IDP.JWT.HEADERNAME' | translate }} + + + + {{ 'IDP.ISSUER' | translate }} + + + + {{ 'IDP.JWT.JWTENDPOINT' | translate }} + + + + {{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }} + + +
+ +
+

{{ 'IDP.OPTIONAL' | translate }}

+ +
+
+ +
+ +
+ +
+ +
+
diff --git a/console/src/app/modules/idp-create/idp-create.component.scss b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.scss similarity index 66% rename from console/src/app/modules/idp-create/idp-create.component.scss rename to console/src/app/modules/providers/provider-jwt/provider-jwt.component.scss index c8495334d0..91a8d06ed4 100644 --- a/console/src/app/modules/idp-create/idp-create.component.scss +++ b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.scss @@ -2,25 +2,23 @@ font-size: 14px; } -.add-line-btn { - margin-bottom: 1rem; -} +.jwt-create-content { + .title-row { + display: flex; + align-items: center; -.first-step-actions { - margin-top: 1rem; -} + .idp-logo { + height: 36px; + width: 36px; + margin-right: 1rem; + flex-shrink: 0; + } -.auto-reg-info { - display: block; - width: 100%; - max-width: 400px; - - .auto-reg-desc { - margin: 0 0 1rem 0; + h1 { + margin: 0 1rem 0 0; + } } -} -.idp-create-content { .formfield { display: block; max-width: 400px; @@ -40,7 +38,7 @@ } } - .idp-content { + .jwt-content { .desc { margin-bottom: 1rem; } @@ -67,17 +65,21 @@ } } -.idp-create-actions { +.jwt-create-actions { display: flex; margin-top: 1rem; - button[mat-stroked-button] { - border-radius: 0.5rem; - margin-right: 1rem; - } - button[mat-raised-button] { border-radius: 0.5rem; - margin-right: 1rem; + padding: 0.5rem 4rem; + } +} + +.optional-h-wrapper { + display: flex; + align-items: center; + + h2 { + margin-right: 0.25rem; } } diff --git a/console/src/app/modules/idp/idp.component.spec.ts b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.spec.ts similarity index 54% rename from console/src/app/modules/idp/idp.component.spec.ts rename to console/src/app/modules/providers/provider-jwt/provider-jwt.component.spec.ts index 41b2c39e14..5da537f3c1 100644 --- a/console/src/app/modules/idp/idp.component.spec.ts +++ b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.spec.ts @@ -1,19 +1,19 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { IdpComponent } from './idp.component'; +import { ProviderJWTComponent } from './provider-jwt.component'; -describe('IdComponent', () => { - let component: IdpComponent; - let fixture: ComponentFixture; +describe('ProviderJWTComponent', () => { + let component: ProviderJWTComponent; + let fixture: ComponentFixture; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [IdpComponent], + declarations: [ProviderJWTComponent], }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(IdpComponent); + fixture = TestBed.createComponent(ProviderJWTComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/console/src/app/modules/providers/provider-jwt/provider-jwt.component.ts b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.ts new file mode 100644 index 0000000000..35a938114b --- /dev/null +++ b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.ts @@ -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); + + const bread: Breadcrumb = { + type: BreadcrumbType.ORG, + routerLink: ['/org'], + }; + + breadcrumbService.setBreadcrumb([bread]); + break; + case PolicyComponentServiceType.ADMIN: + this.service = this.injector.get(AdminService as Type); + + 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'); + } +} diff --git a/console/src/app/modules/providers/provider-jwt/provider-jwt.module.ts b/console/src/app/modules/providers/provider-jwt/provider-jwt.module.ts new file mode 100644 index 0000000000..2231cf807a --- /dev/null +++ b/console/src/app/modules/providers/provider-jwt/provider-jwt.module.ts @@ -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 {} diff --git a/console/src/app/modules/idp-create/idp-create-routing.module.ts b/console/src/app/modules/providers/provider-oidc/provider-oidc-routing.module.ts similarity index 65% rename from console/src/app/modules/idp-create/idp-create-routing.module.ts rename to console/src/app/modules/providers/provider-oidc/provider-oidc-routing.module.ts index 77fb23f96c..c42248bd33 100644 --- a/console/src/app/modules/idp-create/idp-create-routing.module.ts +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc-routing.module.ts @@ -1,12 +1,12 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { IdpCreateComponent } from './idp-create.component'; +import { ProviderOIDCComponent } from './provider-oidc.component'; const routes: Routes = [ { path: '', - component: IdpCreateComponent, + component: ProviderOIDCComponent, data: { animation: 'DetailPage' }, }, ]; @@ -15,4 +15,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) -export class IdpCreateRoutingModule {} +export class ProviderOIDCRoutingModule {} diff --git a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.html b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.html new file mode 100644 index 0000000000..79bcfdfd8c --- /dev/null +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.html @@ -0,0 +1,96 @@ + +
+
+
+ +

{{ 'IDP.CREATE.OIDC.DESCRIPTION' | translate }}

+ +
+
+ + {{ 'IDP.NAME' | translate }} + + + + {{ 'IDP.ISSUER' | translate }} + + +
+ +
+ + {{ 'IDP.CLIENTID' | translate }} + + + + {{ + 'IDP.UPDATECLIENTSECRET' | translate + }} + + {{ 'IDP.CLIENTSECRET' | translate }} + + + +
+

{{ 'IDP.OPTIONAL' | translate }}

+ +
+
+
+
+ + {{ 'IDP.SCOPESLIST' | translate }} + + + + +
+ + + + + {{ scope }} cancel + + + +
+ + +
+
+ +
+ +
+ +
+
diff --git a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.scss b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.scss new file mode 100644 index 0000000000..f0af9c7f12 --- /dev/null +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.scss @@ -0,0 +1,84 @@ +.desc { + font-size: 14px; +} + +.oidc-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; + } + } + + .oidc-content { + .desc { + margin-bottom: 1rem; + } + + .idp-scopes { + padding-bottom: 0.5rem; + + .flex-line { + display: flex; + align-items: flex-start; + max-width: 400px; + + .formfield { + flex: 1; + } + + .scope-add-button { + margin-top: 1.75rem; + } + } + } + } +} + +.oidc-create-actions { + display: flex; + margin-top: 1rem; + + button[mat-raised-button] { + border-radius: 0.5rem; + margin-right: 1rem; + padding: 0.5rem 4rem; + } +} + +.optional-h-wrapper { + display: flex; + align-items: center; + + h2 { + margin-right: 0.25rem; + } +} diff --git a/console/src/app/modules/idp-create/idp-create.component.spec.ts b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.spec.ts similarity index 54% rename from console/src/app/modules/idp-create/idp-create.component.spec.ts rename to console/src/app/modules/providers/provider-oidc/provider-oidc.component.spec.ts index 8ce12d21aa..36c10c671f 100644 --- a/console/src/app/modules/idp-create/idp-create.component.spec.ts +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.spec.ts @@ -1,19 +1,19 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { IdpCreateComponent } from './idp-create.component'; +import { ProviderOIDCComponent } from './provider-oidc.component'; -describe('IdpCreateComponent', () => { - let component: IdpCreateComponent; - let fixture: ComponentFixture; +describe('ProviderOIDCComponent', () => { + let component: ProviderOIDCComponent; + let fixture: ComponentFixture; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [IdpCreateComponent], + declarations: [ProviderOIDCComponent], }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(IdpCreateComponent); + fixture = TestBed.createComponent(ProviderOIDCComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts new file mode 100644 index 0000000000..ab97bb4bc6 --- /dev/null +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts @@ -0,0 +1,267 @@ +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 { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; +import { ActivatedRoute, Router } from '@angular/router'; +import { take } from 'rxjs'; +import { + AddGenericOIDCProviderRequest as AdminAddGenericOIDCProviderRequest, + GetProviderByIDRequest as AdminGetProviderByIDRequest, + UpdateGenericOIDCProviderRequest as AdminUpdateGenericOIDCProviderRequest, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb'; +import { + AddGenericOIDCProviderRequest as MgmtAddGenericOIDCProviderRequest, + GetProviderByIDRequest as MgmtGetProviderByIDRequest, + UpdateGenericOIDCProviderRequest as MgmtUpdateGenericOIDCProviderRequest, +} 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-oidc', + templateUrl: './provider-oidc.component.html', + styleUrls: ['./provider-oidc.component.scss'], +}) +export class ProviderOIDCComponent { + public showOptional: boolean = false; + public options: Options = new Options(); + + public id: string | null = ''; + public updateClientSecret: boolean = false; + public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; + private service!: ManagementService | AdminService; + public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; + public oidcFormGroup!: 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.oidcFormGroup = new UntypedFormGroup({ + name: new UntypedFormControl('', [Validators.required]), + clientId: new UntypedFormControl('', [Validators.required]), + clientSecret: new UntypedFormControl('', [Validators.required]), + issuer: new UntypedFormControl('', [Validators.required]), + scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []), + }); + + this.route.data.pipe(take(1)).subscribe((data) => { + this.serviceType = data.serviceType; + + switch (this.serviceType) { + case PolicyComponentServiceType.MGMT: + this.service = this.injector.get(ManagementService as Type); + + const bread: Breadcrumb = { + type: BreadcrumbType.ORG, + routerLink: ['/org'], + }; + + breadcrumbService.setBreadcrumb([bread]); + break; + case PolicyComponentServiceType.ADMIN: + this.service = this.injector.get(AdminService as Type); + + 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.clientSecret?.setValidators([]); + this.getData(this.id); + } + }); + } + + private getData(id: string): void { + this.loading = true; + 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?.oidc) { + this.oidcFormGroup.patchValue(this.provider.config.oidc); + this.name?.setValue(this.provider.name); + } + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } + + public submitForm(): void { + this.provider ? this.updateGenericOIDCProvider() : this.addGenericOIDCProvider(); + } + + public addGenericOIDCProvider(): void { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtAddGenericOIDCProviderRequest(); + + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setClientSecret(this.clientSecret?.value); + req.setIssuer(this.issuer?.value); + req.setScopesList(this.scopesList?.value); + + this.loading = true; + (this.service as ManagementService) + .addGenericOIDCProvider(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 AdminAddGenericOIDCProviderRequest(); + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setClientSecret(this.clientSecret?.value); + req.setIssuer(this.issuer?.value); + req.setScopesList(this.scopesList?.value); + + this.loading = true; + (this.service as AdminService) + .addGenericOIDCProvider(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 updateGenericOIDCProvider(): void { + if (this.provider) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtUpdateGenericOIDCProviderRequest(); + req.setId(this.provider.id); + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setClientSecret(this.clientSecret?.value); + req.setIssuer(this.issuer?.value); + req.setScopesList(this.scopesList?.value); + + this.loading = true; + (this.service as ManagementService) + .updateGenericOIDCProvider(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 AdminUpdateGenericOIDCProviderRequest(); + req.setId(this.provider.id); + req.setName(this.name?.value); + req.setClientId(this.clientId?.value); + req.setClientSecret(this.clientSecret?.value); + req.setIssuer(this.issuer?.value); + req.setScopesList(this.scopesList?.value); + + this.loading = true; + (this.service as AdminService) + .updateGenericOIDCProvider(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 addScope(event: MatChipInputEvent): void { + const input = event.chipInput?.inputElement; + const value = event.value.trim(); + + if (value !== '') { + if (this.scopesList?.value) { + this.scopesList.value.push(value); + if (input) { + input.value = ''; + } + } + } + } + + public removeScope(uri: string): void { + if (this.scopesList?.value) { + const index = this.scopesList.value.indexOf(uri); + + if (index !== undefined && index >= 0) { + this.scopesList.value.splice(index, 1); + } + } + } + + public get name(): AbstractControl | null { + return this.oidcFormGroup.get('name'); + } + + public get clientId(): AbstractControl | null { + return this.oidcFormGroup.get('clientId'); + } + + public get clientSecret(): AbstractControl | null { + return this.oidcFormGroup.get('clientSecret'); + } + + public get issuer(): AbstractControl | null { + return this.oidcFormGroup.get('issuer'); + } + + public get scopesList(): AbstractControl | null { + return this.oidcFormGroup.get('scopesList'); + } +} diff --git a/console/src/app/modules/idp-create/idp-create.module.ts b/console/src/app/modules/providers/provider-oidc/provider-oidc.module.ts similarity index 61% rename from console/src/app/modules/idp-create/idp-create.module.ts rename to console/src/app/modules/providers/provider-oidc/provider-oidc.module.ts index 9370f39bc3..3f55a8a132 100644 --- a/console/src/app/modules/idp-create/idp-create.module.ts +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.module.ts @@ -5,23 +5,23 @@ 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 { MatLegacyProgressBarModule as MatProgressBarModule } from '@angular/material/legacy-progress-bar'; +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 { IdpCreateRoutingModule } from './idp-create-routing.module'; -import { IdpCreateComponent } from './idp-create.component'; -import { IdpTypeRadioComponent } from './idp-type-radio/idp-type-radio.component'; +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 { ProviderOIDCRoutingModule } from './provider-oidc-routing.module'; +import { ProviderOIDCComponent } from './provider-oidc.component'; @NgModule({ - declarations: [IdpCreateComponent, IdpTypeRadioComponent], + declarations: [ProviderOIDCComponent], imports: [ - IdpCreateRoutingModule, + ProviderOIDCRoutingModule, CommonModule, FormsModule, ReactiveFormsModule, @@ -36,7 +36,8 @@ import { IdpTypeRadioComponent } from './idp-type-radio/idp-type-radio.component MatCheckboxModule, MatTooltipModule, TranslateModule, - MatProgressBarModule, + ProviderOptionsModule, + MatLegacyProgressSpinnerModule, ], }) -export default class IdpCreateModule {} +export default class ProviderOIDCModule {} diff --git a/console/src/app/pages/instance/instance-routing.module.ts b/console/src/app/pages/instance/instance-routing.module.ts index ffeb5c904a..956467ed5b 100644 --- a/console/src/app/pages/instance/instance-routing.module.ts +++ b/console/src/app/pages/instance/instance-routing.module.ts @@ -24,25 +24,51 @@ const routes: Routes = [ }, }, { - path: 'idp', + path: 'provider', + canActivate: [AuthGuard, RoleGuard], + data: { + roles: ['iam.idp.write'], + serviceType: PolicyComponentServiceType.ADMIN, + }, children: [ { - path: 'create', - loadChildren: () => import('src/app/modules/idp-create/idp-create.module'), - canActivate: [AuthGuard, RoleGuard], - data: { - roles: ['iam.idp.write'], - serviceType: PolicyComponentServiceType.ADMIN, - }, + path: 'oidc', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-oidc/provider-oidc.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-oidc/provider-oidc.module'), + }, + ], }, { - path: ':id', - loadChildren: () => import('src/app/modules/idp/idp.module'), - canActivate: [AuthGuard, RoleGuard], - data: { - roles: ['iam.idp.read'], - serviceType: PolicyComponentServiceType.ADMIN, - }, + path: 'jwt', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-jwt/provider-jwt.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-jwt/provider-jwt.module'), + }, + ], + }, + { + path: 'google', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-google/provider-google.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-google/provider-google.module'), + }, + ], }, ], }, diff --git a/console/src/app/pages/orgs/org-routing.module.ts b/console/src/app/pages/orgs/org-routing.module.ts index 09f8211539..93debd337f 100644 --- a/console/src/app/pages/orgs/org-routing.module.ts +++ b/console/src/app/pages/orgs/org-routing.module.ts @@ -1,38 +1,65 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { AuthGuard } from 'src/app/guards/auth.guard'; import { RoleGuard } from 'src/app/guards/role.guard'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { OrgDetailComponent } from './org-detail/org-detail.component'; const routes: Routes = [ - { - path: 'idp', - children: [ - { - path: 'create', - loadChildren: () => import('src/app/modules/idp-create/idp-create.module'), - canActivate: [RoleGuard], - data: { - roles: ['org.idp.write'], - serviceType: PolicyComponentServiceType.MGMT, - }, - }, - { - path: ':id', - loadChildren: () => import('src/app/modules/idp/idp.module'), - canActivate: [RoleGuard], - data: { - roles: ['org.idp.read'], - serviceType: PolicyComponentServiceType.MGMT, - }, - }, - ], - }, { path: 'members', loadChildren: () => import('./org-members/org-members.module'), }, + { + path: 'provider', + canActivate: [AuthGuard, RoleGuard], + data: { + roles: ['org.idp.write'], + serviceType: PolicyComponentServiceType.MGMT, + }, + children: [ + { + path: 'oidc', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-oidc/provider-oidc.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-oidc/provider-oidc.module'), + }, + ], + }, + { + path: 'jwt', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-jwt/provider-jwt.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-jwt/provider-jwt.module'), + }, + ], + }, + { + path: 'google', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-google/provider-google.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-google/provider-google.module'), + }, + ], + }, + ], + }, { path: '', component: OrgDetailComponent, diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index 379e74e848..78fd6be425 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -8,18 +8,20 @@ import { ActivateSMSProviderResponse, AddCustomDomainPolicyRequest, AddCustomOrgIAMPolicyResponse, + AddGenericOIDCProviderRequest, + AddGenericOIDCProviderResponse, + AddGoogleProviderRequest, + AddGoogleProviderResponse, AddIAMMemberRequest, AddIAMMemberResponse, AddIDPToLoginPolicyRequest, AddIDPToLoginPolicyResponse, - AddJWTIDPRequest, - AddJWTIDPResponse, + AddJWTProviderRequest, + AddJWTProviderResponse, AddMultiFactorToLoginPolicyRequest, AddMultiFactorToLoginPolicyResponse, AddNotificationPolicyRequest, AddNotificationPolicyResponse, - AddOIDCIDPRequest, - AddOIDCIDPResponse, AddOIDCSettingsRequest, AddOIDCSettingsResponse, AddSecondFactorToLoginPolicyRequest, @@ -32,6 +34,8 @@ import { DeactivateIDPResponse, DeactivateSMSProviderRequest, DeactivateSMSProviderResponse, + DeleteProviderRequest, + DeleteProviderResponse, GetCustomDomainClaimedMessageTextRequest, GetCustomDomainClaimedMessageTextResponse, GetCustomDomainPolicyRequest, @@ -72,8 +76,6 @@ import { GetDomainPolicyResponse, GetFileSystemNotificationProviderRequest, GetFileSystemNotificationProviderResponse, - GetIDPByIDRequest, - GetIDPByIDResponse, GetLabelPolicyRequest, GetLabelPolicyResponse, GetLockoutPolicyRequest, @@ -96,6 +98,8 @@ import { GetPreviewLabelPolicyResponse, GetPrivacyPolicyRequest, GetPrivacyPolicyResponse, + GetProviderByIDRequest, + GetProviderByIDResponse, GetSecretGeneratorRequest, GetSecretGeneratorResponse, GetSecurityPolicyRequest, @@ -106,7 +110,6 @@ import { GetSMTPConfigResponse, GetSupportedLanguagesRequest, GetSupportedLanguagesResponse, - IDPQuery, ListAggregateTypesRequest, ListAggregateTypesResponse, ListEventsRequest, @@ -119,14 +122,14 @@ import { ListIAMMemberRolesResponse, ListIAMMembersRequest, ListIAMMembersResponse, - ListIDPsRequest, - ListIDPsResponse, ListLoginPolicyIDPsRequest, ListLoginPolicyIDPsResponse, ListLoginPolicyMultiFactorsRequest, ListLoginPolicyMultiFactorsResponse, ListLoginPolicySecondFactorsRequest, ListLoginPolicySecondFactorsResponse, + ListProvidersRequest, + ListProvidersResponse, ListSecretGeneratorsRequest, ListSecretGeneratorsResponse, ListSMSProvidersRequest, @@ -191,14 +194,14 @@ import { UpdateCustomDomainPolicyResponse, UpdateDomainPolicyRequest, UpdateDomainPolicyResponse, + UpdateGenericOIDCProviderRequest, + UpdateGenericOIDCProviderResponse, + UpdateGoogleProviderRequest, + UpdateGoogleProviderResponse, UpdateIAMMemberRequest, UpdateIAMMemberResponse, - UpdateIDPJWTConfigRequest, - UpdateIDPJWTConfigResponse, - UpdateIDPOIDCConfigRequest, - UpdateIDPOIDCConfigResponse, - UpdateIDPRequest, - UpdateIDPResponse, + UpdateJWTProviderRequest, + UpdateJWTProviderResponse, UpdateLabelPolicyRequest, UpdateLabelPolicyResponse, UpdateLockoutPolicyRequest, @@ -871,41 +874,6 @@ export class AdminService { return this.grpcService.admin.listLoginPolicyIDPs(req, null).then((resp) => resp.toObject()); } - public listIDPs(limit?: number, offset?: number, queriesList?: IDPQuery[]): Promise { - const req = new ListIDPsRequest(); - const query = new ListQuery(); - - if (limit) { - query.setLimit(limit); - } - if (offset) { - query.setOffset(offset); - } - if (queriesList) { - req.setQueriesList(queriesList); - } - req.setQuery(query); - return this.grpcService.admin.listIDPs(req, null).then((resp) => resp.toObject()); - } - - public getIDPByID(id: string): Promise { - const req = new GetIDPByIDRequest(); - req.setId(id); - return this.grpcService.admin.getIDPByID(req, null).then((resp) => resp.toObject()); - } - - public updateIDP(req: UpdateIDPRequest): Promise { - return this.grpcService.admin.updateIDP(req, null).then((resp) => resp.toObject()); - } - - public addOIDCIDP(req: AddOIDCIDPRequest): Promise { - return this.grpcService.admin.addOIDCIDP(req, null).then((resp) => resp.toObject()); - } - - public updateIDPOIDCConfig(req: UpdateIDPOIDCConfigRequest): Promise { - return this.grpcService.admin.updateIDPOIDCConfig(req, null).then((resp) => resp.toObject()); - } - public removeIDP(id: string): Promise { const req = new RemoveIDPRequest(); req.setIdpId(id); @@ -924,12 +892,44 @@ export class AdminService { return this.grpcService.admin.reactivateIDP(req, null).then((resp) => resp.toObject()); } - public addJWTIDP(req: AddJWTIDPRequest): Promise { - return this.grpcService.admin.addJWTIDP(req, null).then((resp) => resp.toObject()); + // idp templates + + public addGoogleProvider(req: AddGoogleProviderRequest): Promise { + return this.grpcService.admin.addGoogleProvider(req, null).then((resp) => resp.toObject()); } - public updateIDPJWTConfig(req: UpdateIDPJWTConfigRequest): Promise { - return this.grpcService.admin.updateIDPJWTConfig(req, null).then((resp) => resp.toObject()); + public updateGoogleProvider(req: UpdateGoogleProviderRequest): Promise { + return this.grpcService.admin.updateGoogleProvider(req, null).then((resp) => resp.toObject()); + } + + public addGenericOIDCProvider(req: AddGenericOIDCProviderRequest): Promise { + return this.grpcService.admin.addGenericOIDCProvider(req, null).then((resp) => resp.toObject()); + } + + public updateGenericOIDCProvider( + req: UpdateGenericOIDCProviderRequest, + ): Promise { + return this.grpcService.admin.updateGenericOIDCProvider(req, null).then((resp) => resp.toObject()); + } + + public addJWTProvider(req: AddJWTProviderRequest): Promise { + return this.grpcService.admin.addJWTProvider(req, null).then((resp) => resp.toObject()); + } + + public updateJWTProvider(req: UpdateJWTProviderRequest): Promise { + return this.grpcService.admin.updateJWTProvider(req, null).then((resp) => resp.toObject()); + } + + public deleteProvider(req: DeleteProviderRequest): Promise { + return this.grpcService.admin.deleteProvider(req, null).then((resp) => resp.toObject()); + } + + public listProviders(req: ListProvidersRequest): Promise { + return this.grpcService.admin.listProviders(req, null).then((resp) => resp.toObject()); + } + + public getProviderByID(req: GetProviderByIDRequest): Promise { + return this.grpcService.admin.getProviderByID(req, null).then((resp) => resp.toObject()); } public listIAMMembers( diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index 79e9a94560..db5e37b3df 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -3,7 +3,6 @@ import { SortDirection } from '@angular/material/sort'; import { Empty } from 'google-protobuf/google/protobuf/empty_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { BehaviorSubject } from 'rxjs'; - import { AppQuery } from '../proto/generated/zitadel/app_pb'; import { KeyType } from '../proto/generated/zitadel/auth_n_key_pb'; import { ChangeQuery } from '../proto/generated/zitadel/change_pb'; @@ -30,10 +29,16 @@ import { AddCustomPasswordComplexityPolicyResponse, AddCustomPrivacyPolicyRequest, AddCustomPrivacyPolicyResponse, + AddGenericOIDCProviderRequest, + AddGenericOIDCProviderResponse, + AddGoogleProviderRequest, + AddGoogleProviderResponse, AddHumanUserRequest, AddHumanUserResponse, AddIDPToLoginPolicyRequest, AddIDPToLoginPolicyResponse, + AddJWTProviderRequest, + AddJWTProviderResponse, AddMachineKeyRequest, AddMachineKeyResponse, AddMachineUserRequest, @@ -44,12 +49,8 @@ import { AddOIDCAppResponse, AddOrgDomainRequest, AddOrgDomainResponse, - AddOrgJWTIDPRequest, - AddOrgJWTIDPResponse, AddOrgMemberRequest, AddOrgMemberResponse, - AddOrgOIDCIDPRequest, - AddOrgOIDCIDPResponse, AddOrgRequest, AddOrgResponse, AddPersonalAccessTokenRequest, @@ -96,6 +97,8 @@ import { DeactivateUserResponse, DeleteActionRequest, DeleteActionResponse, + DeleteProviderRequest, + DeleteProviderResponse, GenerateMachineSecretRequest, GenerateMachineSecretResponse, GenerateOrgDomainValidationRequest, @@ -168,8 +171,6 @@ import { GetOIDCInformationResponse, GetOrgByDomainGlobalRequest, GetOrgByDomainGlobalResponse, - GetOrgIDPByIDRequest, - GetOrgIDPByIDResponse, GetPasswordAgePolicyRequest, GetPasswordAgePolicyResponse, GetPasswordComplexityPolicyRequest, @@ -182,6 +183,8 @@ import { GetProjectByIDResponse, GetProjectGrantByIDRequest, GetProjectGrantByIDResponse, + GetProviderByIDRequest, + GetProviderByIDResponse, GetSupportedLanguagesRequest, GetSupportedLanguagesResponse, GetUserByIDRequest, @@ -192,7 +195,6 @@ import { GetUserGrantByIDResponse, GetUserMetadataRequest, GetUserMetadataResponse, - IDPQuery, ListActionsRequest, ListActionsResponse, ListAppChangesRequest, @@ -226,8 +228,6 @@ import { ListOrgChangesResponse, ListOrgDomainsRequest, ListOrgDomainsResponse, - ListOrgIDPsRequest, - ListOrgIDPsResponse, ListOrgMemberRolesRequest, ListOrgMemberRolesResponse, ListOrgMembersRequest, @@ -254,6 +254,8 @@ import { ListProjectRolesResponse, ListProjectsRequest, ListProjectsResponse, + ListProvidersRequest, + ListProvidersResponse, ListUserChangesRequest, ListUserChangesResponse, ListUserGrantRequest, @@ -425,22 +427,22 @@ import { UpdateCustomPasswordComplexityPolicyResponse, UpdateCustomPrivacyPolicyRequest, UpdateCustomPrivacyPolicyResponse, + UpdateGenericOIDCProviderRequest, + UpdateGenericOIDCProviderResponse, + UpdateGoogleProviderRequest, + UpdateGoogleProviderResponse, UpdateHumanEmailRequest, UpdateHumanEmailResponse, UpdateHumanPhoneRequest, UpdateHumanPhoneResponse, UpdateHumanProfileRequest, UpdateHumanProfileResponse, + UpdateJWTProviderRequest, + UpdateJWTProviderResponse, UpdateMachineRequest, UpdateMachineResponse, UpdateOIDCAppConfigRequest, UpdateOIDCAppConfigResponse, - UpdateOrgIDPJWTConfigRequest, - UpdateOrgIDPJWTConfigResponse, - UpdateOrgIDPOIDCConfigRequest, - UpdateOrgIDPOIDCConfigResponse, - UpdateOrgIDPRequest, - UpdateOrgIDPResponse, UpdateOrgMemberRequest, UpdateOrgMemberResponse, UpdateOrgRequest, @@ -692,23 +694,6 @@ export class ManagementService { .then((resp) => resp.toObject()); } - public listOrgIDPs(limit?: number, offset?: number, queryList?: IDPQuery[]): Promise { - const req = new ListOrgIDPsRequest(); - const query = new ListQuery(); - - if (limit) { - query.setLimit(limit); - } - if (offset) { - query.setOffset(offset); - } - req.setQuery(query); - if (queryList) { - req.setQueriesList(queryList); - } - return this.grpcService.mgmt.listOrgIDPs(req, null).then((resp) => resp.toObject()); - } - public updateUserName(userId: string, username: string): Promise { const req = new UpdateUserNameRequest(); req.setUserId(userId); @@ -848,24 +833,6 @@ export class ManagementService { return this.grpcService.mgmt.listLoginPolicyIDPs(req, null).then((resp) => resp.toObject()); } - public getOrgIDPByID(id: string): Promise { - const req = new GetOrgIDPByIDRequest(); - req.setId(id); - return this.grpcService.mgmt.getOrgIDPByID(req, null).then((resp) => resp.toObject()); - } - - public updateOrgIDP(req: UpdateOrgIDPRequest): Promise { - return this.grpcService.mgmt.updateOrgIDP(req, null).then((resp) => resp.toObject()); - } - - public addOrgOIDCIDP(req: AddOrgOIDCIDPRequest): Promise { - return this.grpcService.mgmt.addOrgOIDCIDP(req, null).then((resp) => resp.toObject()); - } - - public updateOrgIDPOIDCConfig(req: UpdateOrgIDPOIDCConfigRequest): Promise { - return this.grpcService.mgmt.updateOrgIDPOIDCConfig(req, null).then((resp) => resp.toObject()); - } - public removeOrgIDP(idpId: string): Promise { const req = new RemoveOrgIDPRequest(); req.setIdpId(idpId); @@ -884,12 +851,44 @@ export class ManagementService { return this.grpcService.mgmt.reactivateOrgIDP(req, null).then((resp) => resp.toObject()); } - public addOrgJWTIDP(req: AddOrgJWTIDPRequest): Promise { - return this.grpcService.mgmt.addOrgJWTIDP(req, null).then((resp) => resp.toObject()); + // idp templates + + public addGoogleProvider(req: AddGoogleProviderRequest): Promise { + return this.grpcService.mgmt.addGoogleProvider(req, null).then((resp) => resp.toObject()); } - public updateOrgIDPJWTConfig(req: UpdateOrgIDPJWTConfigRequest): Promise { - return this.grpcService.mgmt.updateOrgIDPJWTConfig(req, null).then((resp) => resp.toObject()); + public updateGoogleProvider(req: UpdateGoogleProviderRequest): Promise { + return this.grpcService.mgmt.updateGoogleProvider(req, null).then((resp) => resp.toObject()); + } + + public addGenericOIDCProvider(req: AddGenericOIDCProviderRequest): Promise { + return this.grpcService.mgmt.addGenericOIDCProvider(req, null).then((resp) => resp.toObject()); + } + + public updateGenericOIDCProvider( + req: UpdateGenericOIDCProviderRequest, + ): Promise { + return this.grpcService.mgmt.updateGenericOIDCProvider(req, null).then((resp) => resp.toObject()); + } + + public addJWTProvider(req: AddJWTProviderRequest): Promise { + return this.grpcService.mgmt.addJWTProvider(req, null).then((resp) => resp.toObject()); + } + + public updateJWTProvider(req: UpdateJWTProviderRequest): Promise { + return this.grpcService.mgmt.updateJWTProvider(req, null).then((resp) => resp.toObject()); + } + + public deleteProvider(req: DeleteProviderRequest): Promise { + return this.grpcService.mgmt.deleteProvider(req, null).then((resp) => resp.toObject()); + } + + public listProviders(req: ListProvidersRequest): Promise { + return this.grpcService.mgmt.listProviders(req, null).then((resp) => resp.toObject()); + } + + public getProviderByID(req: GetProviderByIDRequest): Promise { + return this.grpcService.mgmt.getProviderByID(req, null).then((resp) => resp.toObject()); } public addHumanUser(req: AddHumanUserRequest): Promise { diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 616904578b..29fb227277 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -159,6 +159,7 @@ "NEXT": "Weiter", "MORE": "mehr", "STEP": "Schritt", + "COMINGSOON": "Coming soon", "TABLE": { "SHOWUSER": "Zeige Benutzer {{value}}" } @@ -1614,15 +1615,38 @@ "ACTIVETITLE": "Aktive Identitäts Provider" }, "CREATE": { - "TITLE": "Neuer Identitäts Provider", - "DESCRIPTION": "Wählen Sie einen der folgenden Typen von Identitäts Providern" + "TITLE": "Provider hinzufügen", + "DESCRIPTION": "Wähle einen der verfügbaren Provider.", + "STEPPERTITLE": "Provider hinzufügen", + "OIDC": { + "TITLE": "OIDC Provider", + "DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren OIDC-Provider ein." + }, + "JWT": { + "TITLE": "JWT Provider", + "DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren JWT-Provider ein." + }, + "GOOGLE": { + "TITLE": "Google Provider", + "DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren Google-Identitätsprovider ein." + } }, "DETAIL": { "TITLE": "Identitäts Provider", - "DESCRIPTION": "Generelle Konfiguration deines Identitäts Providers", + "DESCRIPTION": "Bearbeite deine Providerkonfiguration", "DATECREATED": "Erstellt", "DATECHANGED": "Geändert" }, + "OPTIONS": { + "ISAUTOCREATION": "Automatisches Erstellen", + "ISAUTOCREATION_DESC": "Legt fest ob ein Konto erstellt wird, falls es noch nicht existiert.", + "ISAUTOUPDATE": "Automatisches Update", + "ISAUTOUPDATE_DESC": "Legt fest ob Konten bei der erneuten Authentifizierung aktualisiert werden.", + "ISCREATIONALLOWED": "Account erstellen erlaubt", + "ISCREATIONALLOWED_DESC": "Legt fest, ob Konten erstellt werden können.", + "ISLINKINGALLOWED": "Account linking erlaubt", + "ISLINKINGALLOWED_DESC": "Legt fest, ob eine Identität mit einem bestehenden Konto verknüpft werden kann." + }, "OWNERTYPES": { "0": "unknown", "1": "Instanz", @@ -1637,18 +1661,10 @@ "1": "aktiv", "2": "inaktiv" }, - "MAPPINGFIELD": { - "1": "Preferred Username", - "2": "Email" - }, - "STYLE": "Style", - "STYLEFIELD": { - "0": "kein Styling", - "1": "Google" - }, + "NAMEHINT": "Wenn angegeben, wir er im Login interface angezeigt.", + "OPTIONAL": "optional", + "UPDATECLIENTSECRET": "Client Secret updaten", "ADD": "Identity Provider hinzufügen", - "AUTOREGISTER": "Automatische Registrierung", - "AUTOREGISTER_DESC": "Wenn aktiviert und noch kein Account vorhanden ist, wird einer für den entsprechenden Benutzer erstellt.", "TYPE": "Typ", "OWNER": "Besitzer", "ID": "ID", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 9fc3ab95bc..4d666d555d 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -160,6 +160,7 @@ "MORE": "more", "STEP": "Step", "SETUP": "Setup", + "COMINGSOON": "Coming soon", "TABLE": { "SHOWUSER": "Show user {{value}}" } @@ -1615,41 +1616,51 @@ "ACTIVETITLE": "Active Identity Providers" }, "CREATE": { - "TITLE": "New Identity Provider", - "DESCRIPTION": "Choose one of the following Identity Provider types." + "TITLE": "Add provider", + "DESCRIPTION": "Select one ore more of the following providers.", + "STEPPERTITLE": "Create Provider", + "OIDC": { + "TITLE": "OIDC Provider", + "DESCRIPTION": "Enter the required data for your OIDC provider." + }, + "JWT": { + "TITLE": "JWT Provider", + "DESCRIPTION": "Enter the required data for your JWT provider." + }, + "GOOGLE": { + "TITLE": "Google Provider", + "DESCRIPTION": "Enter the credentials for your Google Identity Provider" + } }, "DETAIL": { "TITLE": "Identity Provider", - "DESCRIPTION": "General Configuration of your identity provider.", + "DESCRIPTION": "Update your provider configuration", "DATECREATED": "Created", "DATECHANGED": "Changed" }, + "OPTIONS": { + "ISAUTOCREATION": "Automatic creation", + "ISAUTOCREATION_DESC": "If selected, an account will be created if it does not exist yet.", + "ISAUTOUPDATE": "Automatic update", + "ISAUTOUPDATE_DESC": "If selected, accounts are updated on reauthentication.", + "ISCREATIONALLOWED": "Account creation allowed", + "ISCREATIONALLOWED_DESC": "Determines whether accounts can be created.", + "ISLINKINGALLOWED": "Account linking allowed", + "ISLINKINGALLOWED_DESC": "Determines whether an identity can be linked to an existing account." + }, "OWNERTYPES": { "0": "unknown", "1": "Instance", "2": "Organization" }, - "TYPES": { - "0": "unknown", - "1": "OIDC", - "3": "JWT" - }, "STATES": { "1": "active", "2": "inactive" }, - "MAPPINGFIELD": { - "1": "Preferred Username", - "2": "Email" - }, - "STYLE": "Style", - "STYLEFIELD": { - "0": "No Styling", - "1": "Google" - }, + "NAMEHINT": "If specified it will be shown in the login interface.", + "OPTIONAL": "optional", + "UPDATECLIENTSECRET": "update client secret", "ADD": "Add Identity Provider", - "AUTOREGISTER": "Auto Register", - "AUTOREGISTER_DESC": "If selected and no account exists yet, one will be created.", "TYPE": "Type", "OWNER": "Owner", "ID": "ID", diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index e0c36be0bb..406226642e 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -159,6 +159,7 @@ "NEXT": "Suivant", "MORE": "plus", "STEP": "Étape", + "COMINGSOON": "Coming soon", "TABLE": { "SHOWUSER": "Afficher l'utilisateur{{value}}" } @@ -1614,15 +1615,38 @@ "ACTIVETITLE": "Fournisseurs d'identité actifs" }, "CREATE": { - "TITLE": "Nouveau fournisseur d'identité", - "DESCRIPTION": "Choisissez l'un des types de fournisseur d'identité suivants." + "TITLE": "Ajouter un fournisseur", + "DESCRIPTION": "Sélectionnez un ou plusieurs des fournisseurs suivants.", + "STEPPERTITLE": "Créer un fournisseur", + "OIDC": { + "TITLE": "Fournisseur OIDC", + "DESCRIPTION": "Entrez les données requises pour votre fournisseur OIDC." + }, + "JWT": { + "TITLE": "Fournisseur JWT", + "DESCRIPTION": "Entrez les données requises pour votre fournisseur JWT." + }, + "GOOGLE": { + "TITLE": "Fournisseur Google", + "DESCRIPTION": "Saisissez les informations d'identification de votre fournisseur d'identité Google" + } }, "DETAIL": { "TITLE": "Fournisseur d'identité", - "DESCRIPTION": "Configuration générale de votre fournisseur d'identité.", + "DESCRIPTION": "Mettez à jour la configuration de votre fournisseur", "DATECREATED": "Créé", "DATECHANGED": "Modifié" }, + "OPTIONS": { + "ISAUTOCREATION": "Création automatique", + "ISAUTOCREATION_DESC": "Détermine si un compte sera créé s'il n'existe pas déjà.", + "ISAUTOUPDATE": "est mise à jour automatiquement", + "ISAUTOUPDATE_DESC": "Si cette option est sélectionnée, les comptes sont mis à jour lors de la réauthentification.", + "ISCREATIONALLOWED": "la création est-elle autorisée", + "ISCREATIONALLOWED_DESC": "Détermine si des comptes peuvent être créés.", + "ISLINKINGALLOWED": "la liaison est-elle autorisée", + "ISLINKINGALLOWED_DESC": "Détermine si une identité peut être liée à un compte existant." + }, "OWNERTYPES": { "0": "inconnu", "1": "Instance", @@ -1637,18 +1661,10 @@ "1": "actif", "2": "inactif" }, - "MAPPINGFIELD": { - "1": "Nom d'utilisateur préféré", - "2": "Courriel" - }, - "STYLE": "Style", - "STYLEFIELD": { - "0": "Pas de style", - "1": "Google" - }, + "NAMEHINT": "Si elle est spécifiée, elle sera affichée dans l'interface de connexion.", + "OPTIONAL": "optionnel", + "UPDATECLIENTSECRET": "mise à jour du secret client", "ADD": "Ajouter un fournisseur d'identité", - "AUTOREGISTER": "Enregistrement automatique", - "AUTOREGISTER_DESC": "Si cette option est sélectionnée et qu'aucun compte n'existe encore, un compte sera créé.", "TYPE": "Type", "OWNER": "Propriétaire", "ID": "ID", diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 3b6f1b5df2..6618e8b3cb 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -159,6 +159,7 @@ "NEXT": "Avanti", "MORE": "azioni", "STEP": "Passo", + "COMINGSOON": "Coming soon", "TABLE": { "SHOWUSER": "Mostra utente {{value}}" } @@ -1615,15 +1616,38 @@ "ACTIVETITLE": "Identity Providers" }, "CREATE": { - "TITLE": "Nuovo IDP", - "DESCRIPTION": "Configura l'endpoint del tuo nuovo IDP." + "TITLE": "Aggiungi provider", + "DESCRIPTION": "Seleziona uno dei seguenti provider.", + "STEPPERTITLE": "Create Provider", + "OIDC": { + "TITLE": "OIDC Provider", + "DESCRIPTION": "Inserisci i dati necessari per il tuo provider OIDC." + }, + "JWT": { + "TITLE": "JWT Provider", + "DESCRIPTION": "Inserisci i dati necessari per il tuo provider JWT." + }, + "GOOGLE": { + "TITLE": "Google Provider", + "DESCRIPTION": "Inserisci i dati necessari per il tuo Google provider." + } }, "DETAIL": { "TITLE": "Identity Provider", - "DESCRIPTION": "Configurazione generale del tuo fornitore di identit\u00e0.", + "DESCRIPTION": "Aggiorna la configurazione del tuo provider", "DATECREATED": "Creato", "DATECHANGED": "Cambiato" }, + "OPTIONS": { + "ISAUTOCREATION": "Creazione automatica", + "ISAUTOCREATION_DESC": "Se selezionato, verrà creato un account se non esiste ancora.", + "ISAUTOUPDATE": "Aggiornamento automatico", + "ISAUTOUPDATE_DESC": "Se selezionato, gli account vengono aggiornati alla riautenticazione.", + "ISCREATIONALLOWED": "Creazione consentita", + "ISCREATIONALLOWED_DESC": "Determina se i conti possono essere creati.", + "ISLINKINGALLOWED": "Collegamento consentito", + "ISLINKINGALLOWED_DESC": "Determina se un'identità può essere collegata a un account esistente." + }, "OWNERTYPES": { "0": "sconosciuto", "1": "Istanza", @@ -1638,18 +1662,10 @@ "1": "attivo", "2": "inattivo" }, - "MAPPINGFIELD": { - "1": "Nome utente preferito", - "2": "Email" - }, - "STYLE": "Stile", - "STYLEFIELD": { - "0": "No Styling", - "1": "Google" - }, + "NAMEHINT": "Se specificato, verrà mostrato nell'interfaccia di accesso.", + "OPTIONAL": "opzionale", + "UPDATECLIENTSECRET": "Aggiorna secret", "ADD": "Aggiungi Identity Provider", - "AUTOREGISTER": "Registrazione automatica", - "AUTOREGISTER_DESC": "Se \u00e8 selezionato e non esiste ancora un account, ne verr\u00e0 creato uno.", "TYPE": "Tipo", "OWNER": "Owner", "ID": "ID", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index da16f884ee..e8e8135ad2 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -159,6 +159,7 @@ "NEXT": "Następny", "MORE": "więcej", "STEP": "Krok", + "COMINGSOON": "Coming soon", "TABLE": { "SHOWUSER": "Pokaż użytkownika {{value}}" } @@ -1614,15 +1615,38 @@ "ACTIVETITLE": "Aktywni dostawcy tożsamości" }, "CREATE": { - "TITLE": "Nowy dostawca tożsamości", - "DESCRIPTION": "Wybierz jeden z poniższych typów dostawcy tożsamości." + "TITLE": "Dodaj dostawcę", + "DESCRIPTION": "Wybierz jednego lub więcej z następujących dostawców.", + "STEPPERTITLE": "Utwórz dostawcę", + "OIDC": { + "TITLE": "OIDC Provider", + "DESCRIPTION": "Wprowadź wymagane dane dla swojego dostawcy OIDC." + }, + "JWT": { + "TITLE": "JWT Provider", + "DESCRIPTION": "Wprowadź wymagane dane dla swojego dostawcy JWT." + }, + "GOOGLE": { + "TITLE": "Google Provider", + "DESCRIPTION": "Wprowadź dane dla swojego dostawcy tożsamości Google" + } }, "DETAIL": { "TITLE": "Dostawca tożsamości", - "DESCRIPTION": "Ogólna konfiguracja dostawcy tożsamości.", + "DESCRIPTION": "Aktualizacja konfiguracji dostawcy", "DATECREATED": "Utworzono", "DATECHANGED": "Zmieniono" }, + "OPTIONS": { + "ISAUTOCREATION": "Automatyczne tworzenie", + "ISAUTOCREATION_DESC": "Jeśli zostanie wybrana, konto zostanie utworzone, jeśli jeszcze nie istnieje.", + "ISAUTOUPDATE": "Automatyczna aktualizacja", + "ISAUTOUPDATE_DESC": "Jeśli zaznaczone, konta są aktualizowane przy ponownym uwierzytelnianiu.", + "ISCREATIONALLOWED": "tworzenie dozwolone", + "ISCREATIONALLOWED_DESC": "Określa, czy można tworzyć konta.", + "ISLINKINGALLOWED": "dozwolone łączenie rachunków", + "ISLINKINGALLOWED_DESC": "Określa, czy tożsamość może być powiązana z istniejącym kontem." + }, "OWNERTYPES": { "0": "nieznany", "1": "Instancja", @@ -1637,18 +1661,10 @@ "1": "aktywny", "2": "nieaktywny" }, - "MAPPINGFIELD": { - "1": "Preferowana nazwa użytkownika", - "2": "E-mail" - }, - "STYLE": "Styl", - "STYLEFIELD": { - "0": "Bez stylizacji", - "1": "Google" - }, + "NAMEHINT": "Jeśli zostanie podany, będzie widoczny w interfejsie logowania.", + "OPTIONAL": "opcjonalnie", + "UPDATECLIENTSECRET": "aktualizacja tajemnicy klienta", "ADD": "Dodaj dostawcę tożsamości", - "AUTOREGISTER": "Automatyczna rejestracja", - "AUTOREGISTER_DESC": "Jeśli zaznaczone, a konto jeszcze nie istnieje, zostanie utworzone.", "TYPE": "Typ", "OWNER": "Właściciel", "ID": "ID", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 42b7c3c085..90ba3002be 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -159,6 +159,7 @@ "NEXT": "下一页", "MORE": "更多", "STEP": "Step", + "COMINGSOON": "Coming soon", "TABLE": { "SHOWUSER": "Show user {{value}}" } @@ -1613,15 +1614,38 @@ "ACTIVETITLE": "启用的身份提供者" }, "CREATE": { - "TITLE": "创建身份提供者", - "DESCRIPTION": "选择以下身份提供者类型之一。" + "TITLE": "创建供应商", + "DESCRIPTION": "选择以下一个或多个供应商。", + "STEPPERTITLE": "创建供应商", + "OIDC": { + "TITLE": "OIDC供应商", + "DESCRIPTION": "输入你的OIDC供应商的必要数据。" + }, + "JWT": { + "TITLE": "JWT供应商", + "DESCRIPTION": "输入你的JWT供应商所需的数据。" + }, + "GOOGLE": { + "TITLE": "谷歌供应商", + "DESCRIPTION": "输入你的谷歌身份提供者的凭证" + } }, "DETAIL": { "TITLE": "身份提供者", - "DESCRIPTION": "身份提供者的常规配置。", + "DESCRIPTION": "更新你的供应商配置", "DATECREATED": "已创建", "DATECHANGED": "已更新" }, + "OPTIONS": { + "ISAUTOCREATION": "是自动创建", + "ISAUTOCREATION_DESC": "如果选择了,如果账户还不存在,就会创建一个账户。", + "ISAUTOUPDATE": "正在自动更新", + "ISAUTOUPDATE_DESC": "如果选择,账户将在重新认证时更新。", + "ISCREATIONALLOWED": "是否允许创作", + "ISCREATIONALLOWED_DESC": "确定是否可以创建账户。", + "ISLINKINGALLOWED": "是否允许连接", + "ISLINKINGALLOWED_DESC": "确定一个身份是否可以与一个现有的账户相联系。" + }, "OWNERTYPES": { "0": "未知", "1": "实例", @@ -1636,18 +1660,10 @@ "1": "启用", "2": "停用" }, - "MAPPINGFIELD": { - "1": "首选用户名", - "2": "电子邮箱" - }, - "STYLE": "样式", - "STYLEFIELD": { - "0": "没有样式", - "1": "Google" - }, + "NAMEHINT": "如果指定,它将显示在登录界面。", + "OPTIONAL": "可选", + "UPDATECLIENTSECRET": "更新客户秘密", "ADD": "添加身份提供者", - "AUTOREGISTER": "自动注册", - "AUTOREGISTER_DESC": "如果勾选,当使用 IDP 登录且用户不存在时,则会创建一个。", "TYPE": "类型", "OWNER": "所有者", "ID": "ID", diff --git a/console/src/assets/images/idp/github-dark.svg b/console/src/assets/images/idp/github-dark.svg new file mode 100644 index 0000000000..ef83996ca0 --- /dev/null +++ b/console/src/assets/images/idp/github-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/console/src/assets/images/idp/github.svg b/console/src/assets/images/idp/github.svg new file mode 100644 index 0000000000..a8d1174049 --- /dev/null +++ b/console/src/assets/images/idp/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/console/src/assets/images/idp/gitlab.svg b/console/src/assets/images/idp/gitlab.svg new file mode 100644 index 0000000000..2c218a2b0c --- /dev/null +++ b/console/src/assets/images/idp/gitlab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/console/src/assets/images/google.png b/console/src/assets/images/idp/google.png similarity index 100% rename from console/src/assets/images/google.png rename to console/src/assets/images/idp/google.png diff --git a/console/src/assets/images/idp/ms.svg b/console/src/assets/images/idp/ms.svg new file mode 100644 index 0000000000..1f73976483 --- /dev/null +++ b/console/src/assets/images/idp/ms.svg @@ -0,0 +1 @@ +MS-SymbolLockup \ No newline at end of file diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index f61889e433..cf3910986e 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -53,12 +53,13 @@ @import 'src/app/modules/sidenav/sidenav.component'; @import 'src/app/modules/user-grants/user-grants.component.scss'; @import 'src/app/modules/shortcuts/shortcuts.component.scss'; -@import 'src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss'; +@import 'src/app/modules/policies/idp-settings/idp-settings.component.scss'; @import 'src/app/pages/actions/add-action-dialog/add-action-dialog.component'; @import 'src/app/modules/project-role-chip/project-role-chip.component'; @import 'src/app/pages/events/events.component'; @import 'src/app/pages/home/home.component.scss'; @import 'src/app/modules/policies/security-policy/security-policy.component.scss'; +@import 'src/app/modules/idp-table/idp-table.component.scss'; @import 'src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss'; @import 'src/app/modules/policies/login-policy/factor-table/factor-table.component.scss'; @import 'src/app/modules/info-overlay/info-overlay.component.scss'; @@ -72,9 +73,9 @@ @include nav-toggle-theme($theme); @include header-theme($theme); @include app-type-radio-theme($theme); + @include idp-table-theme($theme); @include events-theme($theme); @include projects-theme($theme); - @include idp-type-radio-theme($theme); @include top-view-theme($theme); @include info-overlay-theme($theme); @include app-auth-method-radio-theme($theme); @@ -82,6 +83,7 @@ @include search-user-autocomplete-theme($theme); @include project-role-chips-theme($theme); @include card-theme($theme); + @include idp-settings-theme($theme); @include filter-events-theme($theme); @include footer-theme($theme); @include table-theme($theme);
- - - - - google - - {{ 'IDP.AVAILABILITY' | translate }} @@ -129,8 +37,21 @@ {{ 'IDP.TYPE' | translate }} - {{ 'IDP.TYPES.1' | translate }} - {{ 'IDP.TYPES.3' | translate }} +
+
+ + Google +
+
+ + Generic OIDC +
+
+ + Generic JWT +
+
coming soon
+
{{ 'IDP.CREATIONDATE' | translate }} + {{ idp.details.creationDate | timestampToDate | localizedDate : 'dd. MMM, HH:mm' }} {{ 'IDP.CHANGEDATE' | translate }} + {{ idp.details.changeDate | timestampToDate | localizedDate : 'dd. MMM, HH:mm' }} {{ 'IDP.OWNER' | translate }} + {{ 'IDP.OWNERTYPES.' + idp.owner | translate }}