Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add read permissions #2849

Merged
merged 12 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*~
.idea
/web.config
/node_modules/
Expand Down
64 changes: 49 additions & 15 deletions js/components/security/access/configure-access-modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,80 @@
data: {
classes: classes,
isLoading: isLoading,
roleName: roleName,
columns: columns,
accessList: accessList,
readRoleName: readRoleName,
writeRoleName: writeRoleName,
writeAccessColumns: writeAccessColumns,
readAccessColumns: readAccessColumns,
writeAccessList: writeAccessList,
readAccessList: readAccessList,
grantAccess: grantAccess,
revokeRoleAccess: revokeRoleAccess,
roleOptions: roleOptions,
roleSearch: roleSearch
readRoleOptions: readRoleOptions,
readRoleSearch: readRoleSearch,
writeRoleOptions: writeRoleOptions,
writeRoleSearch: writeRoleSearch
}">
<loading data-bind="css: classes('loading-panel'), visible: isLoading()" params="status: ko.i18n('common.configureAccessModal.loadingAccessList', 'Loading access list...')"></loading>
<div data-bind="css: classes()">
<div data-bind="if: !isLoading()">
<div data-bind="css: classes('new-access')">
<label data-bind="css: classes('new-access-label'), text: ko.i18n('common.configureAccessModal.addWriteAccessToRole', 'Add write access to role:')"></label>
<label data-bind="css: classes('new-access-label'), text: ko.i18n('common.configureAccessModal.addWriteAccessToRole', 'Add WRITE access to role:')"></label>
<div class="input-group"
data-bind="css: classes({ element: 'new-access-btn-group', extra: ['new-access-btn-group'] })">
<input
class="form-control"
data-bind="
textInput: roleSearch,
value: roleName,
textInput: writeRoleSearch,
value: writeRoleName,
eventType: 'blur',
ko_autocomplete: { source: roleOptions(), minLength: 0, maxShowItems: 10, scroll: true }"
ko_autocomplete: { source: writeRoleOptions(), minLength: 0, maxShowItems: 10, scroll: true }"
>
<span class="input-group-btn">
<button class="btn btn-primary" type="button" data-bind="click: grantAccess, attr: { disabled: !(roleName() && roleName().length) }, text: ko.i18n('common.add', 'Add')"></button>
<button class="btn btn-primary" type="button" data-bind="click: grantAccess.bind($data,'WRITE'), attr: { disabled: !(writeRoleName() && writeRoleName().length) }, text: ko.i18n('common.add', 'Add')"></button>
</span>
</div>
<div data-bind="css: classes('access-list')">
<label data-bind="css: classes('access-list-label'), text: ko.i18n('common.configureAccessModal.rolesWithWriteAccess', 'Roles with write access:')"></label>
<label data-bind="css: classes('access-list-label'), text: ko.i18n('common.configureAccessModal.rolesWithWriteAccess', 'Roles with WRITE access:')"></label>
<div>
<table data-bind="
css: classes({ element: 'access-table', extra: ['table', 'table-bordered', 'table-hover'] }),
dataTable:{
data: accessList,
options: {columns: columns, language: ko.i18n('datatable.language')},
data: writeAccessList,
options: {columns: writeAccessColumns, language: ko.i18n('datatable.language')},
}
"/>
</div>
</div>
</div>
</div>
<div data-bind="css: classes('new-access')">
<label data-bind="css: classes('new-access-label'), text: ko.i18n('common.configureAccessModal.addReadAccessToRole', 'Add READ access to role:')"></label>
<div class="input-group"
data-bind="css: classes({ element: 'new-access-btn-group', extra: ['new-access-btn-group'] })">
<input
class="form-control"
data-bind="
textInput: readRoleSearch,
value: readRoleName,
eventType: 'blur',
ko_autocomplete: { source: readRoleOptions(), minLength: 0, maxShowItems: 10, scroll: true }"
>
<span class="input-group-btn">
<button class="btn btn-primary" type="button" data-bind="click: grantAccess.bind($data,'READ'), attr: { disabled: !(readRoleName() && readRoleName().length) }, text: ko.i18n('common.add', 'Add')"></button>
</span>
</div>
<div data-bind="css: classes('access-list')">
<label data-bind="css: classes('access-list-label'), text: ko.i18n('common.configureAccessModal.rolesWithReadAccess', 'Roles with READ access:')"></label>
<div>
<table data-bind="
css: classes({ element: 'access-table', extra: ['table', 'table-bordered', 'table-hover'] }),
dataTable:{
data: readAccessList,
options: {columns: readAccessColumns, language: ko.i18n('datatable.language')},
}
"/>
</div>
</div>
</div>
</div>
</div>
</atlas-modal>
</atlas-modal>
100 changes: 72 additions & 28 deletions js/components/security/access/configure-access-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,80 +19,124 @@ define([

this.isModalShown = params.isModalShown;
this.isLoading = ko.observable(false);
this.accessList = ko.observable([]);
this.roleName = ko.observable();

this.roleSuggestions = ko.observable([]);
this.roleOptions = ko.computed(() => this.roleSuggestions().map(r => r.name));
this.roleSearch = ko.observable();
this.roleSearch.subscribe(str => this.loadRoleSuggestions(str));
this.writeRoleName = ko.observable();
this.writeAccessList = ko.observable([]);
this.writeRoleSuggestions = ko.observable([]);
this.writeRoleOptions = ko.computed(() => this.writeRoleSuggestions().map(r => r.name));
this.writeRoleSearch = ko.observable();
this.writeRoleSearch.subscribe(str => this.loadWriteRoleSuggestions(str));

this.readAccessList = ko.observable([]);
this.readRoleName = ko.observable();
this.readRoleSuggestions = ko.observable([]);
this.readRoleOptions = ko.computed(() => this.readRoleSuggestions().map(r => r.name));
this.readRoleSearch = ko.observable();
this.readRoleSearch.subscribe(str => this.loadReadRoleSuggestions(str));

this.isOwnerFn = params.isOwnerFn;
this.grantAccessFn = params.grantAccessFn;
this.loadAccessListFn = params.loadAccessListFn;
this.revokeAccessFn = params.revokeAccessFn;
this.loadRoleSuggestionsFn = params.loadRoleSuggestionsFn;

this.columns = [
this.readAccessColumns = [
{
class: this.classes('access-tbl-col-id'),
title: ko.i18n('readAccessColumns.id', 'ID'),
data: 'id'
},
{
class: this.classes('access-tbl-col-name'),
title: ko.i18n('readAccessColumns.name', 'Name'),
data: 'name'
},
{
class: this.classes('access-tbl-col-action'),
title: ko.i18n('readAccessColumns.action', 'Action'),
render: (s, p, d) => !this.isOwnerFn(d.name) ? `<a data-bind="css: '${this.classes('revoke-link')}', click: revoke, text: ko.i18n('common.configureAccessModal.revoke', 'Revoke')"></a>` : '-'
}
];

this.writeAccessColumns = [
{
class: this.classes('access-tbl-col-id'),
title: ko.i18n('columns.id', 'ID'),
title: ko.i18n('writeAccessColumns.id', 'ID'),
data: 'id'
},
{
class: this.classes('access-tbl-col-name'),
title: ko.i18n('columns.name', 'Name'),
title: ko.i18n('writeAccessColumns.name', 'Name'),
data: 'name'
},
{
class: this.classes('access-tbl-col-action'),
title: ko.i18n('columns.action', 'Action'),
title: ko.i18n('writeAccessColumns.action', 'Action'),
render: (s, p, d) => !this.isOwnerFn(d.name) ? `<a data-bind="css: '${this.classes('revoke-link')}', click: revoke, text: ko.i18n('common.configureAccessModal.revoke', 'Revoke')"></a>` : '-'
}
];

this.isModalShown.subscribe(open => !!open && this.loadAccessList());
}

async _loadAccessList() {
let accessList = await this.loadAccessListFn();
accessList = accessList.map(a => ({ ...a, revoke: () => this.revokeRoleAccess(a.id) }));
this.accessList(accessList);
async _loadReadAccessList() {
let accessList = await this.loadAccessListFn('READ');
accessList = accessList.map(a => ({ ...a, revoke: () => this.revokeRoleAccess(a.id, 'READ') }));
this.readAccessList(accessList);
}

async _loadWriteAccessList() {
let accessList = await this.loadAccessListFn('WRITE');
accessList = accessList.map(a => ({ ...a, revoke: () => this.revokeRoleAccess(a.id, 'WRITE') }));
this.writeAccessList(accessList);
}

async loadRoleSuggestions() {
const res = await this.loadRoleSuggestionsFn(this.roleSearch());
this.roleSuggestions(res);
async loadReadRoleSuggestions() {
const res = await this.loadRoleSuggestionsFn(this.readRoleSearch());
this.readRoleSuggestions(res);
}

async loadWriteRoleSuggestions() {
const res = await this.loadRoleSuggestionsFn(this.writeRoleSearch());
this.writeRoleSuggestions(res);
}

async loadAccessList() {
this.isLoading(false);
this.isLoading(true);
try {
await this._loadAccessList();
await this._loadReadAccessList();
await this._loadWriteAccessList();
} catch (ex) {
console.log(ex);
}
this.isLoading(false);
}

async grantAccess() {
async grantAccess(perm_type) {
this.isLoading(true);
try {
const role = this.roleSuggestions().find(r => r.name === this.roleName());
await this.grantAccessFn(role.id);
await this._loadAccessList();
this.roleName('');
if (perm_type == 'WRITE'){
const role = this.writeRoleSuggestions().find(r => r.name === this.writeRoleName());
await this.grantAccessFn(role.id,'WRITE');
await this._loadWriteAccessList();
this.writeRoleName('');
} else {
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);
}
this.isLoading(false);
}

async revokeRoleAccess(roleId) {
async revokeRoleAccess(roleId, perm_type) {
this.isLoading(true);
try {
await this.revokeAccessFn(roleId);
await this._loadAccessList();
try {
await this.revokeAccessFn(roleId, perm_type);
await this.loadAccessList();
} catch (ex) {
console.log(ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@
<!-- ko ifnot: isNewEntity() -->
<button type="button" class="btn btn-primary" data-bind="click: () => isTagsModalShown(!isTagsModalShown()), visible: isEditPermitted() && !previewVersion(), css: { disabled: isProcessing() }, title: ko.i18n('common.tags', 'Tags')"><i class="fa fa-tags"></i></button>
<button type="button" class="btn btn-primary" data-bind="visible: !previewVersion(), click: copyCc, css: {disabled: !canCopy() || isProcessing() }, title: , title: ko.i18n('common.createACopy', 'Create a copy')"><i class="fa fa-copy"></i></button>

<!-- ko if: enablePermissionManagement -->
<button class="btn btn-primary" data-bind="visible: isOwner() && !previewVersion(), click: () => isAccessModalShown(!isAccessModalShown()), title: ko.i18n('common.configureAccess', 'Configure access')">
<i class="fa fa-lock"></i>
</button>
<button type="button" class="btn btn-danger" data-bind="visible: !previewVersion(), click: deleteCc, css: {disabled: !$component.isDeletePermitted() || isProcessing() }"><i class="fa fa-trash-alt"></i></button>
<!-- /ko -->

<button type="button" class="btn btn-danger" data-bind="visible: !previewVersion(), click: deleteCc, css: {disabled: !$component.isDeletePermitted() || isProcessing() }"><i class="fa fa-trash-alt"></i></button>
<!-- /ko -->
</div>
</div>
Expand Down Expand Up @@ -103,4 +107,4 @@
loadAvailableTagsFn: $component.loadAvailableTags,
checkAssignPermissionFn: $component.checkAssignPermission,
checkUnassignPermissionFn: $component.checkUnassignPermission
"></tags-modal>
"></tags-modal>
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ define([
this.areStratasNamesEmpty = ko.observable();
this.duplicatedStrataNames = ko.observable([]);

this.enablePermissionManagement = ko.pureComputed(() => {
return config.enablePermissionManagement;
});

this.designDirtyFlag = sharedState.CohortCharacterization.dirtyFlag;
this.loading = ko.observable(false);
this.defaultName = ko.unwrap(constants.newEntityNames.characterization);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
<button class="btn btn-primary" data-bind="click: closeAnalysis, enable: !isProcessing(), title: ko.i18n('common.close', 'Close')"><i class="fa fa-times"></i></button>
<!-- ko ifnot: isNewEntity -->
<button type="button" class="btn btn-primary" data-bind="click: copyFeatureAnalysis, css: {disabled: !canCopy() || isProcessing() }, title: ko.i18n('common.createACopy', 'Create a copy')"><i class="fa fa-copy"></i></button>

<!-- ko if: enablePermissionManagement -->
<button class="btn btn-primary" data-bind="visible: isOwner, enable: !isProcessing(), click: () => isAccessModalShown(!isAccessModalShown()), title: ko.i18n('common.configureAccess', 'Configure access')">
<i class="fa fa-lock"></i>
</button>
<!-- /ko -->

<button class="btn btn-danger" data-bind="click: deleteFeature, enable: canDelete() && !isProcessing(), title: ko.i18n('common.delete', 'Delete')"><i class="fa fa-trash-alt"></i></button>
<!-- /ko -->
</div>
Expand Down Expand Up @@ -53,4 +57,4 @@
grantAccessFn: $component.grantAccess,
revokeAccessFn: $component.revokeAccess,
loadRoleSuggestionsFn: $component.loadAccessRoleSuggestions
"></configure-access-modal>
"></configure-access-modal>
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ define([
});
this.editorClasses = ko.computed(() => this.classes({ element: 'content', modifiers: this.canEdit() ? '' : 'disabled' }))

this.enablePermissionManagement = ko.pureComputed(() => {
return config.enablePermissionManagement;
});

this.selectedTabKey = ko.observable();
this.componentParams = ko.observable({
...params,
Expand Down
9 changes: 6 additions & 3 deletions js/pages/cohort-definitions/cohort-definition-manager.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@
data-bind="visible: !previewVersion(), title: ko.i18n('cohortDefinitions.cohortDefinitionManager.createCopyCohortTitle', 'Create a copy of this cohort definition'), click: copy, enable: canCopy() && !isProcessing()"><i
class="fa fa-copy"></i></button>
<button class="btn btn-primary"
data-bind="visible: !previewVersion(), title: ko.i18n('cohortDefinitions.cohortDefinitionManager.getLinkCohortTitle', 'Get a link to this cohort definition'), enable: !dirtyFlag().isDirty() && !isProcessing(), click: function () { $component.cohortLinkModalOpened(true) }"><i
class="fa fa-link"></i></button>
data-bind="visible: !previewVersion(), title: ko.i18n('cohortDefinitions.cohortDefinitionManager.getLinkCohortTitle', 'Get a link to this cohort definition'), enable: !dirtyFlag().isDirty() && !isProcessing(), click: function () { $component.cohortLinkModalOpened(true) }"><i class="fa fa-link"></i></button>

<!-- ko if: enablePermissionManagement -->
<button class="btn btn-primary"
data-bind="title: ko.i18n('common.configureAccess', 'Configure access'), visible: isOwner() && !previewVersion(), click: () => isAccessModalShown(!isAccessModalShown())">
<i class="fa fa-lock"></i>
</button>
<!-- /ko -->

<!-- ko if: !isRunning() -->
<button class="btn btn-danger"
data-bind="visible: !previewVersion(), title: ko.i18n('common.delete', 'Delete'), click: $component.delete, enable: canDelete() && !isProcessing()"><i
Expand Down Expand Up @@ -790,4 +793,4 @@ <h3 data-bind="text: ko.i18n('cohortDefinitions.cohortDefinitionManager.panels.a
</div>
</script>

<!-- /ko -->
<!-- /ko -->
7 changes: 5 additions & 2 deletions js/pages/cohort-definitions/cohort-definition-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,13 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
super(params);

this.previewVersion = sharedState.CohortDefinition.previewVersion;

this.pollTimeoutId = null;
this.authApi = authApi;
this.config = config;
this.config = config;
this.enablePermissionManagement = ko.pureComputed(() => {
return config.enablePermissionManagement;
});
this.relatedSourcecodesOptions = globalConstants.relatedSourcecodesOptions;
this.commonUtils = commonUtils;
this.isLoading = ko.observable(false);
Expand Down
4 changes: 4 additions & 0 deletions js/pages/concept-sets/conceptset-manager.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@
<!-- ko if: $component.currentConceptSet().id != null && $component.currentConceptSet().id != 0 -->
<button type="button" class="btn btn-primary" data-bind="click: () => isTagsModalShown(!isTagsModalShown()), visible: canEdit() && !previewVersion(), css: { disabled: isProcessing() }, title: ko.i18n('common.tags', 'Tags')"><i class="fa fa-tags"></i></button>
<button type="button" class="btn btn-primary" data-bind="visible: !previewVersion(), click: optimize, css: { disabled: !canOptimize() || isProcessing() }, text: ko.i18n('cs.manager.optimize', 'Optimize')"></button>

<!-- ko if: enablePermissionManagement -->
<button class="btn btn-primary" data-bind="visible: !previewVersion() && isOwner(), click: () => isAccessModalShown(!isAccessModalShown()), title: ko.i18n('common.configureAccess', 'Configure access')">
<i class="fa fa-lock"></i>
</button>
<!-- /ko -->

<button type="button" class="btn btn-danger" data-bind="visible: !previewVersion(), click: $component.delete, css: { disabled: !canDelete() || isProcessing() }, title: ko.i18n('common.delete', 'Delete')"><i class="fa fa-trash-alt"></i></button>
<!-- /ko -->
</div>
Expand Down