Add generic function to retrieve config value with metadata (#15083)

`config.ResolveConfigParam` returns the value of a configuration for any
subsystem based on checking env, config store, and default value. Also returns info
about which config source returned the value.

This is useful to return info about config params overridden via env in the user
APIs. Currently implemented only for OpenID subsystem, but will be extended for
others subsequently.
This commit is contained in:
Aditya Manthramurthy
2022-06-17 11:39:21 -07:00
committed by GitHub
parent 98ddc3596c
commit 7f629df4d5
5 changed files with 237 additions and 80 deletions

View File

@@ -243,7 +243,7 @@ func TestKeycloakProviderInitialization(t *testing.T) {
testKvs.Set(Vendor, "keycloak")
testKvs.Set(KeyCloakRealm, "TestRealm")
testKvs.Set(KeyCloakAdminURL, "http://keycloak.test/auth/admin")
cfgGet := func(env, param string) string {
cfgGet := func(param string) string {
return testKvs.Get(param)
}

View File

@@ -36,49 +36,32 @@ import (
"github.com/minio/minio/internal/config"
"github.com/minio/minio/internal/config/identity/openid/provider"
"github.com/minio/minio/internal/hash/sha256"
"github.com/minio/pkg/env"
iampolicy "github.com/minio/pkg/iam/policy"
xnet "github.com/minio/pkg/net"
)
// OpenID keys and envs.
const (
JwksURL = "jwks_url"
ClientID = "client_id"
ClientSecret = "client_secret"
ConfigURL = "config_url"
ClaimName = "claim_name"
ClaimUserinfo = "claim_userinfo"
ClaimPrefix = "claim_prefix"
ClientID = "client_id"
ClientSecret = "client_secret"
RolePolicy = "role_policy"
DisplayName = "display_name"
Vendor = "vendor"
Scopes = "scopes"
RedirectURI = "redirect_uri"
RedirectURIDynamic = "redirect_uri_dynamic"
Vendor = "vendor"
// Vendor specific ENV only enabled if the Vendor matches == "vendor"
KeyCloakRealm = "keycloak_realm"
KeyCloakAdminURL = "keycloak_admin_url"
EnvIdentityOpenIDEnable = "MINIO_IDENTITY_OPENID_ENABLE"
EnvIdentityOpenIDVendor = "MINIO_IDENTITY_OPENID_VENDOR"
EnvIdentityOpenIDClientID = "MINIO_IDENTITY_OPENID_CLIENT_ID"
EnvIdentityOpenIDClientSecret = "MINIO_IDENTITY_OPENID_CLIENT_SECRET"
EnvIdentityOpenIDURL = "MINIO_IDENTITY_OPENID_CONFIG_URL"
EnvIdentityOpenIDClaimName = "MINIO_IDENTITY_OPENID_CLAIM_NAME"
EnvIdentityOpenIDClaimUserInfo = "MINIO_IDENTITY_OPENID_CLAIM_USERINFO"
EnvIdentityOpenIDClaimPrefix = "MINIO_IDENTITY_OPENID_CLAIM_PREFIX"
EnvIdentityOpenIDRolePolicy = "MINIO_IDENTITY_OPENID_ROLE_POLICY"
EnvIdentityOpenIDRedirectURI = "MINIO_IDENTITY_OPENID_REDIRECT_URI"
EnvIdentityOpenIDRedirectURIDynamic = "MINIO_IDENTITY_OPENID_REDIRECT_URI_DYNAMIC"
EnvIdentityOpenIDScopes = "MINIO_IDENTITY_OPENID_SCOPES"
EnvIdentityOpenIDDisplayName = "MINIO_IDENTITY_OPENID_DISPLAY_NAME"
// Vendor specific ENVs only enabled if the Vendor matches == "vendor"
EnvIdentityOpenIDKeyCloakRealm = "MINIO_IDENTITY_OPENID_KEYCLOAK_REALM"
EnvIdentityOpenIDKeyCloakAdminURL = "MINIO_IDENTITY_OPENID_KEYCLOAK_ADMIN_URL"
// Removed params
JwksURL = "jwks_url"
ClaimPrefix = "claim_prefix"
)
// DefaultKVS - default config for OpenID config
@@ -191,7 +174,7 @@ func (r *Config) Clone() Config {
}
// LookupConfig lookup jwks from config, override with any ENVs.
func LookupConfig(kvsMap map[string]config.KVS, transport http.RoundTripper, closeRespFn func(io.ReadCloser), serverRegion string) (c Config, err error) {
func LookupConfig(s config.Config, transport http.RoundTripper, closeRespFn func(io.ReadCloser), serverRegion string) (c Config, err error) {
openIDClientTransport := http.DefaultTransport
if transport != nil {
openIDClientTransport = transport
@@ -209,48 +192,28 @@ func LookupConfig(kvsMap map[string]config.KVS, transport http.RoundTripper, clo
closeRespFn: closeRespFn,
}
// Make a copy of the config we received so we can mutate it safely.
kvsMap2 := make(map[string]config.KVS, len(kvsMap))
for k, v := range kvsMap {
kvsMap2[k] = v
}
// Add in each configuration name found from environment variables, i.e.
// if we see MINIO_IDENTITY_OPENID_CONFIG_URL_2, we add the key "2" to
// `kvsMap2` if it does not already exist.
envs := env.List(EnvIdentityOpenIDURL + config.Default)
for _, k := range envs {
cfgName := strings.TrimPrefix(k, EnvIdentityOpenIDURL+config.Default)
if cfgName == "" {
return c, config.Errorf("Environment variable must have a non-empty config name: %s", k)
}
// It is possible that some variables were specified via config
// commands and some variables are intended to be overridden
// from the environment, so we ensure that the key is not
// overwritten in `kvsMap2` as it may have existing config.
if _, ok := kvsMap2[cfgName]; !ok {
kvsMap2[cfgName] = DefaultKVS
}
}
var (
hasLegacyPolicyMapping = false
seenClientIDs = set.NewStringSet()
)
for cfgName, kvs := range kvsMap2 {
// remove this since we have removed support for this already.
kvs.Delete(JwksURL)
if err = config.CheckValidKeys(config.IdentityOpenIDSubSys, kvs, DefaultKVS); err != nil {
return c, err
}
// remove this since we have removed support for this already.
deprecatedKeys := []string{JwksURL}
if err := s.CheckValidKeys(config.IdentityOpenIDSubSys, deprecatedKeys); err != nil {
return c, err
}
getCfgVal := func(envVar, cfgParam string) string {
if cfgName != config.Default {
envVar += config.Default + cfgName
}
return env.Get(envVar, kvs.Get(cfgParam))
openIDTargets, err := s.GetAvailableTargets(config.IdentityOpenIDSubSys)
if err != nil {
return c, err
}
for _, cfgName := range openIDTargets {
getCfgVal := func(cfgParam string) string {
// As parameters are already validated, we skip checking
// if the config param was found.
val, _ := s.ResolveConfigParam(config.IdentityOpenIDSubSys, cfgName, cfgParam)
return val
}
// In the past, when only one openID provider was allowed, there
@@ -261,7 +224,7 @@ func LookupConfig(kvsMap map[string]config.KVS, transport http.RoundTripper, clo
// setting, otherwise we treat it as enabled if some important
// parameters are non-empty.
var (
cfgEnableVal = getCfgVal(EnvIdentityOpenIDEnable, config.Enable)
cfgEnableVal = getCfgVal(config.Enable)
isExplicitlyEnabled = false
)
if cfgEnableVal != "" {
@@ -281,7 +244,7 @@ func LookupConfig(kvsMap map[string]config.KVS, transport http.RoundTripper, clo
}
p := newProviderCfgFromConfig(getCfgVal)
configURL := getCfgVal(EnvIdentityOpenIDURL, ConfigURL)
configURL := getCfgVal(ConfigURL)
if !isExplicitlyEnabled {
enabled = true
@@ -315,7 +278,7 @@ func LookupConfig(kvsMap map[string]config.KVS, transport http.RoundTripper, clo
return c, errors.New("please specify config_url to enable fetching claims from UserInfo endpoint")
}
if scopeList := getCfgVal(EnvIdentityOpenIDScopes, Scopes); scopeList != "" {
if scopeList := getCfgVal(Scopes); scopeList != "" {
var scopes []string
for _, scope := range strings.Split(scopeList, ",") {
scope = strings.TrimSpace(scope)

View File

@@ -52,17 +52,17 @@ type providerCfg struct {
provider provider.Provider
}
func newProviderCfgFromConfig(getCfgVal func(env, cfgName string) string) providerCfg {
func newProviderCfgFromConfig(getCfgVal func(cfgName string) string) providerCfg {
return providerCfg{
DisplayName: getCfgVal(EnvIdentityOpenIDDisplayName, DisplayName),
ClaimName: getCfgVal(EnvIdentityOpenIDClaimName, ClaimName),
ClaimUserinfo: getCfgVal(EnvIdentityOpenIDClaimUserInfo, ClaimUserinfo) == config.EnableOn,
ClaimPrefix: getCfgVal(EnvIdentityOpenIDClaimPrefix, ClaimPrefix),
RedirectURI: getCfgVal(EnvIdentityOpenIDRedirectURI, RedirectURI),
RedirectURIDynamic: getCfgVal(EnvIdentityOpenIDRedirectURIDynamic, RedirectURIDynamic) == config.EnableOn,
ClientID: getCfgVal(EnvIdentityOpenIDClientID, ClientID),
ClientSecret: getCfgVal(EnvIdentityOpenIDClientSecret, ClientSecret),
RolePolicy: getCfgVal(EnvIdentityOpenIDRolePolicy, RolePolicy),
DisplayName: getCfgVal(DisplayName),
ClaimName: getCfgVal(ClaimName),
ClaimUserinfo: getCfgVal(ClaimUserinfo) == config.EnableOn,
ClaimPrefix: getCfgVal(ClaimPrefix),
RedirectURI: getCfgVal(RedirectURI),
RedirectURIDynamic: getCfgVal(RedirectURIDynamic) == config.EnableOn,
ClientID: getCfgVal(ClientID),
ClientSecret: getCfgVal(ClientSecret),
RolePolicy: getCfgVal(RolePolicy),
}
}
@@ -72,16 +72,16 @@ const (
// initializeProvider initializes if any additional vendor specific information
// was provided, initialization will return an error initial login fails.
func (p *providerCfg) initializeProvider(cfgGet func(string, string) string, transport http.RoundTripper) error {
vendor := cfgGet(EnvIdentityOpenIDVendor, Vendor)
func (p *providerCfg) initializeProvider(cfgGet func(string) string, transport http.RoundTripper) error {
vendor := cfgGet(Vendor)
if vendor == "" {
return nil
}
var err error
switch vendor {
case keyCloakVendor:
adminURL := cfgGet(EnvIdentityOpenIDKeyCloakAdminURL, KeyCloakAdminURL)
realm := cfgGet(EnvIdentityOpenIDKeyCloakRealm, KeyCloakRealm)
adminURL := cfgGet(KeyCloakAdminURL)
realm := cfgGet(KeyCloakRealm)
p.provider, err = provider.KeyCloak(
provider.WithAdminURL(adminURL),
provider.WithOpenIDConfig(provider.DiscoveryDoc(p.DiscoveryDoc)),