Bootstrap, Host-Key-Pruefung und spaeteres Ausrollen brauchen echte sshd-Gegenstellen. Zwei Debian-13-Container (ziel-a, ziel-b) in einem gemeinsamen Netz bilden frische Hosts vor dem Bootstrap nach: root- und Passwort-Login sind absichtlich offen, Host-Keys entstehen erst beim Start, und `hostkeys-tauschen` erlaubt den Test "geaenderter Host-Key wird abgelehnt" ohne Neuerstellen des Containers. Die Testhilfen lesen dieselben Adress-Variablen wie die Compose-Datei und lassen Tests mit Hinweis auf den Startbefehl fehlschlagen, statt sie still zu ueberspringen - ein uebersprungener Integrationstest sieht sonst wie ein bestandener aus. Der Mock-OIDC-Anbieter ersetzt authentik in der Entwicklung (PLAN.md, Entscheidung 7). Bei interaktivem Login uebernimmt er nur Claims aus dem Formular, daher belegt eine eigene Login-Seite email, name und groups vor und laesst die Gruppen frei waehlen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
3.8 KiB
HTML
125 lines
3.8 KiB
HTML
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Mock-OIDC – Anmeldung (nur Entwicklung)</title>
|
||
<style>
|
||
body {
|
||
font-family: system-ui, sans-serif;
|
||
background: #111418;
|
||
color: #e6e8eb;
|
||
margin: 0;
|
||
padding: 2rem 1rem;
|
||
}
|
||
main {
|
||
max-width: 34rem;
|
||
margin: 0 auto;
|
||
}
|
||
h1 {
|
||
font-size: 1.3rem;
|
||
}
|
||
p.hinweis {
|
||
color: #f0b429;
|
||
font-size: 0.9rem;
|
||
}
|
||
label {
|
||
display: block;
|
||
margin: 1rem 0 0.3rem;
|
||
font-weight: 600;
|
||
}
|
||
input,
|
||
textarea {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
background: #1b2027;
|
||
color: inherit;
|
||
border: 1px solid #39414d;
|
||
border-radius: 6px;
|
||
padding: 0.5rem;
|
||
font-family: ui-monospace, monospace;
|
||
font-size: 0.9rem;
|
||
}
|
||
.rollen {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
flex-wrap: wrap;
|
||
margin-top: 1rem;
|
||
}
|
||
button {
|
||
background: #2a3240;
|
||
color: inherit;
|
||
border: 1px solid #39414d;
|
||
border-radius: 6px;
|
||
padding: 0.4rem 0.8rem;
|
||
cursor: pointer;
|
||
}
|
||
button[type="submit"] {
|
||
background: #2f6fde;
|
||
border-color: #2f6fde;
|
||
margin-top: 1rem;
|
||
width: 100%;
|
||
padding: 0.6rem;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main>
|
||
<h1>Mock-OIDC-Anmeldung</h1>
|
||
<p class="hinweis">
|
||
Nur für die Entwicklung (#011). Benutzername wird zu <code>sub</code>, die Claims unten landen im
|
||
ID-Token und unter <code>/userinfo</code>.
|
||
</p>
|
||
<!-- Ohne action postet das Formular an die /authorize-URL samt Query zurück –
|
||
genau das erwartet mock-oauth2-server. -->
|
||
<form method="post">
|
||
<label for="username">Benutzername (sub)</label>
|
||
<input id="username" name="username" required value="admin" autocomplete="off" />
|
||
<div class="rollen">
|
||
<button type="button" data-rolle="admin">Admin</button>
|
||
<button type="button" data-rolle="operator">Operator</button>
|
||
<button type="button" data-rolle="reader">Reader</button>
|
||
<button type="button" data-rolle="ohne">Ohne Gruppe</button>
|
||
</div>
|
||
<label for="claims">Claims (JSON, frei änderbar)</label>
|
||
<textarea id="claims" name="claims" rows="12"></textarea>
|
||
<button type="submit">Anmelden</button>
|
||
</form>
|
||
</main>
|
||
<script>
|
||
// Der Mock übernimmt bei interaktivem Login nur die Claims aus diesem Formular
|
||
// (tokenCallbacks greifen dann nicht). Deshalb wird hier ein vollständiger
|
||
// Standardsatz vorbelegt; Gruppen lassen sich frei ändern.
|
||
const gruppen = {
|
||
admin: ["sshkv-admins"],
|
||
operator: ["sshkv-operators"],
|
||
reader: ["sshkv-readers"],
|
||
ohne: [],
|
||
};
|
||
const benutzer = document.getElementById("username");
|
||
const claims = document.getElementById("claims");
|
||
function fuellen(rolle) {
|
||
const name = benutzer.value || rolle;
|
||
claims.value = JSON.stringify(
|
||
{
|
||
email: name + "@sshkv.test",
|
||
email_verified: true,
|
||
name: name.charAt(0).toUpperCase() + name.slice(1) + " (Mock)",
|
||
preferred_username: name,
|
||
groups: gruppen[rolle],
|
||
},
|
||
null,
|
||
2,
|
||
);
|
||
}
|
||
document.querySelectorAll("button[data-rolle]").forEach((knopf) => {
|
||
knopf.addEventListener("click", () => {
|
||
if (knopf.dataset.rolle !== "ohne") benutzer.value = knopf.dataset.rolle;
|
||
fuellen(knopf.dataset.rolle);
|
||
});
|
||
});
|
||
fuellen("admin");
|
||
</script>
|
||
</body>
|
||
</html>
|