mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-01 00:07:22 +00:00
feat(console): identity providers and login policies (#722)
* idp list, idp create route * idp modules, lazy import, i18n, routing * generic service, i18n * seperate lockout, age policy component * seperate component modules * routing * enum class * login policy * iam policy grid * login policy providers * idps login policy * add idp dialog component * add idp to loginpolicy * delete idp config, iam policy grid * remove idp from loginpolicy * idp detail component, generic idp create * lint * idp detail clientid-secrets, issuer, scopes * hide clientsecret on update * rm background style, idp config * app tooltip fix * lint * dont refresh on idp select * Update console/src/app/modules/idp-create/idp-create.component.html Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
parent
845026e43f
commit
58b01cdf3f
@ -127,6 +127,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
font-size: .9rem;
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { PasswordPolicyComponent } from './password-policy.component';
|
import { IdpCreateComponent } from './idp-create.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: PasswordPolicyComponent,
|
component: IdpCreateComponent,
|
||||||
data: { animation: 'DetailPage' },
|
data: { animation: 'DetailPage' },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -15,4 +15,4 @@ const routes: Routes = [
|
|||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class PasswordPolicyRoutingModule { }
|
export class IdpCreateRoutingModule { }
|
56
console/src/app/modules/idp-create/idp-create.component.html
Normal file
56
console/src/app/modules/idp-create/idp-create.component.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="abort-container">
|
||||||
|
<button (click)="close()" mat-icon-button>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
<span class="abort">{{ 'IDP.CREATE.TITLE' | translate }}</span><span class="abort-2">Step
|
||||||
|
{{ currentCreateStep }} of
|
||||||
|
{{ createSteps }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>{{'IDP.CREATE.TITLE' | translate}}</h1>
|
||||||
|
<p>{{'IDP.CREATE.DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<form (ngSubmit)="addIdp()">
|
||||||
|
<ng-container [formGroup]="formGroup">
|
||||||
|
<div class="content">
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.NAME' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="name" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.ISSUER' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="issuer" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p class="desc">Client Id / Client Secret</p>
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.CLIENTID' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="clientId" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.CLIENTSECRET' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="clientSecret" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.SCOPESLIST' | translate }}</mat-label>
|
||||||
|
<mat-chip-list #chipScopesList aria-label="scope selection" *ngIf="scopesList">
|
||||||
|
<mat-chip class="chip" *ngFor="let scope of scopesList.value" selectable="false" removable
|
||||||
|
(removed)="removeScope(scope)">
|
||||||
|
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
|
<input [matChipInputFor]="chipScopesList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addScope($event)">
|
||||||
|
</mat-chip-list>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<button color="primary" mat-raised-button class="continue-button" [disabled]="formGroup.invalid" type="submit">
|
||||||
|
{{ 'ACTIONS.SAVE' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
64
console/src/app/modules/idp-create/idp-create.component.scss
Normal file
64
console/src/app/modules/idp-create/idp-create.component.scss
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 4rem 4rem 2rem 4rem;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 450px) {
|
||||||
|
padding: 4rem 1rem 2rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abort-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
|
||||||
|
.abort {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abort-2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-left: 2rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-line-btn {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 -.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
flex-basis: 100%;
|
||||||
|
margin: 0 .5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #8795a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formfield {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin: 0 .5rem;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 450px) {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.continue-button {
|
||||||
|
margin-top: 3rem;
|
||||||
|
display: block;
|
||||||
|
padding: .5rem 4rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 450px) {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,20 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { PasswordPolicyComponent } from './password-policy.component';
|
import { IdpCreateComponent } from './idp-create.component';
|
||||||
|
|
||||||
describe('PasswordPolicyComponent', () => {
|
describe('IdpCreateComponent', () => {
|
||||||
let component: PasswordPolicyComponent;
|
let component: IdpCreateComponent;
|
||||||
let fixture: ComponentFixture<PasswordPolicyComponent>;
|
let fixture: ComponentFixture<IdpCreateComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [PasswordPolicyComponent],
|
declarations: [IdpCreateComponent],
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(PasswordPolicyComponent);
|
fixture = TestBed.createComponent(IdpCreateComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
142
console/src/app/modules/idp-create/idp-create.component.ts
Normal file
142
console/src/app/modules/idp-create/idp-create.component.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
import { Component, Injector, OnDestroy, OnInit, Type } from '@angular/core';
|
||||||
|
import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { MatChipInputEvent } from '@angular/material/chips';
|
||||||
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
import { OidcIdpConfigCreate } from 'src/app/proto/generated/admin_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-idp-create',
|
||||||
|
templateUrl: './idp-create.component.html',
|
||||||
|
styleUrls: ['./idp-create.component.scss'],
|
||||||
|
})
|
||||||
|
export class IdpCreateComponent implements OnInit, OnDestroy {
|
||||||
|
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||||
|
private service!: ManagementService | AdminService;
|
||||||
|
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||||
|
|
||||||
|
private subscription?: Subscription;
|
||||||
|
public projectId: string = '';
|
||||||
|
|
||||||
|
public formGroup!: FormGroup;
|
||||||
|
public createSteps: number = 1;
|
||||||
|
public currentCreateStep: number = 1;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private toast: ToastService,
|
||||||
|
private injector: Injector,
|
||||||
|
private _location: Location,
|
||||||
|
) {
|
||||||
|
this.formGroup = new FormGroup({
|
||||||
|
name: new FormControl('', [Validators.required]),
|
||||||
|
logoSrc: new FormControl('', []),
|
||||||
|
clientId: new FormControl('', [Validators.required]),
|
||||||
|
clientSecret: new FormControl('', [Validators.required]),
|
||||||
|
issuer: new FormControl('', [Validators.required]),
|
||||||
|
scopesList: new FormControl([], []),
|
||||||
|
});
|
||||||
|
|
||||||
|
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>);
|
||||||
|
break;
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.subscription = this.route.params.subscribe(params => this.getData(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.subscription?.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getData({ projectid }: Params): void {
|
||||||
|
this.projectId = projectid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addIdp(): void {
|
||||||
|
const req: OidcIdpConfigCreate = new OidcIdpConfigCreate();
|
||||||
|
|
||||||
|
req.setName(this.name?.value);
|
||||||
|
req.setClientId(this.clientId?.value);
|
||||||
|
req.setClientSecret(this.clientSecret?.value);
|
||||||
|
req.setIssuer(this.issuer?.value);
|
||||||
|
req.setLogoSrc(this.logoSrc?.value);
|
||||||
|
req.setScopesList(this.scopesList?.value);
|
||||||
|
|
||||||
|
this.service.CreateOidcIdp(req).then((idp) => {
|
||||||
|
this.router.navigate(['idp', idp.getId()]);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public close(): void {
|
||||||
|
this._location.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
public addScope(event: MatChipInputEvent): void {
|
||||||
|
const input = event.input;
|
||||||
|
const value = event.value.trim();
|
||||||
|
|
||||||
|
if (value !== '') {
|
||||||
|
if (this.scopesList?.value) {
|
||||||
|
this.scopesList.value.push(value);
|
||||||
|
if (input) {
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeScope(uri: string): void {
|
||||||
|
if (this.scopesList?.value) {
|
||||||
|
const index = this.scopesList.value.indexOf(uri);
|
||||||
|
|
||||||
|
if (index !== undefined && index >= 0) {
|
||||||
|
this.scopesList.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get logoSrc(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('logoSrc');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get clientId(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('clientId');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get clientSecret(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('clientSecret');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get issuer(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('issuer');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get scopesList(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('scopesList');
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,31 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatChipsModule } from '@angular/material/chips';
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { SearchOrgAutocompleteComponent } from './search-org-autocomplete.component';
|
import { IdpCreateRoutingModule } from './idp-create-routing.module';
|
||||||
|
import { IdpCreateComponent } from './idp-create.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [SearchOrgAutocompleteComponent],
|
declarations: [IdpCreateComponent],
|
||||||
imports: [
|
imports: [
|
||||||
|
IdpCreateRoutingModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
MatAutocompleteModule,
|
|
||||||
MatChipsModule,
|
|
||||||
MatButtonModule,
|
|
||||||
MatFormFieldModule,
|
|
||||||
MatIconModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
MatProgressSpinnerModule,
|
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatTooltipModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
],
|
],
|
||||||
exports: [
|
|
||||||
SearchOrgAutocompleteComponent,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class SearchOrgAutocompleteModule { }
|
export class IdpCreateModule { }
|
85
console/src/app/modules/idp-table/idp-table.component.html
Normal file
85
console/src/app/modules/idp-table/idp-table.component.html
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||||
|
[timestamp]="idpResult?.viewTimestamp" [selection]="selection">
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.write']" actions>
|
||||||
|
<button (click)="deactivateSelectedIdps()" matTooltip="{{'IDP.DEACTIVATE' | translate}}" class="icon-button"
|
||||||
|
mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
|
||||||
|
<mat-icon svgIcon="mdi_account_cancel"></mat-icon>
|
||||||
|
</button>
|
||||||
|
<button (click)="reactivateSelectedIdps()" matTooltip="{{'IDP.ACTIVATE' | translate}}" class="icon-button"
|
||||||
|
mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
|
||||||
|
<mat-icon svgIcon="mdi_account_check_outline"></mat-icon>
|
||||||
|
</button>
|
||||||
|
<button color="warn" (click)="removeSelectedIdps()" matTooltip="{{'IDP.DELETE' | translate}}"
|
||||||
|
class="icon-button" mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
|
||||||
|
<i class="las la-trash"></i>
|
||||||
|
</button>
|
||||||
|
<a class="add-button" [routerLink]="createRouterLink" color="primary" mat-raised-button [disabled]="disabled">
|
||||||
|
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||||
|
</a>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
|
<ng-container matColumnDef="select">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected()"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||||
|
</mat-checkbox>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let idp">
|
||||||
|
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(idp) : null" [checked]="selection.isSelected(idp)">
|
||||||
|
<img *ngIf="idp?.logoSrc?.startsWith('https://'); else genAvatar" [src]="idp.logoSrc"
|
||||||
|
alt="ipp logo {{idp?.name}}" />
|
||||||
|
<ng-template #genAvatar>
|
||||||
|
<div class="avatar">
|
||||||
|
<span>{{idp.name.charAt(0)}}</span>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</mat-checkbox>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.NAME' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let idp"> {{idp?.name}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="config">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.CONFIG' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let idp">
|
||||||
|
<div *ngFor="let elem of idp?.oidcConfig | keyvalue" class="flex-row">
|
||||||
|
<span class="key">{{elem.key}}:</span>
|
||||||
|
<span class="value">{{elem.value}}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="state">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.STATE' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let idp"> {{ 'IDP.STATES.'+idp.state | translate }} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="creationDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.CREATIONDATE' | translate }} </th>
|
||||||
|
<td class="pointer" mat-cell *matCellDef="let idp">
|
||||||
|
{{idp.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="changeDate">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.CHANGEDATE' | translate }} </th>
|
||||||
|
<td class="pointer" mat-cell *matCellDef="let idp">
|
||||||
|
{{idp.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr class="data-row" mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||||
|
[routerLink]="row.id ? [serviceType == PolicyComponentServiceType.ADMIN ? '/iam' : '/org', 'idp', row.id ]: null">
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<mat-paginator #paginator class="paginator" [length]="idpResult?.totalResult || 0" [pageSize]="10"
|
||||||
|
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</app-refresh-table>
|
77
console/src/app/modules/idp-table/idp-table.component.scss
Normal file
77
console/src/app/modules/idp-table/idp-table.component.scss
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.table,
|
||||||
|
.paginator {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: 0 1rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-row {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ffffff05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
height: 30px;
|
||||||
|
width: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.key {
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-top: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-bottom: .5rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { IdpTableComponent } from './idp-table.component';
|
||||||
|
|
||||||
|
describe('UserTableComponent', () => {
|
||||||
|
let component: IdpTableComponent;
|
||||||
|
let fixture: ComponentFixture<IdpTableComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [IdpTableComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(IdpTableComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
126
console/src/app/modules/idp-table/idp-table.component.ts
Normal file
126
console/src/app/modules/idp-table/idp-table.component.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { SelectionModel } from '@angular/cdk/collections';
|
||||||
|
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { IdpSearchResponse as AdminIdpSearchResponse, IdpView as AdminIdpView } from 'src/app/proto/generated/admin_pb';
|
||||||
|
import { IdpView as MgmtIdpView } from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-idp-table',
|
||||||
|
templateUrl: './idp-table.component.html',
|
||||||
|
styleUrls: ['./idp-table.component.scss'],
|
||||||
|
})
|
||||||
|
export class IdpTableComponent implements OnInit {
|
||||||
|
@Input() public serviceType!: PolicyComponentServiceType;
|
||||||
|
@Input() service!: AdminService | ManagementService;
|
||||||
|
@Input() disabled: boolean = false;
|
||||||
|
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||||
|
public dataSource: MatTableDataSource<AdminIdpView.AsObject | MgmtIdpView.AsObject>
|
||||||
|
= new MatTableDataSource<AdminIdpView.AsObject | MgmtIdpView.AsObject>();
|
||||||
|
public selection: SelectionModel<AdminIdpView.AsObject | MgmtIdpView.AsObject>
|
||||||
|
= new SelectionModel<AdminIdpView.AsObject | MgmtIdpView.AsObject>(true, []);
|
||||||
|
public idpResult!: AdminIdpSearchResponse.AsObject;
|
||||||
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
|
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||||
|
@Input() public displayedColumns: string[] = ['select', 'name', 'config', 'creationDate', 'changeDate', 'state'];
|
||||||
|
|
||||||
|
@Output() public changedSelection: EventEmitter<Array<AdminIdpView.AsObject | MgmtIdpView.AsObject>>
|
||||||
|
= new EventEmitter();
|
||||||
|
|
||||||
|
constructor(public translate: TranslateService, private toast: ToastService) {
|
||||||
|
this.selection.changed.subscribe(() => {
|
||||||
|
this.changedSelection.emit(this.selection.selected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.getData(10, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAllSelected(): boolean {
|
||||||
|
const numSelected = this.selection.selected.length;
|
||||||
|
const numRows = this.dataSource.data.length;
|
||||||
|
return numSelected === numRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public masterToggle(): void {
|
||||||
|
this.isAllSelected() ?
|
||||||
|
this.selection.clear() :
|
||||||
|
this.dataSource.data.forEach(row => this.selection.select(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public changePage(event: PageEvent): void {
|
||||||
|
this.getData(event.pageSize, event.pageIndex * event.pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deactivateSelectedIdps(): void {
|
||||||
|
Promise.all(this.selection.selected.map(value => {
|
||||||
|
return this.service.DeactivateIdpConfig(value.id);
|
||||||
|
})).then(() => {
|
||||||
|
this.toast.showInfo('USER.TOAST.SELECTEDDEACTIVATED', true);
|
||||||
|
this.getData(10, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public reactivateSelectedIdps(): void {
|
||||||
|
Promise.all(this.selection.selected.map(value => {
|
||||||
|
return this.service.ReactivateIdpConfig(value.id);
|
||||||
|
})).then(() => {
|
||||||
|
this.toast.showInfo('USER.TOAST.SELECTEDREACTIVATED', true);
|
||||||
|
this.getData(10, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeSelectedIdps(): void {
|
||||||
|
Promise.all(this.selection.selected.map(value => {
|
||||||
|
return this.service.RemoveIdpConfig(value.id);
|
||||||
|
})).then(() => {
|
||||||
|
this.toast.showInfo('USER.TOAST.SELECTEDDEACTIVATED', true);
|
||||||
|
this.getData(10, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData(limit: number, offset: number): Promise<void> {
|
||||||
|
this.loadingSubject.next(true);
|
||||||
|
|
||||||
|
// let query: AdminIdpSearchQuery | MgmtIdpSearchQuery;
|
||||||
|
// if (this.service instanceof AdminService) {
|
||||||
|
// query = new AdminIdpSearchQuery();
|
||||||
|
// query.setKey(AdminIdpSearchKey.)
|
||||||
|
// } else if (this.service instanceof ManagementService) {
|
||||||
|
// return ['/org', 'idp', 'create'];
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.service.SearchIdps(limit, offset).then(resp => {
|
||||||
|
this.idpResult = resp.toObject();
|
||||||
|
this.dataSource.data = this.idpResult.resultList;
|
||||||
|
console.log(this.idpResult.resultList);
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
this.loadingSubject.next(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshPage(): void {
|
||||||
|
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get createRouterLink(): RouterLink | any {
|
||||||
|
if (this.service instanceof AdminService) {
|
||||||
|
return ['/iam', 'idp', 'create'];
|
||||||
|
} else if (this.service instanceof ManagementService) {
|
||||||
|
return ['/org', 'idp', 'create'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
console/src/app/modules/idp-table/idp-table.module.ts
Normal file
42
console/src/app/modules/idp-table/idp-table.module.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||||
|
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module';
|
||||||
|
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
|
import { IdpTableComponent } from './idp-table.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [IdpTableComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
TranslateModule,
|
||||||
|
LocalizedDatePipeModule,
|
||||||
|
TimestampToDatePipeModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
RouterModule,
|
||||||
|
RefreshTableModule,
|
||||||
|
HasRoleModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
IdpTableComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class IdpTableModule { }
|
18
console/src/app/modules/idp/idp-routing.module.ts
Normal file
18
console/src/app/modules/idp/idp-routing.module.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { IdpComponent } from './idp.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: IdpComponent,
|
||||||
|
data: { animation: 'DetailPage' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class IdpRoutingModule { }
|
68
console/src/app/modules/idp/idp.component.html
Normal file
68
console/src/app/modules/idp/idp.component.html
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="abort-container">
|
||||||
|
<button (click)="close()" mat-icon-button>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
<span class="abort">{{ 'IDP.CREATE.TITLE' | translate }}</span><span class="abort-2">Step
|
||||||
|
{{ currentCreateStep }} of
|
||||||
|
{{ createSteps }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>{{'IDP.CREATE.TITLE' | translate}}</h1>
|
||||||
|
<p>{{'IDP.CREATE.DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<form (ngSubmit)="updateIdp()">
|
||||||
|
<ng-container [formGroup]="formGroup">
|
||||||
|
<div class="content">
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.ID' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="id" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.NAME' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="name" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.LOGOSRC' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="logoSrc" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.ISSUER' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="issuer" />
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<mat-checkbox class="desc" [(ngModel)]="showIdSecretSection" [ngModelOptions]="{standalone: true}">
|
||||||
|
Update Client Id / Client Secret
|
||||||
|
</mat-checkbox>
|
||||||
|
<ng-container *ngIf="showIdSecretSection">
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.CLIENTID' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="clientId" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.CLIENTSECRET' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="clientSecret" />
|
||||||
|
</mat-form-field>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'IDP.SCOPESLIST' | translate }}</mat-label>
|
||||||
|
<mat-chip-list #chipScopesList aria-label="scope selection" *ngIf="scopesList">
|
||||||
|
<mat-chip class="chip" *ngFor="let scope of scopesList.value" selectable="false" removable
|
||||||
|
(removed)="removeScope(scope)">
|
||||||
|
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
|
<input [matChipInputFor]="chipScopesList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addScope($event)">
|
||||||
|
</mat-chip-list>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<button color="primary" mat-raised-button class="continue-button" [disabled]="formGroup.invalid" type="submit">
|
||||||
|
{{ 'ACTIONS.SAVE' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
64
console/src/app/modules/idp/idp.component.scss
Normal file
64
console/src/app/modules/idp/idp.component.scss
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 4rem 4rem 2rem 4rem;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 450px) {
|
||||||
|
padding: 4rem 1rem 2rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abort-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
|
||||||
|
.abort {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abort-2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-left: 2rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-line-btn {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 -.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
flex-basis: 100%;
|
||||||
|
margin: 0 .5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #8795a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formfield {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin: 0 .5rem;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 450px) {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.continue-button {
|
||||||
|
margin-top: 3rem;
|
||||||
|
display: block;
|
||||||
|
padding: .5rem 4rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 450px) {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
}
|
25
console/src/app/modules/idp/idp.component.spec.ts
Normal file
25
console/src/app/modules/idp/idp.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { IdpComponent } from './idp.component';
|
||||||
|
|
||||||
|
describe('IdComponent', () => {
|
||||||
|
let component: IdpComponent;
|
||||||
|
let fixture: ComponentFixture<IdpComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [IdpComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(IdpComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
166
console/src/app/modules/idp/idp.component.ts
Normal file
166
console/src/app/modules/idp/idp.component.ts
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
import { Component, Injector, OnDestroy, OnInit, Type } from '@angular/core';
|
||||||
|
import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { MatChipInputEvent } from '@angular/material/chips';
|
||||||
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { switchMap, take } from 'rxjs/operators';
|
||||||
|
import { OidcIdpConfigUpdate as AdminOidcIdpConfigUpdate } from 'src/app/proto/generated/admin_pb';
|
||||||
|
import { OidcIdpConfigUpdate as MgmtOidcIdpConfigUpdate } from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-idp',
|
||||||
|
templateUrl: './idp.component.html',
|
||||||
|
styleUrls: ['./idp.component.scss'],
|
||||||
|
})
|
||||||
|
export class IdpComponent implements OnInit, OnDestroy {
|
||||||
|
public showIdSecretSection: boolean = false;
|
||||||
|
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||||
|
private service!: ManagementService | AdminService;
|
||||||
|
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||||
|
|
||||||
|
private subscription?: Subscription;
|
||||||
|
public projectId: string = '';
|
||||||
|
|
||||||
|
public formGroup!: FormGroup;
|
||||||
|
public createSteps: number = 1;
|
||||||
|
public currentCreateStep: number = 1;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
// private router: Router,
|
||||||
|
private toast: ToastService,
|
||||||
|
private injector: Injector,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private _location: Location,
|
||||||
|
) {
|
||||||
|
this.formGroup = new FormGroup({
|
||||||
|
id: new FormControl({ disabled: true, value: '' }, [Validators.required]),
|
||||||
|
name: new FormControl({ disabled: true, value: '' }, [Validators.required]),
|
||||||
|
logoSrc: new FormControl({ disabled: true, value: '' }, [Validators.required]),
|
||||||
|
clientId: new FormControl('', [Validators.required]),
|
||||||
|
clientSecret: new FormControl('', [Validators.required]),
|
||||||
|
issuer: new FormControl('', [Validators.required]),
|
||||||
|
scopesList: new FormControl([], []),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route.data.pipe(switchMap(data => {
|
||||||
|
console.log(data.serviceType);
|
||||||
|
this.serviceType = data.serviceType;
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||||
|
break;
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.route.params.pipe(take(1));
|
||||||
|
})).subscribe((params) => {
|
||||||
|
const { id } = params;
|
||||||
|
if (id) {
|
||||||
|
this.service.IdpByID(id).then(idp => {
|
||||||
|
this.formGroup.patchValue(idp.toObject());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.subscription = this.route.params.subscribe(params => this.getData(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.subscription?.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getData({ projectid }: Params): void {
|
||||||
|
this.projectId = projectid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateIdp(): void {
|
||||||
|
let req: AdminOidcIdpConfigUpdate | MgmtOidcIdpConfigUpdate;
|
||||||
|
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
req = new MgmtOidcIdpConfigUpdate();
|
||||||
|
break;
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
req = new AdminOidcIdpConfigUpdate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
req.setClientId(this.clientId?.value);
|
||||||
|
req.setClientSecret(this.clientSecret?.value);
|
||||||
|
req.setIssuer(this.issuer?.value);
|
||||||
|
req.setScopesList(this.scopesList?.value);
|
||||||
|
|
||||||
|
this.service.UpdateOidcIdpConfig(req).then((idp) => {
|
||||||
|
// this.router.navigate(['idp', ]);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public close(): void {
|
||||||
|
this._location.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
public addScope(event: MatChipInputEvent): void {
|
||||||
|
const input = event.input;
|
||||||
|
const value = event.value.trim();
|
||||||
|
|
||||||
|
if (value !== '') {
|
||||||
|
if (this.scopesList?.value) {
|
||||||
|
this.scopesList.value.push(value);
|
||||||
|
if (input) {
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeScope(uri: string): void {
|
||||||
|
if (this.scopesList?.value) {
|
||||||
|
const index = this.scopesList.value.indexOf(uri);
|
||||||
|
|
||||||
|
if (index !== undefined && index >= 0) {
|
||||||
|
this.scopesList.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get clientId(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('clientId');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get clientSecret(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('clientSecret');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get logoSrc(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('logoSrc');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get issuer(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('issuer');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get scopesList(): AbstractControl | null {
|
||||||
|
return this.formGroup.get('scopesList');
|
||||||
|
}
|
||||||
|
}
|
33
console/src/app/modules/idp/idp.module.ts
Normal file
33
console/src/app/modules/idp/idp.module.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { IdpRoutingModule } from './idp-routing.module';
|
||||||
|
import { IdpComponent } from './idp.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [IdpComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IdpRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
TranslateModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatChipsModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class IdpModule { }
|
@ -0,0 +1,36 @@
|
|||||||
|
<h1 mat-dialog-title>
|
||||||
|
<span class="title">{{'LOGINPOLICY.ADDIDP.TITLE' | translate}}</span>
|
||||||
|
</h1>
|
||||||
|
<p class="desc"> {{'LOGINPOLICY.ADDIDP.DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<mat-form-field *ngIf="serviceType == PolicyComponentServiceType.MGMT" class="full-width" appearance="outline">
|
||||||
|
<mat-label>{{ 'IDP.TYPE' | translate }}</mat-label>
|
||||||
|
<mat-select [(ngModel)]="idpType" (selectionChange)="loadIdps()" (selectionChange)="loadIdps()">
|
||||||
|
<mat-option *ngFor="let type of idpTypes" [value]="type">
|
||||||
|
{{ 'IDP.TYPES.'+type | translate}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<p>{{'LOGINPOLICY.ADDIDP.SELECTIDPS' | translate}}</p>
|
||||||
|
|
||||||
|
<mat-form-field class="full-width" appearance="outline">
|
||||||
|
<mat-label>{{ 'LOGINPOLICY.ADDIDP.SELECTIDPS' | translate }}</mat-label>
|
||||||
|
<mat-select [(ngModel)]="idp">
|
||||||
|
<mat-option *ngFor="let idp of availableIdps" [value]="idp">
|
||||||
|
{{ idp.name }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions class="action">
|
||||||
|
<button mat-button (click)="closeDialog()">
|
||||||
|
{{'ACTIONS.CANCEL' | translate}}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button [disabled]="!idp || !idpType " color="primary" mat-raised-button class="ok-button"
|
||||||
|
(click)="closeDialogWithSuccess()">
|
||||||
|
{{'ACTIONS.ADD' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -0,0 +1,25 @@
|
|||||||
|
.title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
color: #8795a1;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.ok-button {
|
||||||
|
margin-left: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AddIdpDialogComponent } from './add-idp-dialog.component';
|
||||||
|
|
||||||
|
|
||||||
|
describe('AddIdpDialogComponent', () => {
|
||||||
|
let component: AddIdpDialogComponent;
|
||||||
|
let fixture: ComponentFixture<AddIdpDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [AddIdpDialogComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AddIdpDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,84 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { IdpView as AdminIdpView } from 'src/app/proto/generated/admin_pb';
|
||||||
|
import {
|
||||||
|
Idp,
|
||||||
|
IdpProviderType,
|
||||||
|
IdpSearchKey,
|
||||||
|
IdpSearchQuery,
|
||||||
|
IdpView as MgmtIdpView,
|
||||||
|
SearchMethod,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
|
||||||
|
import { PolicyComponentServiceType } from '../../policy-component-types.enum';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-add-idp-dialog',
|
||||||
|
templateUrl: './add-idp-dialog.component.html',
|
||||||
|
styleUrls: ['./add-idp-dialog.component.scss'],
|
||||||
|
})
|
||||||
|
export class AddIdpDialogComponent {
|
||||||
|
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||||
|
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||||
|
|
||||||
|
public idpType!: IdpProviderType;
|
||||||
|
public idpTypes: IdpProviderType[] = [
|
||||||
|
IdpProviderType.IDPPROVIDERTYPE_SYSTEM,
|
||||||
|
IdpProviderType.IDPPROVIDERTYPE_ORG,
|
||||||
|
];
|
||||||
|
|
||||||
|
public idp: Idp.AsObject | undefined = undefined;
|
||||||
|
public availableIdps: Array<AdminIdpView.AsObject | MgmtIdpView.AsObject> | string[] = [];
|
||||||
|
public IdpProviderType: any = IdpProviderType;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mgmtService: ManagementService,
|
||||||
|
private adminService: AdminService,
|
||||||
|
public dialogRef: MatDialogRef<AddIdpDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||||
|
) {
|
||||||
|
if (data.serviceType) {
|
||||||
|
this.serviceType = data.serviceType;
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
this.idpType = IdpProviderType.IDPPROVIDERTYPE_ORG;
|
||||||
|
break;
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
this.idpType = IdpProviderType.IDPPROVIDERTYPE_SYSTEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadIdps();
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadIdps(): void {
|
||||||
|
this.idp = undefined;
|
||||||
|
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||||
|
const query: IdpSearchQuery = new IdpSearchQuery();
|
||||||
|
query.setKey(IdpSearchKey.IDPSEARCHKEY_PROVIDER_TYPE);
|
||||||
|
query.setMethod(SearchMethod.SEARCHMETHOD_EQUALS);
|
||||||
|
query.setValue(this.idpType.toString());
|
||||||
|
this.mgmtService.SearchIdps().then(idps => {
|
||||||
|
this.availableIdps = idps.toObject().resultList;
|
||||||
|
});
|
||||||
|
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||||
|
this.adminService.SearchIdps().then(idps => {
|
||||||
|
this.availableIdps = idps.toObject().resultList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this.dialogRef.close(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialogWithSuccess(): void {
|
||||||
|
this.dialogRef.close({
|
||||||
|
idp: this.idp,
|
||||||
|
type: this.idpType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { AddIdpDialogComponent } from './add-idp-dialog.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AddIdpDialogComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatButtonModule,
|
||||||
|
TranslateModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatSelectModule,
|
||||||
|
FormsModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddIdpDialogModule { }
|
@ -0,0 +1,20 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { LoginPolicyComponent } from './login-policy.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: LoginPolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class LoginPolicyRoutingModule { }
|
@ -0,0 +1,51 @@
|
|||||||
|
<app-detail-layout [backRouterLink]="[ '/org']" [title]="'ORG.POLICY.LOGIN_POLICY.TITLECREATE' | translate"
|
||||||
|
[description]="'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATE' | translate">
|
||||||
|
<ng-container *ngIf="(['policy.delete'] | hasRole | async) && serviceType == PolicyComponentServiceType.MGMT">
|
||||||
|
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()"
|
||||||
|
mat-stroked-button>
|
||||||
|
{{'ORG.POLICY.DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="content" *ngIf="loginData">
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="loginData.allowUsernamePassword">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWREGISTER' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="loginData.allowRegister">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWEXTERNALIDP' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="loginData.allowExternalIdp">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</p>
|
||||||
|
|
||||||
|
<div class="idps">
|
||||||
|
<div class="idp" *ngFor="let idp of idps">
|
||||||
|
<mat-icon (click)="removeIdp(idp)" class="rm">remove_circle</mat-icon>
|
||||||
|
<p>{{idp.name}}</p>
|
||||||
|
<span>{{idp.type}}</span>
|
||||||
|
<span>{{idp.configId}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="new-idp" (click)="openDialog()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container">
|
||||||
|
<button (click)="savePolicy()" color="primary" type="submit"
|
||||||
|
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</app-detail-layout>
|
@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: .5rem 0;
|
||||||
|
|
||||||
|
.left-desc {
|
||||||
|
color: #8795a1;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.length-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 3rem;
|
||||||
|
display: block;
|
||||||
|
padding: .5rem 4rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subheader {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.idps {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 -.5rem;
|
||||||
|
|
||||||
|
.idp,
|
||||||
|
.new-idp {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: .5rem;
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
border: 1px solid #8795a1;
|
||||||
|
border-radius: .5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.rm {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ffffff10;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: scale-down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoginPolicyComponent } from './login-policy.component';
|
||||||
|
|
||||||
|
describe('LoginPolicyComponent', () => {
|
||||||
|
let component: LoginPolicyComponent;
|
||||||
|
let fixture: ComponentFixture<LoginPolicyComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [LoginPolicyComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoginPolicyComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,179 @@
|
|||||||
|
import { Component, Injector, OnDestroy, Type } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
DefaultLoginPolicy,
|
||||||
|
DefaultLoginPolicyView,
|
||||||
|
IdpProviderView as AdminIdpProviderView,
|
||||||
|
IdpView as AdminIdpView,
|
||||||
|
} from 'src/app/proto/generated/admin_pb';
|
||||||
|
import {
|
||||||
|
IdpProviderType,
|
||||||
|
IdpProviderView as MgmtIdpProviderView,
|
||||||
|
IdpView as MgmtIdpView,
|
||||||
|
LoginPolicy,
|
||||||
|
LoginPolicyView,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||||
|
import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login-policy',
|
||||||
|
templateUrl: './login-policy.component.html',
|
||||||
|
styleUrls: ['./login-policy.component.scss'],
|
||||||
|
})
|
||||||
|
export class LoginPolicyComponent implements OnDestroy {
|
||||||
|
public loginData!: LoginPolicy.AsObject | DefaultLoginPolicy.AsObject;
|
||||||
|
|
||||||
|
private sub: Subscription = new Subscription();
|
||||||
|
private service!: ManagementService | AdminService;
|
||||||
|
PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||||
|
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||||
|
public idps: MgmtIdpProviderView.AsObject[] | AdminIdpProviderView.AsObject[] = [];
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private toast: ToastService,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private injector: Injector,
|
||||||
|
) {
|
||||||
|
this.sub = this.route.data.pipe(switchMap(data => {
|
||||||
|
console.log(data.serviceType);
|
||||||
|
this.serviceType = data.serviceType;
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||||
|
break;
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.route.params;
|
||||||
|
})).subscribe(() => {
|
||||||
|
this.getData().then(data => {
|
||||||
|
if (data) {
|
||||||
|
this.loginData = data.toObject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.getIdps().then(idps => {
|
||||||
|
console.log(idps);
|
||||||
|
this.idps = idps;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData():
|
||||||
|
Promise<LoginPolicyView | DefaultLoginPolicyView> {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
return (this.service as ManagementService).GetLoginPolicy();
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
return (this.service as AdminService).GetDefaultLoginPolicy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getIdps(): Promise<MgmtIdpProviderView.AsObject[] | AdminIdpProviderView.AsObject[]> {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
return (this.service as ManagementService).GetLoginPolicyIdpProviders()
|
||||||
|
.then((providers) => {
|
||||||
|
return providers.toObject().resultList;
|
||||||
|
});
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
return (this.service as AdminService).GetDefaultLoginPolicyIdpProviders()
|
||||||
|
.then((providers) => {
|
||||||
|
return providers.toObject().resultList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateData():
|
||||||
|
Promise<LoginPolicy | DefaultLoginPolicy> {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
const mgmtreq = new LoginPolicy();
|
||||||
|
mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
||||||
|
mgmtreq.setAllowRegister(this.loginData.allowRegister);
|
||||||
|
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||||
|
return (this.service as ManagementService).UpdateLoginPolicy(mgmtreq);
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
const adminreq = new DefaultLoginPolicy();
|
||||||
|
adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
|
||||||
|
adminreq.setAllowRegister(this.loginData.allowRegister);
|
||||||
|
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||||
|
return (this.service as AdminService).UpdateDefaultLoginPolicy(adminreq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public savePolicy(): void {
|
||||||
|
this.updateData().then(() => {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
break;
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
this.router.navigate(['iam']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePolicy(): Promise<Empty> {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
return (this.service as ManagementService).RemoveLoginPolicy();
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
return (this.service as AdminService).GetDefaultLoginPolicy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public openDialog(): void {
|
||||||
|
const dialogRef = this.dialog.open(AddIdpDialogComponent, {
|
||||||
|
data: {
|
||||||
|
serviceType: this.serviceType,
|
||||||
|
},
|
||||||
|
width: '400px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(resp => {
|
||||||
|
if (resp && resp.idp && resp.type) {
|
||||||
|
this.addIdp(resp.idp, resp.type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addIdp(idp: AdminIdpView.AsObject | MgmtIdpView.AsObject,
|
||||||
|
type: IdpProviderType = IdpProviderType.IDPPROVIDERTYPE_SYSTEM): Promise<any> {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
return (this.service as ManagementService).addIdpProviderToLoginPolicy(idp.id, type);
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
return (this.service as AdminService).AddIdpProviderToDefaultLoginPolicy(idp.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeIdp(idp: AdminIdpView.AsObject | MgmtIdpView.AsObject): void {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case PolicyComponentServiceType.MGMT:
|
||||||
|
(this.service as ManagementService).RemoveIdpProviderFromLoginPolicy(idp.id);
|
||||||
|
break;
|
||||||
|
case PolicyComponentServiceType.ADMIN:
|
||||||
|
(this.service as AdminService).RemoveIdpProviderFromDefaultLoginPolicy(idp.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||||
|
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||||
|
|
||||||
|
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
|
||||||
|
import { LoginPolicyRoutingModule } from './login-policy-routing.module';
|
||||||
|
import { LoginPolicyComponent } from './login-policy.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [LoginPolicyComponent],
|
||||||
|
imports: [
|
||||||
|
LoginPolicyRoutingModule,
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatIconModule,
|
||||||
|
HasRolePipeModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
TranslateModule,
|
||||||
|
DetailLayoutModule,
|
||||||
|
AddIdpDialogModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class LoginPolicyModule { }
|
@ -0,0 +1,30 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
import { PasswordAgePolicyComponent } from './password-age-policy.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: PasswordAgePolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.MODIFY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
component: PasswordAgePolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.CREATE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class PasswordAgePolicyRoutingModule { }
|
@ -0,0 +1,48 @@
|
|||||||
|
<app-detail-layout [backRouterLink]="[ '/org']" [title]="title ? (title | translate) : ''"
|
||||||
|
[description]="desc ? (desc | translate) : ''">
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||||
|
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()"
|
||||||
|
mat-stroked-button>
|
||||||
|
{{'ORG.POLICY.DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="content" *ngIf="ageData">
|
||||||
|
<mat-form-field class="description-formfield" appearance="outline">
|
||||||
|
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
||||||
|
<input matInput name="description" ngDefaultControl [(ngModel)]="ageData.description" required />
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.EXPIREWARNDAYS' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<div class="length-wrapper">
|
||||||
|
<button mat-icon-button (click)="incrementExpireWarnDays()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
<span>{{ageData?.expireWarnDays}}</span>
|
||||||
|
<button mat-icon-button (click)="decrementExpireWarnDays()">
|
||||||
|
<mat-icon>remove</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.MAXAGEDAYS' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<div class="length-wrapper">
|
||||||
|
<button mat-icon-button (click)="incrementMaxAgeDays()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
<span>{{ageData?.maxAgeDays}}</span>
|
||||||
|
<button mat-icon-button (click)="decrementMaxAgeDays()">
|
||||||
|
<mat-icon>remove</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container">
|
||||||
|
<button (click)="savePolicy()" color="primary" type="submit"
|
||||||
|
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</app-detail-layout>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PasswordAgePolicyComponent } from './password-age-policy.component';
|
||||||
|
|
||||||
|
describe('PasswordAgePolicyComponent', () => {
|
||||||
|
let component: PasswordAgePolicyComponent;
|
||||||
|
let fixture: ComponentFixture<PasswordAgePolicyComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [PasswordAgePolicyComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PasswordAgePolicyComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,126 @@
|
|||||||
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
OrgIamPolicy,
|
||||||
|
PasswordAgePolicy,
|
||||||
|
PasswordComplexityPolicy,
|
||||||
|
PasswordLockoutPolicy,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-password-age-policy',
|
||||||
|
templateUrl: './password-age-policy.component.html',
|
||||||
|
styleUrls: ['./password-age-policy.component.scss'],
|
||||||
|
})
|
||||||
|
export class PasswordAgePolicyComponent implements OnDestroy {
|
||||||
|
public title: string = '';
|
||||||
|
public desc: string = '';
|
||||||
|
|
||||||
|
componentAction: PolicyComponentAction = PolicyComponentAction.CREATE;
|
||||||
|
|
||||||
|
public PolicyComponentAction: any = PolicyComponentAction;
|
||||||
|
|
||||||
|
public ageData!: PasswordAgePolicy.AsObject;
|
||||||
|
|
||||||
|
private sub: Subscription = new Subscription();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private mgmtService: ManagementService,
|
||||||
|
private router: Router,
|
||||||
|
private toast: ToastService,
|
||||||
|
) {
|
||||||
|
this.sub = this.route.data.pipe(switchMap(data => {
|
||||||
|
this.componentAction = data.action;
|
||||||
|
return this.route.params;
|
||||||
|
})).subscribe(params => {
|
||||||
|
this.title = 'ORG.POLICY.PWD_AGE.TITLECREATE';
|
||||||
|
this.desc = 'ORG.POLICY.PWD_AGE.DESCRIPTIONCREATE';
|
||||||
|
|
||||||
|
if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
this.getData(params).then(data => {
|
||||||
|
if (data) {
|
||||||
|
this.ageData = data.toObject() as PasswordAgePolicy.AsObject;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData(params: any):
|
||||||
|
Promise<PasswordLockoutPolicy | PasswordAgePolicy | PasswordComplexityPolicy | OrgIamPolicy | undefined> {
|
||||||
|
this.title = 'ORG.POLICY.PWD_AGE.TITLE';
|
||||||
|
this.desc = 'ORG.POLICY.PWD_AGE.DESCRIPTION';
|
||||||
|
return this.mgmtService.GetPasswordAgePolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePolicy(): void {
|
||||||
|
this.mgmtService.DeletePasswordAgePolicy(this.ageData.id).then(() => {
|
||||||
|
this.toast.showInfo('Successfully deleted');
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public incrementExpireWarnDays(): void {
|
||||||
|
if (this.ageData?.expireWarnDays !== undefined) {
|
||||||
|
this.ageData.expireWarnDays++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public decrementExpireWarnDays(): void {
|
||||||
|
if (this.ageData?.expireWarnDays && this.ageData?.expireWarnDays > 0) {
|
||||||
|
this.ageData.expireWarnDays--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public incrementMaxAgeDays(): void {
|
||||||
|
if (this.ageData?.maxAgeDays !== undefined) {
|
||||||
|
this.ageData.maxAgeDays++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public decrementMaxAgeDays(): void {
|
||||||
|
if (this.ageData?.maxAgeDays && this.ageData?.maxAgeDays > 0) {
|
||||||
|
this.ageData.maxAgeDays--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public savePolicy(): void {
|
||||||
|
if (this.componentAction === PolicyComponentAction.CREATE) {
|
||||||
|
|
||||||
|
this.mgmtService.CreatePasswordAgePolicy(
|
||||||
|
this.ageData.description,
|
||||||
|
this.ageData.maxAgeDays,
|
||||||
|
this.ageData.expireWarnDays,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
|
||||||
|
this.mgmtService.UpdatePasswordAgePolicy(
|
||||||
|
this.ageData.description,
|
||||||
|
this.ageData.maxAgeDays,
|
||||||
|
this.ageData.expireWarnDays,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,13 +11,13 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||||
|
|
||||||
import { PasswordPolicyRoutingModule } from './password-policy-routing.module';
|
import { PasswordAgePolicyRoutingModule } from './password-age-policy-routing.module';
|
||||||
import { PasswordPolicyComponent } from './password-policy.component';
|
import { PasswordAgePolicyComponent } from './password-age-policy.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [PasswordPolicyComponent],
|
declarations: [PasswordAgePolicyComponent],
|
||||||
imports: [
|
imports: [
|
||||||
PasswordPolicyRoutingModule,
|
PasswordAgePolicyRoutingModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
@ -31,4 +31,4 @@ import { PasswordPolicyComponent } from './password-policy.component';
|
|||||||
DetailLayoutModule,
|
DetailLayoutModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PasswordPolicyModule { }
|
export class PasswordAgePolicyModule { }
|
@ -0,0 +1,30 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: PasswordComplexityPolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.MODIFY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
component: PasswordComplexityPolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.CREATE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class PasswordComplexityPolicyRoutingModule { }
|
@ -0,0 +1,60 @@
|
|||||||
|
<app-detail-layout [backRouterLink]="[ '/org']" [title]="title ? (title | translate) : ''"
|
||||||
|
[description]="desc ? (desc | translate) : ''">
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||||
|
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()"
|
||||||
|
mat-stroked-button>
|
||||||
|
{{'ORG.POLICY.DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div *ngIf="complexityData" class="content">
|
||||||
|
<mat-form-field class="description-formfield" appearance="outline">
|
||||||
|
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
||||||
|
<input matInput name="description" ngDefaultControl [(ngModel)]="complexityData.description" required />
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.MINLENGTH' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<div class="length-wrapper">
|
||||||
|
<button mat-icon-button (click)="decrementLength()">
|
||||||
|
<mat-icon>remove</mat-icon>
|
||||||
|
</button>
|
||||||
|
<span>{{complexityData?.minLength}}</span>
|
||||||
|
<button mat-icon-button (click)="incrementLength()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.HASNUMBER' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="complexityData.hasNumber">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.HASSYMBOL' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasSymbol" ngDefaultControl [(ngModel)]="complexityData.hasSymbol">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.HASLOWERCASE' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasLowercase" ngDefaultControl
|
||||||
|
[(ngModel)]="complexityData.hasLowercase">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.HASUPPERCASE' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasUppercase" ngDefaultControl
|
||||||
|
[(ngModel)]="complexityData.hasUppercase">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container">
|
||||||
|
<button (click)="savePolicy()" color="primary" type="submit" [disabled]="!complexityData?.description"
|
||||||
|
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</app-detail-layout>
|
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: .5rem 0;
|
||||||
|
|
||||||
|
.left-desc {
|
||||||
|
color: #8795a1;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.length-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 3rem;
|
||||||
|
display: block;
|
||||||
|
padding: .5rem 4rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||||
|
|
||||||
|
describe('PasswordComplexityPolicyComponent', () => {
|
||||||
|
let component: PasswordComplexityPolicyComponent;
|
||||||
|
let fixture: ComponentFixture<PasswordComplexityPolicyComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [PasswordComplexityPolicyComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PasswordComplexityPolicyComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,119 @@
|
|||||||
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
OrgIamPolicy,
|
||||||
|
PasswordAgePolicy,
|
||||||
|
PasswordComplexityPolicy,
|
||||||
|
PasswordLockoutPolicy,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-password-policy',
|
||||||
|
templateUrl: './password-complexity-policy.component.html',
|
||||||
|
styleUrls: ['./password-complexity-policy.component.scss'],
|
||||||
|
})
|
||||||
|
export class PasswordComplexityPolicyComponent implements OnDestroy {
|
||||||
|
public title: string = '';
|
||||||
|
public desc: string = '';
|
||||||
|
|
||||||
|
componentAction: PolicyComponentAction = PolicyComponentAction.CREATE;
|
||||||
|
|
||||||
|
public PolicyComponentAction: any = PolicyComponentAction;
|
||||||
|
|
||||||
|
public complexityData!: PasswordComplexityPolicy.AsObject;
|
||||||
|
|
||||||
|
private sub: Subscription = new Subscription();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private mgmtService: ManagementService,
|
||||||
|
private router: Router,
|
||||||
|
private toast: ToastService,
|
||||||
|
) {
|
||||||
|
this.sub = this.route.data.pipe(switchMap(data => {
|
||||||
|
this.componentAction = data.action;
|
||||||
|
return this.route.params;
|
||||||
|
})).subscribe(params => {
|
||||||
|
this.title = 'ORG.POLICY.PWD_COMPLEXITY.TITLECREATE';
|
||||||
|
this.desc = 'ORG.POLICY.PWD_COMPLEXITY.DESCRIPTIONCREATE';
|
||||||
|
|
||||||
|
if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
this.getData(params).then(data => {
|
||||||
|
if (data) {
|
||||||
|
this.complexityData = data.toObject() as PasswordComplexityPolicy.AsObject;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData(params: any):
|
||||||
|
Promise<PasswordLockoutPolicy | PasswordAgePolicy | PasswordComplexityPolicy | OrgIamPolicy | undefined> {
|
||||||
|
this.title = 'ORG.POLICY.PWD_COMPLEXITY.TITLE';
|
||||||
|
this.desc = 'ORG.POLICY.PWD_COMPLEXITY.DESCRIPTION';
|
||||||
|
return this.mgmtService.GetPasswordComplexityPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePolicy(): void {
|
||||||
|
this.mgmtService.DeletePasswordComplexityPolicy(this.complexityData.id).then(() => {
|
||||||
|
this.toast.showInfo('Successfully deleted');
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public incrementLength(): void {
|
||||||
|
if (this.complexityData?.minLength !== undefined && this.complexityData?.minLength <= 72) {
|
||||||
|
this.complexityData.minLength++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public decrementLength(): void {
|
||||||
|
if (this.complexityData?.minLength && this.complexityData?.minLength > 1) {
|
||||||
|
this.complexityData.minLength--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public savePolicy(): void {
|
||||||
|
if (this.componentAction === PolicyComponentAction.CREATE) {
|
||||||
|
|
||||||
|
this.mgmtService.CreatePasswordComplexityPolicy(
|
||||||
|
this.complexityData.description,
|
||||||
|
this.complexityData.hasLowercase,
|
||||||
|
this.complexityData.hasUppercase,
|
||||||
|
this.complexityData.hasNumber,
|
||||||
|
this.complexityData.hasSymbol,
|
||||||
|
this.complexityData.minLength,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
|
||||||
|
this.mgmtService.UpdatePasswordComplexityPolicy(
|
||||||
|
this.complexityData.description,
|
||||||
|
this.complexityData.hasLowercase,
|
||||||
|
this.complexityData.hasUppercase,
|
||||||
|
this.complexityData.hasNumber,
|
||||||
|
this.complexityData.hasSymbol,
|
||||||
|
this.complexityData.minLength,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||||
|
|
||||||
|
import { PasswordComplexityPolicyRoutingModule } from './password-complexity-policy-routing.module';
|
||||||
|
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [PasswordComplexityPolicyComponent],
|
||||||
|
imports: [
|
||||||
|
PasswordComplexityPolicyRoutingModule,
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatIconModule,
|
||||||
|
HasRoleModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
TranslateModule,
|
||||||
|
DetailLayoutModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class PasswordComplexityPolicyModule { }
|
@ -0,0 +1,30 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
import { PasswordIamPolicyComponent } from './password-iam-policy.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: PasswordIamPolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.MODIFY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
component: PasswordIamPolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.CREATE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class PasswordIamPolicyRoutingModule { }
|
@ -0,0 +1,28 @@
|
|||||||
|
<app-detail-layout [backRouterLink]="[ '/org']" [title]="title ? (title | translate) : ''"
|
||||||
|
[description]="desc ? (desc | translate) : ''">
|
||||||
|
<!-- <ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||||
|
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()"
|
||||||
|
mat-stroked-button>
|
||||||
|
{{'ORG.POLICY.DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
</ng-template> -->
|
||||||
|
|
||||||
|
<div class="content" *ngIf="iamData">
|
||||||
|
<mat-form-field class="description-formfield" appearance="outline">
|
||||||
|
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
||||||
|
<input matInput name="description" ngDefaultControl [(ngModel)]="iamData.description" required />
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="iamData.userLoginMustBeDomain">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container">
|
||||||
|
<button (click)="savePolicy()" color="primary" type="submit" [disabled]="!iamData?.description"
|
||||||
|
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</app-detail-layout>
|
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: .5rem 0;
|
||||||
|
|
||||||
|
.left-desc {
|
||||||
|
color: #8795a1;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.length-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 3rem;
|
||||||
|
display: block;
|
||||||
|
padding: .5rem 4rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PasswordIamPolicyComponent } from './password-iam-policy.component';
|
||||||
|
|
||||||
|
describe('PasswordIamPolicyComponent', () => {
|
||||||
|
let component: PasswordIamPolicyComponent;
|
||||||
|
let fixture: ComponentFixture<PasswordIamPolicyComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [PasswordIamPolicyComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PasswordIamPolicyComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,102 @@
|
|||||||
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
OrgIamPolicy,
|
||||||
|
PasswordAgePolicy,
|
||||||
|
PasswordComplexityPolicy,
|
||||||
|
PasswordLockoutPolicy,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { StorageService } from 'src/app/services/storage.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-password-iam-policy',
|
||||||
|
templateUrl: './password-iam-policy.component.html',
|
||||||
|
styleUrls: ['./password-iam-policy.component.scss'],
|
||||||
|
})
|
||||||
|
export class PasswordIamPolicyComponent implements OnDestroy {
|
||||||
|
public title: string = '';
|
||||||
|
public desc: string = '';
|
||||||
|
|
||||||
|
componentAction: PolicyComponentAction = PolicyComponentAction.CREATE;
|
||||||
|
|
||||||
|
public PolicyComponentAction: any = PolicyComponentAction;
|
||||||
|
|
||||||
|
public iamData!: OrgIamPolicy.AsObject;
|
||||||
|
|
||||||
|
private sub: Subscription = new Subscription();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private mgmtService: ManagementService,
|
||||||
|
private adminService: AdminService,
|
||||||
|
private router: Router,
|
||||||
|
private toast: ToastService,
|
||||||
|
private sessionStorage: StorageService,
|
||||||
|
) {
|
||||||
|
this.sub = this.route.data.pipe(switchMap(data => {
|
||||||
|
this.componentAction = data.action;
|
||||||
|
console.log(data.action);
|
||||||
|
return this.route.params;
|
||||||
|
})).subscribe(params => {
|
||||||
|
this.title = 'ORG.POLICY.IAM_POLICY.TITLECREATE';
|
||||||
|
this.desc = 'ORG.POLICY.IAM_POLICY.DESCRIPTIONCREATE';
|
||||||
|
|
||||||
|
if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
this.getData(params).then(data => {
|
||||||
|
if (data) {
|
||||||
|
this.iamData = data.toObject() as OrgIamPolicy.AsObject;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData(params: any):
|
||||||
|
Promise<PasswordLockoutPolicy | PasswordAgePolicy | PasswordComplexityPolicy | OrgIamPolicy | undefined> {
|
||||||
|
|
||||||
|
this.title = 'ORG.POLICY.IAM_POLICY.TITLECREATE';
|
||||||
|
this.desc = 'ORG.POLICY.IAM_POLICY.DESCRIPTIONCREATE';
|
||||||
|
return this.mgmtService.GetMyOrgIamPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public savePolicy(): void {
|
||||||
|
if (this.componentAction === PolicyComponentAction.CREATE) {
|
||||||
|
const orgId = this.sessionStorage.getItem('organization');
|
||||||
|
if (orgId) {
|
||||||
|
this.adminService.CreateOrgIamPolicy(
|
||||||
|
orgId,
|
||||||
|
this.iamData.description,
|
||||||
|
this.iamData.userLoginMustBeDomain,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
const orgId = this.sessionStorage.getItem('organization');
|
||||||
|
if (orgId) {
|
||||||
|
this.adminService.UpdateOrgIamPolicy(
|
||||||
|
orgId,
|
||||||
|
this.iamData.description,
|
||||||
|
this.iamData.userLoginMustBeDomain,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||||
|
|
||||||
|
import { PasswordIamPolicyRoutingModule } from './password-iam-policy-routing.module';
|
||||||
|
import { PasswordIamPolicyComponent } from './password-iam-policy.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [PasswordIamPolicyComponent],
|
||||||
|
imports: [
|
||||||
|
PasswordIamPolicyRoutingModule,
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatIconModule,
|
||||||
|
HasRoleModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
TranslateModule,
|
||||||
|
DetailLayoutModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class PasswordIamPolicyModule { }
|
@ -0,0 +1,30 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: PasswordLockoutPolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.MODIFY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
component: PasswordLockoutPolicyComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
action: PolicyComponentAction.CREATE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class PasswordLockoutPolicyRoutingModule { }
|
@ -0,0 +1,41 @@
|
|||||||
|
<app-detail-layout [backRouterLink]="[ '/org']" [title]="title ? (title | translate) : ''"
|
||||||
|
[description]="desc ? (desc | translate) : ''">
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||||
|
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()"
|
||||||
|
mat-stroked-button>
|
||||||
|
{{'ORG.POLICY.DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="content" *ngIf="lockoutData">
|
||||||
|
<mat-form-field class="description-formfield" appearance="outline">
|
||||||
|
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
||||||
|
<input matInput name="description" ngDefaultControl [(ngModel)]="lockoutData.description" required />
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.MAXATTEMPTS' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<div class="length-wrapper">
|
||||||
|
<button mat-icon-button (click)="incrementMaxAttempts()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
<span>{{lockoutData?.maxAttempts}}</span>
|
||||||
|
<button mat-icon-button (click)="decrementMaxAttempts()">
|
||||||
|
<mat-icon>remove</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'ORG.POLICY.DATA.SHOWLOCKOUTFAILURES' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="showLockOutFailures" ngDefaultControl
|
||||||
|
[(ngModel)]="lockoutData.showLockOutFailures">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container">
|
||||||
|
<button (click)="savePolicy()" color="primary" type="submit"
|
||||||
|
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</app-detail-layout>
|
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: .5rem 0;
|
||||||
|
|
||||||
|
.left-desc {
|
||||||
|
color: #8795a1;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.length-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 3rem;
|
||||||
|
display: block;
|
||||||
|
padding: .5rem 4rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component';
|
||||||
|
|
||||||
|
describe('PasswordLockoutPolicyComponent', () => {
|
||||||
|
let component: PasswordLockoutPolicyComponent;
|
||||||
|
let fixture: ComponentFixture<PasswordLockoutPolicyComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [PasswordLockoutPolicyComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PasswordLockoutPolicyComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,113 @@
|
|||||||
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
|
import { FormGroup } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
OrgIamPolicy,
|
||||||
|
PasswordAgePolicy,
|
||||||
|
PasswordComplexityPolicy,
|
||||||
|
PasswordLockoutPolicy,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
|
import { PolicyComponentAction } from '../policy-component-action.enum';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-password-lockout-policy',
|
||||||
|
templateUrl: './password-lockout-policy.component.html',
|
||||||
|
styleUrls: ['./password-lockout-policy.component.scss'],
|
||||||
|
})
|
||||||
|
export class PasswordLockoutPolicyComponent implements OnDestroy {
|
||||||
|
public title: string = '';
|
||||||
|
public desc: string = '';
|
||||||
|
|
||||||
|
componentAction: PolicyComponentAction = PolicyComponentAction.CREATE;
|
||||||
|
|
||||||
|
public PolicyComponentAction: any = PolicyComponentAction;
|
||||||
|
|
||||||
|
public lockoutForm!: FormGroup;
|
||||||
|
public lockoutData!: PasswordLockoutPolicy.AsObject;
|
||||||
|
private sub: Subscription = new Subscription();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private mgmtService: ManagementService,
|
||||||
|
private router: Router,
|
||||||
|
private toast: ToastService,
|
||||||
|
) {
|
||||||
|
this.sub = this.route.data.pipe(switchMap(data => {
|
||||||
|
this.componentAction = data.action;
|
||||||
|
return this.route.params;
|
||||||
|
})).subscribe(params => {
|
||||||
|
this.title = 'ORG.POLICY.PWD_LOCKOUT.TITLECREATE';
|
||||||
|
this.desc = 'ORG.POLICY.PWD_LOCKOUT.DESCRIPTIONCREATE';
|
||||||
|
|
||||||
|
if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
this.getData(params).then(data => {
|
||||||
|
if (data) {
|
||||||
|
this.lockoutData = data.toObject() as PasswordLockoutPolicy.AsObject;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData(params: any):
|
||||||
|
Promise<PasswordLockoutPolicy | PasswordAgePolicy | PasswordComplexityPolicy | OrgIamPolicy | undefined> {
|
||||||
|
|
||||||
|
this.title = 'ORG.POLICY.PWD_LOCKOUT.TITLE';
|
||||||
|
this.desc = 'ORG.POLICY.PWD_LOCKOUT.DESCRIPTION';
|
||||||
|
return this.mgmtService.GetPasswordLockoutPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePolicy(): void {
|
||||||
|
this.mgmtService.DeletePasswordLockoutPolicy(this.lockoutData.id).then(() => {
|
||||||
|
this.toast.showInfo('Successfully deleted');
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public incrementMaxAttempts(): void {
|
||||||
|
if (this.lockoutData?.maxAttempts !== undefined) {
|
||||||
|
this.lockoutData.maxAttempts++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public decrementMaxAttempts(): void {
|
||||||
|
if (this.lockoutData?.maxAttempts && this.lockoutData?.maxAttempts > 0) {
|
||||||
|
this.lockoutData.maxAttempts--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public savePolicy(): void {
|
||||||
|
if (this.componentAction === PolicyComponentAction.CREATE) {
|
||||||
|
this.mgmtService.CreatePasswordLockoutPolicy(
|
||||||
|
this.lockoutData.description,
|
||||||
|
this.lockoutData.maxAttempts,
|
||||||
|
this.lockoutData.showLockOutFailures,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
} else if (this.componentAction === PolicyComponentAction.MODIFY) {
|
||||||
|
|
||||||
|
this.mgmtService.UpdatePasswordLockoutPolicy(
|
||||||
|
this.lockoutData.description,
|
||||||
|
this.lockoutData.maxAttempts,
|
||||||
|
this.lockoutData.showLockOutFailures,
|
||||||
|
).then(() => {
|
||||||
|
this.router.navigate(['org']);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||||
|
|
||||||
|
import { PasswordLockoutPolicyRoutingModule } from './password-lockout-policy-routing.module';
|
||||||
|
import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [PasswordLockoutPolicyComponent],
|
||||||
|
imports: [
|
||||||
|
PasswordLockoutPolicyRoutingModule,
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatIconModule,
|
||||||
|
HasRoleModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
TranslateModule,
|
||||||
|
DetailLayoutModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class PasswordLockoutPolicyModule { }
|
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
export enum PolicyComponentAction {
|
||||||
|
CREATE = 'create',
|
||||||
|
MODIFY = 'modify',
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
export enum PolicyComponentType {
|
||||||
|
LOCKOUT = 'lockout',
|
||||||
|
AGE = 'age',
|
||||||
|
COMPLEXITY = 'complexity',
|
||||||
|
IAM = 'iam',
|
||||||
|
LOGIN = 'login',
|
||||||
|
}
|
||||||
|
export enum PolicyComponentServiceType {
|
||||||
|
MGMT = 'mgmt',
|
||||||
|
ADMIN = 'admin',
|
||||||
|
}
|
@ -20,7 +20,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table mat-table class="background-style table" aria-label="Elements" [dataSource]="dataSource">
|
<table mat-table class="table" aria-label="Elements" [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -88,7 +88,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<mat-paginator *ngIf="dataSource" class="paginator background-style" #paginator [pageSize]="INITIALPAGESIZE"
|
<mat-paginator *ngIf="dataSource" class="paginator" #paginator [pageSize]="INITIALPAGESIZE"
|
||||||
[length]="dataSource.totalResult" [pageSizeOptions]="[25, 50, 100, 250]" (page)="changePage($event)">
|
[length]="dataSource.totalResult" [pageSizeOptions]="[25, 50, 100, 250]" (page)="changePage($event)">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<app-refresh-table *ngIf="eventDataSource" (refreshed)="loadEvents()" [dataSize]="eventDataSource.data.length"
|
<app-refresh-table *ngIf="eventDataSource" (refreshed)="loadEvents()" [dataSize]="eventDataSource.data.length"
|
||||||
[loading]="loading$ | async">
|
[loading]="loading$ | async">
|
||||||
|
|
||||||
<table [dataSource]="eventDataSource" mat-table class="table background-style" aria-label="Elements">
|
<table [dataSource]="eventDataSource" mat-table class="table " aria-label="Elements">
|
||||||
<ng-container matColumnDef="viewName">
|
<ng-container matColumnDef="viewName">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'IAM.FAILEDEVENTS.VIEWNAME' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'IAM.FAILEDEVENTS.VIEWNAME' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let event"> {{event.viewName}} </td>
|
<td mat-cell *matCellDef="let event"> {{event.viewName}} </td>
|
||||||
@ -47,8 +47,7 @@
|
|||||||
<tr mat-header-row *matHeaderRowDef="eventDisplayedColumns"></tr>
|
<tr mat-header-row *matHeaderRowDef="eventDisplayedColumns"></tr>
|
||||||
<tr mat-row *matRowDef="let row; columns: eventDisplayedColumns;"></tr>
|
<tr mat-row *matRowDef="let row; columns: eventDisplayedColumns;"></tr>
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator class="paginator background-style" [pageSize]="10" #paginator
|
<mat-paginator class="paginator" [pageSize]="10" #paginator [pageSizeOptions]="[10, 20, 100, 250]">
|
||||||
[pageSizeOptions]="[10, 20, 100, 250]">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
||||||
</div>
|
</div>
|
@ -19,7 +19,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table mat-table class="background-style table" aria-label="Elements" [dataSource]="dataSource">
|
<table mat-table class="table" aria-label="Elements" [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -80,8 +80,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<mat-paginator class="background-style paginator" #paginator [pageSize]="50"
|
<mat-paginator class="paginator" #paginator [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
||||||
[pageSizeOptions]="[25, 50, 100, 250]">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<h1>{{'ORG.POLICY.TITLE' | translate}}</h1>
|
||||||
|
|
||||||
|
<p class="top-desc">{{'ORG.POLICY.DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<div class="row-lyt">
|
||||||
|
<ng-template appHasRole [appHasRole]="['policy.read']">
|
||||||
|
<div class="p-item card">
|
||||||
|
<div class="avatar">
|
||||||
|
<i class="icon las la-gem"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<span>{{'ORG.POLICY.LOGIN_POLICY.TITLE' | translate}}</span>
|
||||||
|
<button mat-icon-button disabled>
|
||||||
|
<i *ngIf="loginPolicy" class="icon las la-check-circle"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="desc">
|
||||||
|
{{'ORG.POLICY.LOGIN_POLICY.DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<div class="btn-wrapper">
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||||
|
<button [disabled]="!loginPolicy" [routerLink]="[ 'policy', PolicyComponentType.LOGIN ]"
|
||||||
|
mat-stroked-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
@ -0,0 +1,76 @@
|
|||||||
|
h1 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-desc {
|
||||||
|
color: #8795a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-lyt {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0 -1rem;
|
||||||
|
|
||||||
|
.p-item {
|
||||||
|
flex-basis: 300px;
|
||||||
|
margin: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 200px;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
@media only screen and (max-width: 450px) {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(40deg, rgb(129, 85, 185) 30%, #8983f7);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
|
||||||
|
.icon,
|
||||||
|
i {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
line-height: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
font-size: .9rem;
|
||||||
|
color: #8795a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrapper {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-right: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { IamPolicyGridComponent } from './iam-policy-grid.component';
|
||||||
|
|
||||||
|
describe('IamPolicyGridComponent', () => {
|
||||||
|
let component: IamPolicyGridComponent;
|
||||||
|
let fixture: ComponentFixture<IamPolicyGridComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [IamPolicyGridComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(IamPolicyGridComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,27 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
|
import { DefaultLoginPolicy } from 'src/app/proto/generated/admin_pb';
|
||||||
|
import { PolicyState } from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-iam-policy-grid',
|
||||||
|
templateUrl: './iam-policy-grid.component.html',
|
||||||
|
styleUrls: ['./iam-policy-grid.component.scss'],
|
||||||
|
})
|
||||||
|
export class IamPolicyGridComponent {
|
||||||
|
public loginPolicy!: DefaultLoginPolicy.AsObject;
|
||||||
|
|
||||||
|
public PolicyState: any = PolicyState;
|
||||||
|
public PolicyComponentType: any = PolicyComponentType;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private adminService: AdminService,
|
||||||
|
) {
|
||||||
|
this.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getData(): void {
|
||||||
|
this.adminService.GetDefaultLoginPolicy().then(data => this.loginPolicy = data.toObject());
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
|
|||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { AuthGuard } from 'src/app/guards/auth.guard';
|
import { AuthGuard } from 'src/app/guards/auth.guard';
|
||||||
import { RoleGuard } from 'src/app/guards/role.guard';
|
import { RoleGuard } from 'src/app/guards/role.guard';
|
||||||
|
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
|
|
||||||
import { IamComponent } from './iam.component';
|
import { IamComponent } from './iam.component';
|
||||||
|
|
||||||
@ -22,6 +23,32 @@ const routes: Routes = [
|
|||||||
roles: ['iam.member.read'],
|
roles: ['iam.member.read'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'idp/create',
|
||||||
|
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||||
|
canActivate: [AuthGuard, RoleGuard],
|
||||||
|
data: {
|
||||||
|
roles: ['iam.idp.write'],
|
||||||
|
serviceType: PolicyComponentServiceType.ADMIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'idp/:id',
|
||||||
|
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||||
|
canActivate: [AuthGuard, RoleGuard],
|
||||||
|
data: {
|
||||||
|
roles: ['iam.idp.read'],
|
||||||
|
serviceType: PolicyComponentServiceType.ADMIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `policy/${PolicyComponentType.LOGIN}`,
|
||||||
|
data: {
|
||||||
|
serviceType: PolicyComponentServiceType.ADMIN,
|
||||||
|
},
|
||||||
|
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||||
|
.then(m => m.LoginPolicyModule),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<app-refresh-table *ngIf="dataSource" (refreshed)="loadViews()" [dataSize]="dataSource.data.length"
|
<app-refresh-table *ngIf="dataSource" (refreshed)="loadViews()" [dataSize]="dataSource.data.length"
|
||||||
[loading]="loading$ | async">
|
[loading]="loading$ | async">
|
||||||
|
|
||||||
<table [dataSource]="dataSource" mat-table class="table background-style" aria-label="Elements">
|
<table [dataSource]="dataSource" mat-table class="table" aria-label="Elements">
|
||||||
<ng-container matColumnDef="viewName">
|
<ng-container matColumnDef="viewName">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'IAM.VIEWS.VIEWNAME' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'IAM.VIEWS.VIEWNAME' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let view"> {{view.viewName}} </td>
|
<td mat-cell *matCellDef="let view"> {{view.viewName}} </td>
|
||||||
@ -40,8 +40,7 @@
|
|||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator class="paginator background-style" [pageSize]="10" #paginator
|
<mat-paginator class="paginator" [pageSize]="10" #paginator [pageSizeOptions]="[10, 20, 100, 250]">
|
||||||
[pageSizeOptions]="[10, 20, 100, 250]">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
||||||
</div>
|
</div>
|
@ -3,6 +3,16 @@
|
|||||||
<h1 class="h1">{{'IAM.DETAIL.TITLE' | translate}}</h1>
|
<h1 class="h1">{{'IAM.DETAIL.TITLE' | translate}}</h1>
|
||||||
<p class="sub">{{'IAM.DETAIL.DESCRIPTION' | translate}} </p>
|
<p class="sub">{{'IAM.DETAIL.DESCRIPTION' | translate}} </p>
|
||||||
|
|
||||||
|
<app-iam-policy-grid></app-iam-policy-grid>
|
||||||
|
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.idp.read']">
|
||||||
|
<app-card title="{{ 'IDP.LIST.TITLE' | translate }}" description="{{ 'IDP.LIST.DESCRIPTION' | translate }}">
|
||||||
|
<app-idp-table [service]="adminService" [serviceType]="PolicyComponentServiceType.ADMIN"
|
||||||
|
[disabled]="(['org.idp.write'] | hasRole | async) == false">
|
||||||
|
</app-idp-table>
|
||||||
|
</app-card>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<app-card title="{{ 'IAM.VIEWS.TITLE' | translate }}" description="{{ 'IAM.VIEWS.DESCRIPTION' | translate }}">
|
<app-card title="{{ 'IAM.VIEWS.TITLE' | translate }}" description="{{ 'IAM.VIEWS.DESCRIPTION' | translate }}">
|
||||||
<app-iam-views></app-iam-views>
|
<app-iam-views></app-iam-views>
|
||||||
</app-card>
|
</app-card>
|
||||||
|
@ -4,6 +4,7 @@ import { Router } from '@angular/router';
|
|||||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||||
import { catchError, finalize, map } from 'rxjs/operators';
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||||
|
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
import { OrgMemberView, UserView } from 'src/app/proto/generated/management_pb';
|
import { OrgMemberView, UserView } from 'src/app/proto/generated/management_pb';
|
||||||
import { AdminService } from 'src/app/services/admin.service';
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
@ -14,13 +15,14 @@ import { ToastService } from 'src/app/services/toast.service';
|
|||||||
styleUrls: ['./iam.component.scss'],
|
styleUrls: ['./iam.component.scss'],
|
||||||
})
|
})
|
||||||
export class IamComponent {
|
export class IamComponent {
|
||||||
|
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
public totalMemberResult: number = 0;
|
public totalMemberResult: number = 0;
|
||||||
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
||||||
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
||||||
|
|
||||||
constructor(private adminService: AdminService, private dialog: MatDialog, private toast: ToastService,
|
constructor(public adminService: AdminService, private dialog: MatDialog, private toast: ToastService,
|
||||||
private router: Router) {
|
private router: Router) {
|
||||||
this.loadMembers();
|
this.loadMembers();
|
||||||
}
|
}
|
||||||
|
@ -22,20 +22,23 @@ import { ContributorsModule } from 'src/app/modules/contributors/contributors.mo
|
|||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
|
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module';
|
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module';
|
||||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
|
import { IdpTableModule } from '../../modules/idp-table/idp-table.module';
|
||||||
import { FailedEventsComponent } from './failed-events/failed-events.component';
|
import { FailedEventsComponent } from './failed-events/failed-events.component';
|
||||||
|
import { IamPolicyGridComponent } from './iam-policy-grid/iam-policy-grid.component';
|
||||||
import { IamRoutingModule } from './iam-routing.module';
|
import { IamRoutingModule } from './iam-routing.module';
|
||||||
import { IamViewsComponent } from './iam-views/iam-views.component';
|
import { IamViewsComponent } from './iam-views/iam-views.component';
|
||||||
import { IamComponent } from './iam.component';
|
import { IamComponent } from './iam.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [IamComponent, IamViewsComponent, FailedEventsComponent],
|
declarations: [IamComponent, IamViewsComponent, FailedEventsComponent, IamPolicyGridComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
IamRoutingModule,
|
IamRoutingModule,
|
||||||
|
IdpTableModule,
|
||||||
ChangesModule,
|
ChangesModule,
|
||||||
CardModule,
|
CardModule,
|
||||||
MatAutocompleteModule,
|
MatAutocompleteModule,
|
||||||
@ -61,6 +64,7 @@ import { IamComponent } from './iam.component';
|
|||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RefreshTableModule,
|
RefreshTableModule,
|
||||||
|
HasRolePipeModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class IamModule { }
|
export class IamModule { }
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
<button (click)="close()" mat-icon-button>
|
<button (click)="close()" mat-icon-button>
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<span class="abort">{{ 'ORG.PAGES.CREATE' | translate }}</span><span class="abort-2">Step
|
|
||||||
{{ currentCreateStep }} of
|
|
||||||
{{ createSteps }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['iam.write']">
|
<ng-template appHasRole [appHasRole]="['iam.write']">
|
||||||
|
@ -27,6 +27,14 @@
|
|||||||
</app-card>
|
</app-card>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template appHasRole [appHasRole]="['org.idp.read']">
|
||||||
|
<app-card title="{{ 'IDP.LIST.TITLE' | translate }}" description="{{ 'IDP.LIST.DESCRIPTION' | translate }}">
|
||||||
|
<app-idp-table [service]="mgmtService" [serviceType]="PolicyComponentServiceType.MGMT"
|
||||||
|
[disabled]="(['iam.idp.write'] | hasRole | async) == false">
|
||||||
|
</app-idp-table>
|
||||||
|
</app-card>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['policy.read']">
|
<ng-template appHasRole [appHasRole]="['policy.read']">
|
||||||
<app-policy-grid></app-policy-grid>
|
<app-policy-grid></app-policy-grid>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -9,6 +9,7 @@ import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
|||||||
import { catchError, finalize, map } from 'rxjs/operators';
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||||
|
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||||
import {
|
import {
|
||||||
Org,
|
Org,
|
||||||
@ -33,6 +34,7 @@ import { DomainVerificationComponent } from './domain-verification/domain-verifi
|
|||||||
})
|
})
|
||||||
export class OrgDetailComponent implements OnInit, OnDestroy {
|
export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||||
public org!: Org.AsObject;
|
public org!: Org.AsObject;
|
||||||
|
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||||
|
|
||||||
public dataSource: MatTableDataSource<OrgMember.AsObject> = new MatTableDataSource<OrgMember.AsObject>();
|
public dataSource: MatTableDataSource<OrgMember.AsObject> = new MatTableDataSource<OrgMember.AsObject>();
|
||||||
public memberResult!: OrgMemberSearchResponse.AsObject;
|
public memberResult!: OrgMemberSearchResponse.AsObject;
|
||||||
@ -56,7 +58,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
private mgmtService: ManagementService,
|
public mgmtService: ManagementService,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
) { }
|
) { }
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table mat-table class="background-style table" aria-label="Elements" [dataSource]="dataSource">
|
<table mat-table class="table" aria-label="Elements" [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -77,8 +77,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<mat-paginator class="paginator background-style" #paginator [pageSize]="50"
|
<mat-paginator class="paginator" #paginator [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
||||||
[pageSizeOptions]="[25, 50, 100, 250]">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { RoleGuard } from 'src/app/guards/role.guard';
|
import { RoleGuard } from 'src/app/guards/role.guard';
|
||||||
|
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
|
|
||||||
import { OrgCreateComponent } from './org-create/org-create.component';
|
import { OrgCreateComponent } from './org-create/org-create.component';
|
||||||
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
||||||
import { OrgGridComponent } from './org-grid/org-grid.component';
|
import { OrgGridComponent } from './org-grid/org-grid.component';
|
||||||
import { PasswordPolicyComponent, PolicyComponentAction } from './password-policy/password-policy.component';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -18,20 +18,60 @@ const routes: Routes = [
|
|||||||
loadChildren: () => import('./org-create/org-create.module').then(m => m.OrgCreateModule),
|
loadChildren: () => import('./org-create/org-create.module').then(m => m.OrgCreateModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'policy/:policytype/create',
|
path: 'idp',
|
||||||
component: PasswordPolicyComponent,
|
children: [
|
||||||
data: {
|
{
|
||||||
action: PolicyComponentAction.CREATE,
|
path: 'create',
|
||||||
},
|
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||||
|
canActivate: [RoleGuard],
|
||||||
|
data: {
|
||||||
|
roles: ['org.idp.write'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':id',
|
||||||
|
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||||
|
canActivate: [RoleGuard],
|
||||||
|
data: {
|
||||||
|
roles: ['iam.idp.read'],
|
||||||
|
serviceType: PolicyComponentServiceType.ADMIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
/// TODO: add roleguard for iam policy
|
|
||||||
{
|
{
|
||||||
path: 'policy/:policytype',
|
path: 'policy',
|
||||||
component: PasswordPolicyComponent,
|
children: [
|
||||||
data: {
|
{
|
||||||
action: PolicyComponentAction.MODIFY,
|
path: PolicyComponentType.AGE,
|
||||||
},
|
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||||
loadChildren: () => import('./password-policy/password-policy.module').then(m => m.PasswordPolicyModule),
|
.then(m => m.PasswordAgePolicyModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: PolicyComponentType.LOCKOUT,
|
||||||
|
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||||
|
.then(m => m.PasswordLockoutPolicyModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: PolicyComponentType.COMPLEXITY,
|
||||||
|
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||||
|
.then(m => m.PasswordComplexityPolicyModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: PolicyComponentType.IAM,
|
||||||
|
loadChildren: () => import('src/app/modules/policies/password-iam-policy/password-iam-policy.module')
|
||||||
|
.then(m => m.PasswordIamPolicyModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: PolicyComponentType.LOGIN,
|
||||||
|
data: {
|
||||||
|
serviceType: PolicyComponentServiceType.MGMT,
|
||||||
|
},
|
||||||
|
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||||
|
.then(m => m.LoginPolicyModule),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'members',
|
path: 'members',
|
||||||
|
@ -16,6 +16,7 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
|||||||
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
||||||
import { CardModule } from 'src/app/modules/card/card.module';
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
||||||
|
import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module';
|
||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
||||||
@ -40,6 +41,7 @@ import { PolicyGridComponent } from './policy-grid/policy-grid.component';
|
|||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
|
IdpTableModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
CardModule,
|
CardModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
<app-detail-layout [backRouterLink]="[ '/org']" [title]="title ? (title | translate) : ''"
|
|
||||||
[description]="desc ? (desc | translate) : ''">
|
|
||||||
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
|
||||||
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn" (click)="deletePolicy()"
|
|
||||||
mat-stroked-button>
|
|
||||||
{{'ORG.POLICY.DELETE' | translate}}
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<div class="content" *ngIf="policyType === PolicyComponentType?.LOCKOUT && lockoutData">
|
|
||||||
<mat-form-field class="description-formfield" appearance="outline">
|
|
||||||
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
|
||||||
<input matInput name="description" ngDefaultControl [(ngModel)]="lockoutData.description" required />
|
|
||||||
</mat-form-field>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.MAXATTEMPTS' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<div class="length-wrapper">
|
|
||||||
<button mat-icon-button (click)="incrementMaxAttempts()">
|
|
||||||
<mat-icon>add</mat-icon>
|
|
||||||
</button>
|
|
||||||
<span>{{lockoutData?.maxAttempts}}</span>
|
|
||||||
<button mat-icon-button (click)="decrementMaxAttempts()">
|
|
||||||
<mat-icon>remove</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.SHOWLOCKOUTFAILURES' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<mat-slide-toggle color="primary" name="showLockOutFailures" ngDefaultControl
|
|
||||||
[(ngModel)]="lockoutData.showLockOutFailures">
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="policyType === PolicyComponentType?.COMPLEXITY && complexityData" class="content">
|
|
||||||
<mat-form-field class="description-formfield" appearance="outline">
|
|
||||||
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
|
||||||
<input matInput name="description" ngDefaultControl [(ngModel)]="complexityData.description" required />
|
|
||||||
</mat-form-field>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.MINLENGTH' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<div class="length-wrapper">
|
|
||||||
<button mat-icon-button (click)="decrementLength()">
|
|
||||||
<mat-icon>remove</mat-icon>
|
|
||||||
</button>
|
|
||||||
<span>{{complexityData?.minLength}}</span>
|
|
||||||
<button mat-icon-button (click)="incrementLength()">
|
|
||||||
<mat-icon>add</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.HASNUMBER' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="complexityData.hasNumber">
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.HASSYMBOL' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<mat-slide-toggle color="primary" name="hasSymbol" ngDefaultControl [(ngModel)]="complexityData.hasSymbol">
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.HASLOWERCASE' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<mat-slide-toggle color="primary" name="hasLowercase" ngDefaultControl
|
|
||||||
[(ngModel)]="complexityData.hasLowercase">
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.HASUPPERCASE' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<mat-slide-toggle color="primary" name="hasUppercase" ngDefaultControl
|
|
||||||
[(ngModel)]="complexityData.hasUppercase">
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content" *ngIf="policyType === PolicyComponentType?.AGE && ageData">
|
|
||||||
<mat-form-field class="description-formfield" appearance="outline">
|
|
||||||
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
|
||||||
<input matInput name="description" ngDefaultControl [(ngModel)]="ageData.description" required />
|
|
||||||
</mat-form-field>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.EXPIREWARNDAYS' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<div class="length-wrapper">
|
|
||||||
<button mat-icon-button (click)="incrementExpireWarnDays()">
|
|
||||||
<mat-icon>add</mat-icon>
|
|
||||||
</button>
|
|
||||||
<span>{{ageData?.expireWarnDays}}</span>
|
|
||||||
<button mat-icon-button (click)="decrementExpireWarnDays()">
|
|
||||||
<mat-icon>remove</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.MAXAGEDAYS' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<div class="length-wrapper">
|
|
||||||
<button mat-icon-button (click)="incrementMaxAgeDays()">
|
|
||||||
<mat-icon>add</mat-icon>
|
|
||||||
</button>
|
|
||||||
<span>{{ageData?.maxAgeDays}}</span>
|
|
||||||
<button mat-icon-button (click)="decrementMaxAgeDays()">
|
|
||||||
<mat-icon>remove</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content" *ngIf="policyType === PolicyComponentType?.IAM_POLICY && iamData">
|
|
||||||
<mat-form-field class="description-formfield" appearance="outline">
|
|
||||||
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
|
|
||||||
<input matInput name="description" ngDefaultControl [(ngModel)]="iamData.description" required />
|
|
||||||
</mat-form-field>
|
|
||||||
<div class="row">
|
|
||||||
<span class="left-desc">{{'ORG.POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate}}</span>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
|
||||||
[(ngModel)]="iamData.userLoginMustBeDomain">
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="btn-container">
|
|
||||||
<button (click)="savePolicy()" color="primary" type="submit"
|
|
||||||
[disabled]="(policyType === PolicyComponentType?.COMPLEXITY && !complexityData?.description) || (policyType === PolicyComponentType?.IAM_POLICY && !iamData?.description)"
|
|
||||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
|
||||||
</div>
|
|
||||||
</app-detail-layout>
|
|
@ -1,329 +0,0 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
|
||||||
import { FormGroup } from '@angular/forms';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { Subscription } from 'rxjs';
|
|
||||||
import { switchMap } from 'rxjs/operators';
|
|
||||||
import {
|
|
||||||
OrgIamPolicy,
|
|
||||||
PasswordAgePolicy,
|
|
||||||
PasswordComplexityPolicy,
|
|
||||||
PasswordLockoutPolicy,
|
|
||||||
} from 'src/app/proto/generated/management_pb';
|
|
||||||
import { AdminService } from 'src/app/services/admin.service';
|
|
||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
|
||||||
import { StorageService } from 'src/app/services/storage.service';
|
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
|
||||||
|
|
||||||
export enum PolicyComponentAction {
|
|
||||||
CREATE = 'create',
|
|
||||||
MODIFY = 'modify',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum PolicyComponentType {
|
|
||||||
LOCKOUT = 'lockout',
|
|
||||||
AGE = 'age',
|
|
||||||
COMPLEXITY = 'complexity',
|
|
||||||
IAM_POLICY = 'iam_policy',
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-password-policy',
|
|
||||||
templateUrl: './password-policy.component.html',
|
|
||||||
styleUrls: ['./password-policy.component.scss'],
|
|
||||||
})
|
|
||||||
export class PasswordPolicyComponent implements OnInit, OnDestroy {
|
|
||||||
public title: string = '';
|
|
||||||
public desc: string = '';
|
|
||||||
|
|
||||||
componentAction: PolicyComponentAction = PolicyComponentAction.CREATE;
|
|
||||||
|
|
||||||
public policyType: PolicyComponentType = PolicyComponentType.COMPLEXITY;
|
|
||||||
|
|
||||||
public PolicyComponentType: any = PolicyComponentType;
|
|
||||||
public PolicyComponentAction: any = PolicyComponentAction;
|
|
||||||
|
|
||||||
public lockoutForm!: FormGroup;
|
|
||||||
public ageForm!: FormGroup;
|
|
||||||
|
|
||||||
public complexityData!: PasswordComplexityPolicy.AsObject;
|
|
||||||
public lockoutData!: PasswordLockoutPolicy.AsObject;
|
|
||||||
public ageData!: PasswordAgePolicy.AsObject;
|
|
||||||
public iamData!: OrgIamPolicy.AsObject;
|
|
||||||
|
|
||||||
private sub: Subscription = new Subscription();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private adminService: AdminService,
|
|
||||||
private mgmtService: ManagementService,
|
|
||||||
private router: Router,
|
|
||||||
private toast: ToastService,
|
|
||||||
private sessionStorage: StorageService,
|
|
||||||
) {
|
|
||||||
this.sub = this.route.data.pipe(switchMap(data => {
|
|
||||||
this.componentAction = data.action;
|
|
||||||
return this.route.params;
|
|
||||||
})).subscribe(params => {
|
|
||||||
this.policyType = params.policytype;
|
|
||||||
|
|
||||||
switch (params.policytype) {
|
|
||||||
case PolicyComponentType.LOCKOUT:
|
|
||||||
this.title = 'ORG.POLICY.PWD_LOCKOUT.TITLECREATE';
|
|
||||||
this.desc = 'ORG.POLICY.PWD_LOCKOUT.DESCRIPTIONCREATE';
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.AGE:
|
|
||||||
this.title = 'ORG.POLICY.PWD_AGE.TITLECREATE';
|
|
||||||
this.desc = 'ORG.POLICY.PWD_AGE.DESCRIPTIONCREATE';
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.COMPLEXITY:
|
|
||||||
this.title = 'ORG.POLICY.PWD_COMPLEXITY.TITLECREATE';
|
|
||||||
this.desc = 'ORG.POLICY.PWD_COMPLEXITY.DESCRIPTIONCREATE';
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.IAM_POLICY:
|
|
||||||
this.title = 'ORG.POLICY.IAM_POLICY.TITLECREATE';
|
|
||||||
this.desc = 'ORG.POLICY.IAM_POLICY.DESCRIPTIONCREATE';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.componentAction === PolicyComponentAction.MODIFY) {
|
|
||||||
this.getData(params).then(data => {
|
|
||||||
if (data) {
|
|
||||||
switch (this.policyType) {
|
|
||||||
case PolicyComponentType.LOCKOUT:
|
|
||||||
this.lockoutData = data.toObject() as PasswordLockoutPolicy.AsObject;
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.AGE:
|
|
||||||
this.ageData = data.toObject() as PasswordAgePolicy.AsObject;
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.COMPLEXITY:
|
|
||||||
this.complexityData = data.toObject() as PasswordComplexityPolicy.AsObject;
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.IAM_POLICY:
|
|
||||||
this.iamData = data.toObject() as OrgIamPolicy.AsObject;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
|
||||||
this.sub.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getData(params: any):
|
|
||||||
Promise<PasswordLockoutPolicy | PasswordAgePolicy | PasswordComplexityPolicy | OrgIamPolicy | undefined> {
|
|
||||||
switch (params.policytype) {
|
|
||||||
case PolicyComponentType.LOCKOUT:
|
|
||||||
this.title = 'ORG.POLICY.PWD_LOCKOUT.TITLE';
|
|
||||||
this.desc = 'ORG.POLICY.PWD_LOCKOUT.DESCRIPTION';
|
|
||||||
return this.mgmtService.GetPasswordLockoutPolicy();
|
|
||||||
case PolicyComponentType.AGE:
|
|
||||||
this.title = 'ORG.POLICY.PWD_AGE.TITLE';
|
|
||||||
this.desc = 'ORG.POLICY.PWD_AGE.DESCRIPTION';
|
|
||||||
return this.mgmtService.GetPasswordAgePolicy();
|
|
||||||
case PolicyComponentType.COMPLEXITY:
|
|
||||||
this.title = 'ORG.POLICY.PWD_COMPLEXITY.TITLE';
|
|
||||||
this.desc = 'ORG.POLICY.PWD_COMPLEXITY.DESCRIPTION';
|
|
||||||
return this.mgmtService.GetPasswordComplexityPolicy();
|
|
||||||
case PolicyComponentType.IAM_POLICY:
|
|
||||||
this.title = 'ORG.POLICY.IAM_POLICY.TITLECREATE';
|
|
||||||
this.desc = 'ORG.POLICY.IAM_POLICY.DESCRIPTIONCREATE';
|
|
||||||
return this.mgmtService.GetMyOrgIamPolicy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public deletePolicy(): void {
|
|
||||||
switch (this.policyType) {
|
|
||||||
case PolicyComponentType.LOCKOUT:
|
|
||||||
this.mgmtService.DeletePasswordLockoutPolicy(this.lockoutData.id).then(() => {
|
|
||||||
this.toast.showInfo('Successfully deleted');
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.AGE:
|
|
||||||
this.mgmtService.DeletePasswordAgePolicy(this.ageData.id).then(() => {
|
|
||||||
this.toast.showInfo('Successfully deleted');
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.COMPLEXITY:
|
|
||||||
this.mgmtService.DeletePasswordComplexityPolicy(this.complexityData.id).then(() => {
|
|
||||||
this.toast.showInfo('Successfully deleted');
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public incrementLength(): void {
|
|
||||||
if (this.complexityData?.minLength !== undefined && this.complexityData?.minLength <= 72) {
|
|
||||||
this.complexityData.minLength++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public decrementLength(): void {
|
|
||||||
if (this.complexityData?.minLength && this.complexityData?.minLength > 1) {
|
|
||||||
this.complexityData.minLength--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public incrementMaxAttempts(): void {
|
|
||||||
if (this.lockoutData?.maxAttempts !== undefined) {
|
|
||||||
this.lockoutData.maxAttempts++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public decrementMaxAttempts(): void {
|
|
||||||
if (this.lockoutData?.maxAttempts && this.lockoutData?.maxAttempts > 0) {
|
|
||||||
this.lockoutData.maxAttempts--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public incrementExpireWarnDays(): void {
|
|
||||||
if (this.ageData?.expireWarnDays !== undefined) {
|
|
||||||
this.ageData.expireWarnDays++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public decrementExpireWarnDays(): void {
|
|
||||||
if (this.ageData?.expireWarnDays && this.ageData?.expireWarnDays > 0) {
|
|
||||||
this.ageData.expireWarnDays--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public incrementMaxAgeDays(): void {
|
|
||||||
if (this.ageData?.maxAgeDays !== undefined) {
|
|
||||||
this.ageData.maxAgeDays++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public decrementMaxAgeDays(): void {
|
|
||||||
if (this.ageData?.maxAgeDays && this.ageData?.maxAgeDays > 0) {
|
|
||||||
this.ageData.maxAgeDays--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public savePolicy(): void {
|
|
||||||
if (this.componentAction === PolicyComponentAction.CREATE) {
|
|
||||||
switch (this.policyType) {
|
|
||||||
case PolicyComponentType.LOCKOUT:
|
|
||||||
this.mgmtService.CreatePasswordLockoutPolicy(
|
|
||||||
this.lockoutData.description,
|
|
||||||
this.lockoutData.maxAttempts,
|
|
||||||
this.lockoutData.showLockOutFailures,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.AGE:
|
|
||||||
this.mgmtService.CreatePasswordAgePolicy(
|
|
||||||
this.ageData.description,
|
|
||||||
this.ageData.maxAgeDays,
|
|
||||||
this.ageData.expireWarnDays,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.COMPLEXITY:
|
|
||||||
this.mgmtService.CreatePasswordComplexityPolicy(
|
|
||||||
this.complexityData.description,
|
|
||||||
this.complexityData.hasLowercase,
|
|
||||||
this.complexityData.hasUppercase,
|
|
||||||
this.complexityData.hasNumber,
|
|
||||||
this.complexityData.hasSymbol,
|
|
||||||
this.complexityData.minLength,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PolicyComponentType.IAM_POLICY:
|
|
||||||
const orgId = this.sessionStorage.getItem('organization');
|
|
||||||
if (orgId) {
|
|
||||||
this.adminService.CreateOrgIamPolicy(
|
|
||||||
orgId,
|
|
||||||
this.iamData.description,
|
|
||||||
this.iamData.userLoginMustBeDomain,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (this.componentAction === PolicyComponentAction.MODIFY) {
|
|
||||||
switch (this.policyType) {
|
|
||||||
case PolicyComponentType.LOCKOUT:
|
|
||||||
this.mgmtService.UpdatePasswordLockoutPolicy(
|
|
||||||
this.lockoutData.description,
|
|
||||||
this.lockoutData.maxAttempts,
|
|
||||||
this.lockoutData.showLockOutFailures,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.AGE:
|
|
||||||
this.mgmtService.UpdatePasswordAgePolicy(
|
|
||||||
this.ageData.description,
|
|
||||||
this.ageData.maxAgeDays,
|
|
||||||
this.ageData.expireWarnDays,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
case PolicyComponentType.COMPLEXITY:
|
|
||||||
this.mgmtService.UpdatePasswordComplexityPolicy(
|
|
||||||
this.complexityData.description,
|
|
||||||
this.complexityData.hasLowercase,
|
|
||||||
this.complexityData.hasUppercase,
|
|
||||||
this.complexityData.hasNumber,
|
|
||||||
this.complexityData.hasSymbol,
|
|
||||||
this.complexityData.minLength,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PolicyComponentType.IAM_POLICY:
|
|
||||||
const orgId = this.sessionStorage.getItem('organization');
|
|
||||||
if (orgId) {
|
|
||||||
this.adminService.UpdateOrgIamPolicy(
|
|
||||||
orgId,
|
|
||||||
this.iamData.description,
|
|
||||||
this.iamData.userLoginMustBeDomain,
|
|
||||||
).then(() => {
|
|
||||||
this.router.navigate(['org']);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -52,9 +52,39 @@
|
|||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="btn-wrapper">
|
<div class="btn-wrapper">
|
||||||
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||||
<button [disabled]="iamPolicy" [routerLink]="[ 'policy', PolicyComponentType.IAM_POLICY,'create' ]"
|
<button [disabled]="iamPolicy" [routerLink]="[ 'policy', PolicyComponentType.IAM,'create' ]"
|
||||||
color="primary" mat-raised-button>{{'ORG.POLICY.BTN_INSTALL' | translate}}</button>
|
color="primary" mat-raised-button>{{'ORG.POLICY.BTN_INSTALL' | translate}}</button>
|
||||||
<button [disabled]="!iamPolicy" [routerLink]="[ 'policy', PolicyComponentType.IAM_POLICY ]"
|
<button [disabled]="!iamPolicy" [routerLink]="[ 'policy', PolicyComponentType.IAM ]"
|
||||||
|
mat-stroked-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template appHasRole [appHasRole]="['policy.read']">
|
||||||
|
<div class="p-item card">
|
||||||
|
<div class="avatar">
|
||||||
|
<i class="icon las la-gem"></i>
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<span>{{'ORG.POLICY.LOGIN_POLICY.TITLE' | translate}}</span>
|
||||||
|
<button mat-icon-button disabled>
|
||||||
|
<i *ngIf="loginPolicy" class="icon las la-check-circle"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-template #showDescIAM>
|
||||||
|
<p class="desc">
|
||||||
|
{{'ORG.POLICY.LOGIN_POLICY.DESCRIPTION' | translate}}</p>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<div class="btn-wrapper">
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||||
|
<button [disabled]="loginPolicy" [routerLink]="[ 'policy', PolicyComponentType.LOGIN,'create' ]"
|
||||||
|
color="primary" mat-raised-button>{{'ORG.POLICY.BTN_INSTALL' | translate}}</button>
|
||||||
|
<button [disabled]="!loginPolicy" [routerLink]="[ 'policy', PolicyComponentType.LOGIN ]"
|
||||||
mat-stroked-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
|
mat-stroked-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,7 +16,7 @@ h1 {
|
|||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 250px;
|
min-height: 200px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
||||||
@media only screen and (max-width: 450px) {
|
@media only screen and (max-width: 450px) {
|
||||||
@ -57,6 +57,7 @@ h1 {
|
|||||||
|
|
||||||
.desc {
|
.desc {
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
|
color: #8795a1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fill-space {
|
.fill-space {
|
||||||
|
@ -1,33 +1,23 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import {
|
import { PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
OrgIamPolicy,
|
import { LoginPolicy, OrgIamPolicy, PasswordComplexityPolicy, PolicyState } from 'src/app/proto/generated/management_pb';
|
||||||
PasswordAgePolicy,
|
|
||||||
PasswordComplexityPolicy,
|
|
||||||
PasswordLockoutPolicy,
|
|
||||||
PolicyState,
|
|
||||||
} from 'src/app/proto/generated/management_pb';
|
|
||||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
|
||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
|
|
||||||
import { PolicyComponentType } from '../password-policy/password-policy.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-policy-grid',
|
selector: 'app-policy-grid',
|
||||||
templateUrl: './policy-grid.component.html',
|
templateUrl: './policy-grid.component.html',
|
||||||
styleUrls: ['./policy-grid.component.scss'],
|
styleUrls: ['./policy-grid.component.scss'],
|
||||||
})
|
})
|
||||||
export class PolicyGridComponent {
|
export class PolicyGridComponent {
|
||||||
public lockoutPolicy!: PasswordLockoutPolicy.AsObject;
|
|
||||||
public agePolicy!: PasswordAgePolicy.AsObject;
|
|
||||||
public complexityPolicy!: PasswordComplexityPolicy.AsObject;
|
public complexityPolicy!: PasswordComplexityPolicy.AsObject;
|
||||||
public iamPolicy!: OrgIamPolicy.AsObject;
|
public iamPolicy!: OrgIamPolicy.AsObject;
|
||||||
|
public loginPolicy!: LoginPolicy.AsObject;
|
||||||
|
|
||||||
public PolicyState: any = PolicyState;
|
public PolicyState: any = PolicyState;
|
||||||
public PolicyComponentType: any = PolicyComponentType;
|
public PolicyComponentType: any = PolicyComponentType;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private mgmtService: ManagementService,
|
private mgmtService: ManagementService,
|
||||||
public authUserService: GrpcAuthService,
|
|
||||||
) {
|
) {
|
||||||
this.getData();
|
this.getData();
|
||||||
}
|
}
|
||||||
@ -35,5 +25,8 @@ export class PolicyGridComponent {
|
|||||||
private getData(): void {
|
private getData(): void {
|
||||||
this.mgmtService.GetPasswordComplexityPolicy().then(data => this.complexityPolicy = data.toObject());
|
this.mgmtService.GetPasswordComplexityPolicy().then(data => this.complexityPolicy = data.toObject());
|
||||||
this.mgmtService.GetMyOrgIamPolicy().then(data => this.iamPolicy = data.toObject());
|
this.mgmtService.GetMyOrgIamPolicy().then(data => this.iamPolicy = data.toObject());
|
||||||
|
this.mgmtService.GetLoginPolicy().then(data => {
|
||||||
|
this.loginPolicy = data.toObject();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
<form>
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
|
||||||
<mat-label>Organization</mat-label>
|
|
||||||
<mat-chip-list #chipList aria-label="org selection">
|
|
||||||
<mat-chip class="chip" *ngFor="let selectedOrg of orgs" [selectable]="selectable" [removable]="removable"
|
|
||||||
(removed)="remove(selectedOrg)">
|
|
||||||
{{selectedOrg.name}}
|
|
||||||
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
|
|
||||||
</mat-chip>
|
|
||||||
<input placeholder="{{'ORG_DETAIL.DETAIL.DOMAIN' | translate}}" #domainInput [formControl]="myControl"
|
|
||||||
[matAutocomplete]="auto" [matChipInputFor]="chipList"
|
|
||||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur"
|
|
||||||
(matChipInputTokenEnd)="add($event)" />
|
|
||||||
</mat-chip-list>
|
|
||||||
|
|
||||||
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)" [displayWith]="displayFn">
|
|
||||||
<mat-option *ngIf="isLoading" class="is-loading">
|
|
||||||
<mat-spinner diameter="30"></mat-spinner>
|
|
||||||
</mat-option>
|
|
||||||
<mat-option *ngFor="let org of filteredOrgs" [value]="org">
|
|
||||||
{{org.name}}
|
|
||||||
</mat-option>
|
|
||||||
</mat-autocomplete>
|
|
||||||
</mat-form-field>
|
|
||||||
</form>
|
|
@ -1,3 +0,0 @@
|
|||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import {
|
|
||||||
SearchProjectAutocompleteComponent,
|
|
||||||
} from '../../../modules/search-project-autocomplete/search-project-autocomplete.component';
|
|
||||||
|
|
||||||
describe('SearchProjectComponent', () => {
|
|
||||||
let component: SearchProjectAutocompleteComponent;
|
|
||||||
let fixture: ComponentFixture<SearchProjectAutocompleteComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [SearchProjectAutocompleteComponent],
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(SearchProjectAutocompleteComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,99 +0,0 @@
|
|||||||
import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
|
||||||
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
|
|
||||||
import { FormControl } from '@angular/forms';
|
|
||||||
import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
|
||||||
import { MatChipInputEvent } from '@angular/material/chips';
|
|
||||||
import { debounceTime, tap } from 'rxjs/operators';
|
|
||||||
import { Org } from 'src/app/proto/generated/management_pb';
|
|
||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-search-org-autocomplete',
|
|
||||||
templateUrl: './search-org-autocomplete.component.html',
|
|
||||||
styleUrls: ['./search-org-autocomplete.component.scss'],
|
|
||||||
})
|
|
||||||
export class SearchOrgAutocompleteComponent {
|
|
||||||
public selectable: boolean = true;
|
|
||||||
public removable: boolean = true;
|
|
||||||
public addOnBlur: boolean = true;
|
|
||||||
public separatorKeysCodes: number[] = [ENTER, COMMA];
|
|
||||||
public myControl: FormControl = new FormControl();
|
|
||||||
public names: string[] = [];
|
|
||||||
public orgs: Array<Org.AsObject> = [];
|
|
||||||
public filteredOrgs: Array<Org.AsObject> = [];
|
|
||||||
public isLoading: boolean = false;
|
|
||||||
@ViewChild('domainInput') public domainInput!: ElementRef<HTMLInputElement>;
|
|
||||||
@ViewChild('auto') public matAutocomplete!: MatAutocomplete;
|
|
||||||
@Input() public singleOutput: boolean = false;
|
|
||||||
@Output() public selectionChanged: EventEmitter<Org.AsObject | Org.AsObject[]> = new EventEmitter();
|
|
||||||
constructor(private mgmtService: ManagementService, private toast: ToastService) {
|
|
||||||
this.myControl.valueChanges.pipe(debounceTime(200), tap(() => this.isLoading = true)).subscribe(value => {
|
|
||||||
return this.mgmtService.getOrgByDomainGlobal(value).then((org) => {
|
|
||||||
this.isLoading = false;
|
|
||||||
if (org) {
|
|
||||||
this.filteredOrgs = [org.toObject()];
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
this.isLoading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public displayFn(org?: Org.AsObject): string | undefined {
|
|
||||||
return org ? `${org.name}` : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public add(event: MatChipInputEvent): void {
|
|
||||||
if (!this.matAutocomplete.isOpen) {
|
|
||||||
const input = event.input;
|
|
||||||
const value = event.value;
|
|
||||||
|
|
||||||
if ((value || '').trim()) {
|
|
||||||
const index = this.filteredOrgs.findIndex((org) => {
|
|
||||||
if (org.name) {
|
|
||||||
return org.name === value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (index > -1) {
|
|
||||||
if (this.orgs && this.orgs.length > 0) {
|
|
||||||
this.orgs.push(this.filteredOrgs[index]);
|
|
||||||
} else {
|
|
||||||
this.orgs = [this.filteredOrgs[index]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input) {
|
|
||||||
input.value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public remove(org: Org.AsObject): void {
|
|
||||||
const index = this.orgs.indexOf(org);
|
|
||||||
|
|
||||||
if (index >= 0) {
|
|
||||||
this.orgs.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public selected(event: MatAutocompleteSelectedEvent): void {
|
|
||||||
const index = this.filteredOrgs.findIndex((org) => org === event.option.value);
|
|
||||||
if (index !== -1) {
|
|
||||||
if (this.singleOutput) {
|
|
||||||
this.selectionChanged.emit(this.filteredOrgs[index]);
|
|
||||||
} else {
|
|
||||||
if (this.orgs && this.orgs.length > 0) {
|
|
||||||
this.orgs.push(this.orgs[index]);
|
|
||||||
this.selectionChanged.emit(this.orgs);
|
|
||||||
} else {
|
|
||||||
this.orgs = [this.filteredOrgs[index]];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.domainInput.nativeElement.value = '';
|
|
||||||
this.myControl.setValue(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,10 +15,12 @@
|
|||||||
<form [formGroup]="appNameForm" (ngSubmit)="saveOIDCApp()">
|
<form [formGroup]="appNameForm" (ngSubmit)="saveOIDCApp()">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<mat-button-toggle-group formControlName="state" class="toggle" (change)="changeState($event)">
|
<mat-button-toggle-group formControlName="state" class="toggle" (change)="changeState($event)">
|
||||||
<mat-button-toggle [value]="AppState.APPSTATE_INACTIVE" matTooltip="Deactivate Org">
|
<mat-button-toggle [value]="AppState.APPSTATE_INACTIVE"
|
||||||
|
matTooltip="{{ 'ACTIONS.DEACTIVATE' | translate}}">
|
||||||
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}}
|
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}}
|
||||||
</mat-button-toggle>
|
</mat-button-toggle>
|
||||||
<mat-button-toggle [value]="AppState.APPSTATE_ACTIVE" matTooltip="Activate Org">
|
<mat-button-toggle [value]="AppState.APPSTATE_ACTIVE"
|
||||||
|
matTooltip="{{ 'ACTIONS.REACTIVATE' | translate}}">
|
||||||
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}}
|
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}}
|
||||||
</mat-button-toggle>
|
</mat-button-toggle>
|
||||||
</mat-button-toggle-group>
|
</mat-button-toggle-group>
|
||||||
@ -41,7 +43,7 @@
|
|||||||
|
|
||||||
<app-card title="{{ 'APP.OIDC.TITLE' | translate }}" *ngIf="app && app.oidcConfig">
|
<app-card title="{{ 'APP.OIDC.TITLE' | translate }}" *ngIf="app && app.oidcConfig">
|
||||||
<div class="card-actions" card-actions
|
<div class="card-actions" card-actions
|
||||||
*ngIf="app?.oidcConfig.authMethodType !== OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE">
|
*ngIf="app?.oidcConfig?.authMethodType !== OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE">
|
||||||
<button mat-stroked-button
|
<button mat-stroked-button
|
||||||
(click)="regenerateOIDCClientSecret()">{{'APP.OIDC.REGENERATESECRET' | translate}}</button>
|
(click)="regenerateOIDCClientSecret()">{{'APP.OIDC.REGENERATESECRET' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
[selection]="selection" [loading]="loading$ | async">
|
[selection]="selection" [loading]="loading$ | async">
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table class="table background-style" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -67,7 +67,7 @@
|
|||||||
[routerLink]="['/granted-projects', row.projectId, 'grant', row.id]"></tr>
|
[routerLink]="['/granted-projects', row.projectId, 'grant', row.id]"></tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator class="paginator background-style" #paginator [length]="totalResult" [pageSize]="10"
|
<mat-paginator class="paginator" #paginator [length]="totalResult" [pageSize]="10"
|
||||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table class="table background-style" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -66,8 +66,8 @@
|
|||||||
[routerLink]="['/projects', row.projectId]"></tr>
|
[routerLink]="['/projects', row.projectId]"></tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator class="paginator background-style" [length]="totalResult" [pageSize]="10"
|
<mat-paginator class="paginator" [length]="totalResult" [pageSize]="10" [pageSizeOptions]="[5, 10, 20]"
|
||||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
(page)="changePage($event)"></mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
||||||
</div>
|
</div>
|
@ -16,8 +16,7 @@
|
|||||||
<div class="spinner-container" *ngIf="dataSource?.loading$ | async">
|
<div class="spinner-container" *ngIf="dataSource?.loading$ | async">
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
<table mat-table class="table" aria-label="Elements"
|
<table mat-table class="table" aria-label="Elements" [dataSource]="dataSource">
|
||||||
[ngClass]="{'background-style': type == ProjectType.PROJECTTYPE_OWNED}" [dataSource]="dataSource">
|
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -78,8 +77,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<mat-paginator class="paginator" [ngClass]="{'background-style': type == ProjectType.PROJECTTYPE_OWNED}"
|
<mat-paginator class="paginator" [ngClass]="{'': type == ProjectType.PROJECTTYPE_OWNED}" #paginator
|
||||||
#paginator [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
[pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
@ -1,6 +1,6 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||||
<table class="table background-style" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="type">
|
<ng-container matColumnDef="type">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table class="table background-style" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th mat-header-cell *matHeaderCellDef>
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -59,7 +59,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator #paginator class="paginator background-style" [length]="keyResult?.totalResult || 0"
|
<mat-paginator #paginator class="paginator" [length]="keyResult?.totalResult || 0" [pageSize]="10"
|
||||||
[pageSize]="10" [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
@ -9,7 +9,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table mat-table class="background-style table" aria-label="Elements" [dataSource]="dataSource">
|
<table mat-table class="table" aria-label="Elements" [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -61,8 +61,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<mat-paginator class="paginator background-style" #paginator [pageSize]="50"
|
<mat-paginator class="paginator" #paginator [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
||||||
[pageSizeOptions]="[25, 50, 100, 250]">
|
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||||
<table class="table background-style" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="type">
|
<ng-container matColumnDef="type">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table class="table background-style" mat-table [dataSource]="dataSource">
|
<table class="table" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th mat-header-cell *matHeaderCellDef>
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||||
@ -75,7 +75,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator #paginator class="paginator background-style" [length]="userResult?.totalResult || 0"
|
<mat-paginator #paginator class="paginator" [length]="userResult?.totalResult || 0" [pageSize]="10"
|
||||||
[pageSize]="10" [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</app-refresh-table>
|
</app-refresh-table>
|
@ -7,6 +7,7 @@ import {
|
|||||||
CreateHumanRequest,
|
CreateHumanRequest,
|
||||||
CreateOrgRequest,
|
CreateOrgRequest,
|
||||||
CreateUserRequest,
|
CreateUserRequest,
|
||||||
|
DefaultLoginPolicy,
|
||||||
DefaultLoginPolicyView,
|
DefaultLoginPolicyView,
|
||||||
FailedEventID,
|
FailedEventID,
|
||||||
FailedEvents,
|
FailedEvents,
|
||||||
@ -15,6 +16,18 @@ import {
|
|||||||
IamMemberSearchQuery,
|
IamMemberSearchQuery,
|
||||||
IamMemberSearchRequest,
|
IamMemberSearchRequest,
|
||||||
IamMemberSearchResponse,
|
IamMemberSearchResponse,
|
||||||
|
Idp,
|
||||||
|
IdpID,
|
||||||
|
IdpProviderID,
|
||||||
|
IdpProviderSearchRequest,
|
||||||
|
IdpProviderSearchResponse,
|
||||||
|
IdpSearchQuery,
|
||||||
|
IdpSearchRequest,
|
||||||
|
IdpSearchResponse,
|
||||||
|
IdpView,
|
||||||
|
OidcIdpConfig,
|
||||||
|
OidcIdpConfigCreate,
|
||||||
|
OidcIdpConfigUpdate,
|
||||||
OrgIamPolicy,
|
OrgIamPolicy,
|
||||||
OrgIamPolicyID,
|
OrgIamPolicyID,
|
||||||
OrgIamPolicyRequest,
|
OrgIamPolicyRequest,
|
||||||
@ -52,11 +65,6 @@ export class AdminService {
|
|||||||
return this.grpcService.admin.getIamMemberRoles(req);
|
return this.grpcService.admin.getIamMemberRoles(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getDefaultLoginPolicy(): Promise<DefaultLoginPolicyView> {
|
|
||||||
const req = new Empty();
|
|
||||||
return this.grpcService.admin.getDefaultLoginPolicy(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async GetViews(): Promise<Views> {
|
public async GetViews(): Promise<Views> {
|
||||||
const req = new Empty();
|
const req = new Empty();
|
||||||
return this.grpcService.admin.getViews(req);
|
return this.grpcService.admin.getViews(req);
|
||||||
@ -82,6 +90,101 @@ export class AdminService {
|
|||||||
return this.grpcService.admin.removeFailedEvent(req);
|
return this.grpcService.admin.removeFailedEvent(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async GetDefaultLoginPolicy(
|
||||||
|
): Promise<DefaultLoginPolicyView> {
|
||||||
|
const req = new Empty();
|
||||||
|
return this.grpcService.admin.getDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UpdateDefaultLoginPolicy(req: DefaultLoginPolicy): Promise<DefaultLoginPolicy> {
|
||||||
|
return this.grpcService.admin.updateDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async AddIdpProviderToDefaultLoginPolicy(configId: string): Promise<IdpProviderID> {
|
||||||
|
const req = new IdpProviderID();
|
||||||
|
req.setIdpConfigId(configId);
|
||||||
|
return this.grpcService.admin.addIdpProviderToDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async RemoveIdpProviderFromDefaultLoginPolicy(configId: string): Promise<Empty> {
|
||||||
|
const req = new IdpProviderID();
|
||||||
|
req.setIdpConfigId(configId);
|
||||||
|
return this.grpcService.admin.removeIdpProviderFromDefaultLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async GetDefaultLoginPolicyIdpProviders(limit?: number, offset?: number): Promise<IdpProviderSearchResponse> {
|
||||||
|
const req = new IdpProviderSearchRequest();
|
||||||
|
if (limit) {
|
||||||
|
req.setLimit(limit);
|
||||||
|
}
|
||||||
|
if (offset) {
|
||||||
|
req.setOffset(offset);
|
||||||
|
}
|
||||||
|
return this.grpcService.admin.getDefaultLoginPolicyIdpProviders(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SearchIdps(
|
||||||
|
limit?: number,
|
||||||
|
offset?: number,
|
||||||
|
queryList?: IdpSearchQuery[],
|
||||||
|
): Promise<IdpSearchResponse> {
|
||||||
|
const req = new IdpSearchRequest();
|
||||||
|
if (limit) {
|
||||||
|
req.setLimit(limit);
|
||||||
|
}
|
||||||
|
if (offset) {
|
||||||
|
req.setOffset(offset);
|
||||||
|
}
|
||||||
|
if (queryList) {
|
||||||
|
req.setQueriesList(queryList);
|
||||||
|
}
|
||||||
|
return this.grpcService.admin.searchIdps(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async IdpByID(
|
||||||
|
id: string,
|
||||||
|
): Promise<IdpView> {
|
||||||
|
const req = new IdpID();
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.admin.idpByID(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async CreateOidcIdp(
|
||||||
|
req: OidcIdpConfigCreate,
|
||||||
|
): Promise<Idp> {
|
||||||
|
return this.grpcService.admin.createOidcIdp(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UpdateOidcIdpConfig(
|
||||||
|
req: OidcIdpConfigUpdate,
|
||||||
|
): Promise<OidcIdpConfig> {
|
||||||
|
return this.grpcService.mgmt.updateOidcIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async RemoveIdpConfig(
|
||||||
|
id: string,
|
||||||
|
): Promise<Empty> {
|
||||||
|
const req = new IdpID;
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.admin.removeIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async DeactivateIdpConfig(
|
||||||
|
id: string,
|
||||||
|
): Promise<Empty> {
|
||||||
|
const req = new IdpID;
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.admin.deactivateIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ReactivateIdpConfig(
|
||||||
|
id: string,
|
||||||
|
): Promise<Empty> {
|
||||||
|
const req = new IdpID;
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.admin.reactivateIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
public async SearchIamMembers(
|
public async SearchIamMembers(
|
||||||
limit: number,
|
limit: number,
|
||||||
offset: number,
|
offset: number,
|
||||||
|
@ -24,7 +24,20 @@ import {
|
|||||||
Gender,
|
Gender,
|
||||||
GrantedProjectSearchRequest,
|
GrantedProjectSearchRequest,
|
||||||
Iam,
|
Iam,
|
||||||
|
Idp,
|
||||||
|
IdpID,
|
||||||
|
IdpProviderAdd,
|
||||||
|
IdpProviderID,
|
||||||
|
IdpProviderSearchRequest,
|
||||||
|
IdpProviderSearchResponse,
|
||||||
|
IdpProviderType,
|
||||||
|
IdpSearchQuery,
|
||||||
|
IdpSearchRequest,
|
||||||
|
IdpSearchResponse,
|
||||||
|
IdpView,
|
||||||
LoginName,
|
LoginName,
|
||||||
|
LoginPolicy,
|
||||||
|
LoginPolicyView,
|
||||||
MachineKeyIDRequest,
|
MachineKeyIDRequest,
|
||||||
MachineKeySearchRequest,
|
MachineKeySearchRequest,
|
||||||
MachineKeySearchResponse,
|
MachineKeySearchResponse,
|
||||||
@ -35,6 +48,9 @@ import {
|
|||||||
OIDCApplicationCreate,
|
OIDCApplicationCreate,
|
||||||
OIDCConfig,
|
OIDCConfig,
|
||||||
OIDCConfigUpdate,
|
OIDCConfigUpdate,
|
||||||
|
OidcIdpConfig,
|
||||||
|
OidcIdpConfigCreate,
|
||||||
|
OidcIdpConfigUpdate,
|
||||||
Org,
|
Org,
|
||||||
OrgCreateRequest,
|
OrgCreateRequest,
|
||||||
OrgDomain,
|
OrgDomain,
|
||||||
@ -145,6 +161,105 @@ export type ResponseMapper<TResp, TMappedResp> = (resp: TResp) => TMappedResp;
|
|||||||
export class ManagementService {
|
export class ManagementService {
|
||||||
constructor(private readonly grpcService: GrpcService) { }
|
constructor(private readonly grpcService: GrpcService) { }
|
||||||
|
|
||||||
|
public async SearchIdps(
|
||||||
|
limit?: number,
|
||||||
|
offset?: number,
|
||||||
|
queryList?: IdpSearchQuery[],
|
||||||
|
): Promise<IdpSearchResponse> {
|
||||||
|
const req = new IdpSearchRequest();
|
||||||
|
if (limit) {
|
||||||
|
req.setLimit(limit);
|
||||||
|
}
|
||||||
|
if (offset) {
|
||||||
|
req.setOffset(offset);
|
||||||
|
}
|
||||||
|
if (queryList) {
|
||||||
|
req.setQueriesList(queryList);
|
||||||
|
}
|
||||||
|
return this.grpcService.mgmt.searchIdps(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async GetLoginPolicy(): Promise<LoginPolicyView> {
|
||||||
|
const req = new Empty();
|
||||||
|
return this.grpcService.mgmt.getLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UpdateLoginPolicy(req: LoginPolicy): Promise<LoginPolicy> {
|
||||||
|
return this.grpcService.mgmt.updateLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async RemoveLoginPolicy(): Promise<Empty> {
|
||||||
|
return this.grpcService.mgmt.removeLoginPolicy(new Empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addIdpProviderToLoginPolicy(configId: string, idpType: IdpProviderType): Promise<IdpProviderID> {
|
||||||
|
const req = new IdpProviderAdd();
|
||||||
|
req.setIdpProviderType(idpType);
|
||||||
|
req.setIdpConfigId(configId);
|
||||||
|
return this.grpcService.mgmt.addIdpProviderToLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async RemoveIdpProviderFromLoginPolicy(configId: string): Promise<Empty> {
|
||||||
|
const req = new IdpProviderID();
|
||||||
|
req.setIdpConfigId(configId);
|
||||||
|
return this.grpcService.mgmt.removeIdpProviderFromLoginPolicy(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async GetLoginPolicyIdpProviders(limit?: number, offset?: number): Promise<IdpProviderSearchResponse> {
|
||||||
|
const req = new IdpProviderSearchRequest();
|
||||||
|
if (limit) {
|
||||||
|
req.setLimit(limit);
|
||||||
|
}
|
||||||
|
if (offset) {
|
||||||
|
req.setOffset(offset);
|
||||||
|
}
|
||||||
|
return this.grpcService.mgmt.getLoginPolicyIdpProviders(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async IdpByID(
|
||||||
|
id: string,
|
||||||
|
): Promise<IdpView> {
|
||||||
|
const req = new IdpID();
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.mgmt.idpByID(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async CreateOidcIdp(
|
||||||
|
req: OidcIdpConfigCreate,
|
||||||
|
): Promise<Idp> {
|
||||||
|
return this.grpcService.mgmt.createOidcIdp(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UpdateOidcIdpConfig(
|
||||||
|
req: OidcIdpConfigUpdate,
|
||||||
|
): Promise<OidcIdpConfig> {
|
||||||
|
return this.grpcService.mgmt.updateOidcIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async RemoveIdpConfig(
|
||||||
|
id: string,
|
||||||
|
): Promise<Empty> {
|
||||||
|
const req = new IdpID;
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.mgmt.removeIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async DeactivateIdpConfig(
|
||||||
|
id: string,
|
||||||
|
): Promise<Empty> {
|
||||||
|
const req = new IdpID;
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.mgmt.deactivateIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ReactivateIdpConfig(
|
||||||
|
id: string,
|
||||||
|
): Promise<Empty> {
|
||||||
|
const req = new IdpID;
|
||||||
|
req.setId(id);
|
||||||
|
return this.grpcService.mgmt.reactivateIdpConfig(req);
|
||||||
|
}
|
||||||
|
|
||||||
public async CreateUserHuman(username: string, user: CreateHumanRequest): Promise<UserResponse> {
|
public async CreateUserHuman(username: string, user: CreateHumanRequest): Promise<UserResponse> {
|
||||||
const req = new CreateUserRequest();
|
const req = new CreateUserRequest();
|
||||||
|
|
||||||
|
@ -260,7 +260,9 @@
|
|||||||
"DEACTIVATED":"User deaktiviert!",
|
"DEACTIVATED":"User deaktiviert!",
|
||||||
"SELECTEDREACTIVATED":"Selektierte User reaktiviert!",
|
"SELECTEDREACTIVATED":"Selektierte User reaktiviert!",
|
||||||
"SELECTEDDEACTIVATED":"Selektierte Benutzer deaktiviert!",
|
"SELECTEDDEACTIVATED":"Selektierte Benutzer deaktiviert!",
|
||||||
"SELECTEDKEYSDELETED":"Selektierte Schlüssel gelöscht!"
|
"SELECTEDKEYSDELETED":"Selektierte Schlüssel gelöscht!",
|
||||||
|
"KEYADDED":"Schlüssel hinzugefügt!",
|
||||||
|
"MACHINEADDED":"Service User erstellt!"
|
||||||
},
|
},
|
||||||
"MEMBERSHIPS": {
|
"MEMBERSHIPS": {
|
||||||
"TITLE":"Zitadel Manager Rollen",
|
"TITLE":"Zitadel Manager Rollen",
|
||||||
@ -395,6 +397,12 @@
|
|||||||
"TITLECREATE":"IAM Zugangseinstellungen festlegen",
|
"TITLECREATE":"IAM Zugangseinstellungen festlegen",
|
||||||
"DESCRIPTIONCREATE":"Emails als Benutzername sind nicht erlaubt wenn UserLoginMustBeDomain gesetzt ist."
|
"DESCRIPTIONCREATE":"Emails als Benutzername sind nicht erlaubt wenn UserLoginMustBeDomain gesetzt ist."
|
||||||
},
|
},
|
||||||
|
"LOGIN_POLICY": {
|
||||||
|
"TITLE":"Login Richtlinien",
|
||||||
|
"DESCRIPTION":"Definiere die Loginmethoden für Benutzer",
|
||||||
|
"TITLECREATE":"Definiere die Loginmethoden für Benutzer",
|
||||||
|
"DESCRIPTIONCREATE":"Nutzer können Sich mit den verfügbaren Idps authentifizieren."
|
||||||
|
},
|
||||||
"BTN_INSTALL":"Installieren",
|
"BTN_INSTALL":"Installieren",
|
||||||
"BTN_EDIT":"Modifizieren",
|
"BTN_EDIT":"Modifizieren",
|
||||||
"DATA": {
|
"DATA": {
|
||||||
@ -408,7 +416,10 @@
|
|||||||
"MAXATTEMPTS":"Maximale Anzahl an Versuchen",
|
"MAXATTEMPTS":"Maximale Anzahl an Versuchen",
|
||||||
"EXPIREWARNDAYS":"Ablauf Warnung nach Tagen",
|
"EXPIREWARNDAYS":"Ablauf Warnung nach Tagen",
|
||||||
"MAXAGEDAYS":"Maximale Gültigkeit in Tagen",
|
"MAXAGEDAYS":"Maximale Gültigkeit in Tagen",
|
||||||
"USERLOGINMUSTBEDOMAIN":"User Login must be Domain"
|
"USERLOGINMUSTBEDOMAIN":"User Login must be Domain",
|
||||||
|
"ALLOWUSERNAMEPASSWORD":"Benutzername Password erlaubt",
|
||||||
|
"ALLOWEXTERNALIDP":"Externer IDP erlaubt",
|
||||||
|
"ALLOWREGISTER":"Registrieren erlaubt"
|
||||||
},
|
},
|
||||||
"DELETE":"Richtlinie entfernen / zurücksetzen"
|
"DELETE":"Richtlinie entfernen / zurücksetzen"
|
||||||
},
|
},
|
||||||
@ -612,6 +623,51 @@
|
|||||||
"DELETED":"Projekt gelöscht!"
|
"DELETED":"Projekt gelöscht!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"IDP":{
|
||||||
|
"LIST": {
|
||||||
|
"TITLE":"Identity Providers",
|
||||||
|
"DESCRIPTION":"Definieren Sie hier Ihre zusätzlichen Idps, die sie für die Authentifizierung in Ihren Organisationen verwenden können."
|
||||||
|
},
|
||||||
|
"CREATE": {
|
||||||
|
"TITLE":"Neuer Identity Provider",
|
||||||
|
"DESCRIPTION":"Definieren Sie hier die Zugangsdaten des neuen Identity Providers"
|
||||||
|
},
|
||||||
|
"TYPES": {
|
||||||
|
"0":"unknown",
|
||||||
|
"1":"System",
|
||||||
|
"2":"Organisation"
|
||||||
|
},
|
||||||
|
"STATES":{
|
||||||
|
"0":"aktiv",
|
||||||
|
"1":"inaktiv"
|
||||||
|
},
|
||||||
|
"TYPE":"Typ",
|
||||||
|
"NAME":"Name",
|
||||||
|
"CONFIG":"Konfiguration",
|
||||||
|
"STATE":"Status",
|
||||||
|
"LOGOSRC":"Logo Src",
|
||||||
|
"ISSUER":"Issuer",
|
||||||
|
"SCOPESLIST":"Scopes List",
|
||||||
|
"CLIENTID":"Client ID",
|
||||||
|
"CLIENTSECRET":"Client Secret",
|
||||||
|
"CREATIONDATE":"Erstelldatum",
|
||||||
|
"CHANGEDATE":"Letzte Änderung",
|
||||||
|
"DEACTIVATE":"Deaktivieren",
|
||||||
|
"ACTIVATE":"Aktivieren",
|
||||||
|
"DELETE":"Löschen"
|
||||||
|
},
|
||||||
|
"LOGINPOLICY": {
|
||||||
|
"CREATE": {
|
||||||
|
"TITLE":"Login Policy",
|
||||||
|
"DESCRIPTION":"Definieren Sie hier, mit welchen Idps sich Ihre Benutzer anmelden können."
|
||||||
|
},
|
||||||
|
"IDPS":"Identity Providers",
|
||||||
|
"ADDIDP": {
|
||||||
|
"TITLE":"Identity Provider hinzufügen",
|
||||||
|
"DESCRIPTION":"Sie können vordefinierte oder selbsterstellten Provider auswählen",
|
||||||
|
"SELECTIDPS":"Identity Provider"
|
||||||
|
}
|
||||||
|
},
|
||||||
"APP": {
|
"APP": {
|
||||||
"LIST": "Applications",
|
"LIST": "Applications",
|
||||||
"PAGES": {
|
"PAGES": {
|
||||||
|
@ -260,7 +260,9 @@
|
|||||||
"DEACTIVATED":"User deactivated",
|
"DEACTIVATED":"User deactivated",
|
||||||
"SELECTEDREACTIVATED":"Selected Users reactivated",
|
"SELECTEDREACTIVATED":"Selected Users reactivated",
|
||||||
"SELECTEDDEACTIVATED":"Selected Users deactivated",
|
"SELECTEDDEACTIVATED":"Selected Users deactivated",
|
||||||
"SELECTEDKEYSDELETED":"Selected Keys deleted!"
|
"SELECTEDKEYSDELETED":"Selected Keys deleted!",
|
||||||
|
"KEYADDED":"Key added!",
|
||||||
|
"MACHINEADDED":"Service User created!"
|
||||||
},
|
},
|
||||||
"MEMBERSHIPS": {
|
"MEMBERSHIPS": {
|
||||||
"TITLE":"Zitadel Manager Roles",
|
"TITLE":"Zitadel Manager Roles",
|
||||||
@ -395,6 +397,12 @@
|
|||||||
"TITLECREATE":"Set IAM Access preferences",
|
"TITLECREATE":"Set IAM Access preferences",
|
||||||
"DESCRIPTIONCREATE":"Emails as username is not allowed for enabled UserLoginMustBeDomain"
|
"DESCRIPTIONCREATE":"Emails as username is not allowed for enabled UserLoginMustBeDomain"
|
||||||
},
|
},
|
||||||
|
"LOGIN_POLICY": {
|
||||||
|
"TITLE":"Login Policy",
|
||||||
|
"DESCRIPTION":"Define how Users can be authenticated",
|
||||||
|
"TITLECREATE":"Define how Users can be authenticated",
|
||||||
|
"DESCRIPTIONCREATE":"Users can choose from all of the available identity providers."
|
||||||
|
},
|
||||||
"BTN_INSTALL":"Setup",
|
"BTN_INSTALL":"Setup",
|
||||||
"BTN_EDIT":"Modify",
|
"BTN_EDIT":"Modify",
|
||||||
"DATA": {
|
"DATA": {
|
||||||
@ -408,7 +416,10 @@
|
|||||||
"MAXATTEMPTS":"Max Attempts",
|
"MAXATTEMPTS":"Max Attempts",
|
||||||
"EXPIREWARNDAYS":"Expiration Warning after day",
|
"EXPIREWARNDAYS":"Expiration Warning after day",
|
||||||
"MAXAGEDAYS":"Max Age in days",
|
"MAXAGEDAYS":"Max Age in days",
|
||||||
"USERLOGINMUSTBEDOMAIN":"User Login must be Domain"
|
"USERLOGINMUSTBEDOMAIN":"User Login must be Domain",
|
||||||
|
"ALLOWUSERNAMEPASSWORD":"Username Password allowed",
|
||||||
|
"ALLOWEXTERNALIDP":"External IDP allowed",
|
||||||
|
"ALLOWREGISTER":"Register allowed"
|
||||||
},
|
},
|
||||||
"DELETE":"Uninstall / Reset Policy"
|
"DELETE":"Uninstall / Reset Policy"
|
||||||
},
|
},
|
||||||
@ -612,6 +623,51 @@
|
|||||||
"DELETED":"Deleted Project!"
|
"DELETED":"Deleted Project!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"IDP":{
|
||||||
|
"LIST": {
|
||||||
|
"TITLE":"Identity Providers",
|
||||||
|
"DESCRIPTION":"Define additional Identity Providers, which can be used to authenticate in your organisations."
|
||||||
|
},
|
||||||
|
"CREATE": {
|
||||||
|
"TITLE":"New Identity Provider",
|
||||||
|
"DESCRIPTION":"Configure the Endpoint of your new service provider."
|
||||||
|
},
|
||||||
|
"TYPES": {
|
||||||
|
"0":"unknown",
|
||||||
|
"1":"System",
|
||||||
|
"2":"Organisation"
|
||||||
|
},
|
||||||
|
"STATES":{
|
||||||
|
"0":"active",
|
||||||
|
"1":"inactive"
|
||||||
|
},
|
||||||
|
"TYPE":"Type",
|
||||||
|
"NAME":"Name",
|
||||||
|
"CONFIG":"Configuration",
|
||||||
|
"STATE":"State",
|
||||||
|
"LOGOSRC":"Logo Src",
|
||||||
|
"ISSUER":"Issuer",
|
||||||
|
"SCOPESLIST":"Scopes List",
|
||||||
|
"CLIENTID":"Client ID",
|
||||||
|
"CLIENTSECRET":"Client Secret",
|
||||||
|
"CREATIONDATE":"Created At",
|
||||||
|
"CHANGEDATE":"Last Modified",
|
||||||
|
"DEACTIVATE":"Deactivate",
|
||||||
|
"ACTIVATE":"Activate",
|
||||||
|
"DELETE":"Delete"
|
||||||
|
},
|
||||||
|
"LOGINPOLICY": {
|
||||||
|
"CREATE": {
|
||||||
|
"TITLE":"Login Policy",
|
||||||
|
"DESCRIPTION":"Define how your users can be authenticated on your organisation."
|
||||||
|
},
|
||||||
|
"IDPS":"Identity Providers",
|
||||||
|
"ADDIDP": {
|
||||||
|
"TITLE":"Add Identity Provider",
|
||||||
|
"DESCRIPTION":"You can select predefined or selfcreated providers for authentication.",
|
||||||
|
"SELECTIDPS":"Identity providers"
|
||||||
|
}
|
||||||
|
},
|
||||||
"APP": {
|
"APP": {
|
||||||
"LIST": "Applications",
|
"LIST": "Applications",
|
||||||
"PAGES": {
|
"PAGES": {
|
||||||
|
@ -11,10 +11,6 @@
|
|||||||
.mat-paginator {
|
.mat-paginator {
|
||||||
background-color: inherit !important;
|
background-color: inherit !important;
|
||||||
transition: background-color .4s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
|
|
||||||
&.background-style {
|
|
||||||
background-color: inherit !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* stylelint-enable */
|
/* stylelint-enable */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user