(Feat-Fix): Lots of fixes done, reporting system fixed, stricter types
This commit is contained in:
@@ -1,16 +1,23 @@
|
||||
import { Router } from "@oak/oak";
|
||||
import { hash, compare, genSalt } from "bcrypt";
|
||||
import { compare, genSalt, hash } from "bcrypt";
|
||||
import { db } from "../config/database.ts";
|
||||
import { config } from "../config/env.ts";
|
||||
|
||||
// Helper function to hash password with proper salt generation
|
||||
async function hashPassword(password: string): Promise<string> {
|
||||
const salt = await genSalt(config.BCRYPT_ROUNDS);
|
||||
return await hash(password, salt);
|
||||
}
|
||||
import { authenticateToken, generateToken, getCurrentUser } from "../middleware/auth.ts";
|
||||
import { sanitizeInput, isValidEmail, isStrongPassword } from "../middleware/security.ts";
|
||||
import type { User, LoginRequest, ChangePasswordRequest } from "../types/index.ts";
|
||||
import {
|
||||
authenticateToken,
|
||||
generateToken,
|
||||
getCurrentUser,
|
||||
} from "../middleware/auth.ts";
|
||||
import { isStrongPassword, sanitizeInput } from "../middleware/security.ts";
|
||||
import type {
|
||||
ChangePasswordRequest,
|
||||
LoginRequest,
|
||||
User,
|
||||
} from "../types/index.ts";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
@@ -19,41 +26,41 @@ router.post("/login", async (ctx) => {
|
||||
try {
|
||||
const body = await ctx.request.body.json() as LoginRequest;
|
||||
const { username, password } = body;
|
||||
|
||||
|
||||
// Input validation
|
||||
if (!username || !password) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Username and password required" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Sanitize input
|
||||
const sanitizedUsername = sanitizeInput(username);
|
||||
|
||||
|
||||
// Query user
|
||||
const users = await db.query<User[]>(
|
||||
"SELECT * FROM users WHERE username = ? AND is_active = TRUE",
|
||||
[sanitizedUsername]
|
||||
[sanitizedUsername],
|
||||
);
|
||||
|
||||
|
||||
if (users.length === 0) {
|
||||
// Use generic message to prevent user enumeration
|
||||
ctx.response.status = 401;
|
||||
ctx.response.body = { error: "Invalid credentials" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const user = users[0];
|
||||
|
||||
|
||||
// Verify password
|
||||
const validPassword = await compare(password, user.password!);
|
||||
|
||||
|
||||
if (!validPassword) {
|
||||
ctx.response.status = 401;
|
||||
ctx.response.body = { error: "Invalid credentials" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Generate JWT token
|
||||
const token = await generateToken({
|
||||
id: user.id,
|
||||
@@ -61,10 +68,10 @@ router.post("/login", async (ctx) => {
|
||||
role: user.role,
|
||||
departmentId: user.department_id,
|
||||
});
|
||||
|
||||
|
||||
// Return user data without password
|
||||
const { password: _, ...userWithoutPassword } = user;
|
||||
|
||||
|
||||
ctx.response.body = {
|
||||
token,
|
||||
user: userWithoutPassword,
|
||||
@@ -80,18 +87,18 @@ router.post("/login", async (ctx) => {
|
||||
router.get("/me", authenticateToken, async (ctx) => {
|
||||
try {
|
||||
const currentUser = getCurrentUser(ctx);
|
||||
|
||||
|
||||
const users = await db.query<User[]>(
|
||||
"SELECT id, username, name, email, role, department_id, contractor_id, is_active FROM users WHERE id = ?",
|
||||
[currentUser.id]
|
||||
[currentUser.id],
|
||||
);
|
||||
|
||||
|
||||
if (users.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "User not found" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ctx.response.body = users[0];
|
||||
} catch (error) {
|
||||
console.error("Get user error:", error);
|
||||
@@ -106,14 +113,14 @@ router.post("/change-password", authenticateToken, async (ctx) => {
|
||||
const currentUser = getCurrentUser(ctx);
|
||||
const body = await ctx.request.body.json() as ChangePasswordRequest;
|
||||
const { currentPassword, newPassword } = body;
|
||||
|
||||
|
||||
// Input validation
|
||||
if (!currentPassword || !newPassword) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Current and new password required" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Validate new password strength (only enforce in production or if explicitly enabled)
|
||||
if (config.isProduction()) {
|
||||
const passwordCheck = isStrongPassword(newPassword);
|
||||
@@ -127,37 +134,37 @@ router.post("/change-password", authenticateToken, async (ctx) => {
|
||||
ctx.response.body = { error: "Password must be at least 6 characters" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get current password hash
|
||||
const users = await db.query<User[]>(
|
||||
"SELECT password FROM users WHERE id = ?",
|
||||
[currentUser.id]
|
||||
[currentUser.id],
|
||||
);
|
||||
|
||||
|
||||
if (users.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "User not found" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Verify current password
|
||||
const validPassword = await compare(currentPassword, users[0].password!);
|
||||
|
||||
|
||||
if (!validPassword) {
|
||||
ctx.response.status = 401;
|
||||
ctx.response.body = { error: "Current password is incorrect" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Hash new password with configured rounds
|
||||
const hashedPassword = await hashPassword(newPassword);
|
||||
|
||||
|
||||
// Update password
|
||||
await db.execute(
|
||||
"UPDATE users SET password = ? WHERE id = ?",
|
||||
[hashedPassword, currentUser.id]
|
||||
[hashedPassword, currentUser.id],
|
||||
);
|
||||
|
||||
|
||||
ctx.response.body = { message: "Password changed successfully" };
|
||||
} catch (error) {
|
||||
console.error("Change password error:", error);
|
||||
|
||||
Reference in New Issue
Block a user