feat(auth): add local and OIDC management to tht

This commit is contained in:
2026-08-16 19:23:30 +02:00
parent d17ad0e95b
commit 9646ae09a0
15 changed files with 992 additions and 13 deletions
+26 -1
View File
@@ -34,9 +34,14 @@ type descriptor struct {
ProjectDirectory string `yaml:"projectDirectory"`
EnvFile string `yaml:"envFile"`
WorkspaceRepository workspaceRepositoryDescriptor `yaml:"workspaceRepository"`
Authentication authenticationDescriptor `yaml:"authentication"`
Overrides []string `yaml:"overrides"`
}
type authenticationDescriptor struct {
ConfigDirectory string `yaml:"configDirectory"`
}
type workspaceRepositoryDescriptor struct {
Remote string `yaml:"remote"`
Branch string `yaml:"branch"`
@@ -50,6 +55,11 @@ type WorkspaceRepository struct {
Access string
}
// Authentication is the non-secret filesystem location for the installation auth configuration.
type Authentication struct {
ConfigDirectory string
}
// Installation is a validated local Compose installation. It intentionally contains paths, not
// environment values or secret content.
type Installation struct {
@@ -58,6 +68,7 @@ type Installation struct {
ProjectDirectory string
EnvFile string
WorkspaceRepository WorkspaceRepository
Authentication Authentication
Overrides []string
}
@@ -99,6 +110,9 @@ func Load(path string) (Installation, error) {
if err := requireRegularFile(raw.EnvFile, "envFile"); err != nil {
return Installation{}, err
}
if err := safeio.ValidateCanonicalPath(raw.Authentication.ConfigDirectory); err != nil {
return Installation{}, errors.New("authentication.configDirectory must be an absolute canonical path")
}
installation := Installation{
Path: path,
@@ -110,7 +124,15 @@ func Load(path string) (Installation, error) {
Branch: raw.WorkspaceRepository.Branch,
Access: raw.WorkspaceRepository.Access,
},
Overrides: make([]string, 0, len(raw.Overrides)),
Authentication: Authentication{ConfigDirectory: raw.Authentication.ConfigDirectory},
Overrides: make([]string, 0, len(raw.Overrides)),
}
values, err := installation.environmentValues()
if err != nil {
return Installation{}, errors.New("installation secret declarations could not be read")
}
if values["THT_AUTH_CONFIG_ROOT"] != installation.AuthenticationDirectory() {
return Installation{}, errors.New("authentication.configDirectory must match THT_AUTH_CONFIG_ROOT")
}
for _, override := range raw.Overrides {
if err := requireRegularFile(override, "override"); err != nil {
@@ -136,6 +158,9 @@ func Load(path string) (Installation, error) {
return installation, nil
}
// AuthenticationDirectory returns the descriptor-owned, non-secret authentication root.
func (i Installation) AuthenticationDirectory() string { return i.Authentication.ConfigDirectory }
var safeGitBranch = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._/-]*$`)
var scpSSHRemote = regexp.MustCompile(`^git@[^:/\s]+:[^\s]+$`)