Skip to content

Commit 66906f2

Browse files
authored
Merge pull request #197 from Vafilor/feat/onepanelio.core.373-permissions
feat: added error page and centralized permissions logic into a service.
2 parents 37cbd9d + 87d754c commit 66906f2

24 files changed

+293
-161
lines changed

src/app/app-routing.module.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { ServiceListComponent } from './services/service-list/service-list.compo
2121
import { ServiceViewComponent } from './services/service-view/service-view.component';
2222
import { DashboardComponent } from './dashboard/dashboard.component';
2323
import { WorkflowComponent } from './workflow/workflow.component';
24+
import { ErrorComponent } from './error/error.component';
2425

2526
const routes: Routes = [
2627
{
@@ -115,6 +116,10 @@ const routes: Routes = [
115116
component: ServiceViewComponent,
116117
canActivate: [AuthGuard],
117118
},
119+
{
120+
path: ':namespace/error/:code',
121+
component: ErrorComponent,
122+
},
118123
{
119124
path: '**',
120125
component: NamespaceSelectComponent,

src/app/app.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ import { WorkspaceListComponent } from './workspace/workspace-list/workspace-lis
129129
import { WorkspacesComponent } from './workspace/workspaces/workspaces.component';
130130
import { WorkStatusComponent } from './ui-tools/work-status/work-status.component';
131131
import { ListFilterComponent } from './list-filter/list-filter.component';
132+
import { ErrorComponent } from './error/error.component';
132133

133134
@NgModule({
134135
declarations: [
@@ -227,7 +228,8 @@ import { ListFilterComponent } from './list-filter/list-filter.component';
227228
WorkspaceListComponent,
228229
WorkspacesComponent,
229230
WorkStatusComponent,
230-
ListFilterComponent
231+
ListFilterComponent,
232+
ErrorComponent,
231233
],
232234
entryComponents: [
233235
WorkflowExecuteDialogComponent,

src/app/error/error.component.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div class="error d-flex justify-content-center align-items-center font-roboto">
2+
{{errorMessage}}
3+
</div>
4+

src/app/error/error.component.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@import '../../styles/colors';
2+
3+
.error {
4+
height: calc(100vh - 50px); // 50 for header bar
5+
font-size: 42px;
6+
color: $danger;
7+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { ErrorComponent } from './error.component';
4+
5+
describe('ErrorComponent', () => {
6+
let component: ErrorComponent;
7+
let fixture: ComponentFixture<ErrorComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ ErrorComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(ErrorComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});

src/app/error/error.component.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { ActivatedRoute } from '@angular/router';
3+
4+
@Component({
5+
selector: 'app-error',
6+
templateUrl: './error.component.html',
7+
styleUrls: ['./error.component.scss']
8+
})
9+
export class ErrorComponent implements OnInit {
10+
errorCode = '';
11+
errorMessage = '';
12+
13+
constructor(private activatedRoute: ActivatedRoute) { }
14+
15+
ngOnInit() {
16+
this.activatedRoute.paramMap.subscribe(next => {
17+
this.errorCode = next.get('code');
18+
console.log(this.errorCode);
19+
20+
switch (this.errorCode) {
21+
case '403':
22+
this.errorMessage = 'You do not have permission to view this page';
23+
break;
24+
default:
25+
this.errorMessage = 'Unknown error';
26+
}
27+
28+
});
29+
}
30+
31+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { Injectable } from '@angular/core';
2+
import { AuthServiceService } from '../../api';
3+
import { combineLatest } from 'rxjs';
4+
import { map } from 'rxjs/operators';
5+
import { Permissions } from '../auth/models';
6+
7+
export type PermissionVerb = 'create' | 'get' | 'watch' | 'list' | 'delete' | 'update';
8+
9+
@Injectable({
10+
providedIn: 'root'
11+
})
12+
export class PermissionService {
13+
constructor(private authServiceService: AuthServiceService) {}
14+
15+
/**
16+
* Combines requests into one call and maps the result into a Permissions object.
17+
* requests and verbs are expected to be in the same order.
18+
* That is, the request for verb i is as index i.
19+
*/
20+
private getPermissionsRequest(requests: any[], verbs: PermissionVerb[]) {
21+
if (verbs.length === 0) {
22+
throw new Error('No verbs');
23+
}
24+
25+
return combineLatest(requests).pipe(
26+
map(items => {
27+
if (!items) {
28+
return new Permissions();
29+
}
30+
31+
const result = new Permissions();
32+
33+
for (let i = 0; i < items.length; i++) {
34+
const resItem: any = items[i];
35+
const verb = verbs[i];
36+
result[verb] = resItem.authorized;
37+
}
38+
39+
return result;
40+
}
41+
));
42+
}
43+
44+
getWorkflowPermissions(namespace: string, resourceName: string, ...verbs: PermissionVerb[]) {
45+
const requests = [];
46+
47+
for (const verb of verbs) {
48+
requests.push(
49+
this.authServiceService.isAuthorized({
50+
namespace: name,
51+
verb,
52+
resource: 'workflows',
53+
resourceName,
54+
group: 'argoproj.io',
55+
})
56+
);
57+
}
58+
59+
return this.getPermissionsRequest(requests, verbs);
60+
}
61+
62+
getWorkspaceTemplatePermissions(namespace: string, resourceName: string, ...verbs: PermissionVerb[]) {
63+
const requests = [];
64+
65+
for (const verb of verbs) {
66+
requests.push(
67+
this.authServiceService.isAuthorized({
68+
namespace: name,
69+
verb,
70+
resource: 'workflowtemplates',
71+
resourceName,
72+
group: 'argoproj.io',
73+
})
74+
);
75+
}
76+
77+
return this.getPermissionsRequest(requests, verbs);
78+
}
79+
80+
getWorkspacePermissions(namespace: string, resourceName: string, ...verbs: PermissionVerb[]) {
81+
const requests = [];
82+
83+
for (const verb of verbs) {
84+
requests.push(
85+
this.authServiceService.isAuthorized({
86+
namespace: name,
87+
verb,
88+
resource: 'workspaces',
89+
resourceName,
90+
group: 'onepanel.io',
91+
})
92+
);
93+
}
94+
95+
return this.getPermissionsRequest(requests, verbs);
96+
}
97+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { PermissionService } from './permission.service';
4+
5+
describe('PermissionsService', () => {
6+
beforeEach(() => TestBed.configureTestingModule({}));
7+
8+
it('should be created', () => {
9+
const service: PermissionService = TestBed.get(PermissionService);
10+
expect(service).toBeTruthy();
11+
});
12+
});

src/app/router/app-router.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,8 @@ export class AppRouter {
188188
public navigateToWorkflowsExecutions(namespace: string) {
189189
return this.router.navigate(['/', namespace, 'workflows']);
190190
}
191+
192+
public navigateToError(namespace: string, code: string) {
193+
return this.router.navigate(['/', namespace, 'error', code]);
194+
}
191195
}

src/app/workflow/workflow-executions-list/workflow-executions-list.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@
114114
<i class="far fa-play-circle mr-1 workflow-menu-icon"></i>
115115
Rerun
116116
</button>
117-
<button *ngIf="workflowExecutionPermissions.get(workflow.uid).delete" mat-menu-item (click)="onDelete(workflow)" [disabled]="!(workflow | workflowIsActive)">
117+
<button *ngIf="workflowExecutionPermissions.get(workflow.uid).update" mat-menu-item (click)="onDelete(workflow)" [disabled]="!(workflow | workflowIsActive)">
118118
<i class="fas fa-trash mr-1 workflow-menu-icon"></i>
119119
Terminate
120120
</button>

0 commit comments

Comments
 (0)