feat(security): add session timeout + transaction wrapping (B1)
Session timeout: configurable idle (default 30min) and absolute (default 8h) timeouts via DB settings, enforced in web/index.php on every request. Timestamps set at login in action layer; graceful fallback for pre-existing sessions. Transaction wrapping: UserAccountService.createFromAdmin/register, roles create/edit, and permissions edit now wrap multi-step DB operations in transactions to guarantee atomicity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -68,11 +68,22 @@ if ($request->hasBody('key')) {
|
||||
$roleIds = [$roleIds];
|
||||
}
|
||||
$roleIds = array_values(array_unique(array_map('intval', $roleIds)));
|
||||
$rolePermissionRepository->replaceForPermission($id, $roleIds);
|
||||
$affectedRoleIds = array_values(array_unique(array_merge($previousRoleIds, $roleIds)));
|
||||
$affectedUserIds = $userRoleRepository->listUserIdsByRoleIds($affectedRoleIds);
|
||||
if ($affectedUserIds) {
|
||||
$userWriteRepository->bumpAuthzVersionByUserIds($affectedUserIds);
|
||||
$dbSession = app(\MintyPHP\Repository\Support\DatabaseSessionRepository::class);
|
||||
$dbSession->beginTransaction();
|
||||
try {
|
||||
$rolePermissionRepository->replaceForPermission($id, $roleIds);
|
||||
$affectedRoleIds = array_values(array_unique(array_merge($previousRoleIds, $roleIds)));
|
||||
$affectedUserIds = $userRoleRepository->listUserIdsByRoleIds($affectedRoleIds);
|
||||
if ($affectedUserIds) {
|
||||
$userWriteRepository->bumpAuthzVersionByUserIds($affectedUserIds);
|
||||
}
|
||||
$dbSession->commitTransaction();
|
||||
} catch (\Throwable) {
|
||||
try {
|
||||
$dbSession->rollbackTransaction();
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
$errorBag->merge([t('Failed to update permission roles')]);
|
||||
}
|
||||
$action = (string) $request->body('action', 'save');
|
||||
if ($action === 'save_close') {
|
||||
|
||||
@@ -50,7 +50,17 @@ if ($request->hasBody('description')) {
|
||||
$permissionIds = array_values(array_unique(array_map('intval', $permissionIds)));
|
||||
$createdId = (int) ($result['id'] ?? 0);
|
||||
if ($createdId > 0) {
|
||||
$rolePermissionRepository->replaceForRole($createdId, $permissionIds);
|
||||
$dbSession = app(\MintyPHP\Repository\Support\DatabaseSessionRepository::class);
|
||||
$dbSession->beginTransaction();
|
||||
try {
|
||||
$rolePermissionRepository->replaceForRole($createdId, $permissionIds);
|
||||
$dbSession->commitTransaction();
|
||||
} catch (\Throwable) {
|
||||
try {
|
||||
$dbSession->rollbackTransaction();
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
$action = (string) $request->body('action', 'create');
|
||||
if ($action === 'create_close') {
|
||||
|
||||
@@ -86,10 +86,21 @@ if ($request->hasBody('description')) {
|
||||
$permissionIds = [$permissionIds];
|
||||
}
|
||||
$permissionIds = array_values(array_unique(array_map('intval', $permissionIds)));
|
||||
$rolePermissionRepository->replaceForRole($roleId, $permissionIds);
|
||||
$affectedUserIds = $userRoleRepository->listUserIdsByRoleIds([$roleId]);
|
||||
if ($affectedUserIds) {
|
||||
$userWriteRepository->bumpAuthzVersionByUserIds($affectedUserIds);
|
||||
$dbSession = app(\MintyPHP\Repository\Support\DatabaseSessionRepository::class);
|
||||
$dbSession->beginTransaction();
|
||||
try {
|
||||
$rolePermissionRepository->replaceForRole($roleId, $permissionIds);
|
||||
$affectedUserIds = $userRoleRepository->listUserIdsByRoleIds([$roleId]);
|
||||
if ($affectedUserIds) {
|
||||
$userWriteRepository->bumpAuthzVersionByUserIds($affectedUserIds);
|
||||
}
|
||||
$dbSession->commitTransaction();
|
||||
} catch (\Throwable) {
|
||||
try {
|
||||
$dbSession->rollbackTransaction();
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
$errorBag->merge([t('Failed to update role permissions')]);
|
||||
}
|
||||
$action = (string) $request->body('action', 'save');
|
||||
if ($action === 'save_close') {
|
||||
|
||||
@@ -312,6 +312,8 @@ if (requestInput()->method() === 'POST') {
|
||||
if ($remember) {
|
||||
$rememberMeService->rememberUser($userId);
|
||||
}
|
||||
$sessionStore->set('session_started_at', time());
|
||||
$sessionStore->set('session_last_activity', time());
|
||||
Router::redirect('admin');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use MintyPHP\Http\SessionStoreInterface;
|
||||
use MintyPHP\Router;
|
||||
use MintyPHP\Service\Auth\AuthServicesFactory;
|
||||
use MintyPHP\Support\Flash;
|
||||
@@ -57,4 +58,7 @@ if (!($loginResult['ok'] ?? false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sessionStore = app(SessionStoreInterface::class);
|
||||
$sessionStore->set('session_started_at', time());
|
||||
$sessionStore->set('session_last_activity', time());
|
||||
Router::redirect('admin');
|
||||
|
||||
Reference in New Issue
Block a user