WIP Access control UI

This commit is contained in:
binwiederhier 2023-01-01 21:56:24 -05:00
parent e650f813c5
commit 7487b0da58
2 changed files with 142 additions and 6 deletions

View File

@ -183,7 +183,7 @@
"account_usage_plan_code_business_plus": "Business Plus", "account_usage_plan_code_business_plus": "Business Plus",
"account_usage_messages_title": "Published messages", "account_usage_messages_title": "Published messages",
"account_usage_emails_title": "Emails sent", "account_usage_emails_title": "Emails sent",
"account_usage_topics_title": "Topics reserved", "account_usage_topics_title": "Reserved topics",
"account_usage_attachment_storage_title": "Attachment storage", "account_usage_attachment_storage_title": "Attachment storage",
"account_usage_attachment_storage_subtitle": "{{filesize}} per file", "account_usage_attachment_storage_subtitle": "{{filesize}} per file",
"account_usage_basis_ip_description": "Usage stats and limits for this account are based on your IP address, so they may be shared with other users.", "account_usage_basis_ip_description": "Usage stats and limits for this account are based on your IP address, so they may be shared with other users.",
@ -239,6 +239,17 @@
"prefs_users_dialog_button_save": "Save", "prefs_users_dialog_button_save": "Save",
"prefs_appearance_title": "Appearance", "prefs_appearance_title": "Appearance",
"prefs_appearance_language_title": "Language", "prefs_appearance_language_title": "Language",
"prefs_access_title": "Reserved topics",
"prefs_access_description": "You may reserve topic names for personal use here, and define access to a topic for other users.",
"prefs_access_add_button": "Add reserved topic",
"prefs_access_edit_button": "Edit topic access",
"prefs_access_delete_button": "Reset topic access",
"prefs_access_table": "Reserved topics table",
"prefs_access_table_topic_header": "Topic",
"prefs_access_table_access_header": "Access",
"prefs_access_table_perms_private": "Only I can publish and subscribe",
"prefs_access_table_perms_public_read": "I can publish, everyone can subscribe",
"prefs_access_table_perms_public": "Everyone can publish and subscribe",
"priority_min": "min", "priority_min": "min",
"priority_low": "low", "priority_low": "low",
"priority_default": "default", "priority_default": "default",

View File

