feat: implement identity and security features
- Added account types and privileged account types constants. - Created admin user controller for updating user security fields. - Developed role assignment controller for managing user roles. - Implemented validation middleware for request schemas. - Defined user role and auth session models for database interactions. - Created services for authentication, email notifications, and OTP handling. - Developed OAuth service for Google and Apple authentication. - Added JWT utility functions for token generation and verification. - Implemented comprehensive tests for authentication, session management, and password policies. - Created migration for updating user schema and adding new tables for auth sessions and user identities.
This commit is contained in:
Vendored
+38
@@ -19,9 +19,22 @@ jest.mock("../../app/config/bullBoard.config", () => {
|
||||
|
||||
const app = require("../../app");
|
||||
const { AppError, errorHandler } = require("../../app/middleware/error.middleware");
|
||||
const db = require("../../app/models");
|
||||
const { generateToken } = require("../../app/utils/jwt.util");
|
||||
|
||||
const authenticated = (accountType = "customer") => {
|
||||
const user = { id: "usr-self", firstName: "Test", lastName: "User", email: "test@example.com", accountType, accountStatus: "ACTIVE", tokenVersion: 0, emailVerifiedAt: new Date(), profile: null };
|
||||
const session = { id: "session-self", user_id: user.id, revoked_at: null, expires_at: new Date(Date.now() + 60000), token_version: 0 };
|
||||
jest.spyOn(db.User, "findByPk").mockResolvedValue(user);
|
||||
jest.spyOn(db.AuthSession, "findByPk").mockResolvedValue(session);
|
||||
jest.spyOn(db.UserRole, "findAll").mockResolvedValue([]);
|
||||
jest.spyOn(db.userPermission, "findAll").mockResolvedValue([]);
|
||||
return `Bearer ${generateToken({ userId: user.id, sessionId: session.id, tokenVersion: 0 })}`;
|
||||
};
|
||||
|
||||
describe("foundation HTTP behavior", () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
mockCheckDatabase.mockResolvedValue(true);
|
||||
mockCheckRedis.mockResolvedValue(true);
|
||||
});
|
||||
@@ -74,4 +87,29 @@ describe("foundation HTTP behavior", () => {
|
||||
test("Bull Board rejects unauthenticated requests", async () => {
|
||||
await request(app).get("/admin/queues").expect(401);
|
||||
});
|
||||
|
||||
test("mounted permission administration rejects unauthenticated requests", async () => {
|
||||
await request(app).get("/api/v1/permissions").expect(401);
|
||||
});
|
||||
|
||||
test("customer cannot mass-assign account type", async () => {
|
||||
const token = authenticated("customer");
|
||||
await request(app).patch("/api/v1/user/me").set("Authorization", token).send({ accountType: "admin" }).expect(400).expect(({ body }) => expect(body.error.code).toBe("UNSAFE_FIELD"));
|
||||
});
|
||||
|
||||
test("customer cannot mutate another user by ID", async () => {
|
||||
const token = authenticated("customer");
|
||||
await request(app).patch("/api/v1/user/usr-other").set("Authorization", token).send({ firstName: "Attack" }).expect(403);
|
||||
});
|
||||
|
||||
test("authenticated admin can reach protected Bull Board", async () => {
|
||||
const token = authenticated("admin");
|
||||
await request(app).get("/admin/queues").set("Authorization", token).expect(200);
|
||||
});
|
||||
|
||||
test("a suspended account cannot use an otherwise valid access token", async () => {
|
||||
const token = authenticated("customer");
|
||||
db.User.findByPk.mockResolvedValue({ id: "usr-self", accountStatus: "SUSPENDED", tokenVersion: 0 });
|
||||
await request(app).get("/api/v1/auth/me").set("Authorization", token).expect(401);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user