Logout on Unauthorized. Fix all UI warnings. (#35)

This commit is contained in:
Daniel Valdivia
2020-04-06 16:35:30 -07:00
committed by GitHub
parent 31f0655ff6
commit 2001ab6dae
18 changed files with 215 additions and 232 deletions

File diff suppressed because one or more lines are too long

View File

@@ -24,7 +24,14 @@ export class API {
.set("Authorization", `Bearer ${token}`) .set("Authorization", `Bearer ${token}`)
.send(data) .send(data)
.then(res => res.body) .then(res => res.body)
.catch(err => this.onError(err)); .catch(err => {
// if we get unauthorized, kick out the user
if (err.status === 401) {
storage.removeItem("token");
window.location.href = "/";
}
this.onError(err);
});
} }
onError(err: any) { onError(err: any) {
@@ -38,5 +45,6 @@ export class API {
} }
} }
} }
const api = new API(); const api = new API();
export default api; export default api;

View File

@@ -35,9 +35,8 @@ import { AppState } from "../../../store";
import { setMenuOpen } from "../../../actions"; import { setMenuOpen } from "../../../actions";
import { ThemedComponentProps } from "@material-ui/core/styles/withTheme"; import { ThemedComponentProps } from "@material-ui/core/styles/withTheme";
import NotFoundPage from "../../NotFoundPage"; import NotFoundPage from "../../NotFoundPage";
import BucketList from "./ListBuckets/ListBuckets";
import ViewBucket from "./ViewBucket/ViewBucket";
import ListBuckets from "./ListBuckets/ListBuckets"; import ListBuckets from "./ListBuckets/ListBuckets";
import ViewBucket from "./ViewBucket/ViewBucket";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({

View File

@@ -38,7 +38,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody"; import TableBody from "@material-ui/core/TableBody";
import Checkbox from "@material-ui/core/Checkbox"; import Checkbox from "@material-ui/core/Checkbox";
import Table from "@material-ui/core/Table"; import Table from "@material-ui/core/Table";
import { ArnList, BucketEventList } from "../types"; import { ArnList } from "../types";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
@@ -156,26 +156,6 @@ class AddEvent extends React.Component<IAddEventProps, IAddEventState> {
{ label: "DELETE - Object Deleted", value: "delete" } { label: "DELETE - Object Deleted", value: "delete" }
]; ];
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const targetD = e.target;
const value = targetD.value;
const checked = targetD.checked;
let elements: string[] = [...selectedEvents]; // We clone the selectedGroups array
if (checked) {
// If the user has checked this field we need to push this to selectedGroupsList
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter(element => element !== value);
}
this.setState({ selectedEvents: selectedEvents });
return elements;
};
const handleClick = ( const handleClick = (
event: React.MouseEvent<unknown> | ChangeEvent<unknown>, event: React.MouseEvent<unknown> | ChangeEvent<unknown>,
name: string name: string

View File

@@ -97,7 +97,7 @@ class DeleteEvent extends React.Component<
} }
render() { render() {
const { classes, deleteOpen, selectedBucket } = this.props; const { classes, deleteOpen } = this.props;
const { deleteLoading, deleteError } = this.state; const { deleteLoading, deleteError } = this.state;
return ( return (

View File

@@ -26,8 +26,7 @@ import {
InputLabel, InputLabel,
LinearProgress, LinearProgress,
MenuItem, MenuItem,
Select, Select
TextField
} from "@material-ui/core"; } from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import api from "../../../../common/api"; import api from "../../../../common/api";

View File

@@ -35,7 +35,6 @@ import {
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import DeleteIcon from "@material-ui/icons/Delete"; import DeleteIcon from "@material-ui/icons/Delete";
import SetAccessPolicy from "./SetAccessPolicy"; import SetAccessPolicy from "./SetAccessPolicy";
import DeleteBucket from "../ListBuckets/DeleteBucket";
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions"; import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
import { CreateIcon } from "../../../../icons"; import { CreateIcon } from "../../../../icons";
import AddEvent from "./AddEvent"; import AddEvent from "./AddEvent";
@@ -130,7 +129,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
fetchEvents() { fetchEvents() {
this.setState({ loading: true }, () => { this.setState({ loading: true }, () => {
const { page, rowsPerPage } = this.state; const { page } = this.state;
const { match } = this.props; const { match } = this.props;
const bucketName = match.params["bucketName"]; const bucketName = match.params["bucketName"];
api api
@@ -205,7 +204,6 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
rowsPerPage, rowsPerPage,
deleteOpen, deleteOpen,
addScreenOpen, addScreenOpen,
selectedBucket,
selectedEvent selectedEvent
} = this.state; } = this.state;

View File

@@ -170,7 +170,6 @@ class Console extends React.Component<
ConsoleProps & RouteComponentProps & StyledProps & ThemedComponentProps ConsoleProps & RouteComponentProps & StyledProps & ThemedComponentProps
> { > {
componentDidMount(): void { componentDidMount(): void {
//TODO: verify the session is still valid
api api
.invoke("GET", `/api/v1/session`) .invoke("GET", `/api/v1/session`)
.then(res => { .then(res => {

View File

@@ -137,7 +137,7 @@ const Dashboard = ({ classes }: IDashboardProps) => {
}); });
}; };
const prettyUsage = (usage: string | undefined) => { const prettyUsage = (usage: string | undefined) => {
if (usage == undefined) { if (usage === undefined) {
return "0"; return "0";
} }
return niceBytes(usage); return niceBytes(usage);
@@ -156,7 +156,7 @@ const Dashboard = ({ classes }: IDashboardProps) => {
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + " " + units[l]; return n.toFixed(n < 10 && l > 0 ? 1 : 0) + " " + units[l];
}; };
const prettyNumber = (usage: number | undefined) => { const prettyNumber = (usage: number | undefined) => {
if (usage == undefined) { if (usage === undefined) {
return 0; return 0;
} }
@@ -165,11 +165,12 @@ const Dashboard = ({ classes }: IDashboardProps) => {
return ( return (
<React.Fragment> <React.Fragment>
<Grid container xs={12}> <Grid container>
<Grid container xs={12} spacing={3} className={classes.container}> <Grid container spacing={3} className={classes.container}>
<Grid container xs={12}> <Grid container>
<Typography variant="h2">MinIO Console</Typography> <Typography variant="h2">MinIO Console</Typography>
</Grid> </Grid>
{error !== "" && <Grid container>{error}</Grid>}
<Grid item xs={12} md={4} lg={4}> <Grid item xs={12} md={4} lg={4}>
<Paper className={fixedHeightPaper}> <Paper className={fixedHeightPaper}>
<Grid container direction="row" alignItems="center"> <Grid container direction="row" alignItems="center">

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useState, useEffect } from "react"; import React, { useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { import {
Button, Button,
@@ -31,8 +31,6 @@ import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import api from "../../../common/api"; import api from "../../../common/api";
import UsersSelectors from "./UsersSelectors"; import UsersSelectors from "./UsersSelectors";
import { GroupsList } from "./types";
import { groupsSort } from "../../../utils/sortFunctions";
import Title from "../../../common/Title"; import Title from "../../../common/Title";
interface IGroupProps { interface IGroupProps {
@@ -87,15 +85,69 @@ const AddGroup = ({
useEffect(() => { useEffect(() => {
if (saving) { if (saving) {
const saveRecord = () => {
if (selectedGroup !== null) {
api
.invoke("PUT", `/api/v1/groups/${groupName}`, {
group: groupName,
members: selectedUsers,
status: groupEnabled
})
.then(res => {
isSaving(false);
setError("");
closeModalAndRefresh();
})
.catch(err => {
isSaving(false);
setError(err);
});
} else {
api
.invoke("POST", "/api/v1/groups", {
group: groupName,
members: selectedUsers
})
.then(res => {
isSaving(false);
setError("");
closeModalAndRefresh();
})
.catch(err => {
isSaving(false);
setError(err);
});
}
};
saveRecord(); saveRecord();
} }
}, [saving]); }, [
saving,
groupName,
selectedUsers,
groupEnabled,
selectedGroup,
closeModalAndRefresh
]);
useEffect(() => { useEffect(() => {
if (selectedGroup && loadingGroup) { if (selectedGroup && loadingGroup) {
const fetchGroupInfo = () => {
api
.invoke("GET", `/api/v1/groups/${selectedGroup}`)
.then((res: MainGroupProps) => {
setGroupEnabled(res.status);
setGroupName(res.name);
setSelectedUsers(res.members);
})
.catch(err => {
setError(err);
isLoadingGroup(false);
});
};
fetchGroupInfo(); fetchGroupInfo();
} }
}, [loadingGroup]); }, [loadingGroup, selectedGroup]);
//Fetch Actions //Fetch Actions
const setSaving = (event: React.FormEvent) => { const setSaving = (event: React.FormEvent) => {
@@ -104,55 +156,6 @@ const AddGroup = ({
isSaving(true); isSaving(true);
}; };
const saveRecord = () => {
if (selectedGroup !== null) {
api
.invoke("PUT", `/api/v1/groups/${groupName}`, {
group: groupName,
members: selectedUsers,
status: groupEnabled
})
.then(res => {
isSaving(false);
setError("");
closeModalAndRefresh();
})
.catch(err => {
isSaving(false);
setError(err);
});
} else {
api
.invoke("POST", "/api/v1/groups", {
group: groupName,
members: selectedUsers
})
.then(res => {
isSaving(false);
setError("");
closeModalAndRefresh();
})
.catch(err => {
isSaving(false);
setError(err);
});
}
};
const fetchGroupInfo = () => {
api
.invoke("GET", `/api/v1/groups/${selectedGroup}`)
.then((res: MainGroupProps) => {
setGroupEnabled(res.status);
setGroupName(res.name);
setSelectedUsers(res.members);
})
.catch(err => {
setError(err);
isLoadingGroup(false);
});
};
return ( return (
<Dialog <Dialog
open={open} open={open}

View File

@@ -54,28 +54,27 @@ const DeleteGroup = ({
useEffect(() => { useEffect(() => {
if (isDeleting) { if (isDeleting) {
const removeRecord = () => {
if (!selectedGroup) {
return;
}
api
.invoke("DELETE", `/api/v1/groups/${selectedGroup}`)
.then((res: UsersList) => {
setDeleteLoading(false);
setError("");
closeDeleteModalAndRefresh(true);
})
.catch(err => {
setDeleteLoading(false);
setError(err);
});
};
removeRecord(); removeRecord();
} }
}, [isDeleting]); }, [isDeleting, selectedGroup, closeDeleteModalAndRefresh]);
const removeRecord = () => {
if (!selectedGroup) {
return;
}
api
.invoke("DELETE", `/api/v1/groups/${selectedGroup}`)
.then((res: UsersList) => {
setDeleteLoading(false);
setError("");
closeDeleteModalAndRefresh(true);
})
.catch(err => {
setDeleteLoading(false);
setError(err);
});
};
const closeNoAction = () => { const closeNoAction = () => {
setError(""); setError("");

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useState, useEffect } from "react"; import React, { useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
@@ -34,15 +34,13 @@ import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow"; import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell"; import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody"; import TableBody from "@material-ui/core/TableBody";
import Checkbox from "@material-ui/core/Checkbox";
import ViewIcon from "@material-ui/icons/Visibility"; import ViewIcon from "@material-ui/icons/Visibility";
import DeleteIcon from "@material-ui/icons/Delete"; import DeleteIcon from "@material-ui/icons/Delete";
import { CreateIcon } from "../../../icons"; import { CreateIcon } from "../../../icons";
import api from "../../../common/api"; import api from "../../../common/api";
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions"; import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
import { GroupsList } from "./types"; import { GroupsList } from "./types";
import { groupsSort, usersSort } from "../../../utils/sortFunctions"; import { groupsSort } from "../../../utils/sortFunctions";
import { UsersList } from "../Users/types";
import AddGroup from "../Groups/AddGroup"; import AddGroup from "../Groups/AddGroup";
import DeleteGroup from "./DeleteGroup"; import DeleteGroup from "./DeleteGroup";
@@ -134,31 +132,30 @@ const Groups = ({ classes }: IGroupsProps) => {
useEffect(() => { useEffect(() => {
if (loading) { if (loading) {
const fetchRecords = () => {
const offset = page * rowsPerPage;
api
.invoke("GET", `/api/v1/groups?offset=${offset}&limit=${rowsPerPage}`)
.then((res: GroupsList) => {
setRecords(res.groups.sort(groupsSort));
setTotalRecords(res.total);
setError("");
isLoading(false);
// if we get 0 results, and page > 0 , go down 1 page
if ((!res.groups || res.groups.length === 0) && page > 0) {
const newPage = page - 1;
setPage(newPage);
}
})
.catch(err => {
setError(err);
isLoading(false);
});
};
fetchRecords(); fetchRecords();
} }
}, [loading]); }, [loading, page, rowsPerPage]);
const fetchRecords = () => {
const offset = page * rowsPerPage;
api
.invoke("GET", `/api/v1/groups?offset=${offset}&limit=${rowsPerPage}`)
.then((res: GroupsList) => {
setRecords(res.groups.sort(groupsSort));
setTotalRecords(res.total);
setError("");
isLoading(false);
// if we get 0 results, and page > 0 , go down 1 page
if ((!res.groups || res.groups.length === 0) && page > 0) {
const newPage = page - 1;
setPage(newPage);
}
})
.catch(err => {
setError(err);
isLoading(false);
});
};
const closeAddModalAndRefresh = () => { const closeAddModalAndRefresh = () => {
setGroupOpen(false); setGroupOpen(false);
@@ -200,6 +197,7 @@ const Groups = ({ classes }: IGroupsProps) => {
<Grid item xs={12}> <Grid item xs={12}>
<br /> <br />
</Grid> </Grid>
{error !== "" ? <Grid container>{error}</Grid> : <React.Fragment />}
<Grid item xs={12} className={classes.actionsTray}> <Grid item xs={12} className={classes.actionsTray}>
<TextField <TextField
placeholder="Search Groups" placeholder="Search Groups"

View File

@@ -163,6 +163,7 @@ const UsersSelectors = ({
return ( return (
<React.Fragment> <React.Fragment>
<Title>Members</Title> <Title>Members</Title>
{error !== "" ? <div>{error}</div> : <React.Fragment />}
<Grid item xs={12}> <Grid item xs={12}>
<Paper className={classes.paper}> <Paper className={classes.paper}>
{loading && <LinearProgress />} {loading && <LinearProgress />}

View File

@@ -40,13 +40,11 @@ import { MinTablePaginationActions } from "../../../common/MinTablePaginationAct
import EditIcon from "@material-ui/icons/Edit"; import EditIcon from "@material-ui/icons/Edit";
import Checkbox from "@material-ui/core/Checkbox"; import Checkbox from "@material-ui/core/Checkbox";
import { CreateIcon } from "../../../icons"; import { CreateIcon } from "../../../icons";
import TextField from '@material-ui/core/TextField'; import TextField from "@material-ui/core/TextField";
import InputBase from '@material-ui/core/InputBase'; import SearchIcon from "@material-ui/icons/Search";
import SearchIcon from '@material-ui/icons/Search'; import InputAdornment from "@material-ui/core/InputAdornment";
import InputAdornment from '@material-ui/core/InputAdornment';
import PlayArrowRoundedIcon from "@material-ui/icons/PlayArrowRounded"; import PlayArrowRoundedIcon from "@material-ui/icons/PlayArrowRounded";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
seeMore: { seeMore: {
@@ -84,14 +82,14 @@ const styles = (theme: Theme) =>
actionsTray: { actionsTray: {
textAlign: "right", textAlign: "right",
"& button": { "& button": {
marginLeft: 10, marginLeft: 10
}, }
}, },
searchField: { searchField: {
background: "#FFFFFF", background: "#FFFFFF",
padding: 12, padding: 12,
borderRadius: 5, borderRadius: 5,
boxShadow: "0px 3px 6px #00000012", boxShadow: "0px 3px 6px #00000012"
} }
}); });
@@ -241,7 +239,6 @@ class Permissions extends React.Component<
return ( return (
<React.Fragment> <React.Fragment>
<AddPermission <AddPermission
open={addScreenOpen} open={addScreenOpen}
selectedPermission={selectedPermission} selectedPermission={selectedPermission}
@@ -269,7 +266,7 @@ class Permissions extends React.Component<
<InputAdornment position="start"> <InputAdornment position="start">
<SearchIcon /> <SearchIcon />
</InputAdornment> </InputAdornment>
), )
}} }}
/> />
<Button <Button
@@ -324,7 +321,7 @@ class Permissions extends React.Component<
<Checkbox <Checkbox
value="secondary" value="secondary"
color="primary" color="primary"
inputProps={{ 'aria-label': 'secondary checkbox' }} inputProps={{ "aria-label": "secondary checkbox" }}
/> />
</TableCell> </TableCell>
<TableCell className={classes.wrapCell}> <TableCell className={classes.wrapCell}>

View File

@@ -31,8 +31,8 @@ import Title from "../../../common/Title";
import api from "../../../common/api"; import api from "../../../common/api";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
import "codemirror/theme/material.css"; import "codemirror/theme/material.css";
import PolicyBuilder from "./PolicyBuilder";
import { Policy } from "./types"; import { Policy } from "./types";
require("codemirror/mode/javascript/javascript"); require("codemirror/mode/javascript/javascript");
const styles = (theme: Theme) => const styles = (theme: Theme) =>
@@ -70,6 +70,7 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
policyName: "", policyName: "",
policyDefinition: "" policyDefinition: ""
}; };
addRecord(event: React.FormEvent) { addRecord(event: React.FormEvent) {
event.preventDefault(); event.preventDefault();
const { policyName, addLoading, policyDefinition } = this.state; const { policyName, addLoading, policyDefinition } = this.state;
@@ -101,9 +102,10 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
}); });
}); });
} }
render() { render() {
const { classes, open, policyEdit } = this.props; const { classes, open, policyEdit } = this.props;
const { addLoading, addError, policyName, policyDefinition } = this.state; const { addLoading, addError } = this.state;
return ( return (
<Dialog <Dialog
fullWidth fullWidth

View File

@@ -35,8 +35,7 @@ import Typography from "@material-ui/core/Typography";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import Moment from "react-moment"; import { Policy, PolicyList } from "./types";
import { PolicyList, Policy } from "./types";
import AddPolicy from "./AddPolicy"; import AddPolicy from "./AddPolicy";
import DeletePolicy from "./DeletePolicy"; import DeletePolicy from "./DeletePolicy";
import api from "../../../common/api"; import api from "../../../common/api";

View File

@@ -16,7 +16,6 @@
import React from "react"; import React from "react";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Title from "../../../common/Title";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import { import {
Button, Button,
@@ -87,7 +86,7 @@ class AddUserContent extends React.Component<
saveRecord(event: React.FormEvent) { saveRecord(event: React.FormEvent) {
event.preventDefault(); event.preventDefault();
const { accessKey, addLoading, secretKey, selectedGroups } = this.state; const { accessKey, addLoading, secretKey } = this.state;
const { selectedUser } = this.props; const { selectedUser } = this.props;
if (addLoading) { if (addLoading) {
return; return;
@@ -97,7 +96,7 @@ class AddUserContent extends React.Component<
api api
.invoke("PUT", `/api/v1/users/${selectedUser.accessKey}`, { .invoke("PUT", `/api/v1/users/${selectedUser.accessKey}`, {
accessKey, accessKey,
secretKey: secretKey != "" ? null : secretKey secretKey: secretKey !== "" ? null : secretKey
}) })
.then(res => { .then(res => {
this.setState( this.setState(
@@ -272,6 +271,7 @@ interface IAddUserState {}
class AddUser extends React.Component<IAddUserProps, IAddUserState> { class AddUser extends React.Component<IAddUserProps, IAddUserState> {
state: IAddUserState = {}; state: IAddUserState = {};
render() { render() {
const { open } = this.props; const { open } = this.props;
return ( return (

View File

@@ -31,8 +31,7 @@ import SearchIcon from "@material-ui/icons/Search";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import Checkbox from "@material-ui/core/Checkbox"; import Checkbox from "@material-ui/core/Checkbox";
import api from "../../../common/api"; import api from "../../../common/api";
import { UsersList } from "./types"; import { groupsSort } from "../../../utils/sortFunctions";
import { groupsSort, usersSort } from "../../../utils/sortFunctions";
import { GroupsList } from "../Groups/types"; import { GroupsList } from "../Groups/types";
interface IGroupsProps { interface IGroupsProps {
@@ -168,6 +167,7 @@ const GroupsSelectors = ({
<Grid item xs={12}> <Grid item xs={12}>
<Paper className={classes.paper}> <Paper className={classes.paper}>
{loading && <LinearProgress />} {loading && <LinearProgress />}
{error !== "" && <div>{error}</div>}
{records != null && records.length > 0 ? ( {records != null && records.length > 0 ? (
<React.Fragment> <React.Fragment>
<Grid item xs={12} className={classes.actionsTray}> <Grid item xs={12} className={classes.actionsTray}>