Read OpenID credentials from file (#3244)

* feat: read OpenID credentials from file

Signed-off-by: Uwe Jäger <uwe.jaeger@valiton.com>

* feat: allow credentials file and secret in config to keep BC

Signed-off-by: Uwe Jäger <uwe.jaeger@valiton.com>

---------

Signed-off-by: Uwe Jäger <uwe.jaeger@valiton.com>
This commit is contained in:
Uwe Jäger
2025-07-09 18:16:49 +02:00
committed by GitHub
parent 432fde45af
commit 06c1be119c
11 changed files with 144 additions and 32 deletions
+32
View File
@@ -863,6 +863,12 @@ func LoadConfiguration(config *config.Config, configPath string) error {
return err
}
if err := updateOpenIDConfig(config); err != nil {
log.Error().Err(err).Msg("failed to read openid provider config file(s)")
return err
}
if err := loadSessionKeys(config); err != nil {
log.Error().Err(err).Msg("failed to read sessionKeysFile")
@@ -926,6 +932,32 @@ func updateLDAPConfig(conf *config.Config) error {
return nil
}
func updateOpenIDConfig(conf *config.Config) error {
if conf.HTTP.Auth == nil || conf.HTTP.Auth.OpenID == nil {
return nil
}
for name, provider := range conf.HTTP.Auth.OpenID.Providers {
if provider.CredentialsFile != "" {
var newOpenIDCredentials config.OpenIDCredentials
if err := readSecretFile(provider.CredentialsFile, &newOpenIDCredentials, true); err != nil {
return err
}
provider.ClientID = newOpenIDCredentials.ClientID
provider.ClientSecret = newOpenIDCredentials.ClientSecret
conf.HTTP.Auth.OpenID.Providers[name] = provider
} else {
log.Warn().Str("provider", name).
Msg("deprecated: use the new OpenID provider credentialsfile instead of clientid and clientsecret.")
}
}
return nil
}
func readSecretFile(path string, v any, checkUnsetFields bool) error { //nolint: varnamelen
viperInstance := viper.NewWithOptions(viper.KeyDelimiter("::"))
+65 -11
View File
@@ -1232,7 +1232,7 @@ storage:
So(err, ShouldNotBeNil)
})
Convey("Test verify oauth2 config with missing parameter", t, func(c C) {
Convey("Test verify oauth2 config with missing parameter scopes", t, func(c C) {
tmpfile, err := os.CreateTemp("", "zot-test*.json")
So(err, ShouldBeNil)
@@ -1252,6 +1252,26 @@ storage:
So(err, ShouldNotBeNil)
})
Convey("Test verify oauth2 config with missing parameter clientid", t, func(c C) {
tmpfile, err := os.CreateTemp("", "zot-test*.json")
So(err, ShouldBeNil)
defer os.Remove(tmpfile.Name()) // clean up
content := []byte(`{"distSpecVersion":"1.1.1","storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"auth":{"openid":{"providers":{"github":{"scopes":["openid"]}}}}},
"log":{"level":"debug"}}`)
_, err = tmpfile.Write(content)
So(err, ShouldBeNil)
err = tmpfile.Close()
So(err, ShouldBeNil)
os.Args = []string{"cli_test", "verify", tmpfile.Name()}
err = cli.NewServerRootCmd().Execute()
So(err, ShouldNotBeNil)
})
Convey("Test verify openid config with unsupported provider", t, func(c C) {
tmpfile, err := os.CreateTemp("", "zot-test*.json")
So(err, ShouldBeNil)
@@ -1278,11 +1298,27 @@ storage:
defer os.Remove(tmpfile.Name()) // clean up
content := []byte(`{"distSpecVersion":"1.1.1","storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"auth":{"openid":{"providers":{"oidc":{"issuer":"http://127.0.0.1:5556/dex",
"clientid":"client_id","scopes":["openid"]}}}}},
"log":{"level":"debug"}}`)
tmpCredsFile, err := os.CreateTemp("", "zot-cred*.json")
So(err, ShouldBeNil)
defer os.Remove(tmpCredsFile.Name())
content := []byte(`{
"clientid":"client-id",
"clientsecret":"client-secret"
}`)
_, err = tmpCredsFile.Write(content)
So(err, ShouldBeNil)
err = tmpCredsFile.Close()
So(err, ShouldBeNil)
content = []byte(fmt.Sprintf(`{"distSpecVersion":"1.1.1","storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"auth":{"openid":{"providers":{"oidc":{"issuer":"http://127.0.0.1:5556/dex",
"credentialsFile":"%s","scopes":["openid"]}}}}},
"log":{"level":"debug"}}`,
tmpCredsFile.Name()),
)
_, err = tmpfile.Write(content)
So(err, ShouldBeNil)
err = tmpfile.Close()
@@ -1641,13 +1677,31 @@ func TestApiKeyConfig(t *testing.T) {
tmpfile, err := os.CreateTemp("", "zot-test*.json")
So(err, ShouldBeNil)
defer os.Remove(tmpfile.Name()) // clean up
tmpCredsFile, err := os.CreateTemp("", "zot-cred*.json")
So(err, ShouldBeNil)
defer os.Remove(tmpCredsFile.Name())
content := []byte(`{
"clientid":"client-id",
"clientsecret":"client-secret"
}`)
_, err = tmpCredsFile.Write(content)
So(err, ShouldBeNil)
err = tmpCredsFile.Close()
So(err, ShouldBeNil)
defer os.Remove(tmpfile.Name())
content := []byte(`{"distSpecVersion":"1.1.1","storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"auth":{"openid":{"providers":{"oidc":{"issuer":"http://127.0.0.1:5556/dex",
"clientid":"client_id","scopes":["openid"]}}}}},
"log":{"level":"debug"}}`)
content = []byte(fmt.Sprintf(`{"distSpecVersion":"1.1.1","storage":{"rootDirectory":"/tmp/zot"},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"auth":{"openid":{"providers":{"oidc":{"issuer":"http://127.0.0.1:5556/dex",
"credentialsFile":"%s","scopes":["openid"]}}}}},
"log":{"level":"debug"}}`,
tmpCredsFile.Name()),
)
err = os.WriteFile(tmpfile.Name(), content, 0o0600)
So(err, ShouldBeNil)