feat(server): activate projected authentication safely
This commit is contained in:
@@ -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"},
|
||||
|
||||
Reference in New Issue
Block a user