user permissions WIP

This commit is contained in:
Yanis Rigaudeau 2022-10-17 00:27:21 +02:00
parent 0c3a3546e6
commit e2b8989ec3
Signed by: yanis
GPG Key ID: 4DD2841DF1C94D83
6 changed files with 199 additions and 22 deletions

View File

@ -1,17 +1,100 @@
import { ErrorRequestHandler, Request, RequestHandler } from 'express';
import { randomUUID } from 'crypto';
import { validationResult } from 'express-validator';
import { UserInfo, UserRoles } from '@core';
import permissions from './permissions';
export function getId(req: Request): string {
return req.get('request-id') || 'unknown';
declare module 'express-session' {
interface SessionData {
user: UserInfo | null;
}
}
export function BeforeEach(): RequestHandler {
export function getId(req: Request): string {
return req.header('request-id') || 'unknown';
}
export function RequestId(): RequestHandler {
return (req, res, next) => {
req.headers['request-id'] = randomUUID();
next();
};
}
export function checkPermissions(): RequestHandler {
const getRoute = (url: string): string => {
for (const route in permissions) {
if (url.startsWith(route)) return route;
}
return '';
};
const canAccess = (req: Request): boolean => {
const user = req.session.user;
if (!user) return false;
//Logout
if (req.url === '/user/logout') {
return true;
}
//User Imself
if (req.params.uuid === user.uuid) {
return true;
}
return false;
};
return (req, res, next) => {
const route = getRoute(req.url);
console.log(canAccess(req));
console.log(route);
if (!req.session.user && req.url === '/user/login') {
next();
return;
}
if (!req.session.user) {
next({ status: 403, messsage: 'Forbidden' });
return;
}
if (
!(route in permissions) ||
(req.session.user.role !== permissions[route] &&
req.session.user.role !== UserRoles.ADMIN) ||
(!canAccess(req) && req.session.user.role !== UserRoles.ADMIN)
) {
next({ status: 403, messsage: 'Forbidden' });
return;
}
if (
req.session.user.role === UserRoles.ADMIN ||
(req.session.user.role === permissions[route] && canAccess(req))
) {
next();
return;
}
next({ status: 403, messsage: 'Forbidden' });
};
}
export function SchemaValidator(): RequestHandler {
return (req, res, next) => {
const error = validationResult(req);
error.isEmpty()
? next()
: next({
status: 400,
...error,
});
};
}
export function ErrorHandler(): ErrorRequestHandler {
return (error, req, res, next) => {
error.status

View File

@ -0,0 +1,10 @@
import { UserRoles } from '@core';
const permissions = {
'/user/login': UserRoles.USER,
'/user/logout': UserRoles.USER,
'/user/read': UserRoles.USER,
'/user/create': UserRoles.ADMIN,
};
export default permissions;

View File

@ -0,0 +1,38 @@
import { checkSchema } from 'express-validator';
import { UserRoles } from '@core';
export const ReadUserSchema = () =>
checkSchema({
uuid: {
isUUID: {
options: 4,
},
},
});
export const LoginUserSchema = () =>
checkSchema({
username: {
isString: true,
},
password: {
isString: true,
},
});
export const LogoutUserSchema = () => checkSchema({});
export const CreateUserSchema = () =>
checkSchema({
username: {
isString: true,
},
password: {
isString: true,
},
role: {
isIn: {
options: [Object.values(UserRoles)],
},
},
});

View File

@ -1,9 +1,11 @@
import express, { Express } from 'express';
import cors from 'cors';
import * as router from './router';
import { BeforeEach, ErrorHandler } from './middleware';
import session from 'express-session';
import { getRoutes } from './router';
import { RequestId, ErrorHandler, checkPermissions } from './middleware';
import { Services } from '../../app';
import { ServerConfig } from '../../config';
import { randomUUID } from 'crypto';
class Server {
private app: Express;
@ -15,8 +17,12 @@ class Server {
this.app = express();
this.app.use(express.json());
this.app.use(cors());
this.app.use(BeforeEach());
this.app.use(router.getRoutes(services));
this.app.use(RequestId());
this.app.use(
session({ secret: randomUUID(), cookie: { maxAge: 1000 * 3600 * 24 } }),
);
this.app.use(checkPermissions());
this.app.use(getRoutes(services));
this.app.use(ErrorHandler());
}

View File

@ -1,21 +1,39 @@
import { RequestHandler, Router } from 'express';
import { Services } from '../../app';
import { Create, Read, Login } from '../../functions/user';
import { getId } from './middleware';
import { CreateUser, ReadUser, LoginUser } from '../../functions/user';
import {
LoginUserSchema,
CreateUserSchema,
ReadUserSchema,
LogoutUserSchema,
} from './schema/user';
import { getId, SchemaValidator } from './middleware';
export function LoginHandler(services: Services): RequestHandler {
const login = Login(services);
function LoginHandler(services: Services): RequestHandler {
const login = LoginUser(services);
return async (req, res, next) => {
const user = await login(getId(req), req.body);
user ? (req.session.user = user) : (req.session.user = null);
user
? res.status(200).send(user)
: next({ status: 404, message: 'bad user or password' });
: next({ status: 404, message: 'wrong user or password' });
};
}
export function CreateHandler(services: Services): RequestHandler {
const createUser = Create(services);
function LogoutHandler(services: Services): RequestHandler {
return async (req, res, next) => {
if (req.session.user) {
req.session.user = null;
res.status(204).send();
} else {
next({ message: 'not logged in' });
}
};
}
function CreateHandler(services: Services): RequestHandler {
const createUser = CreateUser(services);
return async (req, res, next) => {
const user = await createUser(getId(req), req.body);
@ -23,8 +41,8 @@ export function CreateHandler(services: Services): RequestHandler {
};
}
export function ReadHandler(services: Services): RequestHandler {
const readUser = Read(services);
function ReadHandler(services: Services): RequestHandler {
const readUser = ReadUser(services);
return async (req, res, next) => {
const user = await readUser(getId(req), req.params.uuid);
@ -37,9 +55,31 @@ export function ReadHandler(services: Services): RequestHandler {
export function getRoutes(services: Services) {
const router = Router();
router.post('/login', LoginHandler(services));
router.get('/read/:uuid', ReadHandler(services));
router.post('/create', CreateHandler(services));
router.get(
'/read/:uuid',
ReadUserSchema(),
SchemaValidator(),
ReadHandler(services),
);
router.post(
'/login',
LoginUserSchema(),
SchemaValidator(),
LoginHandler(services),
);
router.post(
'/logout',
LogoutUserSchema(),
SchemaValidator(),
LogoutHandler(services),
);
router.post(
'/create',
CreateUserSchema(),
SchemaValidator(),
CreateHandler(services),
);
return router;
}

View File

@ -2,7 +2,7 @@ import { CreateUserBody, LoginUserBody, UserInfo } from '@core';
import { Services } from '../app';
import { UserWithPassword } from '../entities/user';
export function Login(
export function LoginUser(
services: Services,
): (tracker: string, raw: LoginUserBody) => Promise<UserInfo | null> {
const { userModel } = services;
@ -13,7 +13,7 @@ export function Login(
};
}
export function Create(
export function CreateUser(
services: Services,
): (tracker: string, raw: CreateUserBody) => Promise<UserInfo | null> {
const { userModel } = services;
@ -24,7 +24,7 @@ export function Create(
};
}
export function Read(
export function ReadUser(
services: Services,
): (tracker: string, uuid: string) => Promise<UserInfo | null> {
const { userModel } = services;