mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-4295] AuthGuardSsoRoleService - Provide a way to check the resorces_access of the jwt token (#4488)
* Provide a way to check the resorces_access of a jwt token * Add unit test in case the client role is missing or contains a different one * Improve the documentation related to the AuthGuardSSO
This commit is contained in:
committed by
Eugenio Romano
parent
c3bbbe6dab
commit
391094e467
@@ -167,6 +167,8 @@ export const appRoutes: Routes = [
|
||||
},
|
||||
{
|
||||
path: ':appName',
|
||||
canActivate: [AuthGuardSsoRoleService],
|
||||
data: { clientRoles: ['appName'], roles: ['ACTIVITI_USER'], redirectUrl: '/error/403'},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
|
@@ -13,9 +13,10 @@ Checks the user roles of a user.
|
||||
|
||||
The [Auth Guard SSO role service](../../core/services/auth-guard-sso-role.service.md) implements an Angular
|
||||
[route guard](https://angular.io/guide/router#milestone-5-route-guards)
|
||||
to check the user has the right role permission. This is typically used with the
|
||||
`canActivate` guard check in the route definition. The roles that user needs to have in order to access the route has to be specified in the roles array as in the example below:
|
||||
to check the user has the right realms/client roles permission. This is typically used with the
|
||||
`canActivate` guard check in the route definition. The Auth Guard SSO is resposible to check if the JWT contains Realm roles (realm_access) or Client roles (resource_access) based on the route configuration.
|
||||
|
||||
*Realms role Example*
|
||||
```ts
|
||||
const appRoutes: Routes = [
|
||||
...
|
||||
@@ -29,7 +30,24 @@ const appRoutes: Routes = [
|
||||
]
|
||||
```
|
||||
|
||||
If the user now clicks on a link or button that follows this route, they will be not able to access this content if they do not have the roles.
|
||||
If the user now clicks on a link or button that follows this route, they will be not able to access this content if they do not have the Realms roles.
|
||||
|
||||
|
||||
Client role Example
|
||||
```ts
|
||||
const appRoutes: Routes = [
|
||||
...
|
||||
{
|
||||
path: ':examplepath',
|
||||
component: ExampleComponent,
|
||||
canActivate: [ AuthGuardSsoRoleService ],
|
||||
data: { clientRoles: ['examplepath'], roles: ['ACTIVITI_USER']},
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
If the user now clicks on a link or button that follows this route, they will be not able to access this content if they do not have the Client roles.
|
||||
|
||||
## Redirect over forbidden
|
||||
|
||||
|
@@ -116,4 +116,98 @@ describe('Auth Guard SSO role service', () => {
|
||||
expect(routerService.navigate).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('Should canActivate be false hasRealm is true and hasClientRol is false', () => {
|
||||
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
spyOn(this, 'hasRealmRoles').and.returnValue(true);
|
||||
spyOn(this, 'hasRealmRolesForClientRole').and.returnValue(false);
|
||||
|
||||
route.data = { 'clientRoles': ['appName'], 'roles': ['role1', 'role2'] };
|
||||
|
||||
expect(authGuard.canActivate(route, null)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Should canActivate be false hasRealm is false and hasClientRol is true', () => {
|
||||
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
spyOn(this, 'hasRealmRoles').and.returnValue(false);
|
||||
spyOn(this, 'hasRealmRolesForClientRole').and.returnValue(true);
|
||||
|
||||
route.data = { 'clientRoles': ['appName'], 'roles': ['role1', 'role2'] };
|
||||
|
||||
expect(authGuard.canActivate(route, null)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Should canActivate be true if both Real Role and Client Role are present int the JWT token', () => {
|
||||
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
spyOn(storageService, 'getItem').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
|
||||
'realm_access': { roles: ['role1'] },
|
||||
'resource_access': { fakeapp: { roles: ['role2'] }}
|
||||
});
|
||||
|
||||
route.params = {appName: 'fakeapp'};
|
||||
route.data = { 'clientRoles': ['appName'], 'roles': ['role1', 'role2'] };
|
||||
|
||||
expect(authGuard.canActivate(route, null)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should canActivate be false if the Client Role is not present int the JWT token with the correct role', () => {
|
||||
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
spyOn(storageService, 'getItem').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
|
||||
'realm_access': { roles: ['role1'] },
|
||||
'resource_access': { fakeapp: { roles: ['role3'] }}
|
||||
});
|
||||
|
||||
route.params = {appName: 'fakeapp'};
|
||||
route.data = { 'clientRoles': ['appName'], 'roles': ['role1', 'role2'] };
|
||||
|
||||
expect(authGuard.canActivate(route, null)).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('ClientRole ', () => {
|
||||
|
||||
it('Should be true if the resource_access contains the single role', () => {
|
||||
spyOn(storageService, 'getItem').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{'resource_access': { fakeapp: { roles: ['role1'] } }
|
||||
});
|
||||
|
||||
const result = authGuard.hasRealmRolesForClientRole('fakeapp', ['role1'] );
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should be true if the resource_access contains at least one of the roles', () => {
|
||||
spyOn(storageService, 'getItem').and.returnValue('my-access_token');
|
||||
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{'resource_access': { fakeapp: { roles: ['role1'] } }
|
||||
});
|
||||
|
||||
const result = authGuard.hasRealmRolesForClientRole('fakeapp', ['role1', 'role2'] );
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should be false if the resource_access does not contain the role', () => {
|
||||
spyOn(storageService, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{'resource_access': { fakeapp: { roles: ['role3'] } }
|
||||
});
|
||||
const result = authGuard.hasRealmRolesForClientRole('fakeapp', ['role1', 'role2']);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Should be false if the resource_access does not contain the client role related to the app', () => {
|
||||
spyOn(storageService, 'getItem').and.returnValue('my-access_token');
|
||||
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
|
||||
{'resource_access': { anotherfakeapp: { roles: ['role1'] } }
|
||||
});
|
||||
const result = authGuard.hasRealmRolesForClientRole('fakeapp', ['role1', 'role2']);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -27,12 +27,24 @@ export class AuthGuardSsoRoleService implements CanActivate {
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||
let hasRole = false;
|
||||
let hasRealmRole = false;
|
||||
let hasClientRole = true;
|
||||
|
||||
if (route.data) {
|
||||
const rolesToCheck = route.data['roles'];
|
||||
hasRole = this.hasRoles(rolesToCheck);
|
||||
if (route.data['roles']) {
|
||||
const rolesToCheck = route.data['roles'];
|
||||
hasRealmRole = this.hasRealmRoles(rolesToCheck);
|
||||
}
|
||||
|
||||
if (route.data['clientRoles']) {
|
||||
const clientRoleName = route.params[route.data['clientRoles']];
|
||||
const rolesToCheck = route.data['roles'];
|
||||
hasClientRole = this.hasRealmRolesForClientRole(clientRoleName, rolesToCheck);
|
||||
}
|
||||
}
|
||||
|
||||
hasRole = hasRealmRole && hasClientRole;
|
||||
|
||||
if (!hasRole && route.data && route.data['redirectUrl']) {
|
||||
this.router.navigate(['/' + route.data['redirectUrl']]);
|
||||
}
|
||||
@@ -43,33 +55,56 @@ export class AuthGuardSsoRoleService implements CanActivate {
|
||||
constructor(private storageService: StorageService, private jwtHelperService: JwtHelperService, private router: Router) {
|
||||
}
|
||||
|
||||
getRoles(): string[] {
|
||||
getRealmRoles(): string[] {
|
||||
const access = this.getValueFromToken<any>('realm_access');
|
||||
const roles = access ? access['roles'] : [];
|
||||
return roles;
|
||||
}
|
||||
|
||||
getClientRoles(client: string): string[] {
|
||||
const clientRole = this.getValueFromToken<any>('resource_access')[client];
|
||||
const roles = clientRole ? clientRole['roles'] : [];
|
||||
return roles;
|
||||
}
|
||||
|
||||
getAccessToken(): string {
|
||||
return this.storageService.getItem('access_token');
|
||||
}
|
||||
|
||||
hasRole(role: string): boolean {
|
||||
hasRealmRole(role: string): boolean {
|
||||
let hasRole = false;
|
||||
if (this.getAccessToken()) {
|
||||
const roles = this.getRoles();
|
||||
hasRole = roles.some((currentRole) => {
|
||||
const realmRoles = this.getRealmRoles();
|
||||
hasRole = realmRoles.some((currentRole) => {
|
||||
return currentRole === role;
|
||||
});
|
||||
}
|
||||
return hasRole;
|
||||
}
|
||||
|
||||
hasRoles(rolesToCheck: string []): boolean {
|
||||
hasRealmRoles(rolesToCheck: string []): boolean {
|
||||
return rolesToCheck.some((currentRole) => {
|
||||
return this.hasRole(currentRole);
|
||||
return this.hasRealmRole(currentRole);
|
||||
});
|
||||
}
|
||||
|
||||
hasRealmRolesForClientRole(clientRole: string, rolesToCheck: string []): boolean {
|
||||
return rolesToCheck.some((currentRole) => {
|
||||
return this.hasClientRole(clientRole, currentRole);
|
||||
});
|
||||
}
|
||||
|
||||
hasClientRole(clientRole, role: string): boolean {
|
||||
let hasRole = false;
|
||||
if (this.getAccessToken()) {
|
||||
const clientRoles = this.getClientRoles(clientRole);
|
||||
hasRole = clientRoles.some((currentRole) => {
|
||||
return currentRole === role;
|
||||
});
|
||||
}
|
||||
return hasRole;
|
||||
}
|
||||
|
||||
getValueFromToken<T>(key: string): T {
|
||||
let value;
|
||||
const accessToken = this.getAccessToken();
|
||||
|
Reference in New Issue
Block a user