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}`;
};
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 { 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<string>("");
const [isFormValid, setIsFormValid] = useState<boolean>(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);

View File

@@ -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);

View File

@@ -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 = ({
>
<Grid container>
<h3 className={classes.pathLabel}>
Selected Object: {atob(selectedObject)}
Selected Object: {decodeFileName(selectedObject)}
</h3>
<Grid item xs={12}>
<InputBoxWrapper

View File

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

View File

@@ -77,6 +77,7 @@ import EditIcon from "../../../../../../icons/EditIcon";
import SearchIcon from "../../../../../../icons/SearchIcon";
import ObjectBrowserIcon from "../../../../../../icons/ObjectBrowserIcon";
import PreviewFileContent from "../Preview/PreviewFileContent";
import { decodeFileName } from "../../../../../../common/utils";
const styles = (theme: Theme) =>
createStyles({
@@ -256,7 +257,7 @@ const ObjectDetails = ({
const [selectedTab, setSelectedTab] = useState<number>(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() || "";

View File

@@ -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" }

View File

@@ -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}`
)

View File

@@ -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` : ""

View File

@@ -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}`);

View File

@@ -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 (
<React.Fragment key={`breadcrumbs-${index.toString()}`}>

View File

@@ -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 },

View File

@@ -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("/");
})