feat(console): angular 10, iam settings, timestamp to date pipe, org iam indicator, general gui fixes and i18n (#270)

* prettier member dialog, iam indicator

* changes i18n

* fix timestamp conversion, timestamp to date pipe

* rm create, update iam policy

* add iam policy

* add iam section, members component

* add iam contributors

* gen admin protos

* iam member search

* update angular

* update cdk material

* add module for iam members

* add iam roles to member dialog

* home shortcuts

* project view, i18n

* lint
This commit is contained in:
Max Peintner 2020-06-25 12:52:57 +02:00 committed by GitHub
parent d947bb1247
commit 785c8d9763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 7635 additions and 1631 deletions

View File

@ -1,9 +1,9 @@
{ {
"extends": "../tsconfig.json", "extends": "../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"outDir": "../out-tsc/e2e", "outDir": "../out-tsc/e2e",
"module": "commonjs", "module": "commonjs",
"target": "es5", "target": "es2018",
"types": [ "types": [
"jasmine", "jasmine",
"jasminewd2", "jasminewd2",

2653
console/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,17 +14,17 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "~9.1.10", "@angular/animations": "~10.0.0",
"@angular/cdk": "~9.2.4", "@angular/cdk": "~10.0.0",
"@angular/common": "~9.1.10", "@angular/common": "~10.0.0",
"@angular/compiler": "~9.1.10", "@angular/compiler": "~10.0.0",
"@angular/core": "~9.1.10", "@angular/core": "~10.0.0",
"@angular/forms": "~9.1.10", "@angular/forms": "~10.0.0",
"@angular/material": "^9.2.4", "@angular/material": "^10.0.0",
"@angular/platform-browser": "~9.1.10", "@angular/platform-browser": "~10.0.0",
"@angular/platform-browser-dynamic": "~9.1.10", "@angular/platform-browser-dynamic": "~10.0.0",
"@angular/router": "~9.1.10", "@angular/router": "~10.0.0",
"@angular/service-worker": "~9.1.10", "@angular/service-worker": "~10.0.0",
"@ngx-translate/core": "^12.1.2", "@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "^4.0.0", "@ngx-translate/http-loader": "^4.0.0",
"@types/google-protobuf": "^3.7.2", "@types/google-protobuf": "^3.7.2",
@ -36,7 +36,6 @@
"google-protobuf": "^3.12.2", "google-protobuf": "^3.12.2",
"grpc": "^1.24.3", "grpc": "^1.24.3",
"grpc-web": "^1.1.0", "grpc-web": "^1.1.0",
"hammerjs": "^2.0.8",
"ngx-moment": "^4.0.1", "ngx-moment": "^4.0.1",
"moment": "^2.27.0", "moment": "^2.27.0",
"prettier-stylelint": "^0.4.2", "prettier-stylelint": "^0.4.2",
@ -47,25 +46,25 @@
"zone.js": "~0.10.3" "zone.js": "~0.10.3"
}, },
"devDependencies": { "devDependencies": {
"@angular/cli": "~9.1.9", "@angular/cli": "~10.0.0",
"@angular-devkit/build-angular": "~0.901.9", "@angular-devkit/build-angular": "~0.1000.0",
"@angular/compiler-cli": "~9.1.10", "@angular/compiler-cli": "~10.0.0",
"@angular/language-service": "~9.1.11", "@angular/language-service": "~10.0.0",
"@types/jasmine": "~3.5.11", "@types/jasmine": "~3.5.11",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "^14.0.13", "@types/node": "^14.0.13",
"codelyzer": "^5.2.2", "codelyzer": "^5.2.2",
"jasmine-core": "~3.5.0", "jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.2", "jasmine-spec-reporter": "~5.0.0",
"karma": "^5.1.0", "karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.3", "karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "^3.3.1", "karma-jasmine": "~3.3.0",
"karma-jasmine-html-reporter": "^1.5.4", "karma-jasmine-html-reporter": "^1.5.0",
"prettier": "^2.0.5", "prettier": "^2.0.5",
"protractor": "^7.0.0", "protractor": "~7.0.0",
"ts-node": "~8.10.2", "ts-node": "~8.10.2",
"tslint": "~6.1.2", "tslint": "~6.1.0",
"typescript": "^3.8.3" "typescript": "^3.9.5"
} }
} }

View File

@ -31,6 +31,14 @@ const routes: Routes = [
roles: ['user.read'], roles: ['user.read'],
}, },
}, },
{
path: 'iam',
loadChildren: () => import('./pages/iam/iam.module').then(m => m.IamModule),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['iam.read'],
},
},
{ {
path: 'org', path: 'org',
loadChildren: () => import('./pages/orgs/orgs.module').then(m => m.OrgsModule), loadChildren: () => import('./pages/orgs/orgs.module').then(m => m.OrgsModule),

View File

@ -35,7 +35,10 @@
</mat-menu> </mat-menu>
<span class="fill-space"></span> <span class="fill-space"></span>
<div matTooltip="IAM user" class="iamreadwrite"></div> <ng-template appHasRole [appHasRole]="['iam.write']">
<div matTooltip="IAM Administrator" class="iamreadwrite"></div>
</ng-template>
<div (clickOutside)="closeAccountCard()" class="icon-container"> <div (clickOutside)="closeAccountCard()" class="icon-container">
<div class="avatar-wrapper dontcloseonclick" (click)="showAccount = !showAccount"> <div class="avatar-wrapper dontcloseonclick" (click)="showAccount = !showAccount">
<div class="avatar-circle dontcloseonclick" [ngClass]="{'active': showAccount}"> <div class="avatar-circle dontcloseonclick" [ngClass]="{'active': showAccount}">
@ -63,6 +66,11 @@
<span class="label">{{ 'MENU.PERSONAL_INFO' | translate }}</span> <span class="label">{{ 'MENU.PERSONAL_INFO' | translate }}</span>
</a> </a>
<a *ngIf="iamreadwrite" class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/iam']">
<i class="icon las la-gem"></i>
<span class="label">{{'MENU.IAM' | translate}}</span>
</a>
<a *ngIf="showOrgSection" class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/org']"> <a *ngIf="showOrgSection" class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/org']">
<i class="icon las la-archway"></i> <i class="icon las la-archway"></i>
<span class="label">{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}</span> <span class="label">{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}</span>

View File

@ -287,3 +287,4 @@ export class AppComponent implements OnDestroy {
this.router.navigate(['/']); this.router.navigate(['/']);
} }
} }

View File

@ -6,7 +6,7 @@
<app-search-user-autocomplete (selectionChanged)="users = $event"></app-search-user-autocomplete> <app-search-user-autocomplete (selectionChanged)="users = $event"></app-search-user-autocomplete>
<mat-form-field class="full-width" appearance="outline" <mat-form-field class="full-width" appearance="outline"
*ngIf="creationType === CreationType.PROJECT_OWNED || creationType === CreationType.PROJECT_GRANTED"> *ngIf="creationType === CreationType.PROJECT_OWNED || creationType === CreationType.PROJECT_GRANTED || creationType === CreationType.IAM">
<mat-label>{{ 'PROJECT.GRANT.TITLE' | translate }}</mat-label> <mat-label>{{ 'PROJECT.GRANT.TITLE' | translate }}</mat-label>
<mat-select [(ngModel)]="roles" multiple> <mat-select [(ngModel)]="roles" multiple>
<mat-option *ngFor="let role of memberRoleOptions" [value]="role"> <mat-option *ngFor="let role of memberRoleOptions" [value]="role">

View File

@ -1,6 +1,7 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ProjectRole, User } from 'src/app/proto/generated/management_pb'; import { ProjectRole, User } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ProjectService } from 'src/app/services/project.service'; import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@ -8,6 +9,7 @@ export enum CreationType {
PROJECT_OWNED = 0, PROJECT_OWNED = 0,
PROJECT_GRANTED = 1, PROJECT_GRANTED = 1,
ORG = 2, ORG = 2,
IAM = 3,
} }
@Component({ @Component({
selector: 'app-member-create-dialog', selector: 'app-member-create-dialog',
@ -23,6 +25,7 @@ export class MemberCreateDialogComponent {
public memberRoleOptions: string[] = []; public memberRoleOptions: string[] = [];
constructor( constructor(
private projectService: ProjectService, private projectService: ProjectService,
private adminService: AdminService,
public dialogRef: MatDialogRef<MemberCreateDialogComponent>, public dialogRef: MatDialogRef<MemberCreateDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, @Inject(MAT_DIALOG_DATA) public data: any,
toastService: ToastService, toastService: ToastService,
@ -43,6 +46,13 @@ export class MemberCreateDialogComponent {
}).catch(error => { }).catch(error => {
toastService.showError(error.message); toastService.showError(error.message);
}); });
} else if (this.creationType === CreationType.IAM) {
this.adminService.GetIamMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList;
console.log(this.memberRoleOptions);
}).catch(error => {
toastService.showError(error.message);
});
} }
} }

View File

@ -3,7 +3,7 @@
<div class="scroll-container" appScrollable (scrollPosition)="scrollHandler($event)"> <div class="scroll-container" appScrollable (scrollPosition)="scrollHandler($event)">
<li class="item change-item-back" *ngFor="let event of data | async"> <li class="item change-item-back" *ngFor="let event of data | async">
<span class="seq"> <span class="seq">
{{dateFromTimestamp(event.changeDate) | localizedDate: 'EEE dd. MMM, HH:mm'}} {{event.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
</span> </span>
<span class="editor">{{event.editor}}</span> <span class="editor">{{event.editor}}</span>
<span class="desc">{{'CHANGES.EVENTS.'+event.eventType | translate}}</span> <span class="desc">{{'CHANGES.EVENTS.'+event.eventType | translate}}</span>

View File

@ -1,5 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, scan, take, tap } from 'rxjs/operators'; import { catchError, scan, take, tap } from 'rxjs/operators';
import { Change, Changes } from 'src/app/proto/generated/management_pb'; import { Change, Changes } from 'src/app/proto/generated/management_pb';
@ -19,6 +18,7 @@ export enum ChangeType {
export class ChangesComponent implements OnInit { export class ChangesComponent implements OnInit {
@Input() public changeType: ChangeType = ChangeType.USER; @Input() public changeType: ChangeType = ChangeType.USER;
@Input() public id: string = ''; @Input() public id: string = '';
@Input() public sortDirectionAsc: boolean = true;
public errorMessage: string = ''; public errorMessage: string = '';
// Source data // Source data
@ -74,7 +74,6 @@ export class ChangesComponent implements OnInit {
break; break;
case ChangeType.ORG: more = this.mgmtUserService.OrgChanges(this.id, 10, cursor); case ChangeType.ORG: more = this.mgmtUserService.OrgChanges(this.id, 10, cursor);
break; break;
} }
this.mapAndUpdate(more); this.mapAndUpdate(more);
@ -84,8 +83,8 @@ export class ChangesComponent implements OnInit {
private getCursor(): number { private getCursor(): number {
const current = this._data.value; const current = this._data.value;
if (current.length) { if (current.length) {
// return true ? current[0].sequence : return !this.sortDirectionAsc ? current[0].sequence :
return current[current.length - 1].sequence; current[current.length - 1].sequence;
} }
return 0; return 0;
} }
@ -126,9 +125,4 @@ export class ChangesComponent implements OnInit {
take(1), take(1),
).subscribe(); ).subscribe();
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
} }

View File

@ -11,9 +11,10 @@
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['project.role.delete']"> <ng-template appHasRole [appHasRole]="['project.role.delete']">
<button [disabled]="disabled" matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" class="icon-button" <button color="warn" [disabled]="disabled" matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}"
(click)="deleteSelectedRoles()" mat-icon-button *ngIf="selection.hasValue() && actionsVisible"> class="icon-button" (click)="deleteSelectedRoles()" mat-icon-button
<mat-icon>delete_outline</mat-icon> *ngIf="selection.hasValue() && actionsVisible">
<i class="las la-trash"></i>
</button> </button>
</ng-template> </ng-template>
<ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']"> <ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']">
@ -66,7 +67,7 @@
<ng-container matColumnDef="creationDate"> <ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} </th>
<td mat-cell *matCellDef="let role"> <td mat-cell *matCellDef="let role">
<span>{{dateFromTimestamp(role.creationDate) | date: 'dd. MMM, HH:mm' }}</span> <span>{{role.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }}</span>
</td> </td>
</ng-container> </ng-container>

View File

@ -2,7 +2,6 @@ import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { ProjectRole } from 'src/app/proto/generated/management_pb'; import { ProjectRole } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service'; import { ProjectService } from 'src/app/services/project.service';
@ -108,9 +107,4 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
this.toast.showError(data.message); this.toast.showError(data.message);
}); });
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
} }

View File

@ -11,6 +11,7 @@ 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 { PipesModule } from 'src/app/pipes/pipes.module';
import { ProjectRolesComponent } from './project-roles.component'; import { ProjectRolesComponent } from './project-roles.component';
@ -27,6 +28,7 @@ import { ProjectRolesComponent } from './project-roles.component';
MatCheckboxModule, MatCheckboxModule,
RouterModule, RouterModule,
MatTooltipModule, MatTooltipModule,
PipesModule,
TranslateModule, TranslateModule,
MatMenuModule, MatMenuModule,
], ],

View File

@ -39,14 +39,20 @@
<input matInput type="text" [formControl]="globalEmailControl" /> <input matInput type="text" [formControl]="globalEmailControl" />
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="getGlobalUser()"> <button color="primary" mat-icon-button (click)="getGlobalUser()">
<mat-icon>search</mat-icon> <i class="las la-search"></i>
</button> </button>
</div> </div>
<p *ngIf="target == UserTarget.EXTERNAL && users.length > 0">{{'USER.SEARCH.FOUND' | translate}}: <span <div *ngIf="target == UserTarget.EXTERNAL && users.length > 0">
*ngFor="let user of users; index as i">{{user.email}} <mat-icon class="sm-dlt" (click)="users.splice(i, 1)"> <span class="found-label">{{'USER.SEARCH.FOUND' | translate}}:</span>
remove_circle</mat-icon></span></p> <div class="found-user-row" *ngFor="let user of users; index as i">
<span>{{user.email}}</span>
<button mat-icon-button>
<i class="las la-minus-circle" (click)="users.splice(i, 1)"></i>
</button>
</div>
</div>
<p class="target-desc">{{(target == UserTarget.SELF ? 'USER.TARGET.SELF' : 'USER.TARGET.EXTERNAL') | translate}} <p class="target-desc">{{(target == UserTarget.SELF ? 'USER.TARGET.SELF' : 'USER.TARGET.EXTERNAL') | translate}}
<a (click)="changeTarget()">{{'USER.TARGET.CLICKHERE' | translate}}</a> <a (click)="changeTarget()">{{'USER.TARGET.CLICKHERE' | translate}}</a>

View File

@ -33,4 +33,14 @@
.sm-dlt { .sm-dlt {
cursor: pointer; cursor: pointer;
font-size: .8rem; font-size: .8rem;
}
.found-user-row {
display: flex;
align-items: center;
}
.found-label {
font-size: .9rem;
color: #81868a;
} }

View File

@ -16,11 +16,11 @@
<div class="content"> <div class="content">
<mat-button-toggle-group formControlName="state" class="toggle" (change)="changeState($event)"> <mat-button-toggle-group formControlName="state" class="toggle" (change)="changeState($event)">
<mat-button-toggle [value]="AppState.APPSTATE_INACTIVE" matTooltip="Deactivate Org"> <mat-button-toggle [value]="AppState.APPSTATE_INACTIVE" matTooltip="Deactivate Org">
<i class="las la-toggle-off"></i> <!-- <i class="las la-toggle-off"></i> -->
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}} {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}}
</mat-button-toggle> </mat-button-toggle>
<mat-button-toggle [value]="AppState.APPSTATE_ACTIVE" matTooltip="Activate Org"> <mat-button-toggle [value]="AppState.APPSTATE_ACTIVE" matTooltip="Activate Org">
<i class="las la-toggle-on"></i> <!-- <i class="las la-toggle-on"></i> -->
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}} {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}}
</mat-button-toggle> </mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>

View File

