Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions api/middleware/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const jwt = require('jsonwebtoken');

Check warning on line 1 in api/middleware/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/middleware/auth.js#L1

Require statement not part of import statement.

// Secret key - in production, use environment variable
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';

// Authentication middleware - verifies JWT token
const authenticate = (req, res, next) => {
try {
// Get token from Authorization header
const authHeader = req.headers.authorization;

if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: {
message: 'No token provided. Please include a Bearer token in Authorization header.'
Comment on lines +7 to +15

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a default test case for /order where we will just verify the error:
message: 'No token provided. Please include a Bearer token in Authorization header.'

}
});
}

// Extract token (remove 'Bearer ' prefix)
const token = authHeader.substring(7);

// Verify token
const decoded = jwt.verify(token, JWT_SECRET);

// Attach user info to request object
req.user = decoded;
next();
} catch (error) {
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({
error: {
message: 'Invalid token'
}
});
}
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
error: {
message: 'Token expired'
}
});
}
return res.status(500).json({
error: {
message: 'Authentication error'
}
});
}
};

// Authorization middleware - checks user roles
const authorize = (...allowedRoles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
error: {
message: 'Authentication required'
}
});
}

const userRole = req.user.role;

if (!allowedRoles.includes(userRole)) {
return res.status(403).json({
error: {
message: 'Access denied. Insufficient permissions.'
}
});
}

next();
};
};

module.exports = { authenticate, authorize, JWT_SECRET };
162 changes: 162 additions & 0 deletions api/routes/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
const express = require('express');

Check warning on line 1 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L1

Require statement not part of import statement.
const router = express.Router();
const jwt = require('jsonwebtoken');

Check warning on line 3 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L3

Require statement not part of import statement.
const bcrypt = require('bcryptjs');

Check warning on line 4 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L4

Require statement not part of import statement.
const { JWT_SECRET } = require('../middleware/auth');

Check warning on line 5 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L5

Require statement not part of import statement.

// In-memory user store (use database in production)
// Pre-hashed passwords: admin123 and user123
const users = [
{
id: 1,
username: 'admin',
password: '$2a$10$rOzJqZqZqZqZqZqZqZqZqO', // Will be initialized with actual hash
role: 'admin'
},
{
id: 2,
username: 'user',
password: '$2a$10$rOzJqZqZqZqZqZqZqZqZqO', // Will be initialized with actual hash
role: 'user'
}
];

// Initialize with hashed passwords (run once on server start)
async function initializeUsers() {
for (let user of users) {

Check warning on line 26 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L26

Unhandled errors detected in asynchronous function
if (user.username === 'admin' && user.password.startsWith('$2a$10$rOzJqZqZqZqZqZqZqZqZqO')) {
user.password = await bcrypt.hash('admin123', 10);
} else if (user.username === 'user' && user.password.startsWith('$2a$10$rOzJqZqZqZqZqZqZqZqZqO')) {
user.password = await bcrypt.hash('user123', 10);
}
}
}
initializeUsers();

Comment on lines +27 to +35

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initializeUsers() function is asynchronous but is called without a .catch() block. If hashing fails, this will cause an UnhandledPromiseRejection, which may crash the server in production.

// Login endpoint
router.post('/login', async (req, res) => {

Check warning on line 37 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L37

Method (anonymous) has 52 lines of code (limit is 50)
try {
const { username, password } = req.body;

if (!username || !password) {
return res.status(400).json({
error: {
message: 'Username and password are required'
}
});
}

// Find user
const user = users.find(u => u.username === username);
if (!user) {
return res.status(401).json({
error: {
message: 'Invalid credentials'
}
});
}

// Verify password
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res.status(401).json({
error: {
message: 'Invalid credentials'
}
});
}

// Generate JWT token
const token = jwt.sign(
{
id: user.id,
username: user.username,
role: user.role
},
JWT_SECRET,
{ expiresIn: '24h' }
);

res.status(200).json({
message: 'Login successful',
token: token,
user: {
id: user.id,
username: user.username,
role: user.role
}
});
} catch (error) {

Check warning on line 89 in api/routes/auth.js

View workflow job for this annotation

GitHub Actions / eslint

'error' is defined but never used
res.status(500).json({
error: {
message: 'Login failed'
}
});
}
});

