mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-27 07:21:58 -06:00
Added ACLs for User Management Preventing Users from Making Changes, Only Admins
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Paper, Box, Typography } from "@mui/material";
|
import { Paper, Box, Typography } from "@mui/material";
|
||||||
|
|
||||||
export default function ServerInfo() {
|
export default function ServerInfo({ isAdmin = false }) {
|
||||||
const [serverTime, setServerTime] = useState(null);
|
const [serverTime, setServerTime] = useState(null);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isAdmin) return;
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
const fetchTime = async () => {
|
const fetchTime = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -23,7 +24,9 @@ export default function ServerInfo() {
|
|||||||
fetchTime();
|
fetchTime();
|
||||||
const id = setInterval(fetchTime, 60000); // update once per minute
|
const id = setInterval(fetchTime, 60000); // update once per minute
|
||||||
return () => { isMounted = false; clearInterval(id); };
|
return () => { isMounted = false; clearInterval(id); };
|
||||||
}, []);
|
}, [isAdmin]);
|
||||||
|
|
||||||
|
if (!isAdmin) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper sx={{ m: 2, p: 0, bgcolor: "#1e1e1e" }} elevation={2}>
|
<Paper sx={{ m: 2, p: 0, bgcolor: "#1e1e1e" }} elevation={2}>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ async function sha512(text) {
|
|||||||
return arr.map((b) => b.toString(16).padStart(2, "0")).join("");
|
return arr.map((b) => b.toString(16).padStart(2, "0")).join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function UserManagement() {
|
export default function UserManagement({ isAdmin = false }) {
|
||||||
const [rows, setRows] = useState([]); // {username, display_name, role, last_login}
|
const [rows, setRows] = useState([]); // {username, display_name, role, last_login}
|
||||||
const [orderBy, setOrderBy] = useState("username");
|
const [orderBy, setOrderBy] = useState("username");
|
||||||
const [order, setOrder] = useState("asc");
|
const [order, setOrder] = useState("asc");
|
||||||
@@ -113,7 +113,7 @@ export default function UserManagement() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchUsers();
|
if (!isAdmin) return;
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch("/api/auth/me", { credentials: "include" });
|
const resp = await fetch("/api/auth/me", { credentials: "include" });
|
||||||
@@ -123,7 +123,8 @@ export default function UserManagement() {
|
|||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
})();
|
})();
|
||||||
}, [fetchUsers]);
|
fetchUsers();
|
||||||
|
}, [fetchUsers, isAdmin]);
|
||||||
|
|
||||||
const handleSort = (col) => {
|
const handleSort = (col) => {
|
||||||
if (orderBy === col) setOrder(order === "asc" ? "desc" : "asc");
|
if (orderBy === col) setOrder(order === "asc" ? "desc" : "asc");
|
||||||
@@ -291,6 +292,8 @@ export default function UserManagement() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!isAdmin) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Paper sx={tablePaperSx} elevation={2}>
|
<Paper sx={tablePaperSx} elevation={2}>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import React, { useState, useEffect, useCallback, useRef } from "react";
|
|||||||
import { ReactFlowProvider } from "reactflow";
|
import { ReactFlowProvider } from "reactflow";
|
||||||
import "reactflow/dist/style.css";
|
import "reactflow/dist/style.css";
|
||||||
import {
|
import {
|
||||||
CloseAllDialog, CreditsDialog, RenameTabDialog, TabContextMenu
|
CloseAllDialog, CreditsDialog, RenameTabDialog, TabContextMenu, NotAuthorizedDialog
|
||||||
} from "./Dialogs";
|
} from "./Dialogs";
|
||||||
import NavigationSidebar from "./Navigation_Sidebar";
|
import NavigationSidebar from "./Navigation_Sidebar";
|
||||||
|
|
||||||
@@ -104,6 +104,7 @@ export default function App() {
|
|||||||
const [userRole, setUserRole] = useState(null);
|
const [userRole, setUserRole] = useState(null);
|
||||||
const [editingJob, setEditingJob] = useState(null);
|
const [editingJob, setEditingJob] = useState(null);
|
||||||
const [jobsRefreshToken, setJobsRefreshToken] = useState(0);
|
const [jobsRefreshToken, setJobsRefreshToken] = useState(0);
|
||||||
|
const [notAuthorizedOpen, setNotAuthorizedOpen] = useState(false);
|
||||||
|
|
||||||
// Build breadcrumb items for current view
|
// Build breadcrumb items for current view
|
||||||
const breadcrumbs = React.useMemo(() => {
|
const breadcrumbs = React.useMemo(() => {
|
||||||
@@ -383,6 +384,15 @@ export default function App() {
|
|||||||
[tabs, activeTabId]
|
[tabs, activeTabId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isAdmin = (String(userRole || '').toLowerCase() === 'admin');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isAdmin && (currentPage === 'admin_users' || currentPage === 'server_info')) {
|
||||||
|
setNotAuthorizedOpen(true);
|
||||||
|
setCurrentPage('devices');
|
||||||
|
}
|
||||||
|
}, [currentPage, isAdmin]);
|
||||||
|
|
||||||
const renderMainContent = () => {
|
const renderMainContent = () => {
|
||||||
switch (currentPage) {
|
switch (currentPage) {
|
||||||
case "sites":
|
case "sites":
|
||||||
@@ -496,10 +506,10 @@ export default function App() {
|
|||||||
return <ScriptEditor />;
|
return <ScriptEditor />;
|
||||||
|
|
||||||
case "admin_users":
|
case "admin_users":
|
||||||
return <UserManagement />;
|
return <UserManagement isAdmin={isAdmin} />;
|
||||||
|
|
||||||
case "server_info":
|
case "server_info":
|
||||||
return <ServerInfo />;
|
return <ServerInfo isAdmin={isAdmin} />;
|
||||||
|
|
||||||
case "workflow-editor":
|
case "workflow-editor":
|
||||||
return (
|
return (
|
||||||
@@ -647,7 +657,7 @@ export default function App() {
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<Box sx={{ display: "flex", flexGrow: 1, overflow: "hidden" }}>
|
<Box sx={{ display: "flex", flexGrow: 1, overflow: "hidden" }}>
|
||||||
<NavigationSidebar currentPage={currentPage} onNavigate={setCurrentPage} isAdmin={(String(userRole||'').toLowerCase()==='admin')} />
|
<NavigationSidebar currentPage={currentPage} onNavigate={setCurrentPage} isAdmin={isAdmin} />
|
||||||
<Box sx={{ flexGrow: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
|
<Box sx={{ flexGrow: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
|
||||||
{renderMainContent()}
|
{renderMainContent()}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -668,6 +678,7 @@ export default function App() {
|
|||||||
onRename={handleRenameTab}
|
onRename={handleRenameTab}
|
||||||
onCloseTab={handleCloseTab}
|
onCloseTab={handleCloseTab}
|
||||||
/>
|
/>
|
||||||
|
<NotAuthorizedDialog open={notAuthorizedOpen} onClose={() => setNotAuthorizedOpen(false)} />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,26 @@ export function CloseAllDialog({ open, onClose, onConfirm }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NotAuthorizedDialog({ open, onClose }) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}
|
||||||
|
>
|
||||||
|
<DialogTitle>Not Authorized</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText sx={{ color: "#ccc" }}>
|
||||||
|
You are not authorized to access this section.
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onClose} sx={{ color: "#58a6ff" }}>OK</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function CreditsDialog({ open, onClose }) {
|
export function CreditsDialog({ open, onClose }) {
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={onClose} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}>
|
<Dialog open={open} onClose={onClose} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user