feature: Adds logging in support, and fixes permissions.

This commit is contained in:
Tera << 8 2024-04-19 15:43:23 +00:00
parent 0c279b459f
commit a7fd48d732
6 changed files with 136 additions and 14 deletions

View file

@ -0,0 +1,18 @@
meta {
name: Create User
type: http
seq: 1
}
post {
url: http://127.0.0.1:3000/api/v1/users/login
body: json
auth: none
}
body:json {
{
"email": "me@greysoh.dev",
"password": "password"
}
}

View file

@ -5,6 +5,7 @@ import Fastify from "fastify";
import { ServerOptions, SessionToken } from "./libs/types.js"; import { ServerOptions, SessionToken } from "./libs/types.js";
import { route as create } from "./routes/user/create.js"; import { route as create } from "./routes/user/create.js";
import { route as login } from "./routes/user/login.js";
const prisma = new PrismaClient(); const prisma = new PrismaClient();
@ -31,6 +32,7 @@ const fastify = Fastify({
}); });
create(fastify, prisma, sessionTokens, serverOptions); create(fastify, prisma, sessionTokens, serverOptions);
login(fastify, prisma, sessionTokens, serverOptions);
// Run the server! // Run the server!
try { try {

View file

@ -1,4 +1,5 @@
import type { PrismaClient } from "@prisma/client"; import type { PrismaClient } from "@prisma/client";
import type { SessionToken } from "./types.js";
export const permissionListDisabled: Record<string, boolean> = { export const permissionListDisabled: Record<string, boolean> = {
"routes.add": false, "routes.add": false,
@ -29,14 +30,44 @@ for (const index of Object.keys(permissionListEnabled)) {
permissionListEnabled[index] = true; permissionListEnabled[index] = true;
} }
export async function hasPermission(permission: string, uid: number, prisma: PrismaClient): Promise<boolean> { export async function hasPermission(permissionList: string[], uid: number, prisma: PrismaClient): Promise<boolean> {
const permissionNode = await prisma.permission.findFirst({ for (const permission of permissionList) {
where: { const permissionNode = await prisma.permission.findFirst({
userID: uid, where: {
permission userID: uid,
} permission
}); }
});
if (!permissionNode) return false; if (!permissionNode || !permissionNode.has) return false;
return permissionNode.has; }
return true;
}
export async function hasPermissionByToken(permissionList: string[], token: string, tokens: Record<number, SessionToken[]>, prisma: PrismaClient): Promise<boolean> {
let userID = -1;
for (const otherTokenKey of Object.keys(tokens)) {
const otherTokenList = tokens[parseInt(otherTokenKey)];
for (const otherTokenIndex in otherTokenList) {
const otherToken = otherTokenList[otherTokenIndex];
if (otherToken.token == token) {
if (otherToken.expiresAt < otherToken.createdAt + (otherToken.createdAt - Date.now())) {
otherTokenList.splice(parseInt(otherTokenIndex), 1);
continue;
} else {
userID = parseInt(otherTokenKey);
}
}
}
}
// Fine, we'll look up for global tokens...
if (userID == -1) return false;
return true;
} }

12
src/routes/ROUTE_PLAN.md Normal file
View file

@ -0,0 +1,12 @@
# Route Plan
- /api/v1/users/create
- /api/v1/users/disable
- /api/v1/users/remove
- /api/v1/backends/create
- /api/v1/backends/remove
- /api/v1/backends/modify
- /api/v1/backends/search
- /api/v1/routes/create
- /api/v1/routes/remove
- /api/v1/routes/modify
- /api/v1/routes/search

View file

@ -7,8 +7,6 @@ import { permissionListEnabled } from "../../libs/permissions.js";
import { generateToken } from "../../libs/generateToken.js"; import { generateToken } from "../../libs/generateToken.js";
export function route(fastify: FastifyInstance, prisma: PrismaClient, tokens: Record<number, SessionToken[]>, options: ServerOptions) { export function route(fastify: FastifyInstance, prisma: PrismaClient, tokens: Record<number, SessionToken[]>, options: ServerOptions) {
// TODO: Permissions
/** /**
* Creates a new user account to use, only if it is enabled. * Creates a new user account to use, only if it is enabled.
*/ */
@ -63,9 +61,7 @@ export function route(fastify: FastifyInstance, prisma: PrismaClient, tokens: Re
permission: string, permission: string,
has: boolean has: boolean
}[] }[]
}, }
rootToken: null
}; };
// TODO: There's probably a faster way to pull this off, but I'm lazy // TODO: There's probably a faster way to pull this off, but I'm lazy
@ -79,6 +75,7 @@ export function route(fastify: FastifyInstance, prisma: PrismaClient, tokens: Re
}; };
if (options.allowUnsafeGlobalTokens) { if (options.allowUnsafeGlobalTokens) {
// @ts-ignore
userData.rootToken = generateToken() as unknown as null; userData.rootToken = generateToken() as unknown as null;
} }

62
src/routes/user/login.ts Normal file
View file

@ -0,0 +1,62 @@
import type { PrismaClient } from "@prisma/client";
import type { FastifyInstance } from "fastify";
import { compare } from "bcrypt";
import { ServerOptions, SessionToken } from "../../libs/types.js";
import { generateToken } from "../../libs/generateToken.js";
export function route(fastify: FastifyInstance, prisma: PrismaClient, tokens: Record<number, SessionToken[]>, options: ServerOptions) {
/**
* Logs in to a user account.
*/
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;
const userSearch = await prisma.user.findFirst({
where: {
email: body.email
}
});
if (!userSearch) return res.status(403).send({
error: "Email or password is incorrect"
});
const passwordIsValid = compare(userSearch.password, body.password);
if (!passwordIsValid) return res.status(403).send({
error: "Email or password is incorrect"
});
const token = generateToken();
if (!tokens[userSearch.id]) tokens[userSearch.id] = [];
tokens[userSearch.id].push({
createdAt: Date.now(),
expiresAt: Date.now() + (30 * 60_000),
token
});
return {
success: true,
token
}
});
}