From b6cbc918eff3eb72b91e5025a426a59104f0d1f3 Mon Sep 17 00:00:00 2001 From: afmam Date: Mon, 20 Nov 2023 13:08:35 -0500 Subject: [PATCH 1/4] Add sqon support for project folders --- modules/server-filter/authFilter.js | 102 +++++++++++++++++++- modules/server-filter/config.js | 2 + modules/server-filter/models/base_sqon.json | 4 + modules/server-filter/sqonModels.js | 62 ++++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 modules/server-filter/sqonModels.js diff --git a/modules/server-filter/authFilter.js b/modules/server-filter/authFilter.js index c73e6328d..f8df07aab 100644 --- a/modules/server-filter/authFilter.js +++ b/modules/server-filter/authFilter.js @@ -6,9 +6,13 @@ import memoize from 'memoizee'; import { AUTH_SERVICE, AUTH_SERVICE_PAGE_SIZE, - MEMOIZE_TIMEOUT + MEMOIZE_TIMEOUT, METADATA_SERVICE, METADATA_SERVICE_PAGE_SIZE } from './config.js'; +import { + ProjectFolder +} from './sqonModels.js' + /** * Function will read sqon json files * @param path The path to sqon file @@ -95,6 +99,65 @@ function UserRoleCheck(project_code, user_roles) { } +/** + * Function will call metadata service to obtain project folder names that cannot be accessed by user + * @param project_folder_ids array of project folder ids a user does not have access to + * @param page The page number used for pagination + * @param data The resulting data for a single api call + * @returns An array of non user-accessible project folders for a project + */ + +const getProjectFolders = async (project_folder_ids, page = 0, data = []) => { + try { + const zones = {0:'greenroom', 1:'core'} + const params = { + params: { + ids: project_folder_ids, + page: page, + page_size: METADATA_SERVICE_PAGE_SIZE + } + } + const url = `${METADATA_SERVICE}/v1/items/batch`; + const response = await axios.get(url, params); + page++; + if (response.data.result.length !== 0){ + for (const entry of response.data.result) { + data.push(...{"name":entry.name, "zone":zones[entry.zone]}); + } + } + if (response.data.num_of_pages > page) { + return getProjectFolders(project_folder_ids, page, data); + } + return data; + } catch (error) { + console.error(error.message) + throw Error(`Failed to call metadata service`); + } +}; + + +/** + * Function will call auth service to obtain rbac permissions for project_folders + * @param project_code The project code for a project + * @param data The resulting data of api call + * @returns An array of RBAC permissions data for project folders + */ + +const getProjectFolderPermissions = async (project_code, data = []) => { + try { + for (const zone of ["0","1"]){ + const url = `${AUTH_SERVICE}/v1/permissions/project-folder?project_code=${project_code}&zone=${zone}`; + const response = await axios.get(url); + data.push(...response.data.result); + } + return data; + } catch (error) { + console.error(error.message) + throw Error(`Failed to call auth service for project folders`); + } +}; + + /** * Function will call auth service to obtain rbac permissions for a project * @param project_code The project code for a project @@ -131,7 +194,8 @@ const getRBAC = async (project_code, user_roles) => { // define base RBAC const permitted = { file_in_own_namefolder: [], - file_any: [] + file_any: [], + project_folders: [] }; // check if user has role associated with project @@ -152,6 +216,30 @@ const getRBAC = async (project_code, user_roles) => { } } } + + // obtain permissions for project folders from auth service + const rbacProjectFolder = await getProjectFolderPermissions(project_code); + // filter project folders that a user does not have access to + const folderIds = []; + if (rbacProjectFolder.length !==0){ + for (const entry of rbacProjectFolder) { + if (!entry.permissions[validated_role]){ + folderIds.push(entry.folder_id); + } + } + } + + // obtain names of non-accessible project folders from metadata service + if (folderIds.length !== 0){ + const projectFolders = await getProjectFolders(folderIds); + // check for names of non-accessible project folders + if (projectFolders.length !== 0){ + for (const folder of projectFolders) { + permitted['project_folders'].push(folder) + } + } + } + return { "role": validated_role, "permissions": permitted @@ -209,6 +297,16 @@ const buildSQON = async (role_metadata, project_code, username) => { base_sqon['content'][1]['content'].push(owner_sqon); } } + permissions['project_folders'] = [{'name':'tester', 'zone': 'greenroom'}] + if (permissions['project_folders'].length !== 0) { + const projectFolder = new ProjectFolder(project_code); + for (const p of permissions['project_folders']){ + const sqon = projectFolder.populateProjectFolderSQON(p.zone, p.name); + base_sqon['content'][2]['content'].push(sqon); + } + // const projectFolderSQON = projectFolder.SQON; + // base_sqon['content'][2]['content'].push(projectFolderSQON); + } // return sqon with user permissions in place return base_sqon; diff --git a/modules/server-filter/config.js b/modules/server-filter/config.js index 121614df7..18efb9057 100644 --- a/modules/server-filter/config.js +++ b/modules/server-filter/config.js @@ -3,5 +3,7 @@ export const ARRANGER_PROJECT_ID = process.env.ARRANGER_PROJECT_ID || 'pilotdev' export const ELASTICSEARCH = process.env.ELASTICSEARCH || 'http://127.0.0.1:9200'; export const AUTH_SERVICE = process.env.AUTH_SERVICE || 'http://127.0.0.1:5061'; export const AUTH_SERVICE_PAGE_SIZE = process.env.AUTH_SERVICE_PAGE_SIZE || 80; +export const METADATA_SERVICE = process.env.METADATA_SERVICE || 'http://127.0.0.1:5066'; +export const METADATA_SERVICE_PAGE_SIZE = process.env.METADATA_SERVICE_PAGE_SIZE || 80; export const DOWNLOAD_STREAM_BUFFER_SIZE = process.env.DOWNLOAD_STREAM_BUFFER_SIZE || 2000; export const MEMOIZE_TIMEOUT = process.env.MEMOIZE_TIMEOUT || 10000; diff --git a/modules/server-filter/models/base_sqon.json b/modules/server-filter/models/base_sqon.json index a5c079907..ccac06a63 100755 --- a/modules/server-filter/models/base_sqon.json +++ b/modules/server-filter/models/base_sqon.json @@ -13,6 +13,10 @@ { "op":"or", "content":[] + }, + { + "op":"not", + "content":[] } ] } diff --git a/modules/server-filter/sqonModels.js b/modules/server-filter/sqonModels.js new file mode 100644 index 000000000..841a8ce6b --- /dev/null +++ b/modules/server-filter/sqonModels.js @@ -0,0 +1,62 @@ +/** + * Class to manage and create shared project folder SQONs + */ +export class ProjectFolder { + + constructor(projectName) { + this.baseSQON = { + "op": "and", + "content": [{ + "op": "in", + "content": { + "field": "container_code", + "value": [ + projectName + ] + } + }, { + "op": "not", + "content": [] + }] + } + } + + get SQON(){ + return this.baseSQON + } + + populateProjectFolderSQON(zone, folderName) { + //this.baseSQON['content'][1]['content'].push(folderSQON); + return { + 'op': 'and', + 'content': [{ + 'op': 'in', + 'content': { + 'field': 'zonefilter', + 'value': [ + zone, + ], + }, + }, { + 'op': 'or', + 'content': [{ + 'op': 'in', + 'content': { + 'field': 'parent_path.keyword', + 'value': [ + `shared/${folderName}`, + ], + }, + }, { + 'op': 'in', + 'content': { + 'field': 'parent_path.keyword', + 'value': [ + `shared/${folderName}/*`, + ], + }, + }], + }], + }; + } +} From 7f6941db7599738e6e7b3b6977491e3c8cc6095b Mon Sep 17 00:00:00 2001 From: afmam Date: Mon, 20 Nov 2023 13:09:17 -0500 Subject: [PATCH 2/4] Remove test code --- modules/server-filter/authFilter.js | 2 -- modules/server-filter/sqonModels.js | 1 - 2 files changed, 3 deletions(-) diff --git a/modules/server-filter/authFilter.js b/modules/server-filter/authFilter.js index f8df07aab..252564915 100644 --- a/modules/server-filter/authFilter.js +++ b/modules/server-filter/authFilter.js @@ -304,8 +304,6 @@ const buildSQON = async (role_metadata, project_code, username) => { const sqon = projectFolder.populateProjectFolderSQON(p.zone, p.name); base_sqon['content'][2]['content'].push(sqon); } - // const projectFolderSQON = projectFolder.SQON; - // base_sqon['content'][2]['content'].push(projectFolderSQON); } // return sqon with user permissions in place diff --git a/modules/server-filter/sqonModels.js b/modules/server-filter/sqonModels.js index 841a8ce6b..d4575023d 100644 --- a/modules/server-filter/sqonModels.js +++ b/modules/server-filter/sqonModels.js @@ -26,7 +26,6 @@ export class ProjectFolder { } populateProjectFolderSQON(zone, folderName) { - //this.baseSQON['content'][1]['content'].push(folderSQON); return { 'op': 'and', 'content': [{ From e4e8ee1e76491f461aad6c8ce6529ae8dacb79ba Mon Sep 17 00:00:00 2001 From: afmam Date: Mon, 20 Nov 2023 13:42:27 -0500 Subject: [PATCH 3/4] Update project folder class --- modules/server-filter/authFilter.js | 2 +- modules/server-filter/sqonModels.js | 23 ----------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/modules/server-filter/authFilter.js b/modules/server-filter/authFilter.js index 252564915..e9941b7f8 100644 --- a/modules/server-filter/authFilter.js +++ b/modules/server-filter/authFilter.js @@ -297,7 +297,7 @@ const buildSQON = async (role_metadata, project_code, username) => { base_sqon['content'][1]['content'].push(owner_sqon); } } - permissions['project_folders'] = [{'name':'tester', 'zone': 'greenroom'}] + if (permissions['project_folders'].length !== 0) { const projectFolder = new ProjectFolder(project_code); for (const p of permissions['project_folders']){ diff --git a/modules/server-filter/sqonModels.js b/modules/server-filter/sqonModels.js index d4575023d..cad1960db 100644 --- a/modules/server-filter/sqonModels.js +++ b/modules/server-filter/sqonModels.js @@ -2,29 +2,6 @@ * Class to manage and create shared project folder SQONs */ export class ProjectFolder { - - constructor(projectName) { - this.baseSQON = { - "op": "and", - "content": [{ - "op": "in", - "content": { - "field": "container_code", - "value": [ - projectName - ] - } - }, { - "op": "not", - "content": [] - }] - } - } - - get SQON(){ - return this.baseSQON - } - populateProjectFolderSQON(zone, folderName) { return { 'op': 'and', From ae1421d9c7b5befe81ca89e3c6ed579d1a698dac Mon Sep 17 00:00:00 2001 From: Anthony <94942038+afmam@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:28:45 -0500 Subject: [PATCH 4/4] Update zone labels for auth call --- modules/server-filter/authFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/server-filter/authFilter.js b/modules/server-filter/authFilter.js index e9941b7f8..52c6f807b 100644 --- a/modules/server-filter/authFilter.js +++ b/modules/server-filter/authFilter.js @@ -145,7 +145,7 @@ const getProjectFolders = async (project_folder_ids, page = 0, data = []) => { const getProjectFolderPermissions = async (project_code, data = []) => { try { - for (const zone of ["0","1"]){ + for (const zone of ["greenroom","core"]){ const url = `${AUTH_SERVICE}/v1/permissions/project-folder?project_code=${project_code}&zone=${zone}`; const response = await axios.get(url); data.push(...response.data.result);