Add central login recovery and confirmed shared sign-out
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 44s
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 44s
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
This commit is contained in:
parent
89694ad6df
commit
074c2ce498
9 changed files with 334 additions and 49 deletions
17
src/internal/config/account_test.go
Normal file
17
src/internal/config/account_test.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package config
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
func TestAccountRecoveryDestinationsAreOwnerConfiguredHTTPS(t *testing.T) {
|
||||
for _, raw := range []string{"http://users.example", "https://user:password@users.example", "https://users.example/?next=evil", "https://users.example/#fragment", "//users.example"} {
|
||||
cfg:=&Config{AccountPortalURL:raw,BrowserLogoutURL:"https://auth.example/logout"}
|
||||
found:=false
|
||||
for _, err:=range ValidateConfig(cfg) {if strings.HasPrefix(err,"accountPortalURL:") {found=true}}
|
||||
if !found {t.Errorf("accepted unsafe account URL %q",raw)}
|
||||
}
|
||||
cfg:=&Config{AccountPortalURL:"https://users.example"}
|
||||
found:=false
|
||||
for _, err:=range ValidateConfig(cfg) {if strings.Contains(err,"configured together") {found=true}}
|
||||
if !found {t.Fatal("accepted incomplete logout route")}
|
||||
}
|
||||
|
|
@ -18,16 +18,18 @@ import (
|
|||
|
||||
// Config is the top-level server configuration.
|
||||
type Config struct {
|
||||
Issuer string `yaml:"issuer"`
|
||||
Port int `yaml:"port"`
|
||||
TokenLifetime string `yaml:"tokenLifetime"`
|
||||
PrivateKeyPEM string `yaml:"privateKeyPem"`
|
||||
LLDAP lldap.Config `yaml:"lldap"`
|
||||
Authelia authelia.Config `yaml:"authelia"`
|
||||
PrivacyIDEA privacyidea.Config `yaml:"privacyidea"`
|
||||
Clients []ClientConfig `yaml:"clients"`
|
||||
Environment string `yaml:"environment"`
|
||||
TenantEngine TenantEngineConfig `yaml:"tenantEngine,omitempty"`
|
||||
AccountPortalURL string `yaml:"accountPortalURL,omitempty"`
|
||||
BrowserLogoutURL string `yaml:"browserLogoutURL,omitempty"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
Port int `yaml:"port"`
|
||||
TokenLifetime string `yaml:"tokenLifetime"`
|
||||
PrivateKeyPEM string `yaml:"privateKeyPem"`
|
||||
LLDAP lldap.Config `yaml:"lldap"`
|
||||
Authelia authelia.Config `yaml:"authelia"`
|
||||
PrivacyIDEA privacyidea.Config `yaml:"privacyidea"`
|
||||
Clients []ClientConfig `yaml:"clients"`
|
||||
Environment string `yaml:"environment"`
|
||||
TenantEngine TenantEngineConfig `yaml:"tenantEngine,omitempty"`
|
||||
}
|
||||
|
||||
// TenantEngineConfig configures the optional tenant_roles cache claim.
|
||||
|
|
@ -85,6 +87,12 @@ func Load(path string) (*Config, error) {
|
|||
return nil, fmt.Errorf("config: parse %q: %w", path, err)
|
||||
}
|
||||
|
||||
if value := os.Getenv("KEYCAPE_ACCOUNT_PORTAL_URL"); value != "" {
|
||||
cfg.AccountPortalURL = value
|
||||
}
|
||||
if value := os.Getenv("KEYCAPE_BROWSER_LOGOUT_URL"); value != "" {
|
||||
cfg.BrowserLogoutURL = value
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,18 @@ import (
|
|||
// Called at startup — the server must exit 1 if any errors are returned.
|
||||
func ValidateConfig(cfg *Config) []string {
|
||||
var errs []string
|
||||
for name, raw := range map[string]string{"accountPortalURL": cfg.AccountPortalURL, "browserLogoutURL": cfg.BrowserLogoutURL} {
|
||||
if raw == "" {
|
||||
continue
|
||||
}
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" {
|
||||
errs = append(errs, name+": must be an HTTPS URL without credentials, query or fragment")
|
||||
}
|
||||
}
|
||||
if (cfg.AccountPortalURL == "") != (cfg.BrowserLogoutURL == "") {
|
||||
errs = append(errs, "accountPortalURL and browserLogoutURL must be configured together")
|
||||
}
|
||||
|
||||
// Issuer must be a valid URL with an http(s) scheme.
|
||||
if cfg.Issuer == "" {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue