mirror of
https://github.com/zitadel/zitadel.git
synced 2025-06-04 05:18:20 +00:00
feat: console feature api (#1480)
This commit is contained in:
parent
c0b37924c8
commit
0a058a821e
@ -36,6 +36,7 @@ import { AvatarModule } from './modules/avatar/avatar.module';
|
|||||||
import { InputModule } from './modules/input/input.module';
|
import { InputModule } from './modules/input/input.module';
|
||||||
import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.module';
|
import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.module';
|
||||||
import { SignedoutComponent } from './pages/signedout/signedout.component';
|
import { SignedoutComponent } from './pages/signedout/signedout.component';
|
||||||
|
import { HasFeaturePipeModule } from './pipes/has-feature-pipe/has-feature-pipe.module';
|
||||||
import { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module';
|
import { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module';
|
||||||
import { GrpcAuthService } from './services/grpc-auth.service';
|
import { GrpcAuthService } from './services/grpc-auth.service';
|
||||||
import { GrpcService } from './services/grpc.service';
|
import { GrpcService } from './services/grpc.service';
|
||||||
@ -112,6 +113,7 @@ const authConfig: AuthConfig = {
|
|||||||
OutsideClickModule,
|
OutsideClickModule,
|
||||||
InputModule,
|
InputModule,
|
||||||
HasRolePipeModule,
|
HasRolePipeModule,
|
||||||
|
HasFeaturePipeModule,
|
||||||
MatProgressBarModule,
|
MatProgressBarModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
|
@ -75,7 +75,6 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public scrollHandler(e: any): void {
|
public scrollHandler(e: any): void {
|
||||||
console.log('bottom');
|
|
||||||
if (e === 'bottom') {
|
if (e === 'bottom') {
|
||||||
this.more();
|
this.more();
|
||||||
}
|
}
|
||||||
@ -107,7 +106,6 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private more(): void {
|
private more(): void {
|
||||||
const cursor = this.getCursor();
|
const cursor = this.getCursor();
|
||||||
console.log('cursor' + cursor);
|
|
||||||
|
|
||||||
let more: Promise<ListChanges>;
|
let more: Promise<ListChanges>;
|
||||||
|
|
||||||
@ -152,7 +150,6 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
|||||||
take(1),
|
take(1),
|
||||||
tap((res: ListChanges) => {
|
tap((res: ListChanges) => {
|
||||||
const values = res.resultList;
|
const values = res.resultList;
|
||||||
console.log(values);
|
|
||||||
const mapped = this.mapChanges(values);
|
const mapped = this.mapChanges(values);
|
||||||
// update source with new values, done loading
|
// update source with new values, done loading
|
||||||
// this._data.next(values);
|
// this._data.next(values);
|
||||||
@ -226,7 +223,6 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
|||||||
return parseFloat(b.key) - parseFloat(a.key);
|
return parseFloat(b.key) - parseFloat(a.key);
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log(arr);
|
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +233,6 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
getTimestampIndex(date: any): number {
|
getTimestampIndex(date: any): number {
|
||||||
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000 / 1000);
|
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000 / 1000);
|
||||||
console.log(ts);
|
|
||||||
return ts.getTime();
|
return ts.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,14 +36,14 @@
|
|||||||
<ng-container matColumnDef="creationDate">
|
<ng-container matColumnDef="creationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.CREATIONDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let key">
|
<td mat-cell *matCellDef="let key">
|
||||||
{{key.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
{{key.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="expirationDate">
|
<ng-container matColumnDef="expirationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.EXPIRATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.EXPIRATIONDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let key">
|
<td mat-cell *matCellDef="let key">
|
||||||
{{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
{{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -86,7 +86,6 @@ export class ClientKeysComponent implements OnInit {
|
|||||||
|
|
||||||
if (resp.date as Moment) {
|
if (resp.date as Moment) {
|
||||||
const ts = new Timestamp();
|
const ts = new Timestamp();
|
||||||
console.log(resp.date.toDate());
|
|
||||||
const milliseconds = resp.date.toDate().getTime();
|
const milliseconds = resp.date.toDate().getTime();
|
||||||
const seconds = Math.abs(milliseconds / 1000);
|
const seconds = Math.abs(milliseconds / 1000);
|
||||||
const nanos = (milliseconds - seconds * 1000) * 1000 * 1000;
|
const nanos = (milliseconds - seconds * 1000) * 1000 * 1000;
|
||||||
|
20
console/src/app/modules/features/features-routing.module.ts
Normal file
20
console/src/app/modules/features/features-routing.module.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { FeaturesComponent } from './features.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: FeaturesComponent,
|
||||||
|
data: {
|
||||||
|
animation: 'DetailPage',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class FeaturesRoutingModule { }
|
86
console/src/app/modules/features/features.component.html
Normal file
86
console/src/app/modules/features/features.component.html
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<app-detail-layout [backRouterLink]="[ serviceType === FeatureServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||||
|
[title]="'FEATURES.TITLE' | translate" [description]="'FEATURES.DESCRIPTION' | translate">
|
||||||
|
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
|
||||||
|
|
||||||
|
<ng-template appHasRole [appHasRole]="['iam.features.delete']">
|
||||||
|
<button *ngIf="serviceType === FeatureServiceType.MGMT && !isDefault"
|
||||||
|
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="resetFeatures()" mat-stroked-button>
|
||||||
|
{{'POLICY.RESET' | translate}}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="content" *ngIf="features">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.AUDITLOGRETENTION' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<span>{{features.auditLogRetention | json }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYUSERNAMELOGIN' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="features.loginPolicyUsernameLogin"
|
||||||
|
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="features.loginPolicyRegistration"
|
||||||
|
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYIDP' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.loginPolicyIdp"
|
||||||
|
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYFACTORS' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="features.loginPolicyFactors"
|
||||||
|
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYPASSWORDLESS' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="features.loginPolicyPasswordless"
|
||||||
|
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYCOMPLEXITYPOLICY' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||||
|
[(ngModel)]="features.passwordComplexityPolicy"
|
||||||
|
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICY' | translate}}</span>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicy"
|
||||||
|
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container">
|
||||||
|
<button (click)="savePolicy()" [disabled]="(['iam.features.write'] | hasRole | async) == false" color="primary"
|
||||||
|
type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||||
|
}}</button>
|
||||||
|
</div>
|
||||||
|
</app-detail-layout>
|
41
console/src/app/modules/features/features.component.scss
Normal file
41
console/src/app/modules/features/features.component.scss
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
.default {
|
||||||
|
color: var(--color-main);
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: .3rem 0;
|
||||||
|
|
||||||
|
.left-desc {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
25
console/src/app/modules/features/features.component.spec.ts
Normal file
25
console/src/app/modules/features/features.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { FeaturesComponent } from './features.component';
|
||||||
|
|
||||||
|
describe('FeaturesComponent', () => {
|
||||||
|
let component: FeaturesComponent;
|
||||||
|
let fixture: ComponentFixture<FeaturesComponent>;
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [FeaturesComponent],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(FeaturesComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
144
console/src/app/modules/features/features.component.ts
Normal file
144
console/src/app/modules/features/features.component.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { Component, Injector, OnDestroy, Type } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
GetOrgFeaturesResponse,
|
||||||
|
SetDefaultFeaturesRequest,
|
||||||
|
SetOrgFeaturesRequest,
|
||||||
|
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||||
|
import { Features } from 'src/app/proto/generated/zitadel/features_pb';
|
||||||
|
import { GetFeaturesResponse } from 'src/app/proto/generated/zitadel/management_pb';
|
||||||
|
import { Org } from 'src/app/proto/generated/zitadel/org_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 FeatureServiceType {
|
||||||
|
MGMT,
|
||||||
|
ADMIN,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-features',
|
||||||
|
templateUrl: './features.component.html',
|
||||||
|
styleUrls: ['./features.component.scss'],
|
||||||
|
})
|
||||||
|
export class FeaturesComponent implements OnDestroy {
|
||||||
|
private managementService!: ManagementService;
|
||||||
|
public serviceType!: FeatureServiceType;
|
||||||
|
|
||||||
|
public features!: Features.AsObject;
|
||||||
|
|
||||||
|
private sub: Subscription = new Subscription();
|
||||||
|
private org!: Org.AsObject;
|
||||||
|
|
||||||
|
public FeatureServiceType: any = FeatureServiceType;
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private toast: ToastService,
|
||||||
|
private sessionStorage: StorageService,
|
||||||
|
private injector: Injector,
|
||||||
|
private adminService: AdminService,
|
||||||
|
) {
|
||||||
|
const temporg = this.sessionStorage.getItem('organization') as Org.AsObject;
|
||||||
|
if (temporg) {
|
||||||
|
this.org = temporg;
|
||||||
|
}
|
||||||
|
this.sub = this.route.data.pipe(switchMap(data => {
|
||||||
|
this.serviceType = data.serviceType;
|
||||||
|
if (this.serviceType === FeatureServiceType.MGMT) {
|
||||||
|
this.managementService = this.injector.get(ManagementService as Type<ManagementService>);
|
||||||
|
}
|
||||||
|
return this.route.params;
|
||||||
|
})).subscribe(_ => {
|
||||||
|
this.fetchData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchData(): void {
|
||||||
|
this.getData().then(resp => {
|
||||||
|
if (resp?.features) {
|
||||||
|
this.features = resp.features;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getData(): Promise<GetFeaturesResponse.AsObject | GetOrgFeaturesResponse.AsObject | undefined> {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case FeatureServiceType.MGMT:
|
||||||
|
return this.managementService.getFeatures();
|
||||||
|
case FeatureServiceType.ADMIN:
|
||||||
|
if (this.org?.id) {
|
||||||
|
return this.adminService.getDefaultFeatures();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public savePolicy(): void {
|
||||||
|
switch (this.serviceType) {
|
||||||
|
case FeatureServiceType.MGMT:
|
||||||
|
const req = new SetOrgFeaturesRequest();
|
||||||
|
req.setOrgId(this.org.id);
|
||||||
|
|
||||||
|
req.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin);
|
||||||
|
req.setLoginPolicyRegistration(this.features.loginPolicyRegistration);
|
||||||
|
req.setLoginPolicyIdp(this.features.loginPolicyIdp);
|
||||||
|
req.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||||
|
req.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||||
|
req.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||||
|
req.setLabelPolicy(this.features.labelPolicy);
|
||||||
|
|
||||||
|
this.adminService.setOrgFeatures(req).then(() => {
|
||||||
|
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case FeatureServiceType.ADMIN:
|
||||||
|
// update Default org iam policy?
|
||||||
|
const dreq = new SetDefaultFeaturesRequest();
|
||||||
|
dreq.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin);
|
||||||
|
dreq.setLoginPolicyRegistration(this.features.loginPolicyRegistration);
|
||||||
|
dreq.setLoginPolicyIdp(this.features.loginPolicyIdp);
|
||||||
|
dreq.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||||
|
dreq.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||||
|
dreq.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||||
|
dreq.setLabelPolicy(this.features.labelPolicy);
|
||||||
|
|
||||||
|
this.adminService.setDefaultFeatures(dreq).then(() => {
|
||||||
|
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetFeatures(): void {
|
||||||
|
if (this.serviceType === FeatureServiceType.MGMT) {
|
||||||
|
this.adminService.resetOrgFeatures(this.org.id).then(() => {
|
||||||
|
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.fetchData();
|
||||||
|
}, 1000);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isDefault(): boolean {
|
||||||
|
if (this.features && this.serviceType === FeatureServiceType.MGMT) {
|
||||||
|
return this.features.isDefault;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
console/src/app/modules/features/features.module.ts
Normal file
42
console/src/app/modules/features/features.module.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
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 { InputModule } from 'src/app/modules/input/input.module';
|
||||||
|
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||||
|
|
||||||
|
import { InfoSectionModule } from '../info-section/info-section.module';
|
||||||
|
import { FeaturesRoutingModule } from './features-routing.module';
|
||||||
|
import { FeaturesComponent } from './features.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
FeaturesComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
FeaturesRoutingModule,
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
InputModule,
|
||||||
|
MatButtonModule,
|
||||||
|
HasRoleModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatIconModule,
|
||||||
|
HasRoleModule,
|
||||||
|
HasRolePipeModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
InfoSectionModule,
|
||||||
|
TranslateModule,
|
||||||
|
DetailLayoutModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
FeaturesComponent,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class FeaturesModule { }
|
@ -80,7 +80,6 @@ export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestr
|
|||||||
|
|
||||||
@HostListener('blur', ['false'])
|
@HostListener('blur', ['false'])
|
||||||
_focusChanged(isFocused: boolean): void {
|
_focusChanged(isFocused: boolean): void {
|
||||||
console.log('blur1');
|
|
||||||
if (isFocused !== this.focused && (!isFocused)) {
|
if (isFocused !== this.focused && (!isFocused)) {
|
||||||
this.focused = isFocused;
|
this.focused = isFocused;
|
||||||
this.stateChanges.next();
|
this.stateChanges.next();
|
||||||
|
@ -153,7 +153,6 @@ export class IdpTableComponent implements OnInit {
|
|||||||
(this.service as ManagementService).listOrgIDPs(limit, offset).then(resp => {
|
(this.service as ManagementService).listOrgIDPs(limit, offset).then(resp => {
|
||||||
this.idpResult = resp;
|
this.idpResult = resp;
|
||||||
this.dataSource.data = resp.resultList;
|
this.dataSource.data = resp.resultList;
|
||||||
console.log(resp.resultList);
|
|
||||||
this.loadingSubject.next(false);
|
this.loadingSubject.next(false);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
@ -163,7 +162,6 @@ export class IdpTableComponent implements OnInit {
|
|||||||
(this.service as AdminService).listIDPs(limit, offset).then(resp => {
|
(this.service as AdminService).listIDPs(limit, offset).then(resp => {
|
||||||
this.idpResult = resp;
|
this.idpResult = resp;
|
||||||
this.dataSource.data = resp.resultList;
|
this.dataSource.data = resp.resultList;
|
||||||
console.log(resp.resultList);
|
|
||||||
|
|
||||||
this.loadingSubject.next(false);
|
this.loadingSubject.next(false);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="info-section-row" [ngClass]="{'info': type == 'INFO', 'warn': type == 'WARN'}">
|
<div class="info-section-row" [ngClass]="{'info': type == 'INFO', 'warn': type == 'WARN'}">
|
||||||
<i *ngIf="type == 'INFO'" class="icon las la-info"></i>
|
<i *ngIf="type == 'INFO'" class="icon las la-info"></i>
|
||||||
<i *ngIf="type == 'WARN'" class="las la-exclamation"></i>
|
<i *ngIf="type == 'WARN'" class="icon las la-exclamation"></i>
|
||||||
|
|
||||||
<div class="info-section-content">
|
<div class="info-section-content">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
margin-left: .5rem;
|
margin-left: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.info-section-content {
|
.info-section-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@ -37,6 +36,10 @@
|
|||||||
&.warn {
|
&.warn {
|
||||||
background-color: if($is-dark-theme, #4f566b, #ffc1c1);
|
background-color: if($is-dark-theme, #4f566b, #ffc1c1);
|
||||||
color: if($is-dark-theme, #ffc1c1, #620e0e);
|
color: if($is-dark-theme, #ffc1c1, #620e0e);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: if($is-dark-theme, #ffc1c1, #620e0e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,14 +36,14 @@
|
|||||||
<ng-container matColumnDef="creationDate">
|
<ng-container matColumnDef="creationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.CREATIONDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let key">
|
<td mat-cell *matCellDef="let key">
|
||||||
{{key.details?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
{{key.details?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="expirationDate">
|
<ng-container matColumnDef="expirationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.EXPIRATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.EXPIRATIONDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let key">
|
<td mat-cell *matCellDef="let key">
|
||||||
{{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
{{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -85,7 +85,6 @@ export class MachineKeysComponent implements OnInit {
|
|||||||
|
|
||||||
if (resp.date as Moment) {
|
if (resp.date as Moment) {
|
||||||
const ts = new Timestamp();
|
const ts = new Timestamp();
|
||||||
console.log(resp.date.toDate());
|
|
||||||
const milliseconds = resp.date.toDate().getTime();
|
const milliseconds = resp.date.toDate().getTime();
|
||||||
const seconds = Math.abs(milliseconds / 1000);
|
const seconds = Math.abs(milliseconds / 1000);
|
||||||
const nanos = (milliseconds - seconds * 1000) * 1000 * 1000;
|
const nanos = (milliseconds - seconds * 1000) * 1000 * 1000;
|
||||||
@ -123,7 +122,6 @@ export class MachineKeysComponent implements OnInit {
|
|||||||
if (this.userId) {
|
if (this.userId) {
|
||||||
this.mgmtService.listMachineKeys(this.userId, limit, offset).then(resp => {
|
this.mgmtService.listMachineKeys(this.userId, limit, offset).then(resp => {
|
||||||
this.keyResult = resp;
|
this.keyResult = resp;
|
||||||
console.log(resp);
|
|
||||||
if (resp.resultList) {
|
if (resp.resultList) {
|
||||||
this.dataSource.data = resp.resultList;
|
this.dataSource.data = resp.resultList;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.':
|
{{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.':
|
||||||
LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||||
</div>
|
</div>
|
||||||
<div class="new-mfa" (click)="addMfa()" matRipple>
|
<div class="new-mfa" [ngClass]="{'disabled': disabled}" (click)="!disabled ? addMfa(): null" matRipple>
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -49,5 +49,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: .5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,8 +214,6 @@ export class MfaTableComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(this.mfas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshPageAfterTimout(to: number): void {
|
public refreshPageAfterTimout(to: number): void {
|
||||||
|
@ -63,7 +63,6 @@ export class AddIdpDialogComponent {
|
|||||||
case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM:
|
case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM:
|
||||||
this.adminService.listIDPs().then(resp => {
|
this.adminService.listIDPs().then(resp => {
|
||||||
this.availableIdps = resp.resultList;
|
this.availableIdps = resp.resultList;
|
||||||
console.log(resp);
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -37,9 +37,17 @@
|
|||||||
[(ngModel)]="loginData.allowRegister">
|
[(ngModel)]="loginData.allowRegister">
|
||||||
{{'POLICY.DATA.ALLOWREGISTER' | translate}}
|
{{'POLICY.DATA.ALLOWREGISTER' | translate}}
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
<cnsl-info-section class="info">
|
|
||||||
{{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}}
|
<ng-container *ngIf="(['login_policy.registration'] | hasFeature | async) == false; else regInfo">
|
||||||
</cnsl-info-section>
|
<cnsl-info-section type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||||
|
'login_policy.registration'})}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #regInfo>
|
||||||
|
<cnsl-info-section class="info">
|
||||||
|
{{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
|
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
|
||||||
@ -60,14 +68,21 @@
|
|||||||
</cnsl-info-section>
|
</cnsl-info-section>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||||
<cnsl-label>{{'LOGINPOLICY.PASSWORDLESS' | translate}}</cnsl-label>
|
<cnsl-label>{{'LOGINPOLICY.PASSWORDLESS' | translate}}</cnsl-label>
|
||||||
<mat-select [(ngModel)]="loginData.passwordlessType">
|
<mat-select [(ngModel)]="loginData.passwordlessType"
|
||||||
|
[disabled]="disabled || (['login_policy.passwordless'] | hasFeature | async) == false">
|
||||||
<mat-option *ngFor="let pt of passwordlessTypes" [value]="pt">
|
<mat-option *ngFor="let pt of passwordlessTypes" [value]="pt">
|
||||||
{{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}}
|
{{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</cnsl-form-field>
|
</cnsl-form-field>
|
||||||
|
<ng-container *ngIf="(['login_policy.passwordless'] | hasFeature | async) == false">
|
||||||
|
<cnsl-info-section type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||||
|
'login_policy.passwordless'})}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -79,24 +94,40 @@
|
|||||||
<ng-container *ngIf="!isDefault">
|
<ng-container *ngIf="!isDefault">
|
||||||
<h3 class="subheader">{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}</h3>
|
<h3 class="subheader">{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}</h3>
|
||||||
<p class="subdesc">{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}</p>
|
<p class="subdesc">{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}</p>
|
||||||
|
<ng-container *ngIf="(['login_policy.factors'] | hasFeature | async) == false">
|
||||||
|
<cnsl-info-section type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
</ng-container>
|
||||||
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||||
[componentType]="LoginMethodComponentType.MultiFactor"
|
[componentType]="LoginMethodComponentType.MultiFactor"
|
||||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false">
|
[disabled]="(([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false) || ((['login_policy.factors'] | hasFeature | async) == false)">
|
||||||
</app-mfa-table>
|
</app-mfa-table>
|
||||||
|
|
||||||
<h3 class="subheader">{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}</h3>
|
<h3 class="subheader">{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}</h3>
|
||||||
<p class="subdesc">{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}</p>
|
<p class="subdesc">{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}</p>
|
||||||
|
<ng-container *ngIf="(['login_policy.factors'] | hasFeature | async) == false">
|
||||||
|
<cnsl-info-section type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
</ng-container>
|
||||||
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||||
[componentType]="LoginMethodComponentType.SecondFactor"
|
[componentType]="LoginMethodComponentType.SecondFactor"
|
||||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false">
|
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false || ((['login_policy.factors'] | hasFeature | async) == false)">
|
||||||
</app-mfa-table>
|
</app-mfa-table>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<h3 class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</h3>
|
<h3 class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</h3>
|
||||||
|
|
||||||
|
<ng-container *ngIf="(['login_policy.idp'] | hasFeature | async) == false">
|
||||||
|
<cnsl-info-section type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||||
|
'login_policy.idp'})}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<div class="idps">
|
<div class="idps">
|
||||||
<div class="idp" [ngClass]="{'disabled': disabled}" *ngFor="let idp of idps">
|
<div class="idp" [ngClass]="{'disabled': disabled || (['login_policy.idp'] | hasFeature | async) == false}"
|
||||||
<button [disabled]="disabled" mat-icon-button (click)="removeIdp(idp)" class="rm">
|
*ngFor="let idp of idps">
|
||||||
|
<button [disabled]="disabled || (['login_policy.idp'] | hasFeature | async) == false" mat-icon-button
|
||||||
|
(click)="removeIdp(idp)" class="rm">
|
||||||
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">
|
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">
|
||||||
remove_circle</mat-icon>
|
remove_circle</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
@ -105,13 +136,15 @@
|
|||||||
*ngIf="idp.stylingType == IDPStylingType.STYLING_TYPE_GOOGLE" alt="google" />
|
*ngIf="idp.stylingType == IDPStylingType.STYLING_TYPE_GOOGLE" alt="google" />
|
||||||
<div>
|
<div>
|
||||||
<span class="name">{{idp.name}}</span>
|
<span class="name">{{idp.name}}</span>
|
||||||
<span class="meta-info">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.OWNERTYPES.'+idp.idpType | translate
|
<span class="meta-info">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.OWNERTYPES.'+idp.idpType |
|
||||||
|
translate
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="meta-info">{{ 'IDP.ID' | translate }}: {{idp.idpId}}</span>
|
<span class="meta-info">{{ 'IDP.ID' | translate }}: {{idp.idpId}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!disabled" class="new-idp" (click)="openDialog()" matRipple>
|
<div *ngIf="!disabled && (['login_policy.idp'] | hasFeature | async)" class="new-idp" (click)="openDialog()"
|
||||||
|
matRipple>
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -127,7 +160,7 @@
|
|||||||
<i class="lab la-gitlab"></i>
|
<i class="lab la-gitlab"></i>
|
||||||
</div>
|
</div>
|
||||||
<app-idp-table [service]="service" [serviceType]="serviceType"
|
<app-idp-table [service]="service" [serviceType]="serviceType"
|
||||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false">
|
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false || (['login_policy.idp'] | hasFeature | async) == false">
|
||||||
</app-idp-table>
|
</app-idp-table>
|
||||||
</app-card>
|
</app-card>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -11,12 +11,13 @@
|
|||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
|
||||||
.toggle {
|
.toggle {
|
||||||
margin: .3rem 0;
|
margin: .3rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
margin-bottom: 1rem;
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,6 @@ export class LoginPolicyComponent implements OnDestroy {
|
|||||||
});
|
});
|
||||||
this.getIdps().then(resp => {
|
this.getIdps().then(resp => {
|
||||||
this.idps = resp;
|
this.idps = resp;
|
||||||
console.log(resp);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +143,6 @@ export class LoginPolicyComponent implements OnDestroy {
|
|||||||
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||||
// console.log(mgmtreq.toObject());
|
|
||||||
if ((this.loginData as LoginPolicy.AsObject).isDefault) {
|
if ((this.loginData as LoginPolicy.AsObject).isDefault) {
|
||||||
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
|
return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
|
||||||
} else {
|
} else {
|
||||||
@ -158,8 +156,6 @@ export class LoginPolicyComponent implements OnDestroy {
|
|||||||
adminreq.setForceMfa(this.loginData.forceMfa);
|
adminreq.setForceMfa(this.loginData.forceMfa);
|
||||||
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||||
|
|
||||||
// console.log(adminreq.toObject());
|
|
||||||
|
|
||||||
return (this.service as AdminService).updateLoginPolicy(adminreq);
|
return (this.service as AdminService).updateLoginPolicy(adminreq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,11 @@ import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.
|
|||||||
import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module';
|
import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module';
|
||||||
import { InputModule } from 'src/app/modules/input/input.module';
|
import { InputModule } from 'src/app/modules/input/input.module';
|
||||||
import { MfaTableModule } from 'src/app/modules/mfa-table/mfa-table.module';
|
import { MfaTableModule } from 'src/app/modules/mfa-table/mfa-table.module';
|
||||||
|
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||||
|
|
||||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||||
import { LinksModule } from '../../links/links.module';
|
import { LinksModule } from '../../links/links.module';
|
||||||
|
|
||||||
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
|
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
|
||||||
import { LoginPolicyRoutingModule } from './login-policy-routing.module';
|
import { LoginPolicyRoutingModule } from './login-policy-routing.module';
|
||||||
import { LoginPolicyComponent } from './login-policy.component';
|
import { LoginPolicyComponent } from './login-policy.component';
|
||||||
@ -33,6 +34,7 @@ import { LoginPolicyComponent } from './login-policy.component';
|
|||||||
CardModule,
|
CardModule,
|
||||||
InputModule,
|
InputModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
|
HasFeaturePipeModule,
|
||||||
MatSlideToggleModule,
|
MatSlideToggleModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
HasRoleModule,
|
HasRoleModule,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Injector, Input, OnDestroy, Type } from '@angular/core';
|
import { Component, Injector, OnDestroy, Type } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
@ -10,6 +10,7 @@ import { AdminService } from 'src/app/services/admin.service';
|
|||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { StorageService } from 'src/app/services/storage.service';
|
import { StorageService } from 'src/app/services/storage.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
import { CnslLinks } from '../../links/links.component';
|
import { CnslLinks } from '../../links/links.component';
|
||||||
import {
|
import {
|
||||||
IAM_COMPLEXITY_LINK,
|
IAM_COMPLEXITY_LINK,
|
||||||
@ -26,7 +27,6 @@ import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
|||||||
styleUrls: ['./org-iam-policy.component.scss'],
|
styleUrls: ['./org-iam-policy.component.scss'],
|
||||||
})
|
})
|
||||||
export class OrgIamPolicyComponent implements OnDestroy {
|
export class OrgIamPolicyComponent implements OnDestroy {
|
||||||
@Input() service!: AdminService;
|
|
||||||
private managementService!: ManagementService;
|
private managementService!: ManagementService;
|
||||||
public serviceType!: PolicyComponentServiceType;
|
public serviceType!: PolicyComponentServiceType;
|
||||||
|
|
||||||
|
@ -77,7 +77,6 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
|
|||||||
this.getData().then(data => {
|
this.getData().then(data => {
|
||||||
if (data.policy) {
|
if (data.policy) {
|
||||||
this.complexityData = data.policy;
|
this.complexityData = data.policy;
|
||||||
console.log(data.policy);
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -57,10 +57,16 @@
|
|||||||
<p class="desc">
|
<p class="desc">
|
||||||
{{'POLICY.IAM_POLICY.DESCRIPTION' | translate}}</p>
|
{{'POLICY.IAM_POLICY.DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<cnsl-info-section class="warn" *ngIf="(['password_complexity_policy'] | hasFeature | async) == false"
|
||||||
|
type="WARN">
|
||||||
|
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||||
|
'password_complexity_policy'})}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
|
||||||
<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
|
<button [disabled]="(['password_complexity_policy'] | hasFeature | async) == false"
|
||||||
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.IAM ]"
|
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.IAM ]"
|
||||||
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -111,12 +117,18 @@
|
|||||||
<p class="desc">
|
<p class="desc">
|
||||||
{{'POLICY.LABEL.DESCRIPTION' | translate}}</p>
|
{{'POLICY.LABEL.DESCRIPTION' | translate}}</p>
|
||||||
|
|
||||||
|
<cnsl-info-section class="warn" *ngIf="(['label_policy'] | hasFeature | async) == false" type="WARN">
|
||||||
|
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||||
|
'label_policy'})}}
|
||||||
|
</cnsl-info-section>
|
||||||
|
|
||||||
<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
|
<button
|
||||||
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.LABEL ]"
|
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.LABEL ]"
|
||||||
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
mat-stroked-button [disabled]="(['label_policy'] | hasFeature | async) == false">
|
||||||
|
{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,7 @@ h2 {
|
|||||||
|
|
||||||
.top-desc {
|
.top-desc {
|
||||||
color: var(--grey);
|
color: var(--grey);
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-lyt {
|
.row-lyt {
|
||||||
@ -65,6 +66,10 @@ h2 {
|
|||||||
color: var(--grey);
|
color: var(--grey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warn {
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.icons {
|
.icons {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
.icon {
|
.icon {
|
||||||
|
@ -6,8 +6,10 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
|||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
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 { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||||
|
|
||||||
|
import { InfoSectionModule } from '../info-section/info-section.module';
|
||||||
import { PolicyGridComponent } from './policy-grid.component';
|
import { PolicyGridComponent } from './policy-grid.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -21,6 +23,8 @@ import { PolicyGridComponent } from './policy-grid.component';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
|
InfoSectionModule,
|
||||||
|
HasFeaturePipeModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
PolicyGridComponent,
|
PolicyGridComponent,
|
||||||
|
@ -62,7 +62,6 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
|
|||||||
} else if (this.target === UserTarget.SELF) {
|
} else if (this.target === UserTarget.SELF) {
|
||||||
this.getFilteredResults(); // new subscription
|
this.getFilteredResults(); // new subscription
|
||||||
}
|
}
|
||||||
console.log(this.users);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngAfterContentChecked(): void {
|
public ngAfterContentChecked(): void {
|
||||||
@ -90,7 +89,6 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
|
|||||||
}),
|
}),
|
||||||
).subscribe((userresp: ListUsersResponse.AsObject | unknown) => {
|
).subscribe((userresp: ListUsersResponse.AsObject | unknown) => {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
console.log(userresp);
|
|
||||||
if (this.target === UserTarget.SELF && userresp) {
|
if (this.target === UserTarget.SELF && userresp) {
|
||||||
this.filteredUsers = (userresp as ListUsersResponse.AsObject).resultList;
|
this.filteredUsers = (userresp as ListUsersResponse.AsObject).resultList;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,6 @@ export class UserGrantsDataSource extends DataSource<UserGrant.AsObject> {
|
|||||||
catchError(() => of([])),
|
catchError(() => of([])),
|
||||||
finalize(() => this.loadingSubject.next(false)),
|
finalize(() => this.loadingSubject.next(false)),
|
||||||
).subscribe(grants => {
|
).subscribe(grants => {
|
||||||
console.log(grants);
|
|
||||||
this.grantsSubject.next(grants);
|
this.grantsSubject.next(grants);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,6 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getGrantRoleOptions(id: string, projectId: string): void {
|
private getGrantRoleOptions(id: string, projectId: string): void {
|
||||||
console.log(projectId, id);
|
|
||||||
this.mgmtService.getGrantedProjectByID(projectId, id).then(resp => {
|
this.mgmtService.getGrantedProjectByID(projectId, id).then(resp => {
|
||||||
if (resp.grantedProject) {
|
if (resp.grantedProject) {
|
||||||
this.loadedId = id;
|
this.loadedId = id;
|
||||||
|
@ -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 { FeatureServiceType } from 'src/app/modules/features/features.component';
|
||||||
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
|
|
||||||
import { EventstoreComponent } from './eventstore/eventstore.component';
|
import { EventstoreComponent } from './eventstore/eventstore.component';
|
||||||
@ -32,6 +33,15 @@ const routes: Routes = [
|
|||||||
roles: ['iam.member.read'],
|
roles: ['iam.member.read'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'features',
|
||||||
|
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||||
|
// canActivate: [RoleGuard],
|
||||||
|
data: {
|
||||||
|
roles: ['iam.features.read'],
|
||||||
|
serviceType: FeatureServiceType.ADMIN
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'idp',
|
path: 'idp',
|
||||||
children: [
|
children: [
|
||||||
|
@ -3,6 +3,22 @@
|
|||||||
<h1 class="h1">{{'IAM.POLICIES.TITLE' | translate}}</h1>
|
<h1 class="h1">{{'IAM.POLICIES.TITLE' | translate}}</h1>
|
||||||
<p class="sub">{{'IAM.POLICIES.DESCRIPTION' | translate}} </p>
|
<p class="sub">{{'IAM.POLICIES.DESCRIPTION' | translate}} </p>
|
||||||
|
|
||||||
|
<h2>{{'FEATURES.TITLE' | translate}}</h2>
|
||||||
|
<p class="top-desc">{{'FEATURES.DESCRIPTION' | translate}}</p>
|
||||||
|
<div *ngIf="features" class="tier">
|
||||||
|
<mat-icon>stars</mat-icon>
|
||||||
|
<div class="text" *ngIf="features.tier">
|
||||||
|
<p class="title"><strong>ZITADEL {{features.tier.name}}</strong></p>
|
||||||
|
<p>{{features.tier?.description}}</p>
|
||||||
|
<p>{{'FEATURES.TIERSTATES.'+features.tier.state | translate}}</p>
|
||||||
|
<p>{{features.tier?.statusInfo}}</p>
|
||||||
|
</div>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<a class="ext" href="https://zitadel.ch/pricing" target="_blank"><i
|
||||||
|
class="las la-external-link-alt"></i></a>
|
||||||
|
<button mat-raised-button [routerLink]="['/iam','features']">{{'FEATURES.BTN-EDIT' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-policy-grid [type]="PolicyGridType.IAM"></app-policy-grid>
|
<app-policy-grid [type]="PolicyGridType.IAM"></app-policy-grid>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -6,3 +6,48 @@
|
|||||||
color: var(--grey);
|
color: var(--grey);
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-desc {
|
||||||
|
color: var(--grey);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tier {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
color: white;
|
||||||
|
background-color: rgb(245, 203, 99);
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
|
||||||
|
.ext {
|
||||||
|
margin-right: .5rem;
|
||||||
|
align-self: center;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ 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 { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
|
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
|
||||||
|
import { Features } from 'src/app/proto/generated/zitadel/features_pb';
|
||||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||||
import { AdminService } from 'src/app/services/admin.service';
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
@ -25,10 +26,13 @@ export class IamComponent {
|
|||||||
= new BehaviorSubject<Member.AsObject[]>([]);
|
= new BehaviorSubject<Member.AsObject[]>([]);
|
||||||
|
|
||||||
public PolicyGridType: any = PolicyGridType;
|
public PolicyGridType: any = PolicyGridType;
|
||||||
|
public features!: Features.AsObject;
|
||||||
|
|
||||||
constructor(public 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();
|
||||||
|
this.loadFeatures();
|
||||||
|
this.adminService.getDefaultFeatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadMembers(): void {
|
public loadMembers(): void {
|
||||||
@ -79,4 +83,13 @@ export class IamComponent {
|
|||||||
public showDetail(): void {
|
public showDetail(): void {
|
||||||
this.router.navigate(['iam/members']);
|
this.router.navigate(['iam/members']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public loadFeatures(): void {
|
||||||
|
this.loadingSubject.next(true);
|
||||||
|
this.adminService.getDefaultFeatures().then(resp => {
|
||||||
|
if (resp.features) {
|
||||||
|
this.features = resp.features;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,6 @@ export class OrgCreateComponent {
|
|||||||
name: ['', [Validators.required]],
|
name: ['', [Validators.required]],
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(this.orgForm);
|
|
||||||
} else {
|
} else {
|
||||||
this.createSteps = 2;
|
this.createSteps = 2;
|
||||||
|
|
||||||
|
@ -28,6 +28,23 @@
|
|||||||
</app-card>
|
</app-card>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<h2>{{'FEATURES.TITLE' | translate}}</h2>
|
||||||
|
<p class="top-desc">{{'FEATURES.DESCRIPTION' | translate}}</p>
|
||||||
|
<div *ngIf="features" class="tier">
|
||||||
|
<mat-icon>stars</mat-icon>
|
||||||
|
<div class="text" *ngIf="features.tier">
|
||||||
|
<p class="title"><strong>ZITADEL {{features.tier.name}}</strong></p>
|
||||||
|
<p>{{features.tier?.description}}</p>
|
||||||
|
<p>{{'FEATURES.TIERSTATES.'+features.tier.state | translate}}</p>
|
||||||
|
<p>{{features.tier?.statusInfo}}</p>
|
||||||
|
</div>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<a class="ext" href="https://zitadel.ch/pricing" target="_blank"><i
|
||||||
|
class="las la-external-link-alt"></i></a>
|
||||||
|
|
||||||
|
<button mat-raised-button [routerLink]="['features']">{{'FEATURES.BTN-EDIT' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['policy.read']">
|
<ng-template appHasRole [appHasRole]="['policy.read']">
|
||||||
<app-policy-grid [type]="PolicyGridType.ORG"></app-policy-grid>
|
<app-policy-grid [type]="PolicyGridType.ORG"></app-policy-grid>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -2,11 +2,62 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
letter-spacing: .05em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-desc {
|
||||||
|
color: var(--grey);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.sub {
|
.sub {
|
||||||
color: var(--grey);
|
color: var(--grey);
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tier {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
color: white;
|
||||||
|
background-color: rgb(245, 203, 99);
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
|
||||||
|
.ext {
|
||||||
|
margin-right: .5rem;
|
||||||
|
align-self: center;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill-space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.domain {
|
.domain {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -10,6 +10,7 @@ import { ChangeType } from 'src/app/modules/changes/changes.component';
|
|||||||
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||||
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
|
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
|
||||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||||
|
import { Features } from 'src/app/proto/generated/zitadel/features_pb';
|
||||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||||
import { Domain, Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
import { Domain, Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
||||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||||
@ -43,13 +44,17 @@ export class OrgDetailComponent implements OnInit {
|
|||||||
= new BehaviorSubject<Member.AsObject[]>([]);
|
= new BehaviorSubject<Member.AsObject[]>([]);
|
||||||
public PolicyGridType: any = PolicyGridType;
|
public PolicyGridType: any = PolicyGridType;
|
||||||
|
|
||||||
|
public features!: Features.AsObject;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
public mgmtService: ManagementService,
|
public mgmtService: ManagementService,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
) { }
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.getData();
|
this.getData();
|
||||||
@ -65,12 +70,12 @@ export class OrgDetailComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
this.loadMembers();
|
this.loadMembers();
|
||||||
this.loadDomains();
|
this.loadDomains();
|
||||||
|
this.loadFeatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadDomains(): void {
|
public loadDomains(): void {
|
||||||
this.mgmtService.listOrgDomains().then(result => {
|
this.mgmtService.listOrgDomains().then(result => {
|
||||||
this.domains = result.resultList;
|
this.domains = result.resultList;
|
||||||
console.log(this.domains);
|
|
||||||
this.primaryDomain = this.domains.find(domain => domain.isPrimary)?.domainName ?? '';
|
this.primaryDomain = this.domains.find(domain => domain.isPrimary)?.domainName ?? '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -208,4 +213,13 @@ export class OrgDetailComponent implements OnInit {
|
|||||||
this.membersSubject.next(members);
|
this.membersSubject.next(members);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public loadFeatures(): void {
|
||||||
|
this.loadingSubject.next(true);
|
||||||
|
this.mgmtService.getFeatures().then(resp => {
|
||||||
|
if (resp.features) {
|
||||||
|
this.features = resp.features;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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 { FeatureServiceType } from 'src/app/modules/features/features.component';
|
||||||
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
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';
|
||||||
@ -39,6 +40,15 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'features',
|
||||||
|
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||||
|
canActivate: [RoleGuard],
|
||||||
|
data: {
|
||||||
|
roles: ['features.read'],
|
||||||
|
serviceType: FeatureServiceType.MGMT,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'policy',
|
path: 'policy',
|
||||||
children: [
|
children: [
|
||||||
|
@ -15,6 +15,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 { FeaturesModule } from 'src/app/modules/features/features.module';
|
||||||
import { InputModule } from 'src/app/modules/input/input.module';
|
import { InputModule } from 'src/app/modules/input/input.module';
|
||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { PolicyGridModule } from 'src/app/modules/policy-grid/policy-grid.module';
|
import { PolicyGridModule } from 'src/app/modules/policy-grid/policy-grid.module';
|
||||||
@ -57,6 +58,7 @@ import { OrgsRoutingModule } from './orgs-routing.module';
|
|||||||
ContributorsModule,
|
ContributorsModule,
|
||||||
CopyToClipboardModule,
|
CopyToClipboardModule,
|
||||||
PolicyGridModule,
|
PolicyGridModule,
|
||||||
|
FeaturesModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class OrgsModule { }
|
export class OrgsModule { }
|
||||||
|
@ -273,7 +273,6 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private async getData({ projectid }: Params): Promise<void> {
|
private async getData({ projectid }: Params): Promise<void> {
|
||||||
this.projectId = projectid;
|
this.projectId = projectid;
|
||||||
console.log(this.projectId);
|
|
||||||
this.oidcAppRequest.projectId = projectid;
|
this.oidcAppRequest.projectId = projectid;
|
||||||
this.apiAppRequest.projectId = projectid;
|
this.apiAppRequest.projectId = projectid;
|
||||||
}
|
}
|
||||||
|
@ -39,13 +39,13 @@
|
|||||||
<form *ngIf="app && editState" [formGroup]="appNameForm">
|
<form *ngIf="app && editState" [formGroup]="appNameForm">
|
||||||
<div class="name-content">
|
<div class="name-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"
|
<mat-button-toggle [value]="AppState.APP_STATE_INACTIVE"
|
||||||
matTooltip="{{ 'ACTIONS.DEACTIVATE' | translate}}">
|
matTooltip="{{ 'ACTIONS.DEACTIVATE' | translate}}">
|
||||||
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}}
|
{{'APP.PAGES.DETAIL.STATE.'+AppState.APP_STATE_INACTIVE | translate}}
|
||||||
</mat-button-toggle>
|
</mat-button-toggle>
|
||||||
<mat-button-toggle [value]="AppState.APPSTATE_ACTIVE"
|
<mat-button-toggle [value]="AppState.APPSTATE_ACTIVE"
|
||||||
matTooltip="{{ 'ACTIONS.REACTIVATE' | translate}}">
|
matTooltip="{{ 'ACTIONS.REACTIVATE' | translate}}">
|
||||||
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}}
|
{{'APP.PAGES.DETAIL.STATE.'+AppState.APP_STATE_ACTIVE | translate}}
|
||||||
</mat-button-toggle>
|
</mat-button-toggle>
|
||||||
</mat-button-toggle-group>
|
</mat-button-toggle-group>
|
||||||
|
|
||||||
|
@ -211,7 +211,6 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
|||||||
this.mgmtService.getAppByID(projectid, id).then(app => {
|
this.mgmtService.getAppByID(projectid, id).then(app => {
|
||||||
if (app.app) {
|
if (app.app) {
|
||||||
this.app = app.app;
|
this.app = app.app;
|
||||||
console.log(this.app);
|
|
||||||
this.appNameForm.patchValue(this.app);
|
this.appNameForm.patchValue(this.app);
|
||||||
|
|
||||||
if (this.app.oidcConfig) {
|
if (this.app.oidcConfig) {
|
||||||
@ -301,7 +300,6 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const snackRef = this.snackbar.open(message, action, { duration: 5000, verticalPosition: 'top' });
|
const snackRef = this.snackbar.open(message, action, { duration: 5000, verticalPosition: 'top' });
|
||||||
snackRef.onAction().subscribe(() => {
|
snackRef.onAction().subscribe(() => {
|
||||||
console.log(this.app);
|
|
||||||
if (this.app.oidcConfig) {
|
if (this.app.oidcConfig) {
|
||||||
this.saveOIDCApp();
|
this.saveOIDCApp();
|
||||||
} else if (this.app.apiConfig) {
|
} else if (this.app.apiConfig) {
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<div class="meta-details">
|
<div class="meta-details">
|
||||||
<div class="meta-row">
|
<div class="meta-row">
|
||||||
<span class="first">{{'RESOURCEID' | translate}}:</span>
|
<span class="first">{{'RESOURCEID' | translate}}:</span>
|
||||||
<span *ngIf="projectId" class="second">{{ projectId }}</span>
|
<span *ngIf="projectId" class="second">{{ project?.grantId }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta-row">
|
<div class="meta-row">
|
||||||
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
|
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
|
||||||
|
@ -91,7 +91,6 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
|
|||||||
this.loadingSubject.next(true);
|
this.loadingSubject.next(true);
|
||||||
this.mgmtService.listGrantedProjects(limit, offset).then(resp => {
|
this.mgmtService.listGrantedProjects(limit, offset).then(resp => {
|
||||||
this.grantedProjectList = resp.resultList;
|
this.grantedProjectList = resp.resultList;
|
||||||
console.log(this.grantedProjectList);
|
|
||||||
if (resp.details?.totalResult) {
|
if (resp.details?.totalResult) {
|
||||||
this.totalResult = resp.details.totalResult;
|
this.totalResult = resp.details.totalResult;
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,11 @@ export class ApplicationGridComponent implements OnInit {
|
|||||||
public loadApps(): void {
|
public loadApps(): void {
|
||||||
from(this.mgmtService.listApps(this.projectId, 100, 0)).pipe(
|
from(this.mgmtService.listApps(this.projectId, 100, 0)).pipe(
|
||||||
map(resp => {
|
map(resp => {
|
||||||
console.log(resp.resultList);
|
|
||||||
return resp.resultList;
|
return resp.resultList;
|
||||||
}),
|
}),
|
||||||
// catchError(() => of([])),
|
// catchError(() => of([])),
|
||||||
finalize(() => this.loadingSubject.next(false)),
|
finalize(() => this.loadingSubject.next(false)),
|
||||||
).subscribe((apps) => {
|
).subscribe((apps) => {
|
||||||
console.log(apps);
|
|
||||||
this.appsSubject.next(apps as App.AsObject[]);
|
this.appsSubject.next(apps as App.AsObject[]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,6 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
|||||||
private async getData(limit?: number, offset?: number): Promise<void> {
|
private async getData(limit?: number, offset?: number): Promise<void> {
|
||||||
this.loadingSubject.next(true);
|
this.loadingSubject.next(true);
|
||||||
this.mgmtService.listProjects(limit, offset).then(resp => {
|
this.mgmtService.listProjects(limit, offset).then(resp => {
|
||||||
console.log(resp.resultList);
|
|
||||||
this.ownedProjectList = resp.resultList;
|
this.ownedProjectList = resp.resultList;
|
||||||
if (resp.details?.totalResult) {
|
if (resp.details?.totalResult) {
|
||||||
this.totalResult = resp.details.totalResult;
|
this.totalResult = resp.details.totalResult;
|
||||||
|
@ -95,10 +95,6 @@ export class UserGrantCreateComponent implements OnDestroy {
|
|||||||
public addGrant(): void {
|
public addGrant(): void {
|
||||||
switch (this.context) {
|
switch (this.context) {
|
||||||
case UserGrantContext.OWNED_PROJECT:
|
case UserGrantContext.OWNED_PROJECT:
|
||||||
console.log('owned', this.userId,
|
|
||||||
this.rolesList,
|
|
||||||
this.projectId,
|
|
||||||
this.grantId);
|
|
||||||
this.userService.addUserGrant(
|
this.userService.addUserGrant(
|
||||||
this.userId,
|
this.userId,
|
||||||
this.rolesList,
|
this.rolesList,
|
||||||
@ -111,11 +107,6 @@ export class UserGrantCreateComponent implements OnDestroy {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case UserGrantContext.GRANTED_PROJECT:
|
case UserGrantContext.GRANTED_PROJECT:
|
||||||
|
|
||||||
console.log('granted', this.userId,
|
|
||||||
this.rolesList,
|
|
||||||
this.projectId,
|
|
||||||
this.grantId);
|
|
||||||
this.userService.addUserGrant(
|
this.userService.addUserGrant(
|
||||||
this.userId,
|
this.userId,
|
||||||
this.rolesList,
|
this.rolesList,
|
||||||
@ -135,11 +126,6 @@ export class UserGrantCreateComponent implements OnDestroy {
|
|||||||
grantId = (this.project as GrantedProject.AsObject).grantId;
|
grantId = (this.project as GrantedProject.AsObject).grantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(this.userId,
|
|
||||||
this.rolesList,
|
|
||||||
this.projectId,
|
|
||||||
grantId);
|
|
||||||
|
|
||||||
this.userService.addUserGrant(
|
this.userService.addUserGrant(
|
||||||
this.userId,
|
this.userId,
|
||||||
this.rolesList,
|
this.rolesList,
|
||||||
|
@ -10,24 +10,6 @@ import { Gender } from 'src/app/proto/generated/zitadel/user_pb';
|
|||||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
function noEmailValidator(c: AbstractControl): any {
|
|
||||||
const EMAIL_REGEXP: RegExp = /^((?!@).)*$/gm;
|
|
||||||
if (!c.parent || !c) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const username = c.parent.get('userName');
|
|
||||||
|
|
||||||
if (!username) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EMAIL_REGEXP.test(username.value) ? null : {
|
|
||||||
noEmailValidator: {
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-create',
|
selector: 'app-user-create',
|
||||||
templateUrl: './user-create.component.html',
|
templateUrl: './user-create.component.html',
|
||||||
@ -88,7 +70,6 @@ export class UserCreateComponent implements OnDestroy {
|
|||||||
[
|
[
|
||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.minLength(2),
|
Validators.minLength(2),
|
||||||
this.userLoginMustBeDomain ? noEmailValidator : Validators.email,
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
firstName: ['', Validators.required],
|
firstName: ['', Validators.required],
|
||||||
|
@ -56,7 +56,6 @@ export class AuthFactorDialogComponent {
|
|||||||
const credOptions: CredentialCreationOptions = JSON.parse(atob(u2fresp.key?.publicKey as string));
|
const credOptions: CredentialCreationOptions = JSON.parse(atob(u2fresp.key?.publicKey as string));
|
||||||
|
|
||||||
if (credOptions.publicKey?.challenge) {
|
if (credOptions.publicKey?.challenge) {
|
||||||
console.log(credOptions.publicKey);
|
|
||||||
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
|
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
|
||||||
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
|
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
|
||||||
if (credOptions.publicKey.excludeCredentials) {
|
if (credOptions.publicKey.excludeCredentials) {
|
||||||
@ -93,13 +92,12 @@ export class AuthFactorDialogComponent {
|
|||||||
public submitU2F(): void {
|
public submitU2F(): void {
|
||||||
if (this.u2fname && this.u2fCredentialOptions.publicKey) {
|
if (this.u2fname && this.u2fCredentialOptions.publicKey) {
|
||||||
// this.data.credOptions.publicKey.rp.id = 'localhost';
|
// this.data.credOptions.publicKey.rp.id = 'localhost';
|
||||||
navigator.credentials.create(this.data.credOptions).then((resp) => {
|
navigator.credentials.create(this.u2fCredentialOptions).then((resp) => {
|
||||||
if (resp &&
|
if (resp &&
|
||||||
(resp as any).response.attestationObject &&
|
(resp as any).response.attestationObject &&
|
||||||
(resp as any).response.clientDataJSON &&
|
(resp as any).response.clientDataJSON &&
|
||||||
(resp as any).rawId) {
|
(resp as any).rawId) {
|
||||||
|
|
||||||
console.log(resp);
|
|
||||||
const attestationObject = (resp as any).response.attestationObject;
|
const attestationObject = (resp as any).response.attestationObject;
|
||||||
const clientDataJSON = (resp as any).response.clientDataJSON;
|
const clientDataJSON = (resp as any).response.clientDataJSON;
|
||||||
const rawId = (resp as any).rawId;
|
const rawId = (resp as any).rawId;
|
||||||
|
@ -53,7 +53,6 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
|
|||||||
public addPasswordless(): void {
|
public addPasswordless(): void {
|
||||||
this.service.addMyPasswordless().then((resp) => {
|
this.service.addMyPasswordless().then((resp) => {
|
||||||
if (resp.key) {
|
if (resp.key) {
|
||||||
console.log(resp.key);
|
|
||||||
const credOptions: CredentialCreationOptions = JSON.parse(atob(resp.key.publicKey as string));
|
const credOptions: CredentialCreationOptions = JSON.parse(atob(resp.key.publicKey as string));
|
||||||
|
|
||||||
if (credOptions.publicKey?.challenge) {
|
if (credOptions.publicKey?.challenge) {
|
||||||
|
@ -44,9 +44,8 @@
|
|||||||
<mat-icon>refresh</mat-icon>
|
<mat-icon>refresh</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<app-contact *ngIf="user?.human" [human]="user.human" [state]="user.state" canWrite="true"
|
<app-contact *ngIf="user?.human" [human]="user.human" [state]="user.state" canWrite="true"
|
||||||
[userStateEnum]="UserState" (editType)="openEditDialog($event)"
|
(editType)="openEditDialog($event)" (enteredPhoneCode)="enteredPhoneCode($event)"
|
||||||
(enteredPhoneCode)="enteredPhoneCode($event)" (deletedPhone)="deletePhone()"
|
(deletedPhone)="deletePhone()" (resendEmailVerification)="resendEmailVerification()"
|
||||||
(resendEmailVerification)="resendEmailVerification()"
|
|
||||||
(resendPhoneVerification)="resendPhoneVerification()">
|
(resendPhoneVerification)="resendPhoneVerification()">
|
||||||
</app-contact>
|
</app-contact>
|
||||||
</app-card>
|
</app-card>
|
||||||
|
@ -182,7 +182,6 @@ export class AuthUserDetailComponent implements OnDestroy {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case EditDialogType.EMAIL:
|
case EditDialogType.EMAIL:
|
||||||
console.log('email');
|
|
||||||
const dialogRefEmail = this.dialog.open(EditDialogComponent, {
|
const dialogRefEmail = this.dialog.open(EditDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
confirmKey: 'ACTIONS.SAVE',
|
confirmKey: 'ACTIONS.SAVE',
|
||||||
|
@ -72,7 +72,6 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
public getMFAs(): void {
|
public getMFAs(): void {
|
||||||
this.service.listMyMultiFactors().then(mfas => {
|
this.service.listMyMultiFactors().then(mfas => {
|
||||||
const list = mfas.resultList;
|
const list = mfas.resultList;
|
||||||
console.log(list);
|
|
||||||
this.dataSource = new MatTableDataSource(list);
|
this.dataSource = new MatTableDataSource(list);
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ export class EditDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.valueControl.valueChanges.subscribe(value => {
|
this.valueControl.valueChanges.subscribe(value => {
|
||||||
console.log(value);
|
|
||||||
if (value && value.length > 1) {
|
if (value && value.length > 1) {
|
||||||
this.changeValue(value);
|
this.changeValue(value);
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
||||||
|
|
||||||
<ng-container *ngIf="human?.email">
|
<ng-container *ngIf="human?.email">
|
||||||
<a *ngIf="canWrite && state != userStateEnum?.USERSTATE_INITIAL" class="verify"
|
<a *ngIf="canWrite" class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
|
||||||
matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
|
|
||||||
(click)="emitEmailVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
|
(click)="emitEmailVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
@ -35,7 +34,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" [disabled]="!canWrite"
|
<button matTooltip="{{'ACTIONS.EDIT' | translate}}"
|
||||||
|
[disabled]="!canWrite || state == UserState.USER_STATE_INITIAL"
|
||||||
(click)="openEditDialog(EditDialogType.EMAIL)" mat-icon-button>
|
(click)="openEditDialog(EditDialogType.EMAIL)" mat-icon-button>
|
||||||
<i class="las la-edit"></i>
|
<i class="las la-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
|
@ -21,7 +21,7 @@ export class ContactComponent {
|
|||||||
@Output() resendPhoneVerification: EventEmitter<void> = new EventEmitter();
|
@Output() resendPhoneVerification: EventEmitter<void> = new EventEmitter();
|
||||||
@Output() enteredPhoneCode: EventEmitter<string> = new EventEmitter();
|
@Output() enteredPhoneCode: EventEmitter<string> = new EventEmitter();
|
||||||
@Output() deletedPhone: EventEmitter<void> = new EventEmitter();
|
@Output() deletedPhone: EventEmitter<void> = new EventEmitter();
|
||||||
@Input() public userStateEnum: any;
|
public UserState: any = UserState;
|
||||||
|
|
||||||
public EditDialogType: any = EditDialogType;
|
public EditDialogType: any = EditDialogType;
|
||||||
constructor(private dialog: MatDialog) { }
|
constructor(private dialog: MatDialog) { }
|
||||||
|
@ -27,7 +27,10 @@
|
|||||||
<ng-container matColumnDef="memberType">
|
<ng-container matColumnDef="memberType">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MEMBERSHIPS.TYPE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MEMBERSHIPS.TYPE' | translate }} </th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let member">
|
<td class="pointer" mat-cell *matCellDef="let member">
|
||||||
{{'USER.MEMBERSHIPS.TYPES.' + member.memberType | translate }} </td>
|
<span *ngIf="member.orgId && !member.projectGrantId && !member.projectId"> {{'USER.MEMBERSHIPS.TYPES.ORG' | translate }}</span>
|
||||||
|
<span *ngIf="member.projectId && !member.projectGrantId"> {{'USER.MEMBERSHIPS.TYPES.PROJECT' | translate }}</span>
|
||||||
|
<span *ngIf="member.projectId && member.projectGrantId"> {{'USER.MEMBERSHIPS.TYPES.GRANTEDPROJECT' | translate }}</span>
|
||||||
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="displayName">
|
<ng-container matColumnDef="displayName">
|
||||||
@ -39,13 +42,13 @@
|
|||||||
<ng-container matColumnDef="creationDate">
|
<ng-container matColumnDef="creationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MEMBERSHIPS.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MEMBERSHIPS.CREATIONDATE' | translate }} </th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let member">
|
<td class="pointer" mat-cell *matCellDef="let member">
|
||||||
{{member.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} </td>
|
{{member.details?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="changeDate">
|
<ng-container matColumnDef="changeDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MEMBERSHIPS.CHANGEDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MEMBERSHIPS.CHANGEDATE' | translate }} </th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let member">
|
<td class="pointer" mat-cell *matCellDef="let member">
|
||||||
{{member.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
{{member.details?.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -52,7 +52,6 @@ export class UserDetailComponent implements OnInit {
|
|||||||
this.mgmtUserService.getUserByID(id).then(resp => {
|
this.mgmtUserService.getUserByID(id).then(resp => {
|
||||||
if (resp.user) {
|
if (resp.user) {
|
||||||
this.user = resp.user;
|
this.user = resp.user;
|
||||||
console.log(this.user);
|
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -83,7 +82,6 @@ export class UserDetailComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public saveProfile(profileData: Profile.AsObject): void {
|
public saveProfile(profileData: Profile.AsObject): void {
|
||||||
console.log(profileData);
|
|
||||||
if (this.user.human) {
|
if (this.user.human) {
|
||||||
this.user.human.profile = profileData;
|
this.user.human.profile = profileData;
|
||||||
this.mgmtUserService
|
this.mgmtUserService
|
||||||
@ -159,7 +157,6 @@ export class UserDetailComponent implements OnInit {
|
|||||||
this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => {
|
this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => {
|
||||||
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
||||||
if (this.user.state == UserState.USER_STATE_INITIAL) {
|
if (this.user.state == UserState.USER_STATE_INITIAL) {
|
||||||
console.log('init');
|
|
||||||
this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => {
|
this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => {
|
||||||
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
|
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
|
||||||
this.refreshChanges$.emit();
|
this.refreshChanges$.emit();
|
||||||
|
@ -180,8 +180,6 @@ export class UserTableComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.userService.listUsers(limit, offset, [query]).then(resp => {
|
this.userService.listUsers(limit, offset, [query]).then(resp => {
|
||||||
console.log(resp);
|
|
||||||
|
|
||||||
if (resp.details?.totalResult) {
|
if (resp.details?.totalResult) {
|
||||||
this.totalResult = resp.details?.totalResult;
|
this.totalResult = resp.details?.totalResult;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { HasFeaturePipe } from './has-feature.pipe';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
HasFeaturePipe,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
HasFeaturePipe,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class HasFeaturePipeModule { }
|
14
console/src/app/pipes/has-feature-pipe/has-feature.pipe.ts
Normal file
14
console/src/app/pipes/has-feature-pipe/has-feature.pipe.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'hasFeature',
|
||||||
|
})
|
||||||
|
export class HasFeaturePipe implements PipeTransform {
|
||||||
|
constructor(private authService: GrpcAuthService) { }
|
||||||
|
|
||||||
|
public transform(values: string[]): Observable<boolean> {
|
||||||
|
return this.authService.canUseFeature(values);
|
||||||
|
}
|
||||||
|
}
|
@ -19,12 +19,16 @@ import {
|
|||||||
DeactivateIDPResponse,
|
DeactivateIDPResponse,
|
||||||
GetCustomOrgIAMPolicyRequest,
|
GetCustomOrgIAMPolicyRequest,
|
||||||
GetCustomOrgIAMPolicyResponse,
|
GetCustomOrgIAMPolicyResponse,
|
||||||
|
GetDefaultFeaturesRequest,
|
||||||
|
GetDefaultFeaturesResponse,
|
||||||
GetIDPByIDRequest,
|
GetIDPByIDRequest,
|
||||||
GetIDPByIDResponse,
|
GetIDPByIDResponse,
|
||||||
GetLabelPolicyRequest,
|
GetLabelPolicyRequest,
|
||||||
GetLabelPolicyResponse,
|
GetLabelPolicyResponse,
|
||||||
GetLoginPolicyRequest,
|
GetLoginPolicyRequest,
|
||||||
GetLoginPolicyResponse,
|
GetLoginPolicyResponse,
|
||||||
|
GetOrgFeaturesRequest,
|
||||||
|
GetOrgFeaturesResponse,
|
||||||
GetOrgIAMPolicyRequest,
|
GetOrgIAMPolicyRequest,
|
||||||
GetOrgIAMPolicyResponse,
|
GetOrgIAMPolicyResponse,
|
||||||
GetPasswordAgePolicyRequest,
|
GetPasswordAgePolicyRequest,
|
||||||
@ -66,6 +70,12 @@ import {
|
|||||||
RemoveSecondFactorFromLoginPolicyResponse,
|
RemoveSecondFactorFromLoginPolicyResponse,
|
||||||
ResetCustomOrgIAMPolicyToDefaultRequest,
|
ResetCustomOrgIAMPolicyToDefaultRequest,
|
||||||
ResetCustomOrgIAMPolicyToDefaultResponse,
|
ResetCustomOrgIAMPolicyToDefaultResponse,
|
||||||
|
ResetOrgFeaturesRequest,
|
||||||
|
ResetOrgFeaturesResponse,
|
||||||
|
SetDefaultFeaturesRequest,
|
||||||
|
SetDefaultFeaturesResponse,
|
||||||
|
SetOrgFeaturesRequest,
|
||||||
|
SetOrgFeaturesResponse,
|
||||||
SetUpOrgRequest,
|
SetUpOrgRequest,
|
||||||
SetUpOrgResponse,
|
SetUpOrgResponse,
|
||||||
UpdateCustomOrgIAMPolicyRequest,
|
UpdateCustomOrgIAMPolicyRequest,
|
||||||
@ -167,6 +177,33 @@ export class AdminService {
|
|||||||
return this.grpcService.admin.removeFailedEvent(req, null).then(resp => resp.toObject());;
|
return this.grpcService.admin.removeFailedEvent(req, null).then(resp => resp.toObject());;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Features
|
||||||
|
|
||||||
|
public getOrgFeatures(orgId: string): Promise<GetOrgFeaturesResponse.AsObject> {
|
||||||
|
const req = new GetOrgFeaturesRequest();
|
||||||
|
req.setOrgId(orgId);
|
||||||
|
return this.grpcService.admin.getOrgFeatures(req, null).then(resp => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public setOrgFeatures(req: SetOrgFeaturesRequest): Promise<SetOrgFeaturesResponse.AsObject> {
|
||||||
|
return this.grpcService.admin.setOrgFeatures(req, null).then(resp => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetOrgFeatures(orgId: string): Promise<ResetOrgFeaturesResponse.AsObject> {
|
||||||
|
const req = new ResetOrgFeaturesRequest();
|
||||||
|
req.setOrgId(orgId);
|
||||||
|
return this.grpcService.admin.resetOrgFeatures(req, null).then(resp => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDefaultFeatures(): Promise<GetDefaultFeaturesResponse.AsObject> {
|
||||||
|
const req = new GetDefaultFeaturesRequest();
|
||||||
|
return this.grpcService.admin.getDefaultFeatures(req, null).then(resp => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public setDefaultFeatures(req: SetDefaultFeaturesRequest): Promise<SetDefaultFeaturesResponse.AsObject> {
|
||||||
|
return this.grpcService.admin.setDefaultFeatures(req, null).then(resp => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
/* Policies */
|
/* Policies */
|
||||||
|
|
||||||
/* complexity */
|
/* complexity */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { OAuthService } from 'angular-oauth2-oidc';
|
import { OAuthService } from 'angular-oauth2-oidc';
|
||||||
import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs';
|
import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs';
|
||||||
import { catchError, filter, finalize, first, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators';
|
import { catchError, filter, finalize, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AddMyAuthFactorOTPRequest,
|
AddMyAuthFactorOTPRequest,
|
||||||
@ -34,6 +34,8 @@ import {
|
|||||||
ListMyUserGrantsResponse,
|
ListMyUserGrantsResponse,
|
||||||
ListMyUserSessionsRequest,
|
ListMyUserSessionsRequest,
|
||||||
ListMyUserSessionsResponse,
|
ListMyUserSessionsResponse,
|
||||||
|
ListMyZitadelFeaturesRequest,
|
||||||
|
ListMyZitadelFeaturesResponse,
|
||||||
ListMyZitadelPermissionsRequest,
|
ListMyZitadelPermissionsRequest,
|
||||||
ListMyZitadelPermissionsResponse,
|
ListMyZitadelPermissionsResponse,
|
||||||
RemoveMyAuthFactorOTPRequest,
|
RemoveMyAuthFactorOTPRequest,
|
||||||
@ -81,7 +83,10 @@ export class GrpcAuthService {
|
|||||||
private _activeOrgChanged: Subject<Org.AsObject> = new Subject();
|
private _activeOrgChanged: Subject<Org.AsObject> = new Subject();
|
||||||
public user!: Observable<User.AsObject | undefined>;
|
public user!: Observable<User.AsObject | undefined>;
|
||||||
private zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject(['user.resourceowner']);
|
private zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject(['user.resourceowner']);
|
||||||
|
private zitadelFeatures: BehaviorSubject<string[]> = new BehaviorSubject(['']);
|
||||||
|
|
||||||
public readonly fetchedZitadelPermissions: BehaviorSubject<boolean> = new BehaviorSubject(false as boolean);
|
public readonly fetchedZitadelPermissions: BehaviorSubject<boolean> = new BehaviorSubject(false as boolean);
|
||||||
|
public readonly fetchedZitadelFeatures: BehaviorSubject<boolean> = new BehaviorSubject(false as boolean);
|
||||||
|
|
||||||
private cachedOrgs: Org.AsObject[] = [];
|
private cachedOrgs: Org.AsObject[] = [];
|
||||||
|
|
||||||
@ -114,11 +119,13 @@ export class GrpcAuthService {
|
|||||||
}),
|
}),
|
||||||
finalize(() => {
|
finalize(() => {
|
||||||
this.loadPermissions();
|
this.loadPermissions();
|
||||||
|
this.loadFeatures();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.activeOrgChanged.subscribe(() => {
|
this.activeOrgChanged.subscribe(() => {
|
||||||
this.loadPermissions();
|
this.loadPermissions();
|
||||||
|
this.loadFeatures();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,12 +171,7 @@ export class GrpcAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private loadPermissions(): void {
|
private loadPermissions(): void {
|
||||||
merge([
|
from(this.listMyZitadelPermissions()).pipe(
|
||||||
// this.authenticationChanged,
|
|
||||||
this.activeOrgChanged.pipe(map(org => !!org)),
|
|
||||||
]).pipe(
|
|
||||||
first(),
|
|
||||||
switchMap(() => from(this.listMyZitadelPermissions())),
|
|
||||||
map(rolesResp => rolesResp.resultList),
|
map(rolesResp => rolesResp.resultList),
|
||||||
catchError(_ => {
|
catchError(_ => {
|
||||||
return of([]);
|
return of([]);
|
||||||
@ -182,6 +184,20 @@ export class GrpcAuthService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadFeatures(): void {
|
||||||
|
from(this.listMyZitadelFeatures()).pipe(
|
||||||
|
map(featuresResp => featuresResp.resultList),
|
||||||
|
catchError(_ => {
|
||||||
|
return of([]);
|
||||||
|
}),
|
||||||
|
finalize(() => {
|
||||||
|
this.fetchedZitadelFeatures.next(true);
|
||||||
|
}),
|
||||||
|
).subscribe(features => {
|
||||||
|
this.zitadelFeatures.next(features);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns true if user has one of the provided roles
|
* returns true if user has one of the provided roles
|
||||||
* @param roles roles of the user
|
* @param roles roles of the user
|
||||||
@ -207,6 +223,31 @@ export class GrpcAuthService {
|
|||||||
}) > -1;
|
}) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if user has one of the provided features
|
||||||
|
* @param features regex of the user
|
||||||
|
*/
|
||||||
|
public canUseFeature(features: string[] | RegExp[]): Observable<boolean> {
|
||||||
|
if (features && features.length > 0) {
|
||||||
|
return this.zitadelPermissions.pipe(switchMap(zFeatures => of(this.hasFeature(zFeatures, features))));
|
||||||
|
} else {
|
||||||
|
return of(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if user has one of the provided features
|
||||||
|
* @param userFeature features of the user
|
||||||
|
* @param requestedFeature required features for accessing the respective component
|
||||||
|
*/
|
||||||
|
public hasFeature(userFeatures: string[], requestedFeatures: string[] | RegExp[]): boolean {
|
||||||
|
return requestedFeatures.findIndex((regexp: any) => {
|
||||||
|
return userFeatures.findIndex(feature => {
|
||||||
|
return new RegExp(regexp).test(feature);
|
||||||
|
}) > -1;
|
||||||
|
}) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
public getMyProfile(): Promise<GetMyProfileResponse.AsObject> {
|
public getMyProfile(): Promise<GetMyProfileResponse.AsObject> {
|
||||||
return this.grpcService.auth.getMyProfile(new GetMyProfileRequest(), null).then(resp => resp.toObject());
|
return this.grpcService.auth.getMyProfile(new GetMyProfileRequest(), null).then(resp => resp.toObject());
|
||||||
}
|
}
|
||||||
@ -329,6 +370,12 @@ export class GrpcAuthService {
|
|||||||
).then(resp => resp.toObject());
|
).then(resp => resp.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public listMyZitadelFeatures(): Promise<ListMyZitadelFeaturesResponse.AsObject> {
|
||||||
|
return this.grpcService.auth.listMyZitadelFeatures(
|
||||||
|
new ListMyZitadelFeaturesRequest(), null
|
||||||
|
).then(resp => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
public getMyPhone(): Promise<GetMyPhoneResponse.AsObject> {
|
public getMyPhone(): Promise<GetMyPhoneResponse.AsObject> {
|
||||||
return this.grpcService.auth.getMyPhone(
|
return this.grpcService.auth.getMyPhone(
|
||||||
new GetMyPhoneRequest(), null
|
new GetMyPhoneRequest(), null
|
||||||
|
@ -75,6 +75,8 @@ import {
|
|||||||
GetAppByIDResponse,
|
GetAppByIDResponse,
|
||||||
GetDefaultPasswordComplexityPolicyRequest,
|
GetDefaultPasswordComplexityPolicyRequest,
|
||||||
GetDefaultPasswordComplexityPolicyResponse,
|
GetDefaultPasswordComplexityPolicyResponse,
|
||||||
|
GetFeaturesRequest,
|
||||||
|
GetFeaturesResponse,
|
||||||
GetGrantedProjectByIDRequest,
|
GetGrantedProjectByIDRequest,
|
||||||
GetGrantedProjectByIDResponse,
|
GetGrantedProjectByIDResponse,
|
||||||
GetHumanEmailRequest,
|
GetHumanEmailRequest,
|
||||||
@ -701,6 +703,13 @@ export class ManagementService {
|
|||||||
return this.grpcService.mgmt.listOrgMemberRoles(req, null).then(resp => resp.toObject());
|
return this.grpcService.mgmt.listOrgMemberRoles(req, null).then(resp => resp.toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Features
|
||||||
|
|
||||||
|
public getFeatures(): Promise<GetFeaturesResponse.AsObject> {
|
||||||
|
const req = new GetFeaturesRequest();
|
||||||
|
return this.grpcService.mgmt.getFeatures(req, null).then(resp => resp.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
// Policy
|
// Policy
|
||||||
|
|
||||||
public getOrgIAMPolicy(): Promise<GetOrgIAMPolicyResponse.AsObject> {
|
public getOrgIAMPolicy(): Promise<GetOrgIAMPolicyResponse.AsObject> {
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
"mgmtServiceUrl": "https://api.zitadel.io",
|
"mgmtServiceUrl": "https://api.zitadel.io",
|
||||||
"adminServiceUrl":"https://api.zitadel.io",
|
"adminServiceUrl":"https://api.zitadel.io",
|
||||||
"issuer": "https://issuer.zitadel.io",
|
"issuer": "https://issuer.zitadel.io",
|
||||||
"clientid": "100851239960569938@zitadel"
|
"clientid": "100992085175427532@zitadel"
|
||||||
}
|
}
|
||||||
|
@ -440,10 +440,10 @@
|
|||||||
"REMOVE":"Entfernen",
|
"REMOVE":"Entfernen",
|
||||||
"TYPE":"Typ",
|
"TYPE":"Typ",
|
||||||
"TYPES":{
|
"TYPES":{
|
||||||
"0":"Unbekannt",
|
"UNKNOWN":"Unbekannt",
|
||||||
"1":"Organisation",
|
"ORG":"Organisation",
|
||||||
"2":"Projekt",
|
"PROJECT":"Projekt",
|
||||||
"3":"Berechtigtes Projekt"
|
"GRANTEDPROJECT":"Berechtigtes Projekt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -568,6 +568,28 @@
|
|||||||
"SETPRIMARY":"Primäre Domain gesetzt."
|
"SETPRIMARY":"Primäre Domain gesetzt."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"FEATURES": {
|
||||||
|
"TITLE":"Features",
|
||||||
|
"DESCRIPTION":"Hier können Sie Funktionen von ZITADEL auf Basis von Ihrem Tier aktivieren sowie deaktivieren.",
|
||||||
|
"BTN-EDIT":"Featureset anpassen",
|
||||||
|
"DATA": {
|
||||||
|
"AUDITLOGRETENTION":"Audit Log retention",
|
||||||
|
"LOGINPOLICYUSERNAMELOGIN":"Login Policy - Username as login",
|
||||||
|
"LOGINPOLICYREGISTRATION":"Login Policy - Registration",
|
||||||
|
"LOGINPOLICYIDP":"Login Policy - Identity Providers",
|
||||||
|
"LOGINPOLICYFACTORS":"Login Policy - Factors",
|
||||||
|
"LOGINPOLICYPASSWORDLESS": "Login Policy - Passwordless Authentication",
|
||||||
|
"LOGINPOLICYCOMPLEXITYPOLICY":"Password Complexity Settings",
|
||||||
|
"LABELPOLICY":"Label Policy"
|
||||||
|
},
|
||||||
|
"TIERSTATES": {
|
||||||
|
"0":"Aktiv",
|
||||||
|
"1":"Aktion erforderlich",
|
||||||
|
"2":"Annuliert",
|
||||||
|
"3":"Besitzstandswahrend"
|
||||||
|
},
|
||||||
|
"NOTAVAILABLE":"Feature {{value}} ist auf Ihrer organisation nicht freigeschaltet!"
|
||||||
|
},
|
||||||
"POLICY": {
|
"POLICY": {
|
||||||
"TITLE":"Richtlinen entdecken",
|
"TITLE":"Richtlinen entdecken",
|
||||||
"DESCRIPTION":"Vorgefertigte Richtlinien, die Dir Zeit sparen und die Sicherheit erhöhen.",
|
"DESCRIPTION":"Vorgefertigte Richtlinien, die Dir Zeit sparen und die Sicherheit erhöhen.",
|
||||||
|
@ -438,10 +438,10 @@
|
|||||||
"REMOVE":"Remove",
|
"REMOVE":"Remove",
|
||||||
"TYPE":"Type",
|
"TYPE":"Type",
|
||||||
"TYPES":{
|
"TYPES":{
|
||||||
"0":"Unknown",
|
"UNKNOWN":"Unknown",
|
||||||
"1":"Organisation",
|
"ORG":"Organisation",
|
||||||
"2":"Project",
|
"PROJECT":"Project",
|
||||||
"3":"Granted Project"
|
"GRANTEDPROJECT":"Granted Project"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -566,6 +566,28 @@
|
|||||||
"SETPRIMARY":"Primary domain set."
|
"SETPRIMARY":"Primary domain set."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"FEATURES": {
|
||||||
|
"TITLE":"Features",
|
||||||
|
"DESCRIPTION":"here you can edit your ZITADEL Features based on your Tier",
|
||||||
|
"BTN-EDIT":"Edit Featureset",
|
||||||
|
"DATA": {
|
||||||
|
"AUDITLOGRETENTION":"Audit Log retention",
|
||||||
|
"LOGINPOLICYUSERNAMELOGIN":"Login Policy - Benutzername als Loginname",
|
||||||
|
"LOGINPOLICYREGISTRATION":"Login Policy - Registration",
|
||||||
|
"LOGINPOLICYIDP":"Login Policy - Identity Provider",
|
||||||
|
"LOGINPOLICYFACTORS":"Login Policy - Multifaktoren",
|
||||||
|
"LOGINPOLICYPASSWORDLESS": "Login Policy - Passwortlose Authentifizierung",
|
||||||
|
"LOGINPOLICYCOMPLEXITYPOLICY":"Password Komplexitätseinstellungen",
|
||||||
|
"LABELPOLICY":"Labelling Einstellungen"
|
||||||
|
},
|
||||||
|
"TIERSTATES": {
|
||||||
|
"0":"Active",
|
||||||
|
"1":"Action required",
|
||||||
|
"2":"Cancelled",
|
||||||
|
"3":"Grandfathered"
|
||||||
|
},
|
||||||
|
"NOTAVAILABLE":"Feature {{value}} is missing on your organization."
|
||||||
|
},
|
||||||
"POLICY": {
|
"POLICY": {
|
||||||
"TITLE":"Explore Policies",
|
"TITLE":"Explore Policies",
|
||||||
"DESCRIPTION":"Pre-packaged policies that enhance your security.",
|
"DESCRIPTION":"Pre-packaged policies that enhance your security.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user