fix(console): simplify instance page (#7274)

* move settings, rm nav for single org

* move instance pages to settings

* i18n

* revalidate orgs on create

* Update bg.json

* show custome portal link

* Update console/src/app/modules/settings-list/settings.ts

Co-authored-by: Livio Spring <livio.a@gmail.com>

* Update console/src/app/modules/settings-list/settings.ts

Co-authored-by: Livio Spring <livio.a@gmail.com>

* Update console/src/app/modules/settings-list/settings.ts

Co-authored-by: Livio Spring <livio.a@gmail.com>

* add org page to instance settings

* iam.read for org list

* i18n

* instance imgs, cleanup

* rm unused imgs

* remove unused imgs, replace default settings imgs

* event image

* e2e url

* instance url

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Max Peintner
2024-02-06 14:35:43 +01:00
committed by GitHub
parent 7f7fb55f34
commit ca49e0f532
178 changed files with 598 additions and 729 deletions

View File

@@ -125,39 +125,6 @@ const routes: Routes = [
}, },
], ],
}, },
{
path: 'failed-events',
loadChildren: () => import('./pages/failed-events/failed-events.module'),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['iam.read'],
},
},
{
path: 'views',
loadChildren: () => import('./pages/iam-views/iam-views.module'),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['iam.read'],
},
},
{
path: 'events',
loadChildren: () => import('./pages/events/events.module'),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['iam.read'],
},
},
{
path: 'settings',
loadChildren: () => import('./pages/instance-settings/instance-settings.module'),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['iam.read', 'iam.policy.read'],
requiresAll: true,
},
},
{ {
path: 'org-settings', path: 'org-settings',
loadChildren: () => import('./pages/org-settings/org-settings.module'), loadChildren: () => import('./pages/org-settings/org-settings.module'),

View File

@@ -0,0 +1,129 @@
<h2>{{ 'IAM.EVENTS.TITLE' | translate }}</h2>
<p class="events-desc cnsl-secondary-text">{{ 'IAM.EVENTS.DESCRIPTION' | translate }}</p>
<cnsl-refresh-table
[hideRefresh]="true"
(refreshed)="refresh()"
[dataSize]="dataSource.data.length"
[loading]="_loading | async"
>
<div actions>
<cnsl-filter-events (requestChanged)="filterChanged($event)"></cnsl-filter-events>
</div>
<table
[dataSource]="dataSource"
mat-table
class="table views-table"
aria-label="Views"
matSort
(matSortChange)="sortChange($event)"
>
<ng-container matColumnDef="editor">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.EDITOR' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<div class="editor-row" *ngIf="event.editor as editor">
<!-- <cnsl-avatar
*ngIf="editor && editor.displayName; else cog"
class="avatar"
[name]="editor.displayName"
[avatarUrl]="editor.avatarUrl || ''"
[forColor]="editor.preferredLoginName ?? editor.displayName"
[size]="32"
>
</cnsl-avatar>
<ng-template #cog>
<cnsl-avatar [forColor]="editor?.preferredLoginName ?? 'franz'" [isMachine]="true">
<i class="las la-robot"></i>
</cnsl-avatar> </ng-template
> -->
<span class="name" *ngIf="editor.displayName">{{ editor.displayName }}</span>
<span class="state" *ngIf="editor.service">{{ editor.service }}</span>
</div>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="aggregate">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.AGGREGATE' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<div class="aggregate-row">
<span class="id">{{ event.aggregate.id }}</span
><span class="state" *ngIf="event.aggregate?.type?.localized?.localizedMessage">{{
event.aggregate.type.localized.localizedMessage
}}</span>
</div>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="resourceOwner">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.RESOURCEOWNER' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<span *ngIf="event.aggregate.resourceOwner">{{ event.aggregate.resourceOwner }}</span>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="sequence">
<th mat-header-cell *matHeaderCellDef>
{{ 'IAM.EVENTS.SEQUENCE' | translate }}
</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
{{ event.sequence }}
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header [start]="'desc'" [disableClear]="true">
{{ 'IAM.EVENTS.CREATIONDATE' | translate }}
</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<span>{{ event?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm:ss' }}</span>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.TYPE' | translate }}</th>
<td mat-cell *matCellDef="let event" data-e2e="event-type-cell">
<ng-container *ngIf="event | toobject as event">
<span *ngIf="event.type?.localized?.localizedMessage">{{ event.type.localized.localizedMessage }}</span>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="payload">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.PAYLOAD' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | topayload as payload">
<span>{{ payload | json }}</span>
<div class="btn-wrapper">
<button class="open-in-dialog-btn" mat-icon-button (click)="openDialog(event)">
<mat-icon svgIcon="mdi_arrow_expand"></mat-icon>
</button>
</div>
</ng-container>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<cnsl-paginator
#paginator
class="paginator"
[hidePagination]="true"
[showMoreButton]="true"
[disableShowMore]="_done | async"
(moreRequested)="more()"
[length]="dataSource.data.length"
>
</cnsl-paginator>
</cnsl-refresh-table>

View File

@@ -61,10 +61,6 @@
} }
} }
.events-title {
margin: 2rem 0 0 0;
}
.events-desc { .events-desc {
font-size: 14px; font-size: 14px;
} }

View File

@@ -26,13 +26,11 @@ import { DisplayJsonDialogModule } from 'src/app/modules/display-json-dialog/dis
import { FilterEventsModule } from 'src/app/modules/filter-events/filter-events.module'; import { FilterEventsModule } from 'src/app/modules/filter-events/filter-events.module';
import { ToObjectPipeModule } from 'src/app/pipes/to-object/to-object.module'; import { ToObjectPipeModule } from 'src/app/pipes/to-object/to-object.module';
import { ToPayloadPipeModule } from 'src/app/pipes/to-payload/to-payload.module'; import { ToPayloadPipeModule } from 'src/app/pipes/to-payload/to-payload.module';
import { EventsRoutingModule } from './events-routing.module';
import { EventsComponent } from './events.component'; import { EventsComponent } from './events.component';
@NgModule({ @NgModule({
declarations: [EventsComponent], declarations: [EventsComponent],
imports: [ imports: [
EventsRoutingModule,
CommonModule, CommonModule,
TableActionsModule, TableActionsModule,
MatIconModule, MatIconModule,
@@ -60,6 +58,6 @@ import { EventsComponent } from './events.component';
MatSortModule, MatSortModule,
OverlayModule, OverlayModule,
], ],
exports: [], exports: [EventsComponent],
}) })
export default class IamViewsModule {} export default class EventsModule {}

View File

@@ -0,0 +1,68 @@
<h2>{{ 'IAM.FAILEDEVENTS.TITLE' | translate }}</h2>
<p class="failed-events-desc cnsl-secondary-text">{{ 'IAM.FAILEDEVENTS.DESCRIPTION' | translate }}</p>
<div class="table-wrapper">
<cnsl-refresh-table (refreshed)="loadEvents()" [dataSize]="eventDataSource.data.length" [loading]="loading$ | async">
<table [dataSource]="eventDataSource" mat-table class="table" aria-label="Elements">
<ng-container matColumnDef="viewName">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.VIEWNAME' | translate }}</th>
<td mat-cell *matCellDef="let event">{{ event.viewName }}</td>
</ng-container>
<ng-container matColumnDef="database">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.DATABASE' | translate }}</th>
<td mat-cell *matCellDef="let event">{{ event.database }}</td>
</ng-container>
<ng-container matColumnDef="failedSequence">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.FAILEDSEQUENCE' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span>{{ event?.failedSequence }}</span>
</td>
</ng-container>
<ng-container matColumnDef="failureCount">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.FAILURECOUNT' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span>{{ event?.failureCount }}</span>
</td>
</ng-container>
<ng-container matColumnDef="lastFailed">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.LASTFAILED' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span>{{ event?.lastFailed | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="errorMessage">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.ERRORMESSAGE' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span class="failed-event-error-message">{{ event?.errorMessage }}</span>
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td class="back" mat-cell *matCellDef="let event">
<cnsl-table-actions>
<button
actions
color="warn"
mat-icon-button
matTooltip="{{ 'IAM.FAILEDEVENTS.DELETE' | translate }}"
(click)="cancelEvent(event.viewName, event.database, event.failedSequence)"
>
<i class="las la-minus-circle"></i>
</button>
</cnsl-table-actions>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="eventDisplayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: eventDisplayedColumns"></tr>
</table>
<cnsl-paginator #paginator class="paginator" [hidePagination]="true" [length]="eventDataSource.data.length || 0">
</cnsl-paginator>
</cnsl-refresh-table>
</div>

View File

@@ -1,7 +1,3 @@
.failed-events-title {
margin: 2rem 0 0 0;
}
.failed-events-desc { .failed-events-desc {
font-size: 14px; font-size: 14px;
} }

View File

@@ -18,13 +18,11 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module'; import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { FailedEventsRoutingModule } from './failed-events-routing.module';
import { FailedEventsComponent } from './failed-events.component'; import { FailedEventsComponent } from './failed-events.component';
@NgModule({ @NgModule({
declarations: [FailedEventsComponent], declarations: [FailedEventsComponent],
imports: [ imports: [
FailedEventsRoutingModule,
CommonModule, CommonModule,
TableActionsModule, TableActionsModule,
MatIconModule, MatIconModule,
@@ -44,5 +42,6 @@ import { FailedEventsComponent } from './failed-events.component';
MatTableModule, MatTableModule,
MatSortModule, MatSortModule,
], ],
exports: [FailedEventsComponent],
}) })
export default class FailedEventsModule {} export default class FailedEventsModule {}

View File

