Initial permissions framework and setup

This commit is contained in:
2025-03-22 22:11:03 -04:00
parent 1963bee00c
commit 03dc119a15
19 changed files with 1961 additions and 341 deletions

View File

@@ -5,6 +5,7 @@ const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { Pool } = require('pg');
const morgan = require('morgan');
const authRoutes = require('./routes');
// Log startup configuration
console.log('Starting auth server with config:', {
@@ -27,11 +28,14 @@ const pool = new Pool({
port: process.env.DB_PORT,
});
// Make pool available globally
global.pool = pool;
// Middleware
app.use(express.json());
app.use(morgan('combined'));
app.use(cors({
origin: ['http://localhost:5173', 'https://inventory.kent.pw'],
origin: ['http://localhost:5173', 'http://localhost:5174', 'https://inventory.kent.pw'],
credentials: true
}));
@@ -42,7 +46,7 @@ app.post('/login', async (req, res) => {
try {
// Get user from database
const result = await pool.query(
'SELECT id, username, password FROM users WHERE username = $1',
'SELECT id, username, password, is_admin, is_active FROM users WHERE username = $1',
[username]
);
@@ -52,6 +56,11 @@ app.post('/login', async (req, res) => {
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(401).json({ error: 'Invalid username or password' });
}
// Check if user is active
if (!user.is_active) {
return res.status(403).json({ error: 'Account is inactive' });
}
// Generate JWT token
const token = jwt.sign(
@@ -60,31 +69,84 @@ app.post('/login', async (req, res) => {
{ expiresIn: '24h' }
);
res.json({ token });
// Get user permissions for the response
const permissionsResult = await pool.query(`
SELECT code
FROM permissions p
JOIN user_permissions up ON p.id = up.permission_id
WHERE up.user_id = $1
`, [user.id]);
const permissions = permissionsResult.rows.map(row => row.code);
res.json({
token,
user: {
id: user.id,
username: user.username,
is_admin: user.is_admin,
permissions: user.is_admin ? [] : permissions
}
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Protected route to verify token
app.get('/protected', async (req, res) => {
// User info endpoint
app.get('/me', async (req, res) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const token = authHeader.split(' ')[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
res.json({ userId: decoded.userId, username: decoded.username });
// Get user details from database
const userResult = await pool.query(
'SELECT id, username, email, is_admin, is_active FROM users WHERE id = $1',
[decoded.userId]
);
if (userResult.rows.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
const user = userResult.rows[0];
// Get user permissions
let permissions = [];
if (!user.is_admin) {
const permissionsResult = await pool.query(`
SELECT code
FROM permissions p
JOIN user_permissions up ON p.id = up.permission_id
WHERE up.user_id = $1
`, [user.id]);
permissions = permissionsResult.rows.map(row => row.code);
}
res.json({
id: user.id,
username: user.username,
email: user.email,
is_admin: user.is_admin,
permissions: permissions
});
} catch (error) {
console.error('Token verification error:', error);
res.status(401).json({ error: 'Invalid token' });
}
});
// Mount all routes from routes.js
app.use('/', authRoutes);
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });