mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-10 16:08:20 -05:00
Allow access tree relative path graph on path filter
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { MongoAbility, MongoQuery } from "@casl/ability";
|
||||
import {
|
||||
faArrowsToDot,
|
||||
faAnglesUp,
|
||||
faArrowsUpDownLeftRight,
|
||||
faArrowUpRightFromSquare,
|
||||
faDownLeftAndUpRightToCenter,
|
||||
@@ -241,7 +241,7 @@ const AccessTreeContent = ({ permissions }: AccessTreeProps) => {
|
||||
</ControlButton>
|
||||
<ControlButton onClick={goToRootNode}>
|
||||
<Tooltip position="right" content="Go to root folder">
|
||||
<FontAwesomeIcon icon={faArrowsToDot} />
|
||||
<FontAwesomeIcon icon={faAnglesUp} />
|
||||
</Tooltip>
|
||||
</ControlButton>
|
||||
</Controls>
|
||||
|
||||
@@ -88,21 +88,18 @@ export const useAccessTree = (
|
||||
if (!environmentsFolders || !permissions || !environmentsFolders[environment]) return;
|
||||
|
||||
const { folders } = environmentsFolders[environment];
|
||||
|
||||
setTotalFolderCount(folders.length);
|
||||
|
||||
const groupedFolders: Record<string, TSecretFolderWithPath[]> = {};
|
||||
const searchPathFolder = folders.find((folder) => folder.path === searchPath);
|
||||
|
||||
const filteredFolders = folders.filter((folder) => {
|
||||
if (folder.path.startsWith(searchPath)) {
|
||||
if (folder.path === searchPath) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
searchPath.startsWith(folder.path) &&
|
||||
(folder.path === "/" ||
|
||||
searchPath === folder.path ||
|
||||
searchPath.indexOf("/", folder.path.length) === folder.path.length)
|
||||
folder.path.startsWith(searchPath) &&
|
||||
(searchPath === "/" || folder.path.charAt(searchPath.length) === "/")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -110,11 +107,17 @@ export const useAccessTree = (
|
||||
return false;
|
||||
});
|
||||
|
||||
const rootFolder = searchPathFolder || filteredFolders.find((f) => f.path === "/");
|
||||
|
||||
const groupedFolders: Record<string, TSecretFolderWithPath[]> = {};
|
||||
|
||||
filteredFolders.forEach((folder) => {
|
||||
const parentId = folder.parentId || "";
|
||||
|
||||
if (!groupedFolders[parentId]) {
|
||||
groupedFolders[parentId] = [];
|
||||
}
|
||||
|
||||
groupedFolders[parentId].push(folder);
|
||||
});
|
||||
|
||||
@@ -129,7 +132,18 @@ export const useAccessTree = (
|
||||
};
|
||||
});
|
||||
|
||||
setLevelFolderMap(newLevelFolderMap);
|
||||
if (rootFolder) {
|
||||
setLevelFolderMap({
|
||||
...newLevelFolderMap,
|
||||
__rootFolderId: {
|
||||
folders: [rootFolder],
|
||||
visibleCount: 1,
|
||||
hasMore: false
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setLevelFolderMap(newLevelFolderMap);
|
||||
}
|
||||
}, [permissions, environmentsFolders, environment, subject, secretName, searchPath]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -151,11 +165,15 @@ export const useAccessTree = (
|
||||
const actionRuleMap = getSubjectActionRuleMap(subject, permissions);
|
||||
|
||||
const visibleFolders: TSecretFolderWithPath[] = [];
|
||||
|
||||
Object.values(levelFolderMap).forEach((levelData) => {
|
||||
visibleFolders.push(...levelData.folders.slice(0, levelData.visibleCount));
|
||||
Object.entries(levelFolderMap).forEach(([key, levelData]) => {
|
||||
if (key !== "__rootFolderId") {
|
||||
visibleFolders.push(...levelData.folders.slice(0, levelData.visibleCount));
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const rootFolder = levelFolderMap.__rootFolderId?.folders[0];
|
||||
|
||||
const folderNodes = visibleFolders.map((folder) =>
|
||||
createFolderNode({
|
||||
folder,
|
||||
@@ -167,10 +185,45 @@ export const useAccessTree = (
|
||||
})
|
||||
);
|
||||
|
||||
const folderEdges = folderNodes.map(({ data: folder }) => {
|
||||
const actions = Object.values(folder.actions);
|
||||
const folderEdges: Edge[] = [];
|
||||
|
||||
if (rootFolder) {
|
||||
const rootFolderNode = folderNodes.find(
|
||||
(node) => node.data.id === rootFolder.id || node.data.path === rootFolder.path
|
||||
);
|
||||
|
||||
if (rootFolderNode) {
|
||||
const rootActions = Object.values(rootFolderNode.data.actions);
|
||||
let rootAccess: PermissionAccess;
|
||||
|
||||
if (Object.values(rootActions).some((action) => action === PermissionAccess.Full)) {
|
||||
rootAccess = PermissionAccess.Full;
|
||||
} else if (
|
||||
Object.values(rootActions).some((action) => action === PermissionAccess.Partial)
|
||||
) {
|
||||
rootAccess = PermissionAccess.Partial;
|
||||
} else {
|
||||
rootAccess = PermissionAccess.None;
|
||||
}
|
||||
|
||||
folderEdges.push(
|
||||
createBaseEdge({
|
||||
source: roleNode.id,
|
||||
target: rootFolderNode.id,
|
||||
access: rootAccess
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
folderNodes.forEach(({ data: folder }) => {
|
||||
if (rootFolder && (folder.id === rootFolder.id || folder.path === rootFolder.path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actions = Object.values(folder.actions);
|
||||
let access: PermissionAccess;
|
||||
|
||||
if (Object.values(actions).some((action) => action === PermissionAccess.Full)) {
|
||||
access = PermissionAccess.Full;
|
||||
} else if (Object.values(actions).some((action) => action === PermissionAccess.Partial)) {
|
||||
@@ -179,16 +232,20 @@ export const useAccessTree = (
|
||||
access = PermissionAccess.None;
|
||||
}
|
||||
|
||||
return createBaseEdge({
|
||||
source: folder.parentId ?? roleNode.id,
|
||||
target: folder.id,
|
||||
access
|
||||
});
|
||||
folderEdges.push(
|
||||
createBaseEdge({
|
||||
source: folder.parentId ?? roleNode.id,
|
||||
target: folder.id,
|
||||
access
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const addMoreButtons: Node[] = [];
|
||||
|
||||
Object.entries(levelFolderMap).forEach(([parentId, levelData]) => {
|
||||
if (parentId === "__rootFolderId") return;
|
||||
|
||||
const key = parentId === "null" ? null : parentId;
|
||||
|
||||
if (key && levelData.hasMore) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { faSearch, faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { faSearch } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
@@ -34,6 +34,12 @@ export const AccessTreeSecretPathInput = ({
|
||||
}, 200);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFocused) {
|
||||
setIsExpanded(false);
|
||||
}
|
||||
}, [isFocused]);
|
||||
|
||||
const focusInput = () => {
|
||||
const inputElement = inputRef.current?.querySelector("input");
|
||||
if (inputElement) {
|
||||
@@ -69,7 +75,7 @@ export const AccessTreeSecretPathInput = ({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimes} />
|
||||
<FontAwesomeIcon icon={faSearch} />
|
||||
</div>
|
||||
) : (
|
||||
<Tooltip position="bottom" content="Search paths">
|
||||
|
||||
@@ -48,7 +48,6 @@ export const SecretPathInput = ({
|
||||
}, [propValue]);
|
||||
|
||||
useEffect(() => {
|
||||
// update secret path if input is valid
|
||||
if (
|
||||
(debouncedInputValue.length > 0 &&
|
||||
debouncedInputValue[debouncedInputValue.length - 1] === "/") ||
|
||||
@@ -59,7 +58,6 @@ export const SecretPathInput = ({
|
||||
}, [debouncedInputValue]);
|
||||
|
||||
useEffect(() => {
|
||||
// filter suggestions based on matching
|
||||
const searchFragment = debouncedInputValue.split("/").pop() || "";
|
||||
const filteredSuggestions = folders
|
||||
.filter((suggestionEntry) =>
|
||||
@@ -78,7 +76,6 @@ export const SecretPathInput = ({
|
||||
const validPaths = inputValue.split("/");
|
||||
validPaths.pop();
|
||||
|
||||
// removed trailing slash
|
||||
const newValue = `${validPaths.join("/")}/${suggestions[selectedIndex]}`;
|
||||
onChange?.(newValue);
|
||||
setInputValue(newValue);
|
||||
@@ -102,7 +99,6 @@ export const SecretPathInput = ({
|
||||
};
|
||||
|
||||
const handleInputChange = (e: any) => {
|
||||
// propagate event to react-hook-form onChange
|
||||
if (onChange) {
|
||||
onChange(e.target.value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user