389002fc6f
Adds a breadcrumb trail to nested views for orientation. Mechanism: a
{% block breadcrumb %} slot in base.html (+ styling) and a shared crumbs()
macro in templates/_macros.html; each nested page fills the block with its
trail (root→current, last item is the current page). Pages without the block
render no bar, so top-level nav roots stay clean.
Applied to: Ansible (browse, schedules, playbook editor, run detail) +
inventory (targets/groups + detail), host_agent (fleet, host detail,
settings), hosts form, settings tabs (ansible/auth/notifications/plugins/
reports + plugin detail), dashboard (list, edit), and alerts (rule form,
maintenance + new). Dynamic labels (host/target/run/dashboard names) come
from the page context — no route changes. All 60 templates Jinja-compile.
Task #873.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
181 lines
8.2 KiB
HTML
181 lines
8.2 KiB
HTML
{# steward/templates/settings/auth.html #}
|
|
{% extends "base.html" %}
|
|
{% from "_macros.html" import crumbs %}
|
|
{% block title %}Settings — Auth — Steward{% endblock %}
|
|
{% block breadcrumb %}{{ crumbs([("Settings", "/settings/"), ("Authentication", "")]) }}{% endblock %}
|
|
{% block content %}
|
|
{% set active_tab = "auth" %}
|
|
{% include "settings/_tabs.html" %}
|
|
|
|
<form method="post" action="/settings/auth/">
|
|
<div style="display:grid;gap:1.25rem;max-width:640px;">
|
|
|
|
{# ── OIDC ─────────────────────────────────────────────────────────────────── #}
|
|
<div class="card">
|
|
<h2 class="section-title" style="margin-bottom:1.25rem;">OIDC / SSO</h2>
|
|
<p style="font-size:0.82rem;color:var(--text-muted);margin-bottom:1rem;">
|
|
Supports any OIDC provider — Authentik, Keycloak, Authelia, etc. Uses Authorization Code flow.
|
|
Local admin login always works regardless of OIDC status.
|
|
</p>
|
|
|
|
<div class="form-group" style="display:flex;align-items:center;gap:0.75rem;">
|
|
<input type="checkbox" name="oidc.enabled" id="oidc-enabled"
|
|
{% if settings['oidc.enabled'] %}checked{% endif %}>
|
|
<label for="oidc-enabled" style="margin:0;">Enable OIDC login</label>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Discovery URL</label>
|
|
<input type="text" name="oidc.discovery_url" value="{{ settings['oidc.discovery_url'] }}"
|
|
placeholder="https://auth.example.com/application/o/steward/.well-known/openid-configuration">
|
|
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.2rem;">
|
|
Authentik: <code>https://<host>/application/o/<slug>/.well-known/openid-configuration</code>
|
|
</p>
|
|
</div>
|
|
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;">
|
|
<div class="form-group">
|
|
<label>Client ID</label>
|
|
<input type="text" name="oidc.client_id" value="{{ settings['oidc.client_id'] }}"
|
|
autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Client Secret <span style="color:var(--text-dim);font-size:0.8rem;">(blank = keep current)</span></label>
|
|
<input type="password" name="oidc.client_secret" placeholder="••••••••"
|
|
autocomplete="new-password">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Scopes</label>
|
|
<input type="text" name="oidc.scopes" value="{{ settings['oidc.scopes'] }}"
|
|
placeholder="openid profile email">
|
|
</div>
|
|
|
|
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;">
|
|
<div class="form-group">
|
|
<label>Username claim</label>
|
|
<input type="text" name="oidc.username_claim" value="{{ settings['oidc.username_claim'] }}"
|
|
placeholder="preferred_username">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Email claim</label>
|
|
<input type="text" name="oidc.email_claim" value="{{ settings['oidc.email_claim'] }}"
|
|
placeholder="email">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Groups claim</label>
|
|
<input type="text" name="oidc.groups_claim" value="{{ settings['oidc.groups_claim'] }}"
|
|
placeholder="groups">
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;">
|
|
<div class="form-group">
|
|
<label>Admin group name</label>
|
|
<input type="text" name="oidc.admin_group" value="{{ settings['oidc.admin_group'] }}"
|
|
placeholder="steward-admins">
|
|
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.2rem;">Members → admin role</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Operator group name</label>
|
|
<input type="text" name="oidc.operator_group" value="{{ settings['oidc.operator_group'] }}"
|
|
placeholder="steward-operators">
|
|
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.2rem;">Members → operator role; others → viewer</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# ── LDAP ─────────────────────────────────────────────────────────────────── #}
|
|
<div class="card">
|
|
<h2 class="section-title" style="margin-bottom:1.25rem;">LDAP</h2>
|
|
<p style="font-size:0.82rem;color:var(--text-muted);margin-bottom:1rem;">
|
|
Binds user credentials against an LDAP/AD directory. Requires the
|
|
<code>ldap3</code> Python package (<code>pip install ldap3</code>).
|
|
If both LDAP and OIDC are enabled, the login form tries LDAP; OIDC uses the SSO button.
|
|
</p>
|
|
|
|
<div class="form-group" style="display:flex;align-items:center;gap:0.75rem;">
|
|
<input type="checkbox" name="ldap.enabled" id="ldap-enabled"
|
|
{% if settings['ldap.enabled'] %}checked{% endif %}>
|
|
<label for="ldap-enabled" style="margin:0;">Enable LDAP login</label>
|
|
</div>
|
|
|
|
<div style="display:grid;grid-template-columns:1fr auto auto;gap:1rem;align-items:end;">
|
|
<div class="form-group">
|
|
<label>LDAP host</label>
|
|
<input type="text" name="ldap.host" value="{{ settings['ldap.host'] }}"
|
|
placeholder="ldap.example.com">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Port</label>
|
|
<input type="number" name="ldap.port" value="{{ settings['ldap.port'] }}" style="width:80px;">
|
|
</div>
|
|
<div class="form-group" style="display:flex;align-items:center;gap:0.5rem;padding-bottom:0.1rem;">
|
|
<input type="checkbox" name="ldap.tls" id="ldap-tls"
|
|
{% if settings['ldap.tls'] %}checked{% endif %}>
|
|
<label for="ldap-tls" style="margin:0;white-space:nowrap;">StartTLS</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Service account bind DN</label>
|
|
<input type="text" name="ldap.bind_dn" value="{{ settings['ldap.bind_dn'] }}"
|
|
placeholder="cn=svc-steward,ou=service,dc=example,dc=com">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Bind password <span style="color:var(--text-dim);font-size:0.8rem;">(blank = keep current)</span></label>
|
|
<input type="password" name="ldap.bind_password" placeholder="••••••••"
|
|
autocomplete="new-password">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Base DN</label>
|
|
<input type="text" name="ldap.base_dn" value="{{ settings['ldap.base_dn'] }}"
|
|
placeholder="ou=users,dc=example,dc=com">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>User search filter</label>
|
|
<input type="text" name="ldap.user_filter" value="{{ settings['ldap.user_filter'] }}"
|
|
placeholder="(uid={username})">
|
|
<p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.2rem;">
|
|
<code>{username}</code> is replaced with the login username. AD example: <code>(sAMAccountName={username})</code>
|
|
</p>
|
|
</div>
|
|
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;">
|
|
<div class="form-group">
|
|
<label>Admin group DN</label>
|
|
<input type="text" name="ldap.admin_group_dn" value="{{ settings['ldap.admin_group_dn'] }}"
|
|
placeholder="cn=steward-admins,ou=groups,dc=example,dc=com">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Operator group DN</label>
|
|
<input type="text" name="ldap.operator_group_dn" value="{{ settings['ldap.operator_group_dn'] }}"
|
|
placeholder="cn=steward-operators,ou=groups,dc=example,dc=com">
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;">
|
|
<div class="form-group">
|
|
<label>Username attribute</label>
|
|
<input type="text" name="ldap.attr_username" value="{{ settings['ldap.attr_username'] }}"
|
|
placeholder="uid">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Email attribute</label>
|
|
<input type="text" name="ldap.attr_email" value="{{ settings['ldap.attr_email'] }}"
|
|
placeholder="mail">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div style="margin-top:1.25rem;">
|
|
<button type="submit" class="btn">Save</button>
|
|
<span style="color:var(--text-muted);font-size:0.82rem;margin-left:1rem;">
|
|
Changes take effect immediately.
|
|
</span>
|
|
</div>
|
|
</form>
|
|
{% endblock %}
|