- Users: 4 profils (admin/coordinator/operator/viewer) remplacent la matrix - /users/add: picker contacts iTop (plus de creation libre) - /me/change-password: flow force_password_change - LDAP: service + section settings + option login - Sync iTop contacts: filtre par teams (SecOps/iPOP/Externe/DSI/Admin DSI) - Auto-desactivation users si contact inactif - etat: alignement sur enum iTop (production/implementation/stock/obsolete) - Menu: Contacts dans Administration, Serveurs en groupe repliable - Audit bases: demo/prod via JWT mode Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
134 lines
7.3 KiB
HTML
134 lines
7.3 KiB
HTML
{% extends 'base.html' %}
|
|
{% block title %}Utilisateurs{% endblock %}
|
|
{% block content %}
|
|
<div class="flex justify-between items-center mb-4">
|
|
<div>
|
|
<h2 class="text-xl font-bold text-cyber-accent">Utilisateurs</h2>
|
|
<p class="text-xs text-gray-500 mt-1">Gestion des comptes et profils — les utilisateurs proviennent des contacts iTop synchronisés</p>
|
|
</div>
|
|
{% if can_edit_users %}
|
|
<a href="/users/add" class="btn-primary px-4 py-2 text-sm">+ Ajouter un utilisateur{% if available_count %} <span class="text-xs opacity-70">({{ available_count }} contact{{ 's' if available_count > 1 else '' }} disponible{{ 's' if available_count > 1 else '' }})</span>{% endif %}</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if msg %}
|
|
<div class="mb-3 p-2 rounded text-sm {% if 'forbidden' in msg or 'error' in msg or 'cant' in msg or 'invalid' in msg or 'required' in msg %}bg-red-900/30 text-cyber-red{% else %}bg-green-900/30 text-cyber-green{% endif %}">
|
|
{% if msg == 'added' %}Utilisateur ajouté.
|
|
{% elif msg == 'exists' %}Cet utilisateur existe déjà.
|
|
{% elif msg == 'role_changed' %}Profil modifié.
|
|
{% elif msg == 'toggled' %}Statut modifié.
|
|
{% elif msg == 'password_changed' %}Mot de passe modifié.
|
|
{% elif msg == 'auth_changed' %}Méthode d'auth modifiée.
|
|
{% elif msg == 'deleted' %}Utilisateur supprimé.
|
|
{% elif msg == 'cant_self' %}Impossible sur votre propre compte.
|
|
{% elif msg == 'cant_demote_self' %}Vous ne pouvez pas vous rétrograder.
|
|
{% elif msg == 'cant_delete_admin' %}Le compte admin local ne peut pas être supprimé.
|
|
{% elif msg == 'forbidden' %}Permission refusée.
|
|
{% elif msg == 'invalid_role' %}Profil invalide.
|
|
{% elif msg == 'contact_required' %}Sélectionner un contact.
|
|
{% else %}{{ msg }}{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Legende des profils -->
|
|
<div class="card p-4 mb-4">
|
|
<h3 class="text-sm font-bold text-cyber-accent mb-2">Profils</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-3 text-xs">
|
|
{% for r in roles %}
|
|
<div>
|
|
<div class="font-bold text-cyber-accent">{{ profile_labels[r] }}</div>
|
|
<div class="text-gray-400 mt-1">{{ profile_descriptions[r] }}</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tableau users -->
|
|
<div class="card overflow-hidden">
|
|
<table class="w-full table-cyber text-xs">
|
|
<thead><tr>
|
|
<th class="p-2 text-left">Utilisateur</th>
|
|
<th class="p-2">Profil</th>
|
|
<th class="p-2">Auth</th>
|
|
<th class="p-2">Team iTop</th>
|
|
<th class="p-2">Email</th>
|
|
<th class="p-2">Statut</th>
|
|
<th class="p-2">Dernier login</th>
|
|
<th class="p-2">Actions</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
{% for u in users %}
|
|
<tr class="border-t border-cyber-border/30 {% if not u.is_active %}opacity-50{% endif %}">
|
|
<td class="p-2">
|
|
<div class="font-mono font-bold">{{ u.username }}</div>
|
|
<div class="text-gray-400">{{ u.display_name or '' }}</div>
|
|
{% if u.force_password_change %}<div class="text-cyber-yellow" style="font-size:9px">Doit changer mdp</div>{% endif %}
|
|
</td>
|
|
<td class="p-2 text-center">
|
|
{% if can_edit_users %}
|
|
<form method="POST" action="/users/{{ u.id }}/role" style="display:inline">
|
|
<select name="role" onchange="this.form.submit()" class="text-xs py-1 px-2">
|
|
{% for r in roles %}
|
|
<option value="{{ r }}" {% if u.role == r %}selected{% endif %}>{{ profile_labels[r] }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</form>
|
|
{% else %}
|
|
<span class="badge badge-blue">{{ profile_labels[u.role] or u.role }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="p-2 text-center">
|
|
{% if can_edit_users %}
|
|
<form method="POST" action="/users/{{ u.id }}/auth_type" style="display:inline">
|
|
<select name="auth_type" onchange="this.form.submit()" class="text-xs py-1 px-2">
|
|
<option value="local" {% if u.auth_type == 'local' %}selected{% endif %}>Local</option>
|
|
<option value="ldap" {% if u.auth_type == 'ldap' %}selected{% endif %}>LDAP</option>
|
|
</select>
|
|
</form>
|
|
{% else %}
|
|
<span class="badge {% if u.auth_type == 'ldap' %}badge-blue{% else %}badge-gray{% endif %}">{{ u.auth_type }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="p-2 text-center text-gray-400">
|
|
{% if u.contact_team %}<span class="badge badge-gray">{{ u.contact_team }}</span>{% else %}<span class="text-gray-600">—</span>{% endif %}
|
|
</td>
|
|
<td class="p-2 text-gray-400">{{ u.email or '—' }}</td>
|
|
<td class="p-2 text-center">
|
|
<span class="badge {% if u.is_active %}badge-green{% else %}badge-red{% endif %}">{{ 'Actif' if u.is_active else 'Inactif' }}</span>
|
|
</td>
|
|
<td class="p-2 text-center text-gray-400" style="font-size:10px">{% if u.last_login %}{{ u.last_login.strftime('%Y-%m-%d %H:%M') }}{% else %}—{% endif %}</td>
|
|
<td class="p-2 text-center">
|
|
{% if can_edit_users %}
|
|
<form method="POST" action="/users/{{ u.id }}/toggle" style="display:inline">
|
|
<button type="submit" class="text-xs {% if u.is_active %}text-cyber-yellow{% else %}text-cyber-green{% endif %} hover:underline">{{ 'Désactiver' if u.is_active else 'Activer' }}</button>
|
|
</form>
|
|
<button onclick="document.getElementById('pw-{{ u.id }}').style.display='table-row'" class="text-xs text-gray-400 hover:text-cyber-accent ml-2">Mdp</button>
|
|
{% if can_admin_users and u.username != 'admin' %}
|
|
<form method="POST" action="/users/{{ u.id }}/delete" style="display:inline" onsubmit="return confirm('Supprimer {{ u.username }} ?')">
|
|
<button type="submit" class="text-xs text-cyber-red hover:underline ml-2">Supprimer</button>
|
|
</form>
|
|
{% endif %}
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% if can_edit_users %}
|
|
<tr id="pw-{{ u.id }}" style="display:none" class="bg-cyber-border/20">
|
|
<td colspan="8" class="p-3">
|
|
<form method="POST" action="/users/{{ u.id }}/password" class="flex gap-2 items-center">
|
|
<label class="text-xs text-gray-400">Nouveau mot de passe pour <b>{{ u.username }}</b> :</label>
|
|
<input type="password" name="new_password" required minlength="6" class="text-xs py-1 px-2 flex-1">
|
|
<label class="text-xs text-gray-400 flex items-center gap-1">
|
|
<input type="checkbox" name="force_change" checked> Doit changer au 1er login
|
|
</label>
|
|
<button type="submit" class="btn-primary px-3 py-1 text-xs">Enregistrer</button>
|
|
<button type="button" onclick="document.getElementById('pw-{{ u.id }}').style.display='none'" class="text-xs text-gray-500">Annuler</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %}
|