@ -8,9 +8,25 @@
</div> </div>
<div class="container"> <div class="container">
<ng-template appHasRole [appHasRole]="['iam.write']">
<div class="item">
<div class="top">
<h2>
<i class="icon las la-gem"></i>
{{'HOME.IAM'| translate}}</h2>
<p>{{'HOME.IAM_DESC'| translate}}</p>
</div>
<span class="fill-space"></span>
<div class="footer">
<a color="accent" mat-button [routerLink]="['/iam']">{{'HOME.IAM_BUTTON' | translate}}</a>
</div>
</div>
</ng-template>
<div class="item"> <div class="item">
<div class="top"> <div class="top">
<h2>{{'HOME.SECURITYANDPRIVACY'| translate}}</h2> <h2> <i class="icon las la-user-circle"></i>
{{'HOME.SECURITYANDPRIVACY'| translate}}</h2>
<p>{{'HOME.SECURITYANDPRIVACY_DESC'| translate}}</p> <p>{{'HOME.SECURITYANDPRIVACY_DESC'| translate}}</p>
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
@ -19,35 +35,50 @@
[routerLink]="['/users/me']">{{'HOME.SECURITYANDPRIVACY_BUTTON' | translate}}</a> [routerLink]="['/users/me']">{{'HOME.SECURITYANDPRIVACY_BUTTON' | translate}}</a>
</div> </div>
</div> </div>
<div class="item">
<div class="top"> <ng-template appHasRole [appHasRole]="['project.read']">
<h2>{{'HOME.PROJECTS'| translate}}</h2> <div class="item">
<p>{{'HOME.PROJECTS_DESC'| translate}}</p> <div class="top">
<h2>
<i class="icon las la-layer-group"></i>
{{'HOME.PROJECTS'| translate}}</h2>
<p>{{'HOME.PROJECTS_DESC'| translate}}</p>
</div>
<span class="fill-space"></span>
<div class="footer">
<a color="accent" mat-button [routerLink]="['/projects']">{{'HOME.PROJECTS_BUTTON' | translate}}</a>
</div>
</div> </div>
<span class="fill-space"></span> </ng-template>
<div class="footer">
<a color="accent" mat-button [routerLink]="['/users/me']">{{'HOME.PROJECTS_BUTTON' | translate}}</a> <ng-template appHasRole [appHasRole]="['org.read']">
<div class="item">
<div class="top">
<h2> <i class="icon las la-archway"></i>
{{'HOME.PROTECTION'| translate}}</h2>
<p>{{'HOME.PROTECTION_DESC'| translate}}</p>
</div>
<span class="fill-space"></span>
<div class="footer">
<a color="accent" mat-button
[routerLink]="['/users/me']">{{'HOME.PROTECTION_BUTTON' | translate}}</a>
</div>
</div> </div>
</div> </ng-template>
<div class="item">
<div class="top"> <ng-template appHasRole [appHasRole]="['user.read']">
<h2>{{'HOME.PROTECTION'| translate}}</h2> <div class="item">
<p>{{'HOME.PROTECTION_DESC'| translate}}</p> <div class="top">
<h2>
<i class="las la-crosshairs"></i>
{{'HOME.USERS'| translate}}</h2>
<p>{{'HOME.USERS_DESC'| translate}}</p>
</div>
<span class="fill-space"></span>
<div class="footer">
<a color="accent" mat-button [routerLink]="['/users/me']">{{'HOME.USERS_BUTTON' | translate}}</a>
</div>
</div> </div>
<span class="fill-space"></span> </ng-template>
<div class="footer">
<a color="accent" mat-button [routerLink]="['/users/me']">{{'HOME.PROTECTION_BUTTON' | translate}}</a>
</div>
</div>
<div class="item">
<div class="top">
<h2>{{'HOME.USERS'| translate}}</h2>
<p>{{'HOME.USERS_DESC'| translate}}</p>
</div>
<span class="fill-space"></span>
<div class="footer">
<a color="accent" mat-button [routerLink]="['/users/me']">{{'HOME.USERS_BUTTON' | translate}}</a>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -66,26 +66,42 @@
.item { .item {
flex: 1 1 45%; flex: 1 1 45%;
box-sizing: border-box; // box-sizing: border-box;
margin: 1rem; margin: 1rem;
border: 1px solid #ffffff20; border: 1px solid #ffffff20;
border-radius: .5rem; border-radius: .5rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
transition: border-color .1s;
&:hover {
i {
color: #fe11e280;
}
// border-width: 2px;
border-color: #fe11e270;
}
.top { .top {
padding: 1rem 2rem; padding: 1rem 2rem;
h2 { h2 {
display: block;
margin-top: .5rem; margin-top: .5rem;
font-family: 'Rubik'; font-family: 'Rubik';
display: flex;
align-items: center;
} }
p { p {
display: block; display: block;
color: #81868a; color: #81868a;
font-size: .9rem; font-size: .9rem;
} }
i{
font-size: 2.5rem;
margin-right: 1rem;
transition: color .1s;
}
} }
.fill-space { .fill-space {
@ -93,22 +109,11 @@
} }
.footer { .footer {
// position: absolute;
// bottom: 0;
// left: 0;
// right: 0;
height: 60px; height: 60px;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 1rem; padding: 0 1rem;
border-top: 1px solid #ffffff20; border-top: 1px solid #ffffff20;
a {
// text-decoration: none;
// color: #e8eaed;
// font-size: .8rem;
// font-weight: 600;
}
} }
} }
} }

View File

@ -5,6 +5,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpLoaderFactory } from 'src/app/app.module'; import { HttpLoaderFactory } from 'src/app/app.module';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { HomeRoutingModule } from './home-routing.module'; import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component'; import { HomeComponent } from './home.component';
@ -12,19 +13,20 @@ import { HomeComponent } from './home.component';
@NgModule({ @NgModule({
declarations: [HomeComponent], declarations: [HomeComponent],
imports: [ imports: [
CommonModule, CommonModule,
MatIconModule, MatIconModule,
HomeRoutingModule, HasRoleModule,
MatButtonModule, HomeRoutingModule,
TranslateModule.forChild({ MatButtonModule,
loader: { TranslateModule.forChild({
provide: TranslateLoader, loader: {
useFactory: HttpLoaderFactory, provide: TranslateLoader,
deps: [HttpClient], useFactory: HttpLoaderFactory,
}, deps: [HttpClient],
}), },
], }),
],
}) })
export class HomeModule { } export class HomeModule { }

View File

@ -0,0 +1,24 @@
<div class="groups">
<span class="header">{{ 'IAM.MEMBER.TITLE' | translate }}</span>
<span class="sub-header">{{ 'IAM.MEMBER.DESCRIPTION' | translate }}</span>
<div class="people">
<div class="img-list">
<ng-container *ngIf="totalResult < 10; else compact">
<ng-container *ngFor="let member of membersSubject | async">
<div (click)="showDetail()" class="avatar-circle"
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
<i class="avatar las la-user-circle"></i>
</div>
</ng-container>
</ng-container>
<ng-template #compact>
<div (click)="showDetail()" class="avatar-circle" matTooltip="Click to show detail">
<span>{{totalResult}}</span>
</div>
</ng-template>
<button class="add-img" (click)="openAddMember()" mat-icon-button aria-label="Edit contributors">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
</div>

View File

@ -0,0 +1,72 @@
.groups {
padding-top: 1rem;
.header {
display: block;
margin-bottom: 1rem;
font-weight: 400;
}
.sub-header {
font-size: .8rem;
color: #81868a;
}
.people {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-bottom: 1rem;
.owner {
margin-right: 1rem;
}
.img-list {
width: 100%;
margin-top: 0.5rem;
margin-left: 1rem;
display: flex;
align-items: center;
.avatar-img, .avatar-circle {
float: left;
margin: 0 8px 0 -15px;
height: 32px;
width: 32px;
border-radius: 50%;
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.5), 0 3px 6px rgba(0, 0, 0, 0.5);
}
.add-img {
float: left;
margin: 0 8px 0 -15px;
}
.avatar-img {
&:before {
content: '';
display: block;
position: absolute;
top: 0;
bottom: 0;
height: 32px;
width: 32px;
}
}
.avatar-circle {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
background-color: indianred;
}
.margin-neg {
margin-left: -1rem;
}
}
}
}

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { IamContributorsComponent } from './iam-contributors.component';
describe('OrgContributorsComponent', () => {
let component: IamContributorsComponent;
let fixture: ComponentFixture<IamContributorsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [IamContributorsComponent],
imports: [
NoopAnimationsModule,
MatPaginatorModule,
MatSortModule,
MatTableModule,
],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IamContributorsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should compile', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,90 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { Router } from '@angular/router';
import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { OrgMember, OrgMemberView, OrgState, User } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import {
CreationType,
MemberCreateDialogComponent,
} from '../../../modules/add-member-dialog/member-create-dialog.component';
@Component({
selector: 'app-iam-contributors',
templateUrl: './iam-contributors.component.html',
styleUrls: ['./iam-contributors.component.scss'],
})
export class IamContributorsComponent implements OnInit {
@Input() public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<OrgMember.AsObject>;
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public totalResult: number = 0;
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
public OrgState: any = OrgState;
constructor(private adminService: AdminService, private dialog: MatDialog,
private toast: ToastService,
private router: Router) { }
public ngOnInit(): void {
this.loadMembers(0, 25, 'asc');
}
public loadMembers(pageIndex: number, pageSize: number, sortDirection?: string): void {
const offset = pageIndex * pageSize;
this.loadingSubject.next(true);
from(this.adminService.SearchIamMembers(pageSize, offset)).pipe(
map(resp => {
this.totalResult = resp.toObject().totalResult;
return resp.toObject().resultList;
}),
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
).subscribe(members => {
console.log(members);
this.membersSubject.next(members);
});
}
public openAddMember(): void {
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
data: {
creationType: CreationType.IAM,
},
width: '400px',
});
dialogRef.afterClosed().subscribe(resp => {
if (resp) {
const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) {
Promise.all(users.map(user => {
return this.adminService.AddIamMember(user.id, roles);
})).then(() => {
this.toast.showError('members added');
}).catch(error => {
this.toast.showError(error.message);
});
}
}
});
}
public showDetail(): void {
this.router.navigate(['iam/members']);
}
}

