Files
Deep-Package-Inspection/backend/routes/auth/users.js
T

239 lines
9.5 KiB
JavaScript

// backend/routes/auth/users.js
const express = require('express');
const bcrypt = require('bcryptjs');
const User = require('../../models/User');
const { requireAdmin, upload } = require('./helpers');
const router = express.Router();
// Helper to block SOC_ANALYST from write actions
function blockAnalyst(req, res, next) {
if (req.adminUser.role === 'SOC_ANALYST') {
return res.status(403).json({ ok: false, error: 'Aksi ini tidak diizinkan untuk peran SOC Analyst' });
}
next();
}
// GET /api/auth/admin/users — daftar semua users (admin & analyst)
router.get('/admin/users', requireAdmin, async (req, res) => {
try {
let query = {};
if (req.adminUser.role === 'TENANT_ADMIN') {
query = {
$or: [
{ role: 'AGENT_VIEWER', site_uuid: req.adminUser.site_uuid },
{ created_by: req.adminUser.username }
]
};
}
query.username = { $ne: req.adminUser.username };
const users = await User.find(query, '-password_hash').sort({ created_at: 1 });
const data = users.map(u => ({
id: u._id.toString(),
username: u.username,
account_name: u.account_name,
profile_picture: u.profile_picture,
role: u.role,
site_uuid: u.site_uuid,
agent_uuid: u.agent_uuid,
is_active: u.is_active,
login_attempts: u.login_attempts || 0,
lockout_until: u.lockout_until || null,
}));
res.json({ ok: true, data });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
// POST /api/auth/admin/unlock-user — unlock akun yang terkunci
router.post('/admin/unlock-user', requireAdmin, blockAnalyst, async (req, res) => {
try {
const { user_id } = req.body;
if (!user_id) {
return res.status(400).json({ ok: false, error: 'user_id wajib diisi' });
}
const target = await User.findById(user_id);
if (!target) {
return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
}
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
return res.status(403).json({ ok: false, error: 'Unauthorized: Account does not belong to your tenant.' });
}
target.login_attempts = 0;
target.lockout_until = null;
await target.save();
res.json({ ok: true, message: 'Akun berhasil di-unlock' });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
// POST /api/auth/admin/create-agent-user — buat akun Network Agent baru
router.post('/admin/create-agent-user', requireAdmin, blockAnalyst, async (req, res) => {
try {
const { username, password, account_name, agent_uuid } = req.body;
if (!username || !password) {
return res.status(400).json({ ok: false, error: 'Username dan password wajib diisi' });
}
const passwordHash = bcrypt.hashSync(password, 10);
let siteUuid = null;
if (agent_uuid) {
const { Summary } = require('../../models/Schemas');
const summaryDoc = await Summary.findOne({ agent_uuid: agent_uuid.trim() });
if (summaryDoc) {
siteUuid = summaryDoc.site_uuid;
}
}
if (!siteUuid) {
siteUuid = req.adminUser.role === 'SUPER_ADMIN'
? (req.body.site_uuid || process.env.BACKONE_SITE_UUID || process.env.NETIFY_SITE_UUID || null)
: req.adminUser.site_uuid;
}
const newUser = await User.create({
username: username.trim(),
password_hash: passwordHash,
account_name: account_name?.trim() || null,
agent_uuid: agent_uuid?.trim() || null,
role: 'AGENT_VIEWER',
site_uuid: siteUuid,
created_by: req.adminUser.username,
});
res.json({ ok: true, message: 'Akun Network Agent berhasil dibuat', userId: newUser._id.toString() });
} catch (err) {
const msg = err.code === 11000 ? 'Username sudah digunakan' : err.message;
res.status(400).json({ ok: false, error: msg });
}
});
// POST /api/auth/admin/update-agent-user — update akun Network Agent
router.post('/admin/update-agent-user', requireAdmin, blockAnalyst, upload.single('profile_picture'), async (req, res) => {
try {
const { user_id, username, password, account_name, agent_uuid } = req.body;
if (!user_id) return res.status(400).json({ ok: false, error: 'user_id wajib diisi' });
const target = await User.findById(user_id).select('+password_hash');
if (!target) return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
if (target.role === 'SUPER_ADMIN') return res.status(403).json({ ok: false, error: 'Tidak bisa mengubah akun SUPER_ADMIN dari sini' });
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
return res.status(403).json({ ok: false, error: 'Unauthorized: This account does not belong to your tenant.' });
}
if (username?.trim()) {
const existing = await User.findOne({ username: username.trim(), _id: { $ne: user_id } });
if (existing) return res.status(400).json({ ok: false, error: 'Username sudah digunakan' });
target.username = username.trim();
}
if (password) target.password_hash = bcrypt.hashSync(password, 10);
if (account_name != null) target.account_name = account_name?.trim() || null;
if (agent_uuid != null) {
target.agent_uuid = agent_uuid?.trim() || null;
if (agent_uuid.trim()) {
const { Summary } = require('../../models/Schemas');
const summaryDoc = await Summary.findOne({ agent_uuid: agent_uuid.trim() });
if (summaryDoc) {
target.site_uuid = summaryDoc.site_uuid;
}
}
}
if (req.file) target.profile_picture = req.file.filename;
await target.save();
const updated = await User.findById(user_id, '-password_hash');
res.json({ ok: true, message: 'Akun berhasil diperbarui', user: updated });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
// DELETE /api/auth/admin/delete-agent-user/:id — hapus akun Network Agent
router.delete('/admin/delete-agent-user/:id', requireAdmin, blockAnalyst, async (req, res) => {
try {
const target = await User.findById(req.params.id);
if (!target) return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
if (target.role === 'SUPER_ADMIN') return res.status(403).json({ ok: false, error: 'Tidak bisa menghapus SUPER_ADMIN' });
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
return res.status(403).json({ ok: false, error: 'Unauthorized: This account does not belong to your tenant.' });
}
await User.findByIdAndDelete(req.params.id);
res.json({ ok: true, message: 'Akun berhasil dihapus' });
} catch (err) {
res.status(400).json({ ok: false, error: err.message });
}
});
// POST /api/auth/admin/upload-agent-picture/:id — upload foto profil agent oleh admin
router.post('/admin/upload-agent-picture/:id', requireAdmin, blockAnalyst, upload.single('profile_picture'), async (req, res) => {
try {
if (!req.file) return res.status(400).json({ ok: false, error: 'File gambar wajib diupload' });
const target = await User.findById(req.params.id);
if (!target) return res.status(404).json({ ok: false, error: 'User tidak ditemukan' });
if (req.adminUser.role !== 'SUPER_ADMIN' && target.site_uuid !== req.adminUser.site_uuid) {
return res.status(403).json({ ok: false, error: 'Unauthorized: This account does not belong to your tenant.' });
}
target.profile_picture = req.file.filename;
await target.save();
res.json({ ok: true, message: 'Foto profil berhasil diperbarui', filename: req.file.filename });
} catch (err) {
res.status(500).json({ ok: false, error: err.message });
}
});
// POST /api/auth/admin/create-external-user — buat akun Eksternal (SOC Analyst, Engineer, dll)
router.post('/admin/create-external-user', requireAdmin, blockAnalyst, upload.single('profile_picture'), async (req, res) => {
try {
const { username, password, account_name, role } = req.body;
if (!username || !password || !role) {
return res.status(400).json({ ok: false, error: 'Username, password, dan role wajib diisi' });
}
// Validasi role (hanya boleh role tertentu, tidak boleh SUPER_ADMIN baru atau AGENT_VIEWER)
const validRoles = ['SOC_ANALYST', 'ENGINEER', 'TENANT_ADMIN'];
if (!validRoles.includes(role)) {
return res.status(400).json({ ok: false, error: 'Role tidak valid untuk pembuatan akun eksternal' });
}
const existing = await User.findOne({ username: username.trim() });
if (existing) {
return res.status(400).json({ ok: false, error: 'Username sudah digunakan' });
}
const passwordHash = bcrypt.hashSync(password, 10);
const siteUuid = req.adminUser.role === 'SUPER_ADMIN'
? (req.body.site_uuid || process.env.BACKONE_SITE_UUID || process.env.NETIFY_SITE_UUID || null)
: req.adminUser.site_uuid;
const createdBy = req.adminUser.role === 'SUPER_ADMIN'
? (req.body.created_by || req.adminUser.username)
: req.adminUser.username;
const newUser = await User.create({
username: username.trim(),
password_hash: passwordHash,
account_name: account_name?.trim() || null,
role: role,
site_uuid: siteUuid,
created_by: createdBy,
profile_picture: req.file ? req.file.filename : null
});
res.json({ ok: true, message: 'Akun eksternal berhasil dibuat', userId: newUser._id.toString() });
} catch (err) {
const msg = err.code === 11000 ? 'Username sudah digunakan' : err.message;
res.status(400).json({ ok: false, error: msg });
}
});
module.exports = router;