Added ldap and pam authentication

This commit is contained in:
Martino Ferrari
2026-06-23 17:59:40 +02:00
parent ac24011487
commit 11120bedca
36 changed files with 1935 additions and 66 deletions
+25 -3
View File
@@ -3,6 +3,8 @@ import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
import SignalPicker, { SignalOption } from './SignalPicker';
import { VersionTree, DiffViewer } from './VersionHistory';
import CueEditor from './CueEditor';
import { useAuth } from './lib/auth';
import { ScopeFilter, ScopePicker, filterByScope, normalizeScope, type Bucket } from './lib/scope';
// ── Types (mirror internal/confmgr) ──────────────────────────────────────────
@@ -32,6 +34,8 @@ interface ConfigSet {
version: number;
tag?: string;
owner?: string;
scope?: string;
groups?: string[];
parameters: Parameter[];
}
@@ -43,10 +47,12 @@ interface ConfigInstance {
version: number;
tag?: string;
owner?: string;
scope?: string;
groups?: string[];
values: Record<string, any>;
}
interface Meta { id: string; name: string; version: number; setId?: string; enabled?: boolean; }
interface Meta { id: string; name: string; version: number; setId?: string; enabled?: boolean; owner?: string; scope?: string; groups?: string[]; }
// ConfigRule mirrors internal/confmgr.ConfigRule: CUE validation/transformation
// logic bound to a set, run when instances of that set are saved.
@@ -292,7 +298,10 @@ function useUndoKeys(undo: () => void, redo: () => void, canUndo: boolean, canRe
// ── Sets manager ──────────────────────────────────────────────────────────────
function SetsManager({ registerGuard }: { registerGuard: (fn: CloseGuard | null) => void }) {
const me = useAuth();
const [sets, setSets] = useState<Meta[]>([]);
const [bucket, setBucket] = useState<Bucket>('mine');
const [scopeGroup, setScopeGroup] = useState('');
const [selectedId, setSelectedId] = useState<string | null>(null);
const hist = useUndo<ConfigSet>();
const working = hist.present;
@@ -445,6 +454,8 @@ function SetsManager({ registerGuard }: { registerGuard: (fn: CloseGuard | null)
} catch (err) { setError(msg(err)); }
}
const visibleSets = filterByScope(sets, me, bucket, scopeGroup);
return (
<div class="cl-body">
<div class="cl-list">
@@ -457,8 +468,10 @@ function SetsManager({ registerGuard }: { registerGuard: (fn: CloseGuard | null)
<input ref={fileRef} type="file" accept="application/json,.json" style={{ display: 'none' }}
onChange={(e) => { const t = e.target as HTMLInputElement; const f = t.files?.[0]; if (f) importSet(f); t.value = ''; }} />
</div>
<ScopeFilter me={me} value={bucket} onChange={setBucket} group={scopeGroup} onGroupChange={setScopeGroup} />
{sets.length === 0 && <div class="hint cl-list-empty">No config sets yet.</div>}
{sets.map(s => (
{sets.length > 0 && visibleSets.length === 0 && <div class="hint cl-list-empty">No config sets in this view.</div>}
{visibleSets.map(s => (
<div key={s.id} class={`cl-list-item${selectedId === s.id ? ' cl-list-item-active' : ''}`} onClick={() => select(s.id)}>
<span class="cl-list-name" title={s.name}>{s.name || '(unnamed)'}</span>
<span class="cfg-badge" title="Current version">v{s.version}</span>
@@ -484,6 +497,8 @@ function SetsManager({ registerGuard }: { registerGuard: (fn: CloseGuard | null)
</div>
<textarea class="prop-input cfg-desc" rows={2} value={working.description ?? ''} placeholder="Description (optional)"
onInput={(e) => patch({ description: (e.target as HTMLTextAreaElement).value })} />
<ScopePicker me={me} scope={normalizeScope(working.scope)} groups={working.groups ?? []}
onChange={(scope, groups) => patch({ scope, groups })} />
<div class="cfg-section-head">
<span>Parameters</span>
@@ -802,9 +817,12 @@ function ParamEditor({ param, allOptions, onOpenSignals, signalMeta, onChange, o
// ── Instances manager ─────────────────────────────────────────────────────────
function InstancesManager({ registerGuard }: { registerGuard: (fn: CloseGuard | null) => void }) {
const me = useAuth();
const [instances, setInstances] = useState<Meta[]>([]);
const [sets, setSets] = useState<Meta[]>([]);
const [setFilter, setSetFilter] = useState<string>('');
const [bucket, setBucket] = useState<Bucket>('mine');
const [scopeGroup, setScopeGroup] = useState('');
const [selectedId, setSelectedId] = useState<string | null>(null);
const hist = useUndo<ConfigInstance>();
const working = hist.present;
@@ -984,7 +1002,8 @@ function InstancesManager({ registerGuard }: { registerGuard: (fn: CloseGuard |
}
const setName = (id: string) => sets.find(s => s.id === id)?.name ?? id;
const visibleInstances = instances.filter(s => !setFilter || s.setId === setFilter);
const scopedInstances = filterByScope(instances, me, bucket, scopeGroup);
const visibleInstances = scopedInstances.filter(s => !setFilter || s.setId === setFilter);
return (
<div class="cl-body">
@@ -994,6 +1013,7 @@ function InstancesManager({ registerGuard }: { registerGuard: (fn: CloseGuard |
<button class="panel-btn" disabled={busy || sets.length === 0} title="Snapshot a set's current live signal values into a new instance" onClick={() => setShowSnap(true)}> Snapshot</button>
<button class="panel-btn" disabled={busy || sets.length === 0} onClick={() => setShowNew(true)}>+ New</button>
</div>
<ScopeFilter me={me} value={bucket} onChange={setBucket} group={scopeGroup} onGroupChange={setScopeGroup} />
{sets.length > 1 && (
<div class="cl-list-filter">
<select class="prop-select" value={setFilter} title="Filter instances by config set"
@@ -1032,6 +1052,8 @@ function InstancesManager({ registerGuard }: { registerGuard: (fn: CloseGuard |
<button class="panel-btn" disabled={busy} title="Compare stored values against the current live signal values" onClick={liveDiff}>Diff vs current</button>
</div>
<div class="hint">Based on set <b>{setName(working.setId)}</b>{working.setVersion ? ` (v${working.setVersion})` : ''}.</div>
<ScopePicker me={me} scope={normalizeScope(working.scope)} groups={working.groups ?? []}
onChange={(scope, groups) => { hist.commit({ ...working, scope, groups }); setDirty(true); }} />
{!boundSet && <div class="cl-error">Bound set could not be loaded.</div>}
{boundSet && (