feat(server): activate projected authentication safely

This commit is contained in:
User
2026-08-22 01:01:36 +02:00
parent 903c0b4de5
commit 3d9a9f0675
32 changed files with 1837 additions and 38 deletions
+5
View File
@@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/aritmolab/thothii/tools/tht/internal/authconfig"
"github.com/aritmolab/thothii/tools/tht/internal/compose"
"github.com/aritmolab/thothii/tools/tht/internal/config"
)
@@ -17,6 +18,7 @@ import (
const healthTimeout = 5 * time.Minute
var healthPollInterval = time.Second
var requireRuntimeAuthProjectionReady = authconfig.RequireRuntimeAuthProjectionReady
// HealthFailure identifies the last non-ready service after a bounded health wait.
type HealthFailure struct {
@@ -41,6 +43,9 @@ func Start(ctx context.Context, installation config.Installation, runner compose
if runner == nil {
return errors.New("start requires a Docker command runner")
}
if err := requireRuntimeAuthProjectionReady(installation); err != nil {
return errors.New("runtime authentication projection is unavailable")
}
if build {
if err := runCompose(ctx, installation, runner, "build"); err != nil {
return fmt.Errorf("image build: %w", err)
@@ -2,6 +2,7 @@ package service
import (
"context"
"errors"
"io"
"strings"
"testing"
@@ -36,6 +37,30 @@ func TestStartSkipsBuildUnlessRequested(t *testing.T) {
}
}
func TestStartRefusesProjectedAuthenticationBeforeComposeWhenNotReady(t *testing.T) {
for _, state := range []string{"missing", "blocked", "divergent"} {
t.Run(state, func(t *testing.T) {
previous := requireRuntimeAuthProjectionReady
requireRuntimeAuthProjectionReady = func(installation config.Installation) error {
if !installation.HasRuntimeAuthProjection() {
t.Fatal("readiness gate received an unprojected installation")
}
return errors.New("synthetic " + state + " projection")
}
t.Cleanup(func() { requireRuntimeAuthProjectionReady = previous })
runner := &recordingRunner{}
err := Start(context.Background(), projectedTestInstallation(), runner, true)
if err == nil {
t.Fatalf("Start() accepted %s runtime projection", state)
}
if len(runner.stages) != 0 {
t.Fatalf("Start() reached Compose for %s projection: %v", state, runner.stages)
}
})
}
}
type recordingRunner struct{ stages []string }
func (r *recordingRunner) Run(_ context.Context, args []string, _ io.Reader) (compose.Result, error) {
@@ -61,6 +86,15 @@ func testInstallation() config.Installation {
}
}
func projectedTestInstallation() config.Installation {
installation := testInstallation()
installation.Profile = "server"
installation.Authentication.RuntimeProjection = &config.RuntimeProjection{
Directory: "/runtime-auth", UID: 10001, GID: 10001,
}
return installation
}
const healthyServices = `[
{"Service":"core","State":"running","Health":"healthy"},
{"Service":"frontend","State":"running","Health":"healthy"},