@ -10,7 +10,8 @@ import {
TableBody, TableBody,
TableCell, TableCell,
TableHead, TableHead,
TableRow, Tooltip, TableRow,
Tooltip,
useMediaQuery useMediaQuery
} from "@mui/material"; } from "@mui/material";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -32,22 +33,23 @@ import DialogTitle from "@mui/material/DialogTitle";
import DialogContent from "@mui/material/DialogContent"; import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions"; import DialogActions from "@mui/material/DialogActions";
import userManager from "../app/UserManager"; import userManager from "../app/UserManager";
import {playSound, shuffle, sounds, validTopic, validUrl} from "../app/utils"; import {playSound, shuffle, sounds, validUrl} from "../app/utils";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import session from "../app/Session"; import session from "../app/Session";
import routes from "./routes"; import routes from "./routes";
import accountApi, {UnauthorizedError} from "../app/AccountApi"; import accountApi, {UnauthorizedError} from "../app/AccountApi";
import {Pref, PrefGroup} from "./Pref"; import {Pref, PrefGroup} from "./Pref";
import InfoIcon from '@mui/icons-material/Info'; import {useOutletContext} from "react-router-dom";
import {useNavigate} from "react-router-dom"; import LockIcon from "@mui/icons-material/Lock";
const Preferences = () => { const Preferences = () => {
return ( return (
<Container maxWidth="md" sx={{marginTop: 3, marginBottom: 3}}> <Container maxWidth="md" sx={{marginTop: 3, marginBottom: 3}}>
<Stack spacing={3}> <Stack spacing={3}>
<Notifications/> <Notifications/>
<Appearance/> <Access/>
<Users/> <Users/>
<Appearance/>
</Stack> </Stack>
</Container> </Container>
); );
@ -471,6 +473,129 @@ const Language = () => {
) )
}; };
const Access = () => {
const { t } = useTranslation();
const { account } = useOutletContext();
const [dialogKey, setDialogKey] = useState(0);
const [dialogOpen, setDialogOpen] = useState(false);
const handleAddClick = () => {
setDialogKey(prev => prev+1);
setDialogOpen(true);
};
const handleDialogCancel = () => {
setDialogOpen(false);
};
const handleDialogSubmit = async (entry) => {
setDialogOpen(false);
try {
await accountApi.addAccessEntry();
console.debug(`[Preferences] Added entry ${entry.topic}`);
} catch (e) {
console.log(`[Preferences] Error adding access entry.`, e);
}
};
if (!session.exists() || !account) {
return <></>;
}
return (
<Card sx={{ padding: 1 }} aria-label={t("prefs_access_title")}>
<CardContent sx={{ paddingBottom: 1 }}>
<Typography variant="h5" sx={{marginBottom: 2}}>
{t("prefs_access_title")}
</Typography>
<Paragraph>
{t("prefs_access_description")}
</Paragraph>
{account.access.length > 0 && <AccessTable entries={account.access}/>}
</CardContent>
<CardActions>
<Button onClick={handleAddClick}>{t("prefs_access_add_button")}</Button>
{/*<UserDialog
key={`userEditDialog${dialogKey}`}
open={dialogOpen}
user={dialogUser}
users={props.users}
onCancel={handleDialogCancel}
onSubmit={handleDialogSubmit}
/>*/}
</CardActions>
</Card>
);
};
const AccessTable = (props) => {
const { t } = useTranslation();
const [dialogKey, setDialogKey] = useState(0);
const [dialogOpen, setDialogOpen] = useState(false);
const [dialogUser, setDialogUser] = useState(null);
const handleEditClick = (user) => {
setDialogKey(prev => prev+1);
setDialogUser(user);
setDialogOpen(true);
};
const handleDialogCancel = () => {
setDialogOpen(false);
};
const handleDialogSubmit = async (user) => {
setDialogOpen(false);
// FIXME
};
const handleDeleteClick = async (user) => {
// FIXME
};
return (
<Table size="small" aria-label={t("prefs_access_table")}>
<TableHead>
<TableRow>
<TableCell sx={{paddingLeft: 0}}>{t("prefs_access_table_topic_header")}</TableCell>
<TableCell>{t("prefs_access_table_access_header")}</TableCell>
<TableCell/>
</TableRow>
</TableHead>
<TableBody>
{props.entries.map(entry => (
<TableRow
key={entry.topic}
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row" sx={{paddingLeft: 0}} aria-label={t("prefs_access_table_topic_header")}>{entry.topic}</TableCell>
<TableCell aria-label={t("prefs_access_table_access_header")}>
<LockIcon fontSize="small" sx={{verticalAlign: "bottom", mr: 0.5}}/>
{t("prefs_access_table_perms_private")}
</TableCell>
<TableCell align="right">
<IconButton onClick={() => handleEditClick(entry)} aria-label={t("prefs_access_edit_button")}>
<EditIcon/>
</IconButton>
<IconButton onClick={() => handleDeleteClick(entry)} aria-label={t("prefs_access_delete_button")}>
<CloseIcon/>
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
{/*<UserDialog
key={`userEditDialog${dialogKey}`}
open={dialogOpen}
user={dialogUser}
users={props.users}
onCancel={handleDialogCancel}
onSubmit={handleDialogSubmit}
/>*/}
</Table>
);
};
const maybeUpdateAccountSettings = async (payload) => { const maybeUpdateAccountSettings = async (payload) => {
if (!session.exists()) { if (!session.exists()) {
return; return;