mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-05-09 20:12:53 +00:00
Add User management and group management template
This commit is contained in:
parent
108f95de59
commit
d46502b804
@ -76,6 +76,12 @@ const routes: Routes = [
|
|||||||
{ path: 'help', component: HelpComponent },
|
{ path: 'help', component: HelpComponent },
|
||||||
{ path: 'settings', component: SettingsComponent },
|
{ path: 'settings', component: SettingsComponent },
|
||||||
{ path: 'settings/console', component: ConsoleComponent },
|
{ path: 'settings/console', component: ConsoleComponent },
|
||||||
|
{
|
||||||
|
path: 'server/:server_id/user_management',
|
||||||
|
component: UserManagementComponent,
|
||||||
|
canActivate: [LoginGuard],
|
||||||
|
resolve: { server: ServerResolve },
|
||||||
|
},
|
||||||
{ path: 'installed-software', component: InstalledSoftwareComponent },
|
{ path: 'installed-software', component: InstalledSoftwareComponent },
|
||||||
{ path: 'server/:server_id/systemstatus', component: SystemStatusComponent, canActivate: [LoginGuard] },
|
{ path: 'server/:server_id/systemstatus', component: SystemStatusComponent, canActivate: [LoginGuard] },
|
||||||
|
|
||||||
@ -208,10 +214,6 @@ const routes: Routes = [
|
|||||||
component: WebConsoleFullWindowComponent,
|
component: WebConsoleFullWindowComponent,
|
||||||
canActivate: [LoginGuard]
|
canActivate: [LoginGuard]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'user_management',
|
|
||||||
component: UserManagementComponent
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '**',
|
path: '**',
|
||||||
component: PageNotFoundComponent,
|
component: PageNotFoundComponent,
|
||||||
|
@ -272,6 +272,7 @@ import { HttpRequestsInterceptor } from './interceptors/http.interceptor';
|
|||||||
import { UserManagementComponent } from './components/user-management/user-management.component'
|
import { UserManagementComponent } from './components/user-management/user-management.component'
|
||||||
import { UserService } from './services/user.service';
|
import { UserService } from './services/user.service';
|
||||||
import { LoggedUserComponent } from './components/users/logged-user/logged-user.component';
|
import { LoggedUserComponent } from './components/users/logged-user/logged-user.component';
|
||||||
|
import { AddUserDialogComponent } from './components/user-management/add-user-dialog/add-user-dialog.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -459,7 +460,8 @@ import { LoggedUserComponent } from './components/users/logged-user/logged-user.
|
|||||||
ConfigureCustomAdaptersDialogComponent,
|
ConfigureCustomAdaptersDialogComponent,
|
||||||
EditNetworkConfigurationDialogComponent,
|
EditNetworkConfigurationDialogComponent,
|
||||||
UserManagementComponent,
|
UserManagementComponent,
|
||||||
ProjectReadmeComponent
|
ProjectReadmeComponent,
|
||||||
|
AddUserDialogComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
<h1 mat-dialog-title>Create new user</h1>
|
||||||
|
<form [formGroup]="addUserForm" class="input-field">
|
||||||
|
<mat-form-field class="input-field">
|
||||||
|
<input matInput type="text" formControlName="username" placeholder="Username" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="input-field">
|
||||||
|
<input matInput type="text" formControlName="full_name" placeholder="Full name" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="input-field">
|
||||||
|
<input matInput type="text" formControlName="email" placeholder="Email" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="input-field">
|
||||||
|
<input matInput type="text" formControlName="password" placeholder="Password" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox formControlName="is_active" class="checkbox-field">Is active</mat-checkbox>
|
||||||
|
<mat-checkbox formControlName="is_superadmin" class="checkbox-field">Is admin</mat-checkbox>
|
||||||
|
|
||||||
|
<div mat-dialog-actions class="button-div">
|
||||||
|
<button mat-button (click)="onCancelClick()" color="accent">Cancel</button>
|
||||||
|
<button mat-button (click)="onAddClick()" tabindex="2" mat-raised-button color="primary"
|
||||||
|
[disabled]="!addUserForm.valid">
|
||||||
|
Add user
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,12 @@
|
|||||||
|
.input-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-field {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-div {
|
||||||
|
float: right;
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AddUserDialogComponent } from './add-user-dialog.component';
|
||||||
|
|
||||||
|
describe('AddUserDialogComponent', () => {
|
||||||
|
let component: AddUserDialogComponent;
|
||||||
|
let fixture: ComponentFixture<AddUserDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ AddUserDialogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AddUserDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 {FormControl, FormGroup, Validators} from "@angular/forms";
|
||||||
|
import {MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {UserService} from "@services/user.service";
|
||||||
|
import {Server} from "@models/server";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
import {ToasterService} from "@services/toaster.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-add-user-dialog',
|
||||||
|
templateUrl: './add-user-dialog.component.html',
|
||||||
|
styleUrls: ['./add-user-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class AddUserDialogComponent implements OnInit {
|
||||||
|
addUserForm: FormGroup;
|
||||||
|
server: Server;
|
||||||
|
|
||||||
|
constructor(public dialogRef: MatDialogRef<AddUserDialogComponent>,
|
||||||
|
public userService: UserService,
|
||||||
|
private toasterService: ToasterService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.addUserForm = new FormGroup({
|
||||||
|
username: new FormControl(null, [
|
||||||
|
Validators.required,
|
||||||
|
Validators.minLength(3),
|
||||||
|
Validators.pattern("[a-zA-Z0-9_-]+$")]),
|
||||||
|
full_name: new FormControl(),
|
||||||
|
email: new FormControl(null, [Validators.email, Validators.required]),
|
||||||
|
password: new FormControl(null,
|
||||||
|
[Validators.required, Validators.minLength(6), Validators.maxLength(100)]),
|
||||||
|
is_active: new FormControl(true),
|
||||||
|
is_superadmin: new FormControl(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancelClick() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddClick() {
|
||||||
|
if (!this.addUserForm.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newUser = this.addUserForm.value;
|
||||||
|
this.userService.add(this.server, newUser)
|
||||||
|
.subscribe((user: User) => {
|
||||||
|
console.log("Done ", user)
|
||||||
|
this.toasterService.success(`User ${user.username} added`);
|
||||||
|
this.dialogRef.close();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.toasterService.error('Cannot create user : ' + error);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,42 @@
|
|||||||
<p>
|
<div class="content">
|
||||||
user-management works!
|
<div class="default-header">
|
||||||
</p>
|
<div class="row">
|
||||||
|
<h1 class="col">User Management</h1>
|
||||||
|
<button class="col" mat-raised-button color="primary" (click)="addUser()" class="add-button">
|
||||||
|
Add User
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="default-content">
|
||||||
|
<div class="mat-elevation-z8">
|
||||||
|
<mat-table #table [dataSource]="dataSource" matSort>
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">
|
||||||
|
<a class="table-link">{{ row.username }}</a>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="email">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header> Mail </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">{{ row.email }}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="is_active">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header> Active </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">{{row.is_active}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="is_superadmin">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header> Admin </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">{{row.is_superadmin}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="last">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header> Last Update </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">{{row.updated_at ? row.updated_at : row.created_at}}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
|
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||||
|
</mat-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
.add-button {
|
||||||
|
height: 40px;
|
||||||
|
width: 160px;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
@ -1,4 +1,28 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
/*
|
||||||
|
* 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, ViewChild} from '@angular/core';
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import {Server} from "@models/server";
|
||||||
|
import {MatSort, MatSortable} from "@angular/material/sort";
|
||||||
|
import {UserService} from "@services/user.service";
|
||||||
|
import {ProgressService} from "../../common/progress/progress.service";
|
||||||
|
import {User} from "@models/users/user";
|
||||||
|
import {BehaviorSubject, merge, Observable} from "rxjs";
|
||||||
|
import {DataSource} from "@angular/cdk/collections";
|
||||||
|
import {map} from "rxjs/operators";
|
||||||
|
import {AddBlankProjectDialogComponent} from "@components/projects/add-blank-project-dialog/add-blank-project-dialog.component";
|
||||||
|
import {AddUserDialogComponent} from "@components/user-management/add-user-dialog/add-user-dialog.component";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-management',
|
selector: 'app-user-management',
|
||||||
@ -6,10 +30,101 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
styleUrls: ['./user-management.component.scss']
|
styleUrls: ['./user-management.component.scss']
|
||||||
})
|
})
|
||||||
export class UserManagementComponent implements OnInit {
|
export class UserManagementComponent implements OnInit {
|
||||||
|
server: Server;
|
||||||
|
dataSource: UserDataSource;
|
||||||
|
userDatabase = new UserDatabase();
|
||||||
|
displayedColumns = ['name', 'email', 'is_active', 'is_superadmin', 'last'];
|
||||||
|
|
||||||
constructor() { }
|
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private userService: UserService,
|
||||||
|
private progressService: ProgressService,
|
||||||
|
public dialog: MatDialog) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.server = this.route.snapshot.data['server'];
|
||||||
|
if (!this.server) this.router.navigate(['/servers']);
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
this.sort.sort(<MatSortable>{
|
||||||
|
id: 'name',
|
||||||
|
start: 'asc',
|
||||||
|
});
|
||||||
|
this.dataSource = new UserDataSource(this.userDatabase, this.sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.userService.list(this.server).subscribe(
|
||||||
|
(users: User[]) => {
|
||||||
|
this.userDatabase.addUsers(users);
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.progressService.setError(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addUser() {
|
||||||
|
const dialogRef = this.dialog.open(AddUserDialogComponent, {
|
||||||
|
width: '400px',
|
||||||
|
autoFocus: false,
|
||||||
|
disableClose: true,
|
||||||
|
});
|
||||||
|
let instance = dialogRef.componentInstance;
|
||||||
|
instance.server = this.server;
|
||||||
|
dialogRef.afterClosed().subscribe(() => this.refresh());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserDatabase {
|
||||||
|
dataChange: BehaviorSubject<User[]> = new BehaviorSubject<User[]>([]);
|
||||||
|
|
||||||
|
get data(): User[] {
|
||||||
|
return this.dataChange.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addUsers(users: User[]) {
|
||||||
|
this.dataChange.next(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(user: User) {
|
||||||
|
const index = this.data.indexOf(user);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.data.splice(index, 1);
|
||||||
|
this.dataChange.next(this.data.slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserDataSource extends DataSource<any> {
|
||||||
|
constructor(public userDatabase: UserDatabase, private sort: MatSort) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(): Observable<User[]> {
|
||||||
|
const displayDataChanges = [this.userDatabase.dataChange, this.sort.sortChange];
|
||||||
|
|
||||||
|
return merge(...displayDataChanges).pipe(
|
||||||
|
map(() => {
|
||||||
|
if (!this.sort.active || this.sort.direction === '') {
|
||||||
|
return this.userDatabase.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.userDatabase.data.sort((a, b) => {
|
||||||
|
const propertyA = a[this.sort.active];
|
||||||
|
const propertyB = b[this.sort.active];
|
||||||
|
|
||||||
|
const valueA = isNaN(+propertyA) ? propertyA : +propertyA;
|
||||||
|
const valueB = isNaN(+propertyB) ? propertyB : +propertyB;
|
||||||
|
|
||||||
|
return (valueA < valueB ? -1 : 1) * (this.sort.direction === 'asc' ? 1 : -1);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<mat-icon>settings</mat-icon>
|
<mat-icon>settings</mat-icon>
|
||||||
<span>Settings</span>
|
<span>Settings</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item routerLink="/user_management">
|
<button mat-menu-item (click)="goToUserManagement()">
|
||||||
<mat-icon>groups</mat-icon>
|
<mat-icon>groups</mat-icon>
|
||||||
<span>User management</span>
|
<span>User management</span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -129,4 +129,11 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
|||||||
this.serverStatusSubscription.unsubscribe();
|
this.serverStatusSubscription.unsubscribe();
|
||||||
this.routeSubscription.unsubscribe();
|
this.routeSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goToUserManagement() {
|
||||||
|
let serverId = this.router.url.split("/server/")[1].split("/")[0];
|
||||||
|
this.serverService.get(+serverId).then((server: Server) => {
|
||||||
|
this.router.navigate(['/server', server.id, 'user_management']);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Subject } from 'rxjs';
|
import {Observable, Subject} from 'rxjs';
|
||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
import { HttpServer } from './http-server.service';
|
import { HttpServer } from './http-server.service';
|
||||||
import { User } from '../models/users/user';
|
import { User } from '../models/users/user';
|
||||||
|
import {Project} from "@models/project";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
@ -13,4 +14,20 @@ export class UserService {
|
|||||||
getInformationAboutLoggedUser(server: Server) {
|
getInformationAboutLoggedUser(server: Server) {
|
||||||
return this.httpServer.get<User>(server, '/users/me/');
|
return this.httpServer.get<User>(server, '/users/me/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list(server: Server) {
|
||||||
|
return this.httpServer.get<User[]>(server, '/users');
|
||||||
|
}
|
||||||
|
|
||||||
|
add(server: Server, user: any): Observable<User> {
|
||||||
|
console.log(user)
|
||||||
|
return this.httpServer.post<User>(server, `/users`, {
|
||||||
|
username: user.username,
|
||||||
|
is_active: user.is_active,
|
||||||
|
email: user.email,
|
||||||
|
full_name: user.full_name,
|
||||||
|
password: user.password
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user