Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3daeac9e53 | |||
| 7febba2d9c | |||
| 0a3a53763c | |||
| 85a60a2dc7 | |||
| e74e73a3a0 |
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "2.6.1"
|
||||
".": "2.6.2"
|
||||
}
|
||||
|
||||
@@ -5,6 +5,14 @@ All notable changes to PicPeak will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.6.2](https://github.com/the-luap/picpeak/compare/v2.6.1...v2.6.2) (2026-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **security:** invalidate tokens on password change, enforce session timeout, fix role update ([85a60a2](https://github.com/the-luap/picpeak/commit/85a60a2dc7526aa6b673e2a04e9fdfba7de7117f))
|
||||
* **security:** token invalidation on password change, session timeout enforcement ([0a3a537](https://github.com/the-luap/picpeak/commit/0a3a53763c9f3caef9fdceccf9fdbfdefe9bd8bf))
|
||||
|
||||
## [2.6.1](https://github.com/the-luap/picpeak/compare/v2.6.0...v2.6.1) (2026-03-11)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picpeak-backend",
|
||||
"version": "2.6.1",
|
||||
"version": "2.6.2",
|
||||
"description": "Backend for PicPeak event photo sharing platform",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -85,18 +85,28 @@ async function sessionTimeoutMiddleware(req, res, next) {
|
||||
const now = Date.now();
|
||||
const lastActivity = sessions.get(token);
|
||||
const timeout = await getSessionTimeout();
|
||||
|
||||
// If session exists, check if it's expired
|
||||
|
||||
if (lastActivity) {
|
||||
// Existing session — check if idle too long
|
||||
if (now - lastActivity > timeout) {
|
||||
sessions.delete(token);
|
||||
return res.status(401).json({
|
||||
error: 'Session expired',
|
||||
code: 'SESSION_TIMEOUT'
|
||||
return res.status(401).json({
|
||||
error: 'Session expired',
|
||||
code: 'SESSION_TIMEOUT'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// First request with this token — check if token was issued longer ago than the timeout
|
||||
// This prevents old/stolen tokens from bypassing session timeout after server restart
|
||||
const tokenIssuedAt = (decoded.iat || 0) * 1000; // iat is in seconds
|
||||
if (now - tokenIssuedAt > timeout) {
|
||||
return res.status(401).json({
|
||||
error: 'Session expired',
|
||||
code: 'SESSION_TIMEOUT'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update last activity
|
||||
sessions.set(token, now);
|
||||
|
||||
|
||||
@@ -122,13 +122,15 @@ router.post('/change-password', [
|
||||
// Hash new password with more rounds
|
||||
const newPasswordHash = await bcrypt.hash(newPassword, 12);
|
||||
|
||||
// Update password and clear must_change_password flag
|
||||
// Update password, set password_changed_at to invalidate existing tokens, and clear must_change_password flag
|
||||
const now = new Date();
|
||||
await db('admin_users')
|
||||
.where('id', userId)
|
||||
.update({
|
||||
password_hash: newPasswordHash,
|
||||
password_changed_at: now,
|
||||
must_change_password: false,
|
||||
updated_at: new Date()
|
||||
updated_at: now
|
||||
});
|
||||
|
||||
// Log activity
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "picpeak-frontend",
|
||||
"private": true,
|
||||
"version": "2.6.1",
|
||||
"version": "2.6.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -149,7 +149,11 @@ export const userManagementService = {
|
||||
* Update an admin user
|
||||
*/
|
||||
async updateUser(id: number, data: UpdateUserData): Promise<AdminUser> {
|
||||
const response = await api.put<UpdateUserResponse>(`/admin/users/${id}`, data);
|
||||
// Convert camelCase to snake_case for backend API
|
||||
const payload: Record<string, unknown> = {};
|
||||
if (data.roleId !== undefined) payload.role_id = data.roleId;
|
||||
if (data.isActive !== undefined) payload.is_active = data.isActive;
|
||||
const response = await api.put<UpdateUserResponse>(`/admin/users/${id}`, payload);
|
||||
return transformUser(response.data.user);
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user