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>
82 lines
3.5 KiB
PHP
82 lines
3.5 KiB
PHP
<?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()];
|
|
}
|