259 lines
7.1 KiB
PHP
259 lines
7.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
function isLoggedIn(): bool
|
|
{
|
|
return !empty($_SESSION['user_email']);
|
|
}
|
|
|
|
function requireAuth(): void
|
|
{
|
|
if (!isLoggedIn()) {
|
|
$return = $_SERVER['REQUEST_URI'] ?? '/';
|
|
header('Location: /login' . ($return !== '/' ? '?return_to=' . urlencode($return) : ''), true, 302);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function currentUserEmail(): ?string
|
|
{
|
|
return $_SESSION['user_email'] ?? null;
|
|
}
|
|
|
|
function currentUserName(): string
|
|
{
|
|
if (!isLoggedIn()) {
|
|
return '';
|
|
}
|
|
if (isset($_SESSION['user_display_name']) && $_SESSION['user_display_name'] !== '') {
|
|
return $_SESSION['user_display_name'];
|
|
}
|
|
$name = authorDisplayName(currentUserEmail() ?? '');
|
|
$_SESSION['user_display_name'] = $name;
|
|
return $name;
|
|
}
|
|
|
|
function authorDisplayName(string $email): string
|
|
{
|
|
return authorProfile($email)['name'];
|
|
}
|
|
|
|
function authorProfileUrl(string $email): string
|
|
{
|
|
return authorProfile($email)['url'];
|
|
}
|
|
|
|
function authorProfile(string $email): array
|
|
{
|
|
static $cache = [];
|
|
$key = strtolower(trim($email));
|
|
if ($key === '') {
|
|
return ['name' => '', 'url' => ''];
|
|
}
|
|
if (array_key_exists($key, $cache)) {
|
|
return $cache[$key];
|
|
}
|
|
$pdo = dbPdo();
|
|
if ($pdo) {
|
|
try {
|
|
$st = $pdo->prepare('SELECT display_name, profile_url FROM user_profiles WHERE email = :e');
|
|
$st->execute([':e' => $key]);
|
|
$row = $st->fetch(PDO::FETCH_ASSOC);
|
|
if ($row) {
|
|
$cache[$key] = [
|
|
'name' => ($row['display_name'] !== '') ? $row['display_name'] : explode('@', $key)[0],
|
|
'url' => $row['profile_url'] ?? '',
|
|
];
|
|
return $cache[$key];
|
|
}
|
|
} catch (\Throwable) {
|
|
}
|
|
}
|
|
$cache[$key] = ['name' => explode('@', $key)[0], 'url' => ''];
|
|
return $cache[$key];
|
|
}
|
|
|
|
function dbPdo(): ?PDO
|
|
{
|
|
static $pdo = null;
|
|
static $failed = false;
|
|
if ($failed) {
|
|
return null;
|
|
}
|
|
if ($pdo !== null) {
|
|
return $pdo;
|
|
}
|
|
$dsn = $_ENV['DB_DSN'] ?? (getenv('DB_DSN') ?: '');
|
|
$user = $_ENV['DB_USER'] ?? (getenv('DB_USER') ?: '');
|
|
$pass = $_ENV['DB_PASS'] ?? (getenv('DB_PASS') ?: '');
|
|
if (!$dsn) {
|
|
$failed = true;
|
|
return null;
|
|
}
|
|
try {
|
|
$pdo = new PDO($dsn, $user ?: null, $pass ?: null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
|
} catch (\Throwable) {
|
|
$failed = true;
|
|
return null;
|
|
}
|
|
return $pdo;
|
|
}
|
|
|
|
function currentUserRoles(): array
|
|
{
|
|
if (!isLoggedIn()) {
|
|
return [];
|
|
}
|
|
if (isset($_SESSION['user_roles'])) {
|
|
return $_SESSION['user_roles'];
|
|
}
|
|
$pdo = dbPdo();
|
|
if (!$pdo) {
|
|
$_SESSION['user_roles'] = [];
|
|
return [];
|
|
}
|
|
try {
|
|
$st = $pdo->prepare(
|
|
'SELECT r.name FROM roles r
|
|
JOIN user_roles ur ON ur.role_id = r.id
|
|
WHERE ur.user_email = :e'
|
|
);
|
|
$st->execute([':e' => strtolower(currentUserEmail() ?? '')]);
|
|
$_SESSION['user_roles'] = $st->fetchAll(PDO::FETCH_COLUMN) ?: [];
|
|
} catch (\Throwable) {
|
|
$_SESSION['user_roles'] = [];
|
|
}
|
|
return $_SESSION['user_roles'];
|
|
}
|
|
|
|
function hasRole(string $role): bool
|
|
{
|
|
return in_array($role, currentUserRoles(), true);
|
|
}
|
|
|
|
// Capacités connues — clé => label affiché dans l'admin
|
|
const KNOWN_CAPABILITIES = [
|
|
'propose_articles' => 'Proposer des articles',
|
|
'validate_articles_all' => 'Valider des articles',
|
|
'validate_articles_own' => 'Valider ses articles uniquement',
|
|
'publish_articles_all' => 'Publier des articles',
|
|
'publish_articles_own' => 'Publier ses articles uniquement',
|
|
'edit_articles_all' => 'Modifier des articles',
|
|
'edit_articles_own' => 'Modifier ses articles uniquement',
|
|
'rate_articles' => 'Noter des articles',
|
|
'view_previews' => 'Lire des avant-premières',
|
|
'view_drafts_all' => 'Voir tous les brouillons',
|
|
'view_drafts_own' => 'Voir ses brouillons',
|
|
'view_sources_all' => 'Voir les sources (tous les articles)',
|
|
'view_sources_own' => 'Voir les sources de ses articles',
|
|
];
|
|
|
|
// Groupes pour l'interface d'administration
|
|
const CAPABILITY_GROUPS = [
|
|
'Articles' => [
|
|
'propose_articles',
|
|
'validate_articles_all',
|
|
'validate_articles_own',
|
|
'publish_articles_all',
|
|
'publish_articles_own',
|
|
'edit_articles_all',
|
|
'edit_articles_own',
|
|
],
|
|
'Accès & lecture' => [
|
|
'rate_articles',
|
|
'view_previews',
|
|
'view_drafts_all',
|
|
'view_drafts_own',
|
|
'view_sources_all',
|
|
'view_sources_own',
|
|
],
|
|
];
|
|
|
|
function currentUserCapabilities(): array
|
|
{
|
|
if (!isLoggedIn()) {
|
|
return [];
|
|
}
|
|
if (isset($_SESSION['user_capabilities'])) {
|
|
return $_SESSION['user_capabilities'];
|
|
}
|
|
$pdo = dbPdo();
|
|
if (!$pdo) {
|
|
$_SESSION['user_capabilities'] = [];
|
|
return [];
|
|
}
|
|
try {
|
|
$st = $pdo->prepare(
|
|
'SELECT DISTINCT rc.capability
|
|
FROM role_capabilities rc
|
|
JOIN user_roles ur ON ur.role_id = rc.role_id
|
|
WHERE ur.user_email = :e'
|
|
);
|
|
$st->execute([':e' => strtolower(currentUserEmail() ?? '')]);
|
|
$_SESSION['user_capabilities'] = $st->fetchAll(PDO::FETCH_COLUMN) ?: [];
|
|
} catch (\Throwable) {
|
|
$_SESSION['user_capabilities'] = [];
|
|
}
|
|
return $_SESSION['user_capabilities'];
|
|
}
|
|
|
|
function hasCapability(string $cap): bool
|
|
{
|
|
if (isAdmin()) {
|
|
return true;
|
|
}
|
|
return in_array($cap, currentUserCapabilities(), true);
|
|
}
|
|
|
|
function canDoOnArticle(string $baseCap, array $article): bool
|
|
{
|
|
if (isAdmin()) {
|
|
return true;
|
|
}
|
|
if (hasCapability($baseCap . '_all')) {
|
|
return true;
|
|
}
|
|
if (hasCapability($baseCap . '_own')) {
|
|
$owner = strtolower($article['author'] ?? '');
|
|
return $owner !== '' && $owner === strtolower(currentUserEmail() ?? '');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isAdmin(): bool
|
|
{
|
|
$email = currentUserEmail();
|
|
if (!$email) {
|
|
return false;
|
|
}
|
|
// Fallback bootstrap : var d'env
|
|
$rawAdmin = $_ENV['ADMIN_EMAIL'] ?? (getenv('ADMIN_EMAIL') ?: '');
|
|
$allowed = array_filter(array_map('trim', explode(',', (string)$rawAdmin)));
|
|
if (in_array(strtolower($email), array_map('strtolower', $allowed), true)) {
|
|
return true;
|
|
}
|
|
return hasRole('admin');
|
|
}
|
|
|
|
function ssoLogoutUrl(): string
|
|
{
|
|
$issuer = rtrim((string)($_ENV['OIDC_ISSUER'] ?? (getenv('OIDC_ISSUER') ?: '')), '/');
|
|
$clientId = (string)($_ENV['OIDC_CLIENT_ID'] ?? (getenv('OIDC_CLIENT_ID') ?: ''));
|
|
$baseUrl = rtrim((string)($_ENV['APP_URL'] ?? (getenv('APP_URL') ?: '/')), '/');
|
|
|
|
$params = [
|
|
'client_id' => $clientId,
|
|
'post_logout_redirect_uri' => $baseUrl . '/',
|
|
];
|
|
if (!empty($_SESSION['oidc']['id_token'])) {
|
|
$params['id_token_hint'] = $_SESSION['oidc']['id_token'];
|
|
}
|
|
|
|
if (!$issuer) {
|
|
return $baseUrl . '/';
|
|
}
|
|
|
|
return $issuer . '/protocol/openid-connect/logout?' . http_build_query($params);
|
|
}
|