new system prototype

This commit is contained in:
2026-04-15 10:19:42 +02:00
parent e805f225bc
commit 034b108c7e
80 changed files with 12212 additions and 890 deletions

176
seed_user.php Normal file
View File

@ -0,0 +1,176 @@
<?php
// CLI tool for creating users of any role.
//
// Usage:
// php seed_user.php admin <username> <password> [location]
// php seed_user.php supervisor <username> <password> [location]
// php seed_user.php coach <username> <password> <supervisorID>
//
// Flags:
// --force-change Sets mustChangePassword = 1 (default ON, use --no-force-change to skip)
// --no-force-change Sets mustChangePassword = 0
if (php_sapi_name() !== 'cli') {
http_response_code(403);
echo "CLI only\n";
exit(1);
}
require_once __DIR__ . '/db_init.php';
$usage = <<<USAGE
Usage:
php seed_user.php admin <username> <password> [location]
php seed_user.php supervisor <username> <password> [location]
php seed_user.php coach <username> <password> <supervisorID>
Options:
--no-force-change Skip requiring password change on first login (default: require change)
--list List all existing users instead of creating one
USAGE;
// Strip flag args
$args = [];
$mustChangePassword = 1;
$listMode = false;
foreach (array_slice($argv, 1) as $arg) {
if ($arg === '--no-force-change') { $mustChangePassword = 0; continue; }
if ($arg === '--force-change') { $mustChangePassword = 1; continue; }
if ($arg === '--list') { $listMode = true; continue; }
$args[] = $arg;
}
// List mode
if ($listMode) {
try {
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
$rows = $pdo->query(
"SELECT u.username, u.role, u.entityID, u.mustChangePassword,
COALESCE(a.location, s.location, c.supervisorID) AS detail
FROM users u
LEFT JOIN admin a ON a.adminID = u.entityID AND u.role = 'admin'
LEFT JOIN supervisor s ON s.supervisorID = u.entityID AND u.role = 'supervisor'
LEFT JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach'
ORDER BY u.role, u.username"
)->fetchAll(PDO::FETCH_ASSOC);
qdb_discard($tmpDb, $lockFp);
if (empty($rows)) {
echo "No users found.\n";
exit(0);
}
printf("%-20s %-12s %-40s %s\n", 'Username', 'Role', 'EntityID', 'Detail / SupervisorID');
echo str_repeat('-', 90) . "\n";
foreach ($rows as $r) {
$mustChange = (int)$r['mustChangePassword'] ? ' [must change pw]' : '';
printf("%-20s %-12s %-40s %s%s\n",
$r['username'], $r['role'], $r['entityID'],
$r['detail'] ?? '', $mustChange
);
}
exit(0);
} catch (Throwable $e) {
fwrite(STDERR, "Error: " . $e->getMessage() . "\n");
exit(1);
}
}
if (count($args) < 3) {
fwrite(STDERR, $usage);
exit(1);
}
$role = strtolower($args[0]);
$username = $args[1];
$password = $args[2];
$extra = $args[3] ?? ''; // location (admin/supervisor) or supervisorID (coach)
if (!in_array($role, ['admin', 'supervisor', 'coach'], true)) {
fwrite(STDERR, "Error: role must be admin, supervisor, or coach.\n");
exit(1);
}
if ($username === '' || $password === '') {
fwrite(STDERR, $usage);
exit(1);
}
if (strlen($password) < 6) {
fwrite(STDERR, "Error: Password must be at least 6 characters.\n");
exit(1);
}
if ($role === 'coach' && $extra === '') {
fwrite(STDERR, "Error: coach requires a supervisorID as the 4th argument.\n");
exit(1);
}
try {
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
// Duplicate username check
$chk = $pdo->prepare("SELECT userID FROM users WHERE username = :u");
$chk->execute([':u' => $username]);
if ($chk->fetch()) {
qdb_discard($tmpDb, $lockFp);
fwrite(STDERR, "Error: User '$username' already exists.\n");
exit(1);
}
// For coach, verify supervisorID exists
if ($role === 'coach') {
$sv = $pdo->prepare("SELECT supervisorID FROM supervisor WHERE supervisorID = :sid");
$sv->execute([':sid' => $extra]);
if (!$sv->fetch()) {
qdb_discard($tmpDb, $lockFp);
fwrite(STDERR, "Error: supervisorID '$extra' not found. Create the supervisor first.\n");
exit(1);
}
}
$entityID = bin2hex(random_bytes(16));
$userID = bin2hex(random_bytes(16));
$hash = password_hash($password, PASSWORD_DEFAULT);
$now = time();
$pdo->beginTransaction();
switch ($role) {
case 'admin':
$pdo->prepare("INSERT INTO admin (adminID, username, location) VALUES (:id, :u, :loc)")
->execute([':id' => $entityID, ':u' => $username, ':loc' => $extra]);
break;
case 'supervisor':
$pdo->prepare("INSERT INTO supervisor (supervisorID, username, location) VALUES (:id, :u, :loc)")
->execute([':id' => $entityID, ':u' => $username, ':loc' => $extra]);
break;
case 'coach':
$pdo->prepare("INSERT INTO coach (coachID, supervisorID, username) VALUES (:id, :sid, :u)")
->execute([':id' => $entityID, ':sid' => $extra, ':u' => $username]);
break;
}
$pdo->prepare(
"INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt)
VALUES (:uid, :u, :hash, :role, :eid, :mcp, :now)"
)->execute([
':uid' => $userID,
':u' => $username,
':hash' => $hash,
':role' => $role,
':eid' => $entityID,
':mcp' => $mustChangePassword,
':now' => $now,
]);
$pdo->commit();
$pdo = null;
qdb_save($tmpDb, $lockFp);
$changeNote = $mustChangePassword ? ' (must change password on first login)' : '';
echo ucfirst($role) . " '$username' created successfully$changeNote.\n";
echo "EntityID: $entityID\n";
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp);
fwrite(STDERR, "Error: " . $e->getMessage() . "\n");
exit(1);
}