CI / test (push) Successful in 1m56s
Depuis 0.24.0, /mcp exige session ou Basic : le service d'agents s'y faisait refuser en 401 (vu en production, panneau Spark du 2026-10-21). - Table api_tokens (empreinte SHA-256 seule), CRUD dans Mon profil - Bearer honoré par le middleware sur /mcp et /api/ uniquement — jamais l'interface web ; révocation et désactivation immédiates - 235 tests verts (3 nouveaux : cycle de vie, compte désactivé, handshake MCP par jeton quand l'auth est active)
133 lines
7.6 KiB
HTML
133 lines
7.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr" data-theme="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="csrf-token" content="{{ csrf_token }}">
|
|
<title>Mon profil — Kanbante</title>
|
|
<link rel="stylesheet" href="/static/css/style.css?v={{ version }}">
|
|
</head>
|
|
<body>
|
|
{% include "_header.html" %}
|
|
|
|
<main class="focus-main">
|
|
<div class="page-head">
|
|
<h1>👤 Mon profil</h1>
|
|
<p class="page-sub">{{ profile_user.username }} · rôle : {{ profile_user.role }}</p>
|
|
</div>
|
|
{% if msg %}<p class="chip-ok">✅ {{ msg }}</p>{% endif %}
|
|
{% if err %}<p class="chip-warn">⚠️ {{ err }}</p>{% endif %}
|
|
{% if profile_user.must_change_password %}<p class="chip-warn">⚠️ Ton mot de passe est temporaire : change-le ci-dessous.</p>{% endif %}
|
|
|
|
<section class="briefing-card">
|
|
<h2>Informations</h2>
|
|
<form method="post" action="/profile" class="admin-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<label>Nom d'affichage <input type="text" name="display_name" value="{{ profile_user.display_name or '' }}"></label>
|
|
<button type="submit" class="btn-save btn-inline">Enregistrer</button>
|
|
</form>
|
|
<p class="muted">Mes espaces :
|
|
{% for m in memberships %}<span class="ws-dot" style="background: {{ m.color }}"></span> {{ m.name }} ({{ m.member_role }}){% if not loop.last %} · {% endif %}
|
|
{% else %}aucun{% endfor %}
|
|
</p>
|
|
</section>
|
|
|
|
<section class="briefing-card">
|
|
<h2>🔑 Mot de passe</h2>
|
|
{% if profile_user.id %}
|
|
<form method="post" action="/profile/password" class="admin-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<label>Mot de passe actuel <input type="password" name="current_password" required autocomplete="current-password"></label>
|
|
<label>Nouveau mot de passe <input type="password" name="new_password" required autocomplete="new-password"></label>
|
|
<button type="submit" class="btn-save btn-inline">Changer</button>
|
|
</form>
|
|
{% else %}
|
|
<p class="muted">Compte défini par l'environnement (KANBANTE_AUTH_*) : le mot de passe se change dans la configuration du déploiement.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="briefing-card">
|
|
<h2>🗝️ Jetons d'API (MCP)</h2>
|
|
<p class="muted">Pour les consommateurs machine — le service d'agents, en premier lieu. Un jeton (préfixe <code>kbt_</code>) authentifie uniquement <code>/mcp</code> et <code>/api/</code>, jamais l'interface web. Il se présente en en-tête <code>Authorization: Bearer kbt_…</code>. Le texte clair n'est affiché qu'à la création ; ensuite, seule son empreinte est conservée.</p>
|
|
{% if profile_user.id %}
|
|
{% if nouveau_jeton %}
|
|
<p>✅ Nouveau jeton — <strong>copie-le maintenant</strong> :</p>
|
|
<p><code style="font-size:15px">{{ nouveau_jeton }}</code></p>
|
|
{% endif %}
|
|
{% if api_tokens %}
|
|
<ul>
|
|
{% for t in api_tokens %}
|
|
<li>
|
|
<strong>{{ t.name }}</strong> · créé le {{ t.created_at }}
|
|
· {% if t.last_used_at %}dernier usage : {{ t.last_used_at }}{% else %}jamais utilisé{% endif %}
|
|
{% if t.revoked_at %} · <span class="chip-warn">révoqué le {{ t.revoked_at }}</span>
|
|
{% else %}
|
|
<form method="post" action="/profile/tokens/{{ t.id }}/revoke" class="inline-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button type="submit" class="btn-mini">Révoquer</button>
|
|
</form>
|
|
{% endif %}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p class="muted">Aucun jeton pour l'instant.</p>
|
|
{% endif %}
|
|
<form method="post" action="/profile/tokens" class="admin-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<label>Nom du jeton <input type="text" name="name" placeholder="service-agents" required></label>
|
|
<button type="submit" class="btn-save btn-inline">Créer un jeton</button>
|
|
</form>
|
|
{% else %}
|
|
<p class="muted">Compte défini par l'environnement (KANBANTE_AUTH_*) : les jetons appartiennent aux comptes de la base — connecte-toi avec ton compte personnel pour en créer un.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="briefing-card">
|
|
<h2>✦ Liaison draco (SSO)</h2>
|
|
{% if profile_user.draco_sub %}
|
|
<p>Compte lié à l'identité draco <code>{{ profile_user.draco_sub }}</code> — la connexion via ObsiGate ouvre directement cette session.</p>
|
|
<form method="post" action="/profile/draco/unlink" class="inline-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button type="submit" class="btn-mini">Délier</button>
|
|
</form>
|
|
{% else %}
|
|
<p class="muted">Pas de liaison. Elle se crée automatiquement à la première visite où ta session Kanbante et ton cookie draco coexistent (par exemple en arrivant depuis ObsiGate).</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="briefing-card">
|
|
<h2>🔌 Mes connexions personnelles</h2>
|
|
<p class="muted">Visibles de toi seul. Pour les utiliser, ajoute-les à un de tes espaces (ou demande à un admin d'espace).</p>
|
|
{% for c in my_connections %}
|
|
<p>
|
|
{% if c.kind == 'github' %}🐙{% else %}🍵{% endif %} <strong>{{ c.name }}</strong>
|
|
<span class="muted">({{ c.slug }}) · {{ c.base_url }} · compte : {{ c.account_login or 'à tester' }} · jeton : {{ c.token_mask }}</span>
|
|
<form method="post" action="/profile/connections/{{ c.id }}/test" class="inline-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button type="submit" class="btn-mini">🧪 Tester</button>
|
|
</form>
|
|
<form method="post" action="/profile/connections/{{ c.id }}/delete" class="inline-form" onsubmit="return confirm('Supprimer cette connexion personnelle ?');">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button type="submit" class="btn-mini btn-danger">Supprimer</button>
|
|
</form>
|
|
</p>
|
|
{% endfor %}
|
|
<form method="post" action="/profile/connections" class="admin-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<label>Type
|
|
<select name="kind">
|
|
<option value="gitea">Gitea</option>
|
|
<option value="github">GitHub</option>
|
|
</select>
|
|
</label>
|
|
<label>Nom <input type="text" name="name" required></label>
|
|
<label>URL de base <input type="text" name="base_url" required placeholder="https://api.github.com"></label>
|
|
<label>Jeton d'accès <input type="password" name="token" autocomplete="off"></label>
|
|
<button type="submit" class="btn-save btn-inline">Ajouter</button>
|
|
</form>
|
|
</section>
|
|
</main>
|
|
</body>
|
|
</html>
|