Skip to content
This repository has been archived by the owner on May 7, 2021. It is now read-only.

Commit

Permalink
feat(permissions): add permission service for reading scopes and perm…
Browse files Browse the repository at this point in the history
…issions from RPT token (#131)

* feat(permissions): add permission service for reading scopes and permissions from RPT token

* chore(comments): add comments for methods

* fix(permissions): strict null check, make private methods and other fixes

* fix(permissions): checkScope() -> hasScope()
  • Loading branch information
rohitkrai03 authored and joshuawilson committed Oct 19, 2018
1 parent 1cfcb1c commit 72fda77
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 3 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"npm": ">= 5.3.0"
},
"dependencies": {
"@auth0/angular-jwt": "^2.0.0",
"lodash": "^4.17.10"
},
"peerDependencies": {
Expand Down
36 changes: 36 additions & 0 deletions src/app/auth/permission.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { TestBed } from '@angular/core/testing';
import { PermissionService, Permission } from './permission.service';

describe('Service: Permission Service', () => {
// tslint:disable-next-line:max-line-length
const fakeToken = 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImFVR3Y4bVFBODVqZzRWMURVOFVrMVcwdUtzeG4xODdLUU9OQUdsNkFNdGMiLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiIwIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9hdXRoLm9wZW5zaGlmdC5pbyIsImh0dHA6Ly9vcGVuc2hpZnQuaW8iXSwiYXBwcm92ZWQiOnRydWUsImF1ZCI6Imh0dHA6Ly9vcGVuc2hpZnQuaW8iLCJhdXRoX3RpbWUiOjE1MzU0MTQxNjAsImF6cCI6Imh0dHA6Ly9vcGVuc2hpZnQuaW8iLCJlbWFpbCI6IlRlc3RVc2VyLTUwZWRmZjE4LTZjODYtNDkxMC1iMDY5LTM3ZDY4ZjFjMDJjMUB0ZXN0LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZXhwIjoxNTM4MDA2MTYwLCJmYW1pbHlfbmFtZSI6IiIsImdpdmVuX25hbWUiOiJUZXN0VXNlci01MGVkZmYxOC02Yzg2LTQ5MTAtYjA2OS0zN2Q2OGYxYzAyYzEiLCJpYXQiOjE1MzU0MTQxNjAsImlzcyI6Imh0dHA6Ly9hdXRoLm9wZW5zaGlmdC5pbyIsImp0aSI6IjEwOWQwOWVkLTkxY2MtNDM5My04ZmExLWJjMzE4N2FhNDBiYSIsIm5hbWUiOiJUZXN0VXNlci01MGVkZmYxOC02Yzg2LTQ5MTAtYjA2OS0zN2Q2OGYxYzAyYzEiLCJuYmYiOjAsInBlcm1pc3Npb25zIjpbeyJyZXNvdXJjZV9zZXRfbmFtZSI6bnVsbCwicmVzb3VyY2Vfc2V0X2lkIjoiYzBlZTJiOTQtYWVlMy00YzQxLTllMTUtNmZhMzMwY2U4ZTBiIiwic2NvcGVzIjpbImxpbWEiXSwiZXhwIjoxNTM1NTAwNTcyfV0sInByZWZlcnJlZF91c2VybmFtZSI6IlRlc3RVc2VySWRlbnRpdHktNTBlZGZmMTgtNmM4Ni00OTEwLWIwNjktMzdkNjhmMWMwMmMxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19LCJicm9rZXIiOnsicm9sZXMiOlsicmVhZC10b2tlbiJdfX0sInNlc3Npb25fc3RhdGUiOiIiLCJzdWIiOiI3YWNhNThkZi1iNmUxLTRhNTgtOGQzYS02MDBkZjM4MmRkNDAiLCJ0eXAiOiJCZWFyZXIifQ.xbY2neM56yeHRwhXnaKLp67o6ine38MkJb4Yhe-guQ2nN0-aLrXkqxYF7Jgqb-8w1TfDfdUuKQGWUK1Ye-Xh10biZq-Cl7amPIRQwZ8bLsoII9KFXTjkUQbCxOjNxMl89PuliIP_rO3OXydATnL2KAoU36qKbkBiUTKpQNUOXkcb8wtID_SXE1lssHHNeHNVU358kJjMJUqYE0K59C8csddupR1vpEYJknoLW7nKxxWtAJYGYTOjCey8BkVom6bOgOXz0AiEq2aYdjcaRdwz4IeiLGeFIyvT_sIDyPgYFSR2YCN4_N3CSQPfQYdrQhDGKM7fKLBKnYqAwfUe2OeibQ';
const fakeResourceId = 'c0ee2b94-aee3-4c41-9e15-6fa330ce8e0b';
let service: PermissionService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PermissionService
]
});
service = TestBed.get(PermissionService);