@@ -0,0 +1,40 @@
<h2>{{ 'IAM.VIEWS.TITLE' | translate }}</h2>
<p class="views-desc cnsl-secondary-text">{{ 'IAM.VIEWS.DESCRIPTION' | translate }}</p>
<cnsl-refresh-table (refreshed)="loadViews()" [dataSize]="dataSource.data.length" [loading]="loading$ | async">
<table [dataSource]="dataSource" mat-table class="table views-table" aria-label="Views" matSort>
<ng-container matColumnDef="viewName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IAM.VIEWS.VIEWNAME' | translate }}</th>
<td mat-cell *matCellDef="let view">{{ view.viewName }}</td>
</ng-container>
<ng-container matColumnDef="database">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IAM.VIEWS.DATABASE' | translate }}</th>
<td mat-cell *matCellDef="let view">{{ view.database }}</td>
</ng-container>
<ng-container matColumnDef="sequence">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.VIEWS.SEQUENCE' | translate }}</th>
<td mat-cell *matCellDef="let view">{{ view.processedSequence }}</td>
</ng-container>
<ng-container matColumnDef="eventTimestamp">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.VIEWS.EVENTTIMESTAMP' | translate }}</th>
<td mat-cell *matCellDef="let view">
<span>{{ view?.eventTimestamp | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="lastSuccessfulSpoolerRun">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.VIEWS.LASTSPOOL' | translate }}</th>
<td mat-cell *matCellDef="let view">
<span>{{ view?.lastSuccessfulSpoolerRun | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<cnsl-paginator #paginator class="paginator" [hidePagination]="true" [length]="dataSource.data.length || 0">
</cnsl-paginator>
</cnsl-refresh-table>

View File

@@ -0,0 +1,3 @@
.views-desc {
font-size: 14px;
}

View File

@@ -18,13 +18,11 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module'; import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { IamViewsRoutingModule } from './iam-views-routing.module';
import { IamViewsComponent } from './iam-views.component'; import { IamViewsComponent } from './iam-views.component';
@NgModule({ @NgModule({
declarations: [IamViewsComponent], declarations: [IamViewsComponent],
imports: [ imports: [
IamViewsRoutingModule,
CommonModule, CommonModule,
TableActionsModule, TableActionsModule,
MatIconModule, MatIconModule,
@@ -44,6 +42,6 @@ import { IamViewsComponent } from './iam-views.component';
MatTableModule, MatTableModule,
MatSortModule, MatSortModule,
], ],
exports: [], exports: [IamViewsComponent],
}) })
export default class IamViewsModule {} export default class IamViewsModule {}

View File

@@ -7,99 +7,12 @@
*ngIf=" *ngIf="
breadc[breadc.length - 1] && breadc[breadc.length - 1] &&
!breadc[breadc.length - 1].hideNav && !breadc[breadc.length - 1].hideNav &&
breadc[breadc.length - 1].type !== BreadcrumbType.AUTHUSER breadc[breadc.length - 1].type !== BreadcrumbType.AUTHUSER &&
breadc[breadc.length - 1].type !== BreadcrumbType.INSTANCE
" "
[ngSwitch]="breadc[0].type" [ngSwitch]="breadc[0].type"
> >
<div class="nav-row" @navrow> <div class="nav-row" @navrow>
<ng-container *ngSwitchCase="BreadcrumbType.INSTANCE">
<div class="nav-row-abs" @navroworg>
<ng-template cnslHasRole [hasRole]="['iam.read']">
<a
class="nav-item"
[routerLinkActiveOptions]="{ exact: false }"
[routerLinkActive]="['active']"
[routerLink]="['/instance']"
>
<div class="c_label">
<span> {{ 'MENU.INSTANCEOVERVIEW' | translate }} </span>
</div>
</a>
<a
class="nav-item"
[routerLinkActiveOptions]="{ exact: false }"
[routerLinkActive]="['active']"
[routerLink]="['/orgs']"
>
<div class="c_label">
<span> {{ 'MENU.ORGS' | translate }} </span>
</div>
</a>
<a
class="nav-item"
[routerLinkActiveOptions]="{ exact: false }"
[routerLinkActive]="['active']"
[routerLink]="['/events']"
>
<div class="c_label">
<span> {{ 'MENU.EVENTS' | translate }} </span>
</div>
</a>
<a
class="nav-item"
[routerLinkActiveOptions]="{ exact: false }"
[routerLinkActive]="['active']"
[routerLink]="['/views']"
>
<div class="c_label">
<span> {{ 'MENU.VIEWS' | translate }} </span>
</div>
</a>
<a
class="nav-item"
[routerLinkActiveOptions]="{ exact: false }"
[routerLinkActive]="['active']"
[routerLink]="['/failed-events']"
>
<div class="c_label">
<span> {{ 'MENU.FAILEDEVENTS' | translate }} </span>
</div>
</a>
<a
class="nav-item"
[routerLinkActiveOptions]="{ exact: false }"
[routerLinkActive]="['active']"
[routerLink]="['/settings']"
*ngIf="['iam.read', 'iam.policy.read'] | hasRole: true | async"
>
<div class="c_label">
<span> {{ 'MENU.SETTINGS' | translate }} </span>
</div>
</a>
<a
*ngIf="customerPortalLink$ | async as customerPortalLink"
class="nav-item external-link"
[href]="customerPortalLink"
target="_blank"
rel="noreferrer"
>
<div class="c_label">
<span> {{ 'MENU.CUSTOMERPORTAL' | translate }} </span>
</div>
<i class="las la-external-link-alt"></i>
</a>
</ng-template>
<template [ngTemplateOutlet]="shortcutKeyRef"></template>
</div>
</ng-container>
<ng-container *ngSwitchCase="BreadcrumbType.ORG"> <ng-container *ngSwitchCase="BreadcrumbType.ORG">
<div class="nav-row-abs" @navrowproject> <div class="nav-row-abs" @navrowproject>
<a <a
@@ -183,7 +96,7 @@
[routerLinkActive]="['active']" [routerLinkActive]="['active']"
[routerLinkActiveOptions]="{ exact: false }" [routerLinkActiveOptions]="{ exact: false }"
[routerLink]="['/org-settings']" [routerLink]="['/org-settings']"
*ngIf="['policy.read'] | hasRole | async" *ngIf="(['policy.read'] | hasRole | async) && ((authService.cachedOrgs | async)?.length ?? 1) > 1"
> >
<span class="label">{{ 'MENU.SETTINGS' | translate }}</span> <span class="label">{{ 'MENU.SETTINGS' | translate }}</span>
</a> </a>

View File

@@ -90,7 +90,6 @@ export class NavComponent implements OnDestroy {
private destroy$: Subject<void> = new Subject(); private destroy$: Subject<void> = new Subject();
public BreadcrumbType: any = BreadcrumbType; public BreadcrumbType: any = BreadcrumbType;
public customerPortalLink$ = this.envService.env.pipe(map((env) => env.customer_portal));
public positions: ConnectedPosition[] = [ public positions: ConnectedPosition[] = [
new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }, 0, 10), new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }, 0, 10),
@@ -98,7 +97,6 @@ export class NavComponent implements OnDestroy {
]; ];
constructor( constructor(
private envService: EnvironmentService,
public authService: GrpcAuthService, public authService: GrpcAuthService,
public adminService: AdminService, public adminService: AdminService,
public authenticationService: AuthenticationService, public authenticationService: AuthenticationService,

View File

@@ -6,6 +6,12 @@
[settingsList]="settingsList" [settingsList]="settingsList"
queryParam="id" queryParam="id"
> >
<ng-container *ngIf="currentSetting === 'organizations'">
<h2>{{ 'ORG.PAGES.LIST' | translate }}</h2>
<p class="org-desc cnsl-secondary-text">{{ 'ORG.PAGES.LISTDESCRIPTION' | translate }}</p>
<cnsl-org-table></cnsl-org-table>
</ng-container>
<ng-container *ngIf="currentSetting === 'complexity'"> <ng-container *ngIf="currentSetting === 'complexity'">
<cnsl-password-complexity-policy [serviceType]="serviceType"></cnsl-password-complexity-policy> <cnsl-password-complexity-policy [serviceType]="serviceType"></cnsl-password-complexity-policy>
</ng-container> </ng-container>
@@ -57,4 +63,13 @@
<ng-container *ngIf="currentSetting === 'languages' && serviceType === PolicyComponentServiceType.ADMIN"> <ng-container *ngIf="currentSetting === 'languages' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-language-settings></cnsl-language-settings> <cnsl-language-settings></cnsl-language-settings>
</ng-container> </ng-container>
<ng-container *ngIf="currentSetting === 'views' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-iam-views></cnsl-iam-views>
</ng-container>
<ng-container *ngIf="currentSetting === 'events' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-events></cnsl-events>
</ng-container>
<ng-container *ngIf="currentSetting === 'failedevents' && serviceType === PolicyComponentServiceType.ADMIN">
<cnsl-iam-failed-events></cnsl-iam-failed-events>
</ng-container>
</cnsl-sidenav> </cnsl-sidenav>

View File

@@ -24,6 +24,11 @@ import { SecretGeneratorModule } from '../policies/secret-generator/secret-gener
import { SecurityPolicyModule } from '../policies/security-policy/security-policy.module'; import { SecurityPolicyModule } from '../policies/security-policy/security-policy.module';
import { SidenavModule } from '../sidenav/sidenav.module'; import { SidenavModule } from '../sidenav/sidenav.module';
import { SettingsListComponent } from './settings-list.component'; import { SettingsListComponent } from './settings-list.component';
import FailedEventsModule from '../failed-events/failed-events.module';
import IamViewsModule from '../iam-views/iam-views.module';
import EventsModule from '../events/events.module';
import OrgListModule from 'src/app/pages/org-list/org-list.module';
import { OrgTableModule } from '../org-table/org-table.module';
@NgModule({ @NgModule({
declarations: [SettingsListComponent], declarations: [SettingsListComponent],
@@ -44,6 +49,7 @@ import { SettingsListComponent } from './settings-list.component';
SecurityPolicyModule, SecurityPolicyModule,
DomainsModule, DomainsModule,
LoginTextsPolicyModule, LoginTextsPolicyModule,
OrgTableModule,
DomainPolicyModule, DomainPolicyModule,
TranslateModule, TranslateModule,
HasRolePipeModule, HasRolePipeModule,
@@ -51,6 +57,9 @@ import { SettingsListComponent } from './settings-list.component';
NotificationSMSProviderModule, NotificationSMSProviderModule,
OIDCConfigurationModule, OIDCConfigurationModule,
SecretGeneratorModule, SecretGeneratorModule,
FailedEventsModule,
IamViewsModule,
EventsModule,
], ],
exports: [SettingsListComponent], exports: [SettingsListComponent],
}) })

View File

@@ -1,6 +1,15 @@
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import { SidenavSetting } from '../sidenav/sidenav.component'; import { SidenavSetting } from '../sidenav/sidenav.component';
export const ORGANIZATIONS: SidenavSetting = {
id: 'organizations',
i18nKey: 'SETTINGS.LIST.ORGS',
groupI18nKey: 'SETTINGS.GROUPS.GENERAL',
requiredRoles: {
[PolicyComponentServiceType.ADMIN]: ['iam.read'],
},
};
export const LANGUAGES: SidenavSetting = { export const LANGUAGES: SidenavSetting = {
id: 'languages', id: 'languages',
i18nKey: 'SETTINGS.LIST.LANGUAGES', i18nKey: 'SETTINGS.LIST.LANGUAGES',
@@ -33,6 +42,33 @@ export const SECURITY: SidenavSetting = {
}, },
}; };
export const VIEWS: SidenavSetting = {
id: 'views',
i18nKey: 'SETTINGS.LIST.VIEWS',
groupI18nKey: 'SETTINGS.GROUPS.STORAGE',
requiredRoles: {
[PolicyComponentServiceType.ADMIN]: ['iam.read'],
},
};
export const FAILEDEVENTS: SidenavSetting = {
id: 'failedevents',
i18nKey: 'SETTINGS.LIST.FAILEDEVENTS',
groupI18nKey: 'SETTINGS.GROUPS.STORAGE',
requiredRoles: {
[PolicyComponentServiceType.ADMIN]: ['iam.read'],
},
};
export const EVENTS: SidenavSetting = {
id: 'events',
i18nKey: 'SETTINGS.LIST.EVENTS',
groupI18nKey: 'SETTINGS.GROUPS.STORAGE',
requiredRoles: {
[PolicyComponentServiceType.ADMIN]: ['events.read'],
},
};
export const LOGIN: SidenavSetting = { export const LOGIN: SidenavSetting = {
id: 'login', id: 'login',
i18nKey: 'SETTINGS.LIST.LOGIN', i18nKey: 'SETTINGS.LIST.LOGIN',

View File

@@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EventsComponent } from './events.component';
const routes: Routes = [
{
path: '',
component: EventsComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class EventsRoutingModule {}

View File

@@ -1,131 +0,0 @@
<div class="max-width-container">
<h1 class="events-title">{{ 'IAM.EVENTS.TITLE' | translate }}</h1>
<p class="events-desc cnsl-secondary-text">{{ 'IAM.EVENTS.DESCRIPTION' | translate }}</p>
<cnsl-refresh-table
[hideRefresh]="true"
(refreshed)="refresh()"
[dataSize]="dataSource.data.length"
[loading]="_loading | async"
>
<div actions>
<cnsl-filter-events (requestChanged)="filterChanged($event)"></cnsl-filter-events>
</div>
<table
[dataSource]="dataSource"
mat-table
class="table views-table"
aria-label="Views"
matSort
(matSortChange)="sortChange($event)"
>
<ng-container matColumnDef="editor">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.EDITOR' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<div class="editor-row" *ngIf="event.editor as editor">
<!-- <cnsl-avatar
*ngIf="editor && editor.displayName; else cog"
class="avatar"
[name]="editor.displayName"
[avatarUrl]="editor.avatarUrl || ''"
[forColor]="editor.preferredLoginName ?? editor.displayName"
[size]="32"
>
</cnsl-avatar>
<ng-template #cog>
<cnsl-avatar [forColor]="editor?.preferredLoginName ?? 'franz'" [isMachine]="true">
<i class="las la-robot"></i>
</cnsl-avatar> </ng-template
> -->
<span class="name" *ngIf="editor.displayName">{{ editor.displayName }}</span>
<span class="state" *ngIf="editor.service">{{ editor.service }}</span>
</div>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="aggregate">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.AGGREGATE' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<div class="aggregate-row">
<span class="id">{{ event.aggregate.id }}</span
><span class="state" *ngIf="event.aggregate?.type?.localized?.localizedMessage">{{
event.aggregate.type.localized.localizedMessage
}}</span>
</div>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="resourceOwner">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.RESOURCEOWNER' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<span *ngIf="event.aggregate.resourceOwner">{{ event.aggregate.resourceOwner }}</span>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="sequence">
<th mat-header-cell *matHeaderCellDef>
{{ 'IAM.EVENTS.SEQUENCE' | translate }}
</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
{{ event.sequence }}
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header [start]="'desc'" [disableClear]="true">
{{ 'IAM.EVENTS.CREATIONDATE' | translate }}
</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | toobject as event">
<span>{{ event?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm:ss' }}</span>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.TYPE' | translate }}</th>
<td mat-cell *matCellDef="let event" data-e2e="event-type-cell">
<ng-container *ngIf="event | toobject as event">
<span *ngIf="event.type?.localized?.localizedMessage">{{ event.type.localized.localizedMessage }}</span>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="payload">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.EVENTS.PAYLOAD' | translate }}</th>
<td mat-cell *matCellDef="let event">
<ng-container *ngIf="event | topayload as payload">
<span>{{ payload | json }}</span>
<div class="btn-wrapper">
<button class="open-in-dialog-btn" mat-icon-button (click)="openDialog(event)">
<mat-icon svgIcon="mdi_arrow_expand"></mat-icon>
</button>
</div>
</ng-container>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<cnsl-paginator
#paginator
class="paginator"
[hidePagination]="true"
[showMoreButton]="true"
[disableShowMore]="_done | async"
(moreRequested)="more()"
[length]="dataSource.data.length"
>
</cnsl-paginator>
</cnsl-refresh-table>
</div>

View File

@@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { FailedEventsComponent } from './failed-events.component';
const routes: Routes = [
{
path: '',
component: FailedEventsComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class FailedEventsRoutingModule {}

View File

@@ -1,70 +0,0 @@
<div class="max-width-container">
<h1 class="failed-events-title">{{ 'IAM.FAILEDEVENTS.TITLE' | translate }}</h1>
<p class="failed-events-desc cnsl-secondary-text">{{ 'IAM.FAILEDEVENTS.DESCRIPTION' | translate }}</p>
<div class="table-wrapper">
<cnsl-refresh-table (refreshed)="loadEvents()" [dataSize]="eventDataSource.data.length" [loading]="loading$ | async">
<table [dataSource]="eventDataSource" mat-table class="table" aria-label="Elements">
<ng-container matColumnDef="viewName">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.VIEWNAME' | translate }}</th>
<td mat-cell *matCellDef="let event">{{ event.viewName }}</td>
</ng-container>
<ng-container matColumnDef="database">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.DATABASE' | translate }}</th>
<td mat-cell *matCellDef="let event">{{ event.database }}</td>
</ng-container>
<ng-container matColumnDef="failedSequence">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.FAILEDSEQUENCE' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span>{{ event?.failedSequence }}</span>
</td>
</ng-container>
<ng-container matColumnDef="failureCount">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.FAILURECOUNT' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span>{{ event?.failureCount }}</span>
</td>
</ng-container>
<ng-container matColumnDef="lastFailed">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.LASTFAILED' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span>{{ event?.lastFailed | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="errorMessage">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.FAILEDEVENTS.ERRORMESSAGE' | translate }}</th>
<td mat-cell *matCellDef="let event">
<span class="failed-event-error-message">{{ event?.errorMessage }}</span>
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td class="back" mat-cell *matCellDef="let event">
<cnsl-table-actions>
<button
actions
color="warn"
mat-icon-button
matTooltip="{{ 'IAM.FAILEDEVENTS.DELETE' | translate }}"
(click)="cancelEvent(event.viewName, event.database, event.failedSequence)"
>
<i class="las la-minus-circle"></i>
</button>
</cnsl-table-actions>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="eventDisplayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: eventDisplayedColumns"></tr>
</table>
<cnsl-paginator #paginator class="paginator" [hidePagination]="true" [length]="eventDataSource.data.length || 0">
</cnsl-paginator>
</cnsl-refresh-table>
</div>
</div>

View File

@@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { IamViewsComponent } from './iam-views.component';
const routes: Routes = [
{
path: '',
component: IamViewsComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class IamViewsRoutingModule {}

View File

@@ -1,42 +0,0 @@
<div class="max-width-container">
<h1 class="views-title">{{ 'IAM.VIEWS.TITLE' | translate }}</h1>
<p class="views-desc cnsl-secondary-text">{{ 'IAM.VIEWS.DESCRIPTION' | translate }}</p>
<cnsl-refresh-table (refreshed)="loadViews()" [dataSize]="dataSource.data.length" [loading]="loading$ | async">
<table [dataSource]="dataSource" mat-table class="table views-table" aria-label="Views" matSort>
<ng-container matColumnDef="viewName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IAM.VIEWS.VIEWNAME' | translate }}</th>
<td mat-cell *matCellDef="let view">{{ view.viewName }}</td>
</ng-container>
<ng-container matColumnDef="database">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'IAM.VIEWS.DATABASE' | translate }}</th>
<td mat-cell *matCellDef="let view">{{ view.database }}</td>
</ng-container>
<ng-container matColumnDef="sequence">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.VIEWS.SEQUENCE' | translate }}</th>
<td mat-cell *matCellDef="let view">{{ view.processedSequence }}</td>
</ng-container>
<ng-container matColumnDef="eventTimestamp">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.VIEWS.EVENTTIMESTAMP' | translate }}</th>
<td mat-cell *matCellDef="let view">
<span>{{ view?.eventTimestamp | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="lastSuccessfulSpoolerRun">
<th mat-header-cell *matHeaderCellDef>{{ 'IAM.VIEWS.LASTSPOOL' | translate }}</th>
<td mat-cell *matCellDef="let view">
<span>{{ view?.lastSuccessfulSpoolerRun | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<cnsl-paginator #paginator class="paginator" [hidePagination]="true" [length]="dataSource.data.length || 0">
</cnsl-paginator>
</cnsl-refresh-table>
</div>

View File

@@ -1,7 +0,0 @@
.views-title {
margin: 2rem 0 0 0;
}
.views-desc {
font-size: 14px;
}

View File

@@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { InstanceSettingsComponent } from './instance-settings.component';
const routes: Routes = [
{
path: '',
component: InstanceSettingsComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class InstanceSettingsRoutingModule {}

View File

@@ -1,18 +0,0 @@
<div class="max-width-container">
<div class="settings-top-view">
<div>
<div class="settings-title-row">
<h1>{{ 'SETTINGS.INSTANCE.TITLE' | translate }}</h1>
</div>
<p class="desc cnsl-secondary-text">{{ 'SETTINGS.INSTANCE.DESCRIPTION' | translate }}</p>
</div>
<span class="fill-space"></span>
</div>
<ng-container *ngIf="settingsList | async as list">
<cnsl-settings-list
[selectedId]="id"
[serviceType]="PolicyComponentServiceType.ADMIN"
[settingsList]="list"
></cnsl-settings-list>
</ng-container>
</div>

View File

@@ -1,34 +0,0 @@
.settings-top-view {
display: flex;
align-items: center;
padding-top: 2rem;
.settings-title-row {
display: flex;
align-items: center;
h1 {
margin: 0;
}
a i {
font-size: 1.2rem;
height: 1.2rem;
line-height: 1.2rem;
}
}
.fill-space {
flex: 1;
}
.actions {
display: flex;
align-items: center;
}
}
.desc {
margin-bottom: 2rem;
font-size: 14px;
}

View File

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

View File

@@ -1,97 +0,0 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Observable, of, Subject, takeUntil } from 'rxjs';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import {
BRANDING,
COMPLEXITY,
DOMAIN,
LANGUAGES,
IDP,
LOCKOUT,
LOGIN,
LOGINTEXTS,
MESSAGETEXTS,
NOTIFICATIONS,
OIDC,
PRIVACYPOLICY,
SECRETS,
SECURITY,
SMS_PROVIDER,
SMTP_PROVIDER,
} from '../../modules/settings-list/settings';
@Component({
selector: 'cnsl-instance-settings',
templateUrl: './instance-settings.component.html',
styleUrls: ['./instance-settings.component.scss'],
})
export class InstanceSettingsComponent implements OnInit, OnDestroy {
public id: string = '';
public PolicyComponentServiceType: any = PolicyComponentServiceType;
public defaultSettingsList: SidenavSetting[] = [
// notifications
// { showWarn: true, ...NOTIFICATIONS },
NOTIFICATIONS,
SMTP_PROVIDER,
SMS_PROVIDER,
// login
LOGIN,
IDP,
COMPLEXITY,
LOCKOUT,
DOMAIN,
// appearance
BRANDING,
MESSAGETEXTS,
LOGINTEXTS,
// others
PRIVACYPOLICY,
LANGUAGES,
OIDC,
SECRETS,
SECURITY,
];
public settingsList: Observable<SidenavSetting[]> = of([]);
private destroy$: Subject<void> = new Subject();
constructor(
breadcrumbService: BreadcrumbService,
activatedRoute: ActivatedRoute,
public authService: GrpcAuthService,
) {
const breadcrumbs = [
new Breadcrumb({
type: BreadcrumbType.INSTANCE,
name: 'Instance',
routerLink: ['/instance'],
}),
];
breadcrumbService.setBreadcrumb(breadcrumbs);
activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe((params: Params) => {
const { id } = params;
if (id) {
this.id = id;
}
});
}
ngOnInit(): void {
this.settingsList = this.authService.isAllowedMapper(
this.defaultSettingsList,
(setting) => setting.requiredRoles.admin || [],
);
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}

View File

@@ -1,14 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { SettingsListModule } from 'src/app/modules/settings-list/settings-list.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { InstanceSettingsRoutingModule } from './instance-settings-routing.module';
import { InstanceSettingsComponent } from './instance-settings.component';
@NgModule({
declarations: [InstanceSettingsComponent],
imports: [CommonModule, InstanceSettingsRoutingModule, SettingsListModule, HasRolePipeModule, TranslateModule],
})
export default class InstanceSettingsModule {}

View File

@@ -7,29 +7,45 @@
[hasContributors]="true" [hasContributors]="true"
stateTooltip="{{ 'INSTANCE.STATE.' + instance?.state | translate }}" stateTooltip="{{ 'INSTANCE.STATE.' + instance?.state | translate }}"
> >
<cnsl-contributors <div topContributors class="instance-action-wrapper">
topContributors <a
[totalResult]="totalMemberResult" mat-raised-button
[loading]="loading$ | async" color="primary"
[membersSubject]="membersSubject" *ngIf="customerPortalLink$ | async as customerPortalLink"
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}" class="portal-link external-link"
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" [href]="customerPortalLink"
(addClicked)="openAddMember()" target="_blank"
(showDetailClicked)="showDetail()" rel="noreferrer"
(refreshClicked)="loadMembers()" >
[disabled]="(['iam.member.write'] | hasRole | async) === false" <div class="cnsl-action-button">
> <span class="portal-span">{{ 'MENU.CUSTOMERPORTAL' | translate }}</span>
</cnsl-contributors> <i class="las la-external-link-alt"></i>
</div>
</a>
<cnsl-contributors
[totalResult]="totalMemberResult"
[loading]="loading$ | async"
[membersSubject]="membersSubject"
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}"
(addClicked)="openAddMember()"
(showDetailClicked)="showDetail()"
(refreshClicked)="loadMembers()"
[disabled]="(['iam.member.write'] | hasRole | async) === false"
>
</cnsl-contributors>
</div>
<cnsl-info-row topContent *ngIf="instance" [instance]="instance"></cnsl-info-row> <cnsl-info-row topContent *ngIf="instance" [instance]="instance"></cnsl-info-row>
</cnsl-top-view> </cnsl-top-view>
<div class="max-width-container"> <div class="max-width-container">
<h2 class="instance-table-title">{{ 'ORG.LIST.TITLE' | translate }}</h2> <div class="instance-settings-wrapper">
<ng-container *ngIf="settingsList | async as list">
<p class="instance-table-desc cnsl-secondary-text">{{ 'ORG.LIST.DESCRIPTION' | translate }}</p> <cnsl-settings-list
[selectedId]="id"
<cnsl-org-table></cnsl-org-table> [serviceType]="PolicyComponentServiceType.ADMIN"
[settingsList]="list"
<cnsl-settings-grid [type]="PolicyComponentServiceType.ADMIN"></cnsl-settings-grid> ></cnsl-settings-list>
</ng-container>
</div>
</div> </div>

View File

@@ -11,6 +11,23 @@
margin-top: 2rem; margin-top: 2rem;
} }
.instance-action-wrapper {
display: flex;
align-items: center;
.portal-link {
margin-right: 1rem;
.portal-span {
margin-right: 0.5rem;
}
}
}
.instance-table-desc { .instance-table-desc {
font-size: 14px; font-size: 14px;
} }
.instance-settings-wrapper {
margin-top: 2rem;
}

View File

@@ -1,8 +1,8 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of, Subject } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map, takeUntil } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { InstanceDetail, State } from 'src/app/proto/generated/zitadel/instance_pb'; import { InstanceDetail, State } from 'src/app/proto/generated/zitadel/instance_pb';
@@ -11,7 +11,31 @@ import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import {
BRANDING,
COMPLEXITY,
DOMAIN,
LANGUAGES,
IDP,
LOCKOUT,
LOGIN,
LOGINTEXTS,
MESSAGETEXTS,
NOTIFICATIONS,
OIDC,
PRIVACYPOLICY,
SECRETS,
SECURITY,
SMS_PROVIDER,
SMTP_PROVIDER,
VIEWS,
FAILEDEVENTS,
EVENTS,
ORGANIZATIONS,
} from '../../modules/settings-list/settings';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { EnvironmentService } from 'src/app/services/environment.service';
@Component({ @Component({
selector: 'cnsl-instance', selector: 'cnsl-instance',
templateUrl: './instance.component.html', templateUrl: './instance.component.html',
@@ -25,12 +49,51 @@ export class InstanceComponent {
public totalMemberResult: number = 0; public totalMemberResult: number = 0;
public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]); public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
public State: any = State; public State: any = State;
public id: string = '';
public defaultSettingsList: SidenavSetting[] = [
ORGANIZATIONS,
// notifications
// { showWarn: true, ...NOTIFICATIONS },
NOTIFICATIONS,
SMTP_PROVIDER,
SMS_PROVIDER,
// login
LOGIN,
IDP,
COMPLEXITY,
LOCKOUT,
DOMAIN,
// appearance
BRANDING,
MESSAGETEXTS,
LOGINTEXTS,
// storage
VIEWS,
EVENTS,
FAILEDEVENTS,
// others
PRIVACYPOLICY,
LANGUAGES,
OIDC,
SECRETS,
SECURITY,
];
public settingsList: Observable<SidenavSetting[]> = of([]);
public customerPortalLink$ = this.envService.env.pipe(map((env) => env.customer_portal));
private destroy$: Subject<void> = new Subject();
constructor( constructor(
public adminService: AdminService, public adminService: AdminService,
private dialog: MatDialog, private dialog: MatDialog,
private toast: ToastService, private toast: ToastService,
breadcrumbService: BreadcrumbService, breadcrumbService: BreadcrumbService,
private router: Router, private router: Router,
private authService: GrpcAuthService,
private envService: EnvironmentService,
activatedRoute: ActivatedRoute,
) { ) {
this.loadMembers(); this.loadMembers();
@@ -52,6 +115,13 @@ export class InstanceComponent {
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);
}); });
activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe((params: Params) => {
const { id } = params;
if (id) {
this.id = id;
}
});
} }
public loadMembers(): void { public loadMembers(): void {
@@ -113,4 +183,16 @@ export class InstanceComponent {
public showDetail(): void { public showDetail(): void {
this.router.navigate(['/instance', 'members']); this.router.navigate(['/instance', 'members']);
} }
ngOnInit(): void {
this.settingsList = this.authService.isAllowedMapper(
this.defaultSettingsList,
(setting) => setting.requiredRoles.admin || [],
);
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
} }

View File

@@ -29,6 +29,7 @@ import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/
import { IamRoutingModule } from './instance-routing.module'; import { IamRoutingModule } from './instance-routing.module';
import { InstanceComponent } from './instance.component'; import { InstanceComponent } from './instance.component';
import { SettingsListModule } from 'src/app/modules/settings-list/settings-list.module';
@NgModule({ @NgModule({
declarations: [InstanceComponent], declarations: [InstanceComponent],
@@ -61,6 +62,7 @@ import { InstanceComponent } from './instance.component';
TimestampToDatePipeModule, TimestampToDatePipeModule,
RefreshTableModule, RefreshTableModule,
HasRolePipeModule, HasRolePipeModule,
SettingsListModule,
MatSortModule, MatSortModule,
SettingsGridModule, SettingsGridModule,
], ],

View File

@@ -21,6 +21,7 @@ import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { LanguagesService } from '../../services/languages.service'; import { LanguagesService } from '../../services/languages.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
@Component({ @Component({
selector: 'cnsl-org-create', selector: 'cnsl-org-create',
@@ -59,6 +60,7 @@ export class OrgCreateComponent {
private _location: Location, private _location: Location,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private mgmtService: ManagementService, private mgmtService: ManagementService,
private authService: GrpcAuthService,
public langSvc: LanguagesService, public langSvc: LanguagesService,
breadcrumbService: BreadcrumbService, breadcrumbService: BreadcrumbService,
) { ) {
@@ -101,6 +103,7 @@ export class OrgCreateComponent {
this.adminService this.adminService
.SetUpOrg(createOrgRequest, humanRequest) .SetUpOrg(createOrgRequest, humanRequest)
.then(() => { .then(() => {
this.authService.revalidateOrgs();
this.router.navigate(['/orgs']); this.router.navigate(['/orgs']);
}) })
.catch((error) => { .catch((error) => {
@@ -191,6 +194,7 @@ export class OrgCreateComponent {
this.mgmtService this.mgmtService
.addOrg(this.name.value) .addOrg(this.name.value)
.then(() => { .then(() => {
this.authService.revalidateOrgs();
this.router.navigate(['/orgs']); this.router.navigate(['/orgs']);
}) })
.catch((error) => { .catch((error) => {

View File

@@ -9,5 +9,6 @@ import { OrgListComponent } from './org-list.component';
@NgModule({ @NgModule({
declarations: [OrgListComponent], declarations: [OrgListComponent],
imports: [CommonModule, OrgListRoutingModule, OrgTableModule, TranslateModule], imports: [CommonModule, OrgListRoutingModule, OrgTableModule, TranslateModule],
exports: [OrgListComponent],
}) })
export default class OrgListModule {} export default class OrgListModule {}

View File

@@ -140,14 +140,13 @@ export class GrpcAuthService {
public zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]); public zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]);
public readonly fetchedZitadelPermissions: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); public readonly fetchedZitadelPermissions: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
private cachedOrgs: Org.AsObject[] = []; public cachedOrgs: BehaviorSubject<Org.AsObject[]> = new BehaviorSubject<Org.AsObject[]>([]);
private cachedLabelPolicies: { [orgId: string]: LabelPolicy.AsObject } = {}; private cachedLabelPolicies: { [orgId: string]: LabelPolicy.AsObject } = {};
constructor( constructor(
private readonly grpcService: GrpcService, private readonly grpcService: GrpcService,
private oauthService: OAuthService, private oauthService: OAuthService,
private storage: StorageService, private storage: StorageService,
themeService: ThemeService,
) { ) {
this.zitadelPermissions$.subscribe(this.zitadelPermissions); this.zitadelPermissions$.subscribe(this.zitadelPermissions);
@@ -221,15 +220,14 @@ export class GrpcAuthService {
public async getActiveOrg(id?: string): Promise<Org.AsObject> { public async getActiveOrg(id?: string): Promise<Org.AsObject> {
if (id) { if (id) {
const find = this.cachedOrgs.find((tmp) => tmp.id === id); const find = this.cachedOrgs.getValue().find((tmp) => tmp.id === id);
if (find) { if (find) {
this.setActiveOrg(find); this.setActiveOrg(find);
return Promise.resolve(find); return Promise.resolve(find);
} else { } else {
const orgs = (await this.listMyProjectOrgs(10, 0)).resultList; const orgs = (await this.listMyProjectOrgs(10, 0)).resultList;
this.cachedOrgs = orgs; this.cachedOrgs.next(orgs);
const toFind = orgs.find((tmp) => tmp.id === id);
const toFind = this.cachedOrgs.find((tmp) => tmp.id === id);
if (toFind) { if (toFind) {
this.setActiveOrg(toFind); this.setActiveOrg(toFind);
return Promise.resolve(toFind); return Promise.resolve(toFind);
@@ -238,10 +236,10 @@ export class GrpcAuthService {
} }
} }
} else { } else {
let orgs = this.cachedOrgs; let orgs = this.cachedOrgs.getValue();
if (orgs.length === 0) { if (orgs.length === 0) {
orgs = (await this.listMyProjectOrgs()).resultList; orgs = (await this.listMyProjectOrgs()).resultList;
this.cachedOrgs = orgs; this.cachedOrgs.next(orgs);
} }
const org = this.storage.getItem<Org.AsObject>(StorageKey.organization, StorageLocation.local); const org = this.storage.getItem<Org.AsObject>(StorageKey.organization, StorageLocation.local);
@@ -373,6 +371,11 @@ export class GrpcAuthService {
return this.grpcService.auth.listMyAuthFactors(new ListMyAuthFactorsRequest(), null).then((resp) => resp.toObject()); return this.grpcService.auth.listMyAuthFactors(new ListMyAuthFactorsRequest(), null).then((resp) => resp.toObject());
} }
public async revalidateOrgs() {
const orgs = (await this.listMyProjectOrgs()).resultList;
this.cachedOrgs.next(orgs);
}
public listMyProjectOrgs( public listMyProjectOrgs(
limit?: number, limit?: number,
offset?: number, offset?: number,

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Инстанция", "INSTANCE": "настройките по подразбиране",
"DASHBOARD": "Табло", "DASHBOARD": "Табло",
"PERSONAL_INFO": "Лична информация", "PERSONAL_INFO": "Лична информация",
"DOCUMENTATION": "Документация", "DOCUMENTATION": "Документация",
@@ -1017,6 +1017,7 @@
"DESCRIPTION": "Тези настройки разширяват и презаписват настройките на вашия екземпляр." "DESCRIPTION": "Тези настройки разширяват и презаписват настройките на вашия екземпляр."
}, },
"LIST": { "LIST": {
"ORGS": "Организации",
"LANGUAGES": "Езици", "LANGUAGES": "Езици",
"LOGIN": "Поведение при влизане и сигурност", "LOGIN": "Поведение при влизане и сигурност",
"LOCKOUT": "Блокиране", "LOCKOUT": "Блокиране",
@@ -1034,15 +1035,20 @@
"PRIVACYPOLICY": "Политика за бедност", "PRIVACYPOLICY": "Политика за бедност",
"OIDC": "Живот и изтичане на OIDC Token", "OIDC": "Живот и изтичане на OIDC Token",
"SECRETS": "Тайна поява", "SECRETS": "Тайна поява",
"SECURITY": "Настройки на сигурността" "SECURITY": "Настройки на сигурността",
"EVENTS": "Събития",
"FAILEDEVENTS": "Неуспешни събития",
"VIEWS": "Изгледи"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Главна информация",
"NOTIFICATIONS": "Известия", "NOTIFICATIONS": "Известия",
"LOGIN": "Вход и достъп", "LOGIN": "Вход и достъп",
"DOMAIN": "Домейн", "DOMAIN": "Домейн",
"TEXTS": "Текстове и езици", "TEXTS": "Текстове и езици",
"APPEARANCE": "Външен вид", "APPEARANCE": "Външен вид",
"OTHER": "други" "OTHER": "други",
"STORAGE": "Съхранение"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instance", "INSTANCE": "Výchozí nastavení",
"DASHBOARD": "Domů", "DASHBOARD": "Domů",
"PERSONAL_INFO": "Osobní informace", "PERSONAL_INFO": "Osobní informace",
"DOCUMENTATION": "Dokumentace", "DOCUMENTATION": "Dokumentace",
@@ -1024,6 +1024,7 @@
"DESCRIPTION": "Tato nastavení rozšiřují a přepisují nastavení vaší instance." "DESCRIPTION": "Tato nastavení rozšiřují a přepisují nastavení vaší instance."
}, },
"LIST": { "LIST": {
"ORGS": "Organizace",
"LANGUAGES": "Jazyky", "LANGUAGES": "Jazyky",
"LOGIN": "Chování při přihlášení a bezpečnost", "LOGIN": "Chování při přihlášení a bezpečnost",
"LOCKOUT": "Blokování", "LOCKOUT": "Blokování",
@@ -1041,15 +1042,20 @@
"PRIVACYPOLICY": "Zásady ochrany osobních údajů", "PRIVACYPOLICY": "Zásady ochrany osobních údajů",
"OIDC": "Životnost a expirace OIDC tokenu", "OIDC": "Životnost a expirace OIDC tokenu",
"SECRETS": "Generátor tajemství", "SECRETS": "Generátor tajemství",
"SECURITY": "Bezpečnostní nastavení" "SECURITY": "Bezpečnostní nastavení",
"EVENTS": "Události",
"FAILEDEVENTS": "Selhané události",
"VIEWS": "Pohledy"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Obecné informace",
"NOTIFICATIONS": "Oznámení", "NOTIFICATIONS": "Oznámení",
"LOGIN": "Přihlášení a přístup", "LOGIN": "Přihlášení a přístup",
"DOMAIN": "Doména", "DOMAIN": "Doména",
"TEXTS": "Texty a jazyky", "TEXTS": "Texty a jazyky",
"APPEARANCE": "Vzhled", "APPEARANCE": "Vzhled",
"OTHER": "Ostatní" "OTHER": "Ostatní",
"STORAGE": "Data"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instanz", "INSTANCE": "Standardeinstellungen",
"DASHBOARD": "Startseite", "DASHBOARD": "Startseite",
"PERSONAL_INFO": "Persönliche Informationen", "PERSONAL_INFO": "Persönliche Informationen",
"DOCUMENTATION": "Dokumentation", "DOCUMENTATION": "Dokumentation",
@@ -1023,6 +1023,7 @@
"DESCRIPTION": "Diese Einstellungen erweitern bzw. überschreiben die Einstellungen Ihrer Instanz." "DESCRIPTION": "Diese Einstellungen erweitern bzw. überschreiben die Einstellungen Ihrer Instanz."
}, },
"LIST": { "LIST": {
"ORGS": "Organisationen",
"LANGUAGES": "Sprachen", "LANGUAGES": "Sprachen",
"LOGIN": "Loginverhalten und Sicherheit", "LOGIN": "Loginverhalten und Sicherheit",
"LOCKOUT": "Sperrmechanismen", "LOCKOUT": "Sperrmechanismen",
@@ -1040,15 +1041,20 @@
"PRIVACYPOLICY": "Datenschutzrichtlinie", "PRIVACYPOLICY": "Datenschutzrichtlinie",
"OIDC": "OIDC Token Lifetime und Expiration", "OIDC": "OIDC Token Lifetime und Expiration",
"SECRETS": "Secret Generator", "SECRETS": "Secret Generator",
"SECURITY": "Sicherheitseinstellungen" "SECURITY": "Sicherheitseinstellungen",
"EVENTS": "Events",
"FAILEDEVENTS": "Fehlerhafte Events",
"VIEWS": "Views"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Allgemein",
"NOTIFICATIONS": "Benachrichtigungen", "NOTIFICATIONS": "Benachrichtigungen",
"LOGIN": "Login und Zugriff", "LOGIN": "Login und Zugriff",
"DOMAIN": "Domain", "DOMAIN": "Domain",
"TEXTS": "Texte und Sprachen", "TEXTS": "Texte und Sprachen",
"APPEARANCE": "Erscheinungsbild", "APPEARANCE": "Erscheinungsbild",
"OTHER": "Anderes" "OTHER": "Anderes",
"STORAGE": "Speicher"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instance", "INSTANCE": "Default settings",
"DASHBOARD": "Home", "DASHBOARD": "Home",
"PERSONAL_INFO": "Personal Information", "PERSONAL_INFO": "Personal Information",
"DOCUMENTATION": "Documentation", "DOCUMENTATION": "Documentation",
@@ -1024,6 +1024,7 @@
"DESCRIPTION": "These settings extend and overwrite your instance settings." "DESCRIPTION": "These settings extend and overwrite your instance settings."
}, },
"LIST": { "LIST": {
"ORGS": "Organizations",
"LANGUAGES": "Languages", "LANGUAGES": "Languages",
"LOGIN": "Login Behavior and Security", "LOGIN": "Login Behavior and Security",
"LOCKOUT": "Lockout", "LOCKOUT": "Lockout",
@@ -1041,15 +1042,20 @@
"PRIVACYPOLICY": "Privacy Policy", "PRIVACYPOLICY": "Privacy Policy",
"OIDC": "OIDC Token lifetime and expiration", "OIDC": "OIDC Token lifetime and expiration",
"SECRETS": "Secret Generator", "SECRETS": "Secret Generator",
"SECURITY": "Security settings" "SECURITY": "Security settings",
"EVENTS": "Events",
"FAILEDEVENTS": "Failed Events",
"VIEWS": "Views"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "General Information",
"NOTIFICATIONS": "Notifications", "NOTIFICATIONS": "Notifications",
"LOGIN": "Login and Access", "LOGIN": "Login and Access",
"DOMAIN": "Domain", "DOMAIN": "Domain",
"TEXTS": "Texts and Languages", "TEXTS": "Texts and Languages",
"APPEARANCE": "Appearance", "APPEARANCE": "Appearance",
"OTHER": "Other" "OTHER": "Other",
"STORAGE": "Storage"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instancia", "INSTANCE": "configuración por defecto",
"DASHBOARD": "Inicio", "DASHBOARD": "Inicio",
"PERSONAL_INFO": "Información personal", "PERSONAL_INFO": "Información personal",
"DOCUMENTATION": "Documentación", "DOCUMENTATION": "Documentación",
@@ -1025,6 +1025,7 @@
"DESCRIPTION": "Estas configuraciones amplían y sobrescriben tus configuraciones de instancia." "DESCRIPTION": "Estas configuraciones amplían y sobrescriben tus configuraciones de instancia."
}, },
"LIST": { "LIST": {
"ORGS": "Organizaciones",
"LANGUAGES": "Idiomas", "LANGUAGES": "Idiomas",
"LOGIN": "Comportamiento del inicio de sesión y de la seguridad", "LOGIN": "Comportamiento del inicio de sesión y de la seguridad",
"LOCKOUT": "Bloqueo", "LOCKOUT": "Bloqueo",
@@ -1042,15 +1043,20 @@
"PRIVACYPOLICY": "Política de privacidad", "PRIVACYPOLICY": "Política de privacidad",
"OIDC": "OIDC Token lifetime and expiration", "OIDC": "OIDC Token lifetime and expiration",
"SECRETS": "Apariencia del secreto", "SECRETS": "Apariencia del secreto",
"SECURITY": "Ajustes de seguridad" "SECURITY": "Ajustes de seguridad",
"EVENTS": "Eventos",
"FAILEDEVENTS": "Eventos fallidos",
"VIEWS": "Vistas"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "General",
"NOTIFICATIONS": "Notificaciones", "NOTIFICATIONS": "Notificaciones",
"LOGIN": "Inicio de sesión y acceso", "LOGIN": "Inicio de sesión y acceso",
"DOMAIN": "Dominio", "DOMAIN": "Dominio",
"TEXTS": "Textos e idiomas", "TEXTS": "Textos e idiomas",
"APPEARANCE": "Apariencia", "APPEARANCE": "Apariencia",
"OTHER": "Otros" "OTHER": "Otros",
"STORAGE": "Datos"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instance", "INSTANCE": "paramètres par défaut",
"DASHBOARD": "Accueil", "DASHBOARD": "Accueil",
"PERSONAL_INFO": "Informations personnelles", "PERSONAL_INFO": "Informations personnelles",
"DOCUMENTATION": "Documentation", "DOCUMENTATION": "Documentation",
@@ -1023,6 +1023,7 @@
"DESCRIPTION": "Ces paramètres étendent et remplacent les paramètres de votre instance." "DESCRIPTION": "Ces paramètres étendent et remplacent les paramètres de votre instance."
}, },
"LIST": { "LIST": {
"ORGS": "Organisations",
"LANGUAGES": "Langues", "LANGUAGES": "Langues",
"LOGIN": "Comportement de connexion et sécurité", "LOGIN": "Comportement de connexion et sécurité",
"LOCKOUT": "Verrouillage", "LOCKOUT": "Verrouillage",
@@ -1040,15 +1041,20 @@
"PRIVACYPOLICY": "Politique de confidentialité", "PRIVACYPOLICY": "Politique de confidentialité",
"OIDC": "Durée de vie et expiration des jetons OIDC", "OIDC": "Durée de vie et expiration des jetons OIDC",
"SECRETS": "Apparence secrète", "SECRETS": "Apparence secrète",
"SECURITY": "Paramètres de sécurité" "SECURITY": "Paramètres de sécurité",
"EVENTS": "Événements",
"FAILEDEVENTS": "Événements échoués",
"VIEWS": "Vues"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Général",
"NOTIFICATIONS": "Notifications", "NOTIFICATIONS": "Notifications",
"LOGIN": "Connexion et accès", "LOGIN": "Connexion et accès",
"DOMAIN": "Domaine", "DOMAIN": "Domaine",
"TEXTS": "Textes et langues", "TEXTS": "Textes et langues",
"APPEARANCE": "Apparence", "APPEARANCE": "Apparence",
"OTHER": "Autres" "OTHER": "Autres",
"STORAGE": "Stockage"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Istanza", "INSTANCE": "Impostazioni default",
"DASHBOARD": "Pagina iniziale", "DASHBOARD": "Pagina iniziale",
"PERSONAL_INFO": "Informazioni personali", "PERSONAL_INFO": "Informazioni personali",
"DOCUMENTATION": "Documentazione", "DOCUMENTATION": "Documentazione",
@@ -1023,6 +1023,7 @@
"DESCRIPTION": "Queste impostazioni si applicheranno alla organizzazione corrente." "DESCRIPTION": "Queste impostazioni si applicheranno alla organizzazione corrente."
}, },
"LIST": { "LIST": {
"ORGS": "Organizzazioni",
"LANGUAGES": "Lingue", "LANGUAGES": "Lingue",
"LOGIN": "Comportamento login e sicurezza", "LOGIN": "Comportamento login e sicurezza",
"LOCKOUT": "Meccanismi di bloccaggio", "LOCKOUT": "Meccanismi di bloccaggio",
@@ -1040,15 +1041,20 @@
"PRIVACYPOLICY": "Informativa sulla privacy e TOS", "PRIVACYPOLICY": "Informativa sulla privacy e TOS",
"OIDC": "OIDC Token lifetime e scadenza", "OIDC": "OIDC Token lifetime e scadenza",
"SECRETS": "Aspetto dei segreti", "SECRETS": "Aspetto dei segreti",
"SECURITY": "Impostazioni di sicurezza" "SECURITY": "Impostazioni di sicurezza",
"EVENTS": "Eventi",
"FAILEDEVENTS": "Eventi falliti",
"VIEWS": "Views"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Generale",
"NOTIFICATIONS": "Notifiche", "NOTIFICATIONS": "Notifiche",
"LOGIN": "Accesso e login", "LOGIN": "Accesso e login",
"DOMAIN": "Dominio", "DOMAIN": "Dominio",
"TEXTS": "Testi e lingue", "TEXTS": "Testi e lingue",
"APPEARANCE": "Aspetto", "APPEARANCE": "Aspetto",
"OTHER": "Altro" "OTHER": "Altro",
"STORAGE": "Dati"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "インスタンス", "INSTANCE": "デフォルトの設定",
"DASHBOARD": "ホーム", "DASHBOARD": "ホーム",
"PERSONAL_INFO": "個人情報", "PERSONAL_INFO": "個人情報",
"DOCUMENTATION": "ドキュメント", "DOCUMENTATION": "ドキュメント",
@@ -1024,6 +1024,7 @@
"DESCRIPTION": "これらの設定は、インスタンス設定を拡張・上書きします。" "DESCRIPTION": "これらの設定は、インスタンス設定を拡張・上書きします。"
}, },
"LIST": { "LIST": {
"ORGS": "組織",
"LANGUAGES": "一般設定", "LANGUAGES": "一般設定",
"LOGIN": "ログイン動作とセキュリティ", "LOGIN": "ログイン動作とセキュリティ",
"LOCKOUT": "ロックアウト", "LOCKOUT": "ロックアウト",
@@ -1041,15 +1042,20 @@
"PRIVACYPOLICY": "プライバシーポリシー", "PRIVACYPOLICY": "プライバシーポリシー",
"OIDC": "OIDCトークンのライフタイムと有効期限", "OIDC": "OIDCトークンのライフタイムと有効期限",
"SECRETS": "シークレット設定", "SECRETS": "シークレット設定",
"SECURITY": "セキュリティ設定" "SECURITY": "セキュリティ設定",
"EVENTS": "イベント",
"FAILEDEVENTS": "失敗したイベント",
"VIEWS": "ビュー"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "一般",
"NOTIFICATIONS": "通知", "NOTIFICATIONS": "通知",
"LOGIN": "ログインとアクセス", "LOGIN": "ログインとアクセス",
"DOMAIN": "ドメイン", "DOMAIN": "ドメイン",
"TEXTS": "テキストと言語", "TEXTS": "テキストと言語",
"APPEARANCE": "設定", "APPEARANCE": "設定",
"OTHER": "その他" "OTHER": "その他",
"STORAGE": "ストレージ"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Инстанца", "INSTANCE": "стандардни поставки",
"DASHBOARD": "Дома", "DASHBOARD": "Дома",
"PERSONAL_INFO": "Лични информации", "PERSONAL_INFO": "Лични информации",
"DOCUMENTATION": "Документација", "DOCUMENTATION": "Документација",
@@ -1025,6 +1025,7 @@
"DESCRIPTION": "Овие подесувања ги прошируваат и препишуваат подесувањата на вашата инстанца." "DESCRIPTION": "Овие подесувања ги прошируваат и препишуваат подесувањата на вашата инстанца."
}, },
"LIST": { "LIST": {
"ORGS": "Организации",
"LANGUAGES": "Општо", "LANGUAGES": "Општо",
"LOGIN": "Правила и безбедност при најава", "LOGIN": "Правила и безбедност при најава",
"LOCKOUT": "Забрана на пристап", "LOCKOUT": "Забрана на пристап",
@@ -1042,15 +1043,20 @@
"PRIVACYPOLICY": "Политика за приватност", "PRIVACYPOLICY": "Политика за приватност",
"OIDC": "OIDC времетраење и истекување на токени", "OIDC": "OIDC времетраење и истекување на токени",
"SECRETS": "Изглед на тајни", "SECRETS": "Изглед на тајни",
"SECURITY": "Подесувања за безбедност" "SECURITY": "Подесувања за безбедност",
"EVENTS": "Настани",
"FAILEDEVENTS": "Неуспешни настани",
"VIEWS": "Прегледи"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Општи информации",
"NOTIFICATIONS": "Известувања", "NOTIFICATIONS": "Известувања",
"LOGIN": "Најава и пристап", "LOGIN": "Најава и пристап",
"DOMAIN": "Домен", "DOMAIN": "Домен",
"TEXTS": "Текстови и јазици", "TEXTS": "Текстови и јазици",
"APPEARANCE": "Изглед", "APPEARANCE": "Изглед",
"OTHER": "Друго" "OTHER": "Друго",
"STORAGE": "складирање"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instantie", "INSTANCE": "Standaard instellingen",
"DASHBOARD": "Home", "DASHBOARD": "Home",
"PERSONAL_INFO": "Persoonlijke informatie", "PERSONAL_INFO": "Persoonlijke informatie",
"DOCUMENTATION": "Documentatie", "DOCUMENTATION": "Documentatie",
@@ -1024,6 +1024,7 @@
"DESCRIPTION": "Deze instellingen breiden uw instantie instellingen uit en overschrijven deze." "DESCRIPTION": "Deze instellingen breiden uw instantie instellingen uit en overschrijven deze."
}, },
"LIST": { "LIST": {
"ORGS": "Organisaties",
"LANGUAGES": "Talen", "LANGUAGES": "Talen",
"LOGIN": "Login Gedrag en Beveiliging", "LOGIN": "Login Gedrag en Beveiliging",
"LOCKOUT": "Lockout", "LOCKOUT": "Lockout",
@@ -1041,15 +1042,20 @@
"PRIVACYPOLICY": "Privacybeleid", "PRIVACYPOLICY": "Privacybeleid",
"OIDC": "OIDC Token levensduur en vervaldatum", "OIDC": "OIDC Token levensduur en vervaldatum",
"SECRETS": "Secret Generator", "SECRETS": "Secret Generator",
"SECURITY": "Beveiligingsinstellingen" "SECURITY": "Beveiligingsinstellingen",
"EVENTS": "Evenementen",
"FAILEDEVENTS": "Mislukte evenementen",
"VIEWS": "Weergaves"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Algemeen",
"NOTIFICATIONS": "Notificaties", "NOTIFICATIONS": "Notificaties",
"LOGIN": "Login en Toegang", "LOGIN": "Login en Toegang",
"DOMAIN": "Domein", "DOMAIN": "Domein",
"TEXTS": "Teksten en Talen", "TEXTS": "Teksten en Talen",
"APPEARANCE": "Verschijning", "APPEARANCE": "Verschijning",
"OTHER": "Andere" "OTHER": "Andere",
"STORAGE": "opslag"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instancja", "INSTANCE": "Ustawienia domyślne",
"DASHBOARD": "Strona główna", "DASHBOARD": "Strona główna",
"PERSONAL_INFO": "Informacje Osobiste", "PERSONAL_INFO": "Informacje Osobiste",
"DOCUMENTATION": "Dokumentacja", "DOCUMENTATION": "Dokumentacja",
@@ -1023,6 +1023,7 @@
"DESCRIPTION": "Te ustawienia rozszerzają i nadpisują ustawienia instancji." "DESCRIPTION": "Te ustawienia rozszerzają i nadpisują ustawienia instancji."
}, },
"LIST": { "LIST": {
"ORGS": "Organizacje",
"LANGUAGES": "Języki", "LANGUAGES": "Języki",
"LOGIN": "Zachowanie logowania i bezpieczeństwo", "LOGIN": "Zachowanie logowania i bezpieczeństwo",
"LOCKOUT": "Blokada", "LOCKOUT": "Blokada",
@@ -1040,15 +1041,20 @@
"PRIVACYPOLICY": "Polityka prywatności", "PRIVACYPOLICY": "Polityka prywatności",
"OIDC": "Czas trwania tokenów OIDC i wygaśnięcie", "OIDC": "Czas trwania tokenów OIDC i wygaśnięcie",
"SECRETS": "Wygląd sekretów", "SECRETS": "Wygląd sekretów",
"SECURITY": "Ustawienia bezpieczeństwa" "SECURITY": "Ustawienia bezpieczeństwa",
"EVENTS": "Zdarzenia",
"FAILEDEVENTS": "Nieudane Zdarzenia",
"VIEWS": "Widoki"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Informacje ogólne",
"NOTIFICATIONS": "Powiadomienia", "NOTIFICATIONS": "Powiadomienia",
"LOGIN": "Logowanie i dostęp", "LOGIN": "Logowanie i dostęp",
"DOMAIN": "Domena", "DOMAIN": "Domena",
"TEXTS": "Teksty i języki", "TEXTS": "Teksty i języki",
"APPEARANCE": "Wygląd", "APPEARANCE": "Wygląd",
"OTHER": "Inne" "OTHER": "Inne",
"STORAGE": "składowanie"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Instância", "INSTANCE": "Configurações padrão",
"DASHBOARD": "Início", "DASHBOARD": "Início",
"PERSONAL_INFO": "Informações Pessoais", "PERSONAL_INFO": "Informações Pessoais",
"DOCUMENTATION": "Documentação", "DOCUMENTATION": "Documentação",
@@ -1025,6 +1025,7 @@
"DESCRIPTION": "Essas configurações estendem e sobrescrevem as configurações da sua instância." "DESCRIPTION": "Essas configurações estendem e sobrescrevem as configurações da sua instância."
}, },
"LIST": { "LIST": {
"ORGS": "Organizações",
"LANGUAGES": "Idiomas", "LANGUAGES": "Idiomas",
"LOGIN": "Comportamento de Login e Segurança", "LOGIN": "Comportamento de Login e Segurança",
"LOCKOUT": "Bloqueio", "LOCKOUT": "Bloqueio",
@@ -1042,15 +1043,20 @@
"PRIVACYPOLICY": "Política de Privacidade", "PRIVACYPOLICY": "Política de Privacidade",
"OIDC": "Tempo de Vida e Expiração do Token OIDC", "OIDC": "Tempo de Vida e Expiração do Token OIDC",
"SECRETS": "Aparência de Segredo", "SECRETS": "Aparência de Segredo",
"SECURITY": "Configurações de Segurança" "SECURITY": "Configurações de Segurança",
"EVENTS": "Eventos",
"FAILEDEVENTS": "Eventos com Falha",
"VIEWS": "Visualizações"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Geral",
"NOTIFICATIONS": "Notificações", "NOTIFICATIONS": "Notificações",
"LOGIN": "Login e Acesso", "LOGIN": "Login e Acesso",
"DOMAIN": "Domínio", "DOMAIN": "Domínio",
"TEXTS": "Textos e Idiomas", "TEXTS": "Textos e Idiomas",
"APPEARANCE": "Aparência", "APPEARANCE": "Aparência",
"OTHER": "Outro" "OTHER": "Outro",
"STORAGE": "armazenar"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -85,7 +85,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "Система", "INSTANCE": "настройки по умолчанию",
"DASHBOARD": "Главная", "DASHBOARD": "Главная",
"PERSONAL_INFO": "Персональная информация", "PERSONAL_INFO": "Персональная информация",
"DOCUMENTATION": "Документация", "DOCUMENTATION": "Документация",
@@ -1020,6 +1020,7 @@
"DESCRIPTION": "Эти настройки расширяют и перезаписывают настройки вашего экземпляра." "DESCRIPTION": "Эти настройки расширяют и перезаписывают настройки вашего экземпляра."
}, },
"LIST": { "LIST": {
"ORGS": "Организации",
"LANGUAGES": "Языки", "LANGUAGES": "Языки",
"LOGIN": "Поведение при входе и безопасность", "LOGIN": "Поведение при входе и безопасность",
"LOCKOUT": "Блокировка", "LOCKOUT": "Блокировка",
@@ -1034,15 +1035,20 @@
"PRIVACYPOLICY": "Политика конфиденциальности", "PRIVACYPOLICY": "Политика конфиденциальности",
"OIDC": "Срок действия и срок действия токена OIDC", "OIDC": "Срок действия и срок действия токена OIDC",
"SECRETS": "Тайное появление", "SECRETS": "Тайное появление",
"SECURITY": "Настройки безопасности" "SECURITY": "Настройки безопасности",
"EVENTS": "События",
"FAILEDEVENTS": "Неудачные события",
"VIEWS": "Отображение"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "Общая информация",
"NOTIFICATIONS": "Уведомления", "NOTIFICATIONS": "Уведомления",
"LOGIN": "Вход и доступ", "LOGIN": "Вход и доступ",
"DOMAIN": "Домен", "DOMAIN": "Домен",
"TEXTS": "Тексты и языки", "TEXTS": "Тексты и языки",
"APPEARANCE": "Внешний вид", "APPEARANCE": "Внешний вид",
"OTHER": "Другой" "OTHER": "Другой",
"STORAGE": "хранилище"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -90,7 +90,7 @@
} }
}, },
"MENU": { "MENU": {
"INSTANCE": "实例", "INSTANCE": "默认设置",
"DASHBOARD": "首页", "DASHBOARD": "首页",
"PERSONAL_INFO": "个人信息", "PERSONAL_INFO": "个人信息",
"DOCUMENTATION": "文档", "DOCUMENTATION": "文档",
@@ -1023,6 +1023,7 @@
"DESCRIPTION": "这些设置将扩展或覆盖您的实例设置。" "DESCRIPTION": "这些设置将扩展或覆盖您的实例设置。"
}, },
"LIST": { "LIST": {
"ORGS": "组织",
"LANGUAGES": "语言", "LANGUAGES": "语言",
"LOGIN": "登录行为和安全", "LOGIN": "登录行为和安全",
"LOCKOUT": "安全锁策略", "LOCKOUT": "安全锁策略",
@@ -1040,15 +1041,20 @@
"PRIVACYPOLICY": "隐私政策", "PRIVACYPOLICY": "隐私政策",
"OIDC": "OIDC 令牌有效期和过期时间", "OIDC": "OIDC 令牌有效期和过期时间",
"SECRETS": "验证码外观", "SECRETS": "验证码外观",
"SECURITY": "安全设置" "SECURITY": "安全设置",
"EVENTS": "活动",
"FAILEDEVENTS": "失败事件",
"VIEWS": "数据表"
}, },
"GROUPS": { "GROUPS": {
"GENERAL": "通用",
"NOTIFICATIONS": "通知", "NOTIFICATIONS": "通知",
"LOGIN": "登录和访问", "LOGIN": "登录和访问",
"DOMAIN": "域名", "DOMAIN": "域名",
"TEXTS": "文本和语言", "TEXTS": "文本和语言",
"APPEARANCE": "外观", "APPEARANCE": "外观",
"OTHER": "其他" "OTHER": "其他",
"STORAGE": "贮存"
} }
}, },
"SETTING": { "SETTING": {

View File

@@ -57,7 +57,7 @@
@import 'src/app/modules/shortcuts/shortcuts.component.scss'; @import 'src/app/modules/shortcuts/shortcuts.component.scss';
@import 'src/app/modules/policies/idp-settings/idp-settings.component.scss'; @import 'src/app/modules/policies/idp-settings/idp-settings.component.scss';
@import 'src/app/modules/project-role-chip/project-role-chip.component'; @import 'src/app/modules/project-role-chip/project-role-chip.component';
@import 'src/app/pages/events/events.component'; @import 'src/app/modules/events/events.component';
@import 'src/app/pages/home/home.component.scss'; @import 'src/app/pages/home/home.component.scss';
@import 'src/app/modules/policies/security-policy/security-policy.component.scss'; @import 'src/app/modules/policies/security-policy/security-policy.component.scss';
@import 'src/app/modules/idp-table/idp-table.component.scss'; @import 'src/app/modules/idp-table/idp-table.component.scss';

View File

@@ -1,6 +1,6 @@
The login policy can be configured on two levels. Once as default on the instance and this can be overwritten for each organization. The login policy can be configured on two levels. Once as default on the instance and this can be overwritten for each organization.
The only difference is where you configure it. Go either to the settings page of a specific organization or to the settings page of your instance. The only difference is where you configure it. Go either to the settings page of a specific organization or to the settings page of your instance.
Instance: $YOUR-DOMAIN/ui/console/settings?id=general Instance: $YOUR-DOMAIN/ui/console/instance?id=general
Organization: Choose the organization in the menu and go to $YOUR-DOMAIN/ui/console/org-settings?id=login Organization: Choose the organization in the menu and go to $YOUR-DOMAIN/ui/console/org-settings?id=login
1. Go to the Settings 1. Go to the Settings

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 KiB

After

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

After

Width:  |  Height:  |  Size: 455 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 KiB

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Some files were not shown because too many files have changed in this diff Show More