feat(console): Active Directory / LDAP, cleanup idp component routing (#5506)

feat(console): LDAP
This commit is contained in:
Max Peintner 2023-03-28 09:38:17 +02:00 committed by GitHub
parent a14bfff0bb
commit 542271b467
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 1542 additions and 865 deletions

View File

@ -76,6 +76,10 @@
<img class="idp-logo light" src="./assets/images/idp/github.svg" alt="github" />
GitHub Enterprise Server
</div>
<div class="idp-table-provider-type" *ngSwitchCase="ProviderType.PROVIDER_TYPE_LDAP">
<i class="idp-icon las la-building"></i>
Active Directory / LDAP
</div>
<div class="idp-table-provider-type" *ngSwitchDefault>coming soon</div>
</div>
</td>
@ -162,6 +166,10 @@
</button>
<button
actions
*ngIf="
(serviceType === PolicyComponentServiceType.MGMT && idp.owner === IDPOwnerType.IDP_OWNER_TYPE_ORG) ||
(serviceType === PolicyComponentServiceType.ADMIN && idp.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM)
"
[disabled]="
(serviceType === PolicyComponentServiceType.MGMT && idp?.providerType === IDPOwnerType.IDP_OWNER_TYPE_ORG) ||
([

View File

@ -226,6 +226,8 @@ export class IdpTableComponent implements OnInit {
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_LDAP:
return [row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org', 'provider', 'ldap', row.id];
case ProviderType.PROVIDER_TYPE_GITLAB_SELF_HOSTED:
return [
row.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM ? '/instance' : '/org',

View File

@ -161,4 +161,22 @@
<span class="title">Generic JWT</span>
</div>
</a>
<a
class="item card"
[routerLink]="
serviceType === PolicyComponentServiceType.ADMIN
? ['/instance', 'provider', 'ldap', 'create']
: serviceType === PolicyComponentServiceType.MGMT
? ['/org', 'provider', 'ldap', 'create']
: []
"
>
<div class="idp-icon">
<i class="icon las la-building" svgIcon="mdi_jwt" alt="jwt"></i>
</div>
<div class="text-container">
<span class="title">Active Directory / LDAP</span>
</div>
</a>
</div>

View File

@ -49,7 +49,7 @@
*ngIf="(getCustomInitMessageTextMap$ | async) && (getCustomInitMessageTextMap$ | async)?.isDefault === false"
[disabled]="(canWrite$ | async) === false"
(click)="resetDefault()"
color="warn"
color="message-text-warn"
type="submit"
mat-stroked-button
>

View File

@ -35,7 +35,7 @@
}
}
.warn {
.message-text-warn {
margin-top: 1rem;
display: block;
}

View File

@ -0,0 +1,54 @@
<form [formGroup]="form" class="attribute-form">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.LDAPIDATTRIBUTE' | translate }}*</cnsl-label>
<input cnslInput formControlName="idAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.AVATARURLATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="avatarUrlAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.DISPLAYNAMEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="displayNameAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.EMAILATTRIBUTEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="emailAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.EMAILVERIFIEDATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="emailVerifiedAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.FIRSTNAMEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="firstNameAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.LASTNAMEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="lastNameAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.NICKNAMEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="nickNameAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.PHONEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="phoneAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.PHONEVERIFIEDATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="phoneVerifiedAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.PREFERREDLANGUAGEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="preferredLanguageAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.PREFERREDUSERNAMEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="preferredUsernameAttribute" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.PROFILEATTRIBUTE' | translate }}</cnsl-label>
<input cnslInput formControlName="profileAttribute" />
</cnsl-form-field>
</form>

View File

@ -0,0 +1,6 @@
.attribute-form {
display: grid;
grid-template-columns: 1fr;
max-width: 400px;
padding-bottom: 1rem;
}

View File

@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LDAPAttributesComponent } from './ldap-attributes.component';
describe('LDAPAttributesComponent', () => {
let component: LDAPAttributesComponent;
let fixture: ComponentFixture<LDAPAttributesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LDAPAttributesComponent],
}).compileComponents();
fixture = TestBed.createComponent(LDAPAttributesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,64 @@
import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';
import { LDAPAttributes } from 'src/app/proto/generated/zitadel/idp_pb';
import { requiredValidator } from '../../form-field/validators/validators';
@Component({
selector: 'cnsl-ldap-attributes',
templateUrl: './ldap-attributes.component.html',
styleUrls: ['./ldap-attributes.component.scss'],
})
export class LDAPAttributesComponent implements OnChanges, OnDestroy {
@Input() public initialAttributes?: LDAPAttributes.AsObject;
@Output() public attributesChanged: EventEmitter<LDAPAttributes> = new EventEmitter<LDAPAttributes>();
private destroy$: Subject<void> = new Subject();
public form: FormGroup = new FormGroup({
avatarUrlAttribute: new FormControl('', []),
displayNameAttribute: new FormControl('', []),
emailAttribute: new FormControl('', []),
emailVerifiedAttribute: new FormControl('', []),
firstNameAttribute: new FormControl('', []),
idAttribute: new FormControl('', [requiredValidator]),
lastNameAttribute: new FormControl('', []),
nickNameAttribute: new FormControl('', []),
phoneAttribute: new FormControl('', []),
phoneVerifiedAttribute: new FormControl('', []),
preferredLanguageAttribute: new FormControl('', []),
preferredUsernameAttribute: new FormControl('', []),
profileAttribute: new FormControl('', []),
});
constructor() {
this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
if (value) {
const attr = new LDAPAttributes();
attr.setAvatarUrlAttribute(value.avatarUrlAttribute);
attr.setDisplayNameAttribute(value.displayNameAttribute);
attr.setEmailAttribute(value.emailAttribute);
attr.setEmailVerifiedAttribute(value.emailVerifiedAttribute);
attr.setFirstNameAttribute(value.firstNameAttribute);
attr.setIdAttribute(value.idAttribute);
attr.setLastNameAttribute(value.lastNameAttribute);
attr.setNickNameAttribute(value.nickNameAttribute);
attr.setPhoneAttribute(value.phoneAttribute);
attr.setPhoneVerifiedAttribute(value.phoneVerifiedAttribute);
attr.setPreferredLanguageAttribute(value.preferredLanguageAttribute);
attr.setPreferredUsernameAttribute(value.preferredUsernameAttribute);
attr.setProfileAttribute(value.profileAttribute);
this.attributesChanged.emit(attr);
}
});
}
ngOnChanges(): void {
if (this.initialAttributes) {
this.form.patchValue(this.initialAttributes);
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}

View File

@ -1,18 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderAzureADComponent } from './provider-azure-ad.component';
const routes: Routes = [
{
path: '',
component: ProviderAzureADComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderAzureADRoutingModule {}

View File

@ -98,7 +98,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -3,7 +3,7 @@ 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, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import {
AddAzureADProviderRequest as AdminAddAzureADProviderRequest,
@ -18,6 +18,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -50,7 +51,7 @@ export class ProviderAzureADComponent {
];
constructor(
private router: Router,
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -67,6 +68,23 @@ export class ProviderAzureADComponent {
emailVerified: new FormControl(false),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;

View File

@ -1,43 +0,0 @@
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 { ProviderAzureADRoutingModule } from './provider-azure-ad-routing.module';
import { ProviderAzureADComponent } from './provider-azure-ad.component';
@NgModule({
declarations: [ProviderAzureADComponent],
imports: [
ProviderAzureADRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProviderAzureADModule {}

View File

@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderGithubESComponent } from './provider-github-es.component';
const routes: Routes = [
{
path: '',
component: ProviderGithubESComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderGithubESRoutingModule {}

View File

@ -97,7 +97,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -18,6 +18,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -44,6 +45,7 @@ export class ProviderGithubESComponent {
public provider?: Provider.AsObject;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -60,6 +62,23 @@ export class ProviderGithubESComponent {
scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;

View File

@ -1,43 +0,0 @@
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 { ProviderGithubESRoutingModule } from './provider-github-es-routing.module';
import { ProviderGithubESComponent } from './provider-github-es.component';
@NgModule({
declarations: [ProviderGithubESComponent],
imports: [
ProviderGithubESRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProviderGithubESModule {}

View File

@ -1,18 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderGithubComponent } from './provider-github.component';
const routes: Routes = [
{
path: '',
component: ProviderGithubComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderGithubRoutingModule {}

View File

@ -84,7 +84,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -18,6 +18,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -45,6 +46,7 @@ export class ProviderGithubComponent {
public updateClientSecret: boolean = false;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -58,6 +60,23 @@ export class ProviderGithubComponent {
scopesList: new FormControl(['openid', 'profile', 'email'], []),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;

View File

@ -1,43 +0,0 @@
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 { ProviderGithubRoutingModule } from './provider-github-routing.module';
import { ProviderGithubComponent } from './provider-github.component';
@NgModule({
declarations: [ProviderGithubComponent],
imports: [
ProviderGithubRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProviderGithubModule {}

View File

@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderGitlabSelfHostedComponent } from './provider-gitlab-self-hosted.component';
const routes: Routes = [
{
path: '',
component: ProviderGitlabSelfHostedComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderGitlabSelfHostedRoutingModule {}

View File

@ -87,7 +87,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -18,6 +18,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -45,6 +46,7 @@ export class ProviderGitlabSelfHostedComponent {
public updateClientSecret: boolean = false;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -59,6 +61,23 @@ export class ProviderGitlabSelfHostedComponent {
scopesList: new FormControl(['openid', 'profile', 'email'], []),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;

View File

@ -1,43 +0,0 @@
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 {}

View File

@ -1,17 +0,0 @@
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 {}

View File

@ -83,7 +83,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -18,6 +18,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -45,6 +46,7 @@ export class ProviderGitlabComponent {
public updateClientSecret: boolean = false;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -58,6 +60,23 @@ export class ProviderGitlabComponent {
scopesList: new FormControl(['openid', 'profile', 'email'], []),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;

View File

@ -1,43 +0,0 @@
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 {}

View File

@ -1,18 +0,0 @@
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 {}

View File

@ -83,7 +83,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -18,6 +18,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -45,6 +46,7 @@ export class ProviderGoogleComponent {
public updateClientSecret: boolean = false;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -58,6 +60,23 @@ export class ProviderGoogleComponent {
scopesList: new FormControl(['openid', 'profile', 'email'], []),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;

View File

@ -1,43 +0,0 @@
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 { ProviderGoogleRoutingModule } from './provider-google-routing.module';
import { ProviderGoogleComponent } from './provider-google.component';
@NgModule({
declarations: [ProviderGoogleComponent],
imports: [
ProviderGoogleRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProviderGoogleModule {}

View File

@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderJWTComponent } from './provider-jwt.component';
const routes: Routes = [
{
path: '',
component: ProviderJWTComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderJWTCreateRoutingModule {}

View File

@ -50,7 +50,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -17,6 +17,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -42,6 +43,7 @@ export class ProviderJWTComponent {
public provider?: Provider.AsObject;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -87,6 +89,23 @@ export class ProviderJWTComponent {
jwtEndpoint: new UntypedFormControl('', [requiredValidator]),
keysEndpoint: new UntypedFormControl('', [requiredValidator]),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
}
private getData(id: string): void {

View File

@ -1,43 +0,0 @@
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 {}

View File

@ -0,0 +1,125 @@
<cnsl-create-layout
title="{{ id ? ('IDP.DETAIL.TITLE' | translate) : ('IDP.CREATE.TITLE' | translate) }}"
(closed)="close()"
>
<div class="identity-provider-create-content">
<div class="title-row">
<i class="idp-icon las la-building"></i>
<h1>{{ 'IDP.CREATE.LDAP.TITLE' | translate }}</h1>
<mat-spinner diameter="25" *ngIf="loading" color="primary"></mat-spinner>
</div>
<p class="identity-provider-desc cnsl-secondary-text">
{{ !provider ? ('IDP.CREATE.LDAP.DESCRIPTION' | translate) : ('IDP.DETAIL.DESCRIPTION' | translate) }}
</p>
<form [formGroup]="form" (ngSubmit)="submitForm()">
<div class="identity-provider-content">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<h2 class="subheader">{{ 'IDP.LDAPCONNECTION' | translate }}</h2>
<cnsl-string-list
title="{{ 'IDP.SERVERS' | translate }}"
formControlName="serversList"
[required]="true"
></cnsl-string-list>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.BASEDN' | translate }}</cnsl-label>
<input cnslInput formControlName="baseDn" />
</cnsl-form-field>
<div [ngClass]="{ 'identity-provider-2-col': !provider }">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.BINDDN' | translate }}</cnsl-label>
<input cnslInput formControlName="bindDn" />
</cnsl-form-field>
<mat-checkbox *ngIf="provider" [(ngModel)]="updateBindPassword" [ngModelOptions]="{ standalone: true }">{{
'IDP.UPDATEBINDPASSWORD' | translate
}}</mat-checkbox>
<cnsl-form-field *ngIf="!provider || (provider && updateBindPassword)" class="formfield">
<cnsl-label>{{ 'IDP.BINDPASSWORD' | translate }}</cnsl-label>
<input cnslInput formControlName="bindPassword" />
</cnsl-form-field>
</div>
<h2 class="subheader">{{ 'IDP.LDAPUSERBINDING' | translate }}</h2>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.USERBASE' | translate }}</cnsl-label>
<input cnslInput formControlName="userBase" />
</cnsl-form-field>
<cnsl-string-list
title="{{ 'IDP.USERFILTERS' | translate }}"
formControlName="userFiltersList"
[required]="true"
></cnsl-string-list>
<cnsl-string-list
title="{{ 'IDP.USEROBJECTCLASSES' | translate }}"
formControlName="userObjectClassesList"
[required]="true"
></cnsl-string-list>
<div class="identity-provider-optional-h-wrapper">
<h2>{{ 'IDP.LDAPATTRIBUTES' | translate }}</h2>
<button (click)="showAttributes = !showAttributes" type="button" mat-icon-button>
<mat-icon *ngIf="showAttributes">keyboard_arrow_up</mat-icon>
<mat-icon *ngIf="!showAttributes">keyboard_arrow_down</mat-icon>
</button>
<span *ngIf="!provider?.config?.ldap?.attributes?.idAttribute" class="state error">{{
'IDP.REQUIRED' | translate
}}</span>
</div>
<div *ngIf="showAttributes">
<cnsl-ldap-attributes
[initialAttributes]="provider?.config?.ldap?.attributes"
(attributesChanged)="attributes = $event"
></cnsl-ldap-attributes>
</div>
<div class="identity-provider-optional-h-wrapper">
<h2>{{ 'IDP.OPTIONAL' | translate }}</h2>
<button (click)="showOptional = !showOptional" type="button" mat-icon-button>
<mat-icon *ngIf="showOptional">keyboard_arrow_up</mat-icon
><mat-icon *ngIf="!showOptional">keyboard_arrow_down</mat-icon>
</button>
</div>
<div *ngIf="showOptional">
<mat-checkbox formControlName="startTls">{{ 'IDP.STARTTLS' | translate }}</mat-checkbox>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.TIMEOUT' | translate }}</cnsl-label>
<input cnslInput formControlName="timeout" type="number" />
</cnsl-form-field>
<cnsl-provider-options
[initialOptions]="provider?.config?.options"
(optionsChanged)="options = $event"
></cnsl-provider-options>
</div>
</div>
<div class="identity-provider-create-actions">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="(form.invalid && attributes.toObject().idAttribute !== '') || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>
</div>
</form>
</div>
</cnsl-create-layout>

View File

@ -0,0 +1,24 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ProviderLDAPComponent } from './provider-ldap.component';
describe('ProviderLDAPComponent', () => {
let component: ProviderLDAPComponent;
let fixture: ComponentFixture<ProviderLDAPComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ProviderLDAPComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProviderLDAPComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,264 @@
import { Location } from '@angular/common';
import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { take } from 'rxjs';
import {
AddLDAPProviderRequest as AdminAddLDAPProviderRequest,
GetProviderByIDRequest as AdminGetProviderByIDRequest,
UpdateLDAPProviderRequest as AdminUpdateLDAPProviderRequest,
} from 'src/app/proto/generated/zitadel/admin_pb';
import { LDAPAttributes, Options, Provider } from 'src/app/proto/generated/zitadel/idp_pb';
import {
AddLDAPProviderRequest as MgmtAddLDAPProviderRequest,
GetProviderByIDRequest as MgmtGetProviderByIDRequest,
UpdateLDAPProviderRequest as MgmtUpdateLDAPProviderRequest,
} 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 { requiredValidator } from '../../form-field/validators/validators';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
@Component({
selector: 'cnsl-provider-ldap',
templateUrl: './provider-ldap.component.html',
})
export class ProviderLDAPComponent {
public updateBindPassword: boolean = false;
public showAttributes: boolean = false;
public showOptional: boolean = false;
public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true);
public attributes: LDAPAttributes = new LDAPAttributes();
public id: string | null = '';
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
private service!: ManagementService | AdminService;
public form!: FormGroup;
public loading: boolean = false;
public provider?: Provider.AsObject;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
private _location: Location,
private breadcrumbService: BreadcrumbService,
) {
this.form = new FormGroup({
name: new FormControl('', [requiredValidator]),
serversList: new FormControl('', [requiredValidator]),
baseDn: new FormControl('', [requiredValidator]),
bindDn: new FormControl('', [requiredValidator]),
bindPassword: new FormControl('', [requiredValidator]),
userBase: new FormControl('', [requiredValidator]),
userFiltersList: new FormControl('', [requiredValidator]),
userObjectClassesList: new FormControl('', [requiredValidator]),
timeout: new FormControl<number>(0),
startTls: new FormControl(false),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
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<ManagementService>);
const bread: Breadcrumb = {
type: BreadcrumbType.ORG,
routerLink: ['/org'],
};
this.breadcrumbService.setBreadcrumb([bread]);
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
const iamBread = new Breadcrumb({
type: BreadcrumbType.ORG,
name: 'Instance',
routerLink: ['/instance'],
});
this.breadcrumbService.setBreadcrumb([iamBread]);
break;
}
this.id = this.route.snapshot.paramMap.get('id');
if (this.id) {
this.getData(this.id);
this.bindPassword?.setValidators([]);
}
});
}
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?.ldap) {
this.form.patchValue(this.provider.config.ldap);
this.name?.setValue(this.provider.name);
this.timeout?.setValue(this.provider.config.ldap.timeout?.seconds);
}
})
.catch((error) => {
this.toast.showError(error);
this.loading = false;
});
}
public submitForm(): void {
this.provider ? this.updateLDAPProvider() : this.addLDAPProvider();
}
public addLDAPProvider(): void {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtAddLDAPProviderRequest()
: new AdminAddLDAPProviderRequest();
req.setName(this.name?.value);
req.setProviderOptions(this.options);
req.setAttributes(this.attributes);
req.setBaseDn(this.baseDn?.value);
req.setBindDn(this.bindDn?.value);
req.setBindPassword(this.bindPassword?.value);
req.setServersList(this.serversList?.value); // list
req.setStartTls(this.startTls?.value);
req.setTimeout(new Duration().setSeconds(this.timeout?.value ?? 0));
req.setUserBase(this.userBase?.value);
req.setUserFiltersList(this.userFiltersList?.value); // list
req.setUserObjectClassesList(this.userObjectClassesList?.value); // list
this.loading = true;
(this.service as ManagementService)
.addLDAPProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 2000);
})
.catch((error) => {
this.toast.showError(error);
this.loading = false;
});
}
public updateLDAPProvider(): void {
if (this.provider) {
const req =
this.serviceType === PolicyComponentServiceType.MGMT
? new MgmtUpdateLDAPProviderRequest()
: new AdminUpdateLDAPProviderRequest();
req.setId(this.provider.id);
req.setName(this.name?.value);
req.setProviderOptions(this.options);
req.setAttributes(this.attributes);
req.setBaseDn(this.baseDn?.value);
req.setBindDn(this.bindDn?.value);
if (this.updateBindPassword) {
req.setBindPassword(this.bindPassword?.value);
}
req.setServersList(this.serversList?.value);
req.setStartTls(this.startTls?.value);
req.setTimeout(new Duration().setSeconds(this.timeout?.value ?? 0));
req.setUserBase(this.userBase?.value);
req.setUserFiltersList(this.userFiltersList?.value);
req.setUserObjectClassesList(this.userObjectClassesList?.value);
this.loading = true;
(this.service as ManagementService)
.updateLDAPProvider(req)
.then((idp) => {
setTimeout(() => {
this.loading = false;
this.close();
}, 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 baseDn(): AbstractControl | null {
return this.form.get('baseDn');
}
public get bindDn(): AbstractControl | null {
return this.form.get('bindDn');
}
public get bindPassword(): AbstractControl | null {
return this.form.get('bindPassword');
}
public get serversList(): AbstractControl | null {
return this.form.get('serversList');
}
public get startTls(): AbstractControl | null {
return this.form.get('startTls');
}
public get timeout(): AbstractControl | null {
return this.form.get('timeout');
}
public get userBase(): AbstractControl | null {
return this.form.get('userBase');
}
public get userFiltersList(): AbstractControl | null {
return this.form.get('userFiltersList');
}
public get userObjectClassesList(): AbstractControl | null {
return this.form.get('userObjectClassesList');
}
}

View File

@ -1,18 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderOAuthComponent } from './provider-oauth.component';
const routes: Routes = [
{
path: '',
component: ProviderOAuthComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderOAuthRoutingModule {}

View File

@ -101,7 +101,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -3,7 +3,7 @@ import { Location } from '@angular/common';
import { Component, Injector, Type } from '@angular/core';
import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs';
import {
AddGenericOAuthProviderRequest as AdminAddGenericOAuthProviderRequest,
@ -18,6 +18,7 @@ import {
} 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 { requiredValidator } from '../../form-field/validators/validators';
@ -44,7 +45,7 @@ export class ProviderOAuthComponent {
public provider?: Provider.AsObject;
constructor(
private router: Router,
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
@ -62,6 +63,23 @@ export class ProviderOAuthComponent {
scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []),
});
this.authService
.isAllowed(
this.serviceType === PolicyComponentServiceType.ADMIN
? ['iam.idp.write']
: this.serviceType === PolicyComponentServiceType.MGMT
? ['org.idp.write']
: [],
)
.pipe(take(1))
.subscribe((allowed) => {
if (allowed) {
this.form.enable();
} else {
this.form.disable();
}
});
this.route.data.pipe(take(1)).subscribe((data) => {
this.serviceType = data.serviceType;

View File

@ -1,43 +0,0 @@
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 { ProviderOAuthRoutingModule } from './provider-oauth-routing.module';
import { ProviderOAuthComponent } from './provider-oauth.component';
@NgModule({
declarations: [ProviderOAuthComponent],
imports: [
ProviderOAuthRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProviderOAuthModule {}

View File

@ -1,18 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderOIDCComponent } from './provider-oidc.component';
const routes: Routes = [
{
path: '',
component: ProviderOIDCComponent,
data: { animation: 'DetailPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProviderOIDCRoutingModule {}

View File

@ -11,7 +11,7 @@
<p class="identity-provider-desc cnsl-secondary-text">{{ 'IDP.CREATE.OIDC.DESCRIPTION' | translate }}</p>
<form [formGroup]="oidcFormGroup" (ngSubmit)="submitForm()">
<form [formGroup]="form" (ngSubmit)="submitForm()">
<div class="identity-provider-content">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
@ -86,7 +86,13 @@
</div>
<div class="identity-provider-create-actions">
<button color="primary" mat-raised-button class="continue-button" [disabled]="oidcFormGroup.invalid" type="submit">
<button
color="primary"
mat-raised-button
class="continue-button"
[disabled]="form.invalid || form.disabled"
type="submit"
>
<span *ngIf="id">{{ 'ACTIONS.SAVE' | translate }}</span>
<span *ngIf="!id">{{ 'ACTIONS.CREATE' | translate }}</span>
</button>

View File

@ -37,7 +37,7 @@ export class ProviderOIDCComponent {
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public oidcFormGroup!: UntypedFormGroup;
public form!: UntypedFormGroup;
public loading: boolean = false;
@ -50,7 +50,7 @@ export class ProviderOIDCComponent {
private _location: Location,
breadcrumbService: BreadcrumbService,
) {
this.oidcFormGroup = new UntypedFormGroup({
this.form = new UntypedFormGroup({
name: new UntypedFormControl('', [requiredValidator]),
clientId: new UntypedFormControl('', [requiredValidator]),
clientSecret: new UntypedFormControl('', [requiredValidator]),
@ -105,7 +105,7 @@ export class ProviderOIDCComponent {
this.provider = resp.idp;
this.loading = false;
if (this.provider?.config?.oidc) {
this.oidcFormGroup.patchValue(this.provider.config.oidc);
this.form.patchValue(this.provider.config.oidc);
this.name?.setValue(this.provider.name);
}
})
@ -204,22 +204,22 @@ export class ProviderOIDCComponent {
}
public get name(): AbstractControl | null {
return this.oidcFormGroup.get('name');
return this.form.get('name');
}
public get clientId(): AbstractControl | null {
return this.oidcFormGroup.get('clientId');
return this.form.get('clientId');
}
public get clientSecret(): AbstractControl | null {
return this.oidcFormGroup.get('clientSecret');
return this.form.get('clientSecret');
}
public get issuer(): AbstractControl | null {
return this.oidcFormGroup.get('issuer');
return this.form.get('issuer');
}
public get scopesList(): AbstractControl | null {
return this.oidcFormGroup.get('scopesList');
return this.form.get('scopesList');
}
}

View File

@ -1,43 +0,0 @@
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 { ProviderOIDCRoutingModule } from './provider-oidc-routing.module';
import { ProviderOIDCComponent } from './provider-oidc.component';
@NgModule({
declarations: [ProviderOIDCComponent],
imports: [
ProviderOIDCRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProviderOIDCModule {}

View File

@ -0,0 +1,52 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProviderType } from 'src/app/proto/generated/zitadel/idp_pb';
import { ProviderAzureADComponent } from './provider-azure-ad/provider-azure-ad.component';
import { ProviderGithubESComponent } from './provider-github-es/provider-github-es.component';
import { ProviderGithubComponent } from './provider-github/provider-github.component';
import { ProviderGitlabSelfHostedComponent } from './provider-gitlab-self-hosted/provider-gitlab-self-hosted.component';
import { ProviderGitlabComponent } from './provider-gitlab/provider-gitlab.component';
import { ProviderGoogleComponent } from './provider-google/provider-google.component';
import { ProviderJWTComponent } from './provider-jwt/provider-jwt.component';
import { ProviderLDAPComponent } from './provider-ldap/provider-ldap.component';
import { ProviderOAuthComponent } from './provider-oauth/provider-oauth.component';
import { ProviderOIDCComponent } from './provider-oidc/provider-oidc.component';
const typeMap = {
[ProviderType.PROVIDER_TYPE_AZURE_AD]: { path: 'azure', component: ProviderAzureADComponent },
[ProviderType.PROVIDER_TYPE_GITHUB]: { path: 'github', component: ProviderGithubComponent },
[ProviderType.PROVIDER_TYPE_GITHUB_ES]: { path: 'github-es', component: ProviderGithubESComponent },
[ProviderType.PROVIDER_TYPE_GITLAB]: { path: 'gitlab', component: ProviderGitlabComponent },
[ProviderType.PROVIDER_TYPE_GITLAB_SELF_HOSTED]: {
path: 'gitlab-self-hosted',
component: ProviderGitlabSelfHostedComponent,
},
[ProviderType.PROVIDER_TYPE_GOOGLE]: { path: 'google', component: ProviderGoogleComponent },
[ProviderType.PROVIDER_TYPE_JWT]: { path: 'jwt', component: ProviderJWTComponent },
[ProviderType.PROVIDER_TYPE_OAUTH]: { path: 'oauth', component: ProviderOAuthComponent },
[ProviderType.PROVIDER_TYPE_OIDC]: { path: 'oidc', component: ProviderOIDCComponent },
[ProviderType.PROVIDER_TYPE_LDAP]: { path: 'ldap', component: ProviderLDAPComponent },
};
const routes: Routes = Object.entries(typeMap).map(([key, value]) => {
return {
path: value.path,
children: [
{
path: 'create',
component: value.component,
},
{
path: ':id',
component: value.component,
},
],
};
});
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProvidersRoutingModule {}

View File

@ -0,0 +1,68 @@
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 { StringListModule } from '../string-list/string-list.module';
import { LDAPAttributesComponent } from './ldap-attributes/ldap-attributes.component';
import { ProviderAzureADComponent } from './provider-azure-ad/provider-azure-ad.component';
import { ProviderGithubESComponent } from './provider-github-es/provider-github-es.component';
import { ProviderGithubComponent } from './provider-github/provider-github.component';
import { ProviderGitlabSelfHostedComponent } from './provider-gitlab-self-hosted/provider-gitlab-self-hosted.component';
import { ProviderGitlabComponent } from './provider-gitlab/provider-gitlab.component';
import { ProviderGoogleComponent } from './provider-google/provider-google.component';
import { ProviderJWTComponent } from './provider-jwt/provider-jwt.component';
import { ProviderLDAPComponent } from './provider-ldap/provider-ldap.component';
import { ProviderOAuthComponent } from './provider-oauth/provider-oauth.component';
import { ProviderOIDCComponent } from './provider-oidc/provider-oidc.component';
import { ProvidersRoutingModule } from './providers-routing.module';
@NgModule({
declarations: [
ProviderGoogleComponent,
ProviderGithubComponent,
ProviderGithubESComponent,
ProviderAzureADComponent,
LDAPAttributesComponent,
ProviderGitlabSelfHostedComponent,
ProviderGitlabComponent,
ProviderGithubESComponent,
ProviderJWTComponent,
ProviderOIDCComponent,
ProviderOAuthComponent,
ProviderLDAPComponent,
],
imports: [
ProvidersRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CreateLayoutModule,
StringListModule,
InfoSectionModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,
MatChipsModule,
CardModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
ProviderOptionsModule,
MatLegacyProgressSpinnerModule,
],
})
export default class ProvidersModule {}

View File

@ -25,6 +25,12 @@
}
}
.idp-icon {
font-size: 36px;
margin-right: 1rem;
flex-shrink: 0;
}
h1 {
margin: 0 1rem 0 0;
}
@ -54,6 +60,20 @@
}
.identity-provider-content {
display: flex;
flex-direction: column;
.subheader {
margin: 2rem 0 0.5rem 0;
}
.identity-provider-2-col {
max-width: 400px;
display: grid;
grid-template-columns: 1fr 1fr;
column-gap: 1rem;
}
.desc {
margin-bottom: 1rem;
}
@ -93,7 +113,7 @@
align-items: center;
h2 {
margin-right: 0.25rem;
margin: 1rem 0.25rem 1rem 0;
}
}
}

View File

@ -0,0 +1,30 @@
<form class="string-list-form" (ngSubmit)="add(redInput)">
<cnsl-form-field class="formfield">
<cnsl-label>{{ title }}</cnsl-label>
<input #redInput cnslInput [formControl]="control" />
</cnsl-form-field>
<button
matTooltip="{{ 'ACTIONS.ADD' | translate }}"
type="submit"
mat-icon-button
[disabled]="control.invalid || control.disabled"
>
<mat-icon>add</mat-icon>
</button>
</form>
<div class="string-list">
<div *ngFor="let str of value" class="value-line">
<span>{{ str }}</span>
<span class="fill-space"></span>
<button
type="button"
matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
mat-icon-button
(click)="remove(str)"
class="icon-button"
>
<mat-icon class="icon">cancel</mat-icon>
</button>
</div>
</div>

View File

@ -0,0 +1,60 @@
@use '@angular/material' as mat;
@mixin string-list-theme($theme) {
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
$is-dark-theme: map-get($theme, is-dark);
$warn: map-get($theme, warn);
$warn-color: map-get($warn, 500);
$button-text-color: map-get($foreground, text);
$button-disabled-text-color: map-get($foreground, disabled-button);
$divider-color: map-get($foreground, dividers);
$secondary-text: map-get($foreground, secondary-text);
.string-list {
width: 100%;
max-width: 400px;
.value-line {
display: flex;
align-items: center;
margin: 0.5rem 0;
padding: 0 0 0 0.75rem;
border-radius: 4px;
background: map-get($background, infosection);
.fill-space {
flex: 1;
}
.icon-button {
height: 30px;
line-height: 30px;
.icon {
font-size: 1rem;
margin-bottom: 3px;
}
&:not(:hover) {
color: $secondary-text;
}
}
}
}
}
.string-list-form {
display: flex;
align-items: flex-end;
min-width: 320px;
.formfield {
width: 500px;
}
button {
margin-bottom: 0.9rem;
margin-right: -0.5rem;
}
}

View File

@ -0,0 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { StringListComponent } from './string-list.component';
describe('StringListComponent', () => {
let component: StringListComponent;
let fixture: ComponentFixture<StringListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [StringListComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(StringListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,100 @@
import { Component, forwardRef, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable, Subject, takeUntil } from 'rxjs';
import { requiredValidator } from '../form-field/validators/validators';
@Component({
selector: 'cnsl-string-list',
templateUrl: './string-list.component.html',
styleUrls: ['./string-list.component.scss'],
encapsulation: ViewEncapsulation.None,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => StringListComponent),
multi: true,
},
],
})
export class StringListComponent implements ControlValueAccessor, OnInit, OnDestroy {
@Input() title: string = '';
@Input() required: boolean = false;
@Input() public getValues: Observable<void> = new Observable(); // adds formfieldinput to array on emission
@Input() public control: FormControl = new FormControl<string>({ value: '', disabled: true });
private destroy$: Subject<void> = new Subject();
@ViewChild('redInput') input!: any;
private val: string[] = [];
ngOnInit(): void {
this.getValues.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.add(this.input.nativeElement);
});
this.required ? this.control.setValidators([requiredValidator]) : this.control.setValidators([]);
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
onChange: any = () => {};
onTouch: any = () => {};
set value(val: string[]) {
if (val !== undefined && this.val !== val) {
this.val = val;
this.onChange(val);
this.onTouch(val);
}
}
get value() {
return this.val;
}
writeValue(value: string[]) {
this.value = value;
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {
this.onTouch = fn;
}
public setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.control.disable();
} else {
this.control.enable();
}
}
public add(input: any): void {
if (this.control.valid) {
const trimmed = input.value.trim();
if (trimmed) {
this.val ? this.val.push(input.value) : (this.val = [input.value]);
this.onChange(this.val);
this.onTouch(this.val);
}
if (input) {
input.value = '';
}
}
}
public remove(str: string): void {
const index = this.value.indexOf(str);
if (index >= 0) {
this.value.splice(index, 1);
this.onChange(this.value);
this.onTouch(this.value);
}
}
}

View File

@ -0,0 +1,25 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
import { MatLegacyButtonModule } from '@angular/material/legacy-button';
import { MatLegacyTooltipModule } from '@angular/material/legacy-tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from '../input/input.module';
import { StringListComponent } from './string-list.component';
@NgModule({
declarations: [StringListComponent],
imports: [
CommonModule,
InputModule,
FormsModule,
ReactiveFormsModule,
TranslateModule,
MatIconModule,
MatLegacyTooltipModule,
MatLegacyButtonModule,
],
exports: [StringListComponent],
})
export class StringListModule {}

View File

@ -26,131 +26,11 @@ const routes: Routes = [
{
path: 'provider',
canActivate: [AuthGuard, RoleGuard],
loadChildren: () => import('src/app/modules/providers/providers.module'),
data: {
roles: ['iam.idp.write'],
roles: ['iam.idp.read'],
serviceType: PolicyComponentServiceType.ADMIN,
},
children: [
{
path: 'azure-ad',
children: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-azure-ad/provider-azure-ad.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-azure-ad/provider-azure-ad.module'),
},
],
},
{
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: 'oauth',
children: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-oauth/provider-oauth.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-oauth/provider-oauth.module'),
},
],
},
{
path: 'github-es',
children: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-github-es/provider-github-es.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-github-es/provider-github-es.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: '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: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-github/provider-github.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-github/provider-github.module'),
},
],
},
],
},
];

View File

@ -14,131 +14,11 @@ const routes: Routes = [
{
path: 'provider',
canActivate: [AuthGuard, RoleGuard],
loadChildren: () => import('src/app/modules/providers/providers.module'),
data: {
roles: ['org.idp.write'],
roles: ['org.idp.read'],
serviceType: PolicyComponentServiceType.MGMT,
},
children: [
{
path: 'oauth',
children: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-oauth/provider-oauth.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-oauth/provider-oauth.module'),
},
],
},
{
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: 'github-es',
children: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-github-es/provider-github-es.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-github-es/provider-github-es.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: '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: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-github/provider-github.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-github/provider-github.module'),
},
],
},
{
path: 'azure-ad',
children: [
{
path: 'create',
loadChildren: () => import('src/app/modules/providers/provider-azure-ad/provider-azure-ad.module'),
},
{
path: ':id',
loadChildren: () => import('src/app/modules/providers/provider-azure-ad/provider-azure-ad.module'),
},
],
},
],
},
{
path: '',

View File

@ -110,10 +110,9 @@
<cnsl-redirect-uris
class="redirect-section"
[canWrite]="true"
[disabled]="false"
[isNative]="appType?.value.oidcAppType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
(changedUris)="oidcAppRequest.setRedirectUrisList($any($event))"
[urisList]="oidcAppRequest.toObject().redirectUrisList"
[(ngModel)]="redirectUris"
[getValues]="requestRedirectValuesSubject$"
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
data-e2e="redirect-uris"
@ -139,11 +138,10 @@
<cnsl-redirect-uris
class="redirect-section"
[canWrite]="true"
(changedUris)="oidcAppRequest.setPostLogoutRedirectUrisList($any($event))"
[urisList]="oidcAppRequest.toObject().postLogoutRedirectUrisList"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[disabled]="false"
[(ngModel)]="postLogoutUrisList"
[getValues]="requestRedirectValuesSubject$"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[isNative]="appType?.value.oidcAppType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
data-e2e="postlogout-uris"
>
@ -431,20 +429,18 @@
<div class="formfield full-width">
<cnsl-redirect-uris
class="redirect-section"
[canWrite]="true"
(changedUris)="oidcAppRequest.setRedirectUrisList($any($event))"
[urisList]="oidcAppRequest.toObject().redirectUrisList"
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
[disabled]="false"
[(ngModel)]="redirectUris"
[getValues]="requestRedirectValuesSubject$"
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
[isNative]="appType?.value.oidcAppType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
>
</cnsl-redirect-uris>
<cnsl-redirect-uris
class="redirect-section"
[canWrite]="true"
(changedUris)="oidcAppRequest.setPostLogoutRedirectUrisList($any($event))"
[urisList]="oidcAppRequest.toObject().postLogoutRedirectUrisList"
[disabled]="false"
[(ngModel)]="postLogoutUrisList"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[getValues]="requestRedirectValuesSubject$"
[isNative]="appType?.value.oidcAppType === OIDCAppType.OIDC_APP_TYPE_NATIVE"

View File

@ -221,6 +221,22 @@ export class AppCreateComponent implements OnInit, OnDestroy {
});
}
public get redirectUris() {
return this.oidcAppRequest.toObject().redirectUrisList;
}
public set redirectUris(value: string[]) {
this.oidcAppRequest.setRedirectUrisList(value);
}
public get postLogoutUrisList() {
return this.oidcAppRequest.toObject().postLogoutRedirectUrisList;
}
public set postLogoutUrisList(value: string[]) {
this.oidcAppRequest.setPostLogoutRedirectUrisList(value);
}
public ngOnInit(): void {
this.subscription = this.route.params.subscribe((params) => this.getData(params));

View File

@ -334,11 +334,9 @@
<cnsl-redirect-uris
*ngIf="appType?.value !== undefined"
class="redirect-section"
[canWrite]="canWrite"
[disabled]="!canWrite"
[devMode]="devMode?.value"
[getValues]="requestRedirectValuesSubject$"
(changedUris)="redirectUrisList = $any($event)"
[urisList]="redirectUrisList"
[(ngModel)]="redirectUrisList"
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
[isNative]="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE"
>
@ -347,11 +345,9 @@
<cnsl-redirect-uris
*ngIf="appType?.value !== undefined"
class="redirect-section"
[canWrite]="canWrite"
[disabled]="!canWrite"
[devMode]="devMode?.value"
(changedUris)="postLogoutRedirectUrisList = $any($event)"
[urisList]="postLogoutRedirectUrisList"
[getValues]="requestRedirectValuesSubject$"
[(ngModel)]="postLogoutRedirectUrisList"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[isNative]="appType?.value === OIDCAppType.OIDC_APP_TYPE_NATIVE"
>

View File

@ -268,12 +268,6 @@
margin-bottom: 1.5rem;
}
.error {
font-size: 13px;
color: var(--warn);
margin: 0 0.5rem 1.5rem 0.5rem;
}
.btn-container {
display: flex;
justify-content: flex-end;

View File

@ -8,14 +8,14 @@
matTooltip="{{ 'ACTIONS.ADD' | translate }}"
type="submit"
mat-icon-button
[disabled]="redirectControl.invalid || !canWrite"
[disabled]="redirectControl.invalid || redirectControl.disabled"
>
<mat-icon>add</mat-icon>
</button>
</form>
<div class="redirect-uris-list">
<div *ngFor="let uri of urisList" class="uri-line" [ngClass]="{ alert: !devMode && !(uri | redirect : isNative) }">
<div *ngFor="let uri of value" class="uri-line" [ngClass]="{ alert: !devMode && !(uri | redirect : isNative) }">
<span
class="uri"
[ngClass]="{ green: !devMode && uri?.startsWith('https://'), red: !devMode && !uri?.startsWith('https://') }"
@ -29,7 +29,13 @@
"
></i>
<button matTooltip="{{ 'ACTIONS.DELETE' | translate }}" mat-icon-button (click)="remove(uri)" class="icon-button">
<button
type="button"
matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
mat-icon-button
(click)="remove(uri)"
class="icon-button"
>
<mat-icon class="icon">cancel</mat-icon>
</button>
</div>

View File

@ -1,44 +1,83 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { Observable, Subscription } from 'rxjs';
import { Component, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable, Subject, takeUntil } from 'rxjs';
@Component({
selector: 'cnsl-redirect-uris',
templateUrl: './redirect-uris.component.html',
styleUrls: ['./redirect-uris.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RedirectUrisComponent),
multi: true,
},
],
})
export class RedirectUrisComponent implements OnInit, OnDestroy {
export class RedirectUrisComponent implements ControlValueAccessor, OnInit, OnDestroy {
@Input() title: string = '';
@Input() devMode: boolean = false;
@Input() canWrite: boolean = false;
@Input() isNative!: boolean;
@Input() public urisList: string[] = [];
@Input() public redirectControl: UntypedFormControl = new UntypedFormControl({ value: '', disabled: true });
@Output() public changedUris: EventEmitter<string[]> = new EventEmitter();
@Input() public getValues: Observable<void> = new Observable();
@Input() public getValues: Observable<void> = new Observable(); // adds formfieldinput to array on emission
public redirectControl: FormControl = new FormControl<string>({ value: '', disabled: true });
private destroy$: Subject<void> = new Subject();
@ViewChild('redInput') input!: any;
private sub: Subscription = new Subscription();
constructor() {}
ngOnInit(): void {
if (this.canWrite) {
this.redirectControl.enable();
}
this.sub = this.getValues.subscribe(() => {
this.getValues.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.add(this.input.nativeElement);
});
}
ngOnDestroy(): void {
this.sub.unsubscribe();
this.destroy$.next();
this.destroy$.complete();
}
onChange: any = () => {};
onTouch: any = () => {};
private val: string[] = [];
set value(val: string[]) {
if (val !== undefined && this.val !== val) {
this.val = val;
this.onChange(val);
this.onTouch(val);
}
}
get value() {
return this.val;
}
writeValue(value: string[]) {
this.value = value;
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {
this.onTouch = fn;
}
public setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.redirectControl.disable();
} else {
this.redirectControl.enable();
}
}
public add(input: any): void {
if (this.redirectControl.valid) {
if (input.value !== '' && input.value !== ' ' && input.value !== '/') {
this.urisList.push(input.value);
this.val.push(input.value);
this.onChange(this.val);
this.onTouch(this.val);
}
if (input) {
input.value = '';
@ -47,10 +86,12 @@ export class RedirectUrisComponent implements OnInit, OnDestroy {
}
public remove(redirect: any): void {
const index = this.urisList.indexOf(redirect);
const index = this.value.indexOf(redirect);
if (index >= 0) {
this.urisList.splice(index, 1);
this.value.splice(index, 1);
this.onChange(this.value);
this.onTouch(this.value);
}
}
}

View File

@ -30,6 +30,8 @@ import {
AddIDPToLoginPolicyResponse,
AddJWTProviderRequest,
AddJWTProviderResponse,
AddLDAPProviderRequest,
AddLDAPProviderResponse,
AddMultiFactorToLoginPolicyRequest,
AddMultiFactorToLoginPolicyResponse,
AddNotificationPolicyRequest,
@ -226,6 +228,8 @@ import {
UpdateJWTProviderResponse,
UpdateLabelPolicyRequest,
UpdateLabelPolicyResponse,
UpdateLDAPProviderRequest,
UpdateLDAPProviderResponse,
UpdateLockoutPolicyRequest,
UpdateLockoutPolicyResponse,
UpdateLoginPolicyRequest,
@ -926,6 +930,14 @@ export class AdminService {
return this.grpcService.admin.updateGoogleProvider(req, null).then((resp) => resp.toObject());
}
public addLDAPProvider(req: AddLDAPProviderRequest): Promise<AddLDAPProviderResponse.AsObject> {
return this.grpcService.admin.addLDAPProvider(req, null).then((resp) => resp.toObject());
}
public updateLDAPProvider(req: UpdateLDAPProviderRequest): Promise<UpdateLDAPProviderResponse.AsObject> {
return this.grpcService.admin.updateLDAPProvider(req, null).then((resp) => resp.toObject());
}
public addGitLabProvider(req: AddGitLabProviderRequest): Promise<AddGitLabProviderResponse.AsObject> {
return this.grpcService.admin.addGitLabProvider(req, null).then((resp) => resp.toObject());
}

View File

@ -51,6 +51,8 @@ import {
AddIDPToLoginPolicyResponse,
AddJWTProviderRequest,
AddJWTProviderResponse,
AddLDAPProviderRequest,
AddLDAPProviderResponse,
AddMachineKeyRequest,
AddMachineKeyResponse,
AddMachineUserRequest,
@ -461,6 +463,8 @@ import {
UpdateHumanProfileResponse,
UpdateJWTProviderRequest,
UpdateJWTProviderResponse,
UpdateLDAPProviderRequest,
UpdateLDAPProviderResponse,
UpdateMachineRequest,
UpdateMachineResponse,
UpdateOIDCAppConfigRequest,
@ -885,6 +889,14 @@ export class ManagementService {
return this.grpcService.mgmt.updateGoogleProvider(req, null).then((resp) => resp.toObject());
}
public addLDAPProvider(req: AddLDAPProviderRequest): Promise<AddLDAPProviderResponse.AsObject> {
return this.grpcService.mgmt.addLDAPProvider(req, null).then((resp) => resp.toObject());
}
public updateLDAPProvider(req: UpdateLDAPProviderRequest): Promise<UpdateLDAPProviderResponse.AsObject> {
return this.grpcService.mgmt.updateLDAPProvider(req, null).then((resp) => resp.toObject());
}
public addGitLabProvider(req: AddGitLabProviderRequest): Promise<AddGitLabProviderResponse.AsObject> {
return this.grpcService.mgmt.addGitLabProvider(req, null).then((resp) => resp.toObject());
}

View File

@ -1648,6 +1648,10 @@
"AZUREAD": {
"TITLE": "Microsoft Provider",
"DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren Microsoft-Identitätsprovider ein."
},
"LDAP": {
"TITLE": "Active Directory / LDAP",
"DESCRIPTION": "Geben Sie die erforderlichen Daten für Ihren LDAP Provider ein."
}
},
"DETAIL": {
@ -1690,6 +1694,8 @@
"EMAILVERIFIED": "Email verifiziert",
"NAMEHINT": "Wenn angegeben, wir er im Login interface angezeigt.",
"OPTIONAL": "optional",
"LDAPATTRIBUTES": "LDAP Attribute",
"UPDATEBINDPASSWORD": "Bind Password updaten",
"UPDATECLIENTSECRET": "Client Secret updaten",
"ADD": "Identity Provider hinzufügen",
"TYPE": "Typ",
@ -1711,6 +1717,31 @@
"SCOPESLIST": "Scopes List",
"CLIENTID": "Client ID",
"CLIENTSECRET": "Client Secret",
"LDAPCONNECTION": "Verbindung",
"LDAPUSERBINDING": "User binding",
"BASEDN": "BaseDn",
"BINDDN": "BindDn",
"BINDPASSWORD": "Bind Password",
"SERVERS": "Servers",
"STARTTLS": "Start TLS",
"TIMEOUT": "Timeout in Sekunden",
"USERBASE": "Userbase",
"USERFILTERS": "User filters",
"USEROBJECTCLASSES": "User Object Classes",
"REQUIRED": "erfolderlich",
"LDAPIDATTRIBUTE": "ID attribute",
"AVATARURLATTRIBUTE": "Avatar Url attribute",
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
"LASTNAMEATTRIBUTE": "Lastname attribute",
"NICKNAMEATTRIBUTE": "Nickname attribute",
"PHONEATTRIBUTE": "Phone attribute",
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
"PREFERREDLANGUAGEATTRIBUTE": "Preferred language attribute",
"PREFERREDUSERNAMEATTRIBUTE": "Preferred username attribute",
"PROFILEATTRIBUTE": "Profile attribute",
"IDPDISPLAYNAMMAPPING": "IDP Anzeigename Mapping",
"USERNAMEMAPPING": "Username Mapping",
"DATES": "Datum",

View File

@ -1653,6 +1653,10 @@
"AZUREAD": {
"TITLE": "Microsoft Provider",
"DESCRIPTION": "Enter the credentials for your Microsoft Identity Provider"
},
"LDAP": {
"TITLE": "Active Directory / LDAP",
"DESCRIPTION": "Enter the credentials for your LDAP Provider"
}
},
"DETAIL": {
@ -1690,6 +1694,8 @@
"EMAILVERIFIED": "Email verified",
"NAMEHINT": "If specified it will be shown in the login interface.",
"OPTIONAL": "optional",
"LDAPATTRIBUTES": "LDAP Attributes",
"UPDATEBINDPASSWORD": "update Bind Password",
"UPDATECLIENTSECRET": "update client secret",
"ADD": "Add Identity Provider",
"TYPE": "Type",
@ -1711,6 +1717,31 @@
"SCOPESLIST": "Scopes List",
"CLIENTID": "Client ID",
"CLIENTSECRET": "Client Secret",
"LDAPCONNECTION": "Connection",
"LDAPUSERBINDING": "User binding",
"BASEDN": "BaseDn",
"BINDDN": "BindDn",
"BINDPASSWORD": "Bind Password",
"SERVERS": "Servers",
"STARTTLS": "Start TLS",
"TIMEOUT": "Timeout in seconds",
"USERBASE": "Userbase",
"USERFILTERS": "User filters",
"USEROBJECTCLASSES": "User Object Classes",
"REQUIRED": "required",
"LDAPIDATTRIBUTE": "ID attribute",
"AVATARURLATTRIBUTE": "Avatar Url attribute",
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
"LASTNAMEATTRIBUTE": "Lastname attribute",
"NICKNAMEATTRIBUTE": "Nickname attribute",
"PHONEATTRIBUTE": "Phone attribute",
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
"PREFERREDLANGUAGEATTRIBUTE": "Preferred language attribute",
"PREFERREDUSERNAMEATTRIBUTE": "Preferred username attribute",
"PROFILEATTRIBUTE": "Profile attribute",
"IDPDISPLAYNAMMAPPING": "IDP Display Name Mapping",
"USERNAMEMAPPING": "Username Mapping",
"DATES": "Dates",

View File

@ -1652,6 +1652,10 @@
"AZUREAD": {
"TITLE": "Fournisseur Microsoft",
"DESCRIPTION": "Saisissez les informations d'identification de votre fournisseur d'identité Microsoft"
},
"LDAP": {
"TITLE": "Active Directory / LDAP",
"DESCRIPTION": "Saisissez les informations d'identification de votre fournisseur d'identité LDAP"
}
},
"DETAIL": {
@ -1694,6 +1698,8 @@
"EMAILVERIFIED": "Courriel vérifié",
"NAMEHINT": "Si elle est spécifiée, elle sera affichée dans l'interface de connexion.",
"OPTIONAL": "optionnel",
"LDAPATTRIBUTES": "Attributs LDAP",
"UPDATEBINDPASSWORD": "mettre à jour le mot de bind password",
"UPDATECLIENTSECRET": "mise à jour du secret client",
"ADD": "Ajouter un fournisseur d'identité",
"TYPE": "Type",
@ -1715,6 +1721,31 @@
"SCOPESLIST": "Liste des champs d'application",
"CLIENTID": "ID du client",
"CLIENTSECRET": "Secret du client",
"LDAPCONNECTION": "Connexion",
"LDAPUSERBINDING": "Enchères utilisateur",
"BASEDN": "BaseDn",
"BINDDN": "BindDn",
"BINDPASSWORD": "Bind Password",
"SERVERS": "Servers",
"STARTTLS": "Start TLS",
"TIMEOUT": "Timeout in seconds",
"USERBASE": "Userbase",
"USERFILTERS": "User filters",
"USEROBJECTCLASSES": "User Object Classes",
"REQUIRED": "requis",
"LDAPIDATTRIBUTE": "ID attribute",
"AVATARURLATTRIBUTE": "Avatar Url attribute",
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
"LASTNAMEATTRIBUTE": "Lastname attribute",
"NICKNAMEATTRIBUTE": "Nickname attribute",
"PHONEATTRIBUTE": "Phone attribute",
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
"PREFERREDLANGUAGEATTRIBUTE": "Preferred language attribute",
"PREFERREDUSERNAMEATTRIBUTE": "Preferred username attribute",
"PROFILEATTRIBUTE": "Profile attribute",
"IDPDISPLAYNAMMAPPING": "Mappage de IDP nom d'affichage",
"USERNAMEMAPPING": "Mappage des noms d'utilisateur",
"DATES": "Dates",

View File

@ -1653,6 +1653,10 @@
"AZUREAD": {
"TITLE": "Microsoft Provider",
"DESCRIPTION": "Inserisci i dati necessari per il tuo Microsoft provider."
},
"LDAP": {
"TITLE": "Active Directory / LDAP",
"DESCRIPTION": "Inserisci i dati necessari per il tuo provider LDAP."
}
},
"DETAIL": {
@ -1695,6 +1699,8 @@
"EMAILVERIFIED": "Email verificata",
"NAMEHINT": "Se specificato, verrà mostrato nell'interfaccia di accesso.",
"OPTIONAL": "opzionale",
"LDAPATTRIBUTES": "Attributi LDAP",
"UPDATEBINDPASSWORD": "aggiorna Bind Password",
"UPDATECLIENTSECRET": "Aggiorna secret",
"ADD": "Aggiungi Identity Provider",
"TYPE": "Tipo",
@ -1716,6 +1722,31 @@
"SCOPESLIST": "Scope list",
"CLIENTID": "Client ID",
"CLIENTSECRET": "Client Secret",
"LDAPCONNECTION": "Connessione",
"LDAPUSERBINDING": "User binding",
"BASEDN": "BaseDn",
"BINDDN": "BindDn",
"BINDPASSWORD": "Bind Password",
"SERVERS": "Servers",
"STARTTLS": "Start TLS",
"TIMEOUT": "Timeout in seconds",
"USERBASE": "Userbase",
"USERFILTERS": "User filters",
"USEROBJECTCLASSES": "User Object Classes",
"REQUIRED": "necessario",
"LDAPIDATTRIBUTE": "ID attribute",
"AVATARURLATTRIBUTE": "Avatar Url attribute",
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
"LASTNAMEATTRIBUTE": "Lastname attribute",
"NICKNAMEATTRIBUTE": "Nickname attribute",
"PHONEATTRIBUTE": "Phone attribute",
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
"PREFERREDLANGUAGEATTRIBUTE": "Preferred language attribute",
"PREFERREDUSERNAMEATTRIBUTE": "Preferred username attribute",
"PROFILEATTRIBUTE": "Profile attribute",
"IDPDISPLAYNAMMAPPING": "Mapping del nome di visualizzazione IDP",
"USERNAMEMAPPING": "Mapping del nome utente",
"DATES": "Date",

View File

@ -1653,6 +1653,10 @@
"AZUREAD": {
"TITLE": "Microsoftプロバイダー",
"DESCRIPTION": "Microsoftプロバイダーのクレデンシャルを入力してください。"
},
"LDAP": {
"TITLE": "LDAPプロバイダー",
"DESCRIPTION": "LDAPプロバイダーのクレデンシャルを入力してください。"
}
},
"DETAIL": {
@ -1690,6 +1694,8 @@
"EMAILVERIFIED": "検証済みメールアドレス",
"NAMEHINT": "指定されている場合、ログイン画面に表示されます。",
"OPTIONAL": "オプション",
"LDAPATTRIBUTES": "LDAP 属性",
"UPDATEBINDPASSWORD": "バインドパスワードの更新",
"UPDATECLIENTSECRET": "クライアントシークレットを更新する",
"ADD": "IDプロバイダーを追加する",
"TYPE": "タイプ",
@ -1711,6 +1717,31 @@
"SCOPESLIST": "スコープリスト",
"CLIENTID": "クライアントID",
"CLIENTSECRET": "クライアントシークレット",
"LDAPCONNECTION": "繋がり",
"LDAPUSERBINDING": "ユーザーバインディング",
"BASEDN": "BaseDn",
"BINDDN": "BindDn",
"BINDPASSWORD": "Bind Password",
"SERVERS": "Servers",
"STARTTLS": "Start TLS",
"TIMEOUT": "Timeout in seconds",
"USERBASE": "Userbase",
"USERFILTERS": "User filters",
"USEROBJECTCLASSES": "User Object Classes",
"REQUIRED": "必要",
"LDAPIDATTRIBUTE": "ID attribute",
"AVATARURLATTRIBUTE": "Avatar Url attribute",
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
"LASTNAMEATTRIBUTE": "Lastname attribute",
"NICKNAMEATTRIBUTE": "Nickname attribute",
"PHONEATTRIBUTE": "Phone attribute",
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
"PREFERREDLANGUAGEATTRIBUTE": "Preferred language attribute",
"PREFERREDUSERNAMEATTRIBUTE": "Preferred username attribute",
"PROFILEATTRIBUTE": "Profile attribute",
"IDPDISPLAYNAMMAPPING": "IDP表示名マッピング",
"USERNAMEMAPPING": "ユーザー名マッピング",
"DATES": "日付",

View File

@ -1652,6 +1652,10 @@
"AZUREAD": {
"TITLE": "Microsoft Provider",
"DESCRIPTION": "Wprowadź dane dla swojego dostawcy tożsamości Microsoft"
},
"LDAP": {
"TITLE": "LDAP Provider",
"DESCRIPTION": "Wprowadź dane dla swojego dostawcy tożsamości LDAP"
}
},
"DETAIL": {
@ -1694,6 +1698,8 @@
"EMAILVERIFIED": "Email zweryfikowany",
"NAMEHINT": "Jeśli zostanie podany, będzie widoczny w interfejsie logowania.",
"OPTIONAL": "opcjonalnie",
"LDAPATTRIBUTES": "LDAP Atrybuty",
"UPDATEBINDPASSWORD": "aktualizacja bind password",
"UPDATECLIENTSECRET": "aktualizacja tajemnicy klienta",
"ADD": "Dodaj dostawcę tożsamości",
"TYPE": "Typ",
@ -1715,6 +1721,31 @@
"SCOPESLIST": "Lista zakresów",
"CLIENTID": "Identyfikator klienta",
"CLIENTSECRET": "Tajne klienta",
"LDAPCONNECTION": "Połączenie",
"LDAPUSERBINDING": "Wiązanie użytkownika",
"BASEDN": "BaseDn",
"BINDDN": "BindDn",
"BINDPASSWORD": "Bind Password",
"SERVERS": "Servers",
"STARTTLS": "Start TLS",
"TIMEOUT": "Timeout in seconds",
"USERBASE": "Userbase",
"USERFILTERS": "User filters",
"USEROBJECTCLASSES": "User Object Classes",
"REQUIRED": "wymagany",
"LDAPIDATTRIBUTE": "ID attribute",
"AVATARURLATTRIBUTE": "Avatar Url attribute",
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
"LASTNAMEATTRIBUTE": "Lastname attribute",
"NICKNAMEATTRIBUTE": "Nickname attribute",
"PHONEATTRIBUTE": "Phone attribute",
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
"PREFERREDLANGUAGEATTRIBUTE": "Preferred language attribute",
"PREFERREDUSERNAMEATTRIBUTE": "Preferred username attribute",
"PROFILEATTRIBUTE": "Profile attribute",
"IDPDISPLAYNAMMAPPING": "Mapowanie wyświetlanej nazwy IDP",
"USERNAMEMAPPING": "Mapowanie nazwy użytkownika",
"DATES": "Daty",

View File

@ -1651,6 +1651,10 @@
"AZUREAD": {
"TITLE": "Microsoft 身份提供者",
"DESCRIPTION": "输入您的 Microsoft 身份提供商的凭据"
},
"LDAP": {
"TITLE": "LDAP 身份提供者",
"DESCRIPTION": "输入您的 LDAP 身份提供商的凭据"
}
},
"DETAIL": {
@ -1693,6 +1697,8 @@
"EMAILVERIFIED": "电子邮件已验证",
"NAMEHINT": "如果指定,它将显示在登录界面。",
"OPTIONAL": "可选",
"LDAPATTRIBUTES": "LDAP 属性",
"UPDATEBINDPASSWORD": "更新绑定密码",
"UPDATECLIENTSECRET": "更新客户秘密",
"ADD": "添加身份提供者",
"TYPE": "类型",
@ -1714,6 +1720,31 @@
"SCOPESLIST": "Scopes List",
"CLIENTID": "Client ID",
"CLIENTSECRET": "Client Secret",
"LDAPCONNECTION": "联系",
"LDAPUSERBINDING": "用户绑定",
"BASEDN": "BaseDn",
"BINDDN": "BindDn",
"BINDPASSWORD": "Bind Password",
"SERVERS": "Servers",
"STARTTLS": "Start TLS",
"TIMEOUT": "Timeout in seconds",
"USERBASE": "Userbase",
"USERFILTERS": "User filters",
"USEROBJECTCLASSES": "User Object Classes",
"REQUIRED": "必需的",
"LDAPIDATTRIBUTE": "ID attribute",
"AVATARURLATTRIBUTE": "Avatar Url attribute",
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
"LASTNAMEATTRIBUTE": "Lastname attribute",
"NICKNAMEATTRIBUTE": "Nickname attribute",
"PHONEATTRIBUTE": "Phone attribute",
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
"PREFERREDLANGUAGEATTRIBUTE": "Preferred language attribute",
"PREFERREDUSERNAMEATTRIBUTE": "Preferred username attribute",
"PROFILEATTRIBUTE": "Profile attribute",
"IDPDISPLAYNAMMAPPING": "IDP 展示名称映射",
"USERNAMEMAPPING": "用户名映射",
"DATES": "日期",

View File

@ -42,6 +42,7 @@
@import 'src/app/modules/refresh-table/refresh-table.component.scss';
@import 'src/app/modules/form-field/field/form-field.component.scss';
@import 'src/app/modules/label/label.component.scss';
@import 'src/app/modules/string-list/string-list.component.scss';
@import 'src/app/modules/meta-layout/meta.scss';
@import 'src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component';
@import 'src/app/modules/accounts-card/accounts-card.component.scss';
@ -99,6 +100,7 @@
@include input-theme($theme);
@include cnsl-form-field-theme($theme);
@include contributors-theme($theme);
@include string-list-theme($theme);
@include shortcut-theme($theme);
@include message-texts-theme($theme);
@include app-detail-theme($theme);