localStorage.setItem('auth_token', fakeToken);
});

it('should return permission for a resource', () => {
const permission: Permission = service.getPermission(fakeResourceId);
expect(permission.resource_set_id).toBe(fakeResourceId);
});

it('should check for scope for a resource', () => {
expect(service.hasScope(fakeResourceId, 'lima')).toBe(true);
expect(service.hasScope(fakeResourceId, 'bean')).toBe(false);
});

it('should return all scopes for a resource', () => {
const scopes = service.getAllScopes(fakeResourceId);
expect(scopes.length).toBe(1);
expect(scopes.includes('lima')).toBe(true);
});
});
61 changes: 61 additions & 0 deletions src/app/auth/permission.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Injectable } from '@angular/core';
import { JwtHelperService } from '@auth0/angular-jwt';

export type Permission = {
exp: number,
resource_set_id: string,
resource_set_name: string,
scopes: Array<string>
};

@Injectable()
export class PermissionService {
private jwtHelper: JwtHelperService = new JwtHelperService();

/**
* Returns all the scopes a user has for a specific resource.
* @param resourceId ID of a specific resource such as a Space
*/
getAllScopes(resourceId: string): Array<string> {
const permissions = this.getPermission(resourceId);
return permissions ? permissions.scopes : [];
}

/**
* Checks if a user has a specific scope for a resource.
* @param resourceId ID of a specific resource such as a Space
* @param scope the scope you want to check for. Ex - `can edit`
*/
hasScope(resourceId: string, scope: string): boolean {
const permissions = this.getPermission(resourceId);
return permissions ? permissions.scopes.includes(scope) : false;
}

/**
* Returns the permission for a specific resource.
* @param resourceId ID of a specific resource such as a Space
*/
getPermission(resourceId: string): Permission | null {
const decodedToken = this.getDecodedToken();
if (this.isValidRPT(decodedToken)) {
return decodedToken.permissions.find((permission: Permission) => permission.resource_set_id === resourceId);
}
return null;
}

/**
* Decodes the JWT token using JwtHelperService from `angular-jwt`.
*/
private getDecodedToken(): any {
const token = localStorage.getItem('auth_token');
return token ? this.jwtHelper.decodeToken(token) : '';
}

/**
* Checks if the decoded token is valid RPT by checking the permissions claim.
* @param token Decoded JWT token.
*/
private isValidRPT(token: any) {
return token && token.permissions;
}
}
1 change: 1 addition & 0 deletions src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { SSO_API_URL } from './shared/sso-api';
export { WIT_API_PROXY } from './shared/wit-api';
export { AlmUserName } from './user/alm-user-name.pipe';
export { AuthenticationService } from './auth/authentication.service';
export { PermissionService, Permission } from './auth/permission.service';
export { AuthInterceptor } from './shared/auth.interceptor';
export { UserService } from './user/user.service';
export { Entity } from './user/entity';
Expand Down
2 changes: 1 addition & 1 deletion tsconfig-aot.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"inlineSources": true,
"lib": [ "dom", "es6" ],
"lib": [ "dom", "es6", "es2017" ],
"module": "es2015",
"moduleResolution": "node",
"noEmitHelpers": false, // Planner and demo won't run when true
Expand Down
2 changes: 1 addition & 1 deletion tsconfig-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"inlineSources": true,
"lib": [ "dom", "es6" ],
"lib": [ "dom", "es6", "es2017" ],
"module": "es2015",
"moduleResolution": "node",
"noEmitHelpers": false, // Planner and demo won't run when true
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"inlineSources": true,
"lib": [ "dom", "es6" ],
"lib": [ "dom", "es6", "es2017" ],
"module": "es2015",
"moduleResolution": "node",
"noEmitHelpers": false, // Planner and demo won't run when true
Expand Down

0 comments on commit 72fda77

Please sign in to comment.