import { RgwUserFormComponent } from './ceph/rgw/rgw-user-form/rgw-user-form.component';
import { RgwUserListComponent } from './ceph/rgw/rgw-user-list/rgw-user-list.component';
import { LoginComponent } from './core/auth/login/login.component';
+import { RoleFormComponent } from './core/auth/role-form/role-form.component';
+import { RoleListComponent } from './core/auth/role-list/role-list.component';
import { UserFormComponent } from './core/auth/user-form/user-form.component';
import { UserListComponent } from './core/auth/user-list/user-list.component';
import { ForbiddenComponent } from './core/forbidden/forbidden.component';
}
]
},
- // Administration
+ // Dashboard Settings
{
- path: 'users',
+ path: 'user-management',
canActivate: [AuthGuardService],
canActivateChild: [AuthGuardService],
- data: { breadcrumbs: 'Administration/Users' },
+ data: { breadcrumbs: 'User management', path: null },
children: [
- { path: '', component: UserListComponent },
- { path: 'add', component: UserFormComponent, data: { breadcrumbs: 'Add' } },
- { path: 'edit/:username', component: UserFormComponent, data: { breadcrumbs: 'Edit' } }
+ {
+ path: '',
+ redirectTo: 'users',
+ pathMatch: 'full'
+ },
+ {
+ path: 'users',
+ data: { breadcrumbs: 'Users' },
+ children: [
+ { path: '', component: UserListComponent },
+ { path: 'add', component: UserFormComponent, data: { breadcrumbs: 'Add' } },
+ { path: 'edit/:username', component: UserFormComponent, data: { breadcrumbs: 'Edit' } }
+ ]
+ },
+ {
+ path: 'roles',
+ data: { breadcrumbs: 'Roles' },
+ children: [
+ { path: '', component: RoleListComponent },
+ { path: 'add', component: RoleFormComponent, data: { breadcrumbs: 'Add' } },
+ { path: 'edit/:name', component: RoleFormComponent, data: { breadcrumbs: 'Edit' } }
+ ]
+ }
]
},
// System
import { SharedModule } from '../../shared/shared.module';
import { LoginComponent } from './login/login.component';
import { LogoutComponent } from './logout/logout.component';
+import { RoleDetailsComponent } from './role-details/role-details.component';
+import { RoleFormComponent } from './role-form/role-form.component';
+import { RoleListComponent } from './role-list/role-list.component';
import { UserFormComponent } from './user-form/user-form.component';
import { UserListComponent } from './user-list/user-list.component';
+import { UserTabsComponent } from './user-tabs/user-tabs.component';
@NgModule({
imports: [
TabsModule.forRoot(),
RouterModule
],
- declarations: [LoginComponent, LogoutComponent, UserListComponent, UserFormComponent],
+ declarations: [
+ LoginComponent,
+ LogoutComponent,
+ RoleDetailsComponent,
+ RoleFormComponent,
+ RoleListComponent,
+ UserTabsComponent,
+ UserListComponent,
+ UserFormComponent
+ ],
exports: [LogoutComponent]
})
export class AuthModule {}
--- /dev/null
+<tabset *ngIf="selection?.hasSingleSelection">
+ <tab heading="Details" i18n-heading>
+ <table class="table table-bordered table-hover">
+ <thead>
+ <tr>
+ <th></th>
+ <th class="text-center">Read</th>
+ <th class="text-center">Create</th>
+ <th class="text-center">Update</th>
+ <th class="text-center">Delete</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let scope of scopes">
+ <td i18n
+ class="bold col-sm-3">
+ {{ scope }}
+ </td>
+ <td class="col-sm-2 text-center"
+ *ngFor="let column of ['read', 'create', 'update', 'delete']">
+ <span *ngIf="selectedItem.scopes_permissions[scope] && selectedItem.scopes_permissions[scope].indexOf(column) !== -1">
+ <i class="fa fa-check-square-o" aria-hidden="true"></i>
+ </span>
+ <span *ngIf="!selectedItem.scopes_permissions[scope] || selectedItem.scopes_permissions[scope].indexOf(column) === -1">
+ <i class="fa fa-square-o" aria-hidden="true"></i>
+ </span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </tab>
+</tabset>
--- /dev/null
+@import '../../../../defaults';
+
+thead {
+ background-color: $color-table-header-bg;
+}
+
+.fa {
+ font-size: large;
+ &.fa-square-o {
+ color: $color-light-gray;
+ }
+}
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastModule } from 'ng2-toastr';
+import { TabsModule } from 'ngx-bootstrap';
+
+import { SharedModule } from '../../../shared/shared.module';
+import { RoleDetailsComponent } from './role-details.component';
+
+describe('RoleDetailsComponent', () => {
+ let component: RoleDetailsComponent;
+ let fixture: ComponentFixture<RoleDetailsComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ToastModule.forRoot(),
+ TabsModule.forRoot(),
+ RouterTestingModule,
+ HttpClientTestingModule
+ ],
+ declarations: [RoleDetailsComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RoleDetailsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, Input, OnChanges } from '@angular/core';
+
+import { CdTableSelection } from '../../../shared/models/cd-table-selection';
+
+@Component({
+ selector: 'cd-role-details',
+ templateUrl: './role-details.component.html',
+ styleUrls: ['./role-details.component.scss']
+})
+export class RoleDetailsComponent implements OnChanges {
+ @Input()
+ selection: CdTableSelection;
+ @Input()
+ scopes: Array<string>;
+ selectedItem: any;
+
+ constructor() {}
+
+ ngOnChanges() {
+ if (this.selection.hasSelection) {
+ this.selectedItem = this.selection.first();
+ }
+ }
+}
--- /dev/null
+export enum RoleFormMode {
+ editing = 'editing'
+}
--- /dev/null
+<div class="col-sm-12 col-lg-6">
+ <form name="roleForm"
+ class="form-horizontal"
+ #formDir="ngForm"
+ [formGroup]="roleForm"
+ novalidate>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h3 class="panel-title">
+ <span i18n>{mode, select, editing {Edit} other {Add}}</span> Role
+ </h3>
+ </div>
+ <div class="panel-body">
+
+ <!-- Name -->
+ <div class="form-group"
+ [ngClass]="{'has-error': roleForm.showError('name', formDir)}">
+ <label i18n
+ class="control-label col-sm-3"
+ for="name">Name
+ <span class="required"
+ *ngIf="mode !== roleFormMode.editing"></span>
+ </label>
+ <div class="col-sm-9">
+ <input class="form-control"
+ type="text"
+ i18n-placeholder
+ placeholder="Name..."
+ id="name"
+ name="name"
+ formControlName="name"
+ autofocus>
+ <span i18n
+ class="help-block"
+ *ngIf="roleForm.showError('name', formDir, 'required')">
+ This field is required.
+ </span>
+ <span i18n
+ class="help-block"
+ *ngIf="roleForm.showError('name', formDir, 'notUnique')">
+ The chosen name is already in use.
+ </span>
+ </div>
+ </div>
+
+ <!-- Description -->
+ <div class="form-group"
+ [ngClass]="{'has-error': roleForm.showError('description', formDir)}">
+ <label i18n
+ class="control-label col-sm-3"
+ for="name">Description
+ </label>
+ <div class="col-sm-9">
+ <input class="form-control"
+ type="text"
+ i18n-placeholder
+ placeholder="Description..."
+ id="description"
+ name="description"
+ formControlName="description">
+ </div>
+ </div>
+
+ <!-- Permissions -->
+ <div class="form-group">
+ <label i18n
+ class="control-label col-sm-3"
+ for="name">Permissions
+ </label>
+ <div class="col-sm-9">
+ <table class="table table-bordered table-hover">
+ <thead>
+ <tr>
+ <th></th>
+ <th class="text-center">Read</th>
+ <th class="text-center">Create</th>
+ <th class="text-center">Update</th>
+ <th class="text-center">Delete</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let scope of scopes">
+ <td i18n
+ class="bold col-sm-3">
+ {{ scope }}
+ </td>
+ <td class="col-sm-2 text-center clickable"
+ *ngFor="let column of ['read', 'create', 'update', 'delete']">
+ <div class="checkbox checkbox-primary">
+ <input type="checkbox"
+ [checked]="roleForm.getValue('scopes_permissions')[scope] && roleForm.getValue('scopes_permissions')[scope].indexOf(column) !== -1"
+ (change)="hadlePermissionClick(scope, column)">
+ <label></label>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ </div>
+ <div class="panel-footer">
+ <div class="button-group text-right">
+ <cd-submit-button [form]="formDir"
+ type="button"
+ (submitAction)="submit()">
+ <span i18n>{mode, select, editing {Update} other {Create}}</span> Role
+ </cd-submit-button>
+ <button i18n
+ type="button"
+ class="btn btn-sm btn-default"
+ routerLink="/user-management/users/roles">
+ Back
+ </button>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
--- /dev/null
+@import '../../../../defaults';
+
+thead {
+ background-color: $color-table-header-bg;
+}
--- /dev/null
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { Component } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Router, Routes } from '@angular/router';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastModule } from 'ng2-toastr';
+import { of } from 'rxjs';
+
+import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { RoleService } from '../../../shared/api/role.service';
+import { ScopeService } from '../../../shared/api/scope.service';
+import { ComponentsModule } from '../../../shared/components/components.module';
+import { CdFormGroup } from '../../../shared/forms/cd-form-group';
+import { NotificationService } from '../../../shared/services/notification.service';
+import { SharedModule } from '../../../shared/shared.module';
+import { RoleFormComponent } from './role-form.component';
+import { RoleFormModel } from './role-form.model';
+
+describe('RoleFormComponent', () => {
+ let component: RoleFormComponent;
+ let form: CdFormGroup;
+ let fixture: ComponentFixture<RoleFormComponent>;
+ let httpTesting: HttpTestingController;
+ let roleService: RoleService;
+ let router: Router;
+ const setUrl = (url) => Object.defineProperty(router, 'url', { value: url });
+
+ @Component({ selector: 'cd-fake', template: '' })
+ class FakeComponent {}
+
+ const routes: Routes = [{ path: 'roles', component: FakeComponent }];
+
+ configureTestBed(
+ {
+ imports: [
+ [RouterTestingModule.withRoutes(routes)],
+ HttpClientTestingModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ ComponentsModule,
+ ToastModule.forRoot(),
+ SharedModule
+ ],
+ declarations: [RoleFormComponent, FakeComponent]
+ },
+ true
+ );
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RoleFormComponent);
+ component = fixture.componentInstance;
+ form = component.roleForm;
+ httpTesting = TestBed.get(HttpTestingController);
+ roleService = TestBed.get(RoleService);
+ router = TestBed.get(Router);
+ spyOn(router, 'navigate');
+ fixture.detectChanges();
+ const notify = TestBed.get(NotificationService);
+ spyOn(notify, 'show');
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ expect(form).toBeTruthy();
+ });
+
+ describe('create mode', () => {
+ beforeEach(() => {
+ setUrl('/user-management/roles/add');
+ component.ngOnInit();
+ });
+
+ it('should not disable fields', () => {
+ ['name', 'description', 'scopes_permissions'].forEach((key) =>
+ expect(form.get(key).disabled).toBeFalsy()
+ );
+ });
+
+ it('should validate name required', () => {
+ form.get('name').setValue('');
+ expect(form.get('name').hasError('required')).toBeTruthy();
+ });
+
+ it('should set mode', () => {
+ expect(component.mode).toBeUndefined();
+ });
+
+ it('should submit', () => {
+ const role: RoleFormModel = {
+ name: 'role1',
+ description: 'Role 1',
+ scopes_permissions: { osd: ['read'] }
+ };
+ Object.keys(role).forEach((k) => form.get(k).setValue(role[k]));
+ component.submit();
+ const roleReq = httpTesting.expectOne('api/role');
+ expect(roleReq.request.method).toBe('POST');
+ expect(roleReq.request.body).toEqual(role);
+ roleReq.flush({});
+ expect(router.navigate).toHaveBeenCalledWith(['/user-management/roles']);
+ });
+ });
+
+ describe('edit mode', () => {
+ const role: RoleFormModel = {
+ name: 'role1',
+ description: 'Role 1',
+ scopes_permissions: { osd: ['read', 'create'] }
+ };
+ const scopes = ['osd', 'user'];
+ beforeEach(() => {
+ spyOn(roleService, 'get').and.callFake(() => of(role));
+ spyOn(TestBed.get(ScopeService), 'list').and.callFake(() => of(scopes));
+ setUrl('/user-management/roles/edit/role1');
+ component.ngOnInit();
+ const reqScopes = httpTesting.expectOne('ui-api/scope');
+ expect(reqScopes.request.method).toBe('GET');
+ reqScopes.flush(scopes);
+ });
+
+ afterEach(() => {
+ httpTesting.verify();
+ });
+
+ it('should disable fields if editing', () => {
+ expect(form.get('name').disabled).toBeTruthy();
+ ['description', 'scopes_permissions'].forEach((key) =>
+ expect(form.get(key).disabled).toBeFalsy()
+ );
+ });
+
+ it('should set control values', () => {
+ ['name', 'description', 'scopes_permissions'].forEach((key) =>
+ expect(form.getValue(key)).toBe(role[key])
+ );
+ });
+
+ it('should set mode', () => {
+ expect(component.mode).toBe('editing');
+ });
+
+ it('should submit', () => {
+ component.submit();
+ component.hadlePermissionClick('osd', 'update');
+ component.hadlePermissionClick('osd', 'create');
+ component.hadlePermissionClick('user', 'read');
+ const roleReq = httpTesting.expectOne(`api/role/${role.name}`);
+ expect(roleReq.request.method).toBe('PUT');
+ expect(roleReq.request.body).toEqual({
+ name: 'role1',
+ description: 'Role 1',
+ scopes_permissions: { osd: ['read', 'update'], user: ['read'] }
+ });
+ roleReq.flush({});
+ expect(router.navigate).toHaveBeenCalledWith(['/user-management/roles']);
+ });
+ });
+});
--- /dev/null
+import { Component, OnInit } from '@angular/core';
+import { FormControl, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { BsModalRef } from 'ngx-bootstrap';
+
+import { RoleService } from '../../../shared/api/role.service';
+import { ScopeService } from '../../../shared/api/scope.service';
+import { NotificationType } from '../../../shared/enum/notification-type.enum';
+import { CdFormGroup } from '../../../shared/forms/cd-form-group';
+import { CdValidators } from '../../../shared/forms/cd-validators';
+import { NotificationService } from '../../../shared/services/notification.service';
+import { RoleFormMode } from './role-form-mode.enum';
+import { RoleFormModel } from './role-form.model';
+
+@Component({
+ selector: 'cd-role-form',
+ templateUrl: './role-form.component.html',
+ styleUrls: ['./role-form.component.scss']
+})
+export class RoleFormComponent implements OnInit {
+ modalRef: BsModalRef;
+
+ roleForm: CdFormGroup;
+ response: RoleFormModel;
+ scopes: Array<string>;
+
+ roleFormMode = RoleFormMode;
+ mode: RoleFormMode;
+
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ private roleService: RoleService,
+ private scopeService: ScopeService,
+ private notificationService: NotificationService
+ ) {
+ this.createForm();
+ }
+
+ createForm() {
+ this.roleForm = new CdFormGroup({
+ name: new FormControl('', {
+ validators: [Validators.required],
+ asyncValidators: [CdValidators.unique(this.roleService.exists, this.roleService)]
+ }),
+ description: new FormControl(''),
+ scopes_permissions: new FormControl({})
+ });
+ }
+
+ ngOnInit() {
+ if (this.router.url.startsWith('/user-management/roles/edit')) {
+ this.mode = this.roleFormMode.editing;
+ }
+ this.scopeService.list().subscribe((scopes: Array<string>) => {
+ this.scopes = scopes;
+ });
+ if (this.mode === this.roleFormMode.editing) {
+ this.initEdit();
+ }
+ }
+
+ initEdit() {
+ this.disableForEdit();
+ this.route.params.subscribe((params: { name: string }) => {
+ const name = params.name;
+ this.roleService.get(name).subscribe((roleFormModel: RoleFormModel) => {
+ this.setResponse(roleFormModel);
+ });
+ });
+ }
+
+ disableForEdit() {
+ this.roleForm.get('name').disable();
+ }
+
+ setResponse(response: RoleFormModel) {
+ ['name', 'description', 'scopes_permissions'].forEach((key) =>
+ this.roleForm.get(key).setValue(response[key])
+ );
+ }
+
+ hadlePermissionClick(scope: string, permission: string) {
+ const permissions = this.roleForm.getValue('scopes_permissions');
+ if (!permissions[scope]) {
+ permissions[scope] = [];
+ }
+ const index = permissions[scope].indexOf(permission);
+ if (index === -1) {
+ permissions[scope].push(permission);
+ } else {
+ permissions[scope].splice(index, 1);
+ }
+ }
+
+ getRequest(): RoleFormModel {
+ const roleFormModel = new RoleFormModel();
+ ['name', 'description', 'scopes_permissions'].forEach(
+ (key) => (roleFormModel[key] = this.roleForm.get(key).value)
+ );
+ return roleFormModel;
+ }
+
+ createAction() {
+ const roleFormModel = this.getRequest();
+ this.roleService.create(roleFormModel).subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ `Created role '${roleFormModel.name}'`
+ );
+ this.router.navigate(['/user-management/roles']);
+ },
+ () => {
+ this.roleForm.setErrors({ cdSubmitButton: true });
+ }
+ );
+ }
+
+ editAction() {
+ const roleFormModel = this.getRequest();
+ this.roleService.update(roleFormModel).subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ `Updated role '${roleFormModel.name}'`
+ );
+ this.router.navigate(['/user-management/roles']);
+ },
+ () => {
+ this.roleForm.setErrors({ cdSubmitButton: true });
+ }
+ );
+ }
+
+ submit() {
+ if (this.mode === this.roleFormMode.editing) {
+ this.editAction();
+ } else {
+ this.createAction();
+ }
+ }
+}
--- /dev/null
+export class RoleFormModel {
+ name: string;
+ description: string;
+ scopes_permissions: any;
+}
--- /dev/null
+<cd-user-tabs></cd-user-tabs>
+
+<cd-table [data]="roles"
+ columnMode="flex"
+ [columns]="columns"
+ identifier="name"
+ selectionType="single"
+ (fetchData)="getRoles()"
+ (updateSelection)="updateSelection($event)">
+ <div class="table-actions">
+ <div class="btn-group" dropdown>
+ <button type="button"
+ class="btn btn-sm btn-primary"
+ *ngIf="permission.create && (
+ permission.update && !selection.hasSingleSelection ||
+ !permission.update)"
+ routerLink="/user-management/roles/add">
+ <i class="fa fa-fw fa-plus"></i>
+ <span i18n>Add</span>
+ </button>
+ <button type="button"
+ class="btn btn-sm btn-primary"
+ [ngClass]="{'disabled': !selection.hasSelection || selection.first().system}"
+ *ngIf="permission.update && (!permission.create || selection.hasSingleSelection)"
+ routerLink="/user-management/roles/edit/{{ selection.first()?.name }}">
+ <i class="fa fa-fw fa-pencil"></i>
+ <span i18n>Edit</span>
+ </button>
+ <button type="button"
+ class="btn btn-sm btn-primary"
+ [ngClass]="{'disabled': !selection.hasSelection || selection.first().system}"
+ *ngIf="permission.delete && !permission.create && !permission.update"
+ (click)="deleteRoleModal()">
+ <i class="fa fa-fw fa-trash-o"></i>
+ <span i18n>Delete</span>
+ </button>
+ <button type="button"
+ dropdownToggle
+ class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split"
+ *ngIf="((permission.create?1:0) + (permission.update?1:0) + (permission.delete?1:0)) > 1">
+ <span class="caret"></span>
+ <span class="sr-only"></span>
+ </button>
+ <ul *dropdownMenu class="dropdown-menu" role="menu">
+ <li role="menuitem"
+ *ngIf="permission.create">
+ <a class="dropdown-item" routerLink="/user-management/roles/add">
+ <i class="fa fa-fw fa-plus"></i>
+ <span i18n>Add</span>
+ </a>
+ </li>
+ <li role="menuitem"
+ *ngIf="permission.update"
+ [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().system}">
+ <a class="dropdown-item" routerLink="/user-management/roles/edit/{{ selection.first()?.name}}">
+ <i class="fa fa-fw fa-pencil"></i>
+ <span i18n>Edit</span>
+ </a>
+ </li>
+ <li role="menuitem"
+ *ngIf="permission.delete"
+ [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().system}">
+ <a class="dropdown-item" (click)="deleteRoleModal()">
+ <i class="fa fa-fw fa-trash-o"></i>
+ <span i18n>Delete</span>
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <cd-role-details cdTableDetail
+ [selection]="selection"
+ [scopes]="scopes">
+ </cd-role-details>
+</cd-table>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastModule } from 'ng2-toastr';
+import { TabsModule } from 'ngx-bootstrap';
+
+import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { SharedModule } from '../../../shared/shared.module';
+import { RoleDetailsComponent } from '../role-details/role-details.component';
+import { UserTabsComponent } from '../user-tabs/user-tabs.component';
+import { RoleListComponent } from './role-list.component';
+
+describe('RoleListComponent', () => {
+ let component: RoleListComponent;
+ let fixture: ComponentFixture<RoleListComponent>;
+
+ configureTestBed({
+ declarations: [RoleListComponent, RoleDetailsComponent, UserTabsComponent],
+ imports: [
+ SharedModule,
+ ToastModule.forRoot(),
+ TabsModule.forRoot(),
+ RouterTestingModule,
+ HttpClientTestingModule
+ ]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RoleListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, OnInit } from '@angular/core';
+
+import { BsModalRef, BsModalService } from 'ngx-bootstrap';
+import { forkJoin } from 'rxjs';
+
+import { RoleService } from '../../../shared/api/role.service';
+import { ScopeService } from '../../../shared/api/scope.service';
+import { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component';
+import { EmptyPipe } from '../../../shared/empty.pipe';
+import { CellTemplate } from '../../../shared/enum/cell-template.enum';
+import { NotificationType } from '../../../shared/enum/notification-type.enum';
+import { CdTableColumn } from '../../../shared/models/cd-table-column';
+import { CdTableSelection } from '../../../shared/models/cd-table-selection';
+import { Permission } from '../../../shared/models/permissions';
+import { AuthStorageService } from '../../../shared/services/auth-storage.service';
+import { NotificationService } from '../../../shared/services/notification.service';
+
+@Component({
+ selector: 'cd-role-list',
+ templateUrl: './role-list.component.html',
+ styleUrls: ['./role-list.component.scss']
+})
+export class RoleListComponent implements OnInit {
+ permission: Permission;
+ columns: CdTableColumn[];
+ roles: Array<any>;
+ scopes: Array<string>;
+ selection = new CdTableSelection();
+
+ modalRef: BsModalRef;
+
+ constructor(
+ private roleService: RoleService,
+ private scopeService: ScopeService,
+ private emptyPipe: EmptyPipe,
+ private authStorageService: AuthStorageService,
+ private modalService: BsModalService,
+ private notificationService: NotificationService
+ ) {
+ this.permission = this.authStorageService.getPermissions().user;
+ }
+
+ ngOnInit() {
+ this.columns = [
+ {
+ name: 'Name',
+ prop: 'name',
+ flexGrow: 3
+ },
+ {
+ name: 'Description',
+ prop: 'description',
+ flexGrow: 5,
+ pipe: this.emptyPipe
+ },
+ {
+ name: 'System Role',
+ prop: 'system',
+ cellClass: 'text-center',
+ flexGrow: 1,
+ cellTransformation: CellTemplate.checkIcon
+ }
+ ];
+ }
+
+ getRoles() {
+ forkJoin([this.roleService.list(), this.scopeService.list()]).subscribe(
+ (data: [Array<any>, Array<string>]) => {
+ this.roles = data[0];
+ this.scopes = data[1];
+ }
+ );
+ }
+
+ updateSelection(selection: CdTableSelection) {
+ this.selection = selection;
+ }
+
+ deleteRole(role: string) {
+ this.roleService.delete(role).subscribe(
+ () => {
+ this.getRoles();
+ this.modalRef.hide();
+ this.notificationService.show(NotificationType.success, `Deleted role '${role}'`);
+ },
+ () => {
+ this.modalRef.content.stopLoadingSpinner();
+ }
+ );
+ }
+
+ deleteRoleModal() {
+ this.modalRef = this.modalService.show(DeletionModalComponent);
+ const name = this.selection.first().name;
+ this.modalRef.content.setUp({
+ metaType: 'Role',
+ pattern: `${name}`,
+ deletionMethod: () => this.deleteRole(name),
+ modalRef: this.modalRef
+ });
+ }
+}
<button i18n
type="button"
class="btn btn-sm btn-default"
- routerLink="/users">
+ routerLink="/user-management/users">
Back
</button>
</div>
describe('create mode', () => {
beforeEach(() => {
- setUrl('/users/add');
+ setUrl('/user-management/users/add');
component.ngOnInit();
});
expect(userReq.request.method).toBe('POST');
expect(userReq.request.body).toEqual(user);
userReq.flush({});
- expect(router.navigate).toHaveBeenCalledWith(['/users']);
+ expect(router.navigate).toHaveBeenCalledWith(['/user-management/users']);
});
});
beforeEach(() => {
spyOn(userService, 'get').and.callFake(() => of(user));
spyOn(TestBed.get(RoleService), 'list').and.callFake(() => of(roles));
- setUrl('/users/edit/user1');
+ setUrl('/user-management/users/edit/user1');
component.ngOnInit();
const req = httpTesting.expectOne('api/role');
expect(req.request.method).toBe('GET');
roles: ['administrator']
});
userReq.flush({});
- expect(router.navigate).toHaveBeenCalledWith(['/users']);
+ expect(router.navigate).toHaveBeenCalledWith(['/user-management/users']);
});
});
});
}
ngOnInit() {
- if (this.router.url.startsWith('/users/edit')) {
+ if (this.router.url.startsWith('/user-management/users/edit')) {
this.mode = this.userFormMode.editing;
}
this.roleService.list().subscribe((roles: Array<UserFormRoleModel>) => {
`User "${userFormModel.username}" has been created.`,
'Create User'
);
- this.router.navigate(['/users']);
+ this.router.navigate(['/user-management/users']);
},
() => {
this.userForm.setErrors({ cdSubmitButton: true });
`User "${userFormModel.username}" has been updated.`,
'Edit User'
);
- this.router.navigate(['/users']);
+ this.router.navigate(['/user-management/users']);
}
},
() => {
+<cd-user-tabs></cd-user-tabs>
+
<cd-table [data]="users"
columnMode="flex"
[columns]="columns"
*ngIf="permission.create && (
permission.update && !selection.hasSingleSelection ||
!permission.update)"
- routerLink="/users/add">
+ routerLink="/user-management/users/add">
<i class="fa fa-fw fa-plus"></i>
<span i18n>Add</span>
</button>
class="btn btn-sm btn-primary"
[ngClass]="{'disabled': !selection.hasSelection}"
*ngIf="permission.update && (!permission.create || selection.hasSingleSelection)"
- routerLink="/users/edit/{{ selection.first()?.username }}">
+ routerLink="/user-management/users/edit/{{ selection.first()?.username }}">
<i class="fa fa-fw fa-pencil"></i>
<span i18n>Edit</span>
</button>
<ul *dropdownMenu class="dropdown-menu" role="menu">
<li role="menuitem"
*ngIf="permission.create">
- <a class="dropdown-item" routerLink="/users/add">
+ <a class="dropdown-item" routerLink="/user-management/users/add">
<i class="fa fa-fw fa-plus"></i>
<span i18n>Add</span>
</a>
<li role="menuitem"
*ngIf="permission.update"
[ngClass]="{'disabled': !selection.hasSingleSelection}">
- <a class="dropdown-item" routerLink="/users/edit/{{ selection.first()?.username}}">
+ <a class="dropdown-item" routerLink="/user-management/users/edit/{{ selection.first()?.username}}">
<i class="fa fa-fw fa-pencil"></i>
<span i18n>Edit</span>
</a>
import { RouterTestingModule } from '@angular/router/testing';
import { ToastModule } from 'ng2-toastr';
+import { TabsModule } from 'ngx-bootstrap';
import { SharedModule } from '../../../shared/shared.module';
+import { UserTabsComponent } from '../user-tabs/user-tabs.component';
import { UserListComponent } from './user-list.component';
describe('UserListComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
- imports: [SharedModule, ToastModule.forRoot(), RouterTestingModule, HttpClientTestingModule],
- declarations: [UserListComponent]
+ imports: [
+ SharedModule,
+ ToastModule.forRoot(),
+ TabsModule.forRoot(),
+ RouterTestingModule,
+ HttpClientTestingModule
+ ],
+ declarations: [UserListComponent, UserTabsComponent]
}).compileComponents();
}));
--- /dev/null
+<tabset>
+ <tab heading="Users"
+ i18n-heading
+ [active]="url === '/user-management/users'"
+ (select)="navigateTo('/user-management/users')">
+ </tab>
+ <tab heading="Roles"
+ i18n-heading
+ [active]="url === '/user-management/roles'"
+ (select)="navigateTo('/user-management/roles')">
+ </tab>
+</tabset>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastModule } from 'ng2-toastr';
+import { TabsModule } from 'ngx-bootstrap';
+
+import { SharedModule } from '../../../shared/shared.module';
+import { UserTabsComponent } from './user-tabs.component';
+
+describe('UserTabsComponent', () => {
+ let component: UserTabsComponent;
+ let fixture: ComponentFixture<UserTabsComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ SharedModule,
+ ToastModule.forRoot(),
+ TabsModule.forRoot(),
+ RouterTestingModule,
+ HttpClientTestingModule
+ ],
+ declarations: [UserTabsComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(UserTabsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component, OnInit } from '@angular/core';
+
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'cd-user-tabs',
+ templateUrl: './user-tabs.component.html',
+ styleUrls: ['./user-tabs.component.scss']
+})
+export class UserTabsComponent implements OnInit {
+ url: string;
+
+ constructor(private router: Router) {}
+
+ ngOnInit() {
+ this.url = this.router.url;
+ }
+
+ navigateTo(url) {
+ this.router.navigate([url]);
+ }
+}
<span class="caret"></span>
</a>
<ul *dropdownMenu
- class="dropdown-menu">
+ class="dropdown-menu dropdown-menu-right">
<li *ngIf="userPermission.read">
<a i18n
class="dropdown-item"
- routerLink="/users">Users
+ routerLink="/user-management">User management
</a>
</li>
</ul>
const req = httpTesting.expectOne('api/role');
expect(req.request.method).toBe('GET');
});
+
+ it('should call delete', () => {
+ service.delete('role1').subscribe();
+ const req = httpTesting.expectOne('api/role/role1');
+ expect(req.request.method).toBe('DELETE');
+ });
+
+ it('should call get', () => {
+ service.get('role1').subscribe();
+ const req = httpTesting.expectOne('api/role/role1');
+ expect(req.request.method).toBe('GET');
+ });
+
+ it('should check if role name exists', () => {
+ let exists: boolean;
+ service.exists('role1').subscribe((res: boolean) => {
+ exists = res;
+ });
+ const req = httpTesting.expectOne('api/role');
+ expect(req.request.method).toBe('GET');
+ req.flush([{ name: 'role0' }, { name: 'role1' }]);
+ expect(exists).toBeTruthy();
+ });
+
+ it('should check if role name does not exist', () => {
+ let exists: boolean;
+ service.exists('role2').subscribe((res: boolean) => {
+ exists = res;
+ });
+ const req = httpTesting.expectOne('api/role');
+ expect(req.request.method).toBe('GET');
+ req.flush([{ name: 'role0' }, { name: 'role1' }]);
+ expect(exists).toBeFalsy();
+ });
});
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
+import { Observable, of as observableOf } from 'rxjs';
+import { mergeMap } from 'rxjs/operators';
+
+import { RoleFormModel } from '../../core/auth/role-form/role-form.model';
import { ApiModule } from './api.module';
@Injectable({
list() {
return this.http.get('api/role');
}
+
+ delete(role: string) {
+ return this.http.delete(`api/role/${role}`);
+ }
+
+ get(name) {
+ return this.http.get(`api/role/${name}`);
+ }
+
+ create(role: RoleFormModel) {
+ return this.http.post(`api/role`, role);
+ }
+
+ update(role: RoleFormModel) {
+ return this.http.put(`api/role/${role.name}`, role);
+ }
+
+ exists(name: string): Observable<boolean> {
+ return this.list().pipe(
+ mergeMap((roles: Array<RoleFormModel>) => {
+ const exists = roles.some((currentRole: RoleFormModel) => {
+ return currentRole.name === name;
+ });
+ return observableOf(exists);
+ })
+ );
+ }
}
--- /dev/null
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+
+import { configureTestBed } from '../../../testing/unit-test-helper';
+import { ScopeService } from './scope.service';
+
+describe('ScopeService', () => {
+ let service: ScopeService;
+ let httpTesting: HttpTestingController;
+
+ configureTestBed({
+ providers: [ScopeService],
+ imports: [HttpClientTestingModule]
+ });
+
+ beforeEach(() => {
+ service = TestBed.get(ScopeService);
+ httpTesting = TestBed.get(HttpTestingController);
+ });
+
+ afterEach(() => {
+ httpTesting.verify();
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+
+ it('should call list', () => {
+ service.list().subscribe();
+ const req = httpTesting.expectOne('ui-api/scope');
+ expect(req.request.method).toBe('GET');
+ });
+});
--- /dev/null
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+
+import { ApiModule } from './api.module';
+
+@Injectable({
+ providedIn: ApiModule
+})
+export class ScopeService {
+ constructor(private http: HttpClient) {}
+
+ list() {
+ return this.http.get('ui-api/scope');
+ }
+}
rbd = 'RBD',
pool = 'Pool',
osd = 'OSD',
+ role = 'Role',
user = 'User'
}