diff --git a/back-express/src/app.ts b/back-express/src/app.ts index aaf2cca..85bde5e 100644 --- a/back-express/src/app.ts +++ b/back-express/src/app.ts @@ -1,7 +1,8 @@ import express from "express"; import config from "./config"; import cors from "cors"; -import { routes } from "./routes"; +import { routes } from "./modules"; +import { authorizationMiddleware } from "./modules/auth/auth.middleware"; const app = express(); const port = config.port; @@ -12,6 +13,7 @@ if (config.enableCors) { app.use(express.json()); // Global middleware +app.use(authorizationMiddleware as any); app.use((req, _res, next) => { console.log(`LOG: new ${req.method} request for ${req.url}`); next(); @@ -23,7 +25,7 @@ app.use("/example", (req, _res, next) => { next(); }); -app.use("/api/v1", routes); // / serves as the base for the imported routes +app.use(config.baseRoute, routes); // / serves as the base for the imported routes app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); diff --git a/back-express/src/config.ts b/back-express/src/config.ts index f520a1e..a86f5fe 100644 --- a/back-express/src/config.ts +++ b/back-express/src/config.ts @@ -5,6 +5,7 @@ configDotenv(); const config = { enableCors: false, port: process.env.PORT || 3000, + baseRoute: "/api/v1", }; export default config; diff --git a/back-express/src/routes/auth/auth.constants.ts b/back-express/src/modules/auth/auth.constants.ts similarity index 100% rename from back-express/src/routes/auth/auth.constants.ts rename to back-express/src/modules/auth/auth.constants.ts diff --git a/back-express/src/modules/auth/auth.middleware.ts b/back-express/src/modules/auth/auth.middleware.ts new file mode 100644 index 0000000..dd527d9 --- /dev/null +++ b/back-express/src/modules/auth/auth.middleware.ts @@ -0,0 +1,63 @@ +import { NextFunction, Request, Response } from "express"; +import { authService } from "./auth.routes"; +import { Role } from "../users/users.types"; +import { ResponseError } from "../../utils/response/response-error.model"; +import config from "../../config"; + +type Permissions = { + roles: Array; + resource: string; + actions: Array<"get" | "put" | "post" | "delete" | "config">; +}; + +async function authorization(req: Request, res: Response, next: NextFunction) { + const resource = req.url.replace(config.baseRoute, ""); + const isPublic = !!authorizationTable + .find((permission) => permission.resource.startsWith(resource)) + ?.roles.includes(Role.Public); + + if (isPublic) { + return next(); + } + const authHeader = req.get("authorization"); + const token = authHeader && authHeader.split(" ")[1]; + + if (token == null) { + res.status(401); + return res.send(new ResponseError(401)); + } + + const user = authService.verifyToken(token); + if (user && user instanceof Object) { + // FIXME: this check is wrong, needs to take into account the resource + const authorized = authorizationTable.find((auth) => + user.roles.some((role) => auth.roles.includes(role)), + ); + if (authorized) { + return next(); + } + } + res.status(401); + return res.send(new ResponseError(401)); +} + +// TODO: resource should be a regex +const authorizationTable: Permissions[] = [ + { + roles: [Role.Public], + resource: "/auth/login", + actions: ["post"], + }, + { + roles: [Role.Admin], + resource: "/users", + actions: ["get", "put", "post", "delete"], + }, + { + roles: [Role.User], + resource: "/users", + actions: ["get"], + }, +]; + +export { authorization as authorizationMiddleware }; diff --git a/back-express/src/routes/auth/auth.routes.ts b/back-express/src/modules/auth/auth.routes.ts similarity index 94% rename from back-express/src/routes/auth/auth.routes.ts rename to back-express/src/modules/auth/auth.routes.ts index 272402c..2ac4c43 100644 --- a/back-express/src/routes/auth/auth.routes.ts +++ b/back-express/src/modules/auth/auth.routes.ts @@ -19,7 +19,7 @@ authRoutes.post("/login", async (req, res) => { ); if (token) { res.status(200); - res.send(token); + res.send({ access_token: token }); } else { res.status(401); res.send(new ResponseError(401)); diff --git a/back-express/src/routes/auth/auth.service.ts b/back-express/src/modules/auth/auth.service.ts similarity index 71% rename from back-express/src/routes/auth/auth.service.ts rename to back-express/src/modules/auth/auth.service.ts index 63e6e9c..6a1deea 100644 --- a/back-express/src/routes/auth/auth.service.ts +++ b/back-express/src/modules/auth/auth.service.ts @@ -3,6 +3,7 @@ import UsersService from "../users/users.service"; import { compare } from "bcrypt"; import { generateRandomString } from "./auth.utils"; import { AUTH_KEY_SIZE } from "./auth.constants"; +import { Role } from "../users/users.types"; export class AuthService { private secret_key: string; @@ -11,7 +12,6 @@ export class AuthService { } async signIn(username: string, password: string) { - console.log("__LOGIN", { username, password }); const user = await this.usersService.getUserByUsername(username); if (!user) { @@ -29,17 +29,21 @@ export class AuthService { picture: user.picture, }; - const token = sign(payload, this.secret_key, { expiresIn: "1h" }); + const token = sign(payload, this.secret_key, { expiresIn: 60 * 60 }); return token; } - async verifyToken(jwt: string) { + verifyToken(jwt: string) { const token = jwt.split(".")[1]; if (!token) return false; try { - const payload = verify(token, this.secret_key); - if (payload instanceof Object) return payload.username; - else return payload; + const payload = verify(jwt, this.secret_key); + if (payload instanceof Object) + return { + username: payload.username as string, + roles: payload.roles as Role[], + }; + else return false; } catch (e) { return false; } diff --git a/back-express/src/routes/auth/auth.types.ts b/back-express/src/modules/auth/auth.types.ts similarity index 100% rename from back-express/src/routes/auth/auth.types.ts rename to back-express/src/modules/auth/auth.types.ts diff --git a/back-express/src/routes/auth/auth.utils.ts b/back-express/src/modules/auth/auth.utils.ts similarity index 100% rename from back-express/src/routes/auth/auth.utils.ts rename to back-express/src/modules/auth/auth.utils.ts diff --git a/back-express/src/routes/default.route.ts b/back-express/src/modules/default.route.ts similarity index 100% rename from back-express/src/routes/default.route.ts rename to back-express/src/modules/default.route.ts diff --git a/back-express/src/routes/example/example.routes.ts b/back-express/src/modules/example/example.routes.ts similarity index 100% rename from back-express/src/routes/example/example.routes.ts rename to back-express/src/modules/example/example.routes.ts diff --git a/back-express/src/routes/index.ts b/back-express/src/modules/index.ts similarity index 83% rename from back-express/src/routes/index.ts rename to back-express/src/modules/index.ts index 62fc30a..4b6335d 100644 --- a/back-express/src/routes/index.ts +++ b/back-express/src/modules/index.ts @@ -4,9 +4,13 @@ import { defaultRoute } from "./default.route"; import { exampleRoutes } from "./example/example.routes"; import { userRoutes } from "./users/users.routes"; import { authRoutes } from "./auth/auth.routes"; +import { authorizationMiddleware } from "./auth/auth.middleware"; export const routes = express.Router(); +/* + * Routes + * */ routes.use("/", defaultRoute); routes.use("/example", exampleRoutes); routes.use("/users", userRoutes); diff --git a/back-express/src/routes/users/users.routes.ts b/back-express/src/modules/users/users.routes.ts similarity index 100% rename from back-express/src/routes/users/users.routes.ts rename to back-express/src/modules/users/users.routes.ts diff --git a/back-express/src/routes/users/users.service.ts b/back-express/src/modules/users/users.service.ts similarity index 59% rename from back-express/src/routes/users/users.service.ts rename to back-express/src/modules/users/users.service.ts index 160e5c7..3e9231b 100644 --- a/back-express/src/routes/users/users.service.ts +++ b/back-express/src/modules/users/users.service.ts @@ -6,15 +6,25 @@ class UsersService { async getAllUsers(): Promise { const data = await db_query("select * from user"); - return data as User[]; + const users: User[] = data.map((user: any) => ({ + ...user, + roles: user.roles.split(";"), + })); + return users; } async getUserByUsername(username: string): Promise { const data = await db_query( `select * from user as user WHERE LOWER(username) = LOWER('${username}');`, ); - console.log("Data:", data); - return data.length ? (data[0] as User) : null; + if (data.length) { + const user = { + ...(data[0] as User), + roles: (data[0] as any).roles.split(";"), + }; + return user; + } + return null; } } diff --git a/back-express/src/routes/users/users.types.ts b/back-express/src/modules/users/users.types.ts similarity index 100% rename from back-express/src/routes/users/users.types.ts rename to back-express/src/modules/users/users.types.ts diff --git a/back-express/src/routes/users/users.utils.ts b/back-express/src/modules/users/users.utils.ts similarity index 100% rename from back-express/src/routes/users/users.utils.ts rename to back-express/src/modules/users/users.utils.ts