suppression des permissions

This commit is contained in:
Elise Lebeau 2023-09-12 15:03:02 +02:00
parent bae7bcdd8d
commit 7bad625fdd
76 changed files with 6 additions and 2456 deletions

View File

@ -60,20 +60,15 @@ import { ImageManagerComponent } from './components/image-manager/image-manager.
import { UserDetailComponent } from "./components/user-management/user-detail/user-detail.component";
import { UserDetailResolver } from "./resolvers/user-detail.resolver";
import { ManagementComponent } from "./components/management/management.component";
import { PermissionResolver } from "./resolvers/permission.resolver";
import { UserGroupsResolver } from "./resolvers/user-groups.resolver";
import { UserPermissionsResolver } from "./resolvers/user-permissions.resolver";
import { GroupManagementComponent } from "./components/group-management/group-management.component";
import { RoleManagementComponent } from "./components/role-management/role-management.component";
import { PermissionsManagementComponent } from "./components/permissions-management/permissions-management.component";
import { GroupDetailsComponent } from "./components/group-details/group-details.component";
import { GroupMembersResolver } from "./resolvers/group-members.resolver";
import { GroupResolver } from "./resolvers/group.resolver";
import { GroupRoleResolver } from "./resolvers/group-role.resolver";
import { RoleDetailComponent } from "./components/role-management/role-detail/role-detail.component";
import { RoleDetailResolver } from "./resolvers/role-detail.resolver";
import { RolePermissionsComponent } from "./components/role-management/role-detail/role-permissions/role-permissions.component";
import { UserPermissionsComponent } from "./components/user-management/user-detail/user-permissions/user-permissions.component";
const routes: Routes = [
{
@ -102,7 +97,6 @@ const routes: Routes = [
resolve: {
user: UserDetailResolver,
groups: UserGroupsResolver,
permissions: UserPermissionsResolver,
controller: ControllerResolve},
},
{ path: 'installed-software', component: InstalledSoftwareComponent },
@ -237,10 +231,6 @@ const routes: Routes = [
{
path: 'roles',
component: RoleManagementComponent
},
{
path: 'permissions',
component: PermissionsManagementComponent
}
]
},
@ -262,25 +252,6 @@ const routes: Routes = [
controller: ControllerResolve
}
},
{
path: 'controller/:controller_id/management/roles/:role_id/permissions',
component: RolePermissionsComponent,
resolve: {
role: RoleDetailResolver,
controller: ControllerResolve,
permissions: PermissionResolver
}
},
{
path: 'controller/:controller_id/management/users/:user_id/permissions',
component: UserPermissionsComponent,
resolve: {
user: UserDetailResolver,
userPermissions: UserPermissionsResolver,
controller: ControllerResolve,
permissions: PermissionResolver
}
}
],
},
{

View File

@ -295,25 +295,10 @@ 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';
import { PermissionsManagementComponent } from './components/permissions-management/permissions-management.component';
import { PermissionEditLineComponent } from '@components/permissions-management/permission-edit-line/permission-edit-line.component';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import { UserPermissionsComponent } from './components/user-management/user-detail/user-permissions/user-permissions.component';
import {MatAutocompleteModule} from "@angular/material/autocomplete";
import {PathAutoCompleteComponent} from './components/permissions-management/add-permission-line/path-auto-complete/path-auto-complete.component';
import {FilterCompletePipe} from './components/permissions-management/add-permission-line/path-auto-complete/filter-complete.pipe';
import { AddPermissionLineComponent } from './components/permissions-management/add-permission-line/add-permission-line.component';
import { MethodButtonComponent } from './components/permissions-management/method-button/method-button.component';
import { ActionButtonComponent } from './components/permissions-management/action-button/action-button.component';
import { DeletePermissionDialogComponent } from './components/permissions-management/delete-permission-dialog/delete-permission-dialog.component';
import {MatAutocompleteModule} from "@angular/material/autocomplete";;
import { AddRoleToGroupComponent } from './components/group-details/add-role-to-group/add-role-to-group.component';
import {MatFormFieldModule} from "@angular/material/form-field";
import { PermissionsFilterPipe } from './components/permissions-management/permissions-filter.pipe';
import { DisplayPathPipe } from './components/permissions-management/display-path.pipe';
import {RolePermissionsComponent} from "@components/role-management/role-detail/role-permissions/role-permissions.component";
import { ChangeUserPasswordComponent } from './components/user-management/user-detail/change-user-password/change-user-password.component';
import {MatMenuModule} from "@angular/material/menu";
import { ImageManagerComponent } from './components/image-manager/image-manager.component';
@ -533,26 +518,9 @@ import { ProjectMapLockConfirmationDialogComponent } from './components/project-
AddRoleDialogComponent,
DeleteRoleDialogComponent,
RoleDetailComponent,
PermissionEditorComponent,
EditablePermissionComponent,
PermissionEditorValidateDialogComponent,
RemoveToGroupDialogComponent,
PermissionsManagementComponent,
AddRoleToGroupComponent,
PermissionEditLineComponent,
AddPermissionLineComponent,
MethodButtonComponent,
ActionButtonComponent,
DeletePermissionDialogComponent,
PathAutoCompleteComponent,
FilterCompletePipe,
UserPermissionsComponent,
PermissionsFilterPipe,
RolePermissionsComponent,
DisplayPathPipe,
ChangeUserPasswordComponent,
FilterCompletePipe,
DisplayPathPipe,
ChangeUserPasswordComponent,
ProjectReadmeComponent,
ImageManagerComponent,

View File

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

View File

@ -1,6 +0,0 @@
<button [ngClass]="{allow: action === 'ALLOW', deny: action === 'DENY'}"
mat-button
[disabled]="disabled"
(click)="change()">
{{action}}
</button>

View File

@ -1,8 +0,0 @@
.allow {
background-color: green;
border-radius: unset !important;
}
.deny {
background-color: darkred;
}

View File

@ -1,38 +0,0 @@
/*
* 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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {PermissionActions} from "@models/api/permission";
@Component({
selector: 'app-action-button',
templateUrl: './action-button.component.html',
styleUrls: ['./action-button.component.scss']
})
export class ActionButtonComponent implements OnInit {
readonly DENY = 'DENY';
readonly ALLOW = 'ALLOW';
@Input() action: PermissionActions;
@Input() disabled = true;
@Output() update = new EventEmitter<PermissionActions>();
constructor() { }
ngOnInit(): void {
}
change() {
this.action === PermissionActions.DENY ? this.action = PermissionActions.ALLOW : this.action = PermissionActions.DENY;
this.update.emit(this.action);
}
}

View File

@ -1,48 +0,0 @@
<div class="box-border">
<div *ngIf="edit; else add">
<div class="edit-mode">
<div class="information-box">
<div>
<app-path-auto-complete
[controller]="controller"
(update)="permission.path = $event"></app-path-auto-complete>
</div>
<div class="methods">
<app-action-button
[disabled]="false"
[action]="permission.action"></app-action-button>
<div *ngFor="let method of apiInformation.getMethods(permission.path) | async">
<app-method-button
[name]="method"
[disabled]="false"
(update)="updateMethod($event)"></app-method-button>
</div>
<div class="description">
<mat-form-field>
<input
[(ngModel)]="permission.description"
matInput
type="text"
placeholder="Description"/>
</mat-form-field>
</div>
</div>
</div>
<div class="button-box">
<button mat-button (click)="reset()">
<mat-icon>cancel</mat-icon>
</button>
<button mat-button (click)="save()">
<mat-icon>done</mat-icon>
</button>
</div>
</div>
</div>
<ng-template #add>
<div class="not-edit">
<button mat-button (click)="edit = true">
<mat-icon>add</mat-icon>
</button>
</div>
</ng-template>
</div>

View File

@ -1,49 +0,0 @@
.box-border {
width: 100%;
margin-top: 20px;
border-bottom: 1px solid;
}
.edit-mode {
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.information-box {
margin-left: 10px;
width: 100%;
}
.information-box > div {
margin-bottom: 10px;
}
.methods {
display: flex;
flex-direction: row;
align-items: center;
}
.button-box {
display: flex;
flex-direction: column;
justify-content: space-around;
}
.description {
width: 100%;
margin-left: 10px;
margin-right: 10px;
}
.description > mat-form-field {
width: 100%;
}
.not-edit {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}

View File

@ -1,132 +0,0 @@
/* tslint:disable:no-shadowed-variable */
import {fakeAsync, TestBed, tick} from "@angular/core/testing";
import {AddPermissionLineComponent} from "@components/permissions-management/add-permission-line/add-permission-line.component";
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
import {PermissionsService} from "@services/permissions.service";
import {ToasterService} from "@services/toaster.service";
import {Methods, Permission, PermissionActions} from "@models/api/permission";
import {Controller} from "@models/controller";
import {Observable, of, throwError} from "rxjs";
import {HttpErrorResponse} from "@angular/common/http";
class MockApiInformationService {
}
class MockPermissionService {
}
class MockToasterService {
}
describe('AddPermissionLineComponent', () => {
beforeEach(async () => {
TestBed.configureTestingModule({
providers: [
AddPermissionLineComponent,
{provide: ApiInformationService, useClass: MockApiInformationService},
{provide: PermissionsService, useClass: MockPermissionService},
{provide: ToasterService, useClass: MockToasterService}
]
});
});
it('Should add GET method to method list', () => {
const comp = TestBed.inject(AddPermissionLineComponent);
comp.updateMethod({name: Methods.GET, enable: true});
expect(comp.permission.methods).toContain(Methods.GET);
});
it('Should remove GET Method from list', () => {
const comp = TestBed.inject(AddPermissionLineComponent);
comp.permission.methods = [Methods.GET, Methods.PUT, Methods.POST, Methods.DELETE];
comp.updateMethod({name: Methods.GET, enable: false});
expect(comp.permission.methods).not.toContain(Methods.GET);
});
it('Should not add same GET method a second time', () => {
const comp = TestBed.inject(AddPermissionLineComponent);
comp.permission.methods = [Methods.GET, Methods.PUT, Methods.POST, Methods.DELETE];
comp.updateMethod({name: Methods.GET, enable: true});
expect(comp.permission.methods).toEqual([Methods.GET, Methods.PUT, Methods.POST, Methods.DELETE]);
});
it('Should reset permission values', () => {
const comp = TestBed.inject(AddPermissionLineComponent);
comp.permission.methods = [Methods.GET, Methods.PUT, Methods.POST, Methods.DELETE];
comp.permission.path = "/test/path";
comp.permission.action = PermissionActions.DENY;
comp.permission.description = "john doe is here";
comp.reset();
const p = comp.permission;
expect(p.methods).toEqual([]);
expect(p.action).toEqual(PermissionActions.ALLOW);
expect(p.description).toEqual('');
});
it('Should save permission with success', fakeAsync(() => {
const comp = TestBed.inject(AddPermissionLineComponent);
const permissionService = TestBed.inject(PermissionsService);
const toasterService = TestBed.inject(ToasterService);
comp.permission.methods = [Methods.GET, Methods.PUT, Methods.POST, Methods.DELETE];
comp.permission.path = "/test/path";
comp.permission.action = PermissionActions.DENY;
comp.permission.description = "john doe is here";
permissionService.add = (controller: Controller, permission: Permission): Observable<Permission> => {
return of(permission);
};
let message: string;
toasterService.success = (m: string) => {
message = m;
};
comp.save();
const p = comp.permission;
tick();
expect(message).toBeTruthy();
expect(p.methods).toEqual([]);
expect(p.action).toEqual(PermissionActions.ALLOW);
expect(p.description).toEqual('');
}));
it('Should throw error on rejected permission', fakeAsync(() => {
const comp = TestBed.inject(AddPermissionLineComponent);
const permissionService = TestBed.inject(PermissionsService);
const toasterService = TestBed.inject(ToasterService);
comp.permission.methods = [Methods.GET, Methods.PUT, Methods.POST, Methods.DELETE];
comp.permission.path = "/test/path";
comp.permission.action = PermissionActions.DENY;
comp.permission.description = "john doe is here";
let errorMessage: string;
permissionService.add = (controller: Controller, permission: Permission): Observable<Permission> => {
const error = new HttpErrorResponse({
error: new Error("An error occur"),
headers: undefined,
status: 500,
statusText: 'error from controller'
});
return throwError(error);
};
toasterService.error = (message: string) => {
errorMessage = message;
};
comp.save();
tick();
expect(errorMessage).toBeTruthy();
}));
});

View File

@ -1,82 +0,0 @@
/*
* 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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Controller} from "@models/controller";
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
import {Methods, Permission, PermissionActions} from "@models/api/permission";
import {PermissionsService} from "@services/permissions.service";
import {ToasterService} from "@services/toaster.service";
import {HttpErrorResponse} from "@angular/common/http";
@Component({
selector: 'app-add-permission-line',
templateUrl: './add-permission-line.component.html',
styleUrls: ['./add-permission-line.component.scss']
})
export class AddPermissionLineComponent implements OnInit {
@Input() controller: Controller;
@Output() addPermissionEvent = new EventEmitter<void>();
permission: Permission = {
action: PermissionActions.ALLOW,
description: "",
methods: [],
path: "/"
};
edit = false;
constructor(public apiInformation: ApiInformationService,
private permissionService: PermissionsService,
private toasterService: ToasterService) {
}
ngOnInit(): void {
}
updateMethod(data: { name: Methods; enable: boolean }) {
const set = new Set(this.permission.methods);
if (data.enable) {
set.add(data.name);
} else {
set.delete(data.name);
}
this.permission.methods = Array.from(set);
}
reset() {
this.permission = {
action: PermissionActions.ALLOW,
description: "",
methods: [],
path: "/",
};
this.edit = false;
}
save() {
this.permissionService.add(this.controller, this.permission)
.subscribe(() => {
this.toasterService.success(`permission was created`);
this.reset();
}, (error: HttpErrorResponse) => {
this.toasterService.error(`
${error.message}
${error.error.message}`);
});
}
}

View File

@ -1,82 +0,0 @@
import {PermissionPath} from "@components/permissions-management/add-permission-line/path-auto-complete/PermissionPath";
import {SubPath} from "@components/permissions-management/add-permission-line/path-auto-complete/SubPath";
describe('PermissionPath', () => {
it('Should add subPath to path', () => {
const path = new PermissionPath();
path.add(new SubPath('projects', 'projects', undefined));
path.add(new SubPath('1111-2222-3333-4444', 'my project', 'project_id'));
expect(path.getPath()).toEqual(['projects', '1111-2222-3333-4444']);
});
it('Should return display path', () => {
const path = new PermissionPath();
path.add(new SubPath('projects', 'projects', undefined));
path.add(new SubPath('1111-2222-3333-4444', 'my project', 'project_id'));
expect(path.getDisplayPath()).toEqual(['projects', 'my project']);
});
it('Should remove last element', () => {
const path = new PermissionPath();
path.add(new SubPath('projects', 'projects', undefined));
path.add(new SubPath('1111-2222-3333-4444', 'my project', 'project_id'));
path.add(new SubPath('nodes', 'nodes'));
path.add(new SubPath('6666-7777-8888-9999', 'myFirstNode', 'node_id'));
path.removeLast();
expect(path.getPath()).toEqual(['projects', '1111-2222-3333-4444', 'nodes']);
});
it('Should return path variables', () => {
const path = new PermissionPath();
path.add(new SubPath('projects', 'projects', undefined));
path.add(new SubPath('1111-2222-3333-4444', 'my project', 'project_id'));
path.add(new SubPath('nodes', 'nodes'));
path.add(new SubPath('6666-7777-8888-9999', 'myFirstNode', 'node_id'));
expect(path.getVariables())
.toEqual([{key: 'project_id', value: '1111-2222-3333-4444'}, { key: 'node_id', value: '6666-7777-8888-9999'}]);
});
it('Should return true if subPath contain *', () => {
const path = new PermissionPath();
path.add(new SubPath('projects', 'projects', undefined));
path.add(new SubPath('1111-2222-3333-4444', 'my project', 'project_id'));
path.add(new SubPath('nodes', 'nodes'));
path.add(new SubPath('*', 'myFirstNode', 'node_id'));
expect(path.containStar()).toBeTruthy();
});
it('Should return false if subPath does not contain *', () => {
const path = new PermissionPath();
path.add(new SubPath('projects', 'projects', undefined));
path.add(new SubPath('1111-2222-3333-4444', 'my project', 'project_id'));
path.add(new SubPath('nodes', 'nodes'));
path.add(new SubPath('6666-7777-8888-999', 'myFirstNode', 'node_id'));
expect(path.containStar()).toBeFalsy();
});
it('Should return true if path is empty', () => {
const path = new PermissionPath();
expect(path.isEmpty()).toBeTruthy();
});
it('Should return false if path is not empty', () => {
const path = new PermissionPath();
path.add(new SubPath('projects', 'projects', undefined));
path.add(new SubPath('1111-2222-3333-4444', 'my project', 'project_id'));
path.add(new SubPath('nodes', 'nodes'));
path.add(new SubPath('6666-7777-8888-999', 'myFirstNode', 'node_id'));
expect(path.isEmpty()).toBeFalsy();
});
});

View File

@ -1,56 +0,0 @@
/*
* 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 {SubPath} from "./SubPath";
export class PermissionPath {
private subPath: SubPath[] = [];
constructor() {
}
add(subPath: SubPath) {
this.subPath.push(subPath);
}
getDisplayPath() {
return this.subPath
.map((subPath) => subPath.displayValue);
}
removeLast() {
this.subPath.pop();
}
getPath() {
return this.subPath.map((subPath) => subPath.value);
}
isEmpty() {
return this.subPath.length === 0;
}
getVariables(): { key: string; value: string }[] {
return this.subPath
.filter((path) => path.key)
.map((path) => {
return {key: path.key, value: path.value};
});
}
containStar() {
return this.subPath
.map(subPath => subPath.value === '*')
.reduce((previous, next) => previous || next, false);
}
}

View File

@ -1,24 +0,0 @@
/*
* 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
*/
export class SubPath {
/**
* @param {value} original subPath value from gns3 api
* @param {displayValue} displayed value can replace a UUID from original URL
* @param {key} associate key ex: 'project_id'
*/
constructor(public value: string,
public displayValue: string,
public key?: string) {
}
}

View File

@ -1,37 +0,0 @@
import {FilterCompletePipe} from "@components/permissions-management/add-permission-line/path-auto-complete/filter-complete.pipe";
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
describe('FilterCompletePipe', () => {
it('should remove items which not match searchText', function () {
const filter = new FilterCompletePipe();
const items: IGenericApiObject[] = [
{id: 'b2afe0da-b83e-42a8-bcb6-e46ca1bd1747', name: 'test project 1'},
{id: '698d35c1-9fd0-4b89-86dc-336a958b1f70', name: 'test project 2'},
{id: '4bbd57e6-bf99-4387-8948-7e7d8e96de9b', name: 'test project 3'},
{id: '29e9ddb6-1ba0-422d-b767-92592821f011', name: 'test project 4'},
{id: '5a522134-0bfd-4864-b8b3-520bcecd4fc9', name: 'test project 5'},
{id: '7e27f67a-2b63-4d00-936b-e3d8c7e2b751', name: 'test project 6'},
];
expect(filter.transform(items, 'test project 6'))
.toEqual([{id: '7e27f67a-2b63-4d00-936b-e3d8c7e2b751', name: 'test project 6'}]);
});
it('should return entire list if searchText is empty', function () {
const filter = new FilterCompletePipe();
const items: IGenericApiObject[] = [
{id: 'b2afe0da-b83e-42a8-bcb6-e46ca1bd1747', name: 'test project 1'},
{id: '698d35c1-9fd0-4b89-86dc-336a958b1f70', name: 'test project 2'},
{id: '4bbd57e6-bf99-4387-8948-7e7d8e96de9b', name: 'test project 3'},
{id: '29e9ddb6-1ba0-422d-b767-92592821f011', name: 'test project 4'},
{id: '5a522134-0bfd-4864-b8b3-520bcecd4fc9', name: 'test project 5'},
{id: '7e27f67a-2b63-4d00-936b-e3d8c7e2b751', name: 'test project 6'},
];
expect(filter.transform(items, '')).toEqual(items);
expect(filter.transform(items, undefined)).toEqual(items);
});
});

View File

@ -1,32 +0,0 @@
/*
* 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 { Pipe, PipeTransform } from '@angular/core';
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
/**
* Pipe to filter autocomplete proposals
*/
@Pipe({
name: 'filterComplete'
})
export class FilterCompletePipe implements PipeTransform {
transform(value: IGenericApiObject[], searchText: string): IGenericApiObject[] {
if (!searchText || searchText === '') { return value; }
return value.filter((v) => {
return v.name.includes(searchText) || v.id.includes(searchText);
});
}
}

View File

@ -1,37 +0,0 @@
<div class="path">
<div>Path: /</div>
<div *ngFor="let p of path.getDisplayPath()">{{p}}/</div>
<div class="path-edit-line">
<div>
<div *ngIf="mode === 'SELECT'">
<mat-select (valueChange)="valueChanged($event)" class="edit-area">
<mat-option *ngFor="let value of values" value="{{value}}">{{value}}</mat-option>
</mat-select>
</div>
<div *ngIf="mode === 'COMPLETE'">
<input matInput
autofocus
class="complete edit-area"
aria-label="find"
[(ngModel)]="completeField"
[matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option [value]="'*'">*</mat-option>
<mat-option *ngFor="let data of completeData.data | filterComplete: completeField"
[value]="data.name">
<span>{{data.name}}</span>
</mat-option>
</mat-autocomplete>
</div>
</div>
<div class="command-button">
<mat-icon (click)="removePrevious()" *ngIf="!path.isEmpty()">cancel</mat-icon>
<mat-icon (click)="getNext()" *ngIf="!this.mode">add_circle_outline</mat-icon>
<mat-icon
matTooltip="validate data"
(click)="validComplete()"
*ngIf="this.mode === 'COMPLETE'">check_circle
</mat-icon>
</div>
</div>
</div>

View File

@ -1,22 +0,0 @@
.path {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
mat-select {
width: 150px;
}
.edit-area {
border: 1px solid;
}
.command-button {
margin-left: 5px;
}
.path-edit-line {
display: flex;
flex-direction: row;
}

View File

@ -1,98 +0,0 @@
/*
* 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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
import {Controller} from "@models/controller";
import {PermissionPath} from "@components/permissions-management/add-permission-line/path-auto-complete/PermissionPath";
import {SubPath} from "@components/permissions-management/add-permission-line/path-auto-complete/SubPath";
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
@Component({
selector: 'app-path-auto-complete',
templateUrl: './path-auto-complete.component.html',
styleUrls: ['./path-auto-complete.component.scss']
})
export class PathAutoCompleteComponent implements OnInit {
@Output() update = new EventEmitter<string>();
@Input() controller: Controller;
path: PermissionPath = new PermissionPath();
values: string[] = [];
private completeData: { data: IGenericApiObject[]; key: string };
public mode: 'SELECT' | 'COMPLETE' | undefined;
completeField: string;
constructor(private apiInformationService: ApiInformationService) {
}
updatePath(name: string, value: string, key?: string) {
this.path.add(new SubPath(name, value, key));
this.update.emit('/' + this.path.getPath().join("/"));
}
popPath() {
this.path.removeLast();
this.update.emit('/' + this.path.getPath().join("/"));
}
ngOnInit(): void {
}
getNext() {
this.apiInformationService
.getPathNextElement(this.path.getPath())
.subscribe((next: string[]) => {
if (this.path.containStar()) {
next = next.filter(item => !item.match(this.apiInformationService.bracketIdRegex));
}
this.values = next;
this.mode = 'SELECT';
});
}
removePrevious() {
if (this.mode) {
return this.mode = undefined;
}
if (!this.path.isEmpty()) {
return this.popPath();
}
}
valueChanged(value: string) {
if (value.match(this.apiInformationService.bracketIdRegex) && !this.path.containStar()) {
this.apiInformationService.getListByObjectId(this.controller, value, undefined, this.path.getVariables())
.subscribe((data) => {
this.mode = 'COMPLETE';
this.completeData = {data, key: value};
});
} else {
this.updatePath(value, value);
this.mode = undefined;
}
}
validComplete() {
if (this.completeField === '*') {
this.updatePath('*', '*');
} else {
const data = this.completeData.data.find((d) => this.completeField === d.name);
this.updatePath(data.id, data.name, this.completeData.key);
}
this.mode = undefined;
this.completeField = undefined;
}
}

View File

@ -1,12 +0,0 @@
<div class="description">
<div>confirm deleting permission:</div>
<div>{{data.permission_id}}</div>
<div>{{data.path}}</div>
<div>{{data.methods.join(',')}}</div>
<div>{{data.action}}</div>
<div>{{data.description}}</div>
</div>
<div class="button">
<button mat-button mat-raised-button (click)="cancel()">No, cancel</button>
<button mat-button mat-raised-button color="primary" (click)="confirm()">Yes, remove</button>
</div>

View File

@ -1,18 +0,0 @@
.description {
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
margin-top: 20px;
}
.description > div {
margin-bottom: 10px;
}
.button {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-top: 20px;
}

View File

@ -1,37 +0,0 @@
/*
* 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 {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {Permission} from "@models/api/permission";
@Component({
selector: 'app-delete-permission-dialog',
templateUrl: './delete-permission-dialog.component.html',
styleUrls: ['./delete-permission-dialog.component.scss']
})
export class DeletePermissionDialogComponent implements OnInit {
constructor(private dialog: MatDialogRef<DeletePermissionDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: Permission) { }
ngOnInit(): void {
}
cancel() {
this.dialog.close(false);
}
confirm() {
this.dialog.close(true);
}
}

View File

@ -1,57 +0,0 @@
import {async, fakeAsync, TestBed, tick} from "@angular/core/testing";
import {DisplayPathPipe} from "@components/permissions-management/display-path.pipe";
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
import {Controller} from "@models/controller";
import {Observable, of} from "rxjs";
import {IExtraParams} from "@services/ApiInformation/IExtraParams";
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
class MockApiInformationService {
}
describe('DisplayPathPipe', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
DisplayPathPipe,
{provide: ApiInformationService, useClass: MockApiInformationService}]
});
}));
it('Should display human readable path', fakeAsync(() => {
const comp = TestBed.inject(DisplayPathPipe);
const apiService = TestBed.inject(ApiInformationService);
apiService.getKeysForPath = (path: string): Observable<{ key: string; value: string }[]> => {
return of([
{key: 'project_id', value: '1111-2222-3333'},
{key: 'node_id', value: '2222-2222-2222'}
]);
};
apiService
.getListByObjectId = (controller: Controller, key: string, value?: string, extraParams?: IExtraParams[]): Observable<IGenericApiObject[]> => {
if (key === 'project_id') {
return of([{id: '1111-2222-3333', name: 'myProject'}]);
}
if (key === 'node_id') {
return of([{id: '2222-2222-2222', name: 'node1'}]);
}
};
let result: string;
const controller = new Controller();
comp
.transform('/project/1111-2222-3333/nodes/2222-2222-2222', controller)
.subscribe((res: string) => {
result = res;
});
tick();
expect(result).toEqual('/project/myProject/nodes/node1');
}));
});

View File

@ -1,54 +0,0 @@
/*
* 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 {Pipe, PipeTransform} from '@angular/core';
import {map, switchMap} from "rxjs/operators";
import {forkJoin, Observable, of} from "rxjs";
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
import {Controller} from "@models/controller";
@Pipe({
name: 'displayPath'
})
export class DisplayPathPipe implements PipeTransform {
constructor(private apiInformation: ApiInformationService) {
}
transform(originalPath: string, controller: Controller): Observable<string> {
if (!controller) {
return of(originalPath);
}
return this.apiInformation
.getKeysForPath(originalPath)
.pipe(switchMap((values) => {
if (values.length === 0) {
return of([]);
}
const obs = values.map((k) => this.apiInformation.getListByObjectId(controller, k.key, k.value, values));
return forkJoin(obs);
}),
map((values: { id: string; name: string }[][]) => {
let displayPath = `${originalPath}`;
values.forEach((value) => {
if (value[0].id && value[0].name) {
displayPath = displayPath.replace(value[0].id, value[0].name);
} else {
}
});
return displayPath;
})
);
}
}

View File

@ -1,7 +0,0 @@
<button
[disabled]="disabled"
[ngClass]="{enable: enable, disabled: disabled}"
(click)="change()"
mat-button>
{{name}}
</button>

View File

@ -1,10 +0,0 @@
:host {
padding: unset !important;
}
.enable {
color: green !important;
}
.disabled {
color: dimgrey;
}

View File

@ -1,53 +0,0 @@
import {async, fakeAsync, TestBed} from "@angular/core/testing";
import {MethodButtonComponent} from "@components/permissions-management/method-button/method-button.component";
import {Methods} from "@models/api/permission";
describe('MethodButtonComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({declarations: [MethodButtonComponent]});
}));
it('Should set text color to green when button is enable', fakeAsync(() => {
const fixture = TestBed.createComponent(MethodButtonComponent);
const component = fixture.componentInstance;
const debugElement = fixture.debugElement;
component.enable = true;
fixture.detectChanges();
expect(debugElement.nativeElement.querySelector('button').classList).toContain('enable');
}));
it('Should switch to enable on button click', (() => {
const fixture = TestBed.createComponent(MethodButtonComponent);
const component = fixture.componentInstance;
fixture.detectChanges();
component.enable = false;
component.change();
expect(component.enable).toEqual(true);
}));
it('Should emit event enable on button click', (() => {
const fixture = TestBed.createComponent(MethodButtonComponent);
const component = fixture.componentInstance;
fixture.detectChanges();
component.update.subscribe((data) => {
expect(data.enable).toEqual(true);
expect(data.name).toEqual(Methods.GET);
});
component.name = Methods.GET;
component.enable = false;
component.change();
}));
});

View File

@ -1,38 +0,0 @@
/*
* 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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Methods} from "@models/api/permission";
@Component({
selector: 'app-method-button',
templateUrl: './method-button.component.html',
styleUrls: ['./method-button.component.scss']
})
export class MethodButtonComponent implements OnInit {
@Input() enable = false;
@Input() name: Methods;
@Input() disabled = true;
@Output() update = new EventEmitter<{name: Methods; enable: boolean}>();
constructor() { }
ngOnInit(): void {
}
change() {
this.enable = !this.enable;
this.update.emit({name: this.name, enable: this.enable});
}
}

View File

@ -1,57 +0,0 @@
<div class="permission">
<div class="action-button-bar">
<div>
<app-action-button
[action]="permission.action"
[disabled]="!isEditable"
(update)="permission.action = $event"></app-action-button>
</div>
<div class="methods">
<div *ngFor="let method of apiInformation.getMethods(permission.path) | async">
<app-method-button
[name]="method"
[disabled]="!isEditable"
[enable]="permission.methods.includes(method)"
(update)="onMethodUpdate($event)"></app-method-button>
</div>
</div>
</div>
<div>
<div
[matTooltip]="permission.path | displayPath: controller | async"
matTooltipClass="custom-tooltip">
{{permission.path | displayPath: controller | async}}
</div>
</div>
<div>
<mat-form-field class="permission-input" appearance="none">
<input
[(ngModel)]="permission.description"
matInput
type="text"
placeholder="Description"
[matTooltip]="permission.description"
matTooltipClass="custom-tooltip"
[readonly]="!isEditable"/>
</mat-form-field>
</div>
<div class="button-bar">
<div>
<button mat-button matTooltip="Edit permission" (click)="isEditable = true" *ngIf="!isEditable">
<mat-icon>edit</mat-icon>
</button>
<button mat-button matTooltip="Delete permission" (click)="onDelete()" *ngIf="!isEditable">
<mat-icon>delete</mat-icon>
</button>
</div>
<div>
<button mat-button matTooltip="Save Changes" (click)="onSave()" *ngIf="isEditable" color="primary">
<mat-icon>check_circle</mat-icon>
</button>
<button mat-button matTooltip="Cancel Changes" color="warn" (click)="onCancel()" *ngIf="isEditable">
<mat-icon>cancel</mat-icon>
</button>
</div>
</div>
</div>

View File

@ -1,28 +0,0 @@
.permission {
display: flex;
flex-direction: row;
justify-content: space-between;
border-bottom: solid 1px;
margin-top: 10px;
align-items: center;
}
.action-button-bar {
display: flex;
flex-direction: row;
}
.methods {
display: flex;
flex-direction: row;
border: 1px solid;
}
.permission-input {
width: 300px;
}
.button-bar > div > button {
padding: unset !important;
}

View File

@ -1,81 +0,0 @@
/*
* 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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Methods, Permission} from "@models/api/permission";
import {Controller} from '@models/controller';
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
import {PermissionsService} from "@services/permissions.service";
import {ToasterService} from "@services/toaster.service";
import {MatDialog} from "@angular/material/dialog";
import {DeletePermissionDialogComponent} from "@components/permissions-management/delete-permission-dialog/delete-permission-dialog.component";
@Component({
selector: 'app-permission-add-edit-line',
templateUrl: './permission-edit-line.component.html',
styleUrls: ['./permission-edit-line.component.scss']
})
export class PermissionEditLineComponent {
@Input() permission: Permission;
@Input() controller: Controller;
isEditable = false;
@Output() update = new EventEmitter<void>();
constructor(public apiInformation: ApiInformationService,
private permissionService: PermissionsService,
private toasterService: ToasterService,
private dialog: MatDialog) {
}
onDelete() {
this.dialog.open<DeletePermissionDialogComponent>(DeletePermissionDialogComponent,
{width: '700px', height: '500px', data: this.permission})
.afterClosed()
.subscribe((confirm: boolean) => {
if (confirm) {
this.permissionService.delete(this.controller, this.permission.permission_id)
.subscribe(() => {
this.toasterService.success(`Permission was deleted`);
this.update.emit();
}, (e) => {
this.toasterService.error(e);
this.update.emit();
});
}
});
}
onSave() {
this.permissionService.update(this.controller, this.permission)
.subscribe(() => {
this.toasterService.success(`Permission was updated`);
this.update.emit();
}, (e) => {
this.toasterService.error(e);
this.update.emit();
});
}
onCancel() {
this.update.emit();
}
onMethodUpdate(event: { name: Methods; enable: boolean }) {
const set = new Set(this.permission.methods);
event.enable ? set.add(event.name) : set.delete(event.name);
this.permission.methods = Array.from(set);
}
}

View File

@ -1,57 +0,0 @@
import {PermissionsFilterPipe} from './permissions-filter.pipe';
import {Methods, Permission, PermissionActions} from "../../models/api/permission";
const testPermissions: Permission[] = [
{
methods: [Methods.GET, Methods.PUT],
path: '/projects/projet-test',
action: PermissionActions.ALLOW,
description: 'description of permission 1',
created_at: "2022-03-15T09:45:36.531Z",
updated_at: "2022-03-15T09:45:36.531Z",
permission_id: '1'
},
{
methods: [Methods.GET, Methods.PUT],
path: '/projects/projet-test/nodes',
action: PermissionActions.ALLOW,
description: 'permission on projet-test nodes',
created_at: "2022-03-15T09:45:36.531Z",
updated_at: "2022-03-15T09:45:36.531Z",
permission_id: '2'
},
{
methods: [Methods.GET, Methods.PUT],
path: '/projects/projet-bidule',
action: PermissionActions.ALLOW,
description: 'permission on biduler project',
created_at: "2022-03-15T09:45:36.531Z",
updated_at: "2022-03-15T09:45:36.531Z",
permission_id: '3'
}
]
describe('PermissionsFilterPipe', () => {
const pipe = new PermissionsFilterPipe();
it('create an instance', () => {
expect(pipe).toBeTruthy();
});
it('Should return all test permissions', () => {
const res = pipe.transform(testPermissions, '');
expect(res.length).toBe(3);
});
it('Should return both permissions concerning project projet-test', () => {
const res = pipe.transform(testPermissions, 'test');
expect(res.length).toBe(2);
expect(res).toContain(testPermissions[0]);
expect(res).toContain(testPermissions[1]);
});
it('Should return no permissions', () => {
const res = pipe.transform(testPermissions, 'aaaaaa');
expect(res.length).toBe(0);
});
});

View File

@ -1,32 +0,0 @@
/*
* 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 { Pipe, PipeTransform } from '@angular/core';
import {Permission} from "@models/api/permission";
@Pipe({
name: 'permissionsFilter'
})
export class PermissionsFilterPipe implements PipeTransform {
transform(permissions: Permission[], filterText: string): Permission[] {
if (!permissions) {
return [];
}
if (filterText === undefined || filterText === null || filterText === '') {
return permissions;
}
return permissions.filter((permissions: Permission) => permissions.path.toLowerCase().includes(filterText.toLowerCase()));
}
}

View File

@ -1,36 +0,0 @@
<div class="content" *ngIf="isReady; else loading">
<div class="add">
<app-add-permission-line
[controller]="controller"
(addPermissionEvent)="refresh()"></app-add-permission-line>
</div>
<div class="permission-content default-content">
<input type="text"
matInput
name="typeInput"
placeholder="Search by name"
[(ngModel)]="searchPermissions"
[matAutocomplete]="auto"
class="permission-filter"
(input)="changeAutocomplete($event.target.value)">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let option of filteredOptions | filterComplete: searchPermissions" [value]="option">
{{option.name}}
</mat-option>
</mat-autocomplete>
<div *ngFor="let permission of permissions | permissionsFilter: searchPermissions?.id | paginator: pageEvent">
<app-permission-add-edit-line
[permission]="permission"
[controller]="controller"
(update)="refresh()"></app-permission-add-edit-line>
</div>
<mat-paginator [length]="permissions.length" (page)="pageEvent = $event"
[pageSizeOptions]="[5, 20, 50, 100]"></mat-paginator>
</div>
</div>
<ng-template #loading>
<div>
<mat-spinner class="loader"></mat-spinner>
</div>
</ng-template>

View File

@ -1,36 +0,0 @@
.permission-content {
max-width: 1400px;
}
.add-button {
height: 40px;
width: 160px;
margin: 20px;
}
.loader {
position: absolute;
margin: auto;
height: 175px;
bottom: 0;
left: 0;
right: 0;
top: 0;
width: 175px;
}
.add {
/* display: flex;
flex-direction: row;
justify-content: flex-end;
padding-right: 20px;
padding-bottom: 20px;
border-bottom: 1px solid;
align-items: center;*/
}
.permission-filter {
border-bottom: 1px solid;
margin: 5px;
border-bottom-color: #b0bec5;
}

View File

@ -1,80 +0,0 @@
/*
* 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 {Component, ComponentFactoryResolver, OnInit, ViewChild, ViewContainerRef} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import {Controller} from "@models/controller";
import {PermissionsService} from "@services/permissions.service";
import {ProgressService} from "../../common/progress/progress.service";
import {Permission} from "@models/api/permission";
import {AddPermissionLineComponent} from "@components/permissions-management/add-permission-line/add-permission-line.component";
import {ControllerService} from "@services/controller.service";
import {PageEvent} from "@angular/material/paginator";
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
@Component({
selector: 'app-permissions-management',
templateUrl: './permissions-management.component.html',
styleUrls: ['./permissions-management.component.scss']
})
export class PermissionsManagementComponent implements OnInit {
controller: Controller;
permissions: Permission[];
addPermissionLineComp = AddPermissionLineComponent;
newPermissionEdit = false;
searchPermissions: any;
pageEvent: PageEvent | undefined;
filteredOptions: IGenericApiObject[];
options: string[] = [];
@ViewChild('dynamic', {
read: ViewContainerRef
}) viewContainerRef: ViewContainerRef;
isReady = false;
constructor(private route: ActivatedRoute,
private router: Router,
private permissionService: PermissionsService,
private progressService: ProgressService,
private controllerService: ControllerService,
private apiInformationService: ApiInformationService) { }
ngOnInit(): void {
const controllerId = this.route.parent.snapshot.paramMap.get('controller_id');
this.controllerService.get(+controllerId).then((controller: Controller) => {
this.controller = controller;
this.refresh();
});
}
refresh() {
this.permissionService.list(this.controller).subscribe(
(permissions: Permission[]) => {
this.permissions = permissions;
this.isReady = true;
},
(error) => {
this.progressService.setError(error);
}
);
}
displayFn(value): string {
return value && value.name ? value.name : '';
}
changeAutocomplete(inputText) {
this.filteredOptions = this.apiInformationService.getIdByObjNameFromCache(inputText);
}
}

View File

@ -1,32 +0,0 @@
/*
* 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 { Pipe, PipeTransform } from '@angular/core';
import {Permission} from "@models/api/permission";
@Pipe({
name: 'permissionsTypeFilter'
})
export class PermissionsTypeFilterPipe implements PipeTransform {
transform(permissions: Permission[], filterTypeText: string): Permission[] {
if (!permissions) {
return [];
}
if (filterTypeText === undefined || filterTypeText === null || filterTypeText === '') {
return permissions;
}
return permissions.filter((permissions: Permission) => permissions.path.toLowerCase().includes(filterTypeText.toLowerCase()));
}
}

View File

@ -1,15 +0,0 @@
<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 | displayPath: controller | async}}</div>
</div>
<button *ngIf="side === 'LEFT'" mat-button (click)="onClick()">
<mat-icon>keyboard_arrow_right</mat-icon>
</button>
</div>

View File

@ -1,42 +0,0 @@
.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

@ -1,45 +0,0 @@
/*
* 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 {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Permission} from "@models/api/permission";
import {Controller} from '@models/controller';
@Component({
selector: 'app-editable-permission',
templateUrl: './editable-permission.component.html',
styleUrls: ['./editable-permission.component.scss']
})
export class EditablePermissionComponent implements OnInit {
@Input() permission: Permission;
@Input() controller: Controller;
@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(',')}
original path: ${this.permission.path}
`;
}
}

View File

@ -1,33 +0,0 @@
<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

@ -1,33 +0,0 @@
.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

@ -1,38 +0,0 @@
/*
* 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 {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

@ -1,69 +0,0 @@
<div class="header">
<div>
<div>
Allow:
</div>
<div class="box allow"></div>
<div>
Deny:
</div>
<div class="box deny"></div>
</div>
<input type="text"
matInput
name="typeInput"
placeholder="Search by name"
[(ngModel)]="searchPermissions"
[matAutocomplete]="auto"
class="permission-filter"
(input)="changeAutocomplete($event.target.value)">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let option of filteredOptions | filterComplete: searchPermissions" [value]="option">
{{option.name}}
</mat-option>
</mat-autocomplete>
<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"
[controller]="controller"
(click)="remove(permission)"
*ngFor="let permission of ownedArray | permissionsFilter: searchPermissions?.id | paginator: pageEventOwned">
</app-editable-permission>
<mat-paginator [length]="ownedArray.length" (page)="pageEventOwned = $event"
[pageSizeOptions]="[5, 20, 50, 100]"></mat-paginator>
</div>
<mat-divider [vertical]="true"></mat-divider>
<div class="column">
<div class="title">Available</div>
<app-editable-permission
[side]="'RIGHT'"
[permission]="permission"
[controller]="controller"
(click)="add(permission)"
*ngFor="let permission of availableArray | permissionsFilter: searchPermissions?.id | paginator: pageEventAvailable">
</app-editable-permission>
<mat-paginator [length]="availableArray.length" (page)="pageEventAvailable = $event"
[pageSizeOptions]="[5, 20, 50, 100]"></mat-paginator>
</div>
</div>

View File

@ -1,54 +0,0 @@
.editor {
display: flex;
justify-content: stretch;
}
.column {
width: 50vw;
}
.header {
margin: 10px;
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);
}
.permission-filter {
border-bottom: 1px solid;
margin: 5px;
border-bottom-color: #b0bec5;
}

View File

@ -1,114 +0,0 @@
/*
* 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 {Component, Input, OnInit, Output, EventEmitter} from '@angular/core';
import {Controller} from "@models/controller";
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 {ApiInformationService } from "@services/ApiInformation/api-information.service";
import {PageEvent} from "@angular/material/paginator";
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
@Component({
selector: 'app-permission-editor',
templateUrl: './permission-editor.component.html',
styleUrls: ['./permission-editor.component.scss']
})
export class PermissionEditorComponent implements OnInit {
owned: Set<Permission>;
available: Set<Permission>;
searchPermissions: any;
filteredOptions: IGenericApiObject[];
pageEventOwned: PageEvent | undefined;
pageEventAvailable: PageEvent | undefined;
@Input() controller: Controller;
@Input() ownedPermissions: Permission[];
@Input() availablePermissions: Permission[];
@Output() updatedPermissions: EventEmitter<any> = new EventEmitter();
constructor(private dialog: MatDialog,
private apiInformationService: ApiInformationService) {}
ngOnInit(): void {
this.reset();
}
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.ownedPermissions.map(p => p.permission_id);
this.owned = new Set(this.ownedPermissions);
this.available = new Set(this.availablePermissions.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) {
this.updatedPermissions.emit({add, remove});
}
});
}
private diff() {
const add: Permission[] = [];
const currentRolePermissionId = this.ownedPermissions.map(p => p.permission_id);
this.owned.forEach((permission: Permission) => {
if (!currentRolePermissionId.includes(permission.permission_id)) {
add.push(permission);
}
});
const remove: Permission[] = [];
this.ownedPermissions.forEach((permission: Permission) => {
if (!this.owned.has(permission)) {
remove.push(permission);
}
});
return {add, remove};
}
displayFn(value): string {
return value && value.name ? value.name : '';
}
changeAutocomplete(inputText) {
this.filteredOptions = this.apiInformationService.getIdByObjNameFromCache(inputText);
}
get ownedArray() {
return Array.from(this.owned.values());
}
get availableArray() {
return Array.from(this.available.values());
}
}

View File

@ -38,23 +38,7 @@
</div>
</div>
<mat-divider [vertical]="true"></mat-divider>
<div class="permissions">
<div class="header">
<div>Permissions</div>
<div>
<button
mat-button
[routerLink]="['/controller', controller.id, 'management', 'roles', role.role_id, 'permissions']">
<mat-icon>edit</mat-icon>
</button>
</div>
</div>
<app-editable-permission
[permission]="permission"
[controller]="controller"
*ngFor="let permission of role.permissions">
</app-editable-permission>
</div>
</div>
</div>
</div>

View File

@ -1,16 +0,0 @@
<div class="header">
<div>
<a
mat-icon-button
matTooltip="Back to role detail"
matTooltipClass="custom-tooltip"
[routerLink]="['/controller', controller.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>
<app-permission-editor [ownedPermissions]="role.permissions" [availablePermissions]="permissions" [controller]="controller"
(updatedPermissions)="updatePermissions($event)"></app-permission-editor>

View File

@ -1,70 +0,0 @@
/*
* 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 { Component, OnInit } from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import {MatDialog} from "@angular/material/dialog";
import {ToasterService} from "@services/toaster.service";
import {RoleService} from "@services/role.service";
import {Controller} from "@models/controller";
import {Role} from "@models/api/role";
import {Permission} from "@models/api/permission";
import {Observable} from "rxjs/Rx";
import {forkJoin} from "rxjs";
import {HttpErrorResponse} from "@angular/common/http";
@Component({
selector: 'app-role-permissions',
templateUrl: './role-permissions.component.html',
styleUrls: ['./role-permissions.component.scss']
})
export class RolePermissionsComponent implements OnInit {
controller: Controller;
role: Role;
permissions: Permission[];
constructor(private route: ActivatedRoute,
private dialog: MatDialog,
private toastService: ToasterService,
private router: Router,
private roleService: RoleService) {
this.route.data.subscribe((data: { controller: Controller, role: Role, permissions: Permission[] }) => {
this.controller = data.controller;
this.role = data.role;
this.permissions = data.permissions;
});
}
ngOnInit(): void {
}
updatePermissions(toUpdate) {
const {add, remove} = toUpdate;
const obs: Observable<any>[] = [];
add.forEach((permission: Permission) => {
obs.push(this.roleService.addPermission(this.controller, this.role, permission));
});
remove.forEach((permission: Permission) => {
obs.push(this.roleService.removePermission(this.controller, this.role, permission));
});
forkJoin(obs)
.subscribe(() => {
this.toastService.success(`permissions updated`);
this.router.navigate(['/controller', this.controller.id, 'management', 'roles', this.role.role_id]);
},
(error: HttpErrorResponse) => {
this.toastService.error(`${error.message}
${error.error.message}`);
});
}
}

View File

@ -51,18 +51,7 @@
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">

View File

@ -37,7 +37,7 @@ import {HttpErrorResponse} from "@angular/common/http";
export class RoleManagementComponent implements OnInit {
controller: Controller;
dataSource = new MatTableDataSource<Role>();
displayedColumns = ['select', 'name', 'description', 'permissions', 'delete'];
displayedColumns = ['select', 'name', 'description', 'delete'];
selection = new SelectionModel<Role>(true, []);
searchText = '';

View File

@ -80,21 +80,6 @@
</ul>
</div>
</mat-tab>
<mat-tab label="Permissions">
<div>
<button
mat-button
[routerLink]="['/controller', controller.id, 'management', 'users', user.user_id, 'permissions']">
<mat-icon>edit</mat-icon>
</button>
</div>
<app-editable-permission
[permission]="permission"
[controller]="controller"
*ngFor="let permission of permissions">
</app-editable-permission>
</mat-tab>
</mat-tab-group>
</div>

View File

@ -8,7 +8,6 @@ import {Controller} from "@models/controller";
import {userNameAsyncValidator} from "@components/user-management/add-user-dialog/userNameAsyncValidator";
import {userEmailAsyncValidator} from "@components/user-management/add-user-dialog/userEmailAsyncValidator";
import {ActivatedRoute, Router} from "@angular/router";
import {Permission} from "@models/api/permission";
import {Role} from "@models/api/role";
import {AddUserDialogComponent} from "@components/user-management/add-user-dialog/add-user-dialog.component";
import {MatDialog} from "@angular/material/dialog";
@ -27,7 +26,6 @@ export class UserDetailComponent implements OnInit {
user: User;
controller: Controller;
user_id: string;
permissions: Permission[];
changingPassword: boolean = false;
constructor(public userService: UserService,
@ -42,11 +40,10 @@ export class UserDetailComponent implements OnInit {
this.controller = this.route.snapshot.data['controller'];
if (!this.controller) this.router.navigate(['/controllers']);
this.route.data.subscribe((d: { controller: Controller; user: User, groups: Group[], permissions: Permission[]}) => {
this.route.data.subscribe((d: { controller: Controller; user: User, groups: Group[]}) => {
this.user = d.user;
this.user_id = this.user.user_id;
this.groups = d.groups;
this.permissions = d.permissions;
this.initForm();
});

View File

@ -1,16 +0,0 @@
<div class="header">
<div>
<a
mat-icon-button
matTooltip="Back to user detail"
matTooltipClass="custom-tooltip"
[routerLink]="['/controller', controller.id, 'management', 'users', user.user_id]">
<mat-icon aria-label="Back to user detail">keyboard_arrow_left</mat-icon>
</a>
</div>
<div>
Edit {{user.username}} role permissions
</div>
</div>
<app-permission-editor [ownedPermissions]="userPermissions" [availablePermissions]="permissions" [controller]="controller"
(updatedPermissions)="updatePermissions($event)"></app-permission-editor>

View File

@ -1,64 +0,0 @@
import { Component, OnInit } from '@angular/core';
import {Controller} from "@models/controller";
import {Role} from "@models/api/role";
import {Permission} from "@models/api/permission";
import {ActivatedRoute, Router} from "@angular/router";
import {MatDialog} from "@angular/material/dialog";
import {ToasterService} from "@services/toaster.service";
import {RoleService} from "@services/role.service";
import {forkJoin} from "rxjs";
import {Observable} from "rxjs/Rx";
import {UserService} from "@services/user.service";
import {User} from "@models/users/user";
import {HttpErrorResponse} from "@angular/common/http";
@Component({
selector: 'app-user-permissions',
templateUrl: './user-permissions.component.html',
styleUrls: ['./user-permissions.component.scss']
})
export class UserPermissionsComponent implements OnInit {
controller: Controller;
user: User;
userPermissions: Permission[];
permissions: Permission[];
constructor(private route: ActivatedRoute,
private dialog: MatDialog,
private toastService: ToasterService,
private router: Router,
private userService: UserService) {
this.route.data.subscribe((data: { controller: Controller, user: User, userPermissions: Permission[], permissions: Permission[] }) => {
this.controller = data.controller;
this.user = data.user;
this.userPermissions = data.userPermissions;
this.permissions = data.permissions;
});
}
ngOnInit(): void {
}
updatePermissions(toUpdate) {
const {add, remove} = toUpdate;
const obs: Observable<any>[] = [];
add.forEach((permission: Permission) => {
obs.push(this.userService.addPermission(this.controller, this.user.user_id, permission));
});
remove.forEach((permission: Permission) => {
obs.push(this.userService.removePermission(this.controller, this.user.user_id, permission));
});
forkJoin(obs)
.subscribe(() => {
this.toastService.success(`permissions updated`);
this.router.navigate(['/controller', this.controller.id, 'management', 'users', this.user.user_id]);
},
(error: HttpErrorResponse) => {
this.toastService.error(`${error.message}
${error.error.message}`);
});
}
}

View File

@ -1,35 +0,0 @@
/*
* 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
*/
export enum Methods {
GET = 'GET',
HEAD = 'HEAD',
POST = 'POST',
PATCH = 'PATCH',
PUT = 'PUT',
DELETE = 'DELETE'
}
export enum PermissionActions {
ALLOW = 'ALLOW',
DENY = 'DENY'
}
export interface Permission {
methods: Methods[];
path: string;
action: PermissionActions;
description: string;
created_at?: string;
updated_at?: string;
permission_id?: string;
}

View File

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

View File

@ -1,47 +0,0 @@
/*
* 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 {
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 {ControllerService} from "@services/controller.service";
import {Controller} from "@models/controller";
@Injectable({
providedIn: 'root'
})
export class PermissionResolver implements Resolve<Permission[]> {
constructor(private permissionService: PermissionsService,
private controllerService: ControllerService) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Permission[]> {
return new Observable<Permission[]>((observer: Subscriber<Permission[]>) => {
const controllerId = route.paramMap.get('controller_id');
this.controllerService.get(+controllerId).then((controller: Controller) => {
this.permissionService.list(controller).subscribe((permission: Permission[]) => {
observer.next(permission);
observer.complete();
});
});
});
}
}

View File

@ -1,48 +0,0 @@
/*
* 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 {
Router, Resolve,
RouterStateSnapshot,
ActivatedRouteSnapshot
} from '@angular/router';
import {Observable, of, Subscriber} from 'rxjs';
import {ControllerService} from "../services/controller.service";
import {UserService} from "../services/user.service";
import {Controller} from "../models/controller";
import {Permission} from "../models/api/permission";
@Injectable({
providedIn: 'root'
})
export class UserPermissionsResolver implements Resolve<Permission[]> {
constructor(private controllerService: ControllerService,
private userService: UserService) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Permission[]> {
return new Observable<Permission[]>((subscriber: Subscriber<Permission[]>) => {
const controllerId = route.paramMap.get('controller_id');
const userId = route.paramMap.get('user_id');
this.controllerService.get(+controllerId).then((controller: Controller) => {
this.userService.getPermissionsByUserId(controller, userId).subscribe((permissions: Permission[]) => {
subscriber.next(permissions);
subscriber.complete();
});
});
});
}
}

View File

@ -1,11 +1,9 @@
import {ApiInformationService, IPathDict} from "@services/ApiInformation/api-information.service";
import {HttpClient} from "@angular/common/http";
import {fakeAsync, TestBed, tick} from "@angular/core/testing";
import {DisplayPathPipe} from "@components/permissions-management/display-path.pipe";
import {Observable, of, ReplaySubject} from "rxjs";
import {Controller} from "@models/controller";
import {getTestController} from "@services/testing";
import {Methods} from "@models/api/permission";
import {ApiInformationCache} from "@services/ApiInformation/ApiInformationCache";
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
@ -25,48 +23,6 @@ describe('ApiInformationService', () => {
controller = getTestController();
});
describe('ApiInformationService.getMethods() tests', () => {
it('create an instance', () => {
expect(apiService).toBeTruthy();
});
it('Should return methods for /projects/{project_id}', fakeAsync(() => {
let res: Methods[];
const mockGetPath: IPathDict[] = [{
methods: ['GET', 'DELETE', 'PUT'],
originalPath: '/v3/projects/{project_id}',
path: '/projects/{project_id}',
subPaths: ['projects', '{project_id}'],
}, {
methods: ['GET', 'POST'],
originalPath: '/v3/projects/{project_id}/nodes',
path: '/projects/{project_id}/nodes',
subPaths: ['projects', '{project_id}', 'nodes'],
}];
spyOn(apiService, 'getPath').and.returnValue(of(mockGetPath));
apiService.getMethods('/projects/{project_id}').subscribe(data => {
res = data;
});
tick();
expect(res).toContain(Methods.GET)
expect(res).toContain(Methods.PUT)
expect(res).toContain(Methods.POST)
}));
it('Should return empty array if no data available', fakeAsync(() => {
let res: Methods[];
const mockGetPath: IPathDict[] = [];
spyOn(apiService, 'getPath').and.returnValue(of(mockGetPath));
apiService.getMethods('/projects/{project_id}').subscribe(data => {
res = data;
});
tick();
expect(res.length).toBe(0);
}));
})
describe('ApiInformationService.getPath() tests', () => {
it('Should return array of 2', fakeAsync(() => {

View File

@ -14,7 +14,6 @@ import {Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Observable, of, ReplaySubject} from "rxjs";
import {map, switchMap, take, tap} from "rxjs/operators";
import {Methods} from "app/models/api/permission";
import {HttpController} from "app/services/http-controller.service";
import {Controller} from "app/models/controller";
import {GetObjectIdHelper} from "@services/ApiInformation/GetObjectIdHelper";
@ -145,22 +144,6 @@ export class ApiInformationService {
}) as unknown as IPathDict[];
}
/**
* Return availables methods for a path
* @param path '/v3/projects/{project_id} => ['GET', 'POST', 'PUT']
*/
getMethods(path: string): Observable<Methods[]> {
return this.getPath(path)
.pipe(
map((data: IPathDict[]) => {
const availableMethods = new Set<string>();
data.forEach((p: IPathDict) => {
p.methods.forEach(method => availableMethods.add(method));
});
return Array.from(availableMethods) as Methods[];
}),
);
}
/**
* return a list of matching path

View File

@ -1,42 +0,0 @@
/*
* 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 {HttpController} from "./http-controller.service";
import {Controller} from "../models/controller";
import {Permission} from "../models/api/permission";
import {Observable} from "rxjs/Rx";
@Injectable({
providedIn: 'root'
})
export class PermissionsService {
constructor(private httpController: HttpController) {
}
list(controller: Controller) {
return this.httpController.get<Permission[]>(controller, '/permissions');
}
add(controller: Controller, permission: Permission): Observable<Permission> {
return this.httpController.post<Permission>(controller, '/permissions', permission);
}
update(controller: Controller, permission: Permission): Observable<Permission> {
return this.httpController.put<Permission>(controller, `/permissions/${permission.permission_id}`, permission);
}
delete(controller: Controller, permission_id: string) {
return this.httpController.delete(controller, `/permissions/${permission_id}`);
}
}

View File

@ -15,7 +15,6 @@ import {HttpController} from "./http-controller.service";
import {Controller} from "../models/controller";
import {Group} from "../models/groups/group";
import {Role} from "../models/api/role";
import {Permission} from "@models/api/permission";
@Injectable({
providedIn: 'root'
@ -43,13 +42,4 @@ export class RoleService {
update(controller: Controller, role: Role) {
return this.httpController.put(controller, `/roles/${role.role_id}`, {name: role.name, description: role.description});
}
addPermission(controller: Controller, role: Role, permission: Permission) {
return this.httpController.put(controller, `/roles/${role.role_id}/permissions/${permission.permission_id}`, {});
}
removePermission(controller: Controller, role: Role, permission: Permission) {
return this.httpController.delete(controller, `/roles/${role.role_id}/permissions/${permission.permission_id}`);
}
}

View File

@ -5,7 +5,6 @@ import { Controller } from '../models/controller';
import { HttpController } from './http-controller.service';
import { User } from '../models/users/user';
import { Group } from "@models/groups/group";
import { Permission } from "@models/api/permission";
@Injectable()
export class UserService {
@ -40,16 +39,4 @@ export class UserService {
getGroupsByUserId(controller: Controller, user_id: string) {
return this.httpController.get<Group[]>(controller, `/access/users/${user_id}/groups`);
}
getPermissionsByUserId(controller: Controller, user_id: string) {
return this.httpController.get<Permission[]>(controller, `/access/users/${user_id}/permissions`);
}
addPermission(controller: Controller, user_id: string, permission: Permission) {
return this.httpController.put(controller, `/access/users/${user_id}/permissions/${permission.permission_id}`, {});
}
removePermission(controller: Controller, user_id: string, permission: Permission) {
return this.httpController.delete(controller, `/access/users/${user_id}/permissions/${permission.permission_id}`);
}
}