Linux cesa-www-main 6.1.0-49-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
Apache/2.4.68 (Debian)
Server IP : 10.218.0.2 & Your IP : 216.73.217.117
Domains :
Cant Read [ /etc/named.conf ]
User : www-data
Terminal
Auto Root
Create File
Create Folder
Localroot Suggester
Backdoor Destroyer
Readme
/
var /
www /
v3.cesa.co.za /
src /
UserBundle /
Service /
Delete
Unzip
Name
Size
Permission
Date
Action
.htaccess
237
B
-r-xr-xr-x
2026-08-17 03:12
Echasl23SsoTicketService.php
6.17
KB
-rw-r--r--
2026-08-14 12:08
Echasl23UserService.php
2.14
KB
-rwxrwxr-x
2026-05-18 12:51
MemberPortalLoginContext.php
825
B
-rwxrwxr-x
2026-05-20 09:05
MemberPortalUserService.php
1.83
KB
-rwxrwxr-x
2026-05-20 09:05
Save
Rename
<?php namespace App\UserBundle\Service; use App\UserBundle\Entity\UserOld; use App\UserBundle\Repository\UserOldRepository; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; /** * Short-lived HMAC tickets for echasl23 → cesa-v3 SSO (server-side proxy). */ final class Echasl23SsoTicketService { private const DEFAULT_TTL_SECONDS = 120; public function __construct( private readonly UserOldRepository $userOldRepository, private readonly Echasl23UserService $echasl23UserService, private readonly TokenStorageInterface $tokenStorage, private readonly string $sharedSecret = '', ) { } public static function resolveSharedSecret(): string { return (string) ( $_ENV['ECHASL23_SSO_SECRET'] ?? $_SERVER['ECHASL23_SSO_SECRET'] ?? getenv('ECHASL23_SSO_SECRET') ?? '' ); } /** * Mint a ticket for an echasl23 username (used by cesa-main proxy; also usable in tests). */ public function mint(string $username, ?int $ttlSeconds = null): string { $secret = $this->sharedSecret !== '' ? $this->sharedSecret : self::resolveSharedSecret(); if ($secret === '') { throw new \RuntimeException('ECHASL23_SSO_SECRET is not configured.'); } $username = trim($username); if ($username === '') { throw new \InvalidArgumentException('Username is required to mint an SSO ticket.'); } $ttl = $ttlSeconds ?? self::DEFAULT_TTL_SECONDS; if ($ttl <= 0) { $ttl = self::DEFAULT_TTL_SECONDS; } $payload = [ 'u' => $username, 'exp' => time() + $ttl, 'n' => bin2hex(random_bytes(8)), ]; $payloadJson = json_encode($payload, JSON_UNESCAPED_SLASHES); if ($payloadJson === false) { throw new \RuntimeException('Could not encode SSO ticket payload.'); } $payloadB64 = rtrim(strtr(base64_encode($payloadJson), '+/', '-_'), '='); $sig = hash_hmac('sha256', $payloadB64, $secret); return $payloadB64 . '.' . $sig; } /** * @return array{username: string, exp: int, nonce: string}|null */ public function parseAndVerify(string $ticket): ?array { $secret = $this->sharedSecret !== '' ? $this->sharedSecret : self::resolveSharedSecret(); if ($secret === '' || $ticket === '' || !str_contains($ticket, '.')) { return null; } [$payloadB64, $sig] = explode('.', $ticket, 2); if ($payloadB64 === '' || $sig === '') { return null; } $expected = hash_hmac('sha256', $payloadB64, $secret); if (!hash_equals($expected, $sig)) { return null; } $json = base64_decode(strtr($payloadB64, '-_', '+/'), true); if ($json === false) { return null; } $payload = json_decode($json, true); if (!is_array($payload)) { return null; } $username = isset($payload['u']) ? trim((string) $payload['u']) : ''; $exp = isset($payload['exp']) ? (int) $payload['exp'] : 0; $nonce = isset($payload['n']) ? (string) $payload['n'] : ''; if ($username === '' || $exp < time()) { return null; } return [ 'username' => $username, 'exp' => $exp, 'nonce' => $nonce, ]; } public function authenticateFromTicket(string $ticket): ?UserOld { $result = $this->authenticateFromTicketDetailed($ticket); return $result['user']; } /** * @return array{user: ?UserOld, error: string} */ public function authenticateFromTicketDetailed(string $ticket): array { $secret = $this->sharedSecret !== '' ? $this->sharedSecret : self::resolveSharedSecret(); if ($secret === '') { return ['user' => null, 'error' => 'ECHASL23_SSO_SECRET is not configured on the application server.']; } $parsed = $this->parseAndVerify($ticket); if ($parsed === null) { if ($ticket === '' || !str_contains($ticket, '.')) { return ['user' => null, 'error' => 'Malformed SSO ticket.']; } [$payloadB64, $sig] = explode('.', $ticket, 2); $expected = hash_hmac('sha256', $payloadB64, $secret); if ($payloadB64 === '' || $sig === '' || !hash_equals($expected, $sig)) { return ['user' => null, 'error' => 'Invalid SSO ticket signature (shared secret mismatch between cesa-main and cesa-v3).']; } $json = base64_decode(strtr($payloadB64, '-_', '+/'), true); $payload = is_string($json) ? json_decode($json, true) : null; $exp = is_array($payload) && isset($payload['exp']) ? (int) $payload['exp'] : 0; if ($exp > 0 && $exp < time()) { return ['user' => null, 'error' => 'SSO ticket expired.']; } return ['user' => null, 'error' => 'Invalid or unreadable SSO ticket.']; } $user = $this->userOldRepository->findOneBy(['username' => $parsed['username']]); if (!$user instanceof UserOld) { return [ 'user' => null, 'error' => 'SSO user "' . $parsed['username'] . '" was not found in cesa-v3 (UserOld / echasl23 users).', ]; } if (!$user->isActivated()) { return [ 'user' => null, 'error' => 'SSO user "' . $parsed['username'] . '" exists in cesa-v3 but is not activated.', ]; } $roles = $this->echasl23UserService->resolveRoles($user); if ($roles === ['NONE']) { return [ 'user' => null, 'error' => 'SSO user "' . $parsed['username'] . '" has no usable roles in cesa-v3.', ]; } $user->setResolvedRoles($roles); $token = new UsernamePasswordToken($user, 'main', $roles); $this->tokenStorage->setToken($token); return ['user' => $user, 'error' => '']; } }