little authentication overhaul

This commit is contained in:
JandereDev 2022-02-07 09:02:46 +01:00
parent 8b877f4740
commit 7cf3553026
Signed by: Lea
GPG key ID: 5D5E18ACB990F57A
6 changed files with 42 additions and 24 deletions

View file

@ -1,6 +1,6 @@
import { app, db } from '../..'; import { app, db } from '../..';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { badRequest, isAuthenticated, unauthorized } from '../../utils'; import { badRequest, isAuthenticated, requireAuth, unauthorized } from '../../utils';
import { botReq } from '../internal/ws'; import { botReq } from '../internal/ws';
import { FindOneResult } from 'monk'; import { FindOneResult } from 'monk';
@ -13,7 +13,7 @@ type AntispamRule = {
message: string | null; 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); const user = await isAuthenticated(req, res, true);
if (!user) return; if (!user) return;
@ -48,7 +48,7 @@ app.get('/dash/server/:server/automod', async (req: Request, res: Response) => {
res.send(result); 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); const user = await isAuthenticated(req, res, true);
if (!user) return; if (!user) return;
@ -56,16 +56,6 @@ app.patch('/dash/server/:server/automod/:ruleid', async (req: Request, res: Resp
const body = req.body; const body = req.body;
if (!server || !ruleid) return badRequest(res); 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<any> = await db.get('servers').findOne({ id: server }); const serverConfig: FindOneResult<any> = await db.get('servers').findOne({ id: server });
const antiSpamRules: AntispamRule[] = serverConfig.automodSettings?.spam ?? []; const antiSpamRules: AntispamRule[] = serverConfig.automodSettings?.spam ?? [];

View file

@ -1,6 +1,6 @@
import { app, db } from '../..'; import { app, db } from '../..';
import { Request, Response } from 'express'; 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'; import { botReq } from '../internal/ws';
type User = { id: string, username?: string, avatarURL?: string } type User = { id: string, username?: string, avatarURL?: string }
@ -18,7 +18,7 @@ type ServerDetails = {
channels: Channel[], 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); const user = await isAuthenticated(req, res, true);
if (!user) return; if (!user) return;

View file

@ -1,13 +1,13 @@
import { app } from '../..'; import { app } from '../..';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { isAuthenticated, unauthorized } from '../../utils'; import { isAuthenticated, requireAuth, unauthorized } from '../../utils';
import { botReq } from '../internal/ws'; import { botReq } from '../internal/ws';
type Server = { id: string, perms: 0|1|2|3, name: string, iconURL?: string, bannerURL?: string } type Server = { id: string, perms: 0|1|2|3, name: string, iconURL?: string, bannerURL?: string }
app.get('/dash/servers', async (req: Request, res: Response) => { app.get('/dash/servers', requireAuth({ requireLogin: true }), async (req: Request, res: Response) => {
const user = await isAuthenticated(req); const user = await isAuthenticated(req, res, true);
if (!user) return unauthorized(res); if (!user) return;
const response = await botReq('getUserServers', { user }); const response = await botReq('getUserServers', { user });
if (!response.success) { if (!response.success) {

View file

@ -4,7 +4,7 @@ import { Request, Response } from 'express';
import { botReq } from './internal/ws'; import { botReq } from './internal/ws';
import { db } from '..'; import { db } from '..';
import { FindOneResult } from 'monk'; import { FindOneResult } from 'monk';
import { badRequest } from '../utils'; import { badRequest, isAuthenticated, requireAuth } from '../utils';
import { RateLimiter } from '../middlewares/ratelimit'; import { RateLimiter } from '../middlewares/ratelimit';
class BeginReqBody { class BeginReqBody {
@ -19,7 +19,12 @@ class CompleteReqBody {
const beginRatelimiter = new RateLimiter('/login/begin', { limit: 10, timeframe: 300 }); const beginRatelimiter = new RateLimiter('/login/begin', { limit: 10, timeframe: 300 });
const completeRatelimiter = new RateLimiter('/login/complete', { limit: 5, timeframe: 30 }); 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; const body = req.body as BeginReqBody;
if (!body.user || typeof body.user != 'string') return badRequest(res); 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 }); 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; const body = req.body as CompleteReqBody;
if ((!body.user || typeof body.user != 'string') || if ((!body.user || typeof body.user != 'string') ||
(!body.nonce || typeof body.nonce != 'string') || (!body.nonce || typeof body.nonce != 'string') ||

View file

@ -49,4 +49,24 @@ async function getPermissionLevel(user: string, server: string) {
return await botReq('getPermissionLevel', { user, server }); 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 }

View file

@ -100,7 +100,7 @@ async function checkSudoPermission(message: Message): Promise<boolean> {
} }
} }
async function getPermissionLevel(user: User|Member, server: Server): Promise<0|1|2|3> { 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; const member = user instanceof User ? await server.fetchMember(user) : user;
if (user instanceof Member) user = user.user!; if (user instanceof Member) user = user.user!;