mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
diag: invalid_grant is SSO auth-method mismatch, not bad creds
- feat: creds-test.sh gum probe for SMTP vs PeerTube auth + PeerTubeService::probeAuth() - feat: app-token.sh gum probe for long-lived PeerTube app token (client_credentials) - docs: add copy-paste proof commands to demonstrate the SSO break to admins
This commit is contained in:
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* creds-probe.php
|
||||
*
|
||||
* CLI helper used by scripts/creds-test.sh. Performs two live probes using the
|
||||
* credentials stored in the database (SMTP username/password), WITHOUT the SSO
|
||||
* portal in the middle:
|
||||
*
|
||||
* 1. SMTP — real TCP connect + SMTP AUTH, then disconnect (no mail sent)
|
||||
* 2. PeerTube — OAuth2 "password" grant against <instance>/api/v1/users/token
|
||||
*
|
||||
* Output is a single JSON document on stdout:
|
||||
* {
|
||||
* "username": "...", // SMTP username (what PeerTube reuses)
|
||||
* "password": "...", // DECRYPTED password (only when --with-pwd)
|
||||
* "smtp": {... probe result },
|
||||
* "peertube": {... probe result }
|
||||
* }
|
||||
*
|
||||
* Usage:
|
||||
* php creds-probe.php [--db <path>] [--with-pwd] [--instance <url>] [--channel <handle>]
|
||||
*
|
||||
* Exit code 0 when BOTH probes succeed, 1 otherwise. Never prints the password to
|
||||
* stderr/log; only prints it to stdout when --with-pwd is given.
|
||||
*/
|
||||
|
||||
$opts = [
|
||||
'db' => null,
|
||||
'with-pwd' => false,
|
||||
'instance' => null, // override the stored instance URL (optional)
|
||||
'channel' => null, // override the stored channel handle (optional)
|
||||
];
|
||||
$args = $argv ?? ($GLOBALS['argv'] ?? $_SERVER['argv'] ?? []);
|
||||
$args = is_array($args) ? $args : [];
|
||||
array_shift($args);
|
||||
for ($i = 0; $i < count($args); $i++) {
|
||||
$a = $args[$i];
|
||||
if ($a === '--with-pwd') { $opts['with-pwd'] = true; continue; }
|
||||
if (in_array($a, ['--db', '--instance', '--channel'], true)) {
|
||||
$opts[ltrim($a, '-')] = $args[$i + 1] ?? null;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Locate app root (script lives in scripts/ next to app/) ──────────────────
|
||||
$candidates = [__DIR__ . '/../app', __DIR__ . '/..'];
|
||||
$appRoot = null;
|
||||
foreach ($candidates as $c) {
|
||||
if (file_exists(realpath($c) . '/src/Crypto.php')) { $appRoot = realpath($c); break; }
|
||||
}
|
||||
if ($appRoot === null) { throw new RuntimeException('Could not locate app root (src/Crypto.php).'); }
|
||||
|
||||
define('APP_ROOT', $appRoot);
|
||||
|
||||
// Autoload dependencies (PHPMailer, GuzzleHttp) from composer.
|
||||
$autoloads = [
|
||||
$appRoot . '/../vendor/autoload.php', // repo root / vendor
|
||||
$appRoot . '/vendor/autoload.php', // app / vendor
|
||||
];
|
||||
foreach ($autoloads as $a) {
|
||||
if (is_file($a)) { require_once $a; break; }
|
||||
}
|
||||
|
||||
// Read-only probe: we must NOT run schema migrations against the live DB.
|
||||
// If DatabaseMigrations isn't loaded yet, provide a no-op runner so that
|
||||
// Database's constructor just opens a PDO connection.
|
||||
if (!class_exists('DatabaseMigrations', false)) {
|
||||
class DatabaseMigrations
|
||||
{
|
||||
public function run(): void {}
|
||||
}
|
||||
}
|
||||
|
||||
require_once $appRoot . '/src/Crypto.php';
|
||||
require_once $appRoot . '/src/Database.php';
|
||||
require_once $appRoot . '/src/SmtpRelay.php';
|
||||
require_once $appRoot . '/src/PeerTubeService.php';
|
||||
|
||||
$dbPath = $opts['db'] ?: $appRoot . '/storage/xamxam.db';
|
||||
if (!is_file($dbPath)) { throw new RuntimeException("Database not found: $dbPath"); }
|
||||
|
||||
$out = ['username' => '', 'password' => '', 'smtp' => null, 'peertube' => null];
|
||||
|
||||
$db = new Database($dbPath);
|
||||
$smtp = SmtpRelay::getSettings($db);
|
||||
$out['username'] = $smtp['username'];
|
||||
$out['password'] = $opts['with-pwd'] ? $smtp['password'] : '';
|
||||
|
||||
// ── Probe 1: SMTP (connect + AUTH + close, no message sent) ──────────────────
|
||||
if ($smtp['host'] !== '') {
|
||||
$t = SmtpRelay::test($db);
|
||||
$out['smtp'] = ['ok' => $t['ok'], 'error' => $t['error'], 'field' => $t['field']];
|
||||
} else {
|
||||
$out['smtp'] = ['ok' => false, 'error' => 'SMTP not configured.', 'field' => null];
|
||||
}
|
||||
|
||||
// ── Probe 2: PeerTube OAuth password grant ────────────────────────────────────
|
||||
$peertube = PeerTubeService::getSettings($db);
|
||||
if ($opts['instance']) { $peertube['instance_url'] = rtrim($opts['instance'], '/'); }
|
||||
if ($opts['channel']) { $peertube['channel_name'] = $opts['channel']; }
|
||||
|
||||
if ($peertube['instance_url'] === '') {
|
||||
$out['peertube'] = ['ok' => false, 'error' => 'PeerTube instance not configured.', 'token' => false];
|
||||
} else {
|
||||
try {
|
||||
$a = PeerTubeService::probeAuth($peertube);
|
||||
$out['peertube'] = ['ok' => $a['ok'], 'error' => $a['error'], 'token' => $a['ok']];
|
||||
} catch (\Throwable $e) {
|
||||
$out['peertube'] = ['ok' => false, 'error' => $e->getMessage(), 'token' => false];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), "\n";
|
||||
// Exit 0 only if BOTH probes succeeded.
|
||||
exit(($out['smtp']['ok'] === true) && ($out['peertube']['ok'] === true) ? 0 : 1);
|
||||
Reference in New Issue
Block a user