swaf/src/auth/AuthComponent.ts

138 lines
4.1 KiB
TypeScript

import {NextFunction, Request, Response} from "express";
import Application from "../Application.js";
import ApplicationComponent from "../ApplicationComponent.js";
import {route} from "../common/Routing.js";
import {ForbiddenHttpError} from "../HttpError.js";
import Middleware from "../Middleware.js";
import AuthGuard from "./AuthGuard.js";
import AuthMethod from "./AuthMethod.js";
import AuthProof from "./AuthProof.js";
import User from "./models/User.js";
export default class AuthComponent extends ApplicationComponent {
private readonly authGuard: AuthGuard;
public constructor(app: Application, ...authMethods: AuthMethod<AuthProof<User>>[]) {
super();
this.authGuard = new AuthGuard(app, ...authMethods);
}
public async initRoutes(): Promise<void> {
this.use(AuthMiddleware);
}
public getAuthGuard(): AuthGuard {
return this.authGuard;
}
}
export class AuthMiddleware extends Middleware {
private authGuard?: AuthGuard;
private user: User | null = null;
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
this.authGuard = this.app.as(AuthComponent).getAuthGuard();
const proofs = await this.authGuard.getProofsForSession(req.getSession());
if (proofs.length > 0) {
this.user = await proofs[0].getResource();
res.locals.user = this.user;
}
next();
}
public getUser(): User | null {
return this.user;
}
public getAuthGuard(): AuthGuard {
if (!this.authGuard) throw new Error('AuthGuard was not initialized.');
return this.authGuard;
}
}
export class RequireRequestAuthMiddleware extends Middleware {
private user?: User;
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
const proofs = await req.as(AuthMiddleware).getAuthGuard().getProofsForRequest(req);
const user = await proofs[0]?.getResource();
if (user) {
this.user = user;
next();
return;
}
req.flash('error', `You must be logged in to access ${req.url}.`);
res.redirect(route('auth', undefined, {
redirect_uri: req.url,
}));
}
public getUser(): User {
if (!this.user) throw new Error('user not initialized.');
return this.user;
}
}
export class RequireAuthMiddleware extends Middleware {
private user?: User;
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
const authGuard = req.as(AuthMiddleware).getAuthGuard();
// Via request
let proofs = await authGuard.getProofsForRequest(req);
let user = await proofs[0]?.getResource();
if (user) {
this.user = user;
next();
return;
}
// Via session
proofs = await authGuard.getProofsForSession(req.getSession());
user = await proofs[0]?.getResource();
if (user) {
this.user = user;
next();
return;
}
req.flash('error', `You must be logged in to access ${req.url}.`);
res.redirect(route('auth', undefined, {
redirect_uri: req.url,
}));
}
public getUser(): User {
if (!this.user) throw new Error('user not initialized.');
return this.user;
}
}
export class RequireGuestMiddleware extends Middleware {
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
const proofs = await req.as(AuthMiddleware).getAuthGuard().getProofsForSession(req.getSession());
if (proofs.length > 0) {
res.redirect(route('home'));
return;
}
next();
}
}
export class RequireAdminMiddleware extends Middleware {
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
const user = req.as(AuthMiddleware).getUser();
if (!user || !user.is_admin) {
throw new ForbiddenHttpError('secret tool', req.url);
}
next();
}
}