feat(auth): add local and OIDC management to tht
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/authconfig"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/backup"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/compose"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/config"
|
||||
@@ -22,6 +23,7 @@ import (
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/output"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/pi"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/project"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/safeio"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/serverops"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/service"
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/setup"
|
||||
@@ -38,6 +40,10 @@ Commands:
|
||||
setup [--configure-only] [--installation-id ID] [--profile local|server]
|
||||
Create or validate the local non-secret installation configuration.
|
||||
version [--json] Show the host CLI build identity.
|
||||
auth configure --mode local|oidc ...
|
||||
Configure local users or OIDC group mapping; see tht auth for exact options.
|
||||
auth status [--json] Show the redacted authentication configuration status.
|
||||
auth user ... Manage local users; unavailable for OIDC installations.
|
||||
status Show the Compose service state.
|
||||
doctor [--json] Run non-mutating host, Compose, workflow, and Pi diagnostics.
|
||||
logs Show the latest 200 sanitized service log lines (bounded; no follow mode).
|
||||
@@ -130,6 +136,12 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
fmt.Fprintf(stderr, "tht: %s\n", output.Sanitize(err.Error(), nil))
|
||||
return 2
|
||||
}
|
||||
if command != "auth" || len(commandArgs) == 0 || commandArgs[0] != "configure" {
|
||||
if err := safeio.ValidatePrivateDirectory(installation.AuthenticationDirectory()); err != nil {
|
||||
fmt.Fprintln(stderr, "tht: authentication configuration directory is unavailable or unsafe")
|
||||
return 2
|
||||
}
|
||||
}
|
||||
secretFiles, err := installation.SecretFiles()
|
||||
if err != nil {
|
||||
fmt.Fprintln(stderr, "tht: installation secret declarations could not be read")
|
||||
@@ -144,6 +156,8 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
runner := compose.NewRunner("")
|
||||
var result compose.Result
|
||||
switch command {
|
||||
case "auth":
|
||||
return authconfig.Run(ctx, installation, commandArgs, os.Stdin, stdout, stderr)
|
||||
case "status":
|
||||
if len(commandArgs) != 0 {
|
||||
return commandUsageError(stderr, "status does not accept arguments")
|
||||
|
||||
@@ -201,7 +201,7 @@ func TestRootCommandIdentity(t *testing.T) {
|
||||
name: "help banner",
|
||||
args: []string{"--help"},
|
||||
wantCode: 0,
|
||||
wantText: "Usage: tht ",
|
||||
wantText: " auth configure",
|
||||
},
|
||||
{
|
||||
name: "version path",
|
||||
@@ -1446,8 +1446,12 @@ func newCLIFixture(t *testing.T, envTemplate string) cliFixture {
|
||||
}
|
||||
}
|
||||
envFile := filepath.Join(root, "installation.env")
|
||||
authDirectory := filepath.Join(root, "auth")
|
||||
if err := os.Mkdir(authDirectory, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
installationPath := filepath.Join(root, "thothii-installation.yaml")
|
||||
contents := "profile: local\nprojectDirectory: " + projectDirectory + "\nenvFile: " + envFile + "\n"
|
||||
contents := "profile: local\nprojectDirectory: " + projectDirectory + "\nenvFile: " + envFile + "\nauthentication:\n configDirectory: " + authDirectory + "\n"
|
||||
if err := os.WriteFile(installationPath, []byte(contents), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1522,6 +1526,7 @@ func (f cliFixture) setEnvironment(t *testing.T, values ...string) {
|
||||
|
||||
func (f cliFixture) setEnvContents(t *testing.T, env string) {
|
||||
t.Helper()
|
||||
env += "THT_AUTH_CONFIG_ROOT=" + strconv.Quote(filepath.Join(f.root, "auth")) + "\n"
|
||||
if err := os.WriteFile(f.envFile, []byte(env), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1547,7 +1552,7 @@ func (f cliFixture) setProfile(t *testing.T, profile string) {
|
||||
if err := os.WriteFile(composePath, []byte("services: {}\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
contents := "profile: " + profile + "\nprojectDirectory: " + f.projectDirectory + "\nenvFile: " + f.envFile + "\n"
|
||||
contents := "profile: " + profile + "\nprojectDirectory: " + f.projectDirectory + "\nenvFile: " + f.envFile + "\nauthentication:\n configDirectory: " + filepath.Join(f.root, "auth") + "\n"
|
||||
if err := os.WriteFile(f.installationPath, []byte(contents), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user