From c62fecbac12fcf9ba130d8793bfdebf53bcdcbbd Mon Sep 17 00:00:00 2001 From: Lenin Alevski Date: Tue, 19 Oct 2021 12:19:06 -0700 Subject: [PATCH] Fixes object name encoding/decoding crashing because of weird characters (#1124) Fixes uploading, listing and managing objects with characters outside of Latin1 range in file name Signed-off-by: Lenin Alevski --- portal-ui/src/common/utils.ts | 8 ++++++ .../Objects/ListObjects/CreateFolderModal.tsx | 7 ++--- .../Objects/ListObjects/ListObjects.tsx | 26 +++++++++++-------- .../Objects/ObjectDetails/AddTagModal.tsx | 3 ++- .../Objects/ObjectDetails/DeleteTagModal.tsx | 3 ++- .../Objects/ObjectDetails/ObjectDetails.tsx | 3 ++- .../ObjectDetails/SetLegalHoldModal.tsx | 3 ++- .../Objects/ObjectDetails/SetRetention.tsx | 5 ++-- .../Objects/ObjectDetails/ShareFile.tsx | 5 ++-- .../Objects/Preview/PreviewFileContent.tsx | 3 ++- .../ObjectBrowser/BrowserBreadcrumbs.tsx | 3 ++- .../src/screens/Console/Users/ListUsers.tsx | 5 +++- portal-ui/src/screens/LoginPage/LoginPage.tsx | 3 ++- 13 files changed, 51 insertions(+), 26 deletions(-) diff --git a/portal-ui/src/common/utils.ts b/portal-ui/src/common/utils.ts index 250afd062..480da889b 100644 --- a/portal-ui/src/common/utils.ts +++ b/portal-ui/src/common/utils.ts @@ -582,3 +582,11 @@ export const representationNumber = (number: number | undefined) => { return `${returnValue}${unit}`; }; + +export const encodeFileName = (name: string) => { + return btoa(unescape(encodeURIComponent(name))); +}; + +export const decodeFileName = (text: string) => { + return decodeURIComponent(escape(window.atob(text))); +}; diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx index 26f4a7158..bb6dc8667 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreateFolderModal.tsx @@ -23,6 +23,7 @@ import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrar import { connect } from "react-redux"; import { setFileModeEnabled } from "../../../../ObjectBrowser/actions"; import history from "../../../../../../history"; +import { decodeFileName, encodeFileName } from "../../../../../../common/utils"; interface ICreateFolder { classes: any; @@ -57,7 +58,7 @@ const CreateFolderModal = ({ const [nameInputError, setNameInputError] = useState(""); const [isFormValid, setIsFormValid] = useState(false); - const currentPath = `${bucketName}/${atob(folderName)}`; + const currentPath = `${bucketName}/${decodeFileName(folderName)}`; const resetForm = () => { setPathUrl(""); @@ -66,12 +67,12 @@ const CreateFolderModal = ({ const createProcess = () => { let folderPath = ""; if (folderName !== "") { - const decodedFolderName = atob(folderName); + const decodedFolderName = decodeFileName(folderName); folderPath = decodedFolderName.endsWith("/") ? decodedFolderName : `${decodedFolderName}/`; } - const newPath = `/buckets/${bucketName}/browse/${btoa( + const newPath = `/buckets/${bucketName}/browse/${encodeFileName( `${folderPath}${pathUrl}` )}/`; history.push(newPath); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx index f35b4d4eb..5ae01cab4 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx @@ -30,7 +30,11 @@ import { } from "./types"; import api from "../../../../../../common/api"; import TableWrapper from "../../../../Common/TableWrapper/TableWrapper"; -import { niceBytes } from "../../../../../../common/utils"; +import { + decodeFileName, + encodeFileName, + niceBytes, +} from "../../../../../../common/utils"; import DeleteObject from "./DeleteObject"; import { @@ -327,7 +331,7 @@ const ListObjects = ({ const rewindParsed = rewindDate.toISOString(); let pathPrefix = ""; if (internalPaths) { - const decodedPath = atob(internalPaths); + const decodedPath = decodeFileName(internalPaths); pathPrefix = decodedPath.endsWith("/") ? decodedPath : decodedPath + "/"; @@ -336,7 +340,7 @@ const ListObjects = ({ .invoke( "GET", `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${ - pathPrefix ? `?prefix=${btoa(pathPrefix)}` : `` + pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : `` }` ) .then((res: RewindObjectList) => { @@ -372,7 +376,7 @@ const ListObjects = ({ if (loading) { let pathPrefix = ""; if (internalPaths) { - const decodedPath = atob(internalPaths); + const decodedPath = decodeFileName(internalPaths); pathPrefix = decodedPath.endsWith("/") ? decodedPath : decodedPath + "/"; @@ -385,7 +389,7 @@ const ListObjects = ({ .invoke( "GET", `/api/v1/buckets/${bucketName}/objects${ - pathPrefix ? `?prefix=${btoa(pathPrefix)}` : `` + pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : `` }` ) .then((res: BucketObjectsList) => { @@ -411,7 +415,7 @@ const ListObjects = ({ let pathPrefix = ""; if (internalPaths) { - const decodedPath = atob(internalPaths); + const decodedPath = decodeFileName(internalPaths); pathPrefix = decodedPath.endsWith("/") ? decodedPath : decodedPath + "/"; @@ -421,7 +425,7 @@ const ListObjects = ({ .invoke( "GET", `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${ - pathPrefix ? `?prefix=${btoa(pathPrefix)}` : `` + pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : `` }` ) .then((res: RewindObjectList) => { @@ -624,7 +628,7 @@ const ListObjects = ({ const openPath = (idElement: string) => { const newPath = `/buckets/${bucketName}/browse${ - idElement ? `/${btoa(idElement)}` : `` + idElement ? `/${encodeFileName(idElement)}` : `` }`; history.push(newPath); return; @@ -633,10 +637,10 @@ const ListObjects = ({ const uploadObject = (e: any): void => { let pathPrefix = ""; if (internalPaths) { - const decodedPath = atob(internalPaths); + const decodedPath = decodeFileName(internalPaths); pathPrefix = decodedPath.endsWith("/") ? decodedPath : decodedPath + "/"; } - upload(e, bucketName, btoa(pathPrefix)); + upload(e, bucketName, encodeFileName(pathPrefix)); }; const openPreview = (fileObject: BucketObject) => { @@ -895,7 +899,7 @@ const ListObjects = ({ const ccPath = internalPaths.split("/").pop(); - const pageTitle = ccPath !== "" ? atob(ccPath) : "/"; + const pageTitle = ccPath !== "" ? decodeFileName(ccPath) : "/"; // console.log("pageTitle", pageTitle); const currentPath = pageTitle.split("/").filter((i: string) => i !== ""); // console.log("currentPath", currentPath); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/AddTagModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/AddTagModal.tsx index fe848ae0d..e69aff279 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/AddTagModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/AddTagModal.tsx @@ -26,6 +26,7 @@ import { ErrorResponseHandler } from "../../../../../../common/types"; import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; import api from "../../../../../../common/api"; +import { decodeFileName } from "../../../../../../common/utils"; interface ITagModal { modalOpen: boolean; @@ -107,7 +108,7 @@ const AddTagModal = ({ >

- Selected Object: {atob(selectedObject)} + Selected Object: {decodeFileName(selectedObject)}

createStyles({ @@ -256,7 +257,7 @@ const ObjectDetails = ({ const [selectedTab, setSelectedTab] = useState(0); const internalPaths = get(match.params, "subpaths", ""); - const internalPathsDecoded = atob(internalPaths) || ""; + const internalPathsDecoded = decodeFileName(internalPaths) || ""; const bucketName = match.params["bucketName"]; const allPathData = internalPathsDecoded.split("/"); const currentItem = allPathData.pop() || ""; diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetLegalHoldModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetLegalHoldModal.tsx index 483ed8023..f23d7fc39 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetLegalHoldModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetLegalHoldModal.tsx @@ -27,6 +27,7 @@ import { ErrorResponseHandler } from "../../../../../../common/types"; import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; import api from "../../../../../../common/api"; +import { encodeFileName } from "../../../../../../common/utils"; const styles = (theme: Theme) => createStyles({ @@ -76,7 +77,7 @@ const SetLegalHoldModal = ({ api .invoke( "PUT", - `/api/v1/buckets/${bucketName}/objects/legalhold?prefix=${btoa( + `/api/v1/buckets/${bucketName}/objects/legalhold?prefix=${encodeFileName( objectName )}&version_id=${versionId}`, { status: legalHoldEnabled ? "enabled" : "disabled" } diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx index f38db842a..6c3e71cf2 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx @@ -29,6 +29,7 @@ import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapp import RadioGroupSelector from "../../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector"; import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector"; import api from "../../../../../../common/api"; +import { encodeFileName } from "../../../../../../common/utils"; const styles = (theme: Theme) => createStyles({ @@ -119,7 +120,7 @@ const SetRetention = ({ api .invoke( "PUT", - `/api/v1/buckets/${bucketName}/objects/retention?prefix=${btoa( + `/api/v1/buckets/${bucketName}/objects/retention?prefix=${encodeFileName( selectedObject )}&version_id=${versionId}`, { @@ -144,7 +145,7 @@ const SetRetention = ({ api .invoke( "DELETE", - `/api/v1/buckets/${bucketName}/objects/retention?prefix=${btoa( + `/api/v1/buckets/${bucketName}/objects/retention?prefix=${encodeFileName( selectedObject )}&version_id=${versionId}` ) diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx index 9cb14168b..889d29799 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx @@ -35,6 +35,7 @@ import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; import PredefinedList from "../../../../Common/FormComponents/PredefinedList/PredefinedList"; import DaysSelector from "../../../../Common/FormComponents/DaysSelector/DaysSelector"; import { LinearProgress } from "@material-ui/core"; +import { encodeFileName } from "../../../../../../common/utils"; const styles = (theme: Theme) => createStyles({ @@ -95,7 +96,7 @@ const ShareFile = ({ api .invoke( "GET", - `/api/v1/buckets/${bucketName}/objects?prefix=${btoa( + `/api/v1/buckets/${bucketName}/objects?prefix=${encodeFileName( dataObject.name )}${distributedSetup ? "&with_versions=true" : ""}` ) @@ -143,7 +144,7 @@ const ShareFile = ({ api .invoke( "GET", - `/api/v1/buckets/${bucketName}/objects/share?prefix=${btoa( + `/api/v1/buckets/${bucketName}/objects/share?prefix=${encodeFileName( dataObject.name )}&version_id=${versionID}${ selectedDate !== "" ? `&expires=${diffDate}ms` : "" diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx index e39463b03..1848b25db 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/Preview/PreviewFileContent.tsx @@ -19,6 +19,7 @@ import { createStyles, withStyles } from "@material-ui/core/styles"; import { Grid, LinearProgress } from "@material-ui/core"; import { BucketObject } from "../ListObjects/types"; import { extensionPreview } from "../utils"; +import { encodeFileName } from "../../../../../../common/utils"; const styles = () => createStyles({ @@ -72,7 +73,7 @@ const PreviewFile = ({ let path = ""; if (object) { - const encodedPath = btoa(object.name); + const encodedPath = encodeFileName(object.name); path = `${window.location.origin}/api/v1/buckets/${bucketName}/objects/download?preview=true&prefix=${encodedPath}`; if (object.version_id) { path = path.concat(`&version_id=${object.version_id}`); diff --git a/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx b/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx index 4f1be03b3..ed3f0fd4c 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx +++ b/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx @@ -24,6 +24,7 @@ import { createStyles, Theme } from "@material-ui/core/styles"; import { ObjectBrowserState } from "./reducers"; import { objectBrowserCommon } from "../Common/FormComponents/common/styleLibrary"; import { Link } from "react-router-dom"; +import { encodeFileName } from "../../../common/utils"; interface ObjectBrowserReducer { objectBrowser: ObjectBrowserState; @@ -60,7 +61,7 @@ const BrowserBreadcrumbs = ({ (objectItem: string, index: number) => { const subSplit = splitPaths.slice(0, index + 1).join("/"); const route = `/buckets/${bucketName}/browse/${ - subSplit ? `${btoa(subSplit)}` : `` + subSplit ? `${encodeFileName(subSplit)}` : `` }`; return ( diff --git a/portal-ui/src/screens/Console/Users/ListUsers.tsx b/portal-ui/src/screens/Console/Users/ListUsers.tsx index 3a750b3ee..b43e80492 100644 --- a/portal-ui/src/screens/Console/Users/ListUsers.tsx +++ b/portal-ui/src/screens/Console/Users/ListUsers.tsx @@ -37,6 +37,7 @@ import TableWrapper from "../Common/TableWrapper/TableWrapper"; import SetPolicy from "../Policies/SetPolicy"; import PageHeader from "../Common/PageHeader/PageHeader"; import SearchIcon from "../../../icons/SearchIcon"; +import { decodeFileName } from "../../../common/utils"; const styles = (theme: Theme) => createStyles({ @@ -164,7 +165,9 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => { setSelectedUser(selectionElement); }; - const userLoggedIn = atob(localStorage.getItem("userLoggedIn") || ""); + const userLoggedIn = decodeFileName( + localStorage.getItem("userLoggedIn") || "" + ); const tableActions = [ { type: "view", onClick: viewAction }, diff --git a/portal-ui/src/screens/LoginPage/LoginPage.tsx b/portal-ui/src/screens/LoginPage/LoginPage.tsx index 01091fa2c..8a45add2d 100644 --- a/portal-ui/src/screens/LoginPage/LoginPage.tsx +++ b/portal-ui/src/screens/LoginPage/LoginPage.tsx @@ -41,6 +41,7 @@ import api from "../../common/api"; import history from "../../history"; import RefreshIcon from "../../icons/RefreshIcon"; import MainError from "../Console/Common/MainError/MainError"; +import { encodeFileName } from "../../common/utils"; const styles = (theme: Theme) => createStyles({ @@ -240,7 +241,7 @@ const Login = ({ // We set the state in redux userLoggedIn(true); if (loginStrategy.loginStrategy === loginStrategyType.form) { - localStorage.setItem("userLoggedIn", btoa(accessKey)); + localStorage.setItem("userLoggedIn", encodeFileName(accessKey)); } history.push("/"); })