From 5a97b390fad8c2e93d7a3ffc6fd30d636e408c3b Mon Sep 17 00:00:00 2001 From: rkboyce Date: Wed, 17 Apr 2024 01:39:49 +0000 Subject: [PATCH 01/12] brought in first working ability to configure global read access by granting write access to a global shared artifact reader role that would be given to all users. Next steps are to 1) make this configurable, 2) allow users to configure the global author role if they want only some users to be able to grant global read access, and 3) set up global read role for all users as a default assigned system role --- .../access/configure-access-modal.html | 40 +++++++++++++++++-- .../security/access/configure-access-modal.js | 30 +++++++++++++- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/js/components/security/access/configure-access-modal.html b/js/components/security/access/configure-access-modal.html index fb966053a..8f6d40125 100644 --- a/js/components/security/access/configure-access-modal.html +++ b/js/components/security/access/configure-access-modal.html @@ -14,13 +14,18 @@ readRoleOptions: readRoleOptions, readRoleSearch: readRoleSearch, writeRoleOptions: writeRoleOptions, - writeRoleSearch: writeRoleSearch + writeRoleSearch: writeRoleSearch, + shareFlag: shareFlag, + grantGlobalReadAccess: grantGlobalReadAccess, + revokeGlobalReadAccess: revokeGlobalReadAccess, }"> + +
- +
+ +
- +
+ + +

+ +
+ +
+
+
+ +
+

