[ACA-2199] Upload Version - granular permission (#938)

* canUploadVersion evaluator

* change upload version action rule

* update docs

* simplify condition

* unit tests and code fixes
This commit is contained in:
Cilibiu Bogdan 2019-02-13 23:04:48 +02:00 committed by Denys Vuika
parent 7c38201500
commit bb5ce29445
5 changed files with 369 additions and 36 deletions

View File

@ -129,29 +129,30 @@ The button will be visible only when the linked rule evaluates to `true`.
## Application Evaluators
| Key | Description |
| ------------------------------- | ------------------------------------------------------------ |
| app.selection.canDelete | User has permission to delete selected node(s). |
| app.selection.canDownload | User can download selected node(s). |
| app.selection.notEmpty | At least one node is selected. |
| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. |
| app.selection.canAddFavorite | User can add selected node(s) to favorites. |
| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. |
| app.selection.first.canUpdate | User has permission to update selected node(s). |
| app.selection.file | A single File node is selected. |
| app.selection.file.canShare | User is able to share the selected file. |
| app.selection.file.isShared | A shared node is selected |
| app.selection.file.isLocked | File is locked for editing |
| app.selection.library | A single Library node is selected. |
| app.selection.isPrivateLibrary | A private Library node is selected. |
| app.selection.hasLibraryRole | The selected Library node has a role property. |
| app.selection.hasNoLibraryRole | The selected Library node has no role property. |
| app.selection.folder | A single Folder node is selected. |
| app.selection.folder.canUpdate | User has permissions to update the selected folder. |
| app.selection.folder.canUpdate | User has permissions to update the selected folder. |
| app.selection.file.canLock | User has permissions to lock file. |
| app.selection.file.canUnlock | User has permissions to unlock file. |
| repository.isQuickShareEnabled | Whether the quick share repository option is enabled or not. |
| Key | Description |
| ----------------------------------- | ------------------------------------------------------------ |
| app.selection.canDelete | User has permission to delete selected node(s). |
| app.selection.canDownload | User can download selected node(s). |
| app.selection.notEmpty | At least one node is selected. |
| app.selection.canUnshare | User is able to remove selected node(s) from public sharing. |
| app.selection.canAddFavorite | User can add selected node(s) to favorites. |
| app.selection.canRemoveFavorite | User can remove selected node(s) from favorites. |
| app.selection.first.canUpdate | User has permission to update selected node(s). |
| app.selection.file | A single File node is selected. |
| app.selection.file.canShare | User is able to share the selected file. |
| app.selection.file.isShared | A shared node is selected |
| app.selection.file.isLocked | File is locked for editing |
| app.selection.file.canUploadVersion | User can update file version |
| app.selection.library | A single Library node is selected. |
| app.selection.isPrivateLibrary | A private Library node is selected. |
| app.selection.hasLibraryRole | The selected Library node has a role property. |
| app.selection.hasNoLibraryRole | The selected Library node has no role property. |
| app.selection.folder | A single Folder node is selected. |
| app.selection.folder.canUpdate | User has permissions to update the selected folder. |
| app.selection.folder.canUpdate | User has permissions to update the selected folder. |
| app.selection.file.canLock | User has permissions to lock file. |
| app.selection.file.canUnlock | User has permissions to unlock file. |
| repository.isQuickShareEnabled | Whether the quick share repository option is enabled or not. |
## Navigation Evaluators

View File

@ -123,6 +123,7 @@ export class CoreExtensionsModule {
'app.selection.file.canShare': app.canShareFile,
'app.selection.file.isShared': app.isShared,
'app.selection.file.isLocked': app.hasLockedFiles,
'app.selection.file.canUploadVersion': app.canUploadVersion,
'app.selection.library': app.hasLibrarySelected,
'app.selection.isPrivateLibrary': app.isPrivateLibrary,
'app.selection.hasLibraryRole': app.hasLibraryRole,

View File

@ -0,0 +1,316 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import * as app from './app.evaluators';
describe('app.evaluators', () => {
describe('isWriteLocked', () => {
it('should return [true] if lock type is set', () => {
const context: any = {
selection: {
file: {
entry: {
properties: {
'cm:lockType': 'WRITE_LOCK'
}
}
}
}
};
expect(app.isWriteLocked(context, null)).toBe(true);
});
it('should return [false] if lock type is not set', () => {
const context: any = {
selection: {
file: {
entry: {
properties: {}
}
}
}
};
expect(app.isWriteLocked(context, null)).toBe(false);
});
it('should return [false] if selection not present', () => {
const context: any = {};
expect(app.isWriteLocked(context, null)).toBe(false);
});
});
describe('hasLockedFiles', () => {
it('should return [false] if selection not present', () => {
const context: any = {};
expect(app.hasLockedFiles(context, null)).toBe(false);
});
it('should return [false] if nodes not present', () => {
const context: any = {
selection: {
nodes: null
}
};
expect(app.hasLockedFiles(context, null)).toBe(false);
});
it('should return [false] if no files selected', () => {
const context: any = {
selection: {
nodes: [
{
entry: {
isFile: false
}
},
{
entry: {
isFile: false
}
}
]
}
};
expect(app.hasLockedFiles(context, null)).toBe(false);
});
it('should return [true] when one of files is locked', () => {
const context: any = {
selection: {
nodes: [
{
entry: {
isFile: true,
isLocked: true
}
},
{
entry: {
isFile: true,
isLocked: false
}
}
]
}
};
expect(app.hasLockedFiles(context, null)).toBe(true);
});
});
it('should return [true] when one of files has readonly lock', () => {
const context: any = {
selection: {
nodes: [
{
entry: {
isFile: true,
isLocked: false
}
},
{
entry: {
isFile: true,
isLocked: false,
properties: {
'cm:lockType': 'READ_ONLY_LOCK'
}
}
}
]
}
};
expect(app.hasLockedFiles(context, null)).toBe(true);
});
describe('canUpdateSelectedNode', () => {
it('should return [false] if selection not preset', () => {
const context: any = {};
expect(app.canUpdateSelectedNode(context, null)).toBe(false);
});
it('should return [false] if selection is empty', () => {
const context: any = {
selection: {
isEmpty: true
}
};
expect(app.canUpdateSelectedNode(context, null)).toBe(false);
});
it('should return [false] if first selection is not a file', () => {
const context: any = {
permissions: {
check: () => false
},
selection: {
isEmpty: false,
first: {
entry: {
isFile: false
}
}
}
};
expect(app.canUpdateSelectedNode(context, null)).toBe(false);
});
it('should return [false] if the file is locked', () => {
const context: any = {
permissions: {
check: () => true
},
selection: {
isEmpty: false,
nodes: [
{
entry: {
isFile: true,
isLocked: true
}
}
],
first: {
entry: {
isFile: true,
isLocked: true
}
}
}
};
expect(app.canUpdateSelectedNode(context, null)).toBe(false);
});
it('should evaluate allowable operation for the file', () => {
const context: any = {
permissions: {
check: () => true
},
selection: {
isEmpty: false,
nodes: [
{
entry: {
isFile: true,
allowableOperationsOnTarget: []
}
}
],
first: {
entry: {
isFile: true,
allowableOperationsOnTarget: []
}
}
}
};
expect(app.canUpdateSelectedNode(context, null)).toBe(true);
});
});
describe('canUploadVersion', () => {
it('should return [true] if user has locked it previously', () => {
const context: any = {
profile: {
id: 'user1'
},
selection: {
file: {
entry: {
properties: {
'cm:lockType': 'WRITE_LOCK',
'cm:lockOwner': {
id: 'user1'
}
}
}
}
}
};
expect(app.canUploadVersion(context, null)).toBe(true);
});
it('should return [false] if other user has locked it previously', () => {
const context: any = {
profile: {
id: 'user2'
},
selection: {
file: {
entry: {
properties: {
'cm:lockType': 'WRITE_LOCK',
'cm:lockOwner': {
id: 'user1'
}
}
}
}
}
};
expect(app.canUploadVersion(context, null)).toBe(false);
});
it('should check the [update] operation when no write lock present', () => {
let checked = false;
const context: any = {
permissions: {
check: () => (checked = true)
},
selection: {
isEmpty: false,
nodes: [
{
entry: {
isFile: true
}
}
],
first: {
entry: {
isFile: true
}
}
}
};
expect(app.canUploadVersion(context, null)).toBe(true);
expect(checked).toBe(true);
});
});
});

View File

@ -283,17 +283,21 @@ export function hasLockedFiles(
context: RuleContext,
...args: RuleParameter[]
): boolean {
return context.selection.nodes.some(node => {
if (!node.entry.isFile) {
return false;
}
if (context && context.selection && context.selection.nodes) {
return context.selection.nodes.some(node => {
if (!node.entry.isFile) {
return false;
}
return (
node.entry.isLocked ||
(node.entry.properties &&
node.entry.properties['cm:lockType'] === 'READ_ONLY_LOCK')
);
});
return (
node.entry.isLocked ||
(node.entry.properties &&
node.entry.properties['cm:lockType'] === 'READ_ONLY_LOCK')
);
});
}
return false;
}
export function isWriteLocked(
@ -301,6 +305,8 @@ export function isWriteLocked(
...args: RuleParameter[]
): boolean {
return !!(
context &&
context.selection &&
context.selection.file &&
context.selection.file.entry &&
context.selection.file.entry.properties &&
@ -340,3 +346,12 @@ export function canUnlockFile(
isUserWriteLockOwner(context, ...args))
);
}
export function canUploadVersion(
context: AppRuleContext,
...args: RuleParameter[]
): boolean {
return isWriteLocked(context, ...args)
? isUserWriteLockOwner(context, ...args)
: canUpdateSelectedNode(context, ...args);
}

View File

@ -662,7 +662,7 @@
"click": "UPLOAD_FILE_VERSION"
},
"rules": {
"visible": "app.toolbar.versions"
"visible": "app.selection.file.canUploadVersion"
}
},
{
@ -878,7 +878,7 @@
"click": "UPLOAD_FILE_VERSION"
},
"rules": {
"visible": "app.toolbar.versions"
"visible": "app.selection.file.canUploadVersion"
}
},
{
@ -1102,7 +1102,7 @@
"click": "UPLOAD_FILE_VERSION"
},
"rules": {
"visible": "app.toolbar.versions"
"visible": "app.selection.file.canUploadVersion"
}
},
{