From 7cf355302694abb2292f804b8b263b5cc964f1b5 Mon Sep 17 00:00:00 2001 From: JandereDev Date: Mon, 7 Feb 2022 09:02:46 +0100 Subject: [PATCH] little authentication overhaul --- api/src/routes/dash/server-automod.ts | 16 +++------------- api/src/routes/dash/server.ts | 4 ++-- api/src/routes/dash/servers.ts | 8 ++++---- api/src/routes/login.ts | 14 +++++++++++--- api/src/utils.ts | 22 +++++++++++++++++++++- bot/src/bot/util.ts | 2 +- 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/api/src/routes/dash/server-automod.ts b/api/src/routes/dash/server-automod.ts index 2a1f6f8..c0d7cde 100644 --- a/api/src/routes/dash/server-automod.ts +++ b/api/src/routes/dash/server-automod.ts @@ -1,6 +1,6 @@ import { app, db } from '../..'; import { Request, Response } from 'express'; -import { badRequest, isAuthenticated, unauthorized } from '../../utils'; +import { badRequest, isAuthenticated, requireAuth, unauthorized } from '../../utils'; import { botReq } from '../internal/ws'; import { FindOneResult } from 'monk'; @@ -13,7 +13,7 @@ type AntispamRule = { message: string | null; } -app.get('/dash/server/:server/automod', async (req: Request, res: Response) => { +app.get('/dash/server/:server/automod',requireAuth({ permission: 2 }) , async (req: Request, res: Response) => { const user = await isAuthenticated(req, res, true); if (!user) return; @@ -48,7 +48,7 @@ app.get('/dash/server/:server/automod', async (req: Request, res: Response) => { res.send(result); }); -app.patch('/dash/server/:server/automod/:ruleid', async (req: Request, res: Response) => { +app.patch('/dash/server/:server/automod/:ruleid', requireAuth({ permission: 2 }), async (req: Request, res: Response) => { const user = await isAuthenticated(req, res, true); if (!user) return; @@ -56,16 +56,6 @@ app.patch('/dash/server/:server/automod/:ruleid', async (req: Request, res: Resp const body = req.body; if (!server || !ruleid) return badRequest(res); - const response = await botReq('getUserServerDetails', { user, server }); - if (!response.success) { - return res.status(response.statusCode ?? 500).send({ error: response.error }); - } - - if (!response.server) return res.status(404).send({ error: 'Server not found' }); - - const permissionLevel: 0|1|2|3 = response.perms; - if (permissionLevel < 2) return unauthorized(res, `Only bot managers can manage moderation rules.`); - const serverConfig: FindOneResult = await db.get('servers').findOne({ id: server }); const antiSpamRules: AntispamRule[] = serverConfig.automodSettings?.spam ?? []; diff --git a/api/src/routes/dash/server.ts b/api/src/routes/dash/server.ts index 85c264a..bd144ab 100644 --- a/api/src/routes/dash/server.ts +++ b/api/src/routes/dash/server.ts @@ -1,6 +1,6 @@ import { app, db } from '../..'; import { Request, Response } from 'express'; -import { badRequest, getPermissionLevel, isAuthenticated, unauthorized } from '../../utils'; +import { badRequest, getPermissionLevel, isAuthenticated, requireAuth, unauthorized } from '../../utils'; import { botReq } from '../internal/ws'; type User = { id: string, username?: string, avatarURL?: string } @@ -18,7 +18,7 @@ type ServerDetails = { channels: Channel[], } -app.get('/dash/server/:server', async (req: Request, res: Response) => { +app.get('/dash/server/:server', requireAuth({ permission: 0 }), async (req: Request, res: Response) => { const user = await isAuthenticated(req, res, true); if (!user) return; diff --git a/api/src/routes/dash/servers.ts b/api/src/routes/dash/servers.ts index fd36317..1a5a5f7 100644 --- a/api/src/routes/dash/servers.ts +++ b/api/src/routes/dash/servers.ts @@ -1,13 +1,13 @@ import { app } from '../..'; import { Request, Response } from 'express'; -import { isAuthenticated, unauthorized } from '../../utils'; +import { isAuthenticated, requireAuth, unauthorized } from '../../utils'; import { botReq } from '../internal/ws'; type Server = { id: string, perms: 0|1|2|3, name: string, iconURL?: string, bannerURL?: string } -app.get('/dash/servers', async (req: Request, res: Response) => { - const user = await isAuthenticated(req); - if (!user) return unauthorized(res); +app.get('/dash/servers', requireAuth({ requireLogin: true }), async (req: Request, res: Response) => { + const user = await isAuthenticated(req, res, true); + if (!user) return; const response = await botReq('getUserServers', { user }); if (!response.success) { diff --git a/api/src/routes/login.ts b/api/src/routes/login.ts index bb60002..ee7a68a 100644 --- a/api/src/routes/login.ts +++ b/api/src/routes/login.ts @@ -4,7 +4,7 @@ import { Request, Response } from 'express'; import { botReq } from './internal/ws'; import { db } from '..'; import { FindOneResult } from 'monk'; -import { badRequest } from '../utils'; +import { badRequest, isAuthenticated, requireAuth } from '../utils'; import { RateLimiter } from '../middlewares/ratelimit'; class BeginReqBody { @@ -19,7 +19,12 @@ class CompleteReqBody { const beginRatelimiter = new RateLimiter('/login/begin', { limit: 10, timeframe: 300 }); const completeRatelimiter = new RateLimiter('/login/complete', { limit: 5, timeframe: 30 }); -app.post('/login/begin', (...args) => beginRatelimiter.execute(...args), async (req: Request, res: Response) => { +app.post('/login/begin', + (...args) => beginRatelimiter.execute(...args), + requireAuth({ noAuthOnly: true }), + async (req: Request, res: Response) => { + if (typeof await isAuthenticated(req) == 'string') return res.status(403).send({ error: 'You are already authenticated' }); + const body = req.body as BeginReqBody; if (!body.user || typeof body.user != 'string') return badRequest(res); @@ -30,7 +35,10 @@ app.post('/login/begin', (...args) => beginRatelimiter.execute(...args), async ( res.status(200).send({ success: true, nonce: r.nonce, code: r.code, uid: r.uid }); }); -app.post('/login/complete', (...args) => completeRatelimiter.execute(...args), async (req: Request, res: Response) => { +app.post('/login/complete', + (...args) => completeRatelimiter.execute(...args), + requireAuth({ noAuthOnly: true }), + async (req: Request, res: Response) => { const body = req.body as CompleteReqBody; if ((!body.user || typeof body.user != 'string') || (!body.nonce || typeof body.nonce != 'string') || diff --git a/api/src/utils.ts b/api/src/utils.ts index 1c278fa..98228b2 100644 --- a/api/src/utils.ts +++ b/api/src/utils.ts @@ -49,4 +49,24 @@ async function getPermissionLevel(user: string, server: string) { return await botReq('getPermissionLevel', { user, server }); } -export { isAuthenticated, getSessionInfo, badRequest, unauthorized, getPermissionLevel } +type RequireAuthConfig = { permission?: 0|1|2|3, requireLogin?: boolean, noAuthOnly?: boolean } +function requireAuth(config: RequireAuthConfig): (req: Request, res: Response, next: () => void) => void { + return async (req: Request, res: Response, next: () => void) => { + const auth = await isAuthenticated(req); + + if (config.noAuthOnly && typeof auth == 'string') return res.status(403).send({ error: 'Cannot access this route with authentication' }); + if (config.requireLogin && !auth) return unauthorized(res, 'Authentication required for this route'); + + if (config.permission != undefined) { + if (!auth) return unauthorized(res, 'Authentication required for this route'); + const server_id = req.params.serverid || req.params.server; + const levelRes = await getPermissionLevel(auth, server_id); + if (!levelRes.success) return res.status(500).send({ error: 'Unknown server or other error' }); + if (levelRes.level < config.permission) return unauthorized(res, 'Your permission level is too low'); + } + + next(); + } +} + +export { isAuthenticated, getSessionInfo, badRequest, unauthorized, getPermissionLevel, requireAuth } diff --git a/bot/src/bot/util.ts b/bot/src/bot/util.ts index 05ef034..ce629e1 100644 --- a/bot/src/bot/util.ts +++ b/bot/src/bot/util.ts @@ -100,7 +100,7 @@ async function checkSudoPermission(message: Message): Promise { } } async function getPermissionLevel(user: User|Member, server: Server): Promise<0|1|2|3> { - if (isSudo(user instanceof User ? user : (user.user || await client.users.fetch(user._id.user)))) return 2; + if (isSudo(user instanceof User ? user : (user.user || await client.users.fetch(user._id.user)))) return 3; const member = user instanceof User ? await server.fetchMember(user) : user; if (user instanceof Member) user = user.user!;