diff --git a/console/angular.json b/console/angular.json index af04bd3b55..b369ec7795 100644 --- a/console/angular.json +++ b/console/angular.json @@ -85,7 +85,7 @@ "budgets": [ { "type": "initial", - "maximumWarning": "6mb", + "maximumWarning": "7mb", "maximumError": "7mb" }, { 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 4d22549875..b9eeb5e106 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -59,6 +59,14 @@ Generic JWT +
+ + Gitlab +
+
+ + Gitlab Self Hosted +
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 f15a3756fd..147eeb21b0 100644 --- a/console/src/app/modules/idp-table/idp-table.component.ts +++ b/console/src/app/modules/idp-table/idp-table.component.ts @@ -236,6 +236,15 @@ export class IdpTableComponent implements OnInit { 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]; + case ProviderType.PROVIDER_TYPE_GITLAB: + return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'gitlab', row.id]; + case ProviderType.PROVIDER_TYPE_GITLAB_SELF_HOSTED: + return [ + row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', + 'provider', + 'gitlab-self-hosted', + row.id, + ]; case ProviderType.PROVIDER_TYPE_GITHUB: return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'github', row.id]; } 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 aa656bb764..095362b435 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 @@ -69,13 +69,38 @@
-
+ {{ 'ACTIONS.COMINGSOON' | translate }}
GitLab
-
+ + + + +
+ GitLab Self Hosted +
+
+
+
+ +

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

+ +
+ +

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

+ +
+
+ + {{ 'IDP.ISSUER' | 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-gitlab-self-hosted/provider-gitlab-self-hosted.component.scss b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.component.scss new file mode 100644 index 0000000000..0d4325a044 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.component.scss @@ -0,0 +1,87 @@ +.desc { + font-size: 14px; +} + +.gitlab-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; + } + } + + .gitlab-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; + } + } + } + } +} + +.gitlab-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-gitlab-self-hosted/provider-gitlab-self-hosted.component.spec.ts b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.component.spec.ts new file mode 100644 index 0000000000..3b6fdadce3 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.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-gitlab-self-hosted/provider-gitlab-self-hosted.component.ts b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.component.ts new file mode 100644 index 0000000000..3249d36b97 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.component.ts @@ -0,0 +1,277 @@ +import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; +import { Location } from '@angular/common'; +import { Component, Injector, Type } from '@angular/core'; +import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; +import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; +import { ActivatedRoute } from '@angular/router'; +import { take } from 'rxjs'; +import { + AddGitLabSelfHostedProviderRequest as AdminAddGitLabSelfHostedProviderRequest, + GetProviderByIDRequest as AdminGetProviderByIDRequest, + UpdateGitLabSelfHostedProviderRequest as AdminUpdateGitLabSelfHostedProviderRequest, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb'; +import { + AddGitLabSelfHostedProviderRequest as MgmtAddGitLabSelfHostedProviderRequest, + GetProviderByIDRequest as MgmtGetProviderByIDRequest, + UpdateGitLabSelfHostedProviderRequest as MgmtUpdateGitLabSelfHostedProviderRequest, +} 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 { requiredValidator } from '../../form-field/validators/validators'; + +import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; + +@Component({ + selector: 'cnsl-provider-gitlab-self-hosted', + templateUrl: './provider-gitlab-self-hosted.component.html', + styleUrls: ['./provider-gitlab-self-hosted.component.scss'], +}) +export class ProviderGitlabSelfHostedComponent { + 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 route: ActivatedRoute, + private toast: ToastService, + private injector: Injector, + private _location: Location, + private breadcrumbService: BreadcrumbService, + ) { + this.form = new FormGroup({ + name: new FormControl('', []), + issuer: new FormControl('', [requiredValidator]), + clientId: new FormControl('', [requiredValidator]), + clientSecret: new FormControl('', [requiredValidator]), + 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?.gitlabSelfHosted) { + this.form.patchValue(this.provider.config.gitlabSelfHosted); + this.name?.setValue(this.provider.name); + } + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } + + public submitForm(): void { + this.provider ? this.updateGitlabSelfHostedProvider() : this.addGitlabSelfHostedProvider(); + } + + public addGitlabSelfHostedProvider(): void { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtAddGitLabSelfHostedProviderRequest(); + + req.setName(this.name?.value); + req.setIssuer(this.issuer?.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) + .addGitLabSelfHostedProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.close(); + }, 2000); + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } else if (PolicyComponentServiceType.ADMIN) { + const req = new AdminAddGitLabSelfHostedProviderRequest(); + req.setName(this.name?.value); + req.setIssuer(this.issuer?.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) + .addGitLabSelfHostedProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.close(); + }, 2000); + }) + .catch((error) => { + this.loading = false; + this.toast.showError(error); + }); + } + } + + public updateGitlabSelfHostedProvider(): void { + if (this.provider) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtUpdateGitLabSelfHostedProviderRequest(); + req.setId(this.provider.id); + req.setName(this.name?.value); + req.setIssuer(this.issuer?.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) + .updateGitLabSelfHostedProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.close(); + }, 2000); + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } else if (PolicyComponentServiceType.ADMIN) { + const req = new AdminUpdateGitLabSelfHostedProviderRequest(); + req.setId(this.provider.id); + req.setName(this.name?.value); + req.setIssuer(this.issuer?.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) + .updateGitLabSelfHostedProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.close(); + }, 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 issuer(): AbstractControl | null { + return this.form.get('issuer'); + } + + public get scopesList(): AbstractControl | null { + return this.form.get('scopesList'); + } +} diff --git a/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.module.ts b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.module.ts new file mode 100644 index 0000000000..f516dddfe8 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.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 { ProviderGitlabSelfHostedRoutingModule } from './provider-gitlab-self-hosted-routing.module'; +import { ProviderGitlabSelfHostedComponent } from './provider-gitlab-self-hosted.component'; + +@NgModule({ + declarations: [ProviderGitlabSelfHostedComponent], + imports: [ + ProviderGitlabSelfHostedRoutingModule, + CommonModule, + FormsModule, + ReactiveFormsModule, + CreateLayoutModule, + InfoSectionModule, + InputModule, + MatButtonModule, + MatSelectModule, + MatIconModule, + MatChipsModule, + CardModule, + MatCheckboxModule, + MatTooltipModule, + TranslateModule, + ProviderOptionsModule, + MatLegacyProgressSpinnerModule, + ], +}) +export default class ProviderGitlabSelfHostedModule {} diff --git a/console/src/app/modules/providers/provider-gitlab/provider-gitlab-routing.module.ts b/console/src/app/modules/providers/provider-gitlab/provider-gitlab-routing.module.ts new file mode 100644 index 0000000000..ccc9aa76d4 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab/provider-gitlab-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ProviderGitlabComponent } from './provider-gitlab.component'; + +const routes: Routes = [ + { + path: '', + component: ProviderGitlabComponent, + data: { animation: 'DetailPage' }, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class ProviderGitlabRoutingModule {} diff --git a/console/src/app/modules/providers/provider-gitlab/provider-gitlab.component.html b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.component.html new file mode 100644 index 0000000000..85397eb64f --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.component.html @@ -0,0 +1,93 @@ + +
+
+ +

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

+ +
+ +

+ {{ !provider ? ('IDP.CREATE.GITLAB.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-gitlab/provider-gitlab.component.scss b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.component.scss new file mode 100644 index 0000000000..0d4325a044 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.component.scss @@ -0,0 +1,87 @@ +.desc { + font-size: 14px; +} + +.gitlab-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; + } + } + + .gitlab-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; + } + } + } + } +} + +.gitlab-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-gitlab/provider-gitlab.component.spec.ts b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.component.spec.ts new file mode 100644 index 0000000000..3b6fdadce3 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.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-gitlab/provider-gitlab.component.ts b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.component.ts new file mode 100644 index 0000000000..d478c5e686 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.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 } from '@angular/forms'; +import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; +import { ActivatedRoute } from '@angular/router'; +import { take } from 'rxjs'; +import { + AddGitLabProviderRequest as AdminAddGitLabProviderRequest, + GetProviderByIDRequest as AdminGetProviderByIDRequest, + UpdateGitLabProviderRequest as AdminUpdateGitLabProviderRequest, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb'; +import { + AddGitLabProviderRequest as MgmtAddGitLabProviderRequest, + GetProviderByIDRequest as MgmtGetProviderByIDRequest, + UpdateGitLabProviderRequest as MgmtUpdateGitLabProviderRequest, +} 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 { requiredValidator } from '../../form-field/validators/validators'; + +import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; + +@Component({ + selector: 'cnsl-provider-gitlab', + templateUrl: './provider-gitlab.component.html', + styleUrls: ['./provider-gitlab.component.scss'], +}) +export class ProviderGitlabComponent { + 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 route: ActivatedRoute, + private toast: ToastService, + private injector: Injector, + private _location: Location, + private breadcrumbService: BreadcrumbService, + ) { + this.form = new FormGroup({ + name: new FormControl('', []), + clientId: new FormControl('', [requiredValidator]), + clientSecret: new FormControl('', [requiredValidator]), + 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?.gitlab) { + this.form.patchValue(this.provider.config.gitlab); + this.name?.setValue(this.provider.name); + } + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } + + public submitForm(): void { + this.provider ? this.updateGitlabProvider() : this.addGitlabProvider(); + } + + public addGitlabProvider(): void { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtAddGitLabProviderRequest(); + + 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) + .addGitLabProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.close(); + }, 2000); + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } else if (PolicyComponentServiceType.ADMIN) { + const req = new AdminAddGitLabProviderRequest(); + 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) + .addGitLabProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.close(); + }, 2000); + }) + .catch((error) => { + this.loading = false; + this.toast.showError(error); + }); + } + } + + public updateGitlabProvider(): void { + if (this.provider) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new MgmtUpdateGitLabProviderRequest(); + 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) + .updateGitLabProvider(req) + .then((idp) => { + setTimeout(() => { + this.loading = false; + this.close(); + }, 2000); + }) + .catch((error) => { + this.toast.showError(error); + this.loading = false; + }); + } else if (PolicyComponentServiceType.ADMIN) { + const req = new AdminUpdateGitLabProviderRequest(); + 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.close(); + }, 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/providers/provider-gitlab/provider-gitlab.module.ts b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.module.ts new file mode 100644 index 0000000000..328f231366 --- /dev/null +++ b/console/src/app/modules/providers/provider-gitlab/provider-gitlab.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 { ProviderGitlabRoutingModule } from './provider-gitlab-routing.module'; +import { ProviderGitlabComponent } from './provider-gitlab.component'; + +@NgModule({ + declarations: [ProviderGitlabComponent], + imports: [ + ProviderGitlabRoutingModule, + CommonModule, + FormsModule, + ReactiveFormsModule, + CreateLayoutModule, + InfoSectionModule, + InputModule, + MatButtonModule, + MatSelectModule, + MatIconModule, + MatChipsModule, + CardModule, + MatCheckboxModule, + MatTooltipModule, + TranslateModule, + ProviderOptionsModule, + MatLegacyProgressSpinnerModule, + ], +}) +export default class ProviderGitlabModule {} diff --git a/console/src/app/pages/instance/instance-routing.module.ts b/console/src/app/pages/instance/instance-routing.module.ts index 4102c3bd14..766d4c9ddf 100644 --- a/console/src/app/pages/instance/instance-routing.module.ts +++ b/console/src/app/pages/instance/instance-routing.module.ts @@ -96,6 +96,34 @@ const routes: Routes = [ }, ], }, + { + path: 'gitlab', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-gitlab/provider-gitlab.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-gitlab/provider-gitlab.module'), + }, + ], + }, + { + path: 'gitlab-self-hosted', + children: [ + { + path: 'create', + loadChildren: () => + import('src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.module'), + }, + { + path: ':id', + loadChildren: () => + import('src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.module'), + }, + ], + }, { path: 'github', children: [ diff --git a/console/src/app/pages/orgs/org-routing.module.ts b/console/src/app/pages/orgs/org-routing.module.ts index 810ed0eebb..3c2f5d50d2 100644 --- a/console/src/app/pages/orgs/org-routing.module.ts +++ b/console/src/app/pages/orgs/org-routing.module.ts @@ -84,6 +84,34 @@ const routes: Routes = [ }, ], }, + { + path: 'gitlab', + children: [ + { + path: 'create', + loadChildren: () => import('src/app/modules/providers/provider-gitlab/provider-gitlab.module'), + }, + { + path: ':id', + loadChildren: () => import('src/app/modules/providers/provider-gitlab/provider-gitlab.module'), + }, + ], + }, + { + path: 'gitlab-self-hosted', + children: [ + { + path: 'create', + loadChildren: () => + import('src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.module'), + }, + { + path: ':id', + loadChildren: () => + import('src/app/modules/providers/provider-gitlab-self-hosted/provider-gitlab-self-hosted.module'), + }, + ], + }, { path: 'github', children: [ diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index 7c03d46b3e..df94cf5266 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -16,6 +16,10 @@ import { AddGitHubEnterpriseServerProviderResponse, AddGitHubProviderRequest, AddGitHubProviderResponse, + AddGitLabProviderRequest, + AddGitLabProviderResponse, + AddGitLabSelfHostedProviderRequest, + AddGitLabSelfHostedProviderResponse, AddGoogleProviderRequest, AddGoogleProviderResponse, AddIAMMemberRequest, @@ -208,6 +212,10 @@ import { UpdateGitHubEnterpriseServerProviderResponse, UpdateGitHubProviderRequest, UpdateGitHubProviderResponse, + UpdateGitLabProviderRequest, + UpdateGitLabProviderResponse, + UpdateGitLabSelfHostedProviderRequest, + UpdateGitLabSelfHostedProviderResponse, UpdateGoogleProviderRequest, UpdateGoogleProviderResponse, UpdateIAMMemberRequest, @@ -914,6 +922,26 @@ export class AdminService { return this.grpcService.admin.updateGoogleProvider(req, null).then((resp) => resp.toObject()); } + public addGitLabProvider(req: AddGitLabProviderRequest): Promise { + return this.grpcService.admin.addGitLabProvider(req, null).then((resp) => resp.toObject()); + } + + public updateGitLabProvider(req: UpdateGitLabProviderRequest): Promise { + return this.grpcService.admin.updateGitLabProvider(req, null).then((resp) => resp.toObject()); + } + + public addGitLabSelfHostedProvider( + req: AddGitLabSelfHostedProviderRequest, + ): Promise { + return this.grpcService.admin.addGitLabSelfHostedProvider(req, null).then((resp) => resp.toObject()); + } + + public updateGitLabSelfHostedProvider( + req: UpdateGitLabSelfHostedProviderRequest, + ): Promise { + return this.grpcService.admin.updateGitLabSelfHostedProvider(req, null).then((resp) => resp.toObject()); + } + public addGitHubProvider(req: AddGitHubProviderRequest): Promise { return this.grpcService.admin.addGitHubProvider(req, null).then((resp) => resp.toObject()); } diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index af2d200b61..ca9024b116 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -37,6 +37,10 @@ import { AddGitHubEnterpriseServerProviderResponse, AddGitHubProviderRequest, AddGitHubProviderResponse, + AddGitLabProviderRequest, + AddGitLabProviderResponse, + AddGitLabSelfHostedProviderRequest, + AddGitLabSelfHostedProviderResponse, AddGoogleProviderRequest, AddGoogleProviderResponse, AddHumanUserRequest, @@ -441,6 +445,10 @@ import { UpdateGitHubEnterpriseServerProviderResponse, UpdateGitHubProviderRequest, UpdateGitHubProviderResponse, + UpdateGitLabProviderRequest, + UpdateGitLabProviderResponse, + UpdateGitLabSelfHostedProviderRequest, + UpdateGitLabSelfHostedProviderResponse, UpdateGoogleProviderRequest, UpdateGoogleProviderResponse, UpdateHumanEmailRequest, @@ -873,6 +881,26 @@ export class ManagementService { return this.grpcService.mgmt.updateGoogleProvider(req, null).then((resp) => resp.toObject()); } + public addGitLabProvider(req: AddGitLabProviderRequest): Promise { + return this.grpcService.mgmt.addGitLabProvider(req, null).then((resp) => resp.toObject()); + } + + public updateGitLabProvider(req: UpdateGitLabProviderRequest): Promise { + return this.grpcService.mgmt.updateGitLabProvider(req, null).then((resp) => resp.toObject()); + } + + public addGitLabSelfHostedProvider( + req: AddGitLabSelfHostedProviderRequest, + ): Promise { + return this.grpcService.mgmt.addGitLabSelfHostedProvider(req, null).then((resp) => resp.toObject()); + } + + public updateGitLabSelfHostedProvider( + req: UpdateGitLabSelfHostedProviderRequest, + ): Promise { + return this.grpcService.mgmt.updateGitLabSelfHostedProvider(req, null).then((resp) => resp.toObject()); + } + public addGitHubProvider(req: AddGitHubProviderRequest): Promise { return this.grpcService.mgmt.addGitHubProvider(req, null).then((resp) => resp.toObject()); } diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 085af409a3..6b76b3fc76 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1627,6 +1627,14 @@ "TITLE": "Google Provider", "DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren Google-Identitätsprovider ein." }, + "GITLAB": { + "TITLE": "Gitlab Provider", + "DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren Gitlab Self Hosted Identitätsprovider ein." + }, + "GITLABSELFHOSTED": { + "TITLE": "Gitlab Self hosted Provider", + "DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren Gitlab Self Hosted Identitätsprovider ein." + }, "GITHUBES": { "TITLE": "GitHub Enterprise Server Provider", "DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren GitHub Enterprise Server ein." diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 76bafca949..d8e465ee18 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1632,6 +1632,14 @@ "TITLE": "Google Provider", "DESCRIPTION": "Enter the credentials for your Google Identity Provider" }, + "GITLAB": { + "TITLE": "Gitlab Provider", + "DESCRIPTION": "Enter the credentials for your Gitlab Identity Provider" + }, + "GITLABSELFHOSTED": { + "TITLE": "Gitlab Self Hosted Provider", + "DESCRIPTION": "Enter the credentials for your Gitlab Self Hosted Identity Provider" + }, "GITHUBES": { "TITLE": "GitHub Enterprise Server Provider", "DESCRIPTION": "Enter the credentials for your GitHub Enterprise Server Identity Provider" diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index aafc1f7b7e..beb22c68ea 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1631,6 +1631,14 @@ "TITLE": "Fournisseur Google", "DESCRIPTION": "Saisissez les informations d'identification de votre fournisseur d'identité Google" }, + "GITLAB": { + "TITLE": "Fournisseur Gitlab", + "DESCRIPTION": "Saisissez les informations d'identification de votre fournisseur d'identité Gitlab" + }, + "GITLABSELFHOSTED": { + "TITLE": "Fournisseur Gitlab Self Hosted", + "DESCRIPTION": "Saisissez les informations d'identification de votre fournisseur d'identité Gitlab Self Hosted" + }, "GITHUBES": { "TITLE": "Fournisseur GitHub Enterprise Server", "DESCRIPTION": "Saisissez les informations d'identification de votre fournisseur d'identité GitHub Enterprise Server" diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 7651422d20..948062a890 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1632,6 +1632,14 @@ "TITLE": "Google Provider", "DESCRIPTION": "Inserisci i dati necessari per il tuo Google provider." }, + "GITLAB": { + "TITLE": "Gitlab Provider", + "DESCRIPTION": "Inserisci i dati necessari per il tuo Gitlab provider." + }, + "GITLABSELFHOSTED": { + "TITLE": "Gitlab Self Hosted Provider", + "DESCRIPTION": "Inserisci i dati necessari per il tuo Gitlab Self Hosted provider." + }, "GITHUBES": { "TITLE": "GitHub Enterprise Server Provider", "DESCRIPTION": "Inserisci i dati necessari per il tuo GitHub Enterprise Server provider." diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 527179bebf..cfea8f36ea 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1631,6 +1631,14 @@ "TITLE": "Google Provider", "DESCRIPTION": "Wprowadź dane dla swojego dostawcy tożsamości Google" }, + "GITLAB": { + "TITLE": "Gitlab Provider", + "DESCRIPTION": "Wprowadź dane dla swojego dostawcy tożsamości Gitlab" + }, + "GITLABSELFHOSTED": { + "TITLE": "Gitlab Self Hosted Provider", + "DESCRIPTION": "Wprowadź dane dla swojego dostawcy tożsamości Gitlab Self Hosted" + }, "GITHUBES": { "TITLE": "GitHub Enterprise Server Provider", "DESCRIPTION": "Wprowadź dane dla swojego dostawcy tożsamości GitHub Enterprise Server" diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 2ee33ed929..ffeb3831d8 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1630,6 +1630,14 @@ "TITLE": "Google 身份提供者", "DESCRIPTION": "输入您的 Google 身份提供商的凭据" }, + "GITLAB": { + "TITLE": "Gitlab身份提供商", + "DESCRIPTION": "输入您的Gitlab身份提供者的凭据" + }, + "GITLABSELFHOSTED": { + "TITLE": "Gitlab自我托管供应商", + "DESCRIPTION": "输入您的Gitlab自我托管身份提供商的凭据" + }, "GITHUBES": { "TITLE": "Github Enterprise Server 身份提供者", "DESCRIPTION": "输入您的GitHubEnterpriseServer身份提供者的凭据"