diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 0000000..39e61fd --- /dev/null +++ b/.gitconfig @@ -0,0 +1,2 @@ +[core] + hooksPath = .githooks/ \ No newline at end of file diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..672b044 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +shopt -s globstar +"$(git rev-parse --show-toplevel)"/api/node_modules/.bin/prettier --ignore-unknown --write $(git rev-parse --show-toplevel)/api/src/**/*.ts +rustfmt $(git rev-parse --show-toplevel)/gui/src/**/*.rs +git update-index --again +exit 0 \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..57562cf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,16 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "jsxSingleQuote": false, + "printWidth": 80, + "proseWrap": "always", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false +} \ No newline at end of file diff --git a/api/package-lock.json b/api/package-lock.json index f20bcaa..8c8959d 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -21,6 +21,7 @@ "@types/ssh2": "^1.15.0", "@types/ws": "^8.5.10", "nodemon": "^3.0.3", + "prettier": "^3.2.5", "prisma": "^5.13.0", "typescript": "^5.3.3" } @@ -1288,6 +1289,21 @@ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prisma": { "version": "5.13.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz", diff --git a/api/package.json b/api/package.json index 395e32e..67bf1d4 100644 --- a/api/package.json +++ b/api/package.json @@ -19,6 +19,7 @@ "@types/ssh2": "^1.15.0", "@types/ws": "^8.5.10", "nodemon": "^3.0.3", + "prettier": "^3.2.5", "prisma": "^5.13.0", "typescript": "^5.3.3" }, diff --git a/api/src/backendimpl/base.ts b/api/src/backendimpl/base.ts index 701263b..6d031a3 100644 --- a/api/src/backendimpl/base.ts +++ b/api/src/backendimpl/base.ts @@ -1,19 +1,19 @@ -export type ParameterReturnedValue = { - success: boolean, - message?: string -} +export type ParameterReturnedValue = { + success: boolean; + message?: string; +}; export type ForwardRule = { - sourceIP: string, - sourcePort: number, - destPort: number + sourceIP: string; + sourcePort: number; + destPort: number; }; export type ConnectedClient = { - ip: string, - port: number, - - connectionDetails: ForwardRule + ip: string; + port: number; + + connectionDetails: ForwardRule; }; export class BackendBaseClass { @@ -25,12 +25,22 @@ export class BackendBaseClass { constructor(parameters: string) { this.logs = []; this.clients = []; - + this.state = "stopped"; } - addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {}; - removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {}; + addConnection( + sourceIP: string, + sourcePort: number, + destPort: number, + protocol: "tcp" | "udp", + ): void {} + removeConnection( + sourceIP: string, + sourcePort: number, + destPort: number, + protocol: "tcp" | "udp", + ): void {} async start(): Promise { return true; @@ -38,22 +48,27 @@ export class BackendBaseClass { async stop(): Promise { return true; - }; + } getAllConnections(): ConnectedClient[] { if (this.clients == null) return []; return this.clients; - }; + } - static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue { + static checkParametersConnection( + sourceIP: string, + sourcePort: number, + destPort: number, + protocol: "tcp" | "udp", + ): ParameterReturnedValue { return { - success: true - } - }; + success: true, + }; + } static checkParametersBackendInstance(data: string): ParameterReturnedValue { return { - success: true - } - }; -} \ No newline at end of file + success: true, + }; + } +} diff --git a/api/src/backendimpl/ssh.ts b/api/src/backendimpl/ssh.ts index 536216c..4033b33 100644 --- a/api/src/backendimpl/ssh.ts +++ b/api/src/backendimpl/ssh.ts @@ -1,43 +1,51 @@ import { NodeSSH } from "node-ssh"; import { Socket } from "node:net"; -import type { BackendBaseClass, ForwardRule, ConnectedClient, ParameterReturnedValue } from "./base.js"; +import type { + BackendBaseClass, + ForwardRule, + ConnectedClient, + ParameterReturnedValue, +} from "./base.js"; type ForwardRuleExt = ForwardRule & { - enabled: boolean -} + enabled: boolean; +}; // Fight me (for better naming) type BackendParsedProviderString = { - ip: string, - port: number, + ip: string; + port: number; - username: string, - privateKey: string -} + username: string; + privateKey: string; +}; function parseBackendProviderString(data: string): BackendParsedProviderString { try { JSON.parse(data); } catch (e) { - throw new Error("Payload body is not JSON") + throw new Error("Payload body is not JSON"); } const jsonData = JSON.parse(data); - if (typeof jsonData.ip != "string") throw new Error("IP field is not a string"); + if (typeof jsonData.ip != "string") + throw new Error("IP field is not a string"); if (typeof jsonData.port != "number") throw new Error("Port is not a number"); - if (typeof jsonData.username != "string") throw new Error("Username is not a string"); - if (typeof jsonData.privateKey != "string") throw new Error("Private key is not a string"); - + if (typeof jsonData.username != "string") + throw new Error("Username is not a string"); + if (typeof jsonData.privateKey != "string") + throw new Error("Private key is not a string"); + return { ip: jsonData.ip, port: jsonData.port, - + username: jsonData.username, - privateKey: jsonData.privateKey - } + privateKey: jsonData.privateKey, + }; } export class SSHBackendProvider implements BackendBaseClass { @@ -54,7 +62,7 @@ export class SSHBackendProvider implements BackendBaseClass { this.logs = []; this.proxies = []; this.clients = []; - + this.options = parseBackendProviderString(parameters); this.state = "stopped"; @@ -76,7 +84,7 @@ export class SSHBackendProvider implements BackendBaseClass { port: this.options.port, username: this.options.username, - privateKey: this.options.privateKey + privateKey: this.options.privateKey, }); } catch (e) { this.logs.push(`Failed to start SSHBackendProvider! Error: '${e}'`); @@ -86,7 +94,7 @@ export class SSHBackendProvider implements BackendBaseClass { this.sshInstance = null; return false; - }; + } this.state = "started"; this.logs.push("Successfully started SSHBackendProvider."); @@ -109,57 +117,81 @@ export class SSHBackendProvider implements BackendBaseClass { this.state = "stopped"; return true; - }; + } - addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void { - const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol); + addConnection( + sourceIP: string, + sourcePort: number, + destPort: number, + protocol: "tcp" | "udp", + ): void { + const connectionCheck = SSHBackendProvider.checkParametersConnection( + sourceIP, + sourcePort, + destPort, + protocol, + ); if (!connectionCheck.success) throw new Error(connectionCheck.message); - const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort); + const foundProxyEntry = this.proxies.find( + i => + i.sourceIP == sourceIP && + i.sourcePort == sourcePort && + i.destPort == destPort, + ); if (foundProxyEntry) return; - (async() => { - await this.sshInstance.forwardIn("0.0.0.0", destPort, (info, accept, reject) => { - const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort); - if (!foundProxyEntry || !foundProxyEntry.enabled) return reject(); + (async () => { + await this.sshInstance.forwardIn( + "0.0.0.0", + destPort, + (info, accept, reject) => { + const foundProxyEntry = this.proxies.find( + i => + i.sourceIP == sourceIP && + i.sourcePort == sourcePort && + i.destPort == destPort, + ); + if (!foundProxyEntry || !foundProxyEntry.enabled) return reject(); - const client: ConnectedClient = { - ip: info.srcIP, - port: info.srcPort, + const client: ConnectedClient = { + ip: info.srcIP, + port: info.srcPort, - connectionDetails: foundProxyEntry - }; + connectionDetails: foundProxyEntry, + }; - this.clients.push(client); - - const srcConn = new Socket(); - - srcConn.connect({ - host: sourceIP, - port: sourcePort - }); + this.clients.push(client); - // Why is this so confusing - const destConn = accept(); + const srcConn = new Socket(); - destConn.addListener("data", (chunk: Uint8Array) => { - srcConn.write(chunk); - }); + srcConn.connect({ + host: sourceIP, + port: sourcePort, + }); - destConn.addListener("close", () => { - this.clients.splice(this.clients.indexOf(client), 1); - srcConn.end(); - }); + // Why is this so confusing + const destConn = accept(); - srcConn.on("data", (data) => { - destConn.write(data); - }); + destConn.addListener("data", (chunk: Uint8Array) => { + srcConn.write(chunk); + }); - srcConn.on("end", () => { - this.clients.splice(this.clients.indexOf(client), 1); - destConn.close(); - }); - }); + destConn.addListener("close", () => { + this.clients.splice(this.clients.indexOf(client), 1); + srcConn.end(); + }); + + srcConn.on("data", data => { + destConn.write(data); + }); + + srcConn.on("end", () => { + this.clients.splice(this.clients.indexOf(client), 1); + destConn.close(); + }); + }, + ); })(); this.proxies.push({ @@ -167,34 +199,56 @@ export class SSHBackendProvider implements BackendBaseClass { sourcePort, destPort, - enabled: true + enabled: true, }); - }; - - removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void { - const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol); + } + + removeConnection( + sourceIP: string, + sourcePort: number, + destPort: number, + protocol: "tcp" | "udp", + ): void { + const connectionCheck = SSHBackendProvider.checkParametersConnection( + sourceIP, + sourcePort, + destPort, + protocol, + ); if (!connectionCheck.success) throw new Error(connectionCheck.message); - const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort); + const foundProxyEntry = this.proxies.find( + i => + i.sourceIP == sourceIP && + i.sourcePort == sourcePort && + i.destPort == destPort, + ); if (!foundProxyEntry) return; foundProxyEntry.enabled = false; - }; + } getAllConnections(): ConnectedClient[] { return this.clients; - }; + } - static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue { - if (protocol == "udp") return { - success: false, - message: "SSH does not support UDP tunneling! Please use something like PortCopier instead (if it gets done)" - }; + static checkParametersConnection( + sourceIP: string, + sourcePort: number, + destPort: number, + protocol: "tcp" | "udp", + ): ParameterReturnedValue { + if (protocol == "udp") + return { + success: false, + message: + "SSH does not support UDP tunneling! Please use something like PortCopier instead (if it gets done)", + }; return { - success: true - } - }; + success: true, + }; + } static checkParametersBackendInstance(data: string): ParameterReturnedValue { try { @@ -203,12 +257,12 @@ export class SSHBackendProvider implements BackendBaseClass { } catch (e: Error) { return { success: false, - message: e.toString() - } + message: e.toString(), + }; } return { - success: true - } - }; -} \ No newline at end of file + success: true, + }; + } +} diff --git a/api/src/index.ts b/api/src/index.ts index 7a84801..e69b9f2 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -1,10 +1,14 @@ import process from "node:process"; -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from "@prisma/client"; import Fastify from "fastify"; -import type { ServerOptions, SessionToken, RouteOptions } from "./libs/types.js"; -import type { BackendBaseClass } from "./backendimpl/base.js";`` +import type { + ServerOptions, + SessionToken, + RouteOptions, +} from "./libs/types.js"; +import type { BackendBaseClass } from "./backendimpl/base.js"; import { route as getPermissions } from "./routes/getPermissions.js"; @@ -28,20 +32,22 @@ import { backendInit } from "./libs/backendInit.js"; const prisma = new PrismaClient(); -const isSignupEnabled: boolean = Boolean(process.env.IS_SIGNUP_ENABLED); +const isSignupEnabled: boolean = Boolean(process.env.IS_SIGNUP_ENABLED); const unsafeAdminSignup: boolean = Boolean(process.env.UNSAFE_ADMIN_SIGNUP); -const noUsersCheck: boolean = await prisma.user.count() == 0; +const noUsersCheck: boolean = (await prisma.user.count()) == 0; if (unsafeAdminSignup) { - console.error("WARNING: You have admin sign up on! This means that anyone that signs up will have admin rights!"); + console.error( + "WARNING: You have admin sign up on! This means that anyone that signs up will have admin rights!", + ); } const serverOptions: ServerOptions = { isSignupEnabled: isSignupEnabled ? true : noUsersCheck, isSignupAsAdminEnabled: unsafeAdminSignup ? true : noUsersCheck, - allowUnsafeGlobalTokens: process.env.NODE_ENV != "production" + allowUnsafeGlobalTokens: process.env.NODE_ENV != "production", }; const sessionTokens: Record = {}; @@ -58,7 +64,7 @@ const routeOptions: RouteOptions = { tokens: sessionTokens, options: serverOptions, - backends: backends + backends: backends, }; console.log("Initializing forwarding rules..."); @@ -67,7 +73,7 @@ const createdBackends = await prisma.desinationProvider.findMany(); for (const backend of createdBackends) { console.log(`Running init steps for ID '${backend.id}' (${backend.name})`); - const init = await backendInit(backend, backends, prisma); + const init = await backendInit(backend, backends, prisma); if (init) console.log("Init successful."); } @@ -75,7 +81,7 @@ for (const backend of createdBackends) { console.log("Done."); getPermissions(routeOptions); - + backendCreate(routeOptions); backendRemove(routeOptions); backendLookup(routeOptions); @@ -96,9 +102,9 @@ userLogin(routeOptions); try { await fastify.listen({ port: 3000, - host: process.env.NODE_ENV == "production" ? "0.0.0.0" : "127.0.0.1" + host: process.env.NODE_ENV == "production" ? "0.0.0.0" : "127.0.0.1", }); } catch (err) { fastify.log.error(err); process.exit(1); -} \ No newline at end of file +} diff --git a/api/src/libs/backendInit.ts b/api/src/libs/backendInit.ts index a9e8a25..ca7613e 100644 --- a/api/src/libs/backendInit.ts +++ b/api/src/libs/backendInit.ts @@ -5,15 +5,19 @@ import { backendProviders } from "../backendimpl/index.js"; type Backend = { id: number; - name: string; - description: string | null; - backend: string; + name: string; + description: string | null; + backend: string; connectionDetails: string; }; -export async function backendInit(backend: Backend, backends: Record, prisma: PrismaClient): Promise { +export async function backendInit( + backend: Backend, + backends: Record, + prisma: PrismaClient, +): Promise { const ourProvider = backendProviders[backend.backend]; - + if (!ourProvider) { console.log(" - Error: Invalid backend recieved!"); return false; @@ -24,7 +28,7 @@ export async function backendInit(backend: Backend, backends: Record = { - "routes.add": false, - "routes.remove": false, - "routes.start": false, - "routes.stop": false, - "routes.edit": false, - "routes.visible": false, + "routes.add": false, + "routes.remove": false, + "routes.start": false, + "routes.stop": false, + "routes.edit": false, + "routes.visible": false, "routes.visibleConn": false, - "backends.add": false, - "backends.remove": false, - "backends.start": false, - "backends.stop": false, - "backends.edit": false, - "backends.visible": false, + "backends.add": false, + "backends.remove": false, + "backends.start": false, + "backends.stop": false, + "backends.edit": false, + "backends.visible": false, "backends.secretVis": false, - "permissions.see": false, + "permissions.see": false, - "users.add": false, - "users.remove": false, - "users.lookup": false, - "users.edit": false, + "users.add": false, + "users.remove": false, + "users.lookup": false, + "users.edit": false, }; // FIXME: This solution fucking sucks. -export let permissionListEnabled: Record = JSON.parse(JSON.stringify(permissionListDisabled)); +export let permissionListEnabled: Record = JSON.parse( + JSON.stringify(permissionListDisabled), +); for (const index of Object.keys(permissionListEnabled)) { permissionListEnabled[index] = true; } -export async function hasPermission(permissionList: string[], uid: number, prisma: PrismaClient): Promise { +export async function hasPermission( + permissionList: string[], + uid: number, + prisma: PrismaClient, +): Promise { for (const permission of permissionList) { const permissionNode = await prisma.permission.findFirst({ where: { userID: uid, - permission - } + permission, + }, }); if (!permissionNode || !permissionNode.has) return false; @@ -48,7 +54,11 @@ export async function hasPermission(permissionList: string[], uid: number, prism return true; } -export async function getUID(token: string, tokens: Record, prisma: PrismaClient): Promise { +export async function getUID( + token: string, + tokens: Record, + prisma: PrismaClient, +): Promise { let userID = -1; // Look up in our currently authenticated users @@ -59,7 +69,10 @@ export async function getUID(token: string, tokens: Record, prisma: PrismaClient): Promise { +export async function hasPermissionByToken( + permissionList: string[], + token: string, + tokens: Record, + prisma: PrismaClient, +): Promise { const userID = await getUID(token, tokens, prisma); return await hasPermission(permissionList, userID, prisma); -} \ No newline at end of file +} diff --git a/api/src/libs/types.ts b/api/src/libs/types.ts index 23c3c1f..7638d76 100644 --- a/api/src/libs/types.ts +++ b/api/src/libs/types.ts @@ -8,21 +8,21 @@ export type ServerOptions = { isSignupAsAdminEnabled: boolean; allowUnsafeGlobalTokens: boolean; -} +}; // NOTE: Someone should probably use Redis for this, but this is fine... export type SessionToken = { - createdAt: number, - expiresAt: number, // Should be (createdAt + (30 minutes)) - - token: string + createdAt: number; + expiresAt: number; // Should be (createdAt + (30 minutes)) + + token: string; }; export type RouteOptions = { - fastify: FastifyInstance, - prisma: PrismaClient, - tokens: Record, - - options: ServerOptions, - backends: Record -}; \ No newline at end of file + fastify: FastifyInstance; + prisma: PrismaClient; + tokens: Record; + + options: ServerOptions; + backends: Record; +}; diff --git a/api/src/routes/backends/create.ts b/api/src/routes/backends/create.ts index 9412c6f..d6d60a8 100644 --- a/api/src/routes/backends/create.ts +++ b/api/src/routes/backends/create.ts @@ -5,90 +5,95 @@ import { backendProviders } from "../../backendimpl/index.js"; import { backendInit } from "../../libs/backendInit.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens, - backends - } = routeOptions; + const { fastify, prisma, tokens, backends } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; - + } + /** * Creates a new backend to use */ - fastify.post("/api/v1/backends/create", { - schema: { - body: { - type: "object", - required: ["token", "name", "backend", "connectionDetails"], + fastify.post( + "/api/v1/backends/create", + { + schema: { + body: { + type: "object", + required: ["token", "name", "backend", "connectionDetails"], - properties: { - token: { type: "string" }, - name: { type: "string" }, - description: { type: "string" }, - backend: { type: "string" } - } + properties: { + token: { type: "string" }, + name: { type: "string" }, + description: { type: "string" }, + backend: { type: "string" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + name: string; + description?: string; + connectionDetails: any; + backend: string; + } = req.body; + + if (!(await hasPermission(body.token, ["backends.add"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - name: string, - description?: string, - connectionDetails: any, - backend: string - } = req.body; - if (!await hasPermission(body.token, [ - "backends.add" - ])) { - return res.status(403).send({ - error: "Unauthorized" - }); - }; - - if (!backendProviders[body.backend]) { - return res.status(400).send({ - error: "Unknown/unsupported/deprecated backend!" - }); - }; - - const connectionDetails = JSON.stringify(body.connectionDetails); - const connectionDetailsValidityCheck = backendProviders[body.backend].checkParametersBackendInstance(connectionDetails); - - if (!connectionDetailsValidityCheck.success) { - return res.status(400).send({ - error: connectionDetailsValidityCheck.message ?? "Unknown error while attempting to parse connectionDetails (it's on your side)" - }); - }; - - const backend = await prisma.desinationProvider.create({ - data: { - name: body.name, - description: body.description, - - backend: body.backend, - connectionDetails: JSON.stringify(body.connectionDetails) + if (!backendProviders[body.backend]) { + return res.status(400).send({ + error: "Unknown/unsupported/deprecated backend!", + }); } - }); - const init = await backendInit(backend, backends, prisma); - - if (!init) { - // TODO: better error code - return res.status(504).send({ - error: "Backend is created, but failed to initalize correctly", - id: backend.id + const connectionDetails = JSON.stringify(body.connectionDetails); + const connectionDetailsValidityCheck = + backendProviders[body.backend].checkParametersBackendInstance( + connectionDetails, + ); + + if (!connectionDetailsValidityCheck.success) { + return res.status(400).send({ + error: + connectionDetailsValidityCheck.message ?? + "Unknown error while attempting to parse connectionDetails (it's on your side)", + }); + } + + const backend = await prisma.desinationProvider.create({ + data: { + name: body.name, + description: body.description, + + backend: body.backend, + connectionDetails: JSON.stringify(body.connectionDetails), + }, }); - } - return { - success: true, - id: backend.id - }; - }); -} \ No newline at end of file + const init = await backendInit(backend, backends, prisma); + + if (!init) { + // TODO: better error code + return res.status(504).send({ + error: "Backend is created, but failed to initalize correctly", + id: backend.id, + }); + } + + return { + success: true, + id: backend.id, + }; + }, + ); +} diff --git a/api/src/routes/backends/lookup.ts b/api/src/routes/backends/lookup.ts index d6c4566..bb24bfd 100644 --- a/api/src/routes/backends/lookup.ts +++ b/api/src/routes/backends/lookup.ts @@ -2,77 +2,81 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens, - backends - } = routeOptions; + const { fastify, prisma, tokens, backends } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } /** * Creates a new route to use */ - fastify.post("/api/v1/backends/lookup", { - schema: { - body: { - type: "object", - required: ["token"], + fastify.post( + "/api/v1/backends/lookup", + { + schema: { + body: { + type: "object", + required: ["token"], - properties: { - token: { type: "string" }, - id: { type: "number" }, - name: { type: "string" }, - description: { type: "string" }, - backend: { type: "string" } - } + properties: { + token: { type: "string" }, + id: { type: "number" }, + name: { type: "string" }, + description: { type: "string" }, + backend: { type: "string" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + id?: number; + name?: string; + description?: string; + backend?: string; + } = req.body; + + if ( + !(await hasPermission(body.token, [ + "backends.visible", // wtf? + ])) + ) { + return res.status(403).send({ + error: "Unauthorized", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id?: number, - name?: string, - description?: string, - backend?: string - } = req.body; - if (!await hasPermission(body.token, [ - "backends.visible" // wtf? - ])) { - return res.status(403).send({ - error: "Unauthorized" + const canSeeSecrets = await hasPermission(body.token, [ + "backends.secretVis", + ]); + + const prismaBackends = await prisma.desinationProvider.findMany({ + where: { + id: body.id, + name: body.name, + description: body.description, + backend: body.backend, + }, }); - }; - const canSeeSecrets = await hasPermission(body.token, [ - "backends.secretVis" - ]); - - const prismaBackends = await prisma.desinationProvider.findMany({ - where: { - id: body.id, - name: body.name, - description: body.description, - backend: body.backend - } - }); + return { + success: true, + data: prismaBackends.map(i => ({ + name: i.name, + description: i.description, - return { - success: true, - data: prismaBackends.map((i) => ({ - name: i.name, - description: i.description, + backend: i.backend, + connectionDetails: canSeeSecrets ? i.connectionDetails : "", - backend: i.backend, - connectionDetails: canSeeSecrets ? i.connectionDetails : "", - - logs: backends[i.id].logs - })) - } - }); -} \ No newline at end of file + logs: backends[i.id].logs, + })), + }; + }, + ); +} diff --git a/api/src/routes/backends/remove.ts b/api/src/routes/backends/remove.ts index 8a69c31..1de1e34 100644 --- a/api/src/routes/backends/remove.ts +++ b/api/src/routes/backends/remove.ts @@ -2,70 +2,70 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens, - backends - } = routeOptions; + const { fastify, prisma, tokens, backends } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } /** * Creates a new route to use */ - fastify.post("/api/v1/backends/remove", { - schema: { - body: { - type: "object", - required: ["token", "id"], + fastify.post( + "/api/v1/backends/remove", + { + schema: { + body: { + type: "object", + required: ["token", "id"], - properties: { - token: { type: "string" }, - id: { type: "number" } - } + properties: { + token: { type: "string" }, + id: { type: "number" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + id: number; + } = req.body; + + if (!(await hasPermission(body.token, ["backends.remove"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id: number - } = req.body; - if (!await hasPermission(body.token, [ - "backends.remove" - ])) { - return res.status(403).send({ - error: "Unauthorized" - }); - }; - - if (!backends[body.id]) { - return res.status(400).send({ - error: "Backend not found" - }); - }; - - // Unload the backend - if (!await backends[body.id].stop()) { - return res.status(400).send({ - error: "Failed to stop backend! Please report this issue." - }) - } - - delete backends[body.id]; - - await prisma.desinationProvider.delete({ - where: { - id: body.id + if (!backends[body.id]) { + return res.status(400).send({ + error: "Backend not found", + }); } - }); - return { - success: true - } - }); -} \ No newline at end of file + // Unload the backend + if (!(await backends[body.id].stop())) { + return res.status(400).send({ + error: "Failed to stop backend! Please report this issue.", + }); + } + + delete backends[body.id]; + + await prisma.desinationProvider.delete({ + where: { + id: body.id, + }, + }); + + return { + success: true, + }; + }, + ); +} diff --git a/api/src/routes/forward/connections.ts b/api/src/routes/forward/connections.ts index 6cfafd0..4c73a57 100644 --- a/api/src/routes/forward/connections.ts +++ b/api/src/routes/forward/connections.ts @@ -2,61 +2,63 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens, - backends - } = routeOptions; + const { fastify, prisma, tokens, backends } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } - fastify.post("/api/v1/forward/connections", { - schema: { - body: { - type: "object", - required: ["token", "id"], + fastify.post( + "/api/v1/forward/connections", + { + schema: { + body: { + type: "object", + required: ["token", "id"], - properties: { - token: { type: "string" }, - id: { type: "number" } - } + properties: { + token: { type: "string" }, + id: { type: "number" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + id: number; + } = req.body; + + if (!(await hasPermission(body.token, ["routes.visibleConn"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id: number - } = req.body; - if (!await hasPermission(body.token, [ - "routes.visibleConn" - ])) { - return res.status(403).send({ - error: "Unauthorized" + const forward = await prisma.forwardRule.findUnique({ + where: { + id: body.id, + }, }); - }; - const forward = await prisma.forwardRule.findUnique({ - where: { - id: body.id - } - }); + if (!forward) + return res.status(400).send({ + error: "Could not find forward entry", + }); - if (!forward) return res.status(400).send({ - error: "Could not find forward entry" - }); + if (!backends[forward.destProviderID]) + return res.status(400).send({ + error: "Backend not found", + }); - if (!backends[forward.destProviderID]) return res.status(400).send({ - error: "Backend not found" - }); - - return { - success: true, - data: backends[forward.destProviderID].getAllConnections() - } - }) -} \ No newline at end of file + return { + success: true, + data: backends[forward.destProviderID].getAllConnections(), + }; + }, + ); +} diff --git a/api/src/routes/forward/create.ts b/api/src/routes/forward/create.ts index 27864d2..9cb3cad 100644 --- a/api/src/routes/forward/create.ts +++ b/api/src/routes/forward/create.ts @@ -2,107 +2,118 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens - } = routeOptions; + const { fastify, prisma, tokens } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } /** * Creates a new route to use */ - fastify.post("/api/v1/forward/create", { - schema: { - body: { - type: "object", - required: ["token", "name", "protocol", "sourceIP", "sourcePort", "destinationPort", "providerID"], + fastify.post( + "/api/v1/forward/create", + { + schema: { + body: { + type: "object", + required: [ + "token", + "name", + "protocol", + "sourceIP", + "sourcePort", + "destinationPort", + "providerID", + ], - properties: { - token: { type: "string" }, + properties: { + token: { type: "string" }, - name: { type: "string" }, - description: { type: "string" }, + name: { type: "string" }, + description: { type: "string" }, - protocol: { type: "string" }, + protocol: { type: "string" }, - sourceIP: { type: "string" }, - sourcePort: { type: "number" }, + sourceIP: { type: "string" }, + sourcePort: { type: "number" }, - destinationPort: { type: "number" }, + destinationPort: { type: "number" }, - providerID: { type: "number" }, - autoStart: { type: "boolean" } - } + providerID: { type: "number" }, + autoStart: { type: "boolean" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + + name: string; + description?: string; + + protocol: "tcp" | "udp"; + + sourceIP: string; + sourcePort: number; + + destinationPort: number; + + providerID: number; + + autoStart?: boolean; + } = req.body; + + if (body.protocol != "tcp" && body.protocol != "udp") { + return res.status(400).send({ + error: "Body protocol field must be either tcp or udp", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - name: string, - description?: string, + if (!(await hasPermission(body.token, ["routes.add"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); + } - protocol: "tcp" | "udp", + const lookupIDForDestProvider = + await prisma.desinationProvider.findUnique({ + where: { + id: body.providerID, + }, + }); - sourceIP: string, - sourcePort: number, + if (!lookupIDForDestProvider) + return res.status(400).send({ + error: "Could not find provider", + }); - destinationPort: number, + const forwardRule = await prisma.forwardRule.create({ + data: { + name: body.name, + description: body.description, - providerID: number, + protocol: body.protocol, - autoStart?: boolean - } = req.body; + sourceIP: body.sourceIP, + sourcePort: body.sourcePort, - if (body.protocol != "tcp" && body.protocol != "udp") { - return res.status(400).send({ - error: "Body protocol field must be either tcp or udp" + destPort: body.destinationPort, + destProviderID: body.providerID, + + enabled: Boolean(body.autoStart), + }, }); - }; - if (!await hasPermission(body.token, [ - "routes.add" - ])) { - return res.status(403).send({ - error: "Unauthorized" - }); - }; - - const lookupIDForDestProvider = await prisma.desinationProvider.findUnique({ - where: { - id: body.providerID - } - }); - - if (!lookupIDForDestProvider) return res.status(400).send({ - error: "Could not find provider" - }); - - const forwardRule = await prisma.forwardRule.create({ - data: { - name: body.name, - description: body.description, - - protocol: body.protocol, - - sourceIP: body.sourceIP, - sourcePort: body.sourcePort, - - destPort: body.destinationPort, - destProviderID: body.providerID, - - enabled: Boolean(body.autoStart) - } - }); - - return { - success: true, - id: forwardRule.id - } - }); -} \ No newline at end of file + return { + success: true, + id: forwardRule.id, + }; + }, + ); +} diff --git a/api/src/routes/forward/lookup.ts b/api/src/routes/forward/lookup.ts index f5fe701..973ce8b 100644 --- a/api/src/routes/forward/lookup.ts +++ b/api/src/routes/forward/lookup.ts @@ -2,107 +2,112 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens - } = routeOptions; + const { fastify, prisma, tokens } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } /** * Creates a new route to use */ - fastify.post("/api/v1/forward/lookup", { - schema: { - body: { - type: "object", - required: ["token"], - - properties: { - token: { type: "string" }, - id: { type: "number" }, + fastify.post( + "/api/v1/forward/lookup", + { + schema: { + body: { + type: "object", + required: ["token"], - name: { type: "string" }, - protocol: { type: "string" }, - description: { type: "string" }, + properties: { + token: { type: "string" }, + id: { type: "number" }, - sourceIP: { type: "string" }, - sourcePort: { type: "number" }, - destPort: { type: "number" }, + name: { type: "string" }, + protocol: { type: "string" }, + description: { type: "string" }, - providerID: { type: "number" }, - autoStart: { type: "boolean" } - } + sourceIP: { type: "string" }, + sourcePort: { type: "number" }, + destPort: { type: "number" }, + + providerID: { type: "number" }, + autoStart: { type: "boolean" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + + id?: number; + name?: string; + description?: string; + + protocol?: "tcp" | "udp"; + + sourceIP?: string; + sourcePort?: number; + + destinationPort?: number; + + providerID?: number; + autoStart?: boolean; + } = req.body; + + if (body.protocol && body.protocol != "tcp" && body.protocol != "udp") { + return res.status(400).send({ + error: "Protocol specified in body must be either 'tcp' or 'udp'", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id?: number, - name?: string, - description?: string, + if ( + !(await hasPermission(body.token, [ + "routes.visible", // wtf? + ])) + ) { + return res.status(403).send({ + error: "Unauthorized", + }); + } - protocol?: "tcp" | "udp", + const forwardRules = await prisma.forwardRule.findMany({ + where: { + id: body.id, + name: body.name, + description: body.description, - sourceIP?: string, - sourcePort?: number, + sourceIP: body.sourceIP, + sourcePort: body.sourcePort, - destinationPort?: number, + destPort: body.destinationPort, - providerID?: number, - autoStart?: boolean - } = req.body; - - if (body.protocol && body.protocol != "tcp" && body.protocol != "udp") { - return res.status(400).send({ - error: "Protocol specified in body must be either 'tcp' or 'udp'" - }) - } - - if (!await hasPermission(body.token, [ - "routes.visible" // wtf? - ])) { - return res.status(403).send({ - error: "Unauthorized" + destProviderID: body.providerID, + enabled: body.autoStart, + }, }); - }; - const forwardRules = await prisma.forwardRule.findMany({ - where: { - id: body.id, - name: body.name, - description: body.description, + return { + success: true, + data: forwardRules.map(i => ({ + id: i.id, + name: i.name, + description: i.description, - sourceIP: body.sourceIP, - sourcePort: body.sourcePort, + sourceIP: i.sourceIP, + sourcePort: i.sourcePort, - destPort: body.destinationPort, + destPort: i.destPort, - destProviderID: body.providerID, - enabled: body.autoStart - } - }); - - return { - success: true, - data: forwardRules.map((i) => ({ - id: i.id, - name: i.name, - description: i.description, - - sourceIP: i.sourceIP, - sourcePort: i.sourcePort, - - destPort: i.destPort, - - providerID: i.destProviderID, - autoStart: i.enabled // TODO: Add enabled flag in here to see if we're running or not - })) - }; - }); -} \ No newline at end of file + providerID: i.destProviderID, + autoStart: i.enabled, // TODO: Add enabled flag in here to see if we're running or not + })), + }; + }, + ); +} diff --git a/api/src/routes/forward/remove.ts b/api/src/routes/forward/remove.ts index 8f2d087..a64f14f 100644 --- a/api/src/routes/forward/remove.ts +++ b/api/src/routes/forward/remove.ts @@ -2,54 +2,55 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens - } = routeOptions; + const { fastify, prisma, tokens } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } /** * Creates a new route to use */ - fastify.post("/api/v1/forward/remove", { - schema: { - body: { - type: "object", - required: ["token", "id"], - - properties: { - token: { type: "string" }, - id: { type: "number" } - } - } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id: number - } = req.body; + fastify.post( + "/api/v1/forward/remove", + { + schema: { + body: { + type: "object", + required: ["token", "id"], - if (!await hasPermission(body.token, [ - "routes.remove" - ])) { - return res.status(403).send({ - error: "Unauthorized" + properties: { + token: { type: "string" }, + id: { type: "number" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + id: number; + } = req.body; + + if (!(await hasPermission(body.token, ["routes.remove"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); + } + + await prisma.forwardRule.delete({ + where: { + id: body.id, + }, }); - }; - - await prisma.forwardRule.delete({ - where: { - id: body.id - } - }); - return { - success: true - } - }); -} \ No newline at end of file + return { + success: true, + }; + }, + ); +} diff --git a/api/src/routes/forward/start.ts b/api/src/routes/forward/start.ts index 98bca45..6794027 100644 --- a/api/src/routes/forward/start.ts +++ b/api/src/routes/forward/start.ts @@ -2,69 +2,76 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens, - backends - } = routeOptions; + const { fastify, prisma, tokens, backends } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } /** * Creates a new route to use */ - fastify.post("/api/v1/forward/start", { - schema: { - body: { - type: "object", - required: ["token", "id"], + fastify.post( + "/api/v1/forward/start", + { + schema: { + body: { + type: "object", + required: ["token", "id"], - properties: { - token: { type: "string" }, - id: { type: "number" } - } + properties: { + token: { type: "string" }, + id: { type: "number" }, + }, + }, }, - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id: number - } = req.body; + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + id: number; + } = req.body; - if (!await hasPermission(body.token, [ - "routes.start" - ])) { - return res.status(403).send({ - error: "Unauthorized" - }); - }; - - const forward = await prisma.forwardRule.findUnique({ - where: { - id: body.id + if (!(await hasPermission(body.token, ["routes.start"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - }); - if (!forward) return res.status(400).send({ - error: "Could not find forward entry" - }); + const forward = await prisma.forwardRule.findUnique({ + where: { + id: body.id, + }, + }); - if (!backends[forward.destProviderID]) return res.status(400).send({ - error: "Backend not found" - }); + if (!forward) + return res.status(400).send({ + error: "Could not find forward entry", + }); - // Other restrictions in place make it so that it MUST be either TCP or UDP - // @ts-ignore - const protocol: "tcp" | "udp" = forward.protocol; + if (!backends[forward.destProviderID]) + return res.status(400).send({ + error: "Backend not found", + }); - backends[forward.destProviderID].addConnection(forward.sourceIP, forward.sourcePort, forward.destPort, protocol); + // Other restrictions in place make it so that it MUST be either TCP or UDP + // @ts-ignore + const protocol: "tcp" | "udp" = forward.protocol; - return { - success: true - } - }); -} \ No newline at end of file + backends[forward.destProviderID].addConnection( + forward.sourceIP, + forward.sourcePort, + forward.destPort, + protocol, + ); + + return { + success: true, + }; + }, + ); +} diff --git a/api/src/routes/forward/stop.ts b/api/src/routes/forward/stop.ts index 2c81a77..5b1a179 100644 --- a/api/src/routes/forward/stop.ts +++ b/api/src/routes/forward/stop.ts @@ -2,69 +2,76 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens, - backends - } = routeOptions; + const { fastify, prisma, tokens, backends } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } /** * Creates a new route to use */ - fastify.post("/api/v1/forward/stop", { - schema: { - body: { - type: "object", - required: ["token", "id"], + fastify.post( + "/api/v1/forward/stop", + { + schema: { + body: { + type: "object", + required: ["token", "id"], - properties: { - token: { type: "string" }, - id: { type: "number" } - } + properties: { + token: { type: "string" }, + id: { type: "number" }, + }, + }, }, - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id: number - } = req.body; + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + id: number; + } = req.body; - if (!await hasPermission(body.token, [ - "routes.stop" - ])) { - return res.status(403).send({ - error: "Unauthorized" - }); - }; - - const forward = await prisma.forwardRule.findUnique({ - where: { - id: body.id + if (!(await hasPermission(body.token, ["routes.stop"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - }); - if (!forward) return res.status(400).send({ - error: "Could not find forward entry" - }); + const forward = await prisma.forwardRule.findUnique({ + where: { + id: body.id, + }, + }); - if (!backends[forward.destProviderID]) return res.status(400).send({ - error: "Backend not found" - }); + if (!forward) + return res.status(400).send({ + error: "Could not find forward entry", + }); - // Other restrictions in place make it so that it MUST be either TCP or UDP - // @ts-ignore - const protocol: "tcp" | "udp" = forward.protocol; + if (!backends[forward.destProviderID]) + return res.status(400).send({ + error: "Backend not found", + }); - backends[forward.destProviderID].removeConnection(forward.sourceIP, forward.sourcePort, forward.destPort, protocol); + // Other restrictions in place make it so that it MUST be either TCP or UDP + // @ts-ignore + const protocol: "tcp" | "udp" = forward.protocol; - return { - success: true - } - }); -} \ No newline at end of file + backends[forward.destProviderID].removeConnection( + forward.sourceIP, + forward.sourcePort, + forward.destPort, + protocol, + ); + + return { + success: true, + }; + }, + ); +} diff --git a/api/src/routes/getPermissions.ts b/api/src/routes/getPermissions.ts index 8787927..21c7361 100644 --- a/api/src/routes/getPermissions.ts +++ b/api/src/routes/getPermissions.ts @@ -2,52 +2,50 @@ import { hasPermission, getUID } from "../libs/permissions.js"; import type { RouteOptions } from "../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens - } = routeOptions; + const { fastify, prisma, tokens } = routeOptions; /** * Logs in to a user account. */ - fastify.post("/api/v1/getPermissions", { - schema: { - body: { - type: "object", - required: ["token"], + fastify.post( + "/api/v1/getPermissions", + { + schema: { + body: { + type: "object", + required: ["token"], - properties: { - token: { type: "string" } - } + properties: { + token: { type: "string" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + } = req.body; + + const uid = await getUID(body.token, tokens, prisma); + + if (!(await hasPermission(["permissions.see"], uid, prisma))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string - } = req.body; - const uid = await getUID(body.token, tokens, prisma); - - if (!await hasPermission([ - "permissions.see" - ], uid, prisma)) { - return res.status(403).send({ - error: "Unauthorized" + const permissionsRaw = await prisma.permission.findMany({ + where: { + userID: uid, + }, }); - }; - const permissionsRaw = await prisma.permission.findMany({ - where: { - userID: uid - } - }); - - return { - success: true, - // Get the ones that we have, and transform them into just their name - data: permissionsRaw.filter((i) => i.has).map((i) => i.permission) - } - }); + return { + success: true, + // Get the ones that we have, and transform them into just their name + data: permissionsRaw.filter(i => i.has).map(i => i.permission), + }; + }, + ); } diff --git a/api/src/routes/user/create.ts b/api/src/routes/user/create.ts index e41ddc9..4259421 100644 --- a/api/src/routes/user/create.ts +++ b/api/src/routes/user/create.ts @@ -6,113 +6,116 @@ import { generateRandomData } from "../../libs/generateRandom.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens, - options - } = routeOptions; + const { fastify, prisma, tokens, options } = routeOptions; /** * Creates a new user account to use, only if it is enabled. */ - fastify.post("/api/v1/users/create", { - schema: { - body: { - type: "object", - required: ["name", "email", "password"], + fastify.post( + "/api/v1/users/create", + { + schema: { + body: { + type: "object", + required: ["name", "email", "password"], - properties: { - name: { type: "string" }, - email: { type: "string" }, - password: { type: "string" } - } - } - } - }, async(req, res) => { - // @ts-ignore - const body: { - name: string, - email: string, - password: string - } = req.body; + properties: { + name: { type: "string" }, + email: { type: "string" }, + password: { type: "string" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + name: string; + email: string; + password: string; + } = req.body; - if (!options.isSignupEnabled) { - return res.status(403).send({ - error: "Signing up is not enabled at this time." - }); - }; - - const userSearch = await prisma.user.findFirst({ - where: { - email: body.email - } - }); - - if (userSearch) { - return res.status(400).send({ - error: "User already exists" - }) - }; - - const saltedPassword: string = await hash(body.password, 15); - - const userData = { - name: body.name, - email: body.email, - password: saltedPassword, - - permissions: { - create: [] as { - permission: string, - has: boolean - }[] - } - }; - - // TODO: There's probably a faster way to pull this off, but I'm lazy - for (const permissionKey of Object.keys(permissionListEnabled)) { - if (options.isSignupAsAdminEnabled || (permissionKey.startsWith("routes") || permissionKey == "permissions.see")) { - userData.permissions.create.push({ - permission: permissionKey, - has: permissionListEnabled[permissionKey] + if (!options.isSignupEnabled) { + return res.status(403).send({ + error: "Signing up is not enabled at this time.", }); } - }; - if (options.allowUnsafeGlobalTokens) { - // @ts-ignore - userData.rootToken = generateRandomData(); - // @ts-ignore - userData.isRootServiceAccount = true; - } - - const userCreateResults = await prisma.user.create({ - data: userData - }); - - // FIXME(?): Redundant checks - if (options.allowUnsafeGlobalTokens) { - return { - success: true, - token: userCreateResults.rootToken - }; - } else { - const generatedToken = generateRandomData(); - - tokens[userCreateResults.id] = []; - - tokens[userCreateResults.id].push({ - createdAt: Date.now(), - expiresAt: Date.now() + (30 * 60_000), - - token: generatedToken + const userSearch = await prisma.user.findFirst({ + where: { + email: body.email, + }, }); - return { - success: true, - token: generatedToken + if (userSearch) { + return res.status(400).send({ + error: "User already exists", + }); + } + + const saltedPassword: string = await hash(body.password, 15); + + const userData = { + name: body.name, + email: body.email, + password: saltedPassword, + + permissions: { + create: [] as { + permission: string; + has: boolean; + }[], + }, }; - }; - }); -} \ No newline at end of file + + // TODO: There's probably a faster way to pull this off, but I'm lazy + for (const permissionKey of Object.keys(permissionListEnabled)) { + if ( + options.isSignupAsAdminEnabled || + permissionKey.startsWith("routes") || + permissionKey == "permissions.see" + ) { + userData.permissions.create.push({ + permission: permissionKey, + has: permissionListEnabled[permissionKey], + }); + } + } + + if (options.allowUnsafeGlobalTokens) { + // @ts-ignore + userData.rootToken = generateRandomData(); + // @ts-ignore + userData.isRootServiceAccount = true; + } + + const userCreateResults = await prisma.user.create({ + data: userData, + }); + + // FIXME(?): Redundant checks + if (options.allowUnsafeGlobalTokens) { + return { + success: true, + token: userCreateResults.rootToken, + }; + } else { + const generatedToken = generateRandomData(); + + tokens[userCreateResults.id] = []; + + tokens[userCreateResults.id].push({ + createdAt: Date.now(), + expiresAt: Date.now() + 30 * 60_000, + + token: generatedToken, + }); + + return { + success: true, + token: generatedToken, + }; + } + }, + ); +} diff --git a/api/src/routes/user/login.ts b/api/src/routes/user/login.ts index 727d800..14b04c3 100644 --- a/api/src/routes/user/login.ts +++ b/api/src/routes/user/login.ts @@ -4,63 +4,65 @@ import { generateRandomData } from "../../libs/generateRandom.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens - } = routeOptions; + const { fastify, prisma, tokens } = routeOptions; /** * Logs in to a user account. */ - fastify.post("/api/v1/users/login", { - schema: { - body: { - type: "object", - required: ["email", "password"], + fastify.post( + "/api/v1/users/login", + { + schema: { + body: { + type: "object", + required: ["email", "password"], - properties: { - email: { type: "string" }, - password: { type: "string" } - } - } - } - }, async(req, res) => { - // @ts-ignore - const body: { - email: string, - password: string - } = req.body; + properties: { + email: { type: "string" }, + password: { type: "string" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + email: string; + password: string; + } = req.body; - const userSearch = await prisma.user.findFirst({ - where: { - email: body.email - } - }); + const userSearch = await prisma.user.findFirst({ + where: { + email: body.email, + }, + }); - if (!userSearch) return res.status(403).send({ - error: "Email or password is incorrect" - }); + if (!userSearch) + return res.status(403).send({ + error: "Email or password is incorrect", + }); - const passwordIsValid = await compare(body.password, userSearch.password); - - if (!passwordIsValid) return res.status(403).send({ - error: "Email or password is incorrect" - }); + const passwordIsValid = await compare(body.password, userSearch.password); - const token = generateRandomData(); - if (!tokens[userSearch.id]) tokens[userSearch.id] = []; - - tokens[userSearch.id].push({ - createdAt: Date.now(), - expiresAt: Date.now() + (30 * 60_000), + if (!passwordIsValid) + return res.status(403).send({ + error: "Email or password is incorrect", + }); - token - }); + const token = generateRandomData(); + if (!tokens[userSearch.id]) tokens[userSearch.id] = []; - return { - success: true, - token - } - }); -} \ No newline at end of file + tokens[userSearch.id].push({ + createdAt: Date.now(), + expiresAt: Date.now() + 30 * 60_000, + + token, + }); + + return { + success: true, + token, + }; + }, + ); +} diff --git a/api/src/routes/user/lookup.ts b/api/src/routes/user/lookup.ts index 05f2cc9..e9b00fe 100644 --- a/api/src/routes/user/lookup.ts +++ b/api/src/routes/user/lookup.ts @@ -2,65 +2,66 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens - } = routeOptions; + const { fastify, prisma, tokens } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; + } - fastify.post("/api/v1/users/lookup", { - schema: { - body: { - type: "object", - required: ["token"], + fastify.post( + "/api/v1/users/lookup", + { + schema: { + body: { + type: "object", + required: ["token"], - properties: { - token: { type: "string" }, - id: { type: "number" }, - name: { type: "string" }, - email: { type: "string" }, - isServiceAccount: { type: "boolean" } - } + properties: { + token: { type: "string" }, + id: { type: "number" }, + name: { type: "string" }, + email: { type: "string" }, + isServiceAccount: { type: "boolean" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + id?: number; + name?: string; + email?: string; + isServiceAccount?: boolean; + } = req.body; + + if (!(await hasPermission(body.token, ["users.lookup"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - id?: number, - name?: string, - email?: string, - isServiceAccount?: boolean - } = req.body; - if (!await hasPermission(body.token, [ - "users.lookup" - ])) { - return res.status(403).send({ - error: "Unauthorized" + const users = await prisma.user.findMany({ + where: { + id: body.id, + name: body.name, + email: body.email, + isRootServiceAccount: body.isServiceAccount, + }, }); - }; - const users = await prisma.user.findMany({ - where: { - id: body.id, - name: body.name, - email: body.email, - isRootServiceAccount: body.isServiceAccount - } - }); - - return { - success: true, - data: users.map((i) => ({ - name: i.name, - email: i.email, - isServiceAccount: i.isRootServiceAccount - })) - } - }); -} \ No newline at end of file + return { + success: true, + data: users.map(i => ({ + name: i.name, + email: i.email, + isServiceAccount: i.isRootServiceAccount, + })), + }; + }, + ); +} diff --git a/api/src/routes/user/remove.ts b/api/src/routes/user/remove.ts index 7c2d2a1..8f65807 100644 --- a/api/src/routes/user/remove.ts +++ b/api/src/routes/user/remove.ts @@ -2,60 +2,61 @@ import { hasPermissionByToken } from "../../libs/permissions.js"; import type { RouteOptions } from "../../libs/types.js"; export function route(routeOptions: RouteOptions) { - const { - fastify, - prisma, - tokens - } = routeOptions; + const { fastify, prisma, tokens } = routeOptions; - function hasPermission(token: string, permissionList: string[]): Promise { + function hasPermission( + token: string, + permissionList: string[], + ): Promise { return hasPermissionByToken(permissionList, token, tokens, prisma); - }; - + } + /** * Creates a new backend to use */ - fastify.post("/api/v1/users/remove", { - schema: { - body: { - type: "object", - required: ["token", "uid"], + fastify.post( + "/api/v1/users/remove", + { + schema: { + body: { + type: "object", + required: ["token", "uid"], - properties: { - token: { type: "string" }, - uid: { type: "number" } - } + properties: { + token: { type: "string" }, + uid: { type: "number" }, + }, + }, + }, + }, + async (req, res) => { + // @ts-ignore + const body: { + token: string; + uid: number; + } = req.body; + + if (!(await hasPermission(body.token, ["users.remove"]))) { + return res.status(403).send({ + error: "Unauthorized", + }); } - } - }, async(req, res) => { - // @ts-ignore - const body: { - token: string, - uid: number - } = req.body; - if (!await hasPermission(body.token, [ - "users.remove" - ])) { - return res.status(403).send({ - error: "Unauthorized" + await prisma.permission.deleteMany({ + where: { + userID: body.uid, + }, }); - }; - await prisma.permission.deleteMany({ - where: { - userID: body.uid - } - }); + await prisma.user.delete({ + where: { + id: body.uid, + }, + }); - await prisma.user.delete({ - where: { - id: body.uid - } - }); - - return { - success: true - } - }); -}; \ No newline at end of file + return { + success: true, + }; + }, + ); +} diff --git a/gui/src/api.rs b/gui/src/api.rs index 40939eb..17d1091 100644 --- a/gui/src/api.rs +++ b/gui/src/api.rs @@ -3,17 +3,22 @@ use serde::Deserialize; use serde_json::json; pub struct NextAPIClient { - pub url: String + pub url: String, } #[derive(Deserialize, Debug, Default)] pub struct LoginResponse { pub error: Option, - pub token: Option + pub token: Option, } impl NextAPIClient { - pub fn login(&self, email: &str, password: &str, mut callback: impl 'static + Send + FnMut(LoginResponse)) { + pub fn login( + &self, + email: &str, + password: &str, + mut callback: impl 'static + Send + FnMut(LoginResponse), + ) { let json_data = json!({ "email": email, "password": password @@ -24,7 +29,10 @@ impl NextAPIClient { println!("{}", json_str); - let mut request = Request::post(self.url.clone() + "/api/v1/users/login", json_str.as_bytes().to_vec()); + let mut request = Request::post( + self.url.clone() + "/api/v1/users/login", + json_str.as_bytes().to_vec(), + ); request.headers.insert("Content-Type", "application/json"); fetch(request, move |result: ehttp::Result| { @@ -36,9 +44,7 @@ impl NextAPIClient { } pub fn new(url: String) -> NextAPIClient { - let api_client: NextAPIClient = NextAPIClient { - url - }; + let api_client: NextAPIClient = NextAPIClient { url }; return api_client; -} \ No newline at end of file +} diff --git a/gui/src/components/log_in.rs b/gui/src/components/log_in.rs index d488001..b4cdaaa 100644 --- a/gui/src/components/log_in.rs +++ b/gui/src/components/log_in.rs @@ -1,5 +1,5 @@ -use std::sync::Arc; use eframe::egui; +use std::sync::Arc; use crate::api; use crate::ApplicationState; @@ -12,7 +12,7 @@ pub fn main(state: &mut ApplicationState, api: &api::NextAPIClient, ctx: &eframe ui.label("Email: "); ui.text_edit_singleline(&mut state.username); }); - + ui.horizontal(|ui| { let label = ui.label("Password: "); ui.add(egui::TextEdit::singleline(&mut state.password).password(true)) @@ -21,18 +21,20 @@ pub fn main(state: &mut ApplicationState, api: &api::NextAPIClient, ctx: &eframe if ui.button("Login").clicked() { let token_clone = Arc::clone(&state.token); - api.login(state.username.as_str(), state.password.as_str(), Box::new(move |res: api::LoginResponse| { - match res.token { + api.login( + state.username.as_str(), + state.password.as_str(), + Box::new(move |res: api::LoginResponse| match res.token { Some(x) => { let mut token = token_clone.lock().unwrap(); *token = x; - }, + } None => { let mut token = token_clone.lock().unwrap(); *token = "".to_string(); } - } - })); + }), + ); } }); -} \ No newline at end of file +} diff --git a/gui/src/components/mod.rs b/gui/src/components/mod.rs index c1b4734..cf639a1 100644 --- a/gui/src/components/mod.rs +++ b/gui/src/components/mod.rs @@ -1 +1 @@ -pub mod log_in; \ No newline at end of file +pub mod log_in; diff --git a/gui/src/main.rs b/gui/src/main.rs index abc8a7f..504295a 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -1,8 +1,8 @@ -use std::sync::{Arc, Mutex}; use eframe::egui; +use std::sync::{Arc, Mutex}; -mod components; mod api; +mod components; pub struct ApplicationState { token: Arc>, @@ -20,21 +20,21 @@ fn main() -> Result<(), eframe::Error> { let mut app_state: ApplicationState = ApplicationState { token: Arc::new(Mutex::new("".to_string())), - + // /!\ NOT THREAD SAFE FIELDS /!\ // These are used internally for each application (immediate mode + functions which are stateless, - // and we need *a* state somehow) + // and we need *a* state somehow) // components/log_in.rs username: "replace@gmail.com".to_owned(), - password: "replace123".to_owned() + password: "replace123".to_owned(), }; eframe::run_simple_native("NextNet GUI", options, move |ctx, _frame| { egui::CentralPanel::default().show(ctx, |_ui| { let token_clone = Arc::clone(&app_state.token); let token = token_clone.lock().unwrap(); - + if *token == "".to_string() { components::log_in::main(&mut app_state, &api, ctx); } else { @@ -42,4 +42,4 @@ fn main() -> Result<(), eframe::Error> { } }); }) -} \ No newline at end of file +} diff --git a/init.sh b/init.sh index e4586ef..a09f3f0 100644 --- a/init.sh +++ b/init.sh @@ -1,3 +1,5 @@ pushd api > /dev/null 2> /dev/null source init.sh + +git config --local include.path .gitconfig popd > /dev/null 2> /dev/null \ No newline at end of file diff --git a/shell.nix b/shell.nix index 88b4558..e83f2bb 100644 --- a/shell.nix +++ b/shell.nix @@ -5,6 +5,7 @@ # gui/ cargo rustc + rustfmt # api/ nodejs