patchcenter/app/templates/users.html
Khalid MOUTAOUAKIL 53c393b49b Permissions DB, créneaux auto, assignations, audit Splunk, accents
- Permissions 100% depuis user_permissions (plus de hardcode)
- Middleware injecte perms dans chaque requête
- Créneaux auto: 09h-12h30 / 14h-16h45, pas 15min, hprod lun-mar, prod mer-jeu
- Assignations par défaut: par domaine, app_type, zone, serveur (table default_assignments)
- Auto-liaison app_group: même intervenant recette+prod
- Audit Splunk: /var/log/patchcenter_audit.json (JSON one-line par event)
- Login/logout/campagnes/prereqs loggés en base + fichier
- Page erreur maintenance (500/404) avec contact SecOps
- Accents français dans toute lUI
- Operator affiché comme Intervenant
- Session 1h, redirect / vers dashboard si connecté
- Demo mode prereqs (DEMO_MODE=True)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 15:25:43 +02:00

137 lines
8.1 KiB
HTML

{% extends 'base.html' %}
{% block title %}Utilisateurs{% endblock %}
{% block content %}
<h2 class="text-xl font-bold text-cyber-accent mb-6">Utilisateurs & Permissions</h2>
{% if msg %}
<div class="mb-4 p-3 rounded text-sm {% if msg in ('forbidden','exists','exists_inactive','cant_self') %}bg-red-900/30 text-cyber-red{% else %}bg-green-900/30 text-cyber-green{% endif %}">
{% if msg == 'added' %}Utilisateur créé.{% elif msg == 'edited' %}Utilisateur modifié.{% elif msg == 'password_changed' %}Mot de passe modifié.{% elif msg == 'toggled' %}Statut modifié.{% elif msg == 'perms_saved' %}Permissions sauvegardées.{% elif msg == 'deleted' %}Utilisateur supprimé.{% elif msg == 'exists' %}Ce nom d'utilisateur existe déjà.{% elif msg == 'exists_inactive' %}Ce nom existe déjà (désactivé). Réactivez-le plutôt.{% elif msg == 'cant_self' %}Vous ne pouvez pas vous désactiver/supprimer vous-même.{% elif msg == 'forbidden' %}Action non autorisée.{% endif %}
</div>
{% endif %}
<!-- Liste utilisateurs -->
<div x-data="{ editing: '', editUser: null }" class="space-y-3">
{% for ud in users_data %}
<div class="card overflow-hidden">
<div class="flex items-center justify-between p-4 cursor-pointer hover:bg-cyber-border/20" @click="editing = editing === '{{ ud.user.id }}' ? '' : '{{ ud.user.id }}'">
<div class="flex items-center gap-3">
<span class="font-bold {% if ud.user.is_active %}text-cyber-accent{% else %}text-gray-600 line-through{% endif %}">{{ ud.user.username }}</span>
<span class="text-sm text-gray-400">{{ ud.user.display_name }}</span>
<span class="badge {% if ud.user.role == 'admin' %}badge-red{% elif ud.user.role == 'coordinator' %}badge-yellow{% elif ud.user.role == 'operator' %}badge-blue{% else %}badge-gray{% endif %}">{% if ud.user.role == "operator" %}intervenant{% else %}{{ ud.user.role }}{% endif %}</span>
<span class="badge {% if ud.user.is_active %}badge-green{% else %}badge-red{% endif %}">{{ 'Actif' if ud.user.is_active else 'Inactif' }}</span>
{% if ud.user.email %}<span class="text-xs text-gray-500">{{ ud.user.email }}</span>{% endif %}
</div>
<div class="flex items-center gap-2">
{% for m in modules %}
{% if ud.perms.get(m) %}
<span class="text-xs px-1 rounded {% if ud.perms[m] == 'admin' %}bg-red-900/30 text-cyber-red{% elif ud.perms[m] == 'edit' %}bg-blue-900/30 text-cyber-accent{% else %}bg-gray-800 text-gray-500{% endif %}" title="{{ m }}:{{ ud.perms[m] }}">{{ m[:3] }}</span>
{% endif %}
{% endfor %}
<span class="text-gray-500 text-lg" x-text="editing === '{{ ud.user.id }}' ? '&#9660;' : '&#9654;'"></span>
</div>
</div>
<div x-show="editing === '{{ ud.user.id }}'" class="border-t border-cyber-border p-4 space-y-4">
{% if can_edit_users %}
<!-- Éditer infos user -->
<form method="POST" action="/users/{{ ud.user.id }}/edit" class="flex gap-3 items-end">
<div>
<label class="text-xs text-gray-500">Nom complet</label>
<input type="text" name="display_name" value="{{ ud.user.display_name }}" class="text-xs py-1 px-2 w-40">
</div>
<div>
<label class="text-xs text-gray-500">Email</label>
<input type="email" name="email" value="{{ ud.user.email or '' }}" class="text-xs py-1 px-2 w-44">
</div>
<div>
<label class="text-xs text-gray-500">Role</label>
<select name="role" class="text-xs py-1 px-2">
{% for r in ['admin','coordinator','operator','viewer'] %}
<option value="{{ r }}" {% if r == ud.user.role %}selected{% endif %}>{% if r == "operator" %}intervenant{% else %}{{ r }}{% endif %}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn-sm bg-cyber-accent text-black">Modifier</button>
</form>
<!-- Permissions par module -->
<form method="POST" action="/users/{{ ud.user.id }}/permissions">
<h4 class="text-xs text-cyber-accent font-bold uppercase mb-2">Permissions par module</h4>
<div class="grid grid-cols-8 gap-2">
{% for m in modules %}
<div>
<label class="text-xs text-gray-500 block mb-1">{{ m }}</label>
<select name="perm_{{ m }}" class="w-full text-xs py-1">
<option value=""></option>
{% for l in levels %}
<option value="{{ l }}" {% if ud.perms.get(m) == l %}selected{% endif %}>{{ l }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
<button type="submit" class="btn-primary px-4 py-1 text-sm mt-2">Sauvegarder permissions</button>
</form>
<!-- Actions -->
<div class="flex gap-3 pt-2 border-t border-cyber-border items-center">
<form method="POST" action="/users/{{ ud.user.id }}/password" class="flex gap-2 items-center">
<input type="password" name="new_password" placeholder="Nouveau mot de passe" class="text-xs py-1 px-2 w-48">
<button type="submit" class="btn-sm bg-cyber-border text-cyber-accent">Changer MDP</button>
</form>
<form method="POST" action="/users/{{ ud.user.id }}/toggle">
<button type="submit" class="btn-sm {% if ud.user.is_active %}bg-red-900/30 text-cyber-red{% else %}bg-green-900/30 text-cyber-green{% endif %}">
{{ 'Désactiver' if ud.user.is_active else 'Activer' }}
</button>
</form>
<form method="POST" action="/users/{{ ud.user.id }}/delete">
<button type="submit" class="btn-sm bg-red-900/50 text-cyber-red" onclick="return confirm('SUPPRIMER définitivement {{ ud.user.username }} ?')">Supprimer</button>
</form>
</div>
{% else %}
<p class="text-xs text-gray-500">Permissions en lecture seule</p>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- Ajouter un utilisateur -->
{% if can_edit_users %}
<div class="card p-5 mt-6">
<h3 class="text-sm font-bold text-cyber-accent mb-3">Ajouter un utilisateur</h3>
<form method="POST" action="/users/add" class="space-y-3">
<div class="grid grid-cols-4 gap-3">
<div>
<label class="text-xs text-gray-500">Username</label>
<input type="text" name="new_username" required class="w-full">
</div>
<div>
<label class="text-xs text-gray-500">Nom complet</label>
<input type="text" name="new_display_name" required class="w-full">
</div>
<div>
<label class="text-xs text-gray-500">Email</label>
<input type="email" name="new_email" class="w-full">
</div>
<div>
<label class="text-xs text-gray-500">Role</label>
<select name="new_role" class="w-full">
<option value="operator">intervenant</option>
<option value="coordinator">coordinator</option>
<option value="admin">admin</option>
<option value="viewer">viewer</option>
</select>
</div>
</div>
<div class="w-64">
<label class="text-xs text-gray-500">Mot de passe</label>
<input type="password" name="new_password" required class="w-full">
</div>
<p class="text-xs text-gray-600">Permissions pre-remplies selon le role. Modifiables ensuite.</p>
<button type="submit" class="btn-primary px-4 py-2 text-sm">Créer</button>
</form>
</div>
{% endif %}
{% endblock %}