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 <alevsk.8772@gmail.com>
This commit is contained in:
Lenin Alevski
2021-10-19 12:19:06 -07:00
committed by GitHub
parent 3d858ac04a
commit c62fecbac1
13 changed files with 51 additions and 26 deletions

View File

@@ -582,3 +582,11 @@ export const representationNumber = (number: number | undefined) => {
return `${returnValue}${unit}`; return `${returnValue}${unit}`;
}; };
export const encodeFileName = (name: string) => {
return btoa(unescape(encodeURIComponent(name)));
};
export const decodeFileName = (text: string) => {
return decodeURIComponent(escape(window.atob(text)));
};

View File

@@ -23,6 +23,7 @@ import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrar
import { connect } from "react-redux"; import { connect } from "react-redux";
import { setFileModeEnabled } from "../../../../ObjectBrowser/actions"; import { setFileModeEnabled } from "../../../../ObjectBrowser/actions";
import history from "../../../../../../history"; import history from "../../../../../../history";
import { decodeFileName, encodeFileName } from "../../../../../../common/utils";
interface ICreateFolder { interface ICreateFolder {
classes: any; classes: any;
@@ -57,7 +58,7 @@ const CreateFolderModal = ({
const [nameInputError, setNameInputError] = useState<string>(""); const [nameInputError, setNameInputError] = useState<string>("");
const [isFormValid, setIsFormValid] = useState<boolean>(false); const [isFormValid, setIsFormValid] = useState<boolean>(false);
const currentPath = `${bucketName}/${atob(folderName)}`; const currentPath = `${bucketName}/${decodeFileName(folderName)}`;
const resetForm = () => { const resetForm = () => {
setPathUrl(""); setPathUrl("");
@@ -66,12 +67,12 @@ const CreateFolderModal = ({
const createProcess = () => { const createProcess = () => {
let folderPath = ""; let folderPath = "";
if (folderName !== "") { if (folderName !== "") {
const decodedFolderName = atob(folderName); const decodedFolderName = decodeFileName(folderName);
folderPath = decodedFolderName.endsWith("/") folderPath = decodedFolderName.endsWith("/")
? decodedFolderName ? decodedFolderName
: `${decodedFolderName}/`; : `${decodedFolderName}/`;
} }
const newPath = `/buckets/${bucketName}/browse/${btoa( const newPath = `/buckets/${bucketName}/browse/${encodeFileName(
`${folderPath}${pathUrl}` `${folderPath}${pathUrl}`
)}/`; )}/`;
history.push(newPath); history.push(newPath);

View File

@@ -30,7 +30,11 @@ import {
} from "./types"; } from "./types";
import api from "../../../../../../common/api"; import api from "../../../../../../common/api";
import TableWrapper from "../../../../Common/TableWrapper/TableWrapper"; import TableWrapper from "../../../../Common/TableWrapper/TableWrapper";
import { niceBytes } from "../../../../../../common/utils"; import {
decodeFileName,
encodeFileName,
niceBytes,
} from "../../../../../../common/utils";
import DeleteObject from "./DeleteObject"; import DeleteObject from "./DeleteObject";
import { import {
@@ -327,7 +331,7 @@ const ListObjects = ({
const rewindParsed = rewindDate.toISOString(); const rewindParsed = rewindDate.toISOString();
let pathPrefix = ""; let pathPrefix = "";
if (internalPaths) { if (internalPaths) {
const decodedPath = atob(internalPaths); const decodedPath = decodeFileName(internalPaths);
pathPrefix = decodedPath.endsWith("/") pathPrefix = decodedPath.endsWith("/")
? decodedPath ? decodedPath
: decodedPath + "/"; : decodedPath + "/";
@@ -336,7 +340,7 @@ const ListObjects = ({
.invoke( .invoke(
"GET", "GET",
`/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${ `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${
pathPrefix ? `?prefix=${btoa(pathPrefix)}` : `` pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : ``
}` }`
) )
.then((res: RewindObjectList) => { .then((res: RewindObjectList) => {
@@ -372,7 +376,7 @@ const ListObjects = ({
if (loading) { if (loading) {
let pathPrefix = ""; let pathPrefix = "";
if (internalPaths) { if (internalPaths) {
const decodedPath = atob(internalPaths); const decodedPath = decodeFileName(internalPaths);
pathPrefix = decodedPath.endsWith("/") pathPrefix = decodedPath.endsWith("/")
? decodedPath ? decodedPath
: decodedPath + "/"; : decodedPath + "/";
@@ -385,7 +389,7 @@ const ListObjects = ({
.invoke( .invoke(
"GET", "GET",
`/api/v1/buckets/${bucketName}/objects${ `/api/v1/buckets/${bucketName}/objects${
pathPrefix ? `?prefix=${btoa(pathPrefix)}` : `` pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : ``
}` }`
) )
.then((res: BucketObjectsList) => { .then((res: BucketObjectsList) => {
@@ -411,7 +415,7 @@ const ListObjects = ({
let pathPrefix = ""; let pathPrefix = "";
if (internalPaths) { if (internalPaths) {
const decodedPath = atob(internalPaths); const decodedPath = decodeFileName(internalPaths);
pathPrefix = decodedPath.endsWith("/") pathPrefix = decodedPath.endsWith("/")
? decodedPath ? decodedPath
: decodedPath + "/"; : decodedPath + "/";
@@ -421,7 +425,7 @@ const ListObjects = ({
.invoke( .invoke(
"GET", "GET",
`/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${ `/api/v1/buckets/${bucketName}/rewind/${rewindParsed}${
pathPrefix ? `?prefix=${btoa(pathPrefix)}` : `` pathPrefix ? `?prefix=${encodeFileName(pathPrefix)}` : ``
}` }`
) )
.then((res: RewindObjectList) => { .then((res: RewindObjectList) => {
@@ -624,7 +628,7 @@ const ListObjects = ({
const openPath = (idElement: string) => { const openPath = (idElement: string) => {
const newPath = `/buckets/${bucketName}/browse${ const newPath = `/buckets/${bucketName}/browse${
idElement ? `/${btoa(idElement)}` : `` idElement ? `/${encodeFileName(idElement)}` : ``
}`; }`;
history.push(newPath); history.push(newPath);
return; return;
@@ -633,10 +637,10 @@ const ListObjects = ({
const uploadObject = (e: any): void => { const uploadObject = (e: any): void => {
let pathPrefix = ""; let pathPrefix = "";
if (internalPaths) { if (internalPaths) {
const decodedPath = atob(internalPaths); const decodedPath = decodeFileName(internalPaths);
pathPrefix = decodedPath.endsWith("/") ? decodedPath : decodedPath + "/"; pathPrefix = decodedPath.endsWith("/") ? decodedPath : decodedPath + "/";
} }
upload(e, bucketName, btoa(pathPrefix)); upload(e, bucketName, encodeFileName(pathPrefix));
}; };
const openPreview = (fileObject: BucketObject) => { const openPreview = (fileObject: BucketObject) => {
@@ -895,7 +899,7 @@ const ListObjects = ({
const ccPath = internalPaths.split("/").pop(); const ccPath = internalPaths.split("/").pop();
const pageTitle = ccPath !== "" ? atob(ccPath) : "/"; const pageTitle = ccPath !== "" ? decodeFileName(ccPath) : "/";
// console.log("pageTitle", pageTitle); // console.log("pageTitle", pageTitle);
const currentPath = pageTitle.split("/").filter((i: string) => i !== ""); const currentPath = pageTitle.split("/").filter((i: string) => i !== "");
// console.log("currentPath", currentPath); // console.log("currentPath", currentPath);

View File

@@ -26,6 +26,7 @@ import { ErrorResponseHandler } from "../../../../../../common/types";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import api from "../../../../../../common/api"; import api from "../../../../../../common/api";
import { decodeFileName } from "../../../../../../common/utils";
interface ITagModal { interface ITagModal {
modalOpen: boolean; modalOpen: boolean;
@@ -107,7 +108,7 @@ const AddTagModal = ({
> >
<Grid container> <Grid container>
<h3 className={classes.pathLabel}> <h3 className={classes.pathLabel}>
Selected Object: {atob(selectedObject)} Selected Object: {decodeFileName(selectedObject)}
</h3> </h3>
<Grid item xs={12}> <Grid item xs={12}>
<InputBoxWrapper <InputBoxWrapper

View File

@@ -32,6 +32,7 @@ import { setErrorSnackMessage } from "../../../../../../actions";
import { AppState } from "../../../../../../store"; import { AppState } from "../../../../../../store";
import { ErrorResponseHandler } from "../../../../../../common/types"; import { ErrorResponseHandler } from "../../../../../../common/types";
import api from "../../../../../../common/api"; import api from "../../../../../../common/api";
import { encodeFileName } from "../../../../../../common/utils";
interface IDeleteTagModal { interface IDeleteTagModal {
deleteOpen: boolean; deleteOpen: boolean;
@@ -83,7 +84,7 @@ const DeleteTagModal = ({
api api
.invoke( .invoke(
"PUT", "PUT",
`/api/v1/buckets/${bucketName}/objects/tags?prefix=${btoa( `/api/v1/buckets/${bucketName}/objects/tags?prefix=${encodeFileName(
selectedObject selectedObject
)}&version_id=${verID}`, )}&version_id=${verID}`,
{ tags: cleanObject } { tags: cleanObject }

View File

@@ -77,6 +77,7 @@ import EditIcon from "../../../../../../icons/EditIcon";
import SearchIcon from "../../../../../../icons/SearchIcon"; import SearchIcon from "../../../../../../icons/SearchIcon";
import ObjectBrowserIcon from "../../../../../../icons/ObjectBrowserIcon"; import ObjectBrowserIcon from "../../../../../../icons/ObjectBrowserIcon";
import PreviewFileContent from "../Preview/PreviewFileContent"; import PreviewFileContent from "../Preview/PreviewFileContent";
import { decodeFileName } from "../../../../../../common/utils";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -256,7 +257,7 @@ const ObjectDetails = ({
const [selectedTab, setSelectedTab] = useState<number>(0); const [selectedTab, setSelectedTab] = useState<number>(0);
const internalPaths = get(match.params, "subpaths", ""); const internalPaths = get(match.params, "subpaths", "");
const internalPathsDecoded = atob(internalPaths) || ""; const internalPathsDecoded = decodeFileName(internalPaths) || "";
const bucketName = match.params["bucketName"]; const bucketName = match.params["bucketName"];
const allPathData = internalPathsDecoded.split("/"); const allPathData = internalPathsDecoded.split("/");
const currentItem = allPathData.pop() || ""; const currentItem = allPathData.pop() || "";

View File

@@ -27,6 +27,7 @@ import { ErrorResponseHandler } from "../../../../../../common/types";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import api from "../../../../../../common/api"; import api from "../../../../../../common/api";
import { encodeFileName } from "../../../../../../common/utils";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -76,7 +77,7 @@ const SetLegalHoldModal = ({
api api
.invoke( .invoke(
"PUT", "PUT",
`/api/v1/buckets/${bucketName}/objects/legalhold?prefix=${btoa( `/api/v1/buckets/${bucketName}/objects/legalhold?prefix=${encodeFileName(
objectName objectName
)}&version_id=${versionId}`, )}&version_id=${versionId}`,
{ status: legalHoldEnabled ? "enabled" : "disabled" } { status: legalHoldEnabled ? "enabled" : "disabled" }

View File

@@ -29,6 +29,7 @@ import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapp
import RadioGroupSelector from "../../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector"; import RadioGroupSelector from "../../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector"; import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector";
import api from "../../../../../../common/api"; import api from "../../../../../../common/api";
import { encodeFileName } from "../../../../../../common/utils";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -119,7 +120,7 @@ const SetRetention = ({
api api
.invoke( .invoke(
"PUT", "PUT",
`/api/v1/buckets/${bucketName}/objects/retention?prefix=${btoa( `/api/v1/buckets/${bucketName}/objects/retention?prefix=${encodeFileName(
selectedObject selectedObject
)}&version_id=${versionId}`, )}&version_id=${versionId}`,
{ {
@@ -144,7 +145,7 @@ const SetRetention = ({
api api
.invoke( .invoke(
"DELETE", "DELETE",
`/api/v1/buckets/${bucketName}/objects/retention?prefix=${btoa( `/api/v1/buckets/${bucketName}/objects/retention?prefix=${encodeFileName(
selectedObject selectedObject
)}&version_id=${versionId}` )}&version_id=${versionId}`
) )

View File

@@ -35,6 +35,7 @@ import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import PredefinedList from "../../../../Common/FormComponents/PredefinedList/PredefinedList"; import PredefinedList from "../../../../Common/FormComponents/PredefinedList/PredefinedList";
import DaysSelector from "../../../../Common/FormComponents/DaysSelector/DaysSelector"; import DaysSelector from "../../../../Common/FormComponents/DaysSelector/DaysSelector";
import { LinearProgress } from "@material-ui/core"; import { LinearProgress } from "@material-ui/core";
import { encodeFileName } from "../../../../../../common/utils";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -95,7 +96,7 @@ const ShareFile = ({
api api
.invoke( .invoke(
"GET", "GET",
`/api/v1/buckets/${bucketName}/objects?prefix=${btoa( `/api/v1/buckets/${bucketName}/objects?prefix=${encodeFileName(
dataObject.name dataObject.name
)}${distributedSetup ? "&with_versions=true" : ""}` )}${distributedSetup ? "&with_versions=true" : ""}`
) )
@@ -143,7 +144,7 @@ const ShareFile = ({
api api
.invoke( .invoke(
"GET", "GET",
`/api/v1/buckets/${bucketName}/objects/share?prefix=${btoa( `/api/v1/buckets/${bucketName}/objects/share?prefix=${encodeFileName(
dataObject.name dataObject.name
)}&version_id=${versionID}${ )}&version_id=${versionID}${
selectedDate !== "" ? `&expires=${diffDate}ms` : "" selectedDate !== "" ? `&expires=${diffDate}ms` : ""

View File

@@ -19,6 +19,7 @@ import { createStyles, withStyles } from "@material-ui/core/styles";
import { Grid, LinearProgress } from "@material-ui/core"; import { Grid, LinearProgress } from "@material-ui/core";
import { BucketObject } from "../ListObjects/types"; import { BucketObject } from "../ListObjects/types";
import { extensionPreview } from "../utils"; import { extensionPreview } from "../utils";
import { encodeFileName } from "../../../../../../common/utils";
const styles = () => const styles = () =>
createStyles({ createStyles({
@@ -72,7 +73,7 @@ const PreviewFile = ({
let path = ""; let path = "";
if (object) { 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}`; path = `${window.location.origin}/api/v1/buckets/${bucketName}/objects/download?preview=true&prefix=${encodedPath}`;
if (object.version_id) { if (object.version_id) {
path = path.concat(`&version_id=${object.version_id}`); path = path.concat(`&version_id=${object.version_id}`);

View File

@@ -24,6 +24,7 @@ import { createStyles, Theme } from "@material-ui/core/styles";
import { ObjectBrowserState } from "./reducers"; import { ObjectBrowserState } from "./reducers";
import { objectBrowserCommon } from "../Common/FormComponents/common/styleLibrary"; import { objectBrowserCommon } from "../Common/FormComponents/common/styleLibrary";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { encodeFileName } from "../../../common/utils";
interface ObjectBrowserReducer { interface ObjectBrowserReducer {
objectBrowser: ObjectBrowserState; objectBrowser: ObjectBrowserState;
@@ -60,7 +61,7 @@ const BrowserBreadcrumbs = ({
(objectItem: string, index: number) => { (objectItem: string, index: number) => {
const subSplit = splitPaths.slice(0, index + 1).join("/"); const subSplit = splitPaths.slice(0, index + 1).join("/");
const route = `/buckets/${bucketName}/browse/${ const route = `/buckets/${bucketName}/browse/${
subSplit ? `${btoa(subSplit)}` : `` subSplit ? `${encodeFileName(subSplit)}` : ``
}`; }`;
return ( return (
<React.Fragment key={`breadcrumbs-${index.toString()}`}> <React.Fragment key={`breadcrumbs-${index.toString()}`}>

View File

@@ -37,6 +37,7 @@ import TableWrapper from "../Common/TableWrapper/TableWrapper";
import SetPolicy from "../Policies/SetPolicy"; import SetPolicy from "../Policies/SetPolicy";
import PageHeader from "../Common/PageHeader/PageHeader"; import PageHeader from "../Common/PageHeader/PageHeader";
import SearchIcon from "../../../icons/SearchIcon"; import SearchIcon from "../../../icons/SearchIcon";
import { decodeFileName } from "../../../common/utils";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -164,7 +165,9 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
setSelectedUser(selectionElement); setSelectedUser(selectionElement);
}; };
const userLoggedIn = atob(localStorage.getItem("userLoggedIn") || ""); const userLoggedIn = decodeFileName(
localStorage.getItem("userLoggedIn") || ""
);
const tableActions = [ const tableActions = [
{ type: "view", onClick: viewAction }, { type: "view", onClick: viewAction },

View File

@@ -41,6 +41,7 @@ import api from "../../common/api";
import history from "../../history"; import history from "../../history";
import RefreshIcon from "../../icons/RefreshIcon"; import RefreshIcon from "../../icons/RefreshIcon";
import MainError from "../Console/Common/MainError/MainError"; import MainError from "../Console/Common/MainError/MainError";
import { encodeFileName } from "../../common/utils";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -240,7 +241,7 @@ const Login = ({
// We set the state in redux // We set the state in redux
userLoggedIn(true); userLoggedIn(true);
if (loginStrategy.loginStrategy === loginStrategyType.form) { if (loginStrategy.loginStrategy === loginStrategyType.form) {
localStorage.setItem("userLoggedIn", btoa(accessKey)); localStorage.setItem("userLoggedIn", encodeFileName(accessKey));
} }
history.push("/"); history.push("/");
}) })