feat(server): activate projected authentication safely
This commit is contained in:
@@ -32,6 +32,8 @@ import (
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/workspaceops"
|
||||
)
|
||||
|
||||
var requireRuntimeAuthProjectionReady = authconfig.RequireRuntimeAuthProjectionReady
|
||||
|
||||
const usage = `Usage: tht [--installation <absolute-path>/thothii-installation.yaml] <command>
|
||||
|
||||
When --installation is omitted, tht uses THOTHII_INSTALLATION or discovers one valid
|
||||
@@ -192,6 +194,9 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
if len(commandArgs) != 1 || commandArgs[0] != "--check-only" {
|
||||
return commandUsageError(stderr, "update currently requires --check-only")
|
||||
}
|
||||
if err := requireRuntimeAuthProjectionReady(installation); err != nil {
|
||||
return lifecycleFailure(stderr, errors.New("runtime authentication projection is unavailable"), secretValues)
|
||||
}
|
||||
result, err = runner.Run(ctx, installation.ComposeArgs("config", "--quiet"), nil)
|
||||
case "backup":
|
||||
return backupCommand(ctx, installation, commandArgs, stdout, stderr)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -1049,6 +1050,31 @@ func TestRunPreservesChildExitCodes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunUpdateCheckOnlyRefusesProjectedAuthenticationBeforeCompose(t *testing.T) {
|
||||
for _, state := range []string{"missing", "blocked", "divergent"} {
|
||||
t.Run(state, func(t *testing.T) {
|
||||
fixture := projectedUpdateFixture(t)
|
||||
previous := requireRuntimeAuthProjectionReady
|
||||
requireRuntimeAuthProjectionReady = func(installation config.Installation) error {
|
||||
if !installation.HasRuntimeAuthProjection() {
|
||||
t.Fatal("update readiness gate received an unprojected installation")
|
||||
}
|
||||
return errors.New("synthetic " + state + " projection")
|
||||
}
|
||||
t.Cleanup(func() { requireRuntimeAuthProjectionReady = previous })
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
if code := run(context.Background(), []string{"--installation", fixture.installationPath, "update", "--check-only"}, &stdout, &stderr); code == 0 {
|
||||
t.Fatalf("update --check-only accepted %s projection", state)
|
||||
}
|
||||
assertDockerNotInvoked(t, fixture)
|
||||
if strings.Contains(stdout.String()+stderr.String(), "synthetic "+state+" projection") {
|
||||
t.Fatalf("update leaked readiness detail: stdout=%q stderr=%q", stdout.String(), stderr.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPiStatusUsesImageBundledPi(t *testing.T) {
|
||||
fixture := newCLIFixture(t, "THT_LLM_URL=https://llm.example.invalid\n")
|
||||
fixture.setEnvironment(t)
|
||||
@@ -1575,6 +1601,25 @@ func (f cliFixture) setProfile(t *testing.T, profile string) {
|
||||
}
|
||||
}
|
||||
|
||||
func projectedUpdateFixture(t *testing.T) cliFixture {
|
||||
t.Helper()
|
||||
fixture := newCLIFixture(t, "SAFE_VALUE=1\n")
|
||||
fixture.setProfile(t, "server")
|
||||
runtimeRoot := filepath.Join(fixture.root, "runtime-auth")
|
||||
if err := os.Mkdir(runtimeRoot, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(fixture.projectDirectory, "deploy", "compose.auth-runtime-projection.yaml"), []byte("services: {}\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
descriptor := "profile: server\nprojectDirectory: " + fixture.projectDirectory + "\nenvFile: " + fixture.envFile + "\nauthentication:\n configDirectory: " + filepath.Join(fixture.root, "auth") + "\n runtimeProjection:\n directory: " + runtimeRoot + "\n uid: 10001\n gid: 10001\n"
|
||||
if err := os.WriteFile(fixture.installationPath, []byte(descriptor), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fixture.setEnvContents(t, "SAFE_VALUE=1\nTHT_AUTH_RUNTIME_ROOT="+strconv.Quote(runtimeRoot)+"\n")
|
||||
return fixture
|
||||
}
|
||||
|
||||
func (f cliFixture) invocations(t *testing.T) [][]string {
|
||||
t.Helper()
|
||||
contents, err := os.ReadFile(f.argsFile)
|
||||
|
||||
Reference in New Issue
Block a user