diff --git a/src/services/permissions-checker.js b/src/services/permissions-checker.js index 1b4006ff6..d75426081 100644 --- a/src/services/permissions-checker.js +++ b/src/services/permissions-checker.js @@ -40,7 +40,20 @@ class PermissionsChecker { }); } - static async _isCollectionBrowseAllowed(collectionPermissions, permissionInfos) { + static async _isCollectionBrowseAllowed(permissions, permissionInfos) { + const { collection: collectionPermissions, segments } = permissions; + + // NOTICE: Security - Segment Query check additional permission + if (permissionInfos.segmentQuery) { + // NOTICE: The segmentQuery should be in the segments + if (!segments) { + return false; + } + if (!segments.includes(permissionInfos.segmentQuery)) { + return false; + } + } + return collectionPermissions && permissionInfos && PermissionsChecker @@ -53,7 +66,7 @@ class PermissionsChecker { return PermissionsChecker._isSmartActionAllowed(permissions.actions, permissionInfos); case 'browseEnabled': return PermissionsChecker - ._isCollectionBrowseAllowed(permissions.collection, permissionInfos); + ._isCollectionBrowseAllowed(permissions, permissionInfos); case 'liveQueries': return PermissionsChecker._isLiveQueryAllowed(permissions.stats.queries, permissionInfos); case 'statWithParameters': diff --git a/src/services/permissions-formatter.js b/src/services/permissions-formatter.js index 99b557141..ec07ef046 100644 --- a/src/services/permissions-formatter.js +++ b/src/services/permissions-formatter.js @@ -27,6 +27,7 @@ const transformPermissionsFromOldToNewFormat = (permissions) => { exportEnabled: collection.export, }, scope: modelPermissions.scope, + segments: modelPermissions.segments, }; if (modelPermissions.actions) { diff --git a/src/services/permissions-getter.js b/src/services/permissions-getter.js index c795bef19..0aa8ecad0 100644 --- a/src/services/permissions-getter.js +++ b/src/services/permissions-getter.js @@ -62,14 +62,14 @@ class PermissionsGetter { : null; } - _getScopePermissions(renderingId, collectionName, { environmentId } = {}) { + _getSegmentsPermissions(renderingId, collectionName, { environmentId } = {}) { const getPermissionsInRendering = this._getPermissionsInRendering( renderingId, { environmentId }, ); return getPermissionsInRendering && getPermissionsInRendering.data && getPermissionsInRendering.data[collectionName] - ? getPermissionsInRendering.data[collectionName].scope + ? getPermissionsInRendering.data[collectionName].segments : null; } @@ -248,14 +248,14 @@ class PermissionsGetter { const collectionPermissions = this._getCollectionPermissions( renderingId, collectionName, { environmentId }, ); - const scope = this._getScopePermissions(renderingId, collectionName, { environmentId }); + const segments = this._getSegmentsPermissions(renderingId, collectionName, { environmentId }); const stats = this._getStatsPermissions(renderingId, { environmentId }); return { collection: collectionPermissions ? collectionPermissions.collection : null, actions: collectionPermissions ? collectionPermissions.actions : null, stats, - scope, + segments, }; } } diff --git a/test/services/permissions.test.js b/test/services/permissions.test.js index 07753e172..5fad8809b 100644 --- a/test/services/permissions.test.js +++ b/test/services/permissions.test.js @@ -1402,4 +1402,90 @@ describe('services > permissions', () => { }); }); }); + + describe('with segments permissions', () => { + describe('if the segment is not allowed to be executed', () => { + it('should return a rejected promise', async () => { + expect.assertions(1); + + resetAndClearCache(); + nock.cleanAll(); + nockObj.persist().get('/liana/v3/permissions?renderingId=1').reply(200, { + meta: { rolesACLActivated: false }, + data: { + products: { + collection: { + list: true, + }, + }, + }, + }); + + const permissionInfos = { userId: 1, segmentQuery: 'SELECT COUNT(*) AS value FROM products;' }; + await expect(new PermissionsChecker(context.inject()).checkPermissions(1, 'products', 'browseEnabled', permissionInfos)) + .rejects.toThrow("'browseEnabled' access forbidden on products"); + nockObj.persist(false); + }); + }); + + describe('if the segment query is allowed', () => { + describe('on not rolesACLActivated (OLD permissions)', () => { + it('should return a resolved promise', async () => { + expect.assertions(1); + + resetAndClearCache(); + nock.cleanAll(); + nockObj.get('/liana/v3/permissions?renderingId=1').reply(200, { + meta: { rolesACLActivated: false }, + data: { + products: { + collection: { + list: true, + }, + segments: ['SELECT COUNT(*) AS value FROM products;'], + }, + }, + }); + + const permissionInfos = { userId: 1, segmentQuery: 'SELECT COUNT(*) AS value FROM products;' }; + await expect(new PermissionsChecker(context.inject()).checkPermissions(1, 'products', 'browseEnabled', permissionInfos)) + .toResolve(); + }); + }); + describe('on rolesACLActivated', () => { + it('should return a resolved promise', async () => { + expect.assertions(1); + + resetAndClearCache(); + nock.cleanAll(); + nockObj.get('/liana/v3/permissions?renderingId=1').reply(200, { + meta: { rolesACLActivated: true }, + data: { + collections: { + products: { + collection: { + addEnabled: false, + browseEnabled: true, + deleteEnabled: false, + editEnabled: false, + exportEnabled: false, + readEnabled: false, + }, + }, + }, + renderings: { + 1: { + products: { segments: ['SELECT COUNT(*) AS value FROM products;'] }, + }, + }, + }, + }); + + const permissionInfos = { userId: 1, segmentQuery: 'SELECT COUNT(*) AS value FROM products;' }; + await expect(new PermissionsChecker(context.inject()).checkPermissions(1, 'products', 'browseEnabled', permissionInfos)) + .toResolve(); + }); + }); + }); + }); });