token-permissions: update authRoutes

This commit is contained in:
split / May 2023-10-02 19:14:00 -07:00
parent fad320d7fb
commit 2006fa1cca
3 changed files with 42 additions and 34 deletions

View file

@ -1,4 +1,5 @@
import crypto from "crypto" import crypto from "crypto"
import express from "express"
import { readFile, writeFile } from "fs/promises" import { readFile, writeFile } from "fs/promises"
export let AuthTokens: AuthToken[] = [] export let AuthTokens: AuthToken[] = []
export let AuthTokenTO:{[key:string]:NodeJS.Timeout} = {} export let AuthTokenTO:{[key:string]:NodeJS.Timeout} = {}
@ -6,8 +7,10 @@ export let AuthTokenTO:{[key:string]:NodeJS.Timeout} = {}
export const ValidTokenPermissions = [ export const ValidTokenPermissions = [
"user", // permissions to /auth/me, with email docked "user", // permissions to /auth/me, with email docked
"email", // adds email back to /auth/me "email", // adds email back to /auth/me
"private", // allows app to read private files
"upload", // allows an app to upload under an account "upload", // allows an app to upload under an account
"manage", // allows an app to manage an account's files "manage", // allows an app to manage an account's files
"customize", // allows an app to change customization settings
"admin" // only available for accounts with admin "admin" // only available for accounts with admin
// gives an app access to all admin tools // gives an app access to all admin tools
] as const ] as const
@ -48,6 +51,14 @@ export function create(
return token.token return token.token
} }
export function tokenFor(req: express.Request) {
return req.cookies.auth || (
req.header("authorization")?.startsWith("Bearer ")
? req.header("authorization")?.split(" ")[1]
: undefined
)
}
export function validate(token:string) { export function validate(token:string) {
return AuthTokens.find(e => e.token == token && Date.now() < e.expire)?.account return AuthTokens.find(e => e.token == token && Date.now() < e.expire)?.account
} }

View file

@ -3,16 +3,8 @@ import express, { type RequestHandler } from "express"
import ServeError from "../lib/errors"; import ServeError from "../lib/errors";
import * as auth from "./auth"; import * as auth from "./auth";
function tokenFor(req: express.Request) {
return req.cookies.auth || (
req.header("authorization")?.startsWith("Bearer ")
? req.header("authorization")?.split(" ")[1]
: undefined
)
}
export const getAccount: RequestHandler = function(req, res, next) { export const getAccount: RequestHandler = function(req, res, next) {
res.locals.acc = Accounts.getFromToken(tokenFor(req)) res.locals.acc = Accounts.getFromToken(auth.tokenFor(req))
next() next()
} }
@ -40,7 +32,7 @@ export const requiresAdmin: RequestHandler = function(_req, res, next) {
export const requiresPermissions = function(...tokenPermissions: auth.TokenPermission[]): RequestHandler { export const requiresPermissions = function(...tokenPermissions: auth.TokenPermission[]): RequestHandler {
return function(req, res, next) { return function(req, res, next) {
let token = tokenFor(req) let token = auth.tokenFor(req)
let type = auth.getType(token) let type = auth.getType(token)
if (type == "App") { if (type == "App") {
@ -67,6 +59,6 @@ export const requiresPermissions = function(...tokenPermissions: auth.TokenPermi
*/ */
export const noAPIAccess: RequestHandler = function(req, res, next) { export const noAPIAccess: RequestHandler = function(req, res, next) {
if (auth.getType(tokenFor(req)) == "App") ServeError(res, 403, "apps are not allowed to access this endpoint") if (auth.getType(auth.tokenFor(req)) == "App") ServeError(res, 403, "apps are not allowed to access this endpoint")
else next() else next()
} }

View file

@ -3,7 +3,7 @@ import { Router } from "express";
import * as Accounts from "../lib/accounts"; import * as Accounts from "../lib/accounts";
import * as auth from "../lib/auth"; import * as auth from "../lib/auth";
import { sendMail } from "../lib/mail"; import { sendMail } from "../lib/mail";
import { getAccount, requiresAccount } from "../lib/middleware" import { getAccount, noAPIAccess, requiresAccount, requiresPermissions } from "../lib/middleware"
import { accountRatelimit } from "../lib/ratelimit" import { accountRatelimit } from "../lib/ratelimit"
import ServeError from "../lib/errors"; import ServeError from "../lib/errors";
@ -123,7 +123,7 @@ authRoutes.post("/logout", (req,res) => {
res.send("logged out") res.send("logged out")
}) })
authRoutes.post("/dfv", requiresAccount, parser, (req,res) => { authRoutes.post("/dfv", requiresAccount, requiresPermissions("manage"), parser, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
if (['public','private','anonymous'].includes(req.body.defaultFileVisibility)) { if (['public','private','anonymous'].includes(req.body.defaultFileVisibility)) {
@ -136,7 +136,7 @@ authRoutes.post("/dfv", requiresAccount, parser, (req,res) => {
} }
}) })
authRoutes.post("/customcss", requiresAccount, parser, (req,res) => { authRoutes.post("/customcss", requiresAccount, requiresPermissions("customize"), parser, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
if (typeof req.body.fileId != "string") req.body.fileId = undefined; if (typeof req.body.fileId != "string") req.body.fileId = undefined;
@ -158,7 +158,7 @@ authRoutes.post("/customcss", requiresAccount, parser, (req,res) => {
} }
}) })
authRoutes.post("/embedcolor", requiresAccount, parser, (req,res) => { authRoutes.post("/embedcolor", requiresAccount, requiresPermissions("customize"), parser, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
if (typeof req.body.color != "string") req.body.color = undefined; if (typeof req.body.color != "string") req.body.color = undefined;
@ -181,7 +181,7 @@ authRoutes.post("/embedcolor", requiresAccount, parser, (req,res) => {
} }
}) })
authRoutes.post("/embedsize", requiresAccount, parser, (req,res) => { authRoutes.post("/embedsize", requiresAccount, requiresPermissions("customize"), parser, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
if (typeof req.body.largeImage != "boolean") req.body.color = false; if (typeof req.body.largeImage != "boolean") req.body.color = false;
@ -193,7 +193,7 @@ authRoutes.post("/embedsize", requiresAccount, parser, (req,res) => {
res.send(`custom embed image size saved`) res.send(`custom embed image size saved`)
}) })
authRoutes.post("/delete_account", requiresAccount, parser, async (req,res) => { authRoutes.post("/delete_account", requiresAccount, noAPIAccess, parser, async (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
let accId = acc.id let accId = acc.id
@ -217,7 +217,7 @@ authRoutes.post("/delete_account", requiresAccount, parser, async (req,res) => {
} else cpl() } else cpl()
}) })
authRoutes.post("/change_username", requiresAccount, parser, (req,res) => { authRoutes.post("/change_username", requiresAccount, noAPIAccess, parser, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
if (typeof req.body.username != "string" || req.body.username.length < 3 || req.body.username.length > 20) { if (typeof req.body.username != "string" || req.body.username.length < 3 || req.body.username.length > 20) {
@ -253,7 +253,7 @@ authRoutes.post("/change_username", requiresAccount, parser, (req,res) => {
let verificationCodes = new Map<string, {code: string, email: string, expiry: NodeJS.Timeout}>() let verificationCodes = new Map<string, {code: string, email: string, expiry: NodeJS.Timeout}>()
authRoutes.post("/request_email_change", requiresAccount, accountRatelimit({ requests: 4, per: 60*60*1000 }), parser, (req,res) => { authRoutes.post("/request_email_change", requiresAccount, noAPIAccess, accountRatelimit({ requests: 4, per: 60*60*1000 }), parser, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
@ -292,7 +292,7 @@ authRoutes.post("/request_email_change", requiresAccount, accountRatelimit({ req
}) })
}) })
authRoutes.get("/confirm_email/:code", requiresAccount, (req,res) => { authRoutes.get("/confirm_email/:code", requiresAccount, noAPIAccess, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
@ -314,7 +314,7 @@ authRoutes.get("/confirm_email/:code", requiresAccount, (req,res) => {
} }
}) })
authRoutes.post("/remove_email", requiresAccount, (req,res) => { authRoutes.post("/remove_email", requiresAccount, noAPIAccess, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
if (acc.email) { if (acc.email) {
@ -404,7 +404,7 @@ authRoutes.get("/emergency_login/:code", (req,res) => {
} }
}) })
authRoutes.post("/change_password", requiresAccount, parser, (req,res) => { authRoutes.post("/change_password", requiresAccount, noAPIAccess, parser, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
if (typeof req.body.password != "string" || req.body.password.length < 8) { if (typeof req.body.password != "string" || req.body.password.length < 8) {
@ -429,7 +429,7 @@ authRoutes.post("/change_password", requiresAccount, parser, (req,res) => {
res.send("password changed - logged out all sessions") res.send("password changed - logged out all sessions")
}) })
authRoutes.post("/logout_sessions", requiresAccount, (req,res) => { authRoutes.post("/logout_sessions", requiresAccount, noAPIAccess, (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
let accId = acc.id let accId = acc.id
@ -441,15 +441,20 @@ authRoutes.post("/logout_sessions", requiresAccount, (req,res) => {
res.send("logged out all sessions") res.send("logged out all sessions")
}) })
authRoutes.get("/me", requiresAccount, (req,res) => { authRoutes.get("/me", requiresAccount, requiresPermissions("user"), (req,res) => {
let acc = res.locals.acc as Accounts.Account let acc = res.locals.acc as Accounts.Account
let sessionToken = auth.tokenFor(req)
let accId = acc.id let accId = acc.id
res.send({ res.send({
...acc, ...acc,
sessionCount: auth.AuthTokens.filter(e => e.account == accId && e.expire > Date.now()).length, sessionCount: auth.AuthTokens.filter(e => e.type != "App" && e.account == accId && e.expire > Date.now()).length,
sessionExpires: auth.AuthTokens.find(e => e.token == req.cookies.auth)?.expire, sessionExpires: auth.AuthTokens.find(e => e.token == sessionToken)?.expire,
password: undefined password: undefined,
email:
auth.getType(sessionToken) == "User" || auth.getPermissions(sessionToken)?.includes("email")
? acc.email
: undefined
}) })
}) })