Initial import of fitme3070.pt platform

Plain-PHP fleet platform with id.andmore.pt SSO. Wires shared/platform-tools
as a submodule (platform_update, backups, git/migration tools) like the other
platforms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
andreandClaude Opus 4.8 committed 2026-06-12 10:44:58 +01:00
commit e68d357177
2330 files changed
+215968

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
<?php
// ─── Backoffice user creation ────────────────────────────────────────────────
// This install is SSO-only — clients authenticate on id.andmore.pt and their
// local row is provisioned by sso_resolve_session() (config/sso.php). The only
// users created locally are backoffice accounts (sysadmin / admin / staff):
// - auth_create_sysadmin() → first-install bootstrap (views/auth/setup.php)
// - auth_create_admin()/staff() → admin-vouched, link the central Andmore ID
// row immediately so the new person can SSO on any sibling .andmore.pt site.
//
// parse_user_agent() lives in config/functions.php; db_get_all_staff() /
// db_get_active_admins() live in config/functions_db.php — not redefined here.
// True once any sysadmin/admin row exists — gates views/auth/setup.php.
function admin_exists(PDO $pdo): bool {
return (bool) $pdo->query(
"SELECT 1 FROM users WHERE role IN ('sysadmin','admin') LIMIT 1"
)->fetchColumn();
}
// Creates a sysadmin (full access incl. backups + platform update). Local-only
// password row; federated linkage happens on first SSO by email match.
function auth_create_sysadmin(PDO $pdo, string $name, string $email, string $password): int {
$stmt = $pdo->prepare("
INSERT INTO users (name, email, password_hash, role)
VALUES (?, ?, ?, 'sysadmin')
");
$stmt->execute([
$name,
strtolower(trim($email)),
password_hash($password, PASSWORD_DEFAULT),
]);
return (int) $pdo->lastInsertId();
}
// Returns ['id'=>int] on success, or ['error'=>'andmore_email_taken'|'unavailable'].
function auth_create_admin(PDO $pdo, string $name, string $email, string $phone, string $password): array {
return _create_backoffice_user($pdo, 'admin', $name, $email, $phone, $password);
}
function auth_create_staff(PDO $pdo, string $name, string $email, string $phone, string $password): array {
return _create_backoffice_user($pdo, 'staff', $name, $email, $phone, $password);
}
// Internal: create an admin/staff row and, in federated mode, link it to the
// central Andmore ID (admin-vouched — the local password is mirrored upstream
// so SSO_REQUIRED=true never locks out the new user). See config/sso.php
// identity_upstream_link_immediate() for the upstream rules.
function _create_backoffice_user(PDO $pdo, string $role, string $name, string $email, string $phone, string $password): array {
if (!in_array($role, ['admin', 'staff'], true)) {
throw new InvalidArgumentException("Invalid backoffice role: {$role}");
}
$email = strtolower(trim($email));
$canonicalId = null;
if (identity_is_federated()) {
$link = identity_upstream_link_immediate($email, $name, $phone, $password);
if (($link['error'] ?? '') === 'andmore_email_taken') {
return ['error' => 'andmore_email_taken'];
}
if (($link['error'] ?? '') === 'unavailable' && identity_require_upstream()) {
return ['error' => 'unavailable'];
}
if (isset($link['canonical_id'])) {
$canonicalId = (int) $link['canonical_id'];
}
}
$stmt = $pdo->prepare("
INSERT INTO users (name, email, phone, password_hash, role, andmore_id)
VALUES (?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$name,
$email,
$phone ?: null,
password_hash($password, PASSWORD_DEFAULT),
$role,
$canonicalId,
]);
return ['id' => (int) $pdo->lastInsertId()];
}