user permissions WIP
This commit is contained in:
parent
0c3a3546e6
commit
e2b8989ec3
@ -1,17 +1,100 @@
|
|||||||
import { ErrorRequestHandler, Request, RequestHandler } from 'express';
|
import { ErrorRequestHandler, Request, RequestHandler } from 'express';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
|
import { validationResult } from 'express-validator';
|
||||||
|
import { UserInfo, UserRoles } from '@core';
|
||||||
|
import permissions from './permissions';
|
||||||
|
|
||||||
export function getId(req: Request): string {
|
declare module 'express-session' {
|
||||||
return req.get('request-id') || 'unknown';
|
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) => {
|
return (req, res, next) => {
|
||||||
req.headers['request-id'] = randomUUID();
|
req.headers['request-id'] = randomUUID();
|
||||||
next();
|
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 {
|
export function ErrorHandler(): ErrorRequestHandler {
|
||||||
return (error, req, res, next) => {
|
return (error, req, res, next) => {
|
||||||
error.status
|
error.status
|
||||||
|
10
api/src/framework/express/permissions.ts
Normal file
10
api/src/framework/express/permissions.ts
Normal 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;
|
38
api/src/framework/express/schema/user.ts
Normal file
38
api/src/framework/express/schema/user.ts
Normal 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)],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -1,9 +1,11 @@
|
|||||||
import express, { Express } from 'express';
|
import express, { Express } from 'express';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import * as router from './router';
|
import session from 'express-session';
|
||||||
import { BeforeEach, ErrorHandler } from './middleware';
|
import { getRoutes } from './router';
|
||||||
|
import { RequestId, ErrorHandler, checkPermissions } from './middleware';
|
||||||
import { Services } from '../../app';
|
import { Services } from '../../app';
|
||||||
import { ServerConfig } from '../../config';
|
import { ServerConfig } from '../../config';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private app: Express;
|
private app: Express;
|
||||||
@ -15,8 +17,12 @@ class Server {
|
|||||||
this.app = express();
|
this.app = express();
|
||||||
this.app.use(express.json());
|
this.app.use(express.json());
|
||||||
this.app.use(cors());
|
this.app.use(cors());
|
||||||
this.app.use(BeforeEach());
|
this.app.use(RequestId());
|
||||||
this.app.use(router.getRoutes(services));
|
this.app.use(
|
||||||
|
session({ secret: randomUUID(), cookie: { maxAge: 1000 * 3600 * 24 } }),
|
||||||
|
);
|
||||||
|
this.app.use(checkPermissions());
|
||||||
|
this.app.use(getRoutes(services));
|
||||||
this.app.use(ErrorHandler());
|
this.app.use(ErrorHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,39 @@
|
|||||||
import { RequestHandler, Router } from 'express';
|
import { RequestHandler, Router } from 'express';
|
||||||
import { Services } from '../../app';
|
import { Services } from '../../app';
|
||||||
import { Create, Read, Login } from '../../functions/user';
|
import { CreateUser, ReadUser, LoginUser } from '../../functions/user';
|
||||||
import { getId } from './middleware';
|
import {
|
||||||
|
LoginUserSchema,
|
||||||
|
CreateUserSchema,
|
||||||
|
ReadUserSchema,
|
||||||
|
LogoutUserSchema,
|
||||||
|
} from './schema/user';
|
||||||
|
import { getId, SchemaValidator } from './middleware';
|
||||||
|
|
||||||
export function LoginHandler(services: Services): RequestHandler {
|
function LoginHandler(services: Services): RequestHandler {
|
||||||
const login = Login(services);
|
const login = LoginUser(services);
|
||||||
|
|
||||||
return async (req, res, next) => {
|
return async (req, res, next) => {
|
||||||
const user = await login(getId(req), req.body);
|
const user = await login(getId(req), req.body);
|
||||||
|
user ? (req.session.user = user) : (req.session.user = null);
|
||||||
user
|
user
|
||||||
? res.status(200).send(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 {
|
function LogoutHandler(services: Services): RequestHandler {
|
||||||
const createUser = Create(services);
|
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) => {
|
return async (req, res, next) => {
|
||||||
const user = await createUser(getId(req), req.body);
|
const user = await createUser(getId(req), req.body);
|
||||||
@ -23,8 +41,8 @@ export function CreateHandler(services: Services): RequestHandler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReadHandler(services: Services): RequestHandler {
|
function ReadHandler(services: Services): RequestHandler {
|
||||||
const readUser = Read(services);
|
const readUser = ReadUser(services);
|
||||||
|
|
||||||
return async (req, res, next) => {
|
return async (req, res, next) => {
|
||||||
const user = await readUser(getId(req), req.params.uuid);
|
const user = await readUser(getId(req), req.params.uuid);
|
||||||
@ -37,9 +55,31 @@ export function ReadHandler(services: Services): RequestHandler {
|
|||||||
export function getRoutes(services: Services) {
|
export function getRoutes(services: Services) {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/login', LoginHandler(services));
|
router.get(
|
||||||
router.get('/read/:uuid', ReadHandler(services));
|
'/read/:uuid',
|
||||||
router.post('/create', CreateHandler(services));
|
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;
|
return router;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { CreateUserBody, LoginUserBody, UserInfo } from '@core';
|
|||||||
import { Services } from '../app';
|
import { Services } from '../app';
|
||||||
import { UserWithPassword } from '../entities/user';
|
import { UserWithPassword } from '../entities/user';
|
||||||
|
|
||||||
export function Login(
|
export function LoginUser(
|
||||||
services: Services,
|
services: Services,
|
||||||
): (tracker: string, raw: LoginUserBody) => Promise<UserInfo | null> {
|
): (tracker: string, raw: LoginUserBody) => Promise<UserInfo | null> {
|
||||||
const { userModel } = services;
|
const { userModel } = services;
|
||||||
@ -13,7 +13,7 @@ export function Login(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Create(
|
export function CreateUser(
|
||||||
services: Services,
|
services: Services,
|
||||||
): (tracker: string, raw: CreateUserBody) => Promise<UserInfo | null> {
|
): (tracker: string, raw: CreateUserBody) => Promise<UserInfo | null> {
|
||||||
const { userModel } = services;
|
const { userModel } = services;
|
||||||
@ -24,7 +24,7 @@ export function Create(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Read(
|
export function ReadUser(
|
||||||
services: Services,
|
services: Services,
|
||||||
): (tracker: string, uuid: string) => Promise<UserInfo | null> {
|
): (tracker: string, uuid: string) => Promise<UserInfo | null> {
|
||||||
const { userModel } = services;
|
const { userModel } = services;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user