Skip to content

Commit

Permalink
Adding auth-role middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmedmohmd committed Aug 10, 2024
1 parent 00e3006 commit 1ff2217
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 34 deletions.
28 changes: 25 additions & 3 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import "express-async-errors";
import { config } from "./config/config";
import { ENV } from "./config/env";
import bootstrap from "./src/bootstrap";
import { connectToDatabase } from "./src/db";
import logger from "./src/logging";
import { findAllUsers, insertUser } from "./src/users/users.service";

const app = express();
app.use(express.json());
Expand All @@ -14,11 +16,31 @@ bootstrap.handleCors(app);
bootstrap.handleSecurity(app);
bootstrap.handleRateLimiting(app);

app.get("/", async (req, res) => {
const user = {
name: "Ahmed",
email: "ahmed@gmail.com",
password: "12345",
};

await insertUser(user);

const users = await findAllUsers();
res.send(users);
});

// All App Routes
bootstrap.handleRoutes(app);

// App Listening Configs
const port = Number(ENV.PORT) || config.defaults.port;
app.listen(port, () => {
logger.general.info(`listening on ${config.apiEndPoint}.`);
});

async function main() {
await connectToDatabase();
logger.general.info(`connected to database successfully.`);

app.listen(port, () => {
logger.general.info(`listening on ${config.apiEndPoint}.`);
});
}
main();
6 changes: 6 additions & 0 deletions src/common/enums/user-role.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
enum Role {
ADMIN = "admin",
MODERATOR = "moderator",
}

export { Role };
66 changes: 66 additions & 0 deletions src/common/middleware/auth-user-roles.middleware.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { Role } from "../enums/user-role.enum";
import { authUserRoles } from "./auth-user-roles.middleware";

vi.mock("../../logging", () => ({
default: {
errors: {
error: vi.fn(),
},
},
}));

describe("authUserRoles()", () => {
let fakeRequest: Request = {} as Request;
const fakeResponse: Response = {} as Response;
const fakeNextFunction: NextFunction = vi.fn();

beforeEach(() => {
Object.assign(fakeRequest, { user: { id: 1 } });
});

it("Should Throw if User not found", async () => {
try {
fakeRequest = {} as Request;

await authUserRoles(Role.ADMIN)(
fakeRequest,
fakeResponse,
fakeNextFunction
);
} catch (error) {
expect(error).toBeInstanceOf(createHttpError.Unauthorized);
}
});

it("Should Throw if User not authorized", async () => {
try {
await authUserRoles(Role.ADMIN)(
fakeRequest,
fakeResponse,
fakeNextFunction
);
} catch (error) {
expect(error).toBeInstanceOf(createHttpError.Forbidden);
}
});

it("Should call next function if all is well", async () => {
Object.assign(fakeRequest, {
user: {
id: 1,
role: "admin",
},
});

await authUserRoles(Role.ADMIN)(
fakeRequest,
fakeResponse,
fakeNextFunction
);

expect(fakeNextFunction).toHaveBeenCalledOnce();
});
});
26 changes: 26 additions & 0 deletions src/common/middleware/auth-user-roles.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { RequestHandler } from "express";
import createHttpError from "http-errors";
import logger from "../../logging";
import { Role } from "../enums/user-role.enum";

const authUserRoles =
(...roles: Role[]): RequestHandler =>
async (req, res, next) => {
// eslint-disable-next-line
const { user } = req as any;

if (!user) {
logger.errors.error(`Unauthorized Access Try.`);
throw new createHttpError.Unauthorized("Unauthorized User.");
}

const isUserAllowed = roles.includes(user?.role);

if (!isUserAllowed) {
logger.errors.error(`Unauthorized Access Try.`);
throw new createHttpError.Forbidden("Forbidden Resource.");
}

next();
};
export { authUserRoles };
29 changes: 0 additions & 29 deletions src/common/middleware/authenticate.middleware.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/common/middleware/authenticate.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { authenticate } from "./authenticate.middleware";
import { authenticate } from "./authenticate";

vi.mock("../../logging", () => ({
default: {
Expand Down
25 changes: 25 additions & 0 deletions src/common/middleware/authenticate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { RequestHandler } from "express";
import createHttpError from "http-errors";
import { decodeJwtToken } from "../../auth/utils/jwt.util";
import logger from "../../logging";

const authenticate: RequestHandler = async (req, res, next) => {
const authorizationHeader = req.headers?.authorization;

if (!authorizationHeader) {
logger.errors.error(`Failed authentication try.`);

throw new createHttpError.Unauthorized(`Your are not Authorized.`);
}

const [, jwtToken] = authorizationHeader.split(" ");
const payload = await decodeJwtToken(jwtToken);

Object.assign(req, {
user: payload,
});

next();
};

export { authenticate };
2 changes: 1 addition & 1 deletion src/global-errors/global-error.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const globalErrorHandler: ErrorRequestHandler = (err, req, res, next) => {

if (createHttpError.isHttpError(err)) {
return res.status(err.statusCode).json({
errors: JSON.parse(err.message),
errors: err.message,
success: false,
status: err.statusCode,
});
Expand Down

0 comments on commit 1ff2217

Please sign in to comment.