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:
@@ -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)));
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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() || "";
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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}`
|
||||
)
|
||||
|
||||
@@ -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` : ""
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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()}`}>
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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("/");
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user