v3: added a roles/permissions system, added user management tab on user profile
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 42s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 42s
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
import { Context, Next, State } from "@oak/oak";
|
||||
import {
|
||||
type Next,
|
||||
type RouteParams,
|
||||
type RouterContext,
|
||||
type State,
|
||||
} from "@oak/oak";
|
||||
import { verifyJWT } from "../lib/jwt.ts";
|
||||
import {
|
||||
APIErrorCode,
|
||||
@@ -6,16 +11,24 @@ import {
|
||||
type AuthPayload,
|
||||
} from "../model/interfaces.ts";
|
||||
import { getUserById } from "../services/user-service.ts";
|
||||
|
||||
export interface AuthContext extends Context {
|
||||
state: AuthState;
|
||||
}
|
||||
import { can, type Permission } from "../lib/permissions.ts";
|
||||
|
||||
export interface AuthState extends State {
|
||||
user: AuthPayload;
|
||||
}
|
||||
|
||||
export async function authMiddleware(ctx: AuthContext, next: Next) {
|
||||
// A router context whose state is guaranteed to carry the authenticated user
|
||||
// (populated by authMiddleware). Generic over the route path so `ctx.params`
|
||||
// stays typed per-route — e.g. `AuthContext<"/:userId/role">`.
|
||||
export type AuthContext<
|
||||
R extends string = string,
|
||||
P extends RouteParams<R> = RouteParams<R>,
|
||||
> = RouterContext<R, P, AuthState>;
|
||||
|
||||
export async function authMiddleware<R extends string>(
|
||||
ctx: AuthContext<R>,
|
||||
next: Next,
|
||||
): Promise<void> {
|
||||
const authHeader = ctx.request.headers.get("Authorization");
|
||||
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
@@ -44,8 +57,11 @@ export async function authMiddleware(ctx: AuthContext, next: Next) {
|
||||
await next();
|
||||
}
|
||||
|
||||
export async function adminOnlyMiddleware(ctx: AuthContext, next: Next) {
|
||||
if (!ctx.state.user?.isAdmin) {
|
||||
export async function adminOnlyMiddleware<R extends string>(
|
||||
ctx: AuthContext<R>,
|
||||
next: Next,
|
||||
): Promise<void> {
|
||||
if (ctx.state.user.role !== "admin") {
|
||||
throw new APIException(
|
||||
APIErrorCode.UNAUTHORIZED,
|
||||
403,
|
||||
@@ -55,3 +71,21 @@ export async function adminOnlyMiddleware(ctx: AuthContext, next: Next) {
|
||||
|
||||
await next();
|
||||
}
|
||||
|
||||
// Gates a route on a named permission. Must run after authMiddleware. Generic
|
||||
// over the route path so it composes with path-typed route handlers.
|
||||
export function requirePermission(permission: Permission) {
|
||||
return async <R extends string>(
|
||||
ctx: AuthContext<R>,
|
||||
next: Next,
|
||||
): Promise<void> => {
|
||||
if (!ctx.state.user || !can(ctx.state.user, permission)) {
|
||||
throw new APIException(
|
||||
APIErrorCode.UNAUTHORIZED,
|
||||
403,
|
||||
"Insufficient permissions",
|
||||
);
|
||||
}
|
||||
await next();
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user