mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +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:
@@ -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');
|
||||
|
Reference in New Issue
Block a user