hasConstant('TMP_ROLE_HOST'), 'MySQLService must define a TMP_ROLE_HOST constant'); assert_same('127.0.0.1', $mysqlServiceClass->getConstant('TMP_ROLE_HOST'), 'TMP_ROLE_HOST must scope the temporary SSO role to the phpMyAdmin app host'); // --- phpMyAdmin login ticket files must not be world-readable --- $ssoDir = sys_get_temp_dir() . '/altmysql-pma-sso-' . bin2hex(random_bytes(4)); mkdir($ssoDir, 0755, true); $settings = load_settings("PHPMYADMIN_SSO_DIR={$ssoDir}\nPHPMYADMIN_TICKET_TTL=60\n"); $ssoClass = new ReflectionClass(PhpMyAdminSso::class); /** @var PhpMyAdminSso $sso */ $sso = $ssoClass->newInstanceWithoutConstructor(); $settingsProp = $ssoClass->getProperty('settings'); $settingsProp->setAccessible(true); $settingsProp->setValue($sso, $settings); $writeTicket = $ssoClass->getMethod('writeLoginTicket'); $writeTicket->setAccessible(true); $auth = [ 'user' => 'da_tmp_phpmyadmin_demo_test', 'password' => 'secret-password', 'host' => '127.0.0.1', 'port' => 33033, 'db' => 'demo_db', ]; $token = $writeTicket->invoke($sso, $auth, 'demo_db'); assert_true(preg_match('/\A[a-f0-9]{64}\z/', $token) === 1, 'ticket token must be a 64 char lowercase hex string'); $ticketPath = $ssoDir . '/tickets/' . $token . '.json'; assert_true(is_file($ticketPath), 'ticket file must be written to /tickets/.json'); $mode = fileperms($ticketPath) & 0777; assert_same(0640, $mode, 'ticket files contain a plaintext MySQL password and must not be world-readable (expected 0640)'); $decoded = json_decode((string)file_get_contents($ticketPath), true); assert_true(is_array($decoded), 'ticket file must contain valid JSON'); assert_same('demo_db', $decoded['db'] ?? null, 'ticket must record the target database'); assert_same('da_tmp_phpmyadmin_demo_test', $decoded['auth']['user'] ?? null, 'ticket must record the temporary role name'); // cleanup array_map('unlink', glob($ssoDir . '/tickets/*') ?: []); @rmdir($ssoDir . '/tickets'); @rmdir($ssoDir); echo "phpmyadmin_sso_test: OK\n";