feat(settings): hub auth-policy panel — T4/T5 security parity in the GUI
Some checks failed
Security / Security check (push) Failing after 1s
Some checks failed
Security / Security check (push) Failing after 1s
Settings → Security now shows the hub's effective auth policy via the new read-only AuthStatus RPC: active token validator (static / jwt-rs256 with issuer, audience, JWKS source), anonymous-access warning, per-token cards with scope grants, env-var presence and rate limits, plus a localized admin-denied story for non-admin tokens. Live-reloads on endpoint change. Also fixes a batch of fai→chain rename leftovers this panel's verification uncovered: hub_auth_token.dart and registry_token.dart read/wrote ~/.fai/ while the hub reads ~/.chain/ (stored registry tokens never reached the hub), today_story_loader + tools/today used ~/.fai/today, chain_log legacy ~/.fai/logs migration removed per the no-legacy-recognisers decision, and UI strings still advertised the retired .fai bundle extension. Includes 5 widget tests for the panel, an integration-test screenshot harness (auth_policy_shots_test.dart, guide-shots style), and DE+EN l10n. flutter analyze clean, 58 tests green. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
c6da5025ce
commit
efaa089454
18 changed files with 1208 additions and 74 deletions
|
|
@ -1,6 +1,6 @@
|
|||
// Hub fixture for integration-level tests.
|
||||
//
|
||||
// Spawns a real `fai serve` subprocess on a free port in a
|
||||
// Spawns a real `chain serve` subprocess on a free port in a
|
||||
// temporary directory, waits for its gRPC endpoint to answer
|
||||
// `Healthy`, and exposes the port + a teardown so test files
|
||||
// can talk to a clean Hub without setting up an isolate-side
|
||||
|
|
@ -8,13 +8,13 @@
|
|||
//
|
||||
// Cost / scope notes:
|
||||
//
|
||||
// * The `fai` binary must be on the operator's PATH or at
|
||||
// `target/release/fai` relative to the platform repo. We
|
||||
// * The `chain` binary must be on the operator's PATH or at
|
||||
// `target/release/chain` relative to the platform repo. We
|
||||
// resolve those two paths in order; if neither exists, the
|
||||
// fixture skips the suite with a clear message rather than
|
||||
// failing — these tests are opt-in.
|
||||
// * The hub is started with `--quiet` (no log spam) against a
|
||||
// temp dir, so the operator's real `~/.fai/` stays untouched.
|
||||
// temp dir, so the operator's real `~/.chain/` stays untouched.
|
||||
// * Cleanup runs `process.kill(SIGTERM)` then `process.kill()`
|
||||
// after a grace period. The temp dir is removed.
|
||||
// * Each fixture is independent — call `HubFixture.start()` in
|
||||
|
|
@ -59,6 +59,13 @@ class HubFixture {
|
|||
/// `markSkipped` instead of failing when the binary isn't
|
||||
/// present — that's the desired behaviour in CI environments
|
||||
/// that don't build the binary first.
|
||||
///
|
||||
/// [configYaml], when given, is written to `<tempDir>/config.yaml`
|
||||
/// and handed to the hub via `$CHAIN_CONFIG` — tests can exercise
|
||||
/// operator policy (e.g. `auth.tokens`) without touching the real
|
||||
/// `~/.chain/config.yaml`. Token *values* go through
|
||||
/// [extraEnvironment]; pass the matching [authToken] so the
|
||||
/// readiness poll (and [client]) authenticate against the hub.
|
||||
static Future<HubFixture?> start({
|
||||
// A cold hub spends its first 30s building the curated-
|
||||
// model database + initialising SQLite. 60s gives that
|
||||
|
|
@ -66,6 +73,9 @@ class HubFixture {
|
|||
// a slow one.
|
||||
Duration readyTimeout = const Duration(seconds: 60),
|
||||
bool skipIfBinaryMissing = true,
|
||||
String? configYaml,
|
||||
Map<String, String> extraEnvironment = const {},
|
||||
String? authToken,
|
||||
}) async {
|
||||
final binary = await _resolveBinary();
|
||||
if (binary == null) {
|
||||
|
|
@ -84,10 +94,16 @@ class HubFixture {
|
|||
final port = await _pickFreePort();
|
||||
final addr = '127.0.0.1:$port';
|
||||
|
||||
String? configPath;
|
||||
if (configYaml != null) {
|
||||
configPath = '${tempDir.path}/config.yaml';
|
||||
File(configPath).writeAsStringSync(configYaml);
|
||||
}
|
||||
|
||||
// Run as a child process. `serve --bind` lets us avoid the
|
||||
// default 50051 (and any local daemon the developer happens
|
||||
// to be running). CHAIN_DATA_DIR keeps the temp footprint
|
||||
// contained — no state leaks into ~/.fai.
|
||||
// contained — no state leaks into ~/.chain.
|
||||
final process = await Process.start(
|
||||
binary,
|
||||
['serve', '--bind', addr],
|
||||
|
|
@ -95,6 +111,8 @@ class HubFixture {
|
|||
...Platform.environment,
|
||||
'CHAIN_DATA_DIR': tempDir.path,
|
||||
'CHAIN_MODULES_DIR': '${tempDir.path}/modules',
|
||||
'CHAIN_CONFIG': ?configPath,
|
||||
...extraEnvironment,
|
||||
// Suppress info logs without a CLI flag — the binary
|
||||
// respects RUST_LOG for tracing-subscriber.
|
||||
'RUST_LOG': 'warn',
|
||||
|
|
@ -111,6 +129,7 @@ class HubFixture {
|
|||
// a hung hub is a test failure, not a hang.
|
||||
final client = HubClient(
|
||||
endpoint: HubEndpoint(host: '127.0.0.1', port: port),
|
||||
authToken: authToken,
|
||||
);
|
||||
final deadline = DateTime.now().add(readyTimeout);
|
||||
while (DateTime.now().isBefore(deadline)) {
|
||||
|
|
@ -136,7 +155,7 @@ class HubFixture {
|
|||
await tempDir.delete(recursive: true);
|
||||
throw StateError(
|
||||
'Hub did not become healthy within $readyTimeout. '
|
||||
'Check `fai serve` works manually with CHAIN_DATA_DIR=$tempDir.',
|
||||
'Check `chain serve` works manually with CHAIN_DATA_DIR=$tempDir.',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue