UNPKG

security-express

Version:
107 lines (103 loc) 3.51 kB
import { NextFunction, Request, Response } from "express" export type Handle = (req: Request, res: Response, next: NextFunction) => void export type Authorize = (privilege: string, action?: number) => Handle export interface SimpleMap { [key: string]: string | number | boolean | Date } // tslint:disable-next-line:max-classes-per-file export class Authorizer { protected userId: string protected permissions: string constructor( protected privilege: (userId: string, privilegeId: string) => Promise<number>, protected logError: (msg: string, m?: SimpleMap, ctx?: any) => void, protected exact?: boolean, userId?: string, permissions?: string, ) { this.userId = userId ? userId : "userId" this.permissions = permissions ? permissions : "permissions" this.exact = exact !== undefined ? exact : true this.authorize = this.authorize.bind(this) } authorize(privilege: string, action?: number): Handle { return (req: Request, res: Response, next: NextFunction) => { const userId = res.locals[this.userId] if (!userId) { res.status(401).end("Authentication is required or session has expired.") } else { this.privilege(userId, privilege) .then((p) => { if (p === none) { res.status(403).end("no permission for " + userId) } else { res.locals[this.permissions] = p if (!action) { next() } else { if (this.exact) { // tslint:disable-next-line:no-bitwise const sum = action & p if (sum === action) { return next() } else { res.status(403).end("no permission") } } else { if (p >= action) { return next() } else { res.status(403).end("no permission") } } } } }) .catch((err) => { this.logError(`Error in privilege check for user ${userId} with privilege ${privilege}. Details: ${toString(err)}`) res.status(403).end("no permission") }) } } } } export const none = 0 export const read = 1 export const write = 2 export const approve = 4 export const all = 2147483647 // tslint:disable-next-line:max-classes-per-file export class PrivilegeLoader { constructor( public sql: string, public query: <T>(sql: string, args?: any[]) => Promise<T[]>, ) { this.privilege = this.privilege.bind(this) } privilege(userId: string, privilegeId: string): Promise<number> { return this.query<any>(this.sql, [userId, privilegeId]).then((v) => { if (!v || v.length === 0) { return none } const keys = Object.keys(v[0]) if (keys.length === 0) { return all } const k: string = keys[0] let permissions = 0 let ok = false for (const p of v) { const x = p[k] if (typeof x === "number") { // tslint:disable-next-line:no-bitwise permissions = permissions | x ok = true } } return ok ? permissions : all }) } } export function toString(err: any): string { return typeof err === "string" ? err : JSON.stringify(err) }