View 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 { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { MemberCreateDialogModule } from '../../../modules/add-member-dialog/member-create-dialog.module';
import { IamContributorsComponent } from './iam-contributors.component';
@NgModule({
declarations: [IamContributorsComponent],
imports: [
CommonModule,
FormsModule,
MemberCreateDialogModule,
HasRoleModule,
MatButtonModule,
MatDialogModule,
MatTableModule,
MatPaginatorModule,
MatIconModule,
RouterModule,
MatProgressSpinnerModule,
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
],
exports: [
IamContributorsComponent,
],
})
export class IamContributorsModule { }

View File

@ -0,0 +1,64 @@
import { DataSource } from '@angular/cdk/collections';
import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { IamMemberSearchResponse } from 'src/app/proto/generated/admin_pb';
import { ProjectMember } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service';
/**
* Data source for the ProjectMembers view. This class should
* encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering).
*/
export class IamMembersDataSource extends DataSource<ProjectMember.AsObject> {
public totalResult: number = 0;
public membersSubject: BehaviorSubject<ProjectMember.AsObject[]> = new BehaviorSubject<ProjectMember.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
constructor(private adminService: AdminService) {
super();
}
public loadMembers(
pageIndex: number, pageSize: number, grantId?: string, sortDirection?: string): void {
const offset = pageIndex * pageSize;
this.loadingSubject.next(true);
// TODO
const promise: Promise<IamMemberSearchResponse> =
this.adminService.SearchIamMembers(pageSize, offset);
if (promise) {
from(promise).pipe(
map(resp => {
this.totalResult = resp.toObject().totalResult;
console.log(this.totalResult);
return resp.toObject().resultList;
}),
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
).subscribe(members => {
this.membersSubject.next(members);
});
}
}
/**
* Connect this data source to the table. The table will only update when
* the returned stream emits new items.
* @returns A stream of the items to be rendered.
*/
public connect(): Observable<ProjectMember.AsObject[]> {
return this.membersSubject.asObservable();
}
/**
* Called when the table is being destroyed. Use this function, to clean up
* any open connections or free any held resources that were set up during connect.
*/
public disconnect(): void {
this.membersSubject.complete();
this.loadingSubject.complete();
}
}

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { IamMembersComponent } from './iam-members.component';
const routes: Routes = [
{
path: '',
component: IamMembersComponent,
data: { animation: 'AddPage' },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class IamMembersRoutingModule { }

View File

@ -0,0 +1,95 @@
<div class="max-width-container">
<div class="container">
<div class="head">
<h1>{{ 'IAM.MEMBER.TITLE' | translate }}</h1>
<p class="desc">{{ 'IAM.MEMBER.DESCRIPTION' | translate }}</p>
</div>
<div class="table-header-row" *ngIf="org">
<div class="col">
<ng-container *ngIf="!selection.hasValue()">
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
<span class="count">{{dataSource?.membersSubject.value.length}}</span>
</ng-container>
<ng-container *ngIf="selection.hasValue()">
<span class="desc">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
<span class="count">{{selection?.selected?.length}}</span>
</ng-container>
</div>
<span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['org.member.delete:'+org.id,'org.member.delete']">
<button (click)="removeProjectMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()">
<i class="las la-trash"></i>
</button>
</ng-template>
<ng-template appHasRole [appHasRole]="['org.member.write:'+org.id,'org.member.write']">
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary"
mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a>
</ng-template>
</div>
<div class="table-wrapper">
<div class="spinner-container" *ngIf="dataSource?.loading$ | async">
<mat-spinner diameter="50"></mat-spinner>
</div>
<table mat-table class="background-style full-width-table" aria-label="Elements" [dataSource]="dataSource">
<ng-container matColumnDef="select">
<th class="selection" mat-header-cell *matHeaderCellDef>
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</th>
<td class="selection" mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="firstname">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.MEMBER.FIRSTNAME' | translate }} </th>
<td class="pointer" [routerLink]="['/user', member.userId]" mat-cell *matCellDef="let member">
{{member.firstName}} </td>
</ng-container>
<ng-container matColumnDef="lastname">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.MEMBER.LASTNAME' | translate }} </th>
<td class="pointer" [routerLink]="['/user', member.userId]" mat-cell *matCellDef="let member">
{{member.lastName}} </td>
</ng-container>
<ng-container matColumnDef="username">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.MEMBER.USERNAME' | translate }} </th>
<td class="pointer" [routerLink]="['/user', member.userId]" mat-cell *matCellDef="let member">
{{member.userName}} </td>
</ng-container>
<ng-container matColumnDef="email">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.MEMBER.EMAIL' | translate }} </th>
<td class="pointer" [routerLink]="['/user', member.userId]" mat-cell *matCellDef="let member">
{{member.email}}
</td>
</ng-container>
<ng-container matColumnDef="roles">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.MEMBER.ROLES' | translate }} </th>
<td class="pointer" [routerLink]="['/user', member.userId]" mat-cell *matCellDef="let member">
<span class="role app-label" *ngFor="let role of member.rolesList; index as i">
{{ 'ROLES.'+role | translate }}</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="data-row" mat-row *matRowDef="let row; columns: displayedColumns;">
</tr>
</table>
<mat-paginator class="background-style" #paginator [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
</mat-paginator>
</div>
</div>
</div>

View File

@ -0,0 +1,106 @@
.container {
padding-bottom: 3rem;
.head {
display: flex;
align-items: center;
border-bottom: 1px solid #ffffff20;
margin-bottom: 2rem;
flex-wrap: wrap;
a {
display: block;
}
h1 {
font-size: 1.2rem;
}
.desc {
width: 100%;
display: block;
font-size: .9rem;
color: #81868a;
}
}
}
.table-header-row {
display: flex;
align-items: center;
.col {
display: flex;
flex-direction: column;
.desc {
font-size: .8rem;
color: #81868a;
}
.count {
font-size: 2rem;
}
}
.fill-space {
flex: 1;
}
.icon-button {
margin-right: .5rem;
}
.add-button {
border-radius: .5rem;
}
}
.table-wrapper {
overflow: auto;
.spinner-container {
display: flex;
align-items: center;
justify-content: center;
}
table, mat-paginator {
width: 100%;
td, th {
padding: .5rem;
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:last-child {
padding-right: 0;
}
}
.action {
width: 40px;
}
.data-row {
&:hover {
background-color: #ffffff05;
}
}
.selection {
width: 50px;
max-width: 50px;
}
.role {
display: inline-block;
margin: .25rem;
}
}
}
.pointer {
outline: none;
cursor: pointer;
}

View File

@ -0,0 +1,34 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ProjectMembersComponent } from './project-members.component';
describe('ProjectMembersComponent', () => {
let component: ProjectMembersComponent;
let fixture: ComponentFixture<ProjectMembersComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ProjectMembersComponent],
imports: [
NoopAnimationsModule,
MatPaginatorModule,
MatSortModule,
MatTableModule,
],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProjectMembersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should compile', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,109 @@
import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { tap } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { Org, ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
import { IamMembersDataSource } from './iam-members-datasource';
@Component({
selector: 'app-iam-members',
templateUrl: './iam-members.component.html',
styleUrls: ['./iam-members.component.scss'],
})
export class IamMembersComponent implements AfterViewInit {
public org!: Org.AsObject;
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<ProjectMember.AsObject>;
public dataSource!: IamMembersDataSource;
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
constructor(private adminService: AdminService,
private dialog: MatDialog,
private toast: ToastService) {
this.dataSource = new IamMembersDataSource(this.adminService);
this.dataSource.loadMembers(0, 25, 'asc');
}
public ngAfterViewInit(): void {
this.paginator.page
.pipe(
tap(() => this.loadMembersPage()),
)
.subscribe();
}
private loadMembersPage(): void {
this.dataSource.loadMembers(
this.paginator.pageIndex,
this.paginator.pageSize,
);
}
public removeProjectMemberSelection(): void {
Promise.all(this.selection.selected.map(member => {
return this.adminService.RemoveIamMember(member.userId).then(() => {
this.toast.showInfo('Removed successfully');
}).catch(error => {
this.toast.showError(error.message);
});
}));
}
public removeMember(member: ProjectMember.AsObject): void {
this.adminService.RemoveIamMember(member.userId).then(() => {
this.toast.showInfo('Member removed successfully');
}).catch(error => {
this.toast.showError(error.message);
});
}
public isAllSelected(): boolean {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.membersSubject.value.length;
return numSelected === numRows;
}
public masterToggle(): void {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.membersSubject.value.forEach(row => this.selection.select(row));
}
public openAddMember(): void {
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
data: {
creationType: CreationType.ORG,
},
width: '400px',
});
dialogRef.afterClosed().subscribe(resp => {
if (resp) {
const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) {
Promise.all(users.map(user => {
return this.adminService.AddIamMember(user.id, roles);
})).then(() => {
this.toast.showError('members added');
}).catch(error => {
this.toast.showError(error.message);
});
}
}
});
}
}

View File

@ -0,0 +1,40 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { IamMembersRoutingModule } from './iam-members-routing.module';
import { IamMembersComponent } from './iam-members.component';
@NgModule({
declarations: [IamMembersComponent],
imports: [
IamMembersRoutingModule,
CommonModule,
MatAutocompleteModule,
MatChipsModule,
MatButtonModule,
MatCheckboxModule,
MatIconModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatTooltipModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
FormsModule,
TranslateModule,
],
})
export class IamMembersModule { }

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { IamComponent } from './iam.component';
const routes: Routes = [
{
path: '',
component: IamComponent,
},
{
path: 'members',
loadChildren: () => import('./iam-members/iam-members.module').then(m => m.IamMembersModule),
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class IamRoutingModule { }

View File

@ -0,0 +1,25 @@
<app-meta-layout>
<div class="enlarged-container">
<h1>{{'IAM.DETAIL.TITLE' | translate}}</h1>
<p class="sub">{{'IAM.DETAIL.DESCRIPTION' | translate}}
</p>
<app-card title="{{ 'ORG.DOMAINS.TITLE' | translate }}"
description="{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}">
</app-card>
</div>
<metainfo class="side">
<!-- <div class="details">
</div> -->
<!-- <mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details"> -->
<app-iam-contributors>
</app-iam-contributors>
<!-- </mat-tab>
<mat-tab label="{{ 'CHANGES.ORG.TITLE' | translate }}" class="flex-col">
</mat-tab>
</mat-tab-group> -->
</metainfo>
</app-meta-layout>

View File

@ -0,0 +1,151 @@
h1 {
font-family: ailerons;
margin-top: 0;
}
.sub {
color: #81868a;
margin-bottom: 2rem;
}
.state-label {
font-size: .9rem;
color: #81868a;
margin-bottom: .5rem;
}
.content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
mat-form-field {
flex: 1 1 33%;
margin: 0 .5rem;
}
}
.table-header-row {
display: flex;
align-items: center;
.col {
display: flex;
flex-direction: column;
.desc {
font-size: .8rem;
color: #81868a;
}
.count {
font-size: 2rem;
}
}
.fill-space {
flex: 1;
}
.icon-button {
margin-right: .5rem;
}
.add-button {
border-radius: .5rem;
}
}
.domain {
display: flex;
align-items: center;
padding: .5rem 0;
flex-wrap: wrap;
.title {
font-size: 16px;
margin-right: 1rem;
}
.verified, .primary{
color: #5282c1;
margin-right: 1rem;
}
.fill-space {
flex: 1;
}
}
.new-desc {
font-size: 14px;
color: #818a8a;
}
.new-row {
display: flex;
flex-wrap: wrap;
align-items: center;
mat-form-field {
flex: 1;
}
}
.side {
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
.row {
display: flex;
margin-bottom: 0.5rem;
align-items: center;
button {
display: none;
visibility: hidden;
}
&:hover {
button {
display: inline-block;
visibility: visible;
mat-icon {
font-size: 1.2rem;
}
}
}
.first {
flex: 1;
font-size: 0.8rem;
margin-right: 0.5rem;
}
.fill-space {
flex: 1;
}
.second {
font-size: 0.8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
a {
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
}
.side-section {
color: #81868a;
}
}
}

View File

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

View File

@ -0,0 +1,17 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-iam',
templateUrl: './iam.component.html',
styleUrls: ['./iam.component.scss'],
})
export class IamComponent implements OnInit {
constructor() {
}
ngOnInit(): void {
}
}

View File

@ -0,0 +1,56 @@
import { CommonModule } from '@angular/common';
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { ChangesModule } from 'src/app/modules/changes/changes.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { IamContributorsModule } from './iam-contributors/iam-contributors.module';
import { IamRoutingModule } from './iam-routing.module';
import { IamComponent } from './iam.component';
@NgModule({
declarations: [IamComponent],
imports: [
CommonModule,
IamRoutingModule,
ChangesModule,
MatAutocompleteModule,
MatChipsModule,
MatButtonModule,
HasRoleModule,
MatCheckboxModule,
MetaLayoutModule,
MatIconModule,
MatTabsModule,
MatTableModule,
MatPaginatorModule,
MatFormFieldModule,
MatSortModule,
MatTooltipModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
FormsModule,
TranslateModule,
MatDialogModule,
IamContributorsModule,
],
schemas: [NO_ERRORS_SCHEMA],
})
export class IamModule { }

View File

@ -10,9 +10,9 @@
<i matTooltip="verified" *ngIf="domain.verified" class="verified las la-check-circle"></i> <i matTooltip="verified" *ngIf="domain.verified" class="verified las la-check-circle"></i>
<i matTooltip="primary" *ngIf="domain.primary" class="primary las la-chess-queen"></i> <i matTooltip="primary" *ngIf="domain.primary" class="primary las la-chess-queen"></i>
<span class="fill-space"></span> <span class="fill-space"></span>
<button disabled mat-icon-button <!-- <button disabled mat-icon-button
matTooltip="download /.well-known/caos-developer-domain-association.txt and deploy it on your domain. Then verify"><i matTooltip="download /.well-known/caos-developer-domain-association.txt and deploy it on your domain. Then verify"><i
class="las la-file-download"></i></button> class="las la-file-download"></i></button> -->
<button matTooltip="Remove domain" color="warn" mat-icon-button (click)="removeDomain(domain.domain)"><i <button matTooltip="Remove domain" color="warn" mat-icon-button (click)="removeDomain(domain.domain)"><i
class="las la-trash"></i></button> class="las la-trash"></i></button>
</div> </div>
@ -28,10 +28,10 @@
<mat-icon>check</mat-icon> <mat-icon>check</mat-icon>
</button> </button>
<button disabled mat-icon-button <!-- <button disabled mat-icon-button
matTooltip="download /.well-known/caos-developer-domain-association.txt and deploy it on your domain. Then verify"><i matTooltip="download /.well-known/caos-developer-domain-association.txt and deploy it on your domain. Then verify"><i
class="las la-file-download"></i></button> class="las la-file-download"></i></button> -->
<button disabled mat-icon-button matTooltip="Verify"><i class=" las la-check-circle"></i></button> <!-- <button disabled mat-icon-button matTooltip="Verify"><i class=" las la-check-circle"></i></button> -->
</div> </div>
</app-card> </app-card>

View File

@ -76,6 +76,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
public saveNewOrgDomain(): void { public saveNewOrgDomain(): void {
this.orgService.AddMyOrgDomain(this.newDomain).then(domain => { this.orgService.AddMyOrgDomain(this.newDomain).then(domain => {
this.domains.push(domain.toObject()); this.domains.push(domain.toObject());
this.newDomain = '';
}); });
} }

View File

@ -14,14 +14,7 @@
<div matTooltip="{{'ORG.PAGES.SELECTORGTOOLTIP' | translate}}" class="item card" <div matTooltip="{{'ORG.PAGES.SELECTORGTOOLTIP' | translate}}" class="item card"
*ngFor="let org of orgList; index as i" (click)="selectOrg(org, $event)" *ngFor="let org of orgList; index as i" (click)="selectOrg(org, $event)"
[ngClass]="{ selected: selection.isSelected(org),active: activeOrg?.id === org?.id }"> [ngClass]="{ selected: selection.isSelected(org),active: activeOrg?.id === org?.id }">
<!-- <mat-icon matTooltip="select org" (click)="selection.toggle(org)" class="selection-icon">
check_circle</mat-icon> -->
<div class="text-part"> <div class="text-part">
<!-- <span *ngIf="org?.changeDate" class="top">last modified on
{{
dateFromTimestamp(org.changeDate) | date: 'EEE dd. MMM, HH:mm'
}}</span> -->
<span class="description">{{org.id}}</span> <span class="description">{{org.id}}</span>
<span class="name" *ngIf="org.name">{{ org.name }}</span> <span class="name" *ngIf="org.name">{{ org.name }}</span>
@ -39,9 +32,6 @@
<button (click)="routeToOrg(org)" mat-menu-item> <button (click)="routeToOrg(org)" mat-menu-item>
{{'ACTIONS.VIEW' | translate}} {{'ACTIONS.VIEW' | translate}}
</button> </button>
<!-- <button (click)="selection.toggle(org)" mat-menu-item>
{{'ACTIONS.INFO' | translate}}
</button> -->
</ng-template> </ng-template>
</mat-menu> </mat-menu>
</div> </div>

View File

@ -1,7 +1,6 @@
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { Org } from 'src/app/proto/generated/auth_pb'; import { Org } from 'src/app/proto/generated/auth_pb';
import { AuthUserService } from 'src/app/services/auth-user.service'; import { AuthUserService } from 'src/app/services/auth-user.service';
import { AuthService } from 'src/app/services/auth.service'; import { AuthService } from 'src/app/services/auth.service';
@ -49,9 +48,4 @@ export class OrgGridComponent {
public routeToOrg(item: Org.AsObject): void { public routeToOrg(item: Org.AsObject): void {
this.router.navigate(['/orgs', item.id]); this.router.navigate(['/orgs', item.id]);
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
} }

View File

@ -26,8 +26,8 @@
<ng-template appHasRole [appHasRole]="['org.member.delete:'+org.id,'org.member.delete']"> <ng-template appHasRole [appHasRole]="['org.member.delete:'+org.id,'org.member.delete']">
<button (click)="removeProjectMemberSelection()" <button (click)="removeProjectMemberSelection()"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
*ngIf="selection.hasValue()"> *ngIf="selection.hasValue()" color="warn">
<mat-icon>remove_circle</mat-icon> <i class="las la-trash"></i>
</button> </button>
</ng-template> </ng-template>
<ng-template appHasRole [appHasRole]="['org.member.write:'+org.id,'org.member.write']"> <ng-template appHasRole [appHasRole]="['org.member.write:'+org.id,'org.member.write']">

View File

@ -24,6 +24,7 @@ const routes: Routes = [
action: PolicyComponentAction.CREATE, action: PolicyComponentAction.CREATE,
}, },
}, },
/// TODO: add roleguard for iam policy
{ {
path: 'policy/:policytype', path: 'policy/:policytype',
component: PasswordPolicyComponent, component: PasswordPolicyComponent,

View File

@ -123,6 +123,20 @@
</div> </div>
</div> </div>
<div class="content" *ngIf="policyType === PolicyComponentType?.IAM_POLICY">
<mat-form-field class="description-formfield" appearance="outline">
<mat-label>{{ 'ORG.POLICY.DATA.DESCRIPTION' | translate }}</mat-label>
<input matInput name="description" ngDefaultControl [(ngModel)]="iamData.description"
required />
</mat-form-field>
<div class="row">
<span class="left-desc">{{'ORG.POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate}}</span>
<span class="fill-space"></span>
<mat-slide-toggle name="hasNumber" ngDefaultControl [(ngModel)]="iamData.userLoginMustBeDomain">
</mat-slide-toggle>
</div>
</div>
<div class="btn-container"> <div class="btn-container">
<button (click)="savePolicy()" color="accent" type="submit" <button (click)="savePolicy()" color="accent" type="submit"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button> mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>

View File

@ -3,8 +3,15 @@ import { FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject, Subscription } from 'rxjs'; import { BehaviorSubject, Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { PasswordAgePolicy, PasswordComplexityPolicy, PasswordLockoutPolicy } from 'src/app/proto/generated/management_pb'; import {
OrgIamPolicy,
PasswordAgePolicy,
PasswordComplexityPolicy,
PasswordLockoutPolicy,
} from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service';
import { OrgService } from 'src/app/services/org.service'; import { OrgService } from 'src/app/services/org.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';
export enum PolicyComponentAction { export enum PolicyComponentAction {
@ -16,6 +23,7 @@ export enum PolicyComponentType {
LOCKOUT = 'lockout', LOCKOUT = 'lockout',
AGE = 'age', AGE = 'age',
COMPLEXITY = 'complexity', COMPLEXITY = 'complexity',
IAM_POLICY = 'iam_policy',
} }
@Component({ @Component({
@ -24,13 +32,15 @@ export enum PolicyComponentType {
styleUrls: ['./password-policy.component.scss'], styleUrls: ['./password-policy.component.scss'],
}) })
export class PasswordPolicyComponent implements OnInit, OnDestroy { export class PasswordPolicyComponent implements OnInit, OnDestroy {
public orgId: string = '';
titleSub: BehaviorSubject<string> = new BehaviorSubject(''); titleSub: BehaviorSubject<string> = new BehaviorSubject('');
descSub: BehaviorSubject<string> = new BehaviorSubject(''); descSub: BehaviorSubject<string> = new BehaviorSubject('');
componentAction: PolicyComponentAction = PolicyComponentAction.CREATE; componentAction: PolicyComponentAction = PolicyComponentAction.CREATE;
policyData!: PasswordLockoutPolicy.AsObject | PasswordAgePolicy.AsObject | PasswordComplexityPolicy.AsObject; policyData!: PasswordLockoutPolicy.AsObject |
PasswordAgePolicy.AsObject |
PasswordComplexityPolicy.AsObject |
OrgIamPolicy.AsObject;
policyType: PolicyComponentType = PolicyComponentType.COMPLEXITY; policyType: PolicyComponentType = PolicyComponentType.COMPLEXITY;
public PolicyComponentType: any = PolicyComponentType; public PolicyComponentType: any = PolicyComponentType;
@ -60,19 +70,25 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
maxAgeDays: 90, maxAgeDays: 90,
}; };
public iamData: any = {
description: '',
userLoginMustBeDomain: false,
};
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private adminService: AdminService,
private orgService: OrgService, private orgService: OrgService,
private router: Router, private router: Router,
private toast: ToastService, private toast: ToastService,
private sessionStorage: StorageService,
) { ) {
this.sub = this.route.data.pipe(switchMap(data => { this.sub = this.route.data.pipe(switchMap(data => {
this.componentAction = data.action; this.componentAction = data.action;
return this.route.params; return this.route.params;
})).subscribe(params => { })).subscribe(params => {
this.orgId = params.id;
this.policyType = params.policytype; this.policyType = params.policytype;
switch (params.policytype) { switch (params.policytype) {
@ -88,6 +104,10 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.titleSub.next('ORG.POLICY.PWD_COMPLEXITY.TITLECREATE'); this.titleSub.next('ORG.POLICY.PWD_COMPLEXITY.TITLECREATE');
this.descSub.next('ORG.POLICY.PWD_COMPLEXITY.DESCRIPTIONCREATE'); this.descSub.next('ORG.POLICY.PWD_COMPLEXITY.DESCRIPTIONCREATE');
break; break;
case PolicyComponentType.IAM_POLICY:
this.titleSub.next('ORG.POLICY.IAM_POLICY.TITLECREATE');
this.descSub.next('ORG.POLICY.IAM_POLICY.DESCRIPTIONCREATE');
break;
} }
if (this.componentAction === PolicyComponentAction.MODIFY) { if (this.componentAction === PolicyComponentAction.MODIFY) {
@ -102,6 +122,10 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
case PolicyComponentType.COMPLEXITY: case PolicyComponentType.COMPLEXITY:
this.complexityData = data.toObject(); this.complexityData = data.toObject();
break; break;
case PolicyComponentType.IAM_POLICY:
this.iamData = data.toObject();
console.log(this.iamData);
break;
} }
}); });
} }
@ -129,6 +153,10 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.titleSub.next('ORG.POLICY.PWD_COMPLEXITY.TITLE'); this.titleSub.next('ORG.POLICY.PWD_COMPLEXITY.TITLE');
this.descSub.next('ORG.POLICY.PWD_COMPLEXITY.DESCRIPTION'); this.descSub.next('ORG.POLICY.PWD_COMPLEXITY.DESCRIPTION');
return this.orgService.GetPasswordComplexityPolicy(); return this.orgService.GetPasswordComplexityPolicy();
case PolicyComponentType.IAM_POLICY:
this.titleSub.next('ORG.POLICY.IAM_POLICY.TITLECREATE');
this.descSub.next('ORG.POLICY.IAM_POLICY.DESCRIPTIONCREATE');
return this.orgService.GetMyOrgIamPolicy();
} }
} }
@ -189,7 +217,7 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.lockoutData.maxAttempts, this.lockoutData.maxAttempts,
this.lockoutData.showLockOutFailures, this.lockoutData.showLockOutFailures,
).then(() => { ).then(() => {
this.router.navigate(['orgs', this.orgId]); this.router.navigate(['org']);
}).catch(error => { }).catch(error => {
this.toast.showError(error.message); this.toast.showError(error.message);
}); });
@ -201,7 +229,7 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.ageData.maxAgeDays, this.ageData.maxAgeDays,
this.ageData.expireWarnDays, this.ageData.expireWarnDays,
).then(() => { ).then(() => {
this.router.navigate(['orgs', this.orgId]); this.router.navigate(['org']);
}).catch(error => { }).catch(error => {
this.toast.showError(error.message); this.toast.showError(error.message);
}); });
@ -217,11 +245,27 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.complexityData.hasSymbol, this.complexityData.hasSymbol,
this.complexityData.minLength, this.complexityData.minLength,
).then(() => { ).then(() => {
this.router.navigate(['orgs', this.orgId]); this.router.navigate(['org']);
}).catch(error => { }).catch(error => {
this.toast.showError(error.message); this.toast.showError(error.message);
}); });
break; break;
case PolicyComponentType.IAM_POLICY:
console.log(this.complexityData);
const orgId = this.sessionStorage.getItem('organization');
if (orgId) {
this.adminService.CreateOrgIamPolicy(
orgId,
this.complexityData.description,
this.complexityData.userLoginMustBeDomain,
).then(() => {
this.router.navigate(['org']);
}).catch(error => {
this.toast.showError(error.message);
});
}
break;
} }
} else if (this.componentAction === PolicyComponentAction.MODIFY) { } else if (this.componentAction === PolicyComponentAction.MODIFY) {
switch (this.policyType) { switch (this.policyType) {
@ -231,7 +275,7 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.lockoutData.maxAttempts, this.lockoutData.maxAttempts,
this.lockoutData.showLockOutFailures, this.lockoutData.showLockOutFailures,
).then(() => { ).then(() => {
this.router.navigate(['orgs', this.orgId]); this.router.navigate(['org']);
}).catch(error => { }).catch(error => {
this.toast.showError(error.message); this.toast.showError(error.message);
}); });
@ -243,7 +287,7 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.ageData.maxAgeDays, this.ageData.maxAgeDays,
this.ageData.expireWarnDays, this.ageData.expireWarnDays,
).then(() => { ).then(() => {
this.router.navigate(['orgs', this.orgId]); this.router.navigate(['org']);
}).catch(error => { }).catch(error => {
this.toast.showError(error.message); this.toast.showError(error.message);
}); });
@ -259,11 +303,27 @@ export class PasswordPolicyComponent implements OnInit, OnDestroy {
this.complexityData.hasSymbol, this.complexityData.hasSymbol,
this.complexityData.minLength, this.complexityData.minLength,
).then(() => { ).then(() => {
this.router.navigate(['orgs', this.orgId]); this.router.navigate(['org']);
}).catch(error => { }).catch(error => {
this.toast.showError(error.message); this.toast.showError(error.message);
}); });
break; break;
case PolicyComponentType.IAM_POLICY:
console.log(this.complexityData);
const orgId = this.sessionStorage.getItem('organization');
if (orgId) {
this.adminService.UpdateOrgIamPolicy(
orgId,
this.complexityData.description,
this.complexityData.userLoginMustBeDomain,
).then(() => {
this.router.navigate(['org']);
}).catch(error => {
this.toast.showError(error.message);
});
}
break;
} }
} }
} }

