$query * @param array $post */ public static function isCallbackPost(string $method, array $query, array $post): bool { $realm = filter_var($query['realm'] ?? null, FILTER_VALIDATE_INT); return $method === 'POST' && in_array($query['action'] ?? '', ['acs', 'callback'], true) && $realm !== false && $realm >= 1000 && is_string($post['SAMLResponse'] ?? null) && $post['SAMLResponse'] !== ''; } /** @param array $query * @param array $post */ public static function relay(array $query, array $post): never { $nonce = bin2hex(random_bytes(16)); header('Content-Type: text/html; charset=UTF-8'); header('Cache-Control: no-store'); header('Referrer-Policy: no-referrer'); header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$nonce'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'"); print self::render($query, $post, $nonce); exit; } /** @param array $query * @param array $post */ public static function render(array $query, array $post, string $nonce): string { if (!self::isCallbackPost('POST', $query, $post)) { throw new \InvalidArgumentException('Invalid SAML callback'); } $target = 'login_sso.php?action=' . $query['action'] . '&realm=' . (int) $query['realm']; $fields = ['SAMLResponse' => (string) $post['SAMLResponse'], 'cacti_saml_relay' => '1']; if (is_string($post['RelayState'] ?? null)) { $fields['RelayState'] = $post['RelayState']; } $escape = static fn (string $value): string => htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $html = '
'; foreach ($fields as $name => $value) { $html .= ''; } // The browser supplies its localized default submit label without a session. $html .= '
'; return $html; } }