acl frontend started

This commit is contained in:
2024-03-27 13:05:37 +01:00
parent ec7fb0ecc3
commit 603b3676d2
6 changed files with 199 additions and 36 deletions

View File

@@ -12,13 +12,14 @@ import './res/adminlte.min.css';
// views
import View404 from "./views/404";
import LogView from "./views/log-view";
import clsx from "clsx";
const Overview = lazy(() => import('./views/overview'));
const UserListView = lazy(() => import('./views/user/user-list'));
const UserEditView = lazy(() => import('./views/user/user-edit'));
const GroupListView = lazy(() => import('./views/group-list'));
const EditGroupView = lazy(() => import('./views/group-edit'));
const LogView = lazy(() => import("./views/log-view"));
const AccessControlList = lazy(() => import("./views/access-control-list"));
export default function AdminDashboard(props) {
@@ -57,6 +58,11 @@ export default function AdminDashboard(props) {
hideDialog: hideDialog
};
// add fixed-layout to body, I don't want to rewrite my base.twig template
if (!document.body.className.includes("layout-fixed")) {
document.body.className = clsx(document.body.className, "layout-fixed");
}
return <BrowserRouter>
<Header {...controlObj} />
<Sidebar {...controlObj} />
@@ -72,6 +78,7 @@ export default function AdminDashboard(props) {
<Route path={"/admin/groups"} element={<GroupListView {...controlObj} />}/>
<Route path={"/admin/group/:groupId"} element={<EditGroupView {...controlObj} />}/>
<Route path={"/admin/logs"} element={<LogView {...controlObj} />}/>
<Route path={"/admin/acl"} element={<AccessControlList {...controlObj} />}/>
<Route path={"*"} element={<View404 />} />
</Routes>
</Suspense>

View File

@@ -40,6 +40,10 @@ export default function Sidebar(props) {
"name": "admin.settings",
"icon": "tools"
},
"acl": {
"name": "admin.acl",
"icon": "door-open"
},
"logs": {
"name": "admin.logs",
"icon": "file-medical-alt"
@@ -71,7 +75,7 @@ export default function Sidebar(props) {
</a>
</li>);
return (
return <>
<aside className={"main-sidebar sidebar-dark-primary elevation-4"}>
<Link href={"#"} className={"brand-link"} to={"/admin/dashboard"}>
<img src={"/img/icons/logo.png"} alt={"Logo"} className={"brand-image img-circle elevation-3"} style={{opacity: ".8"}} />
@@ -92,7 +96,7 @@ export default function Sidebar(props) {
<div className={"os-content"} style={{padding: "0px 0px", height: "100%", width: "100%"}}>
<div className="user-panel mt-3 pb-3 mb-3 d-flex">
<div className="info">
<span className={"d-block"}>{L("account.logged_in_as")}:&nbsp;
<span className={"d-block text-light"}>{L("account.logged_in_as")}:&nbsp;
<Link to={"/admin/user/" + api.user.id}>{api.user.name}</Link>
</span>
</div>
@@ -107,5 +111,5 @@ export default function Sidebar(props) {
</div>
</div>
</aside>
)
</>
}

View File

@@ -0,0 +1,143 @@
import {useCallback, useContext, useEffect, useState} from "react";
import {LocaleContext} from "shared/locale";
import {Link, useNavigate} from "react-router-dom";
import {Button, Checkbox, TextField, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow} from "@material-ui/core";
import {Add, Refresh} from "@material-ui/icons";
export default function AccessControlList(props) {
// meta
const showDialog = props.showDialog;
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
const navigate = useNavigate();
// data
const [acl, setACL] = useState([]);
const [groups, setGroups] = useState([]);
const [fetchACL, setFetchACL] = useState(true);
// filters
const [query, setQuery] = useState("");
const onFetchACL = useCallback((force = false) => {
if (force || fetchACL) {
setFetchACL(false);
props.api.fetchGroups().then(res => {
if (!res.success) {
props.showDialog(res.msg, "Error fetching groups");
navigate("/admin/dashboard");
} else {
setGroups(res.groups);
props.api.fetchPermissions().then(res => {
if (!res.success) {
props.showDialog(res.msg, "Error fetching permissions");
navigate("/admin/dashboard");
} else {
setACL(res.permissions);
}
});
}
});
}
}, [fetchACL]);
useEffect(() => {
onFetchACL();
}, []);
useEffect(() => {
requestModules(props.api, ["general"], currentLocale).then(data => {
if (!data.success) {
props.showDialog("Error fetching translations: " + data.msg);
}
});
}, [currentLocale]);
const PermissionList = () => {
let rows = [];
for (let index = 0; index < acl.length; index++) {
const permission = acl[index];
if (query) {
if (!permission.method.toLowerCase().includes(query.toLowerCase()) ||
!permission.description.toLowerCase().includes(query.toLowerCase())) {
continue;
}
}
rows.push(
<TableRow key={"perm-" + index}>
<TableCell>
<b>{permission.method}</b><br />
<i>{permission.description}</i>
</TableCell>
{groups.map(group => <TableCell key={"perm-" + index + "-group-" + group.id} align={"center"}>
<Checkbox checked={!permission.groups.length || permission.groups.includes(group.id)} />
</TableCell>)}
</TableRow>
);
}
return <>{rows}</>
}
return <>
<div className={"content-header"}>
<div className={"container-fluid"}>
<div className={"row mb-2"}>
<div className={"col-sm-6"}>
<h1 className={"m-0 text-dark"}>Access Control List</h1>
</div>
<div className={"col-sm-6"}>
<ol className={"breadcrumb float-sm-right"}>
<li className={"breadcrumb-item"}><Link to={"/admin/dashboard"}>Home</Link></li>
<li className="breadcrumb-item active">ACL</li>
</ol>
</div>
</div>
</div>
</div>
<div className={"row"}>
<div className={"col-6"}>
<div className={"form-group"}>
<label>{L("query")}</label>
<TextField
className={"form-control"}
placeholder={L("search_query") + "…"}
value={query}
onChange={e => setQuery(e.target.value)}
variant={"outlined"}
size={"small"} />
</div>
</div>
<div className={"col-6 text-right"}>
<label>{L("general.controls")}</label>
<div className={"form-group"}>
<Button variant={"outlined"} color={"primary"} className={"m-1"} startIcon={<Refresh />} onClick={() => onFetchACL(true)}>
{L("general.reload")}
</Button>
<Button variant={"outlined"} className={"m-1"} startIcon={<Add />} onClick={() => navigate("/admin/acl/new")}>
{L("general.add")}
</Button>
</div>
</div>
</div>
<div>
<TableContainer component={Paper}>
<Table size={"small"}>
<TableHead>
<TableRow>
<TableCell sx={{width: "auto"}}>{L("permission")}</TableCell>
{ groups.map(group => <TableCell key={"group-" + group.id} align={"center"}>{group.name}</TableCell>) }
</TableRow>
</TableHead>
<TableBody>
<PermissionList />
</TableBody>
</Table>
</TableContainer>
</div>
</>
}

View File

@@ -12,13 +12,14 @@ import {format, toDate} from "date-fns";
export default function LogView(props) {
//
const LOG_LEVELS = ['debug', 'info', 'warning', 'error', 'severe'];
// meta
const api = props.api;
const showDialog = props.showDialog;
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
const pagination = usePagination();
// data
const LOG_LEVELS = ['debug', 'info', 'warning', 'error', 'severe'];
const [logEntries, setLogEntries] = useState([]);
// filters
@@ -68,7 +69,7 @@ export default function LogView(props) {
new NumericColumn(L("general.id"), "id"),
new StringColumn(L("module"), "module"),
new StringColumn(L("severity"), "severity"),
new DateTimeColumn(L("timestamp"), "timestamp"),
new DateTimeColumn(L("timestamp"), "timestamp", { precise: true }),
messageColumn,
];