Skip to content

Commit

Permalink
Restricts user from accessing screens & UI elements not allowed by th…
Browse files Browse the repository at this point in the history
…eir role

Change-Id: I1edc70bf9c777741bd7aa753561bfaff03be6b13
  • Loading branch information
frague committed Apr 27, 2017
1 parent 4a25693 commit 389ce7f
Show file tree
Hide file tree
Showing 34 changed files with 433 additions and 112 deletions.
21 changes: 18 additions & 3 deletions ui/app/admin/admin.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,25 @@ import { AdminComponent, UsersComponent, RolesComponent } from './index';
export const adminRoutes: Routes = [
{
path: 'admin', component: AdminComponent, canActivate: [LoggedIn],
data: {restrictTo: ['view_user', 'view_role']},
children: [
{path: 'users', component: UsersComponent, canActivate: [LoggedIn]},
{path: 'roles', component: RolesComponent, canActivate: [LoggedIn]},
{path: '**', redirectTo: 'users', pathMatch: 'full'}
{
path: 'users',
component: UsersComponent,
canActivate: [LoggedIn],
data: {restrictTo: 'view_user'}
},
{
path: 'roles',
component: RolesComponent,
canActivate: [LoggedIn],
data: {restrictTo: 'view_role'}
},
{
path: '**',
redirectTo: 'users',
pathMatch: 'full'
}
]
}
];
38 changes: 25 additions & 13 deletions ui/app/admin/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class UsersComponent {
newUser: User = new User({});
shownUserId: string = null;
pagedData: pagedResult = {} as pagedResult;
canSeeRoles: boolean = false;

constructor(
private auth: AuthService,
Expand All @@ -61,21 +62,28 @@ export class UsersComponent {
},
(error: any) => this.data.handleResponseError(error)
);
this.data.role().findAll({})
.then(
(roles: pagedResult) => {
this.roles = roles.items;
this.roleIds = _.reduce(
this.roles,
(result: roleIdsType, role: Role) => {
result[role.id] = role;
return result;

this.auth.isAuthorizedTo('view_role')
.then(canSeeRoles => {
this.canSeeRoles = canSeeRoles;
if (!canSeeRoles) return;

this.data.role().findAll({})
.then(
(roles: pagedResult) => {
this.roles = roles.items;
this.roleIds = _.reduce(
this.roles,
(result: roleIdsType, role: Role) => {
result[role.id] = role;
return result;
},
{} as roleIdsType
);
},
{} as roleIdsType
(error: any) => this.data.handleResponseError(error)
);
},
(error: any) => this.data.handleResponseError(error)
);
});
}

getRoleName(role_id: string): string {
Expand All @@ -91,6 +99,10 @@ export class UsersComponent {
}

saveUser() {
if (!this.canSeeRoles) {
_.unset(this.newUser, 'data.role_id');
}

var savePromise: Promise<any>;
if (this.newUser.id) {
// Update existing user' data
Expand Down
17 changes: 14 additions & 3 deletions ui/app/admin/wizard_steps/user.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import { UserStep } from './user';
import { AppModule } from '../../app.module';
import { BaseModel } from '../../models';
import { DataService, pagedResult } from '../../services/data';
import { AuthService } from '../../services/auth';
import { MockDataService } from '../../../testing/mock.data';
import { MockAuthService } from '../../../testing/mock.auth';

describe('New user wizard step', () => {
let fixture: ComponentFixture<UserStep>;
Expand All @@ -34,6 +36,7 @@ describe('New user wizard step', () => {
imports: [AppModule],
providers: [
{provide: DataService, useClass: MockDataService},
{provide: AuthService, useClass: MockAuthService},
{provide: APP_BASE_HREF, useValue: '/'}
]
})
Expand All @@ -56,9 +59,17 @@ describe('New user wizard step', () => {
expect(component.model.data.role_id).toBeNull();
});

it('fetches roles', () => {
let dataService = fixture.debugElement.injector.get(DataService);
expect(dataService.role().getAll).toHaveBeenCalledTimes(1);
it('fetches roles', done => {
component.fetchData()
.then(() => {
let authService = fixture.debugElement.injector.get(AuthService);
expect(authService.isAuthorizedTo).toHaveBeenCalledWith('view_role');

let dataService = fixture.debugElement.injector.get(DataService);
expect(dataService.role().getAll).toHaveBeenCalledTimes(2);

done();
})
});

it('is valid when its the fields are entered correctly', () => {
Expand Down
22 changes: 14 additions & 8 deletions ui/app/admin/wizard_steps/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Component } from '@angular/core';
import { WizardStepBase } from '../../wizard_step';
import { WizardService } from '../../services/wizard';
import { DataService, pagedResult } from '../../services/data';
import { AuthService } from '../../services/auth';
import { Role } from '../../models';

// New user wizard step
Expand All @@ -30,6 +31,7 @@ export class UserStep extends WizardStepBase {
private mandatoryFields = ['login', 'full_name', 'email'];
private emailRegex = new RegExp('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]{2,}\\.[a-zA-Z0-9-.]{2,}');
roles: Role[] = [];
canSeeRoles = false;

init() {
_.forEach(
Expand All @@ -40,24 +42,28 @@ export class UserStep extends WizardStepBase {
}

fetchData() {
return this.data.role().getAll()
.then(
(roles: pagedResult) => this.roles = roles.items,
(error: any) => this.data.handleResponseError(error)
);
return this.auth.isAuthorizedTo('view_role')
.then(canSeeRoles => {
this.canSeeRoles = canSeeRoles;
if (!canSeeRoles) return;
return this.data.role().getAll()
.then(
(roles: pagedResult) => this.roles = roles.items,
(error: any) => {}
);
})
}

isEmailValid() {
return this.emailRegex.test(this.model.data.email);
}

isValid() {
return !!_.get(this.model, 'data.role_id')
&& !_.some(this.mandatoryFields, (field: string) => !_.get(this.model.data, field))
return !_.some(this.mandatoryFields, (field: string) => !_.get(this.model.data, field))
&& this.isEmailValid();
}

constructor(wizard: WizardService, private data: DataService) {
constructor(wizard: WizardService, private data: DataService, private auth: AuthService) {
super(wizard);
this.fetchData();
}
Expand Down
14 changes: 4 additions & 10 deletions ui/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,25 @@ import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app';
import { LoginComponent, DashboardComponent, PasswordResetComponent } from './dashboard/index';
import { PageNotFoundComponent } from './404';

import { AdminModule } from './admin/admin.module';
import { ClustersModule } from './clusters/clusters.module';
import { ConfigurationsModule } from './configurations/configurations.module';
import { PlaybooksModule } from './playbooks/playbooks.module';
import { ServersModule } from './servers/servers.module';
import { ExecutionsModule } from './executions/executions.module';
import { DashboardModule } from './dashboard/dashboard.module';

import { AuthService, LoggedIn} from './services/auth';
import { AuthService, LoggedIn } from './services/auth';
import { SessionService } from './services/session';
// import { CookieService } from 'angular2-cookie/core';
import { DataService } from './services/data';
import { ErrorService } from './services/error';

import { appRoutingProviders, routing } from './app.routes';

@NgModule({
declarations: [
AppComponent,
LoginComponent,
PasswordResetComponent,
DashboardComponent,
PageNotFoundComponent,
AppComponent
],
imports: [
routing,
Expand All @@ -54,14 +48,14 @@ import { appRoutingProviders, routing } from './app.routes';
PlaybooksModule,
ServersModule,
ExecutionsModule,
DashboardModule,
FormsModule,
BrowserModule,
],
providers: [
LoggedIn,
AuthService,
DataService,
// CookieService,
SessionService,
ErrorService,
appRoutingProviders,
Expand Down
2 changes: 1 addition & 1 deletion ui/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const appRoutes: Routes = [
...serversRoutes,
...executionsRoutes,
...adminRoutes,
{path: '**', redirectTo: 'clusters'}
{path: '**', redirectTo: 'playbooks'}
]
},
{path: '**', redirectTo: 'login', pathMatch: 'full'}
Expand Down
12 changes: 11 additions & 1 deletion ui/app/clusters/clusters.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,15 @@ import { LoggedIn } from '../services/auth';
import { ClustersComponent } from './index';

export const clustersRoutes: Routes = [
{path: 'clusters', component: ClustersComponent, canActivate: [LoggedIn]}
{
path: 'clusters',
component: ClustersComponent,
canActivate: [LoggedIn],
data: {restrictTo: [
'create_cluster',
'view_cluster',
'edit_cluster',
'delete_cluster'
]}
}
];
3 changes: 3 additions & 0 deletions ui/app/clusters/clusters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import { ClustersComponent } from './clusters';
import { AppModule } from '../app.module';
import { ActivatedRoute } from '@angular/router';
import { DataService, pagedResult } from '../services/data';
import { AuthService } from '../services/auth';
import { Cluster } from '../models';
import * as _ from 'lodash';

import { MockDataService, amount } from '../../testing/mock.data';
import { MockAuthService } from '../../testing/mock.auth';
import { MockActivatedRoute } from '../../testing/mock.router';
import { DOMHelper } from '../../testing/dom';

Expand All @@ -45,6 +47,7 @@ describe('Clusters Component', () => {
providers: [
{provide: APP_BASE_HREF, useValue: '/'},
{provide: DataService, useClass: MockDataService},
{provide: AuthService, useClass: MockAuthService},
{provide: ActivatedRoute, useClass: MockActivatedRoute}
]
})
Expand Down
13 changes: 12 additions & 1 deletion ui/app/configurations/configurations.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,16 @@ import { LoggedIn } from '../services/auth';
import { ConfigurationsComponent } from './index';

export const configurationsRoutes: Routes = [
{path: 'configurations', component: ConfigurationsComponent, canActivate: [LoggedIn]}
{
path: 'configurations',
component: ConfigurationsComponent,
canActivate: [LoggedIn],
data: {restrictTo: [
'create_playbook_configuration',
'view_playbook_configuration',
'view_playbook_configuration_version',
'edit_playbook_configuration',
'delete_playbook_configuration'
]}
}
];
14 changes: 7 additions & 7 deletions ui/app/configurations/wizard_steps/servers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe('Configuration wizard: servers step component', () => {
beforeEach(() => {
component.model.data.cluster_id = 'dummy_cluster_id1';
component.servers = _.map(_.range(0, 10), (index) => new Server({
id: index,
id: '' + index,
data: {
name: 'name' + index,
fqdn: 'name' + index,
Expand All @@ -157,32 +157,32 @@ describe('Configuration wizard: servers step component', () => {

it('returns servers that belong to the selected cluster for "in_this_cluster" policy', () => {
imitatePolicy('in_this_cluster');
expect(getServersIds()).toEqual([3, 4, 5, 6]);
expect(getServersIds()).toEqual(['3', '4', '5', '6']);
});

it('returns servers that do not belong to the selected cluster for "not_in_this_cluster" policy', () => {
imitatePolicy('not_in_this_cluster');
expect(getServersIds()).toEqual([0, 1, 2, 7, 8, 9]);
expect(getServersIds()).toEqual(['0', '1', '2', '7', '8', '9']);
});

it('returns servers that are not in selected cluster for "in_other_cluster" policy', () => {
imitatePolicy('in_other_cluster');
expect(getServersIds()).toEqual([7, 8, 9]);
expect(getServersIds()).toEqual(['7', '8', '9']);
});

it('returns unassigned servers or ones that belong to selected cluster for "not_in_other_cluster" policy', () => {
imitatePolicy('not_in_other_cluster');
expect(getServersIds()).toEqual([0, 1, 2, 3, 4, 5, 6]);
expect(getServersIds()).toEqual(['0', '1', '2', '3', '4', '5', '6']);
});

it('returns assigned to clusters servers for "in_any_cluster" policy', () => {
imitatePolicy('in_any_cluster');
expect(getServersIds()).toEqual([3, 4, 5, 6, 7, 8, 9]);
expect(getServersIds()).toEqual(['3', '4', '5', '6', '7', '8', '9']);
});

it('returns unassigned to clusters servers for "not_in_any_cluster" policy', () => {
imitatePolicy('not_in_any_cluster');
expect(getServersIds()).toEqual([0, 1, 2]);
expect(getServersIds()).toEqual(['0', '1', '2']);
});
});
});
41 changes: 41 additions & 0 deletions ui/app/dashboard/dashboard.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (c) 2016 Mirantis Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';

import { SharedModule } from '../shared.module';

import { DashboardComponent, LoginComponent, PasswordResetComponent } from './index';

@NgModule({
declarations: [
DashboardComponent, LoginComponent, PasswordResetComponent
],
imports: [
BrowserModule,
RouterModule,
FormsModule,
SharedModule
],
exports: [
DashboardComponent, LoginComponent, PasswordResetComponent
]
})
export class DashboardModule { }

0 comments on commit 389ce7f

Please sign in to comment.