mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[AAE-4985] - Make SSO Role Service accept a content admin role that is not part of the JWT token (#6942)
* Add ability to check if the user is an ACS_ADMIN - not part of JTW token * Make get user api call only once * Add unit tests * Add documentation * Fix comments * Exclude flaky tests, dependent on another test * Fix unit test * Fix comments * Update documentation
This commit is contained in:
parent
585a1b6918
commit
574db8d7cc
@ -31,9 +31,10 @@ 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 Realms roles.
|
||||
<br>**Note**: An additional role ALFRESCO_ADMINISTRATORS can be used in the roles array, which will result in checking whether the logged in user has Content Admin capabilities or not, as this role is not part of the JWT token it will call a Content API to determine it.
|
||||
|
||||
|
||||
Client role Example
|
||||
Client role Example
|
||||
```ts
|
||||
const appRoutes: Routes = [
|
||||
...
|
||||
|
@ -5,5 +5,7 @@
|
||||
"C279931": "login problem APS not basic",
|
||||
"C279930": "login problem APS not basic",
|
||||
"C593560": "https://alfresco.atlassian.net/browse/ADF-5366",
|
||||
"C269081": "https://alfresco.atlassian.net/browse/ADF-5385",
|
||||
"C272819": "https://alfresco.atlassian.net/browse/ADF-5385",
|
||||
"C290069": "https://alfresco.atlassian.net/browse/ADF-5387"
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
import { EcmCompanyModel } from '../models/ecm-company.model';
|
||||
import { PersonEntry, Person } from '@alfresco/js-api';
|
||||
|
||||
export let fakeEcmCompany: EcmCompanyModel = {
|
||||
organization: 'company-fake-name',
|
||||
@ -99,3 +100,25 @@ export const createNewPersonMock = {
|
||||
password: 'fake-avatar-id',
|
||||
email: 'fakeEcm@ecmUser.com'
|
||||
};
|
||||
|
||||
export function getFakeUserWithContentAdminCapability(): PersonEntry {
|
||||
const fakeEcmUserWithAdminCapabilities = {
|
||||
...fakeEcmUser,
|
||||
capabilities: {
|
||||
isAdmin: true
|
||||
}
|
||||
};
|
||||
const mockPerson = new Person(fakeEcmUserWithAdminCapabilities);
|
||||
return { entry: mockPerson };
|
||||
}
|
||||
|
||||
export function getFakeUserWithContentUserCapability(): PersonEntry {
|
||||
const fakeEcmUserWithAdminCapabilities = {
|
||||
...fakeEcmUser,
|
||||
capabilities: {
|
||||
isAdmin: false
|
||||
}
|
||||
};
|
||||
const mockPerson = new Person(fakeEcmUserWithAdminCapabilities);
|
||||
return { entry: mockPerson };
|
||||
}
|
||||
|
@ -23,12 +23,16 @@ import { AuthGuardSsoRoleService } from './auth-guard-sso-role.service';
|
||||
import { JwtHelperService } from './jwt-helper.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PeopleContentService } from './people-content.service';
|
||||
import { of } from 'rxjs';
|
||||
import { getFakeUserWithContentAdminCapability, getFakeUserWithContentUserCapability } from '../mock/ecm-user.service.mock';
|
||||
|
||||
describe('Auth Guard SSO role service', () => {
|
||||
|
||||
let authGuard: AuthGuardSsoRoleService;
|
||||
let jwtHelperService: JwtHelperService;
|
||||
let routerService: Router;
|
||||
let peopleContentService: PeopleContentService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@ -42,6 +46,7 @@ describe('Auth Guard SSO role service', () => {
|
||||
authGuard = TestBed.inject(AuthGuardSsoRoleService);
|
||||
jwtHelperService = TestBed.inject(JwtHelperService);
|
||||
routerService = TestBed.inject(Router);
|
||||
peopleContentService = TestBed.inject(PeopleContentService);
|
||||
});
|
||||
|
||||
it('Should canActivate be true if the Role is present int the JWT token', async(async () => {
|
||||
@ -185,4 +190,39 @@ describe('Auth Guard SSO role service', () => {
|
||||
expect(await authGuard.canActivate(route)).toBeFalsy();
|
||||
expect(materialDialog.closeAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Content Admin', () => {
|
||||
|
||||
afterEach(() => {
|
||||
peopleContentService.hasCheckedIsContentAdmin = false;
|
||||
});
|
||||
|
||||
it('Should give access to a content section (ALFRESCO_ADMINISTRATORS) when the user has content admin capability', async () => {
|
||||
spyOn(peopleContentService, 'getCurrentPerson').and.returnValue(of(getFakeUserWithContentAdminCapability()));
|
||||
|
||||
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
router.data = { 'roles': ['ALFRESCO_ADMINISTRATORS'] };
|
||||
|
||||
expect(await authGuard.canActivate(router)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should not give access to a content section (ALFRESCO_ADMINISTRATORS) when the user does not have content admin capability', async () => {
|
||||
spyOn(peopleContentService, 'getCurrentPerson').and.returnValue(of(getFakeUserWithContentUserCapability()));
|
||||
|
||||
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
router.data = { 'roles': ['ALFRESCO_ADMINISTRATORS'] };
|
||||
|
||||
expect(await authGuard.canActivate(router)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Should not call the service to check if the user has content admin capability when the roles do not contain ALFRESCO_ADMINISTRATORS', async () => {
|
||||
const getCurrentPersonSpy = spyOn(peopleContentService, 'getCurrentPerson').and.returnValue(of(getFakeUserWithContentAdminCapability()));
|
||||
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
router.data = { 'roles': ['fakeRole'] };
|
||||
|
||||
await authGuard.canActivate(router);
|
||||
|
||||
expect(getCurrentPersonSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -19,24 +19,28 @@ import { Injectable } from '@angular/core';
|
||||
import { JwtHelperService } from './jwt-helper.service';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ContentGroups, PeopleContentService } from './people-content.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthGuardSsoRoleService implements CanActivate {
|
||||
|
||||
constructor(private jwtHelperService: JwtHelperService, private router: Router, private dialog: MatDialog) {
|
||||
constructor(private jwtHelperService: JwtHelperService,
|
||||
private router: Router,
|
||||
private dialog: MatDialog,
|
||||
private peopleContentService: PeopleContentService) {
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot): boolean {
|
||||
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||
let hasRole;
|
||||
let hasRealmRole = false;
|
||||
let hasClientRole = true;
|
||||
|
||||
if (route.data) {
|
||||
if (route.data['roles']) {
|
||||
const rolesToCheck = route.data['roles'];
|
||||
hasRealmRole = this.jwtHelperService.hasRealmRoles(rolesToCheck);
|
||||
const rolesToCheck: string[] = route.data['roles'];
|
||||
const isContentAdmin = rolesToCheck.includes(ContentGroups.ALFRESCO_ADMINISTRATORS) ? await this.peopleContentService.isContentAdmin() : false;
|
||||
hasRealmRole = this.jwtHelperService.hasRealmRoles(rolesToCheck) || isContentAdmin;
|
||||
}
|
||||
|
||||
if (route.data['clientRoles']) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fakeEcmUser, createNewPersonMock } from '../mock/ecm-user.service.mock';
|
||||
import { fakeEcmUser, createNewPersonMock, getFakeUserWithContentAdminCapability } from '../mock/ecm-user.service.mock';
|
||||
import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock';
|
||||
import { CoreTestingModule } from '../testing/core.testing.module';
|
||||
import { PeopleContentService } from './people-content.service';
|
||||
@ -24,6 +24,7 @@ import { setupTestBed } from '../testing/setup-test-bed';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { LogService } from './log.service';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('PeopleContentService', () => {
|
||||
|
||||
@ -101,4 +102,16 @@ describe('PeopleContentService', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should make the api call to check if the user is a content admin only once', async () => {
|
||||
const getCurrentPersonSpy = spyOn(service.peopleApi, 'getPerson').and.returnValue(of(getFakeUserWithContentAdminCapability()));
|
||||
|
||||
expect(await service.isContentAdmin()).toBe(true);
|
||||
expect(getCurrentPersonSpy.calls.count()).toEqual(1);
|
||||
|
||||
await service.isContentAdmin();
|
||||
|
||||
expect(await service.isContentAdmin()).toBe(true);
|
||||
expect(getCurrentPersonSpy.calls.count()).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
@ -23,10 +23,16 @@ import { PersonEntry, PeopleApi, PersonBodyCreate } from '@alfresco/js-api';
|
||||
import { EcmUserModel } from '../models/ecm-user.model';
|
||||
import { LogService } from './log.service';
|
||||
|
||||
export enum ContentGroups {
|
||||
ALFRESCO_ADMINISTRATORS = 'ALFRESCO_ADMINISTRATORS'
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PeopleContentService {
|
||||
private hasContentAdminRole: boolean = false;
|
||||
hasCheckedIsContentAdmin: boolean = false;
|
||||
|
||||
private _peopleApi: PeopleApi;
|
||||
|
||||
@ -60,6 +66,7 @@ export class PeopleContentService {
|
||||
/**
|
||||
* Creates new person.
|
||||
* @param newPerson Object containing the new person details.
|
||||
* @param opts Optional parameters
|
||||
* @returns Created new person
|
||||
*/
|
||||
createPerson(newPerson: PersonBodyCreate, opts?: any): Observable<EcmUserModel> {
|
||||
@ -69,6 +76,15 @@ export class PeopleContentService {
|
||||
);
|
||||
}
|
||||
|
||||
async isContentAdmin(): Promise<boolean> {
|
||||
if (!this.hasCheckedIsContentAdmin) {
|
||||
const user: PersonEntry = await this.getCurrentPerson().toPromise();
|
||||
this.hasContentAdminRole = user?.entry?.capabilities?.isAdmin;
|
||||
this.hasCheckedIsContentAdmin = true;
|
||||
}
|
||||
return this.hasContentAdminRole;
|
||||
}
|
||||
|
||||
private handleError(error: any) {
|
||||
this.logService.error(error);
|
||||
return throwError(error || 'Server error');
|
||||
|
Loading…
x
Reference in New Issue
Block a user