+
diff --git a/js/components/security/access/configure-access-modal.js b/js/components/security/access/configure-access-modal.js index 61efbe232..c66fa655a 100644 --- a/js/components/security/access/configure-access-modal.js +++ b/js/components/security/access/configure-access-modal.js @@ -33,7 +33,9 @@ define([ this.readRoleOptions = ko.computed(() => this.readRoleSuggestions().map(r => r.name)); this.readRoleSearch = ko.observable(); this.readRoleSearch.subscribe(str => this.loadReadRoleSuggestions(str)); - + + this.shareFlag = ko.observable(true); + this.isOwnerFn = params.isOwnerFn; this.grantAccessFn = params.grantAccessFn; this.loadAccessListFn = params.loadAccessListFn; @@ -124,7 +126,6 @@ define([ const role = this.readRoleSuggestions().find(r => r.name === this.readRoleName()); await this.grantAccessFn(role.id,'READ'); await this._loadReadAccessList(); - this.readRoleName(''); } } catch (ex) { console.log(ex); @@ -142,6 +143,31 @@ define([ } this.isLoading(false); } + + + async grantGlobalReadAccess() { + this.isLoading(true); + try { + console.log('grantGlobalReadAccess function called to grant read permissions!! shareflag: ' + this.shareFlag()); + await this.grantAccessFn('1006','READ'); // temporarily 1006 is 'shared artifacts reader' + await this._loadReadAccessList(); + } catch (ex) { + console.log(ex); + } + this.isLoading(false); + } + + async revokeGlobalReadAccess() { + this.isLoading(true); + try { + console.log('revokeGlobalReadAccess function called to REVOKE read permissions!! shareflag: ' + this.shareFlag()); + await this.revokeAccessFn('1006','READ'); // temporarily 1006 is 'shared artifacts reader' + await this.loadAccessList(); + } catch (ex) { + console.log(ex); + } + this.isLoading(false); + } } return commonUtils.build('configure-access-modal', ConfigureAccessModal, view); From 61dcae0d7578b9c436928a30cf914ec6de8e24c1 Mon Sep 17 00:00:00 2001 From: rkboyce Date: Mon, 22 Apr 2024 09:21:14 +0000 Subject: [PATCH 02/12] first working configuration of sharing using an authoring role --- .../cohort-definition-manager.html | 10 +++---- .../cohort-definition-manager.js | 29 +++++++++++++++---- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/js/pages/cohort-definitions/cohort-definition-manager.html b/js/pages/cohort-definitions/cohort-definition-manager.html index 2665a0ca7..5c2260659 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.html +++ b/js/pages/cohort-definitions/cohort-definition-manager.html @@ -33,7 +33,7 @@ - +
+
+

diff --git a/js/components/security/access/configure-access-modal.js b/js/components/security/access/configure-access-modal.js index 017790506..0e95ae08a 100644 --- a/js/components/security/access/configure-access-modal.js +++ b/js/components/security/access/configure-access-modal.js @@ -1,6 +1,6 @@ define([ 'knockout', - 'text!./configure-access-modal.html', + 'text!./configure-access-modal.html', 'components/Component', 'utils/CommonUtils', 'utils/AutoBind', @@ -8,7 +8,7 @@ define([ 'databindings', ], function ( ko, - view, + view, Component, commonUtils, AutoBind @@ -33,7 +33,7 @@ define([ this.readRoleOptions = ko.computed(() => this.readRoleSuggestions().map(r => r.name)); this.readRoleSearch = ko.observable(); this.readRoleSearch.subscribe(str => this.loadReadRoleSuggestions(str)); - + this.shareFlag = ko.observable(true); this.isOwnerFn = params.isOwnerFn; diff --git a/js/pages/concept-sets/conceptset-manager.html b/js/pages/concept-sets/conceptset-manager.html index a1643e25a..4ff046c19 100644 --- a/js/pages/concept-sets/conceptset-manager.html +++ b/js/pages/concept-sets/conceptset-manager.html @@ -27,7 +27,7 @@ - + diff --git a/js/pages/concept-sets/conceptset-manager.js b/js/pages/concept-sets/conceptset-manager.js index 7402046ed..b4430c230 100644 --- a/js/pages/concept-sets/conceptset-manager.js +++ b/js/pages/concept-sets/conceptset-manager.js @@ -8,7 +8,8 @@ define([ './const', 'const', 'components/conceptset/utils', - 'services/Vocabulary', + 'services/Vocabulary', + 'services/ShareRoleCheck', 'services/Permission', 'services/Tags', 'components/security/access/const', @@ -55,7 +56,8 @@ define([ constants, globalConstants, utils, - vocabularyAPI, + vocabularyAPI, + shareRoleCheck, GlobalPermissionService, TagsService, { entityType }, @@ -174,7 +176,25 @@ define([ this.canCopy = ko.computed(() => { return this.currentConceptSet() && this.currentConceptSet().id > 0; }); - this.enablePermissionManagement = config.enablePermissionManagement; + + this.enablePermissionManagement = ko.observable(false); + this.enablePermissionManagement(config.enablePermissionManagement); + + this.userCanShare = ko.observable(false); + if (config.permissionManagementRoleId === "") { + this.userCanShare(true); + } else { + shareRoleCheck.checkIfRoleCanShare(authApi.subject(), config.permissionManagementRoleId) + .then(res=>{ + this.userCanShare(res); + }) + .catch(error => { + console.error(error); + alert(ko.i18n('conceptSets.conceptSetManager.shareRoleCheck', 'Error when determining if user can share cohorts')()); + }); + } + + this.isSaving = ko.observable(false); this.isDeleting = ko.observable(false); this.isOptimizing = ko.observable(false); From 8396b4d2d38ad9eaaa05bc2d2cc587f563a00385 Mon Sep 17 00:00:00 2001 From: rkboyce Date: Mon, 6 May 2024 13:50:22 +0000 Subject: [PATCH 05/12] changed the 'shared artifact reader' role checked by Atlas to be the 'public' role since this would remove the need to change WebAPI to add a new system role that pretty much duplicates 'public' --- .../security/access/configure-access-modal.js | 6 +++--- .../cohort-definition-manager.html | 16 ++++++++-------- js/pages/concept-sets/conceptset-manager.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/js/components/security/access/configure-access-modal.js b/js/components/security/access/configure-access-modal.js index 0e95ae08a..ce6c795ee 100644 --- a/js/components/security/access/configure-access-modal.js +++ b/js/components/security/access/configure-access-modal.js @@ -115,7 +115,7 @@ define([ // update shareFlag depending on if the shared artifacts reader role is in readAccessList function testForGlobalRead(value, index, array) { - return value.id === 16; + return value.id === 1; // the 'public' role that every use should have } let tst = this.readAccessList().some(testForGlobalRead); this.shareFlag(tst); @@ -156,7 +156,7 @@ define([ this.isLoading(true); try { console.log('grantGlobalReadAccess function called to grant read permissions!! shareflag: ' + this.shareFlag()); - await this.grantAccessFn('16','READ'); // 16 is 'shared artifacts reader', a SYSTEM role every user should have + await this.grantAccessFn('1','READ'); // 16 is the 'public' role, a SYSTEM role every user should have await this.loadAccessList(); } catch (ex) { console.log(ex); @@ -168,7 +168,7 @@ define([ this.isLoading(true); try { console.log('revokeGlobalReadAccess function called to REVOKE read permissions!! shareflag: ' + this.shareFlag()); - await this.revokeAccessFn('16','READ'); // 16 is 'shared artifacts reader', a SYSTEM role every user should have + await this.revokeAccessFn('1','READ'); // 16 is the 'public' role, a SYSTEM role every user should have await this.loadAccessList(); } catch (ex) { console.log(ex); diff --git a/js/pages/cohort-definitions/cohort-definition-manager.html b/js/pages/cohort-definitions/cohort-definition-manager.html index 5c2260659..9ea780951 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.html +++ b/js/pages/cohort-definitions/cohort-definition-manager.html @@ -76,21 +76,21 @@ - +
  • + +
  • - +
  • diff --git a/js/pages/concept-sets/conceptset-manager.js b/js/pages/concept-sets/conceptset-manager.js index b4430c230..c1ee6f61e 100644 --- a/js/pages/concept-sets/conceptset-manager.js +++ b/js/pages/concept-sets/conceptset-manager.js @@ -190,7 +190,7 @@ define([ }) .catch(error => { console.error(error); - alert(ko.i18n('conceptSets.conceptSetManager.shareRoleCheck', 'Error when determining if user can share cohorts')()); + alert(ko.i18n('conceptSets.conceptSetManager.shareRoleCheck', 'Error when determining if user can share concept sets')()); }); } From 0493316c7ca03d073a8152b408023ddd466fef4c Mon Sep 17 00:00:00 2001 From: rkboyce Date: Thu, 9 May 2024 23:51:59 +0000 Subject: [PATCH 06/12] added the configurable ability to restrict generation of a given cohort to only persons who have been granted permission to change the same cohort. --- .../security/access/configure-access-modal.html | 15 ++++++++------- .../security/access/configure-access-modal.js | 4 ++-- js/services/AuthAPI.js | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/js/components/security/access/configure-access-modal.html b/js/components/security/access/configure-access-modal.html index 8a4edcebf..672339db7 100644 --- a/js/components/security/access/configure-access-modal.html +++ b/js/components/security/access/configure-access-modal.html @@ -92,19 +92,20 @@
    -
    diff --git a/js/components/security/access/configure-access-modal.js b/js/components/security/access/configure-access-modal.js index ce6c795ee..3f8a950f7 100644 --- a/js/components/security/access/configure-access-modal.js +++ b/js/components/security/access/configure-access-modal.js @@ -156,7 +156,7 @@ define([ this.isLoading(true); try { console.log('grantGlobalReadAccess function called to grant read permissions!! shareflag: ' + this.shareFlag()); - await this.grantAccessFn('1','READ'); // 16 is the 'public' role, a SYSTEM role every user should have + await this.grantAccessFn('1','READ'); // 1 is the 'public' role, a SYSTEM role every user should have await this.loadAccessList(); } catch (ex) { console.log(ex); @@ -168,7 +168,7 @@ define([ this.isLoading(true); try { console.log('revokeGlobalReadAccess function called to REVOKE read permissions!! shareflag: ' + this.shareFlag()); - await this.revokeAccessFn('1','READ'); // 16 is the 'public' role, a SYSTEM role every user should have + await this.revokeAccessFn('1','READ'); // 1 is the 'public' role, a SYSTEM role every user should have await this.loadAccessList(); } catch (ex) { console.log(ex); diff --git a/js/services/AuthAPI.js b/js/services/AuthAPI.js index cd88bbb32..b0a28eeba 100644 --- a/js/services/AuthAPI.js +++ b/js/services/AuthAPI.js @@ -407,8 +407,18 @@ define(function(require, exports) { } var isPermittedGenerateCohort = function(cohortId, sourceKey) { - return isPermitted('cohortdefinition:' + cohortId + ':generate:' + sourceKey + ':get') && - isPermitted('cohortdefinition:' + cohortId + ':info:get'); + var v = isPermitted('cohortdefinition:' + cohortId + ':generate:' + sourceKey + ':get') && + isPermitted('cohortdefinition:' + cohortId + ':info:get'); + + // By default, everyone can generate any artifact they have + // permission to read. If a permissionManagementRoleId has + // been assigned, (non- empty string assignment), the default + // generate functionality is not desired. Rather, users will have to + // have a role that allows them to update the specific cohort definition. + if (config.permissionManagementRoleId !== ""){ + v = v && isPermitted('cohortdefinition:' + cohortId + ':put') + } + return v } var isPermittedReadCohortReport = function(cohortId, sourceKey) { From 7e2137822595149a13cc09ac4d778094ee324a4e Mon Sep 17 00:00:00 2001 From: pieterlukasse Date: Fri, 10 May 2024 20:30:26 +0200 Subject: [PATCH 07/12] feat: introduce a new permission instead of relying on a role --- js/const.js | 1 + .../cohort-definition-manager.js | 11 ++----- js/pages/concept-sets/conceptset-manager.js | 11 ++----- js/services/AuthAPI.js | 32 ++++++++++++------- js/services/ShareRoleCheck.js | 30 ++++++++--------- 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/js/const.js b/js/const.js index 9bb28330d..6df92fe7b 100644 --- a/js/const.js +++ b/js/const.js @@ -192,6 +192,7 @@ define([ const apiPaths = { role: (id = '') => `${config.api.url}role/${id}`, roleUsers: roleId => `${config.api.url}role/${roleId}/users`, + userRoles: userId => `${config.api.url}user/${userId}/roles`, permissions: () => `${config.api.url}permission`, rolePermissions: roleId => `${config.api.url}role/${roleId}/permissions`, relations: (roleId, relation, ids = []) => `${config.api.url}role/${roleId}/${relation}/${ids.join('+')}`, diff --git a/js/pages/cohort-definitions/cohort-definition-manager.js b/js/pages/cohort-definitions/cohort-definition-manager.js index fe43bff49..7ceeb8360 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.js +++ b/js/pages/cohort-definitions/cohort-definition-manager.js @@ -208,15 +208,8 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', if (config.permissionManagementRoleId === "") { this.userCanShare(true); } else { - shareRoleCheck.checkIfRoleCanShare(authApi.subject(), config.permissionManagementRoleId) - .then(res=>{ - this.userCanShare(res); - }) - .catch(error => { - console.error(error); - alert(ko.i18n('cohortDefinitions.cohortDefinitionManager.shareRoleCheck', 'Error when determining if user can share cohorts')()); - }); - } + this.userCanShare(authApi.isPermittedGlobalShareCohort()); + } this.relatedSourcecodesOptions = globalConstants.relatedSourcecodesOptions; this.commonUtils = commonUtils; diff --git a/js/pages/concept-sets/conceptset-manager.js b/js/pages/concept-sets/conceptset-manager.js index c1ee6f61e..43005eace 100644 --- a/js/pages/concept-sets/conceptset-manager.js +++ b/js/pages/concept-sets/conceptset-manager.js @@ -184,15 +184,8 @@ define([ if (config.permissionManagementRoleId === "") { this.userCanShare(true); } else { - shareRoleCheck.checkIfRoleCanShare(authApi.subject(), config.permissionManagementRoleId) - .then(res=>{ - this.userCanShare(res); - }) - .catch(error => { - console.error(error); - alert(ko.i18n('conceptSets.conceptSetManager.shareRoleCheck', 'Error when determining if user can share concept sets')()); - }); - } + this.userCanShare(authApi.isPermittedGlobalShareCohort()); + } this.isSaving = ko.observable(false); diff --git a/js/services/AuthAPI.js b/js/services/AuthAPI.js index b0a28eeba..10edfebf9 100644 --- a/js/services/AuthAPI.js +++ b/js/services/AuthAPI.js @@ -62,6 +62,7 @@ define(function(require, exports) { var subject = ko.observable(); var permissions = ko.observable(); var fullName = ko.observable(); + var userId = ko.observable(); const authProvider = ko.observable(); authProvider.subscribe(provider => { @@ -82,6 +83,7 @@ define(function(require, exports) { subject(info.login); authProvider(jqXHR.getResponseHeader('x-auth-provider')); fullName(info.name ? info.name : info.login); + userId(info.id); resolve(); }, error: function (err) { @@ -395,6 +397,12 @@ define(function(require, exports) { return isPermitted('cohortdefinition:' + id + ':copy:get'); } + var isPermittedGlobalShareCohort = function() { + // special * permission (intended for admins) that allows the + // user to share any cohort with a "global reader role": + return isPermitted('cohortdefinition:global:share:put'); + } + var isPermittedUpdateCohort = function(id) { var permission = 'cohortdefinition:' + id + ':put'; return isPermitted(permission); @@ -407,17 +415,17 @@ define(function(require, exports) { } var isPermittedGenerateCohort = function(cohortId, sourceKey) { - var v = isPermitted('cohortdefinition:' + cohortId + ':generate:' + sourceKey + ':get') && - isPermitted('cohortdefinition:' + cohortId + ':info:get'); - - // By default, everyone can generate any artifact they have - // permission to read. If a permissionManagementRoleId has - // been assigned, (non- empty string assignment), the default - // generate functionality is not desired. Rather, users will have to - // have a role that allows them to update the specific cohort definition. - if (config.permissionManagementRoleId !== ""){ - v = v && isPermitted('cohortdefinition:' + cohortId + ':put') - } + var v = isPermitted('cohortdefinition:' + cohortId + ':generate:' + sourceKey + ':get') && + isPermitted('cohortdefinition:' + cohortId + ':info:get'); + + // By default, everyone can generate any artifact they have + // permission to read. If limitedPermissionManagement has + // been set to true, the default + // generate functionality is not desired. Rather, users will have to + // have a permission that allows them to update the specific cohort definition. + if (config.limitedPermissionManagement){ + v = v && isPermitted('cohortdefinition:' + cohortId + ':put') + } return v } @@ -560,6 +568,7 @@ define(function(require, exports) { reloginRequired: reloginRequired, subject: subject, fullName, + userId, tokenExpirationDate: tokenExpirationDate, tokenExpired: tokenExpired, authProvider: authProvider, @@ -586,6 +595,7 @@ define(function(require, exports) { isPermittedReadCohort: isPermittedReadCohort, isPermittedCreateCohort: isPermittedCreateCohort, isPermittedCopyCohort: isPermittedCopyCohort, + isPermittedGlobalShareCohort: isPermittedGlobalShareCohort, isPermittedUpdateCohort: isPermittedUpdateCohort, isPermittedDeleteCohort: isPermittedDeleteCohort, isPermittedGenerateCohort: isPermittedGenerateCohort, diff --git a/js/services/ShareRoleCheck.js b/js/services/ShareRoleCheck.js index eaac53a60..1644cd5c4 100644 --- a/js/services/ShareRoleCheck.js +++ b/js/services/ShareRoleCheck.js @@ -2,26 +2,26 @@ define(function (require, exports) { var $ = require('jquery'); var constants = require('const'); const httpService = require('services/http'); + const authApi = require('services/AuthAPI'); - async function getRoleUsers(subject, permissionManagementRoleId) { - return await httpService.doGet(constants.apiPaths.roleUsers(permissionManagementRoleId)) - .then(({ data = [] }) => data) - .catch((er) => { - console.error('ERROR: Can\'t find users with permissionManagementRoleId: ' + permissionManagementRoleId); - }); - }; - + async function getUserRoles() { + return await httpService.doGet(constants.apiPaths.userRoles(authApi.userId())) + .then(({ data = [] }) => data) + .catch((er) => { + console.error('ERROR: Can\'t find user roles for: ' + authApi.userId()); + }); + }; async function checkIfRoleCanShare(subject, permissionManagementRoleId) { var isAbleToShare = false; - const roleUsers = await getRoleUsers(subject, permissionManagementRoleId); - console.log("INFO: roleUsers:" + roleUsers.toString()); + const userRoles = await getUserRoles(); + console.log("INFO: roleUsers:" + userRoles.toString()); - roleUsers.forEach((user) => { - console.log("INFO: user.login of user that has the permissionManagementRoleId " + permissionManagementRoleId + ": " + user.login + "; subject (currently logged in user): " + subject); - if (subject == user.login){ - isAbleToShare = true; + userRoles.forEach((role) => { + console.log("INFO: role " + role ); + if (role.id == permissionManagementRoleId){ + isAbleToShare = true; } - }); + }); console.log("INFO: isAbleToShare: " + isAbleToShare); return isAbleToShare; }; From 649cc7e9da002984cb3b44164be9ac1498275665 Mon Sep 17 00:00:00 2001 From: pieterlukasse Date: Tue, 14 May 2024 20:02:37 +0200 Subject: [PATCH 08/12] fix: correct styling for sharing buttons on access modal ui --- js/components/security/access/configure-access-modal.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/components/security/access/configure-access-modal.html b/js/components/security/access/configure-access-modal.html index 672339db7..b437d938f 100644 --- a/js/components/security/access/configure-access-modal.html +++ b/js/components/security/access/configure-access-modal.html @@ -92,15 +92,14 @@
    -
  • - -
  • - -
  • -
  • From 48723455dd563b24a6f4a9e5b94248f4ccabc11e Mon Sep 17 00:00:00 2001 From: pieterlukasse Date: Fri, 17 May 2024 14:47:38 +0200 Subject: [PATCH 12/12] fix: better name for isPermittedGlobalShareArtifact ...previous name isPermittedGlobalShareCohort did not reflect the fact that it is about all kind of artifacts --- js/pages/cohort-definitions/cohort-definition-manager.js | 2 +- js/pages/concept-sets/conceptset-manager.js | 2 +- js/services/AuthAPI.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/pages/cohort-definitions/cohort-definition-manager.js b/js/pages/cohort-definitions/cohort-definition-manager.js index d8040396d..895a3b80b 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.js +++ b/js/pages/cohort-definitions/cohort-definition-manager.js @@ -203,7 +203,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', if (config.enablePermissionManagement) { this.userCanShare = ko.observable( !config.limitedPermissionManagement || - authApi.isPermittedGlobalShareCohort()); + authApi.isPermittedGlobalShareArtifact()); } else { this.userCanShare = ko.observable(false); } diff --git a/js/pages/concept-sets/conceptset-manager.js b/js/pages/concept-sets/conceptset-manager.js index 8704fad6b..6fb3f5aab 100644 --- a/js/pages/concept-sets/conceptset-manager.js +++ b/js/pages/concept-sets/conceptset-manager.js @@ -179,7 +179,7 @@ define([ if (config.enablePermissionManagement) { this.userCanShare = ko.observable( !config.limitedPermissionManagement || - authApi.isPermittedGlobalShareCohort()); + authApi.isPermittedGlobalShareArtifact()); } else { this.userCanShare = ko.observable(false); } diff --git a/js/services/AuthAPI.js b/js/services/AuthAPI.js index 77c7ea4c1..312ac890f 100644 --- a/js/services/AuthAPI.js +++ b/js/services/AuthAPI.js @@ -395,7 +395,7 @@ define(function(require, exports) { return isPermitted('cohortdefinition:' + id + ':copy:get'); } - var isPermittedGlobalShareCohort = function() { + var isPermittedGlobalShareArtifact = function() { // special * permission (intended for admins) that allows the // user to share any artifact with a "global reader role": return isPermitted('artifact:global:share:put'); @@ -592,7 +592,7 @@ define(function(require, exports) { isPermittedReadCohort: isPermittedReadCohort, isPermittedCreateCohort: isPermittedCreateCohort, isPermittedCopyCohort: isPermittedCopyCohort, - isPermittedGlobalShareCohort: isPermittedGlobalShareCohort, + isPermittedGlobalShareArtifact: isPermittedGlobalShareArtifact, isPermittedUpdateCohort: isPermittedUpdateCohort, isPermittedDeleteCohort: isPermittedDeleteCohort, isPermittedGenerateCohort: isPermittedGenerateCohort,