Skip to content

Commit

Permalink
Merge pull request #4589 from gchq/gh-4588-move-needs-edit-perms
Browse files Browse the repository at this point in the history
PR for #4588 - Explorer API allows document move with only VIEW permission
  • Loading branch information
at055612 authored Nov 6, 2024
2 parents ae24f38 + 9b27a97 commit 0917231
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ protected void onBind() {
final boolean singleSelection = selectedItems.size() == 1;
final ExplorerNode primarySelection = getPrimarySelection();

if (selectedItems.size() > 0 && !ExplorerConstants.isFavouritesNode(primarySelection)) {
if (!selectedItems.isEmpty() && !ExplorerConstants.isFavouritesNode(primarySelection)) {
showItemContextMenu(event, selectedItems, singleSelection, primarySelection);
}
}));
Expand Down Expand Up @@ -550,8 +550,8 @@ private void renameItems(final List<ExplorerNode> explorerNodeList) {
if (!dirtyList.isEmpty()) {
final DocRef docRef = dirtyList.get(0).getDocRef();
AlertEvent.fireWarn(this, "You must save changes to " + docRef.getType() + " '"
+ docRef.getDisplayValue()
+ "' before it can be renamed.", null);
+ docRef.getDisplayValue()
+ "' before it can be renamed.", null);
} else if (!cleanList.isEmpty()) {
ShowRenameDocumentDialogEvent.fire(DocumentPluginEventManager.this, cleanList);
}
Expand Down Expand Up @@ -778,9 +778,9 @@ public static String buildNotFoundMessage(final DocRef docRef) {
: "Document";

return prefix +
" " +
displayName +
" doesn't exist or you do not have permission to open it.";
" " +
displayName +
" doesn't exist or you do not have permission to open it.";
} else {
return null;
}
Expand Down Expand Up @@ -811,7 +811,21 @@ private List<ExplorerNode> getExplorerNodeListWithPermission(
final List<ExplorerNode> list = new ArrayList<>();
for (final Map.Entry<ExplorerNode, ExplorerNodePermissions> entry : documentPermissionMap.entrySet()) {
if ((includeSystemNodes || !DocumentTypes.isSystem(entry.getKey().getType()))
&& entry.getValue().hasDocumentPermission(permission)) {
&& entry.getValue().hasDocumentPermission(permission)) {
list.add(entry.getKey());
}
}

list.sort(Comparator.comparing(HasDisplayValue::getDisplayValue));
return list;
}

