feat(wireguard): harden security constraints and fix db manager UI
This commit is contained in:
parent
b99b623355
commit
e057aee8ba
3 changed files with 34 additions and 20 deletions
|
|
@ -17,6 +17,7 @@ const internalAuditLog = {
|
|||
|
||||
const query = auditLogModel
|
||||
.query()
|
||||
.andWhere("user_id", access.token.getUserId(1))
|
||||
.orderBy("created_on", "DESC")
|
||||
.orderBy("id", "DESC")
|
||||
.limit(100)
|
||||
|
|
@ -49,6 +50,7 @@ const internalAuditLog = {
|
|||
const query = auditLogModel
|
||||
.query()
|
||||
.andWhere("id", data.id)
|
||||
.andWhere("user_id", access.token.getUserId(1))
|
||||
.allowGraph("[user]")
|
||||
.first();
|
||||
|
||||
|
|
|
|||
|
|
@ -213,8 +213,7 @@ const internalWireguard = {
|
|||
.select("wg_client.*", "wg_interface.name as interface_name")
|
||||
.orderBy("wg_client.created_on", "desc");
|
||||
|
||||
// Filter by owner if not admin
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("wg_client.owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
|
||||
|
|
@ -280,6 +279,10 @@ const internalWireguard = {
|
|||
const allocatedIPs = existingClients.map((c) => c.ipv4_address);
|
||||
const ipv4Address = wgHelpers.findNextAvailableIP(iface.ipv4_cidr, allocatedIPs);
|
||||
|
||||
if (!ipv4Address) {
|
||||
throw new Error("No available IP addresses remaining in this WireGuard server subnet.");
|
||||
}
|
||||
|
||||
const clientData = {
|
||||
name: data.name || "Unnamed Client",
|
||||
enabled: true,
|
||||
|
|
@ -309,7 +312,7 @@ const internalWireguard = {
|
|||
*/
|
||||
async deleteClient(knex, clientId, access, accessData) {
|
||||
const query = knex("wg_client").where("id", clientId);
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
const client = await query.first();
|
||||
|
|
@ -328,7 +331,7 @@ const internalWireguard = {
|
|||
*/
|
||||
async toggleClient(knex, clientId, enabled, access, accessData) {
|
||||
const query = knex("wg_client").where("id", clientId);
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
const client = await query.first();
|
||||
|
|
@ -351,7 +354,7 @@ const internalWireguard = {
|
|||
*/
|
||||
async updateClient(knex, clientId, data, access, accessData) {
|
||||
const query = knex("wg_client").where("id", clientId);
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
const client = await query.first();
|
||||
|
|
@ -414,7 +417,17 @@ const internalWireguard = {
|
|||
*/
|
||||
async createInterface(knex, data, access, accessData) {
|
||||
const existingIfaces = await knex("wg_interface").select("name", "listen_port");
|
||||
const newIndex = existingIfaces.length;
|
||||
|
||||
if (existingIfaces.length >= 100) {
|
||||
throw new Error("Maximum limit of 100 WireGuard servers reached.");
|
||||
}
|
||||
|
||||
// Find the lowest available index between 0 and 99
|
||||
const usedPorts = new Set(existingIfaces.map(i => i.listen_port));
|
||||
let newIndex = 0;
|
||||
while (usedPorts.has(51820 + newIndex)) {
|
||||
newIndex++;
|
||||
}
|
||||
|
||||
const name = `wg${newIndex}`;
|
||||
const listen_port = 51820 + newIndex;
|
||||
|
|
@ -470,7 +483,7 @@ const internalWireguard = {
|
|||
*/
|
||||
async updateInterface(knex, id, data, access, accessData) {
|
||||
const query = knex("wg_interface").where("id", id);
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
const iface = await query.first();
|
||||
|
|
@ -493,7 +506,7 @@ const internalWireguard = {
|
|||
*/
|
||||
async deleteInterface(knex, id, access, accessData) {
|
||||
const query = knex("wg_interface").where("id", id);
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
const iface = await query.first();
|
||||
|
|
@ -519,7 +532,7 @@ const internalWireguard = {
|
|||
async updateInterfaceLinks(knex, id, linkedServers, access, accessData) {
|
||||
// Verify ownership
|
||||
const query = knex("wg_interface").where("id", id);
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
const iface = await query.first();
|
||||
|
|
@ -546,8 +559,7 @@ const internalWireguard = {
|
|||
*/
|
||||
async getInterfacesInfo(knex, access, accessData) {
|
||||
const query = knex("wg_interface").select("*");
|
||||
// Filter by owner if not admin
|
||||
if (access && (!accessData || accessData.permission_visibility !== "all")) {
|
||||
if (access) {
|
||||
query.andWhere("owner_user_id", access.token.getUserId(1));
|
||||
}
|
||||
const ifaces = await query;
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ export default function DatabaseManager() {
|
|||
}
|
||||
};
|
||||
|
||||
const renderTableData = (data: any[], schema?: any[]) => {
|
||||
const renderTableData = (data: any[]) => {
|
||||
if (!data || data.length === 0) return <div className="text-muted p-3">No data</div>;
|
||||
|
||||
const columns = schema ? schema.map((s: any) => s.name || s.Field) : Object.keys(data[0]);
|
||||
// In SQLite, raw SQL mapping might mismatch explicit schemas, so strictly read keys from the first row.
|
||||
const columns = Object.keys(data[0]);
|
||||
|
||||
return (
|
||||
<div className="table-responsive">
|
||||
|
|
@ -119,7 +119,7 @@ export default function DatabaseManager() {
|
|||
<Row>
|
||||
<Col md={3}>
|
||||
<Card className="shadow-sm">
|
||||
<Card.Header className="bg-light fw-bold">Tables</Card.Header>
|
||||
<Card.Header className="bg-body-tertiary text-body fw-bold">Tables</Card.Header>
|
||||
<div className="list-group list-group-flush" style={{ maxHeight: "70vh", overflowY: "auto" }}>
|
||||
{tablesLoading && <div className="p-3"><Loading /></div>}
|
||||
{tables?.map((table: string) => (
|
||||
|
|
@ -136,7 +136,7 @@ export default function DatabaseManager() {
|
|||
</Col>
|
||||
<Col md={9}>
|
||||
<Card className="shadow-sm">
|
||||
<Card.Header className="bg-light d-flex justify-content-between align-items-center">
|
||||
<Card.Header className="bg-body-tertiary text-body d-flex justify-content-between align-items-center">
|
||||
<h5 className="mb-0 fw-bold">{activeTable || "Select a table"}</h5>
|
||||
{tableData && <Badge bg="secondary">{tableData.total} rows</Badge>}
|
||||
</Card.Header>
|
||||
|
|
@ -144,7 +144,7 @@ export default function DatabaseManager() {
|
|||
{tableDataLoading ? (
|
||||
<div className="p-5 d-flex justify-content-center"><Loading /></div>
|
||||
) : (
|
||||
tableData && renderTableData(tableData.rows, tableData.schema)
|
||||
tableData && renderTableData(tableData.rows)
|
||||
)}
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
|
@ -154,7 +154,7 @@ export default function DatabaseManager() {
|
|||
|
||||
{activeTab === "query" && (
|
||||
<Card className="shadow-sm">
|
||||
<Card.Header className="bg-light fw-bold">Execute SQL Query</Card.Header>
|
||||
<Card.Header className="bg-body-tertiary text-body fw-bold">Execute SQL Query</Card.Header>
|
||||
<Card.Body>
|
||||
<div className="mb-3 border rounded">
|
||||
<CodeEditor
|
||||
|
|
@ -185,8 +185,8 @@ export default function DatabaseManager() {
|
|||
{queryResult && (
|
||||
<div className="mt-4">
|
||||
<h5 className="mb-3 border-bottom pb-2">Results</h5>
|
||||
{Array.isArray(queryResult) ? renderTableData(queryResult) : (
|
||||
<pre className="p-3 bg-light rounded border">
|
||||
{Array.isArray(queryResult) && queryResult.length > 0 ? renderTableData(queryResult) : (
|
||||
<pre className="p-3 bg-body-tertiary text-body rounded border">
|
||||
{JSON.stringify(queryResult, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue