Add role management

This commit is contained in:
Sylvain MATHIEU 2022-01-06 16:41:37 +01:00
parent 36e39f908b
commit 920154e7b9
47 changed files with 1534 additions and 117 deletions

View File

@ -1,107 +1,112 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component';
import { DirectLinkComponent } from './components/direct-link/direct-link.component';
import { HelpComponent } from './components/help/help.component';
import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component';
import { LoginComponent } from './components/login/login.component';
import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
import { BuiltInPreferencesComponent } from './components/preferences/built-in/built-in-preferences.component';
import { CloudNodesAddTemplateComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-add-template/cloud-nodes-add-template.component';
import { CloudNodesTemplateDetailsComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-template-details/cloud-nodes-template-details.component';
import { CloudNodesTemplatesComponent } from './components/preferences/built-in/cloud-nodes/cloud-nodes-templates/cloud-nodes-templates.component';
import { EthernetHubsAddTemplateComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-add-template/ethernet-hubs-add-template.component';
import { EthernetHubsTemplateDetailsComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-template-details/ethernet-hubs-template-details.component';
import { EthernetHubsTemplatesComponent } from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-templates/ethernet-hubs-templates.component';
import { EthernetSwitchesAddTemplateComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-add-template/ethernet-switches-add-template.component';
import { EthernetSwitchesTemplateDetailsComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-template-details/ethernet-switches-template-details.component';
import { EthernetSwitchesTemplatesComponent } from './components/preferences/built-in/ethernet-switches/ethernet-switches-templates/ethernet-switches-templates.component';
import { AddDockerTemplateComponent } from './components/preferences/docker/add-docker-template/add-docker-template.component';
import { CopyDockerTemplateComponent } from './components/preferences/docker/copy-docker-template/copy-docker-template.component';
import { DockerTemplateDetailsComponent } from './components/preferences/docker/docker-template-details/docker-template-details.component';
import { DockerTemplatesComponent } from './components/preferences/docker/docker-templates/docker-templates.component';
import { AddIosTemplateComponent } from './components/preferences/dynamips/add-ios-template/add-ios-template.component';
import { CopyIosTemplateComponent } from './components/preferences/dynamips/copy-ios-template/copy-ios-template.component';
import { IosTemplateDetailsComponent } from './components/preferences/dynamips/ios-template-details/ios-template-details.component';
import { IosTemplatesComponent } from './components/preferences/dynamips/ios-templates/ios-templates.component';
import { AddIouTemplateComponent } from './components/preferences/ios-on-unix/add-iou-template/add-iou-template.component';
import { CopyIouTemplateComponent } from './components/preferences/ios-on-unix/copy-iou-template/copy-iou-template.component';
import { IouTemplateDetailsComponent } from './components/preferences/ios-on-unix/iou-template-details/iou-template-details.component';
import { IouTemplatesComponent } from './components/preferences/ios-on-unix/iou-templates/iou-templates.component';
import { PreferencesComponent } from './components/preferences/preferences.component';
import { AddQemuVmTemplateComponent } from './components/preferences/qemu/add-qemu-vm-template/add-qemu-vm-template.component';
import { CopyQemuVmTemplateComponent } from './components/preferences/qemu/copy-qemu-vm-template/copy-qemu-vm-template.component';
import { QemuVmTemplateDetailsComponent } from './components/preferences/qemu/qemu-vm-template-details/qemu-vm-template-details.component';
import { QemuVmTemplatesComponent } from './components/preferences/qemu/qemu-vm-templates/qemu-vm-templates.component';
import { AddVirtualBoxTemplateComponent } from './components/preferences/virtual-box/add-virtual-box-template/add-virtual-box-template.component';
import { VirtualBoxTemplateDetailsComponent } from './components/preferences/virtual-box/virtual-box-template-details/virtual-box-template-details.component';
import { VirtualBoxTemplatesComponent } from './components/preferences/virtual-box/virtual-box-templates/virtual-box-templates.component';
import { AddVmwareTemplateComponent } from './components/preferences/vmware/add-vmware-template/add-vmware-template.component';
import { VmwareTemplateDetailsComponent } from './components/preferences/vmware/vmware-template-details/vmware-template-details.component';
import { VmwareTemplatesComponent } from './components/preferences/vmware/vmware-templates/vmware-templates.component';
import { AddVpcsTemplateComponent } from './components/preferences/vpcs/add-vpcs-template/add-vpcs-template.component';
import { VpcsTemplateDetailsComponent } from './components/preferences/vpcs/vpcs-template-details/vpcs-template-details.component';
import { VpcsTemplatesComponent } from './components/preferences/vpcs/vpcs-templates/vpcs-templates.component';
import { ProjectMapComponent } from './components/project-map/project-map.component';
import { ProjectsComponent } from './components/projects/projects.component';
import { ServersComponent } from './components/servers/servers.component';
import { ConsoleComponent } from './components/settings/console/console.component';
import { SettingsComponent } from './components/settings/settings.component';
import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
import { SystemStatusComponent } from './components/system-status/system-status.component';
import { WebConsoleFullWindowComponent } from './components/web-console-full-window/web-console-full-window.component';
import { ConsoleGuard } from './guards/console-guard';
import { LoginGuard } from './guards/login-guard';
import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component';
import { ServerResolve } from './resolvers/server-resolve';
import { UserManagementComponent } from './components/user-management/user-management.component';
import { LoggedUserComponent } from './components/users/logged-user/logged-user.component';
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {BundledServerFinderComponent} from './components/bundled-server-finder/bundled-server-finder.component';
import {DirectLinkComponent} from './components/direct-link/direct-link.component';
import {HelpComponent} from './components/help/help.component';
import {InstalledSoftwareComponent} from './components/installed-software/installed-software.component';
import {LoginComponent} from './components/login/login.component';
import {PageNotFoundComponent} from './components/page-not-found/page-not-found.component';
import {BuiltInPreferencesComponent} from './components/preferences/built-in/built-in-preferences.component';
import {CloudNodesAddTemplateComponent} from './components/preferences/built-in/cloud-nodes/cloud-nodes-add-template/cloud-nodes-add-template.component';
import {CloudNodesTemplateDetailsComponent} from './components/preferences/built-in/cloud-nodes/cloud-nodes-template-details/cloud-nodes-template-details.component';
import {CloudNodesTemplatesComponent} from './components/preferences/built-in/cloud-nodes/cloud-nodes-templates/cloud-nodes-templates.component';
import {EthernetHubsAddTemplateComponent} from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-add-template/ethernet-hubs-add-template.component';
import {EthernetHubsTemplateDetailsComponent} from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-template-details/ethernet-hubs-template-details.component';
import {EthernetHubsTemplatesComponent} from './components/preferences/built-in/ethernet-hubs/ethernet-hubs-templates/ethernet-hubs-templates.component';
import {EthernetSwitchesAddTemplateComponent} from './components/preferences/built-in/ethernet-switches/ethernet-switches-add-template/ethernet-switches-add-template.component';
import {EthernetSwitchesTemplateDetailsComponent} from './components/preferences/built-in/ethernet-switches/ethernet-switches-template-details/ethernet-switches-template-details.component';
import {EthernetSwitchesTemplatesComponent} from './components/preferences/built-in/ethernet-switches/ethernet-switches-templates/ethernet-switches-templates.component';
import {AddDockerTemplateComponent} from './components/preferences/docker/add-docker-template/add-docker-template.component';
import {CopyDockerTemplateComponent} from './components/preferences/docker/copy-docker-template/copy-docker-template.component';
import {DockerTemplateDetailsComponent} from './components/preferences/docker/docker-template-details/docker-template-details.component';
import {DockerTemplatesComponent} from './components/preferences/docker/docker-templates/docker-templates.component';
import {AddIosTemplateComponent} from './components/preferences/dynamips/add-ios-template/add-ios-template.component';
import {CopyIosTemplateComponent} from './components/preferences/dynamips/copy-ios-template/copy-ios-template.component';
import {IosTemplateDetailsComponent} from './components/preferences/dynamips/ios-template-details/ios-template-details.component';
import {IosTemplatesComponent} from './components/preferences/dynamips/ios-templates/ios-templates.component';
import {AddIouTemplateComponent} from './components/preferences/ios-on-unix/add-iou-template/add-iou-template.component';
import {CopyIouTemplateComponent} from './components/preferences/ios-on-unix/copy-iou-template/copy-iou-template.component';
import {IouTemplateDetailsComponent} from './components/preferences/ios-on-unix/iou-template-details/iou-template-details.component';
import {IouTemplatesComponent} from './components/preferences/ios-on-unix/iou-templates/iou-templates.component';
import {PreferencesComponent} from './components/preferences/preferences.component';
import {AddQemuVmTemplateComponent} from './components/preferences/qemu/add-qemu-vm-template/add-qemu-vm-template.component';
import {CopyQemuVmTemplateComponent} from './components/preferences/qemu/copy-qemu-vm-template/copy-qemu-vm-template.component';
import {QemuVmTemplateDetailsComponent} from './components/preferences/qemu/qemu-vm-template-details/qemu-vm-template-details.component';
import {QemuVmTemplatesComponent} from './components/preferences/qemu/qemu-vm-templates/qemu-vm-templates.component';
import {AddVirtualBoxTemplateComponent} from './components/preferences/virtual-box/add-virtual-box-template/add-virtual-box-template.component';
import {VirtualBoxTemplateDetailsComponent} from './components/preferences/virtual-box/virtual-box-template-details/virtual-box-template-details.component';
import {VirtualBoxTemplatesComponent} from './components/preferences/virtual-box/virtual-box-templates/virtual-box-templates.component';
import {AddVmwareTemplateComponent} from './components/preferences/vmware/add-vmware-template/add-vmware-template.component';
import {VmwareTemplateDetailsComponent} from './components/preferences/vmware/vmware-template-details/vmware-template-details.component';
import {VmwareTemplatesComponent} from './components/preferences/vmware/vmware-templates/vmware-templates.component';
import {AddVpcsTemplateComponent} from './components/preferences/vpcs/add-vpcs-template/add-vpcs-template.component';
import {VpcsTemplateDetailsComponent} from './components/preferences/vpcs/vpcs-template-details/vpcs-template-details.component';
import {VpcsTemplatesComponent} from './components/preferences/vpcs/vpcs-templates/vpcs-templates.component';
import {ProjectMapComponent} from './components/project-map/project-map.component';
import {ProjectsComponent} from './components/projects/projects.component';
import {ServersComponent} from './components/servers/servers.component';
import {ConsoleComponent} from './components/settings/console/console.component';
import {SettingsComponent} from './components/settings/settings.component';
import {ListOfSnapshotsComponent} from './components/snapshots/list-of-snapshots/list-of-snapshots.component';
import {SystemStatusComponent} from './components/system-status/system-status.component';
import {WebConsoleFullWindowComponent} from './components/web-console-full-window/web-console-full-window.component';
import {ConsoleGuard} from './guards/console-guard';
import {LoginGuard} from './guards/login-guard';
import {DefaultLayoutComponent} from './layouts/default-layout/default-layout.component';
import {ServerResolve} from './resolvers/server-resolve';
import {UserManagementComponent} from './components/user-management/user-management.component';
import {LoggedUserComponent} from './components/users/logged-user/logged-user.component';
import {GroupManagementComponent} from "./components/group-management/group-management.component";
import {GroupDetailsComponent} from "@components/group-details/group-details.component";
import {UserDetailComponent} from "@components/user-management/user-detail/user-detail.component";
import {GroupDetailsResolver} from "@resolvers/group-details.resolver";
import {ManagementComponent} from "@components/management/management.component";
import {RoleManagementComponent} from "@components/role-management/role-management.component";
import {RoleDetailComponent} from "@components/role-management/role-detail/role-detail.component";
import {RoleDetailResolver} from "@resolvers/role-detail.resolver";
import {PermissionEditorComponent} from "@components/role-management/role-detail/permission-editor/permission-editor.component";
import {PermissionResolver} from "@resolvers/permission.resolver";
const routes: Routes = [
{
path: '',
component: DefaultLayoutComponent,
children: [
{ path: '', redirectTo: 'servers', pathMatch: 'full' },
{ path: 'servers', component: ServersComponent },
{ path: 'bundled', component: BundledServerFinderComponent },
{ path: 'server/:server_id/login', component: LoginComponent },
{ path: 'server/:server_id/loggeduser', component: LoggedUserComponent },
{path: '', redirectTo: 'servers', pathMatch: 'full'},
{path: 'servers', component: ServersComponent},
{path: 'bundled', component: BundledServerFinderComponent},
{path: 'server/:server_id/login', component: LoginComponent},
{path: 'server/:server_id/loggeduser', component: LoggedUserComponent},
{
path: 'server/:server_id/projects',
component: ProjectsComponent,
canActivate: [LoginGuard],
resolve: { server: ServerResolve },
resolve: {server: ServerResolve},
},
{ path: 'help', component: HelpComponent },
{ path: 'settings', component: SettingsComponent },
{ path: 'settings/console', component: ConsoleComponent },
{path: 'help', component: HelpComponent},
{path: 'settings', component: SettingsComponent},
{path: 'settings/console', component: ConsoleComponent},
{
path: 'server/:server_id/management/users/:user_id',
component: UserDetailComponent,
canActivate: [LoginGuard],
resolve: { server: ServerResolve },
resolve: {server: ServerResolve},
},
{ path: 'installed-software', component: InstalledSoftwareComponent },
{ path: 'server/:server_id/systemstatus', component: SystemStatusComponent, canActivate: [LoginGuard] },
{path: 'installed-software', component: InstalledSoftwareComponent},
{path: 'server/:server_id/systemstatus', component: SystemStatusComponent, canActivate: [LoginGuard]},
{ path: 'server/:server_ip/:server_port/project/:project_id', component: DirectLinkComponent, canActivate: [LoginGuard] },
{path: 'server/:server_ip/:server_port/project/:project_id', component: DirectLinkComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/project/:project_id/snapshots',
component: ListOfSnapshotsComponent,
canActivate: [LoginGuard],
resolve: { server: ServerResolve },
resolve: {server: ServerResolve},
},
{ path: 'server/:server_id/preferences', component: PreferencesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences', component: PreferencesComponent, canActivate: [LoginGuard]},
// { path: 'server/:server_id/preferences/general', component: GeneralPreferencesComponent },
{ path: 'server/:server_id/preferences/builtin', component: BuiltInPreferencesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/builtin', component: BuiltInPreferencesComponent, canActivate: [LoginGuard]},
{ path: 'server/:server_id/preferences/builtin/ethernet-hubs', component: EthernetHubsTemplatesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/builtin/ethernet-hubs', component: EthernetHubsTemplatesComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/builtin/ethernet-hubs/addtemplate',
component: EthernetHubsAddTemplateComponent,
@ -129,7 +134,7 @@ const routes: Routes = [
canActivate: [LoginGuard]
},
{ path: 'server/:server_id/preferences/builtin/cloud-nodes', component: CloudNodesTemplatesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/builtin/cloud-nodes', component: CloudNodesTemplatesComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/builtin/cloud-nodes/addtemplate',
component: CloudNodesAddTemplateComponent,
@ -142,9 +147,13 @@ const routes: Routes = [
},
//{ path: 'server/:server_id/preferences/dynamips', component: DynamipsPreferencesComponent },
{ path: 'server/:server_id/preferences/dynamips/templates', component: IosTemplatesComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/dynamips/templates/addtemplate', component: AddIosTemplateComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/dynamips/templates/:template_id', component: IosTemplateDetailsComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/dynamips/templates', component: IosTemplatesComponent, canActivate: [LoginGuard]},
{path: 'server/:server_id/preferences/dynamips/templates/addtemplate', component: AddIosTemplateComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/dynamips/templates/:template_id',
component: IosTemplateDetailsComponent,
canActivate: [LoginGuard]
},
{
path: 'server/:server_id/preferences/dynamips/templates/:template_id/copy',
component: CopyIosTemplateComponent,
@ -152,39 +161,47 @@ const routes: Routes = [
},
// { path: 'server/:server_id/preferences/qemu', component: QemuPreferencesComponent },
{ path: 'server/:server_id/preferences/qemu/templates', component: QemuVmTemplatesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/qemu/templates', component: QemuVmTemplatesComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/qemu/templates/:template_id/copy',
component: CopyQemuVmTemplateComponent,
canActivate: [LoginGuard]
},
{ path: 'server/:server_id/preferences/qemu/templates/:template_id', component: QemuVmTemplateDetailsComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/qemu/addtemplate', component: AddQemuVmTemplateComponent, canActivate: [LoginGuard] },
{
path: 'server/:server_id/preferences/qemu/templates/:template_id',
component: QemuVmTemplateDetailsComponent,
canActivate: [LoginGuard]
},
{path: 'server/:server_id/preferences/qemu/addtemplate', component: AddQemuVmTemplateComponent, canActivate: [LoginGuard]},
// { path: 'server/:server_id/preferences/vpcs', component: VpcsPreferencesComponent },
{ path: 'server/:server_id/preferences/vpcs/templates', component: VpcsTemplatesComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/vpcs/templates/:template_id', component: VpcsTemplateDetailsComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/vpcs/addtemplate', component: AddVpcsTemplateComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/vpcs/templates', component: VpcsTemplatesComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/vpcs/templates/:template_id',
component: VpcsTemplateDetailsComponent,
canActivate: [LoginGuard]
},
{path: 'server/:server_id/preferences/vpcs/addtemplate', component: AddVpcsTemplateComponent, canActivate: [LoginGuard]},
// { path: 'server/:server_id/preferences/virtualbox', component: VirtualBoxPreferencesComponent },
{ path: 'server/:server_id/preferences/virtualbox/templates', component: VirtualBoxTemplatesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/virtualbox/templates', component: VirtualBoxTemplatesComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/virtualbox/templates/:template_id',
component: VirtualBoxTemplateDetailsComponent,
canActivate: [LoginGuard]
},
{ path: 'server/:server_id/preferences/virtualbox/addtemplate', component: AddVirtualBoxTemplateComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/virtualbox/addtemplate', component: AddVirtualBoxTemplateComponent, canActivate: [LoginGuard]},
// { path: 'server/:server_id/preferences/vmware', component: VmwarePreferencesComponent },
{ path: 'server/:server_id/preferences/vmware/templates', component: VmwareTemplatesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/vmware/templates', component: VmwareTemplatesComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/vmware/templates/:template_id',
component: VmwareTemplateDetailsComponent,
canActivate: [LoginGuard]
},
{ path: 'server/:server_id/preferences/vmware/addtemplate', component: AddVmwareTemplateComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/vmware/addtemplate', component: AddVmwareTemplateComponent, canActivate: [LoginGuard]},
{ path: 'server/:server_id/preferences/docker/templates', component: DockerTemplatesComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/docker/templates', component: DockerTemplatesComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/docker/templates/:template_id',
component: DockerTemplateDetailsComponent,
@ -195,27 +212,58 @@ const routes: Routes = [
component: CopyDockerTemplateComponent,
canActivate: [LoginGuard]
},
{ path: 'server/:server_id/preferences/docker/addtemplate', component: AddDockerTemplateComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/docker/addtemplate', component: AddDockerTemplateComponent, canActivate: [LoginGuard]},
{ path: 'server/:server_id/preferences/iou/templates', component: IouTemplatesComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/iou/templates/:template_id', component: IouTemplateDetailsComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/iou/templates/:template_id/copy', component: CopyIouTemplateComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/iou/addtemplate', component: AddIouTemplateComponent, canActivate: [LoginGuard] },
{path: 'server/:server_id/preferences/iou/templates', component: IouTemplatesComponent, canActivate: [LoginGuard]},
{path: 'server/:server_id/preferences/iou/templates/:template_id', component: IouTemplateDetailsComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/preferences/iou/templates/:template_id/copy',
component: CopyIouTemplateComponent,
canActivate: [LoginGuard]
},
{path: 'server/:server_id/preferences/iou/addtemplate', component: AddIouTemplateComponent, canActivate: [LoginGuard]},
{
path: 'server/:server_id/management',
component: ManagementComponent,
children: [
{
path: 'users',
component: UserManagementComponent
},
{
path: 'groups',
component: GroupManagementComponent
children: [
{
path: 'users',
component: UserManagementComponent
},
{
path: 'groups',
component: GroupManagementComponent
},
{
path: 'roles',
component: RoleManagementComponent
}
]
},
{
path: 'server/:server_id/management/groups/:user_group_id',
component: GroupDetailsComponent,
resolve: {
group: GroupDetailsResolver
}
]},
{ path: 'server/:server_id/management/groups/:user_group_id', component: GroupDetailsComponent, resolve: {group: GroupDetailsResolver}},
},
{
path: 'server/:server_id/management/roles/:role_id',
component: RoleDetailComponent,
resolve: {
role: RoleDetailResolver,
server: ServerResolve
}
},
{
path: 'server/:server_id/management/roles/:role_id/permissions',
component: PermissionEditorComponent,
resolve: {
role: RoleDetailResolver,
server: ServerResolve,
permissions: PermissionResolver
}
},
],
},
{
@ -251,4 +299,5 @@ const routes: Routes = [
],
exports: [RouterModule],
})
export class AppRoutingModule {}
export class AppRoutingModule {
}

View File

@ -287,6 +287,14 @@ import { RemoveUserToGroupDialogComponent } from './components/group-details/rem
import { PaginatorPipe } from './components/group-details/paginator.pipe';
import { MembersFilterPipe } from './components/group-details/members-filter.pipe';
import { ManagementComponent } from './components/management/management.component';
import { RoleManagementComponent } from './components/role-management/role-management.component';
import { RoleFilterPipe } from './components/role-management/role-filter.pipe';
import { AddRoleDialogComponent } from './components/role-management/add-role-dialog/add-role-dialog.component';
import { DeleteRoleDialogComponent } from './components/role-management/delete-role-dialog/delete-role-dialog.component';
import { RoleDetailComponent } from './components/role-management/role-detail/role-detail.component';
import { PermissionEditorComponent } from './components/role-management/role-detail/permission-editor/permission-editor.component';
import { EditablePermissionComponent } from './components/role-management/role-detail/permission-editor/editable-permission/editable-permission.component';
import { PermissionEditorValidateDialogComponent } from './components/role-management/role-detail/permission-editor/permission-editor-validate-dialog/permission-editor-validate-dialog.component';
@NgModule({
declarations: [
@ -488,7 +496,15 @@ import { ManagementComponent } from './components/management/management.componen
RemoveUserToGroupDialogComponent,
PaginatorPipe,
MembersFilterPipe,
ManagementComponent
ManagementComponent,
RoleManagementComponent,
RoleFilterPipe,
AddRoleDialogComponent,
DeleteRoleDialogComponent,
RoleDetailComponent,
PermissionEditorComponent,
EditablePermissionComponent,
PermissionEditorValidateDialogComponent,
],
imports: [
BrowserModule,

View File

@ -23,7 +23,7 @@ import {ServerService} from "@services/server.service";
export class ManagementComponent implements OnInit {
server: Server;
links = ['users', 'groups'];
links = ['users', 'groups', 'roles'];
activeLink: string = this.links[0];
constructor(

View File

@ -0,0 +1,30 @@
<h1 mat-dialog-title>Create new role</h1>
<form [formGroup]="roleNameForm" class="file-name-form">
<mat-form-field>
<input
matInput
type="text"
formControlName="name"
[ngClass]="{ 'is-invalid': form.name?.errors }"
placeholder="Please enter role name"/>
<mat-error *ngIf="form.name?.touched && form.name?.errors && form.name?.errors.required">
role name is required
</mat-error>
<mat-error *ngIf="form.name?.errors && form.name?.errors.invalidName">
Role name is incorrect
</mat-error>
</mat-form-field>
<mat-form-field>
<input
matInput
type="text"
formControlName="description"
placeholder="Please enter a description"/>
</mat-form-field>
<div mat-dialog-actions>
<button mat-button (click)="onNoClick()" color="accent">Cancel</button>
<button mat-button (click)="onAddClick()" tabindex="2" class="add-project-button" mat-raised-button color="primary">
Add Role
</button>
</div>
</form>

View File

@ -0,0 +1,7 @@
mat-form-field {
width: 100%;
}
.project-snackbar {
background: #2196f3;
}

View File

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

View File

@ -0,0 +1,48 @@
import {Component, Inject, OnInit} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {Server} from "@models/server";
import {GroupNameValidator} from "@components/group-management/add-group-dialog/GroupNameValidator";
import {GroupService} from "@services/group.service";
import {groupNameAsyncValidator} from "@components/group-management/add-group-dialog/groupNameAsyncValidator";
@Component({
selector: 'app-add-role-dialog',
templateUrl: './add-role-dialog.component.html',
styleUrls: ['./add-role-dialog.component.scss']
})
export class AddRoleDialogComponent implements OnInit {
roleNameForm: FormGroup;
constructor(private dialogRef: MatDialogRef<AddRoleDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { server: Server },
private formBuilder: FormBuilder) {
}
ngOnInit(): void {
this.roleNameForm = this.formBuilder.group({
name: new FormControl(),
description: new FormControl()
});
}
get form() {
return this.roleNameForm.controls;
}
onAddClick() {
if (this.roleNameForm.invalid) {
return;
}
const roleName = this.roleNameForm.controls['name'].value;
const description = this.roleNameForm.controls['description'].value;
this.dialogRef.close({name: roleName, description});
}
onNoClick() {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,8 @@
<h1 mat-dialog-title>Are you sure to delete role named: </h1>
<p *ngFor="let role of data.roles">{{role.name}}</p>
<div mat-dialog-actions>
<button mat-button (click)="onCancel()" color="accent">No, cancel</button>
<button mat-button (click)="onDelete()" tabindex="2" class="add-project-button" mat-raised-button color="primary">
Yes, delete!
</button>
</div>

View File

@ -0,0 +1,6 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

View File

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

View File

@ -0,0 +1,27 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {Role} from "@models/api/role";
@Component({
selector: 'app-delete-role-dialog',
templateUrl: './delete-role-dialog.component.html',
styleUrls: ['./delete-role-dialog.component.scss']
})
export class DeleteRoleDialogComponent implements OnInit {
constructor(private dialogRef: MatDialogRef<DeleteRoleDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { roles: Role[] }) { }
ngOnInit(): void {
}
onCancel() {
this.dialogRef.close();
}
onDelete() {
this.dialogRef.close(true);
}
}

View File

@ -0,0 +1,14 @@
<div [matTooltip]="getToolTip()"
matTooltipClass="permission-tooltip"
class="box" [ngClass]="{allow: permission.action === 'ALLOW', left: side === 'LEFT'}">
<button *ngIf="side === 'RIGHT'" mat-button (click)="onClick()">
<mat-icon>keyboard_arrow_left</mat-icon>
</button>
<div class="content">
<div class="center">{{permission.methods.join(",")}}</div>
<div class="center">{{permission.path}}</div>
</div>
<button *ngIf="side === 'LEFT'" mat-button (click)="onClick()">
<mat-icon>keyboard_arrow_right</mat-icon>
</button>
</div>

View File

@ -0,0 +1,42 @@
.box {
display: flex;
flex-direction: row;
border: 1px solid;
border-radius: 20px;
margin: 10px;
font-size: 12px;
font-family: monospace;
justify-items: center;
background-color: rgba(130, 8, 8, 0.36);
justify-content: flex-start;
}
.left {
justify-content: flex-end;
}
.allow {
background-color: rgba(5, 76, 5, 0.38);
}
.content {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-around;
}
.content > div {
padding-right: 20px;
}
.center {
display: flex;
align-items: center;
justify-content: center;
}
button {
border-radius: 20px;
}

View File

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

View File

@ -0,0 +1,32 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Permission} from "@models/api/permission";
@Component({
selector: 'app-editable-permission',
templateUrl: './editable-permission.component.html',
styleUrls: ['./editable-permission.component.scss']
})
export class EditablePermissionComponent implements OnInit {
@Input() permission: Permission;
@Input() side: 'LEFT' | 'RIGHT';
@Output() click = new EventEmitter();
constructor() { }
ngOnInit(): void {
}
onClick() {
this.click.emit();
}
getToolTip() {
return `
action: ${this.permission.action}
methods: ${this.permission.methods.join(',')}
path: ${this.permission.path}
`;
}
}

View File

@ -0,0 +1,33 @@
<div>
<div class="button">
<button
mat-button
mat-raised-button
(click)="close()">
Cancel
</button>
<button *ngIf="data.add.length > 0 || data.remove.length > 0"
mat-button
mat-raised-button
color="primary"
(click)="update()">
Apply
</button>
</div>
<div class="change" *ngIf="data.add.length > 0 || data.remove.length > 0; else nothingTodo">
<p></p>
<div class="title" *ngIf="data.add.length > 0">Permission to Add:</div>
<div *ngFor="let permission of data.add">
<app-editable-permission [permission]="permission"></app-editable-permission>
</div>
<div class="title" *ngIf="data.remove.length > 0">Permission to Remove:</div>
<div *ngFor="let permission of data.remove">
<app-editable-permission [permission]="permission"></app-editable-permission>
</div>
</div>
<ng-template #nothingTodo>
<div class="noChange">
No change
</div>
</ng-template>
</div>

View File

@ -0,0 +1,33 @@
.change {
height: 350px;
display: flex;
flex-direction: column;
justify-content: center;
overflow-y: auto;
}
.change div {
justify-content: center;
justify-items: center;
text-align: center;
}
.title {
font-size: 20px;
}
.button {
position: relative;
top: 400px;
z-index: 1;
}
.button button {
margin-right: 50px;
}
.noChange {
display: flex;
justify-content: center;
justify-items: center;
}

View File

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

View File

@ -0,0 +1,26 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {Group} from "@models/groups/group";
import {Permission} from "@models/api/permission";
@Component({
selector: 'app-permission-editor-validate-dialog',
templateUrl: './permission-editor-validate-dialog.component.html',
styleUrls: ['./permission-editor-validate-dialog.component.scss']
})
export class PermissionEditorValidateDialogComponent implements OnInit {
constructor(private dialogRef: MatDialogRef<PermissionEditorValidateDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { add: Permission[], remove: Permission[] }) { }
ngOnInit(): void {
}
close() {
this.dialogRef.close(false);
}
update() {
this.dialogRef.close(true);
}
}

View File

@ -0,0 +1,62 @@
<div class="header">
<div>
<div>
<a
mat-icon-button
matTooltip="Back to role detail"
matTooltipClass="custom-tooltip"
[routerLink]="['/server', server.id, 'management', 'roles', role.role_id]">
<mat-icon aria-label="Back to role detail">keyboard_arrow_left</mat-icon>
</a>
</div>
<div>
Edit {{role.name}} role permissions
</div>
</div>
<div>
<div>
Allow:
</div>
<div class="box allow"></div>
<div>
Deny:
</div>
<div class="box deny"></div>
</div>
<div>
<button
mat-button
mat-raised-button
(click)="reset()">Reset
</button>
<button
mat-button
mat-raised-button
(click)="update()"
color="primary">Update
</button>
</div>
</div>
<div class="editor">
<div class="column">
<div class="title">Owned</div>
<app-editable-permission
[side]="'LEFT'"
[permission]="permission"
(click)="remove(permission)"
*ngFor="let permission of owned">
</app-editable-permission>
</div>
<mat-divider [vertical]="true"></mat-divider>
<div class="column">
<div class="title">available</div>
<app-editable-permission
[side]="'RIGHT'"
[permission]="permission"
(click)="add(permission)"
*ngFor="let permission of available">
</app-editable-permission>
</div>
</div>

View File

@ -0,0 +1,47 @@
.editor {
display: flex;
justify-content: stretch;
}
.column {
width: 50vw;
}
.header {
margin-top: 5px;
display: flex;
flex-direction: row;
justify-content: space-between;
font-size: 20px;
}
.header > div > button {
margin-right: 30px;
}
.header > div {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
}
.title {
font-size: 20px;
margin-left: 20px;
}
.box {
width: 50px;
height: 25px;
border: 1px solid;
margin-right: 20px;
margin-left: 10px;
}
.allow {
background-color: rgba(5, 76, 5, 0.38);
}
.deny {
background-color: rgba(130, 8, 8, 0.36);
}

View File

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

View File

@ -0,0 +1,104 @@
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import {Role} from "@models/api/role";
import {Server} from "@models/server";
import {Permission} from "@models/api/permission";
import {MatDialog} from "@angular/material/dialog";
import {PermissionEditorValidateDialogComponent} from "@components/role-management/role-detail/permission-editor/permission-editor-validate-dialog/permission-editor-validate-dialog.component";
import {forkJoin, Observable} from "rxjs";
import {RoleService} from "@services/role.service";
import {ToasterService} from "@services/toaster.service";
@Component({
selector: 'app-permission-editor',
templateUrl: './permission-editor.component.html',
styleUrls: ['./permission-editor.component.scss']
})
export class PermissionEditorComponent implements OnInit {
server: Server;
role: Role;
private permissions: Permission[];
owned: Set<Permission>;
available: Set<Permission>;
constructor(private route: ActivatedRoute,
private dialog: MatDialog,
private toastService: ToasterService,
private router: Router,
private roleService: RoleService) {
this.route.data.subscribe((data: { server: Server, role: Role, permissions: Permission[] }) => {
this.server = data.server;
this.role = data.role;
this.permissions = data.permissions;
this.reset();
});
}
ngOnInit(): void {
}
add(permission: Permission) {
this.available.delete(permission);
this.owned.add(permission);
}
remove(permission: Permission) {
this.owned.delete(permission);
this.available.add(permission);
}
reset() {
const ownedPermissionId = this.role.permissions.map(p => p.permission_id);
this.owned = new Set(this.role.permissions);
this.available = new Set(this.permissions.filter(p => !ownedPermissionId.includes(p.permission_id)));
}
update() {
const {add, remove} = this.diff();
this.dialog
.open(PermissionEditorValidateDialogComponent,
{width: '700px', height: '500px', data: {add, remove}})
.afterClosed()
.subscribe((confirmed: boolean) => {
if (confirmed) {
const obs: Observable<any>[] = [];
add.forEach((permission: Permission) => {
obs.push(this.roleService.addPermission(this.server, this.role, permission));
});
remove.forEach((permission: Permission) => {
obs.push(this.roleService.removePermission(this.server, this.role, permission));
});
forkJoin(obs)
.subscribe(() => {
this.toastService.success(`permissions updated`);
this.router.navigate(['/server', this.server.id, 'management', 'roles', this.role.role_id]);
},
(error) => {
this.toastService.error(error);
});
}
});
}
private diff() {
const add: Permission[] = [];
const currentRolePermissionId = this.role.permissions.map(p => p.permission_id);
this.owned.forEach((permission: Permission) => {
if (!currentRolePermissionId.includes(permission.permission_id)) {
add.push(permission);
}
});
const remove: Permission[] = [];
this.role.permissions.forEach((permission: Permission) => {
if (!this.owned.has(permission)) {
remove.push(permission);
}
});
return {add, remove};
}
}

View File

@ -0,0 +1,59 @@
<div class="content">
<div class="default-header">
<div class="row align-items-center">
<a
mat-icon-button
matTooltip="Back to role management"
matTooltipClass="custom-tooltip"
[routerLink]="['/server', server.id, 'management', 'roles']">
<mat-icon aria-label="Back to role management">keyboard_arrow_left</mat-icon>
</a>
<h1 class="col">Role {{role.name}} details</h1>
</div>
<div class="main">
<div class="details">
<div>
<mat-form-field>
<mat-label>Role name:</mat-label>
<input matInput type="text" [ngModel]="role.name">
</mat-form-field>
</div>
<div>
<mat-form-field>
<mat-label>Description:</mat-label>
<input matInput type="text" [ngModel]="role.description">
</mat-form-field>
</div>
<div>Creation date: {{role.created_at}}</div>
<div>Last update Date: {{role.updated_at}}</div>
<div>UUID: {{role.role_id}}</div>
<div>
<mat-checkbox [checked]="role.is_builtin" disabled>Is build in</mat-checkbox>
</div>
<div mat-dialog-actions class="button-div">
<button mat-button (click)="onUpdate()" tabindex="2" mat-raised-button color="primary"
[disabled]="!editRoleForm.valid">
Update Role
</button>
</div>
</div>
<mat-divider [vertical]="true"></mat-divider>
<div class="permissions">
<div class="header">
<div>Permissions</div>
<div>
<button
mat-button
[routerLink]="['/server', server.id, 'management', 'roles', role.role_id, 'permissions']">
<mat-icon>edit</mat-icon>
</button>
</div>
</div>
<app-editable-permission
[permission]="permission"
*ngFor="let permission of role.permissions">
</app-editable-permission>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,40 @@
.main {
display: flex;
justify-content: space-around;
}
.details {
width: 30vw;
display: flex;
flex-direction: column;
justify-content: center;
}
.permissions {
width: 35vw;
display: flex;
flex-direction: column;
justify-content: stretch;
}
.permission {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 10px;
border: 1px solid;
padding: 5px;
border-radius: 5px;
font-family: monospace;
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
padding-bottom: 20px;
}
.header > div {
font-size: 2em;
}

View File

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

View File

@ -0,0 +1,47 @@
import {Component, OnInit} from '@angular/core';
import {RoleService} from "@services/role.service";
import {ActivatedRoute} from "@angular/router";
import {Server} from "@models/server";
import {ServerService} from "@services/server.service";
import {Role} from "@models/api/role";
import {FormControl, FormGroup} from "@angular/forms";
import {ToasterService} from "@services/toaster.service";
@Component({
selector: 'app-role-detail',
templateUrl: './role-detail.component.html',
styleUrls: ['./role-detail.component.scss']
})
export class RoleDetailComponent implements OnInit {
server: Server;
role: Role;
editRoleForm: FormGroup;
constructor(private roleService: RoleService,
private serverService: ServerService,
private toastService: ToasterService,
private route: ActivatedRoute) {
this.editRoleForm = new FormGroup({
rolename: new FormControl(),
description: new FormControl(),
});
}
ngOnInit(): void {
this.route.data.subscribe((d: { server: Server; role: Role }) => {
this.server = d.server;
this.role = d.role;
});
}
onUpdate() {
this.roleService.update(this.server, this.role)
.subscribe(() => {
this.toastService.success(`role: ${this.role.name} was updated`);
},
(error) => {
this.toastService.error(error);
});
}
}

View File

@ -0,0 +1,8 @@
import { RoleFilterPipe } from './role-filter.pipe';
describe('RoleFilterPipe', () => {
it('create an instance', () => {
const pipe = new RoleFilterPipe();
expect(pipe).toBeTruthy();
});
});

View File

@ -0,0 +1,22 @@
import { Pipe, PipeTransform } from '@angular/core';
import {Role} from "@models/api/role";
import {User} from "@models/users/user";
import {MatTableDataSource} from "@angular/material/table";
@Pipe({
name: 'roleFilter'
})
export class RoleFilterPipe implements PipeTransform {
transform(roles: MatTableDataSource<Role[]>, searchText: string): MatTableDataSource<Role[]> {
if (!searchText) {
return roles;
}
searchText = searchText.trim().toLowerCase();
roles.filter = searchText;
return roles;
}
}

View File

@ -0,0 +1,90 @@
<div class="content" *ngIf="isReady; else loading">
<div class="default-header">
<div class="row">
<h1 class="col">Roles Management</h1>
<button class="col" mat-raised-button color="primary" (click)="onDelete(selection.selected)" class="add-button"
[disabled]="selection.selected.length == 0">
Delete roles
</button>
<button class="col" mat-raised-button color="primary" (click)="addRole()" class="add-button">
Add role
</button>
</div>
</div>
<form>
<mat-form-field class="full-width">
<input matInput placeholder="Search role by name" [(ngModel)]="searchText"
[ngModelOptions]="{ standalone: true }"/>
</mat-form-field>
</form>
<div class="default-content">
<div class="mat-elevation-z8">
<mat-table #table [dataSource]="dataSource | roleFilter: searchText" matSort>
<ng-container matColumnDef="select">
<mat-header-cell *matHeaderCellDef class="small-col">
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()">
</mat-checkbox>
</mat-header-cell>
<mat-cell *matCellDef="let row" class="small-col">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)">
</mat-checkbox>
</mat-cell>
</ng-container>
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
<mat-cell *matCellDef="let row">
<a [routerLink]="['/server', server.id, 'management','roles', row.role_id]"
class="table-link" [matTooltip]="row.name">{{ row.name }}</a>
</mat-cell>
</ng-container>
<ng-container matColumnDef="description">
<mat-header-cell *matHeaderCellDef mat-sort-header>Description</mat-header-cell>
<mat-cell *matCellDef="let row">
<div [matTooltip]="row.description" matTooltipClass="custom-tooltip"
class="overflow-col">{{ row.description }}</div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="permissions">
<mat-header-cell *matHeaderCellDef mat-sort-header>Permissions (Allow)</mat-header-cell>
<mat-cell *matCellDef="let row">
<div class="permissions-list">
<div class="overflow-col permission" *ngFor="let permission of row.permissions">
<div>{{permission.action}}</div>
<div>{{permission.methods.join(',')}}</div>
<div>{{permission.path}}</div>
</div>
</div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="delete">
<mat-header-cell *matHeaderCellDef class="small-col"></mat-header-cell>
<mat-cell *matCellDef="let row" class="small-col">
<button mat-button (click)="onDelete([row])">
<mat-icon>delete</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
<mat-paginator [pageSizeOptions]="[5, 10, 20]"
showFirstLastButtons
aria-label="Select page">
</mat-paginator>
</div>
</div>
</div>
<ng-template #loading>
<div>
<mat-spinner class="loader"></mat-spinner>
</div>
</ng-template>

View File

@ -0,0 +1,56 @@
.add-button {
height: 40px;
width: 160px;
margin: 20px;
}
.full-width {
width: 940px;
margin-left: -470px;
left: 50%;
}
.small-col {
flex-grow: 0.3;
}
.active-col {
flex-grow: 0.5;
}
.overflow-col {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
padding-right: 5px;
}
.permission {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.permission > div {
margin-right: 20px;
font-size: 10px;
}
.custom-tooltip {
font-size:100px;
}
.loader {
position: absolute;
margin: auto;
height: 175px;
bottom: 0;
left: 0;
right: 0;
top: 0;
width: 175px;
}
.permissions-list {
display: flex;
flex-direction: column;
justify-content: stretch;
}

View File

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

View File

@ -0,0 +1,120 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {Server} from "@models/server";
import {MatTableDataSource} from "@angular/material/table";
import {SelectionModel} from "@angular/cdk/collections";
import {MatPaginator} from "@angular/material/paginator";
import {MatSort} from "@angular/material/sort";
import {ActivatedRoute, Router} from "@angular/router";
import {ProgressService} from "../../common/progress/progress.service";
import {ServerService} from "@services/server.service";
import {MatDialog} from "@angular/material/dialog";
import {ToasterService} from "@services/toaster.service";
import {Role} from "@models/api/role";
import {RoleService} from "@services/role.service";
import {AddRoleDialogComponent} from "@components/role-management/add-role-dialog/add-role-dialog.component";
import {DeleteRoleDialogComponent} from "@components/role-management/delete-role-dialog/delete-role-dialog.component";
import {forkJoin} from "rxjs";
@Component({
selector: 'app-role-management',
templateUrl: './role-management.component.html',
styleUrls: ['./role-management.component.scss']
})
export class RoleManagementComponent implements OnInit {
server: Server;
dataSource = new MatTableDataSource<Role>();
displayedColumns = ['select', 'name', 'description', 'permissions', 'delete'];
selection = new SelectionModel<Role>(true, []);
searchText = '';
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort, {static: true}) sort: MatSort;
isReady = false;
constructor(
private route: ActivatedRoute,
private router: Router,
private roleService: RoleService,
private progressService: ProgressService,
private serverService: ServerService,
public dialog: MatDialog,
private toasterService: ToasterService) {
}
ngOnInit() {
const serverId = this.route.parent.snapshot.paramMap.get('server_id');
this.serverService.get(+serverId).then((server: Server) => {
this.server = server;
this.refresh();
});
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
refresh() {
this.roleService.get(this.server).subscribe(
(roles: Role[]) => {
this.isReady = true;
this.dataSource.data = roles;
},
(error) => {
this.progressService.setError(error);
}
);
}
addRole() {
const dialogRef = this.dialog.open(AddRoleDialogComponent, {
width: '400px',
autoFocus: false,
disableClose: true,
data: {server: this.server},
})
.afterClosed()
.subscribe((role: { name: string; description: string }) => {
if (role) {
this.roleService.create(this.server, role)
.subscribe(() => {
this.toasterService.success(`${role.name} role created`);
this.refresh();
},
(error) => this.toasterService.error(error));
}
});
}
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
onDelete(rolesToDelete: Role[]) {
this.dialog
.open(DeleteRoleDialogComponent, {width: '500px', height: '250px', data: {roles: rolesToDelete}})
.afterClosed()
.subscribe((isDeletedConfirm) => {
if (isDeletedConfirm) {
const observables = rolesToDelete.map((role: Role) => this.roleService.delete(this.server, role.role_id));
forkJoin(observables)
.subscribe(() => {
this.refresh();
},
(error) => {
this.toasterService.error(`An error occur while trying to delete role`);
});
}
});
}
}

View File

@ -27,6 +27,7 @@
.custom-tooltip {
font-size:100px;
white-space: pre-line;
}
.loader {

View File

View File

@ -25,11 +25,11 @@ export enum PermissionActions {
}
export interface Permission {
methods: Methods[],
path: string,
action: PermissionActions,
description: string,
created_at: string,
updated_at: string,
permission_id: string
methods: Methods[];
path: string;
action: PermissionActions;
description: string;
created_at: string;
updated_at: string;
permission_id: string;
}

View File

@ -0,0 +1,11 @@
import {Permission} from "./permission";
export interface Role {
name: string;
description: string;
created_at: string;
updated_at: string;
role_id: string;
is_builtin: boolean;
permissions: Permission[];
}

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { PermissionResolver } from './permission.resolver';
describe('PermissionResolver', () => {
let resolver: PermissionResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
resolver = TestBed.inject(PermissionResolver);
});
it('should be created', () => {
expect(resolver).toBeTruthy();
});
});

View File

@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import {
Router, Resolve,
RouterStateSnapshot,
ActivatedRouteSnapshot
} from '@angular/router';
import {Observable, of, Subscriber} from 'rxjs';
import {Permission} from "@models/api/permission";
import {PermissionsService} from "@services/permissions.service";
import {ServerService} from "@services/server.service";
import {Server} from "@models/server";
@Injectable({
providedIn: 'root'
})
export class PermissionResolver implements Resolve<Permission[]> {
constructor(private permissionService: PermissionsService,
private serverService: ServerService) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Permission[]> {
return new Observable<Permission[]>((observer: Subscriber<Permission[]>) => {
const serverId = route.paramMap.get('server_id');
this.serverService.get(+serverId).then((server: Server) => {
this.permissionService.list(server).subscribe((permission: Permission[]) => {
observer.next(permission);
observer.complete();
});
});
});
}
}

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { RoleDetailResolver } from './role-detail.resolver';
describe('RoleDetailResolver', () => {
let resolver: RoleDetailResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
resolver = TestBed.inject(RoleDetailResolver);
});
it('should be created', () => {
expect(resolver).toBeTruthy();
});
});

View File

@ -0,0 +1,36 @@
import {Injectable} from '@angular/core';
import {
Router, Resolve,
RouterStateSnapshot,
ActivatedRouteSnapshot
} from '@angular/router';
import {Observable, of, Subscriber} from 'rxjs';
import {Server} from "../models/server";
import {Role} from "../models/api/role";
import {ServerService} from "../services/server.service";
import {RoleService} from "../services/role.service";
@Injectable({
providedIn: 'root'
})
export class RoleDetailResolver implements Resolve<Role> {
constructor(private serverService: ServerService,
private roleService: RoleService) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Role> {
return new Observable<Role>((observer: Subscriber<Role>) => {
const serverId = route.paramMap.get('server_id');
const roleId = route.paramMap.get('role_id');
this.serverService.get(+serverId).then((server: Server) => {
this.roleService.getById(server, roleId).subscribe((role: Role) => {
observer.next( role);
observer.complete();
});
});
});
}
}

View File

@ -13,7 +13,7 @@
import { Injectable } from '@angular/core';
import {HttpServer} from "./http-server.service";
import {Server} from "../models/server";
import {Permission} from "../models/permission";
import {Permission} from "../models/api/permission";
import {Observable} from "rxjs/Rx";
@Injectable({

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { RoleService } from './role.service';
describe('RoleService', () => {
let service: RoleService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(RoleService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,43 @@
import { Injectable } from '@angular/core';
import {HttpServer} from "./http-server.service";
import {Server} from "../models/server";
import {Group} from "../models/groups/group";
import {Role} from "../models/api/role";
import {Permission} from "@models/api/permission";
@Injectable({
providedIn: 'root'
})
export class RoleService {
constructor(private httpServer: HttpServer) { }
get(server: Server) {
return this.httpServer.get<Role[]>(server, '/roles');
}
delete(server: Server, role_id: string) {
return this.httpServer.delete(server, `/roles/${role_id}`);
}
create(server: Server, newRole: { name: string; description: string }) {
return this.httpServer.post(server, `/roles`, newRole);
}
getById(server: Server, roleId: string) {
return this.httpServer.get<Role>(server, `/roles/${roleId}`);
}
update(server: Server, role: Role) {
return this.httpServer.put(server, `/roles/${role.role_id}`, {name: role.name, description: role.description});
}
addPermission(server: Server, role: Role, permission: Permission) {
return this.httpServer.put(server, `/roles/${role.role_id}/permissions/${permission.permission_id}`, {});
}
removePermission(server: Server, role: Role, permission: Permission) {
return this.httpServer.delete(server, `/roles/${role.role_id}/permissions/${permission.permission_id}`);
}
}

View File

@ -0,0 +1,29 @@
/*
* Software Name : GNS3 Web UI
* Version: 3
* SPDX-FileCopyrightText: Copyright (c) 2022 Orange Business Services
* SPDX-License-Identifier: GPL-3.0-or-later
*
* This software is distributed under the GPL-3.0 or any later version,
* the text of which is available at https://www.gnu.org/licenses/gpl-3.0.txt
* or see the "LICENSE" file for more details.
*
* Author: Sylvain MATHIEU, Elise LEBEAU
*/
import { Injectable } from '@angular/core';
import {HttpServer} from "./http-server.service";
import {Server} from "../models/server";
import {Observable} from "rxjs";
@Injectable({
providedIn: 'root'
})
export class ServerVersionService {
constructor(private httpServer: HttpServer) { }
public checkServerVersion(server: Server): Observable<any> {
return this.httpServer.get(server, '/version');
}
}

View File

@ -51,3 +51,14 @@ mat-menu-panel {
background-color: grey;
color: #ffffff;
}
.permission-tooltip {
max-width: unset !important;
background-color: grey;
color: #ffffff;
background-color: grey;
color: #ffffff;
white-space: pre-line;
font-size: 12px !important;
font-family: monospace;
}

View File

@ -4,6 +4,8 @@
],
"rules": {
"arrow-return-shorthand": true,
"no-implicit-dependencies": false,
"no-submodule-imports": false,
"callable-types": true,
"class-name": true,
"comment-format": [