private List<ExplorerNode> getExplorerNodeList(
final Map<ExplorerNode, ExplorerNodePermissions> documentPermissionMap,
final boolean includeSystemNodes) {
final List<ExplorerNode> list = new ArrayList<>();
for (final Map.Entry<ExplorerNode, ExplorerNodePermissions> entry : documentPermissionMap.entrySet()) {
if ((includeSystemNodes || !DocumentTypes.isSystem(entry.getKey().getType()))) {
list.add(entry.getKey());
}
}
Expand Down Expand Up @@ -1035,6 +1049,7 @@ private IconMenuItem createIconMenuItemFromDocumentType(
private void addModifyMenuItems(final List<Item> menuItems,
final boolean singleSelection,
final Map<ExplorerNode, ExplorerNodePermissions> documentPermissionMap) {
final List<ExplorerNode> allItems = getExplorerNodeList(documentPermissionMap, false);
final List<ExplorerNode> readableItems = getExplorerNodeListWithPermission(documentPermissionMap,
DocumentPermission.VIEW,
false);
Expand All @@ -1061,8 +1076,8 @@ private void addModifyMenuItems(final List<Item> menuItems,

// Feeds are a special case so can't be renamed, see https://github.com/gchq/stroom/issues/2912
final boolean isRenameEnabled = singleSelection
&& allowUpdate
&& !hasFeed;
&& allowUpdate
&& !hasFeed;

// Feeds are a special case so can't be copied, see https://github.com/gchq/stroom/issues/3048
final boolean isCopyEnabled = allowRead && !hasFeed;
Expand All @@ -1078,9 +1093,7 @@ private void addModifyMenuItems(final List<Item> menuItems,
menuItems.add(createRemoveTagsMenuItem(updatableItems, 22, isRemoveTagsEnabled));
}
menuItems.add(createCopyMenuItem(readableItems, 23, isCopyEnabled));

menuItems.add(createCopyAsMenuItem(readableItems, 24));

menuItems.add(createCopyAsMenuItem(allItems, readableItems, 24));
menuItems.add(createMoveMenuItem(updatableItems, 25, allowUpdate));
menuItems.add(createRenameMenuItem(updatableItems, 26, isRenameEnabled));
menuItems.add(createDeleteMenuItem(deletableItems, 27, allowDelete));
Expand All @@ -1092,16 +1105,21 @@ private void addModifyMenuItems(final List<Item> menuItems,
menuItems.add(createExportMenuItem(29, readableItems));
}

// Only allow users to change permissions if they have a single item selected.
if (singleSelection) {
if (readableItems.size() == 1) {
// Only need VIEW to see the deps
final DocRef docRef = readableItems.get(0).getDocRef();
menuItems.add(new Separator(30));
menuItems.add(createShowDependenciesFromMenuItem(docRef, 31));
menuItems.add(createShowDependantsMenuItem(docRef, 32));
}

final List<ExplorerNode> ownedItems = getExplorerNodeListWithPermission(documentPermissionMap,
DocumentPermission.OWNER,
true);
if (ownedItems.size() == 1) {
// Only allow users to change permissions if they have a single item selected.
final DocRef docRef = ownedItems.get(0).getDocRef();
menuItems.add(new Separator(30));
menuItems.add(createShowDependenciesFromMenuItem(docRef, 31));
menuItems.add(createShowDependantsMenuItem(docRef, 32));
menuItems.add(new Separator(33));
menuItems.add(createPermissionsMenuItem(docRef, 34, true));
}
Expand Down Expand Up @@ -1331,9 +1349,10 @@ private MenuItem createCopyMenuItem(final List<ExplorerNode> explorerNodeList,
.build();
}

private MenuItem createCopyAsMenuItem(final List<ExplorerNode> explorerNodes,
private MenuItem createCopyAsMenuItem(final List<ExplorerNode> allNodes,
final List<ExplorerNode> readableNodes,
final int priority) {
List<Item> children = createCopyAsChildMenuItems(explorerNodes);
List<Item> children = createCopyAsChildMenuItems(allNodes, readableNodes);

return new IconParentMenuItem.Builder()
.priority(priority)
Expand All @@ -1344,62 +1363,68 @@ private MenuItem createCopyAsMenuItem(final List<ExplorerNode> explorerNodes,
.build();
}

private List<Item> createCopyAsChildMenuItems(final List<ExplorerNode> explorerNodes) {
final List<Item> children = new ArrayList<>();
final int count = explorerNodes.size();
private List<Item> createCopyAsChildMenuItems(final List<ExplorerNode> allNodes,
final List<ExplorerNode> readableNodes) {
// If a user has VIEW on a doc they will also see (but not have VIEW) all ancestor
// docs, so we need to only allow 'copy name' for these 'see but not view' cases.
// Thus, totalCount may be bigger than readableCount
final List<Item> childMenuItems = new ArrayList<>();
final int totalCount = GwtNullSafe.size(allNodes);
final int readableCount = GwtNullSafe.size(readableNodes);
int priority = 1;
if (count == 1) {
children.add(new IconMenuItem.Builder()
if (totalCount == 1) {
childMenuItems.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy Name to Clipboard")
.enabled(true)
.command(() -> copyAs(explorerNodes, ExplorerNode::getName, "\n"))
.command(() -> copyAs(allNodes, ExplorerNode::getName, "\n"))
.build());

children.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy UUID to Clipboard")
.enabled(true)
.command(() -> copyAs(explorerNodes, ExplorerNode::getUuid, "\n"))
.build());
} else if (count > 1) {
children.add(new IconMenuItem.Builder()
if (readableCount > 0) {
childMenuItems.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy UUID to Clipboard")
.enabled(true)
.command(() -> copyAs(allNodes, ExplorerNode::getUuid, "\n"))
.build());
childMenuItems.add(createCopyLinkMenuItem(allNodes.get(0), priority++));
}
} else if (totalCount > 1) {
childMenuItems.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy Names to Clipboard (lines)")
.enabled(true)
.command(() -> copyAs(explorerNodes, ExplorerNode::getName, "\n"))
.command(() -> copyAs(allNodes, ExplorerNode::getName, "\n"))
.build());
children.add(new IconMenuItem.Builder()
childMenuItems.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy Names to Clipboard (comma delimited)")
.enabled(true)
.command(() -> copyAs(explorerNodes, ExplorerNode::getName, ","))
.build());
children.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy UUIDs to Clipboard (lines)")
.enabled(true)
.command(() -> copyAs(explorerNodes, ExplorerNode::getUuid, "\n"))
.build());
children.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy UUIDs to Clipboard (comma delimited)")
.enabled(true)
.command(() -> copyAs(explorerNodes, ExplorerNode::getUuid, ","))
.command(() -> copyAs(allNodes, ExplorerNode::getName, ","))
.build());
}

if (explorerNodes.size() == 1) {
children.add(createCopyLinkMenuItem(explorerNodes.get(0), priority++));
if (readableCount > 0) {
childMenuItems.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy UUIDs to Clipboard (lines)")
.enabled(true)
.command(() -> copyAs(readableNodes, ExplorerNode::getUuid, "\n"))
.build());
childMenuItems.add(new IconMenuItem.Builder()
.priority(priority++)
.icon(SvgImage.COPY)
.text("Copy UUIDs to Clipboard (comma delimited)")
.enabled(true)
.command(() -> copyAs(readableNodes, ExplorerNode::getUuid, ","))
.build());
}
}

return children;
return childMenuItems;
}

private void copyAs(final List<ExplorerNode> nodes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ public static boolean isSystemNode(final ExplorerNode node) {
}
}

public static boolean isSystemNode(final String type,
final String uuid) {
return Objects.equals(SYSTEM_NODE.getType(), type)
&& Objects.equals(SYSTEM_NODE.getUuid(), uuid);
}

/**
* Tests whether a node is the root Favourites node
*/
Expand All @@ -51,6 +57,12 @@ public static boolean isFavouritesNode(final ExplorerNode node) {
}
}

public static boolean isFavouritesNode(final String type,
final String uuid) {
return Objects.equals(SYSTEM_NODE.getType(), type)
&& Objects.equals(SYSTEM_NODE.getUuid(), uuid);
}

/**
* @return True if node is non-null and one of the root nodes
*/
Expand All @@ -59,7 +71,7 @@ public static boolean isRootNode(final ExplorerNode node) {
return false;
} else {
return Objects.equals(SYSTEM_NODE, node)
|| Objects.equals(FAVOURITES_NODE, node);
|| Objects.equals(FAVOURITES_NODE, node);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import stroom.security.api.SecurityContext;
import stroom.security.shared.DocumentPermission;
import stroom.util.NullSafe;
import stroom.util.shared.UserRef;

import jakarta.inject.Inject;
import org.slf4j.Logger;
Expand All @@ -37,6 +38,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -270,7 +272,7 @@ public Optional<ExplorerNode> getNodeWithRoot(final DocRef docRef) {

private synchronized void createRoot() {
final List<ExplorerTreeNode> roots = explorerTreeDao.getRoots();
if (roots == null || roots.size() == 0) {
if (NullSafe.isEmptyCollection(roots)) {
// Insert System root node.
final DocRef root = ExplorerConstants.SYSTEM_DOC_REF;
addNode(root);
Expand Down Expand Up @@ -391,50 +393,45 @@ private void updateNode(final DocRef docRef) {

private void addDocumentPermissions(final DocRef source,
final DocRef dest,
final boolean owner,
final boolean setOwnerPerm,
final boolean cascade) {
final String sourceType = NullSafe.get(source, DocRef::getType);
final String sourceUuid = NullSafe.get(source, DocRef::getUuid);

if (cascade
&& sourceType != null
&& sourceUuid != null
&& DocumentTypes.isFolder(sourceType)) {
final List<ExplorerNode> descendants = getDescendants(dest);
descendants.forEach(descendant -> {
documentPermissionService.addDocumentPermissions(source, descendant.getDocRef());
if (owner) {
documentPermissionService.setPermission(
descendant.getDocRef(),
securityContext.getUserRef(),
DocumentPermission.OWNER);
}
final UserRef userRef = securityContext.getUserRef();
final boolean isFolder = NullSafe.test(source, DocRef::getType, DocumentTypes::isFolder);

final Consumer<DocRef> docRefConsumer = destDocRef -> {
if (setOwnerPerm) {
// We are making them the owner therefore, they won't hold owner, and thus we will get
// a perm exception as they need owner to change the perms. Hence, run as proc user.
securityContext.asProcessingUser(() ->
documentPermissionService.setPermission(destDocRef, userRef, DocumentPermission.OWNER));
}
// User should now be in a position to add other perms
documentPermissionService.addDocumentPermissions(source, destDocRef);
};

if (cascade && isFolder) {
getDescendants(dest).forEach(descendant -> {
final DocRef descendantDocRef = descendant.getDocRef();
docRefConsumer.accept(descendantDocRef);
});
}

documentPermissionService.addDocumentPermissions(source, dest);
if (owner) {
documentPermissionService.setPermission(
dest,
securityContext.getUserRef(),
DocumentPermission.OWNER);
}
docRefConsumer.accept(dest);
}

private void removeAllDocumentPermissions(final DocRef docRef) {
documentPermissionService.removeAllDocumentPermissions(docRef);
}

private ExplorerNode createExplorerNode(final ExplorerTreeNode explorerTreeNode) {
if (Objects.equals(ExplorerConstants.SYSTEM_NODE.getType(), explorerTreeNode.getType())
&& Objects.equals(ExplorerConstants.SYSTEM_NODE.getUuid(), explorerTreeNode.getUuid())) {
if (ExplorerConstants.isSystemNode(explorerTreeNode.getType(), explorerTreeNode.getUuid())) {
return ExplorerConstants.SYSTEM_NODE;
} else if (Objects.equals(ExplorerConstants.FAVOURITES_NODE.getType(), explorerTreeNode.getType())
&& Objects.equals(ExplorerConstants.FAVOURITES_NODE.getUuid(), explorerTreeNode.getUuid())) {
} else if (ExplorerConstants.isFavouritesNode(explorerTreeNode.getType(), explorerTreeNode.getUuid())) {
return ExplorerConstants.FAVOURITES_NODE;
} else {
return explorerTreeNode.buildExplorerNode()
.addNodeFlag(ExplorerFlags.getStandardFlagByDocType(explorerTreeNode.getType()).orElse(null))
.addNodeFlag(ExplorerFlags.getStandardFlagByDocType(explorerTreeNode.getType())
.orElse(null))
.build();
}
}
Expand Down
Loading

0 comments on commit 0917231

Please sign in to comment.