diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index 9a27ab3..2120aaa 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -539,7 +539,7 @@ $routes->post('administrator/event-charges/remove/(:num)', 'View\EventController
$routes->post('administrator/event-charges/payment/(:num)', 'View\EventController::toggleEventPayment/$1');
$routes->get('administrator/get-students-with-charges', 'View\EventController::getStudentsWithCharges');
-$routes->get('parent/events', 'View\ParentController::parentEventPage');
+$routes->get('parent/events', 'View\ParentController::parentEventPage', ['filter' => 'auth:parent']);
// parent event participation page
$routes->post('parent/updateParticipation', 'View\ParentController::updateParticipation'); // handle parent participation updates
diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php
index aee212d..df2a879 100644
--- a/app/Controllers/AuthController.php
+++ b/app/Controllers/AuthController.php
@@ -56,13 +56,32 @@ class AuthController extends Controller
public function loginMask()
{
- // Serve the login view directly here
- return view('user/login'); // Adjust the view path as needed
+ $redirectTo = $this->sanitizeRedirectTarget((string) ($this->request->getGet('redirect_to') ?? ''));
+
+ if (session()->get('is_logged_in')) {
+ if ($redirectTo !== null) {
+ return redirect()->to($redirectTo);
+ }
+
+ $roles = session()->get('roles') ?? [];
+ if (empty($roles) && session()->get('role')) {
+ $roles = [session()->get('role')];
+ }
+
+ if (!empty($roles)) {
+ return $this->redirectToDashboard($roles);
+ }
+ }
+
+ return view('user/login', [
+ 'redirect_to' => $redirectTo ?? '',
+ ]);
}
public function login()
{
log_message('info', 'Processing login form submission.');
+ $redirectTo = $this->sanitizeRedirectTarget((string) ($this->request->getPost('redirect_to') ?? $this->request->getGet('redirect_to') ?? ''));
// Step 1: Get email, password, and IP from the request
$email = $this->request->getPost('email');
@@ -98,7 +117,7 @@ class AuthController extends Controller
$this->logLoginAttempt($user['id'], $user['email'], $ip, $this->request->getUserAgent());
// ✅ Step 7: Call loginUser() to set session and redirect
- return $this->loginUser($user);
+ return $this->loginUser($user, $redirectTo);
} else {
// Step 8: Password mismatch — log failed attempt
$this->handleFailedLogin($user['id'], $user['email'], $ip);
@@ -491,7 +510,7 @@ class AuthController extends Controller
return true;
}
- private function loginUser($user)
+ private function loginUser($user, ?string $redirectTo = null)
{
$userRoleModel = new UserRoleModel();
$roles = $userRoleModel->select('roles.name')
@@ -525,9 +544,17 @@ class AuthController extends Controller
if (count($roleNames) === 1) {
// One role → set and redirect directly
session()->set('role', $roleNames[0]);
+ if ($redirectTo !== null) {
+ return redirect()->to($redirectTo);
+ }
return $this->redirectToDashboard([$roleNames[0]]);
} else {
// Multiple roles → redirect to role selection view
+ if ($redirectTo !== null) {
+ session()->set('post_login_redirect', $redirectTo);
+ } else {
+ session()->remove('post_login_redirect');
+ }
return redirect()->to('/select-role');
}
@@ -592,12 +619,17 @@ private function redirectToDashboard(array $roles)
{
$selectedRole = $this->request->getPost('selected_role');
$availableRoles = session()->get('roles');
+ $redirectTo = $this->sanitizeRedirectTarget((string) ($this->request->getPost('redirect_to') ?? session()->get('post_login_redirect') ?? ''));
if (!$selectedRole || !in_array($selectedRole, $availableRoles)) {
return redirect()->to('/select-role')->with('error', 'Invalid role selected.');
}
session()->set('role', $selectedRole);
+ session()->remove('post_login_redirect');
+ if ($redirectTo !== null) {
+ return redirect()->to($redirectTo);
+ }
return $this->redirectToDashboard([$selectedRole]);
}
@@ -609,7 +641,44 @@ private function redirectToDashboard(array $roles)
return redirect()->to('/login')->with('error', 'No roles available.');
}
- return view('auth/select_role', ['roles' => $roles]);
+ return view('auth/select_role', [
+ 'roles' => $roles,
+ 'redirect_to' => (string) (session()->get('post_login_redirect') ?? ''),
+ ]);
+ }
+
+ private function sanitizeRedirectTarget(string $redirectTo): ?string
+ {
+ $redirectTo = trim($redirectTo);
+ if ($redirectTo === '') {
+ return null;
+ }
+
+ if (preg_match('#^https?://#i', $redirectTo)) {
+ $appHost = (string) parse_url(base_url('/'), PHP_URL_HOST);
+ $targetHost = (string) parse_url($redirectTo, PHP_URL_HOST);
+ $targetPath = (string) parse_url($redirectTo, PHP_URL_PATH);
+ $targetQuery = (string) parse_url($redirectTo, PHP_URL_QUERY);
+
+ if ($appHost === '' || $targetHost === '' || strcasecmp($appHost, $targetHost) !== 0) {
+ return null;
+ }
+
+ $redirectTo = $targetPath !== '' ? $targetPath : '/';
+ if ($targetQuery !== '') {
+ $redirectTo .= '?' . $targetQuery;
+ }
+ }
+
+ if (!str_starts_with($redirectTo, '/')) {
+ $redirectTo = '/' . ltrim($redirectTo, '/');
+ }
+
+ if (str_starts_with($redirectTo, '//')) {
+ return null;
+ }
+
+ return $redirectTo;
}
}
diff --git a/app/Filters/AuthFilter.php b/app/Filters/AuthFilter.php
index f3fcae6..0bc85f3 100644
--- a/app/Filters/AuthFilter.php
+++ b/app/Filters/AuthFilter.php
@@ -41,7 +41,13 @@ class AuthFilter implements FilterInterface
// Must be logged in
if (empty($userRoles) || empty($userId)) {
- return redirect()->to('/login');
+ $target = ltrim($request->getUri()->getPath(), '/');
+ $query = $request->getUri()->getQuery();
+ if ($query !== '') {
+ $target .= '?' . $query;
+ }
+
+ return redirect()->to('/login?redirect_to=' . rawurlencode('/' . ltrim($target, '/')));
}
// Enforce 12-hour session lifetime
@@ -110,7 +116,14 @@ class AuthFilter implements FilterInterface
]);
}
- return redirect()->to('/login')->with('error', 'Your session has expired. Please log in again.');
+ $target = ltrim($request->getUri()->getPath(), '/');
+ $query = $request->getUri()->getQuery();
+ if ($query !== '') {
+ $target .= '?' . $query;
+ }
+
+ return redirect()->to('/login?redirect_to=' . rawurlencode('/' . ltrim($target, '/')))
+ ->with('error', 'Your session has expired. Please log in again.');
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
diff --git a/app/Views/auth/select_role.php b/app/Views/auth/select_role.php
index fc89aec..3d7b95a 100644
--- a/app/Views/auth/select_role.php
+++ b/app/Views/auth/select_role.php
@@ -25,6 +25,7 @@
+