// Register endpoint
router.post('/register', async (req, res) => {

Check warning on line 99 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L99

Method (anonymous) has 51 lines of code (limit is 50)
try {
const { username, password, role = 'user' } = req.body;

if (!username || !password) {
return res.status(400).json({
error: {
message: 'Username and password are required'
}
});
}
Comment thread
adminvns marked this conversation as resolved.

// Check if user exists
if (users.find(u => u.username === username)) {
return res.status(409).json({
error: {
message: 'Username already exists'
}
});
}

// Hash password
const hashedPassword = await bcrypt.hash(password, 10);

// Create new user
const newUser = {
id: users.length + 1,
username,
password: hashedPassword,
role: role || 'user'
};

users.push(newUser);

// Generate token
const token = jwt.sign(

Check warning on line 134 in api/routes/auth.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/auth.js#L134

The application is storing a password in the JWT token payload.
{
id: newUser.id,
username: newUser.username,
role: newUser.role
},
JWT_SECRET,
{ expiresIn: '24h' }
);

res.status(201).json({
message: 'User registered successfully',
token: token,
user: {
id: newUser.id,
username: newUser.username,
role: newUser.role
}
});
} catch (error) {

Check warning on line 153 in api/routes/auth.js

View workflow job for this annotation

GitHub Actions / eslint

'error' is defined but never used
res.status(500).json({
error: {
message: 'Registration failed'
}
});
}
});

module.exports = router;
20 changes: 14 additions & 6 deletions api/routes/orders.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
const express = require('express');
const router = express.Router();
const { authenticate, authorize } = require('../middleware/auth');

Check warning on line 3 in api/routes/orders.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/orders.js#L3

Require statement not part of import statement.

router.get('/', (req, res) => {
// All routes require authentication
router.use(authenticate);

// GET all orders - requires 'user' or 'admin' role
router.get('/', authorize('user', 'admin'), (req, res) => {

res.status(200).json({

message: 'Order was fetched'
message: 'Order was fetched',
user: req.user.username
});

});

router.post('/', (req, res) => {
// POST order - requires 'user' or 'admin' role
router.post('/', authorize('user', 'admin'), (req, res) => {

res.status(201).json({

Expand All @@ -19,8 +26,8 @@

});


router.get('/:orderID', (req, res) => {
// GET order by ID - requires 'user' or 'admin' role
router.get('/:orderID', authorize('user', 'admin'), (req, res) => {

res.status(200).json({
message: 'order details',
Expand All @@ -29,7 +36,8 @@

});

router.delete('/:orderID', (req, res) => {
// DELETE order - requires 'admin' role only
router.delete('/:orderID', authorize('admin'), (req, res) => {

res.status(200).json({
message: 'order deleted',
Expand Down
26 changes: 16 additions & 10 deletions api/routes/products.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
const express = require('express');
const router = express.Router();
const { authenticate, authorize } = require('../middleware/auth');

Check warning on line 3 in api/routes/products.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/products.js#L3

Require statement not part of import statement.

router.get('/', (req, res) => {
// All routes require authentication
router.use(authenticate);

// GET all products - requires 'user' or 'admin' role
router.get('/', authorize('user', 'admin'), (req, res) => {

res.status(200).json({

message: 'Products(GET method)'
message: 'Products(GET method)',
user: req.user.username
});

});

router.post('/', (req, res) => {
// POST product - requires 'admin' role only
router.post('/', authorize('admin'), (req, res) => {

res.status(201).json({

message: 'Products(POST method)'
});
});



router.get('/:productID', (req, res) => {
// GET product by ID - requires 'user' or 'admin' role
router.get('/:productID', authorize('user', 'admin'), (req, res) => {

const id = req.params.productID;
if (id == 123) {
Expand All @@ -40,16 +46,16 @@

});


router.patch('/:productID', (req, res) => {
// PATCH product - requires 'admin' role only
router.patch('/:productID', authorize('admin'), (req, res) => {
res.status(200).json({

message: ' Product updated successfully!'
});
});


router.delete('/:productID', (req, res) => {
// DELETE product - requires 'admin' role only
router.delete('/:productID', authorize('admin'), (req, res) => {
res.status(200).json({

message: ' Product deleted successfully.'
Expand Down
18 changes: 12 additions & 6 deletions api/routes/visitors.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
const express = require('express');
const router = express.Router();
const { authenticate, authorize } = require('../middleware/auth');

Check warning on line 3 in api/routes/visitors.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

api/routes/visitors.js#L3

Require statement not part of import statement.

// Store visitor logs in memory (Note: In production, you'd want to use a database)
const visitorLogs = [];

router.get('/', (req, res) => {
// All routes require authentication
router.use(authenticate);

// GET visitor logs - requires 'user' or 'admin' role
router.get('/', authorize('user', 'admin'), (req, res) => {
const ip = req.ip || req.connection.remoteAddress;
const timestamp = new Date().toISOString();

// Add new visit to logs
visitorLogs.push({
ip,
timestamp,
userAgent: req.get('User-Agent')
userAgent: req.get('User-Agent'),
username: req.user.username
});

// Return all logs
Expand All @@ -24,10 +30,10 @@
});
});

// Get statistics about visitors
router.get('/stats', (req, res) => {
// Get statistics about visitors - requires 'admin' role only
router.get('/stats', authorize('admin'), (req, res) => {
const uniqueIPs = new Set(visitorLogs.map(log => log.ip)).size;

res.status(200).json({
status: 'ok',
totalVisits: visitorLogs.length,
Expand Down
Loading
Loading