View File

@ -16,7 +16,7 @@
<ng-template appHasRole [appHasRole]="['policy.delete']"> <ng-template appHasRole [appHasRole]="['policy.delete']">
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" (click)="deletePolicy(PolicyComponentType.AGE)" <button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" (click)="deletePolicy(PolicyComponentType.AGE)"
mat-icon-button> mat-icon-button>
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> </button>
</ng-template> </ng-template>
</div> </div>
@ -46,12 +46,11 @@
<div class="title"> <div class="title">
<span>{{'ORG.POLICY.PWD_COMPLEXITY.TITLE' | translate}}</span> <span>{{'ORG.POLICY.PWD_COMPLEXITY.TITLE' | translate}}</span>
<button mat-icon-button disabled> <button mat-icon-button disabled>
<mat-icon class="icon" *ngIf="complexityPolicy"> <i *ngIf="complexityPolicy" class="icon las la-check-circle"></i>
check_circle</mat-icon>
</button> </button>
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" <button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn"
(click)="deletePolicy(PolicyComponentType.COMPLEXITY)" mat-icon-button> (click)="deletePolicy(PolicyComponentType.COMPLEXITY)" mat-icon-button>
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> </button>
</div> </div>
@ -81,7 +80,7 @@
</button> </button>
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" (click)="deletePolicy(PolicyComponentType.LOCKOUT)" <button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" (click)="deletePolicy(PolicyComponentType.LOCKOUT)"
mat-icon-button> mat-icon-button>
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> </button>
</div> </div>
@ -99,4 +98,39 @@
mat-raised-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button> mat-raised-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
</div> </div>
</div> --> </div> -->
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
<div class="p-item card">
<div class="avatar"><i class="icon las la-gem"></i>
</div>
<div class="title">
<span>{{'ORG.POLICY.IAM_POLICY.TITLE' | translate}}</span>
<button mat-icon-button disabled>
<i *ngIf="iamPolicy" class="icon las la-check-circle"></i>
</button>
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
<button matTooltip="{{'ORG.POLICY.DELETE' | translate}}" color="warn"
(click)="deletePolicy(PolicyComponentType.IAM_POLICY)" mat-icon-button>
<i class="las la-trash"></i>
</button>
</ng-template>
</div>
<p *ngIf="iamPolicy?.description; else showDescIAM" class="desc">
{{ iamPolicy.description }}</p>
<ng-template #showDescIAM>
<p class="desc">
{{'ORG.POLICY.IAM_POLICY.DESCRIPTION' | translate}}</p>
</ng-template>
<span class="fill-space"></span>
<div class="btn-wrapper">
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
<button [disabled]="iamPolicy" [routerLink]="[ 'policy', PolicyComponentType.IAM_POLICY,'create' ]"
color="primary" mat-raised-button>{{'ORG.POLICY.BTN_INSTALL' | translate}}</button>
<button [disabled]="!iamPolicy" [routerLink]="[ 'policy', PolicyComponentType.IAM_POLICY ]"
mat-raised-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
</ng-template>
</div>
</div>
</ng-template>
</div> </div>

View File

@ -30,7 +30,7 @@ h1 {
justify-content: center; justify-content: center;
margin-bottom: .5rem; margin-bottom: .5rem;
mat-icon { mat-icon, i {
font-size: 2.5rem; font-size: 2.5rem;
height: 2.5rem; height: 2.5rem;
line-height: 2.5rem; line-height: 2.5rem;

View File

@ -1,10 +1,12 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { import {
OrgIamPolicy,
PasswordAgePolicy, PasswordAgePolicy,
PasswordComplexityPolicy, PasswordComplexityPolicy,
PasswordLockoutPolicy, PasswordLockoutPolicy,
PolicyState, PolicyState,
} from 'src/app/proto/generated/management_pb'; } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service';
import { AuthUserService } from 'src/app/services/auth-user.service'; import { AuthUserService } from 'src/app/services/auth-user.service';
import { OrgService } from 'src/app/services/org.service'; import { OrgService } from 'src/app/services/org.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@ -20,11 +22,17 @@ export class PolicyGridComponent implements OnInit {
public lockoutPolicy!: PasswordLockoutPolicy.AsObject; public lockoutPolicy!: PasswordLockoutPolicy.AsObject;
public agePolicy!: PasswordAgePolicy.AsObject; public agePolicy!: PasswordAgePolicy.AsObject;
public complexityPolicy!: PasswordComplexityPolicy.AsObject; public complexityPolicy!: PasswordComplexityPolicy.AsObject;
public iamPolicy!: OrgIamPolicy.AsObject;
public PolicyState: any = PolicyState; public PolicyState: any = PolicyState;
public PolicyComponentType: any = PolicyComponentType; public PolicyComponentType: any = PolicyComponentType;
constructor(private orgService: OrgService, public authUserService: AuthUserService, private toast: ToastService) { constructor(
private orgService: OrgService,
private adminService: AdminService,
public authUserService: AuthUserService,
private toast: ToastService,
) {
this.getData(); this.getData();
} }
@ -36,6 +44,8 @@ export class PolicyGridComponent implements OnInit {
// this.orgService.GetPasswordAgePolicy().then(data => this.agePolicy = data.toObject()).catch(error => { }); // this.orgService.GetPasswordAgePolicy().then(data => this.agePolicy = data.toObject()).catch(error => { });
this.orgService.GetPasswordComplexityPolicy().then(data => this.complexityPolicy = data.toObject()) this.orgService.GetPasswordComplexityPolicy().then(data => this.complexityPolicy = data.toObject())
.catch(error => { }); .catch(error => { });
this.orgService.GetMyOrgIamPolicy().then(data => this.iamPolicy = data.toObject())
.catch(error => { });
} }
public deletePolicy(type: PolicyComponentType): void { public deletePolicy(type: PolicyComponentType): void {

View File

@ -26,7 +26,7 @@
<div class="card org" *ngIf="org"> <div class="card org" *ngIf="org">
<p>{{org?.name}}</p> <p>{{org?.name}}</p>
<span *ngIf="org.creationDate">created: <span *ngIf="org.creationDate">created:
{{dateFromTimestamp(org.creationDate) | date: 'EEE dd. MMM, HH:mm'}}</span> {{org.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
</div> </div>
</ng-container> </ng-container>

View File

@ -1,7 +1,6 @@
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Org, ProjectRole } from 'src/app/proto/generated/management_pb'; import { Org, ProjectRole } from 'src/app/proto/generated/management_pb';
import { AuthService } from 'src/app/services/auth.service'; import { AuthService } from 'src/app/services/auth.service';
@ -76,12 +75,6 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
}); });
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
public selectRoles(roles: ProjectRole.AsObject[]): void { public selectRoles(roles: ProjectRole.AsObject[]): void {
this.rolesKeyList = roles.map(role => role.key); this.rolesKeyList = roles.map(role => role.key);
} }

View File

@ -12,6 +12,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CardModule } from 'src/app/modules/card/card.module'; import { CardModule } from 'src/app/modules/card/card.module';
import { PipesModule } from 'src/app/pipes/pipes.module';
import { ProjectRolesModule } from '../../modules/project-roles/project-roles.module'; import { ProjectRolesModule } from '../../modules/project-roles/project-roles.module';
import { ProjectGrantCreateRoutingModule } from './project-grant-create-routing.module'; import { ProjectGrantCreateRoutingModule } from './project-grant-create-routing.module';
@ -32,6 +33,7 @@ import { ProjectGrantCreateComponent } from './project-grant-create.component';
ProjectRolesModule, ProjectRolesModule,
MatIconModule, MatIconModule,
MatTooltipModule, MatTooltipModule,
PipesModule,
ReactiveFormsModule, ReactiveFormsModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
FormsModule, FormsModule,

View File

@ -29,8 +29,9 @@
<mat-label>{{ 'PROJECT.ROLE.GROUP' | translate }}</mat-label> <mat-label>{{ 'PROJECT.ROLE.GROUP' | translate }}</mat-label>
<input matInput formControlName="group" /> <input matInput formControlName="group" />
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="removeEntry(i)" matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"> <button mat-icon-button (click)="removeEntry(i)" color="warn"
<mat-icon>remove_circle</mat-icon> matTooltip="{{ 'ACTIONS.REMOVE' | translate }}">
<i class="las la-trash"></i>
</button> </button>
</ng-container> </ng-container>
</div> </div>

View File

@ -24,17 +24,17 @@
<div class="text-part"> <div class="text-part">
<span *ngIf="item.changeDate" class="top">last modified on <span *ngIf="item.changeDate" class="top">last modified on
{{ {{
dateFromTimestamp(item.changeDate) | date: 'EEE dd. MMM, HH:mm' item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
}}</span> }}</span>
<span class="name" *ngIf="item.projectName">{{ item.projectName }}</span> <span class="name" *ngIf="item.projectName">{{ item.projectName }}</span>
<span class="description" *ngIf="item.state">{{'PROJECT.STATE.'+item.state | translate}}</span> <span class="description" *ngIf="item.grantedOrgName">{{item.grantedOrgName}}</span>
<!-- <span class="description" *ngIf="item.state">{{'PROJECT.STATE.'+item.state | translate}}</span> -->
<span *ngIf="item.changeDate" class="created">created on <span *ngIf="item.changeDate" class="created">created on
{{ {{
dateFromTimestamp(item.creationDate) | date: 'EEE dd. MMM, HH:mm' item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
}}</span> }}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="icons"> <div class="icons">
<mat-icon class="icon">apps</mat-icon>
</div> </div>
</div> </div>
<button [matMenuTriggerFor]="editMenu" class="edit-button" mat-icon-button> <button [matMenuTriggerFor]="editMenu" class="edit-button" mat-icon-button>
@ -53,4 +53,4 @@
</mat-menu> </mat-menu>
</div> </div>
<p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p> <p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
</div> </div>

View File

@ -2,7 +2,6 @@ import { animate, animateChild, query, stagger, style, transition, trigger } fro
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { ProjectGrantView, ProjectState, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb'; import { ProjectGrantView, ProjectState, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service'; import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@ -57,9 +56,4 @@ export class GrantedProjectGridComponent {
public addItem(): void { public addItem(): void {
this.newClicked.emit(true); this.newClicked.emit(true);
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
} }

View File

@ -78,7 +78,7 @@
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
<td mat-cell *matCellDef="let project"> <td mat-cell *matCellDef="let project">
<span <span
*ngIf="project.creationDate">{{dateFromTimestamp(project.creationDate) | date: 'EEE dd. MMM, HH:mm'}}</span> *ngIf="project.creationDate">{{project.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
</td> </td>
</ng-container> </ng-container>
@ -87,7 +87,7 @@
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th>
<td mat-cell *matCellDef="let project"> <td mat-cell *matCellDef="let project">
<span <span
*ngIf="project.changeDate">{{dateFromTimestamp(project.changeDate) | date: 'EEE dd. MMM, HH:mm'}}</span> *ngIf="project.changeDate">{{project.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
</td> </td>
</ng-container> </ng-container>

View File

@ -5,7 +5,6 @@ import { PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { ProjectGrantView } from 'src/app/proto/generated/management_pb'; import { ProjectGrantView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service'; import { ProjectService } from 'src/app/services/project.service';
@ -99,11 +98,6 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
}); });
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
public reactivateSelectedProjects(): void { public reactivateSelectedProjects(): void {
const promises = this.selection.selected.map(project => { const promises = this.selection.selected.map(project => {
this.projectService.ReactivateProject(project.id); this.projectService.ReactivateProject(project.id);

View File

@ -25,17 +25,17 @@
<div class="text-part"> <div class="text-part">
<span *ngIf="item.changeDate" class="top">last modified on <span *ngIf="item.changeDate" class="top">last modified on
{{ {{
dateFromTimestamp(item.changeDate) | date: 'EEE dd. MMM, HH:mm' item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
}}</span> }}</span>
<span class="name" *ngIf="item.name">{{ item.name }}</span> <span class="name" *ngIf="item.name">{{ item.name }}</span>
<span class="description" *ngIf="item.state">{{'PROJECT.STATE.'+item.state | translate}}</span> <!-- <span class="description" *ngIf="item.state">{{'PROJECT.STATE.'+item.state | translate}}</span> -->
<span *ngIf="item.changeDate" class="created">created on <span *ngIf="item.changeDate" class="created">created on
{{ {{
dateFromTimestamp(item.creationDate) | date: 'EEE dd. MMM, HH:mm' item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
}}</span> }}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="icons"> <div class="icons">
<mat-icon class="icon">apps</mat-icon>
</div> </div>
</div> </div>
<button [matMenuTriggerFor]="editMenu" class="edit-button" mat-icon-button> <button [matMenuTriggerFor]="editMenu" class="edit-button" mat-icon-button>
@ -56,8 +56,10 @@
<p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p> <p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
<div class="add-project-button card" (click)="addItem()"> <ng-template appHasRole [appHasRole]="['project.write']">
<mat-icon class="icon">add</mat-icon> <div class="add-project-button card" (click)="addItem()">
<span>Add new project</span> <mat-icon class="icon">add</mat-icon>
</div> <span>Add new project</span>
</div> </div>
</ng-template>
</div>

View File

@ -2,7 +2,6 @@ import { animate, animateChild, query, stagger, style, transition, trigger } fro
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { ProjectState, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb'; import { ProjectState, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service'; import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@ -58,11 +57,6 @@ export class OwnedProjectGridComponent {
this.newClicked.emit(true); this.newClicked.emit(true);
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
public reactivateProjects(selected: ProjectView.AsObject[]): void { public reactivateProjects(selected: ProjectView.AsObject[]): void {
Promise.all([selected.map(proj => { Promise.all([selected.map(proj => {
return this.projectService.ReactivateProject(proj.projectId); return this.projectService.ReactivateProject(proj.projectId);

View File

@ -30,9 +30,11 @@
<mat-icon svgIcon="mdi_light_on"></mat-icon> <mat-icon svgIcon="mdi_light_on"></mat-icon>
</button> </button>
</div> </div>
<a class="add-button" [routerLink]="[ '/projects', 'create']" color="primary" mat-raised-button> <ng-template appHasRole [appHasRole]="['project.write']">
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <a class="add-button" [routerLink]="[ '/projects', 'create']" color="primary" mat-raised-button>
</a> <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a>
</ng-template>
</div> </div>
<div class="table-wrapper"> <div class="table-wrapper">
<div class="spinner-container" *ngIf="(loading$ | async) || (loading$ | async)"> <div class="spinner-container" *ngIf="(loading$ | async) || (loading$ | async)">
@ -69,7 +71,7 @@
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
<td mat-cell *matCellDef="let project"> <td mat-cell *matCellDef="let project">
<span <span
*ngIf="project.creationDate">{{dateFromTimestamp(project.creationDate) | date: 'EEE dd. MMM, HH:mm'}}</span> *ngIf="project.creationDate">{{project.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
</td> </td>
</ng-container> </ng-container>
@ -78,7 +80,7 @@
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th>
<td mat-cell *matCellDef="let project"> <td mat-cell *matCellDef="let project">
<span <span
*ngIf="project.changeDate">{{dateFromTimestamp(project.changeDate) | date: 'EEE dd. MMM, HH:mm'}}</span> *ngIf="project.changeDate">{{project.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
</td> </td>
</ng-container> </ng-container>
@ -90,4 +92,4 @@
<mat-paginator class="background-style" [length]="totalResult" [pageSize]="10" [pageSizeOptions]="[5, 10, 20]" <mat-paginator class="background-style" [length]="totalResult" [pageSize]="10" [pageSizeOptions]="[5, 10, 20]"
(page)="changePage($event)"></mat-paginator> (page)="changePage($event)"></mat-paginator>
</div> </div>
</div> </div>

View File

@ -5,7 +5,6 @@ import { PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { ProjectView } from 'src/app/proto/generated/management_pb'; import { ProjectView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service'; import { ProjectService } from 'src/app/services/project.service';
@ -103,11 +102,6 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
this.ownedProjectList = []; this.ownedProjectList = [];
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
public reactivateSelectedProjects(): void { public reactivateSelectedProjects(): void {
const promises = this.selection.selected.map(project => { const promises = this.selection.selected.map(project => {
this.projectService.ReactivateProject(project.projectId); this.projectService.ReactivateProject(project.projectId);

View File

@ -12,7 +12,7 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<!-- <button [disabled]="disabled" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" <!-- <button [disabled]="disabled" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button"
mat-icon-button *ngIf="selection.hasValue()"> mat-icon-button *ngIf="selection.hasValue()">
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> --> </button> -->
<ng-template appHasRole [appHasRole]="['project.app.write']"> <ng-template appHasRole [appHasRole]="['project.app.write']">
<a [disabled]="disabled" class="add-button" [routerLink]="[ '/projects', projectId, 'apps', 'create']" <a [disabled]="disabled" class="add-button" [routerLink]="[ '/projects', projectId, 'apps', 'create']"

View File

@ -14,8 +14,8 @@
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<button (click)="removeProjectMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" <button (click)="removeProjectMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()"> class="icon-button" color="warn" mat-icon-button *ngIf="selection.hasValue()">
<mat-icon>remove_circle</mat-icon> <i class="las la-trash"></i>
</button> </button>
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary" <a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary"
mat-raised-button> mat-raised-button>

View File

@ -12,8 +12,8 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['project.grant.member.delete:'+projectId, 'project.grant.member.delete']"> <ng-template appHasRole [appHasRole]="['project.grant.member.delete:'+projectId, 'project.grant.member.delete']">
<button [disabled]="disabled" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" <button [disabled]="disabled" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button"
mat-icon-button *ngIf="selection.hasValue()"> mat-icon-button *ngIf="selection.hasValue()" color="warn">
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> </button>
</ng-template> </ng-template>
<ng-template appHasRole [appHasRole]="['project.grant.member.write:'+projectId,'project.grant.member.write']"> <ng-template appHasRole [appHasRole]="['project.grant.member.write:'+projectId,'project.grant.member.write']">
@ -59,13 +59,13 @@
<ng-container matColumnDef="creationDate"> <ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let grant"> <td class="pointer" mat-cell *matCellDef="let grant">
{{dateFromTimestamp(grant.creationDate) | date: 'dd. MMM, HH:mm' }} </td> {{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="changeDate"> <ng-container matColumnDef="changeDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let grant"> <td class="pointer" mat-cell *matCellDef="let grant">
{{dateFromTimestamp(grant.changeDate) | date: 'dd. MMM, HH:mm' }} </td> {{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
</ng-container> </ng-container>

View File

@ -4,7 +4,6 @@ import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/cor
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { ProjectGrant, ProjectMemberView } from 'src/app/proto/generated/management_pb'; import { ProjectGrant, ProjectMemberView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service'; import { ProjectService } from 'src/app/services/project.service';
@ -131,9 +130,4 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
this.toast.showInfo(error.message); this.toast.showInfo(error.message);
}); });
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
} }

View File

@ -25,10 +25,10 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<ng-template appHasRole <ng-template appHasRole
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']"> [appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']">
<button (click)="removeProjectMemberSelection()" <button (click)="removeProjectMemberSelection()" color="warn"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
*ngIf="selection.hasValue()"> *ngIf="selection.hasValue()">
<mat-icon>remove_circle</mat-icon> <i class="las la-trash"></i>
</button> </button>
</ng-template> </ng-template>
<ng-template appHasRole <ng-template appHasRole

View File

@ -1,5 +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 { AuthGuard } from 'src/app/guards/auth.guard';
import { RoleGuard } from 'src/app/guards/role.guard';
import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component'; import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component';
import { OwnedProjectDetailComponent } from './owned-project-detail/owned-project-detail.component'; import { OwnedProjectDetailComponent } from './owned-project-detail/owned-project-detail.component';
@ -14,6 +16,10 @@ const routes: Routes = [
{ {
path: 'create', path: 'create',
loadChildren: () => import('../project-create/project-create.module').then(m => m.ProjectCreateModule), loadChildren: () => import('../project-create/project-create.module').then(m => m.ProjectCreateModule),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['project.write'],
},
}, },
{ {
path: ':id/grant/:grantId', path: ':id/grant/:grantId',

View File

@ -25,6 +25,7 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { CardModule } from 'src/app/modules/card/card.module'; import { CardModule } from 'src/app/modules/card/card.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module'; import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { SearchUserAutocompleteModule } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.module'; import { SearchUserAutocompleteModule } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.module';
import { PipesModule } from 'src/app/pipes/pipes.module';
import { ChangesModule } from '../../modules/changes/changes.module'; import { ChangesModule } from '../../modules/changes/changes.module';
import { ProjectRolesModule } from '../../modules/project-roles/project-roles.module'; import { ProjectRolesModule } from '../../modules/project-roles/project-roles.module';
@ -95,6 +96,7 @@ import { ProjectsComponent } from './projects.component';
CardModule, CardModule,
MatTooltipModule, MatTooltipModule,
MatSortModule, MatSortModule,
PipesModule,
OrgContributorsModule, OrgContributorsModule,
TranslateModule.forChild({ TranslateModule.forChild({
loader: { loader: {

View File

@ -167,8 +167,8 @@
<button (click)="phoneEditState = true" mat-icon-button> <button (click)="phoneEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button *ngIf="phone?.phone" (click)="deletePhone()" mat-icon-button> <button color="warn" *ngIf="phone?.phone" (click)="deletePhone()" mat-icon-button>
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> </button>
</div> </div>
</ng-container> </ng-container>

View File

@ -3,8 +3,9 @@
<div class="row" *ngFor="let mfa of mfaSubject | async"> <div class="row" *ngFor="let mfa of mfaSubject | async">
<span>{{'USER.MFA.TYPE.'+ mfa.type | translate}}</span> <span>{{'USER.MFA.TYPE.'+ mfa.type | translate}}</span>
<span>{{'USER.MFA.STATE.'+ mfa.state | translate}}</span> <span>{{'USER.MFA.STATE.'+ mfa.state | translate}}</span>
<button mat-icon-button (click)="deleteMFA(mfa.type)" matTooltip="{{'ACTIONS.DELETE' | translate}}"> <button mat-icon-button (click)="deleteMFA(mfa.type)" color="warn"
<mat-icon>delete_outline</mat-icon> matTooltip="{{'ACTIONS.DELETE' | translate}}">
<i class="las la-trash"></i>
</button> </button>
</div> </div>
<p class="row" *ngIf="error">{{error}}</p> <p class="row" *ngIf="error">{{error}}</p>

View File

@ -153,8 +153,8 @@
<button (click)="phoneEditState = true" mat-icon-button> <button (click)="phoneEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button *ngIf="user?.phone" (click)="deletePhone()" mat-icon-button> <button *ngIf="user?.phone" (click)="deletePhone()" color="warn" mat-icon-button>
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> </button>
</div> </div>
</ng-container> </ng-container>

View File

@ -11,9 +11,9 @@
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['user.grant.delete:'+userId,'user.grant.delete']"> <ng-template appHasRole [appHasRole]="['user.grant.delete:'+userId,'user.grant.delete']">
<button matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button <button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button
*ngIf="selection.hasValue()"> *ngIf="selection.hasValue()">
<mat-icon>delete_outline</mat-icon> <i class="las la-trash"></i>
</button> </button>
</ng-template> </ng-template>
<ng-template appHasRole [appHasRole]="['user.grant.write:'+userId,'user.grant.write']"> <ng-template appHasRole [appHasRole]="['user.grant.write:'+userId,'user.grant.write']">
@ -59,13 +59,13 @@
<ng-container matColumnDef="creationDate"> <ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let grant"> <td class="pointer" mat-cell *matCellDef="let grant">
{{dateFromTimestamp(grant.creationDate) | date: 'dd. MMM, HH:mm' }} </td> {{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="changeDate"> <ng-container matColumnDef="changeDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let grant"> <td class="pointer" mat-cell *matCellDef="let grant">
{{dateFromTimestamp(grant.changeDate) | date: 'dd. MMM, HH:mm' }} </td> {{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
</ng-container> </ng-container>

View File

@ -2,7 +2,6 @@ import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { ProjectGrant, UserGrant } from 'src/app/proto/generated/management_pb'; import { ProjectGrant, UserGrant } from 'src/app/proto/generated/management_pb';
import { MgmtUserService } from 'src/app/services/mgmt-user.service'; import { MgmtUserService } from 'src/app/services/mgmt-user.service';
@ -59,9 +58,4 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
this.selection.clear() : this.selection.clear() :
this.dataSource.grantsSubject.value.forEach(row => this.selection.select(row)); this.dataSource.grantsSubject.value.forEach(row => this.selection.select(row));
} }
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
} }

View File

@ -11,6 +11,7 @@ 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 { PipesModule } from 'src/app/pipes/pipes.module';
import { UserGrantsComponent } from './user-grants.component'; import { UserGrantsComponent } from './user-grants.component';
@ -31,6 +32,7 @@ import { UserGrantsComponent } from './user-grants.component';
MatCheckboxModule, MatCheckboxModule,
MatTooltipModule, MatTooltipModule,
TranslateModule, TranslateModule,
PipesModule,
], ],
exports: [ exports: [
UserGrantsComponent, UserGrantsComponent,

View File

@ -3,11 +3,13 @@ import { NgModule } from '@angular/core';
import { MomentModule } from 'ngx-moment'; import { MomentModule } from 'ngx-moment';
import { LocalizedDatePipe } from './localized-date.pipe'; import { LocalizedDatePipe } from './localized-date.pipe';
import { TimestampToDatePipe } from './timestamp-to-date.pipe';
@NgModule({ @NgModule({
declarations: [ declarations: [
LocalizedDatePipe, LocalizedDatePipe,
TimestampToDatePipe,
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -15,6 +17,7 @@ import { LocalizedDatePipe } from './localized-date.pipe';
], ],
exports: [ exports: [
LocalizedDatePipe, LocalizedDatePipe,
TimestampToDatePipe,
], ],
}) })
export class PipesModule { } export class PipesModule { }

View File

@ -0,0 +1,18 @@
import { Pipe, PipeTransform } from '@angular/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
@Pipe({
name: 'timestampToDate',
})
export class TimestampToDatePipe implements PipeTransform {
transform(value: Timestamp.AsObject, ...args: unknown[]): unknown {
return this.dateFromTimestamp(value);
}
private dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000 / 1000);
return ts;
}
}

View File

@ -9,6 +9,14 @@ import * as protoc$gen$swagger_options_annotations_pb from './protoc-gen-swagger
import * as authoption_options_pb from './authoption/options_pb'; import * as authoption_options_pb from './authoption/options_pb';
import { import {
AddIamMemberRequest,
ChangeIamMemberRequest,
FailedEventID,
FailedEvents,
IamMember,
IamMemberRoles,
IamMemberSearchRequest,
IamMemberSearchResponse,
Org, Org,
OrgID, OrgID,
OrgIamPolicy, OrgIamPolicy,
@ -18,8 +26,11 @@ import {
OrgSearchResponse, OrgSearchResponse,
OrgSetUpRequest, OrgSetUpRequest,
OrgSetUpResponse, OrgSetUpResponse,
RemoveIamMemberRequest,
UniqueOrgRequest, UniqueOrgRequest,
UniqueOrgResponse} from './admin_pb'; UniqueOrgResponse,
ViewID,
Views} from './admin_pb';
export class AdminServiceClient { export class AdminServiceClient {
constructor (hostname: string, constructor (hostname: string,
@ -103,6 +114,69 @@ export class AdminServiceClient {
response: google_protobuf_empty_pb.Empty) => void response: google_protobuf_empty_pb.Empty) => void
): grpcWeb.ClientReadableStream<google_protobuf_empty_pb.Empty>; ): grpcWeb.ClientReadableStream<google_protobuf_empty_pb.Empty>;
getIamMemberRoles(
request: google_protobuf_empty_pb.Empty,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: IamMemberRoles) => void
): grpcWeb.ClientReadableStream<IamMemberRoles>;
addIamMember(
request: AddIamMemberRequest,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: IamMember) => void
): grpcWeb.ClientReadableStream<IamMember>;
changeIamMember(
request: ChangeIamMemberRequest,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: IamMember) => void
): grpcWeb.ClientReadableStream<IamMember>;
removeIamMember(
request: RemoveIamMemberRequest,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: google_protobuf_empty_pb.Empty) => void
): grpcWeb.ClientReadableStream<google_protobuf_empty_pb.Empty>;
searchIamMembers(
request: IamMemberSearchRequest,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: IamMemberSearchResponse) => void
): grpcWeb.ClientReadableStream<IamMemberSearchResponse>;
getViews(
request: google_protobuf_empty_pb.Empty,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: Views) => void
): grpcWeb.ClientReadableStream<Views>;
clearView(
request: ViewID,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: google_protobuf_empty_pb.Empty) => void
): grpcWeb.ClientReadableStream<google_protobuf_empty_pb.Empty>;
getFailedEvents(
request: google_protobuf_empty_pb.Empty,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: FailedEvents) => void
): grpcWeb.ClientReadableStream<FailedEvents>;
removeFailedEvent(
request: FailedEventID,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: google_protobuf_empty_pb.Empty) => void
): grpcWeb.ClientReadableStream<google_protobuf_empty_pb.Empty>;
} }
export class AdminServicePromiseClient { export class AdminServicePromiseClient {
@ -165,5 +239,50 @@ export class AdminServicePromiseClient {
metadata?: grpcWeb.Metadata metadata?: grpcWeb.Metadata
): Promise<google_protobuf_empty_pb.Empty>; ): Promise<google_protobuf_empty_pb.Empty>;
getIamMemberRoles(
request: google_protobuf_empty_pb.Empty,
metadata?: grpcWeb.Metadata
): Promise<IamMemberRoles>;
addIamMember(
request: AddIamMemberRequest,
metadata?: grpcWeb.Metadata
): Promise<IamMember>;
changeIamMember(
request: ChangeIamMemberRequest,
metadata?: grpcWeb.Metadata
): Promise<IamMember>;
removeIamMember(
request: RemoveIamMemberRequest,
metadata?: grpcWeb.Metadata
): Promise<google_protobuf_empty_pb.Empty>;
searchIamMembers(
request: IamMemberSearchRequest,
metadata?: grpcWeb.Metadata
): Promise<IamMemberSearchResponse>;
getViews(
request: google_protobuf_empty_pb.Empty,
metadata?: grpcWeb.Metadata
): Promise<Views>;
clearView(
request: ViewID,
metadata?: grpcWeb.Metadata
): Promise<google_protobuf_empty_pb.Empty>;
getFailedEvents(
request: google_protobuf_empty_pb.Empty,
metadata?: grpcWeb.Metadata
): Promise<FailedEvents>;
removeFailedEvent(
request: FailedEventID,
metadata?: grpcWeb.Metadata
): Promise<google_protobuf_empty_pb.Empty>;
} }

View File

@ -964,5 +964,725 @@ proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.deleteOrgIam
}; };
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.admin.api.v1.IamMemberRoles>}
*/
const methodDescriptor_AdminService_GetIamMemberRoles = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/GetIamMemberRoles',
grpc.web.MethodType.UNARY,
google_protobuf_empty_pb.Empty,
proto.caos.zitadel.admin.api.v1.IamMemberRoles,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMemberRoles.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.admin.api.v1.IamMemberRoles>}
*/
const methodInfo_AdminService_GetIamMemberRoles = new grpc.web.AbstractClientBase.MethodInfo(
proto.caos.zitadel.admin.api.v1.IamMemberRoles,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMemberRoles.deserializeBinary
);
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.caos.zitadel.admin.api.v1.IamMemberRoles)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.caos.zitadel.admin.api.v1.IamMemberRoles>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.getIamMemberRoles =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/GetIamMemberRoles',
request,
metadata || {},
methodDescriptor_AdminService_GetIamMemberRoles,
callback);
};
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.caos.zitadel.admin.api.v1.IamMemberRoles>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.getIamMemberRoles =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/GetIamMemberRoles',
request,
metadata || {},
methodDescriptor_AdminService_GetIamMemberRoles);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.caos.zitadel.admin.api.v1.AddIamMemberRequest,
* !proto.caos.zitadel.admin.api.v1.IamMember>}
*/
const methodDescriptor_AdminService_AddIamMember = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/AddIamMember',
grpc.web.MethodType.UNARY,
proto.caos.zitadel.admin.api.v1.AddIamMemberRequest,
proto.caos.zitadel.admin.api.v1.IamMember,
/**
* @param {!proto.caos.zitadel.admin.api.v1.AddIamMemberRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMember.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.caos.zitadel.admin.api.v1.AddIamMemberRequest,
* !proto.caos.zitadel.admin.api.v1.IamMember>}
*/
const methodInfo_AdminService_AddIamMember = new grpc.web.AbstractClientBase.MethodInfo(
proto.caos.zitadel.admin.api.v1.IamMember,
/**
* @param {!proto.caos.zitadel.admin.api.v1.AddIamMemberRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMember.deserializeBinary
);
/**
* @param {!proto.caos.zitadel.admin.api.v1.AddIamMemberRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.caos.zitadel.admin.api.v1.IamMember)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.caos.zitadel.admin.api.v1.IamMember>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.addIamMember =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/AddIamMember',
request,
metadata || {},
methodDescriptor_AdminService_AddIamMember,
callback);
};
/**
* @param {!proto.caos.zitadel.admin.api.v1.AddIamMemberRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.caos.zitadel.admin.api.v1.IamMember>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.addIamMember =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/AddIamMember',
request,
metadata || {},
methodDescriptor_AdminService_AddIamMember);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.caos.zitadel.admin.api.v1.ChangeIamMemberRequest,
* !proto.caos.zitadel.admin.api.v1.IamMember>}
*/
const methodDescriptor_AdminService_ChangeIamMember = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/ChangeIamMember',
grpc.web.MethodType.UNARY,
proto.caos.zitadel.admin.api.v1.ChangeIamMemberRequest,
proto.caos.zitadel.admin.api.v1.IamMember,
/**
* @param {!proto.caos.zitadel.admin.api.v1.ChangeIamMemberRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMember.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.caos.zitadel.admin.api.v1.ChangeIamMemberRequest,
* !proto.caos.zitadel.admin.api.v1.IamMember>}
*/
const methodInfo_AdminService_ChangeIamMember = new grpc.web.AbstractClientBase.MethodInfo(
proto.caos.zitadel.admin.api.v1.IamMember,
/**
* @param {!proto.caos.zitadel.admin.api.v1.ChangeIamMemberRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMember.deserializeBinary
);
/**
* @param {!proto.caos.zitadel.admin.api.v1.ChangeIamMemberRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.caos.zitadel.admin.api.v1.IamMember)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.caos.zitadel.admin.api.v1.IamMember>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.changeIamMember =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/ChangeIamMember',
request,
metadata || {},
methodDescriptor_AdminService_ChangeIamMember,
callback);
};
/**
* @param {!proto.caos.zitadel.admin.api.v1.ChangeIamMemberRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.caos.zitadel.admin.api.v1.IamMember>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.changeIamMember =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/ChangeIamMember',
request,
metadata || {},
methodDescriptor_AdminService_ChangeIamMember);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.caos.zitadel.admin.api.v1.RemoveIamMemberRequest,
* !proto.google.protobuf.Empty>}
*/
const methodDescriptor_AdminService_RemoveIamMember = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/RemoveIamMember',
grpc.web.MethodType.UNARY,
proto.caos.zitadel.admin.api.v1.RemoveIamMemberRequest,
google_protobuf_empty_pb.Empty,
/**
* @param {!proto.caos.zitadel.admin.api.v1.RemoveIamMemberRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
google_protobuf_empty_pb.Empty.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.caos.zitadel.admin.api.v1.RemoveIamMemberRequest,
* !proto.google.protobuf.Empty>}
*/
const methodInfo_AdminService_RemoveIamMember = new grpc.web.AbstractClientBase.MethodInfo(
google_protobuf_empty_pb.Empty,
/**
* @param {!proto.caos.zitadel.admin.api.v1.RemoveIamMemberRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
google_protobuf_empty_pb.Empty.deserializeBinary
);
/**
* @param {!proto.caos.zitadel.admin.api.v1.RemoveIamMemberRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.google.protobuf.Empty)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.google.protobuf.Empty>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.removeIamMember =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/RemoveIamMember',
request,
metadata || {},
methodDescriptor_AdminService_RemoveIamMember,
callback);
};
/**
* @param {!proto.caos.zitadel.admin.api.v1.RemoveIamMemberRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.google.protobuf.Empty>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.removeIamMember =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/RemoveIamMember',
request,
metadata || {},
methodDescriptor_AdminService_RemoveIamMember);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.caos.zitadel.admin.api.v1.IamMemberSearchRequest,
* !proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse>}
*/
const methodDescriptor_AdminService_SearchIamMembers = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/SearchIamMembers',
grpc.web.MethodType.UNARY,
proto.caos.zitadel.admin.api.v1.IamMemberSearchRequest,
proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse,
/**
* @param {!proto.caos.zitadel.admin.api.v1.IamMemberSearchRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.caos.zitadel.admin.api.v1.IamMemberSearchRequest,
* !proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse>}
*/
const methodInfo_AdminService_SearchIamMembers = new grpc.web.AbstractClientBase.MethodInfo(
proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse,
/**
* @param {!proto.caos.zitadel.admin.api.v1.IamMemberSearchRequest} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse.deserializeBinary
);
/**
* @param {!proto.caos.zitadel.admin.api.v1.IamMemberSearchRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.searchIamMembers =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/SearchIamMembers',
request,
metadata || {},
methodDescriptor_AdminService_SearchIamMembers,
callback);
};
/**
* @param {!proto.caos.zitadel.admin.api.v1.IamMemberSearchRequest} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.caos.zitadel.admin.api.v1.IamMemberSearchResponse>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.searchIamMembers =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/SearchIamMembers',
request,
metadata || {},
methodDescriptor_AdminService_SearchIamMembers);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.admin.api.v1.Views>}
*/
const methodDescriptor_AdminService_GetViews = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/GetViews',
grpc.web.MethodType.UNARY,
google_protobuf_empty_pb.Empty,
proto.caos.zitadel.admin.api.v1.Views,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.Views.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.admin.api.v1.Views>}
*/
const methodInfo_AdminService_GetViews = new grpc.web.AbstractClientBase.MethodInfo(
proto.caos.zitadel.admin.api.v1.Views,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.Views.deserializeBinary
);
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.caos.zitadel.admin.api.v1.Views)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.caos.zitadel.admin.api.v1.Views>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.getViews =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/GetViews',
request,
metadata || {},
methodDescriptor_AdminService_GetViews,
callback);
};
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.caos.zitadel.admin.api.v1.Views>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.getViews =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/GetViews',
request,
metadata || {},
methodDescriptor_AdminService_GetViews);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.caos.zitadel.admin.api.v1.ViewID,
* !proto.google.protobuf.Empty>}
*/
const methodDescriptor_AdminService_ClearView = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/ClearView',
grpc.web.MethodType.UNARY,
proto.caos.zitadel.admin.api.v1.ViewID,
google_protobuf_empty_pb.Empty,
/**
* @param {!proto.caos.zitadel.admin.api.v1.ViewID} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
google_protobuf_empty_pb.Empty.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.caos.zitadel.admin.api.v1.ViewID,
* !proto.google.protobuf.Empty>}
*/
const methodInfo_AdminService_ClearView = new grpc.web.AbstractClientBase.MethodInfo(
google_protobuf_empty_pb.Empty,
/**
* @param {!proto.caos.zitadel.admin.api.v1.ViewID} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
google_protobuf_empty_pb.Empty.deserializeBinary
);
/**
* @param {!proto.caos.zitadel.admin.api.v1.ViewID} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.google.protobuf.Empty)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.google.protobuf.Empty>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.clearView =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/ClearView',
request,
metadata || {},
methodDescriptor_AdminService_ClearView,
callback);
};
/**
* @param {!proto.caos.zitadel.admin.api.v1.ViewID} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.google.protobuf.Empty>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.clearView =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/ClearView',
request,
metadata || {},
methodDescriptor_AdminService_ClearView);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.admin.api.v1.FailedEvents>}
*/
const methodDescriptor_AdminService_GetFailedEvents = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/GetFailedEvents',
grpc.web.MethodType.UNARY,
google_protobuf_empty_pb.Empty,
proto.caos.zitadel.admin.api.v1.FailedEvents,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.FailedEvents.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.admin.api.v1.FailedEvents>}
*/
const methodInfo_AdminService_GetFailedEvents = new grpc.web.AbstractClientBase.MethodInfo(
proto.caos.zitadel.admin.api.v1.FailedEvents,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.admin.api.v1.FailedEvents.deserializeBinary
);
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.caos.zitadel.admin.api.v1.FailedEvents)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.caos.zitadel.admin.api.v1.FailedEvents>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.getFailedEvents =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/GetFailedEvents',
request,
metadata || {},
methodDescriptor_AdminService_GetFailedEvents,
callback);
};
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.caos.zitadel.admin.api.v1.FailedEvents>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.getFailedEvents =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/GetFailedEvents',
request,
metadata || {},
methodDescriptor_AdminService_GetFailedEvents);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.caos.zitadel.admin.api.v1.FailedEventID,
* !proto.google.protobuf.Empty>}
*/
const methodDescriptor_AdminService_RemoveFailedEvent = new grpc.web.MethodDescriptor(
'/caos.zitadel.admin.api.v1.AdminService/RemoveFailedEvent',
grpc.web.MethodType.UNARY,
proto.caos.zitadel.admin.api.v1.FailedEventID,
google_protobuf_empty_pb.Empty,
/**
* @param {!proto.caos.zitadel.admin.api.v1.FailedEventID} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
google_protobuf_empty_pb.Empty.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.caos.zitadel.admin.api.v1.FailedEventID,
* !proto.google.protobuf.Empty>}
*/
const methodInfo_AdminService_RemoveFailedEvent = new grpc.web.AbstractClientBase.MethodInfo(
google_protobuf_empty_pb.Empty,
/**
* @param {!proto.caos.zitadel.admin.api.v1.FailedEventID} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
google_protobuf_empty_pb.Empty.deserializeBinary
);
/**
* @param {!proto.caos.zitadel.admin.api.v1.FailedEventID} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.google.protobuf.Empty)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.google.protobuf.Empty>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.admin.api.v1.AdminServiceClient.prototype.removeFailedEvent =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/RemoveFailedEvent',
request,
metadata || {},
methodDescriptor_AdminService_RemoveFailedEvent,
callback);
};
/**
* @param {!proto.caos.zitadel.admin.api.v1.FailedEventID} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.google.protobuf.Empty>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.admin.api.v1.AdminServicePromiseClient.prototype.removeFailedEvent =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.admin.api.v1.AdminService/RemoveFailedEvent',
request,
metadata || {},
methodDescriptor_AdminService_RemoveFailedEvent);
};
module.exports = proto.caos.zitadel.admin.api.v1; module.exports = proto.caos.zitadel.admin.api.v1;

View File

@ -546,6 +546,422 @@ export namespace OrgIamPolicyID {
} }
} }
export class IamMemberRoles extends jspb.Message {
getRolesList(): Array<string>;
setRolesList(value: Array<string>): void;
clearRolesList(): void;
addRoles(value: string, index?: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): IamMemberRoles.AsObject;
static toObject(includeInstance: boolean, msg: IamMemberRoles): IamMemberRoles.AsObject;
static serializeBinaryToWriter(message: IamMemberRoles, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): IamMemberRoles;
static deserializeBinaryFromReader(message: IamMemberRoles, reader: jspb.BinaryReader): IamMemberRoles;
}
export namespace IamMemberRoles {
export type AsObject = {
rolesList: Array<string>,
}
}
export class IamMember extends jspb.Message {
getUserId(): string;
setUserId(value: string): void;
getRolesList(): Array<string>;
setRolesList(value: Array<string>): void;
clearRolesList(): void;
addRoles(value: string, index?: number): void;
getChangeDate(): google_protobuf_timestamp_pb.Timestamp | undefined;
setChangeDate(value?: google_protobuf_timestamp_pb.Timestamp): void;
hasChangeDate(): boolean;
clearChangeDate(): void;
getCreationDate(): google_protobuf_timestamp_pb.Timestamp | undefined;
setCreationDate(value?: google_protobuf_timestamp_pb.Timestamp): void;
hasCreationDate(): boolean;
clearCreationDate(): void;
getSequence(): number;
setSequence(value: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): IamMember.AsObject;
static toObject(includeInstance: boolean, msg: IamMember): IamMember.AsObject;
static serializeBinaryToWriter(message: IamMember, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): IamMember;
static deserializeBinaryFromReader(message: IamMember, reader: jspb.BinaryReader): IamMember;
}
export namespace IamMember {
export type AsObject = {
userId: string,
rolesList: Array<string>,
changeDate?: google_protobuf_timestamp_pb.Timestamp.AsObject,
creationDate?: google_protobuf_timestamp_pb.Timestamp.AsObject,
sequence: number,
}
}
export class AddIamMemberRequest extends jspb.Message {
getUserId(): string;
setUserId(value: string): void;
getRolesList(): Array<string>;
setRolesList(value: Array<string>): void;
clearRolesList(): void;
addRoles(value: string, index?: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): AddIamMemberRequest.AsObject;
static toObject(includeInstance: boolean, msg: AddIamMemberRequest): AddIamMemberRequest.AsObject;
static serializeBinaryToWriter(message: AddIamMemberRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): AddIamMemberRequest;
static deserializeBinaryFromReader(message: AddIamMemberRequest, reader: jspb.BinaryReader): AddIamMemberRequest;
}
export namespace AddIamMemberRequest {
export type AsObject = {
userId: string,
rolesList: Array<string>,
}
}
export class ChangeIamMemberRequest extends jspb.Message {
getUserId(): string;
setUserId(value: string): void;
getRolesList(): Array<string>;
setRolesList(value: Array<string>): void;
clearRolesList(): void;
addRoles(value: string, index?: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ChangeIamMemberRequest.AsObject;
static toObject(includeInstance: boolean, msg: ChangeIamMemberRequest): ChangeIamMemberRequest.AsObject;
static serializeBinaryToWriter(message: ChangeIamMemberRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ChangeIamMemberRequest;
static deserializeBinaryFromReader(message: ChangeIamMemberRequest, reader: jspb.BinaryReader): ChangeIamMemberRequest;
}
export namespace ChangeIamMemberRequest {
export type AsObject = {
userId: string,
rolesList: Array<string>,
}
}
export class RemoveIamMemberRequest extends jspb.Message {
getUserId(): string;
setUserId(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): RemoveIamMemberRequest.AsObject;
static toObject(includeInstance: boolean, msg: RemoveIamMemberRequest): RemoveIamMemberRequest.AsObject;
static serializeBinaryToWriter(message: RemoveIamMemberRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): RemoveIamMemberRequest;
static deserializeBinaryFromReader(message: RemoveIamMemberRequest, reader: jspb.BinaryReader): RemoveIamMemberRequest;
}
export namespace RemoveIamMemberRequest {
export type AsObject = {
userId: string,
}
}
export class IamMemberSearchResponse extends jspb.Message {
getOffset(): number;
setOffset(value: number): void;
getLimit(): number;
setLimit(value: number): void;
getTotalResult(): number;
setTotalResult(value: number): void;
getResultList(): Array<IamMemberView>;
setResultList(value: Array<IamMemberView>): void;
clearResultList(): void;
addResult(value?: IamMemberView, index?: number): IamMemberView;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): IamMemberSearchResponse.AsObject;
static toObject(includeInstance: boolean, msg: IamMemberSearchResponse): IamMemberSearchResponse.AsObject;
static serializeBinaryToWriter(message: IamMemberSearchResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): IamMemberSearchResponse;
static deserializeBinaryFromReader(message: IamMemberSearchResponse, reader: jspb.BinaryReader): IamMemberSearchResponse;
}
export namespace IamMemberSearchResponse {
export type AsObject = {
offset: number,
limit: number,
totalResult: number,
resultList: Array<IamMemberView.AsObject>,
}
}
export class IamMemberView extends jspb.Message {
getUserId(): string;
setUserId(value: string): void;
getRolesList(): Array<string>;
setRolesList(value: Array<string>): void;
clearRolesList(): void;
addRoles(value: string, index?: number): void;
getChangeDate(): google_protobuf_timestamp_pb.Timestamp | undefined;
setChangeDate(value?: google_protobuf_timestamp_pb.Timestamp): void;
hasChangeDate(): boolean;
clearChangeDate(): void;
getCreationDate(): google_protobuf_timestamp_pb.Timestamp | undefined;
setCreationDate(value?: google_protobuf_timestamp_pb.Timestamp): void;
hasCreationDate(): boolean;
clearCreationDate(): void;
getSequence(): number;
setSequence(value: number): void;
getUserName(): string;
setUserName(value: string): void;
getEmail(): string;
setEmail(value: string): void;
getFirstName(): string;
setFirstName(value: string): void;
getLastName(): string;
setLastName(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): IamMemberView.AsObject;
static toObject(includeInstance: boolean, msg: IamMemberView): IamMemberView.AsObject;
static serializeBinaryToWriter(message: IamMemberView, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): IamMemberView;
static deserializeBinaryFromReader(message: IamMemberView, reader: jspb.BinaryReader): IamMemberView;
}
export namespace IamMemberView {
export type AsObject = {
userId: string,
rolesList: Array<string>,
changeDate?: google_protobuf_timestamp_pb.Timestamp.AsObject,
creationDate?: google_protobuf_timestamp_pb.Timestamp.AsObject,
sequence: number,
userName: string,
email: string,
firstName: string,
lastName: string,
}
}
export class IamMemberSearchRequest extends jspb.Message {
getOffset(): number;
setOffset(value: number): void;
getLimit(): number;
setLimit(value: number): void;
getQueriesList(): Array<IamMemberSearchQuery>;
setQueriesList(value: Array<IamMemberSearchQuery>): void;
clearQueriesList(): void;
addQueries(value?: IamMemberSearchQuery, index?: number): IamMemberSearchQuery;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): IamMemberSearchRequest.AsObject;
static toObject(includeInstance: boolean, msg: IamMemberSearchRequest): IamMemberSearchRequest.AsObject;
static serializeBinaryToWriter(message: IamMemberSearchRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): IamMemberSearchRequest;
static deserializeBinaryFromReader(message: IamMemberSearchRequest, reader: jspb.BinaryReader): IamMemberSearchRequest;
}
export namespace IamMemberSearchRequest {
export type AsObject = {
offset: number,
limit: number,
queriesList: Array<IamMemberSearchQuery.AsObject>,
}
}
export class IamMemberSearchQuery extends jspb.Message {
getKey(): IamMemberSearchKey;
setKey(value: IamMemberSearchKey): void;
getMethod(): SearchMethod;
setMethod(value: SearchMethod): void;
getValue(): string;
setValue(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): IamMemberSearchQuery.AsObject;
static toObject(includeInstance: boolean, msg: IamMemberSearchQuery): IamMemberSearchQuery.AsObject;
static serializeBinaryToWriter(message: IamMemberSearchQuery, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): IamMemberSearchQuery;
static deserializeBinaryFromReader(message: IamMemberSearchQuery, reader: jspb.BinaryReader): IamMemberSearchQuery;
}
export namespace IamMemberSearchQuery {
export type AsObject = {
key: IamMemberSearchKey,
method: SearchMethod,
value: string,
}
}
export class FailedEventID extends jspb.Message {
getDatabase(): string;
setDatabase(value: string): void;
getViewName(): string;
setViewName(value: string): void;
getFailedSequence(): number;
setFailedSequence(value: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): FailedEventID.AsObject;
static toObject(includeInstance: boolean, msg: FailedEventID): FailedEventID.AsObject;
static serializeBinaryToWriter(message: FailedEventID, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): FailedEventID;
static deserializeBinaryFromReader(message: FailedEventID, reader: jspb.BinaryReader): FailedEventID;
}
export namespace FailedEventID {
export type AsObject = {
database: string,
viewName: string,
failedSequence: number,
}
}
export class FailedEvents extends jspb.Message {
getFailedEventsList(): Array<FailedEvent>;
setFailedEventsList(value: Array<FailedEvent>): void;
clearFailedEventsList(): void;
addFailedEvents(value?: FailedEvent, index?: number): FailedEvent;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): FailedEvents.AsObject;
static toObject(includeInstance: boolean, msg: FailedEvents): FailedEvents.AsObject;
static serializeBinaryToWriter(message: FailedEvents, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): FailedEvents;
static deserializeBinaryFromReader(message: FailedEvents, reader: jspb.BinaryReader): FailedEvents;
}
export namespace FailedEvents {
export type AsObject = {
failedEventsList: Array<FailedEvent.AsObject>,
}
}
export class FailedEvent extends jspb.Message {
getDatabase(): string;
setDatabase(value: string): void;
getViewName(): string;
setViewName(value: string): void;
getFailedSequence(): number;
setFailedSequence(value: number): void;
getFailureCount(): number;
setFailureCount(value: number): void;
getErrorMessage(): string;
setErrorMessage(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): FailedEvent.AsObject;
static toObject(includeInstance: boolean, msg: FailedEvent): FailedEvent.AsObject;
static serializeBinaryToWriter(message: FailedEvent, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): FailedEvent;
static deserializeBinaryFromReader(message: FailedEvent, reader: jspb.BinaryReader): FailedEvent;
}
export namespace FailedEvent {
export type AsObject = {
database: string,
viewName: string,
failedSequence: number,
failureCount: number,
errorMessage: string,
}
}
export class ViewID extends jspb.Message {
getDatabase(): string;
setDatabase(value: string): void;
getViewName(): string;
setViewName(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ViewID.AsObject;
static toObject(includeInstance: boolean, msg: ViewID): ViewID.AsObject;
static serializeBinaryToWriter(message: ViewID, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ViewID;
static deserializeBinaryFromReader(message: ViewID, reader: jspb.BinaryReader): ViewID;
}
export namespace ViewID {
export type AsObject = {
database: string,
viewName: string,
}
}
export class Views extends jspb.Message {
getViewsList(): Array<View>;
setViewsList(value: Array<View>): void;
clearViewsList(): void;
addViews(value?: View, index?: number): View;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Views.AsObject;
static toObject(includeInstance: boolean, msg: Views): Views.AsObject;
static serializeBinaryToWriter(message: Views, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Views;
static deserializeBinaryFromReader(message: Views, reader: jspb.BinaryReader): Views;
}
export namespace Views {
export type AsObject = {
viewsList: Array<View.AsObject>,
}
}
export class View extends jspb.Message {
getDatabase(): string;
setDatabase(value: string): void;
getViewName(): string;
setViewName(value: string): void;
getSequence(): number;
setSequence(value: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): View.AsObject;
static toObject(includeInstance: boolean, msg: View): View.AsObject;
static serializeBinaryToWriter(message: View, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): View;
static deserializeBinaryFromReader(message: View, reader: jspb.BinaryReader): View;
}
export namespace View {
export type AsObject = {
database: string,
viewName: string,
sequence: number,
}
}
export enum OrgState { export enum OrgState {
ORGSTATE_UNSPECIFIED = 0, ORGSTATE_UNSPECIFIED = 0,
ORGSTATE_ACTIVE = 1, ORGSTATE_ACTIVE = 1,
@ -577,3 +993,23 @@ export enum Gender {
GENDER_MALE = 2, GENDER_MALE = 2,
GENDER_DIVERSE = 3, GENDER_DIVERSE = 3,
} }
export enum IamMemberSearchKey {
IAMMEMBERSEARCHKEY_UNSPECIFIED = 0,
IAMMEMBERSEARCHKEY_FIRST_NAME = 1,
IAMMEMBERSEARCHKEY_LAST_NAME = 2,
IAMMEMBERSEARCHKEY_EMAIL = 3,
IAMMEMBERSEARCHKEY_USER_ID = 4,
}
export enum SearchMethod {
SEARCHMETHOD_EQUALS = 0,
SEARCHMETHOD_STARTS_WITH = 1,
SEARCHMETHOD_CONTAINS = 2,
SEARCHMETHOD_EQUALS_IGNORE_CASE = 3,
SEARCHMETHOD_STARTS_WITH_IGNORE_CASE = 4,
SEARCHMETHOD_CONTAINS_IGNORE_CASE = 5,
SEARCHMETHOD_NOT_EQUALS = 6,
SEARCHMETHOD_GREATER_THAN = 7,
SEARCHMETHOD_LESS_THAN = 8,
SEARCHMETHOD_IS_ONE_OF = 9,
SEARCHMETHOD_LIST_CONTAINS = 10,
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,24 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
import { Metadata } from 'grpc-web'; import { Metadata } from 'grpc-web';
import { AdminServicePromiseClient } from '../proto/generated/admin_grpc_web_pb'; import { AdminServicePromiseClient } from '../proto/generated/admin_grpc_web_pb';
import { CreateOrgRequest, CreateUserRequest, OrgSetUpRequest, OrgSetUpResponse } from '../proto/generated/admin_pb'; import {
AddIamMemberRequest,
CreateOrgRequest,
CreateUserRequest,
IamMember,
IamMemberRoles,
IamMemberSearchQuery,
IamMemberSearchRequest,
IamMemberSearchResponse,
OrgIamPolicy,
OrgIamPolicyID,
OrgIamPolicyRequest,
OrgSetUpRequest,
OrgSetUpResponse,
RemoveIamMemberRequest,
} from '../proto/generated/admin_pb';
import { GrpcBackendService } from './grpc-backend.service'; import { GrpcBackendService } from './grpc-backend.service';
import { GrpcService, RequestFactory, ResponseMapper } from './grpc.service'; import { GrpcService, RequestFactory, ResponseMapper } from './grpc.service';
@ -42,4 +58,114 @@ export class AdminService {
f => f, f => f,
); );
} }
public async GetIamMemberRoles(): Promise<IamMemberRoles> {
return await this.request(
c => c.getIamMemberRoles,
new Empty(),
f => f,
);
}
public async SearchIamMembers(
limit: number,
offset: number,
queryList?: IamMemberSearchQuery[],
): Promise<IamMemberSearchResponse> {
const req = new IamMemberSearchRequest();
req.setLimit(limit);
req.setOffset(offset);
if (queryList) {
req.setQueriesList(queryList);
}
return await this.request(
c => c.searchIamMembers,
req,
f => f,
);
}
public async RemoveIamMember(
userId: string,
): Promise<Empty> {
const req = new RemoveIamMemberRequest();
req.setUserId(userId);
return await this.request(
c => c.removeIamMember,
req,
f => f,
);
}
public async AddIamMember(
userId: string,
rolesList: string[],
): Promise<IamMember> {
const req = new AddIamMemberRequest();
req.setUserId(userId);
req.setRolesList(rolesList);
return await this.request(
c => c.addIamMember,
req,
f => f,
);
}
public async GetOrgIamPolicy(orgId: string): Promise<OrgIamPolicy> {
const req = new OrgIamPolicyID();
req.setOrgId(orgId);
return await this.request(
c => c.getOrgIamPolicy,
req,
f => f,
);
}
public async CreateOrgIamPolicy(
orgId: string,
description: string,
userLoginMustBeDomain: boolean): Promise<OrgIamPolicy> {
const req = new OrgIamPolicyRequest();
req.setOrgId(orgId);
req.setDescription(description);
req.setUserLoginMustBeDomain(userLoginMustBeDomain);
return await this.request(
c => c.createOrgIamPolicy,
req,
f => f,
);
}
public async UpdateOrgIamPolicy(
orgId: string,
description: string,
userLoginMustBeDomain: boolean): Promise<OrgIamPolicy> {
const req = new OrgIamPolicyRequest();
req.setOrgId(orgId);
req.setDescription(description);
req.setUserLoginMustBeDomain(userLoginMustBeDomain);
return await this.request(
c => c.updateOrgIamPolicy,
req,
f => f,
);
}
public async deleteOrgIamPolicy(
orgId: string,
): Promise<Empty> {
const req = new OrgIamPolicyID();
req.setOrgId(orgId);
return await this.request(
c => c.deleteOrgIamPolicy,
req,
f => f,
);
}
} }

View File

@ -297,6 +297,7 @@ export class AuthUserService {
userRoles = ['user.resourceowner']; userRoles = ['user.resourceowner'];
} }
this._roleCache = userRoles; this._roleCache = userRoles;
console.log(roles);
return of(this.hasRoles(userRoles, roles, each)); return of(this.hasRoles(userRoles, roles, each));
}), }),
); );

View File

@ -11,6 +11,8 @@ export abstract class StatehandlerService {
public abstract initStateHandler(): void; public abstract initStateHandler(): void;
} }
// TODO: Add Angular decorator.
// TODO: Add Angular decorator.
@Injectable() @Injectable()
export class StatehandlerServiceImpl export class StatehandlerServiceImpl
implements StatehandlerService, OnDestroy { implements StatehandlerService, OnDestroy {

View File

@ -14,10 +14,14 @@
"PROJECTS_BUTTON": "Projekt Übersicht", "PROJECTS_BUTTON": "Projekt Übersicht",
"USERS": "Totale Kontrolle und Überwachbarkeit", "USERS": "Totale Kontrolle und Überwachbarkeit",
"USERS_DESC": "Überwachen Sie Ihr Rollenkonzept in Echtzeit. Ergreifen Sie sofort Maßnahmen.", "USERS_DESC": "Überwachen Sie Ihr Rollenkonzept in Echtzeit. Ergreifen Sie sofort Maßnahmen.",
"USERS_BUTTON": "Benutzer anzeigen" "USERS_BUTTON": "Benutzer anzeigen",
"IAM": "Identity and Access Management",
"IAM_DESC": "Fügen Sie neue Organisationen hinzu oder editieren Sie Ihre IAM Manager",
"IAM_BUTTON": "Zitadel verwalten"
}, },
"MENU": { "MENU": {
"PERSONAL_INFO": "Persönliche Informationen", "PERSONAL_INFO": "Persönliche Informationen",
"IAM":"IAM",
"ORGANIZATION": "Organisation", "ORGANIZATION": "Organisation",
"PROJECT": "Projekte", "PROJECT": "Projekte",
"USER": "Benutzer", "USER": "Benutzer",
@ -189,6 +193,15 @@
"EDITACCOUNT":"Account bearbeiten", "EDITACCOUNT":"Account bearbeiten",
"ADDACCOUNT":"mit einem anderen Account anmelden" "ADDACCOUNT":"mit einem anderen Account anmelden"
}, },
"IAM": {
"DETAIL": {
"TITLE":"Identity and Access Management",
"DESCRIPTION":"This site is shows Zitadel Management preferences. You can make changes to views, manage managers and define global policies."
},
"MEMBER": {
"TITLE":"IAM Members"
}
},
"ORG": { "ORG": {
"PAGES": { "PAGES": {
"LIST": "Organisationen", "LIST": "Organisationen",
@ -241,6 +254,12 @@
"TITLECREATE":"Kennwortsperrrichtlinie festlegen", "TITLECREATE":"Kennwortsperrrichtlinie festlegen",
"DESCRIPTIONCREATE":"Kennwortwiederholungen sind im Standardmodus unendlich. Sie müssen diese Erweiterung installieren, wenn Sie Wiederholungsversuche anzeigen oder eine maximale Versuchsnummer festlegen möchten." "DESCRIPTIONCREATE":"Kennwortwiederholungen sind im Standardmodus unendlich. Sie müssen diese Erweiterung installieren, wenn Sie Wiederholungsversuche anzeigen oder eine maximale Versuchsnummer festlegen möchten."
}, },
"IAM_POLICY": {
"TITLE":"IAM Zugangseinstellungen",
"DESCRIPTION":"Definieren Sie Zugangseistellungen für Nutzer",
"TITLECREATE":"IAM Zugangseinstellungen festlegen",
"DESCRIPTIONCREATE":"Emails als Benutzername sind nicht erlaubt wenn UserLoginMustBeDomain gesetzt ist."
},
"BTN_INSTALL":"Installieren", "BTN_INSTALL":"Installieren",
"BTN_EDIT":"Modifizieren", "BTN_EDIT":"Modifizieren",
"DATA": { "DATA": {
@ -253,7 +272,8 @@
"SHOWLOCKOUTFAILURES":"Zeige Anzahl Anmeldeversuche", "SHOWLOCKOUTFAILURES":"Zeige Anzahl Anmeldeversuche",
"MAXATTEMPTS":"Maximale Anzahl an Versuchen", "MAXATTEMPTS":"Maximale Anzahl an Versuchen",
"EXPIREWARNDAYS":"Ablauf Warnung nach Tagen", "EXPIREWARNDAYS":"Ablauf Warnung nach Tagen",
"MAXAGEDAYS":"Maximale Gültigkeit in Tagen" "MAXAGEDAYS":"Maximale Gültigkeit in Tagen",
"USERLOGINMUSTBEDOMAIN":"User Login must be Domain"
}, },
"DELETE":"Richtlinie entfernen" "DELETE":"Richtlinie entfernen"
} }
@ -318,7 +338,7 @@
"MEMBER": { "MEMBER": {
"TITLE": "Manager", "TITLE": "Manager",
"TITLEDESC":"Manager können Änderungen an dieser Entität vornehmen", "TITLEDESC":"Manager können Änderungen an dieser Entität vornehmen",
"DESCRIPTION":"Hier finden Sie alle Mitwirkenden dieses Projekts. Sie können ein neues Mitglied hinzufügen und bestehende verwalten.", "DESCRIPTION":"Hier finden Sie alle Manager dieses Projekts. Sie können ein neues Mitglied hinzufügen und bestehende verwalten.",
"USERNAME": "Benutzername", "USERNAME": "Benutzername",
"FIRSTNAME": "Vorname", "FIRSTNAME": "Vorname",
"LASTNAME": "Nachname", "LASTNAME": "Nachname",
@ -432,7 +452,7 @@
"en": "Englisch" "en": "Englisch"
}, },
"MEMBER":{ "MEMBER":{
"ADD":"Mitwirkenden hinzufügen" "ADD":"Manager hinzufügen"
}, },
"ROLES": { "ROLES": {
"ORG_OWNER": "Org Owner", "ORG_OWNER": "Org Owner",
@ -502,6 +522,7 @@
"org.domain.added":"Org Domain hinzugefügt", "org.domain.added":"Org Domain hinzugefügt",
"org.domain.verified":"Org Domain verifiziert", "org.domain.verified":"Org Domain verifiziert",
"org.domain.primary.set":"Primäre domain gesetzt", "org.domain.primary.set":"Primäre domain gesetzt",
"org.domain.removed":"Org domain entfernt",
"org.iam.policy.added":"IAM Policy hinzugefügt", "org.iam.policy.added":"IAM Policy hinzugefügt",
"project.added": "Projekt erstellt", "project.added": "Projekt erstellt",
"project.changed":"Projekt editiert", "project.changed":"Projekt editiert",
@ -545,7 +566,8 @@
"user.password.check.succeeded":"Password check erfolgreich", "user.password.check.succeeded":"Password check erfolgreich",
"user.mfa.init.skipped":"MFA init übersprungen", "user.mfa.init.skipped":"MFA init übersprungen",
"user.mfa.otp.added":"MFA OTP hinzugefügt", "user.mfa.otp.added":"MFA OTP hinzugefügt",
"user.mfa.otp.verified":"MFA OTP verifiziert" "user.mfa.otp.verified":"MFA OTP verifiziert",
"user.selfregistered":"selbst registriert"
} }
} }
} }

View File

@ -14,10 +14,14 @@
"PROJECTS_BUTTON": "Project Overview", "PROJECTS_BUTTON": "Project Overview",
"USERS": "Total control and monitorability", "USERS": "Total control and monitorability",
"USERS_DESC": "Monitor your role concept in real time. Take immediate action.", "USERS_DESC": "Monitor your role concept in real time. Take immediate action.",
"USERS_BUTTON": "Show users" "USERS_BUTTON": "Show users",
"IAM": "Identity and Access Management",
"IAM_DESC": "Add new organizations or elect managers to do it for you.",
"IAM_BUTTON": "Manage Zitadel"
}, },
"MENU": { "MENU": {
"PERSONAL_INFO": "Personal Information", "PERSONAL_INFO": "Personal Information",
"IAM":"IAM",
"ORGANIZATION": "Organization", "ORGANIZATION": "Organization",
"PROJECT": "Projects", "PROJECT": "Projects",
"USER": "Users", "USER": "Users",
@ -189,6 +193,16 @@
"EDITACCOUNT":"Edit Account", "EDITACCOUNT":"Edit Account",
"ADDACCOUNT":"log in with another account" "ADDACCOUNT":"log in with another account"
}, },
"IAM": {
"DETAIL": {
"TITLE":"Identity and Access Management",
"DESCRIPTION":"This site is shows Zitadel Management preferences. You can make changes to views, elect managers and define global policies."
},
"MEMBER": {
"TITLE":"Managers",
"DESCRIPTION":"Managers can add and edit organizations and make changes to their corresponding projects and apps"
}
},
"ORG": { "ORG": {
"PAGES": { "PAGES": {
"LIST": "Organizations", "LIST": "Organizations",
@ -218,7 +232,7 @@
}, },
"POLICY": { "POLICY": {
"TITLE":"Explore Policies", "TITLE":"Explore Policies",
"DESCRIPTION":"Pre-packaged solutions that save you time and ensure security", "DESCRIPTION":"Pre-packaged solutions that to ensure security of your data",
"PWD_COMPLEXITY": { "PWD_COMPLEXITY": {
"TITLE":"Password Complexity", "TITLE":"Password Complexity",
"DESCRIPTION":"Ensures that all set passwords correspond to a specific pattern", "DESCRIPTION":"Ensures that all set passwords correspond to a specific pattern",
@ -241,7 +255,13 @@
"TITLECREATE":"Create Password Lockout Policy", "TITLECREATE":"Create Password Lockout Policy",
"DESCRIPTIONCREATE":"Password retries are infinite in default mode. You have to install this extention if you want to show retries or set a max try number." "DESCRIPTIONCREATE":"Password retries are infinite in default mode. You have to install this extention if you want to show retries or set a max try number."
}, },
"BTN_INSTALL":"Install", "IAM_POLICY": {
"TITLE":"IAM Access Preferences",
"DESCRIPTION":"Define access properties of your users",
"TITLECREATE":"Set IAM Access preferences",
"DESCRIPTIONCREATE":"Emails as username is not allowed for enabled UserLoginMustBeDomain"
},
"BTN_INSTALL":"Setup",
"BTN_EDIT":"Modify", "BTN_EDIT":"Modify",
"DATA": { "DATA": {
"DESCRIPTION":"Description", "DESCRIPTION":"Description",
@ -253,7 +273,8 @@
"SHOWLOCKOUTFAILURES":"show lockout failures", "SHOWLOCKOUTFAILURES":"show lockout failures",
"MAXATTEMPTS":"Max Attempts", "MAXATTEMPTS":"Max Attempts",
"EXPIREWARNDAYS":"Expiration Warning after day", "EXPIREWARNDAYS":"Expiration Warning after day",
"MAXAGEDAYS":"Max Age in days" "MAXAGEDAYS":"Max Age in days",
"USERLOGINMUSTBEDOMAIN":"User Login must be Domain"
}, },
"DELETE":"Uninstall Policy" "DELETE":"Uninstall Policy"
} }
@ -432,7 +453,7 @@
"en": "English" "en": "English"
}, },
"MEMBER":{ "MEMBER":{
"ADD":"Add a contributor" "ADD":"Add a manager"
}, },
"ROLES": { "ROLES": {
"ORG_OWNER": "Org Owner", "ORG_OWNER": "Org Owner",
@ -495,23 +516,24 @@
"DESCRIPTION":"Here you can see the latest events that have affected a userchange" "DESCRIPTION":"Here you can see the latest events that have affected a userchange"
}, },
"EVENTS": { "EVENTS": {
"org.added": "Org created", "org.added": "created",
"org.changed":"Org edited", "org.changed":"changed",
"org.member.added":"Org Member created", "org.member.added":"member created",
"org.member.removed":"Org Member deleted", "org.member.removed":"member deleted",
"org.domain.added":"Org Domain added", "org.domain.added":"domain added",
"org.domain.verified":"Org Domain verified", "org.domain.verified":"domain verified",
"org.domain.primary.set":"Primary domain set", "org.domain.primary.set":"set primary domain",
"org.iam.policy.added":"IAM Policy added", "org.domain.removed":"domain removed",
"project.added": "Project created", "org.iam.policy.added":"IAM policy added",
"project.changed":"Project edited", "project.added": "added",
"project.deactivated":"Project deactivated", "project.changed":"changed",
"project.member.added":"Project Member created", "project.deactivated":"deactivated",
"project.member.removed":"Project Member deleted", "project.member.added":"added member",
"project.role.added":"Role created", "project.member.removed":"member deleted",
"project.role.removed":"Role deleted", "project.role.added":"created role",
"project.application.added":"App added", "project.role.removed":"deleted role",
"project.application.config.oidc.added":"OIDC config added", "project.application.added":"added app",
"project.application.config.oidc.added":"added OIDC config",
"user.added":"User added", "user.added":"User added",
"user.initialization.code.added":"Init code added", "user.initialization.code.added":"Init code added",
"user.phone.code.added":"Phone code added", "user.phone.code.added":"Phone code added",
@ -545,7 +567,8 @@
"user.password.check.succeeded":"Password check succeeded", "user.password.check.succeeded":"Password check succeeded",
"user.mfa.init.skipped":"MFA init skipped", "user.mfa.init.skipped":"MFA init skipped",
"user.mfa.otp.added":"MFA OTP added", "user.mfa.otp.added":"MFA OTP added",
"user.mfa.otp.verified":"MFA OTP verified" "user.mfa.otp.verified":"MFA OTP verified",
"user.selfregistered":"self registered"
} }
} }
} }

View File

@ -1,4 +1,4 @@
import 'hammerjs';
import { enableProdMode } from '@angular/core'; import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

View File

@ -1,5 +1,5 @@
{ {
"extends": "./tsconfig.json", "extends": "./tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/app", "outDir": "./out-tsc/app",
"types": [] "types": []

View File

@ -0,0 +1,22 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": ["node_modules/@types"],
"lib": ["es2018", "dom"],
"strict": true
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}

View File

@ -1,22 +1,17 @@
/*
This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScripts language server to improve development experience.
It is not intended to be used to perform a compilation.
To learn more about this file see: https://angular.io/config/solution-tsconfig.
*/
{ {
"compileOnSave": false, "files": [],
"compilerOptions": { "references": [
"baseUrl": "./", {
"outDir": "./dist/out-tsc", "path": "./tsconfig.app.json"
"sourceMap": true, },
"declaration": false, {
"downlevelIteration": true, "path": "./tsconfig.spec.json"
"experimentalDecorators": true, }
"module": "esnext", ]
"moduleResolution": "node", }
"importHelpers": true,
"target": "es2015",
"typeRoots": ["node_modules/@types"],
"lib": ["es2018", "dom"],
"strict": true
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}

View File

@ -1,5 +1,5 @@
{ {
"extends": "./tsconfig.json", "extends": "./tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/spec", "outDir": "./out-tsc/spec",
"types": [ "types": [

2
go.mod
View File

@ -23,8 +23,8 @@ require (
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/mock v1.4.3 github.com/golang/mock v1.4.3
github.com/gorilla/csrf v1.7.0
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/gorilla/csrf v1.7.0
github.com/gorilla/mux v1.7.4 github.com/gorilla/mux v1.7.4
github.com/gorilla/schema v1.1.0 github.com/gorilla/schema v1.1.0
github.com/gorilla/securecookie v1.1.1 github.com/gorilla/securecookie v1.1.1