package config import ( "encoding/json" "io" "log" "net/http" "strings" "github.com/spf13/viper" "zeevdiukman.com/zprox/pkg/helper" ) const Viper_File_Name string = "config" const Viper_File_Type string = "yaml" const Viper_File_Path string = "." var ( configData *Config ) func init() { h := helper.New() h.Screen.Clear() configData = createConfig() } func Wrapper(fn func(c *Config)) { fn(configData) } func Get() *Config { return configData } func Get2() *Config { return configData } func createConfig() *Config { c := &Config{} c.initViper() c.setViperOptions(Viper_File_Name, Viper_File_Type, Viper_File_Path) c.viperReadYaml() c.viperUnmarshalYaml() c.initDomainToProxyNameMap() c.initDomainToProxyAuthName() c.initDomainToCertName() c.initOpenIDEndPoints() return c } func (c *Config) initViper() { c.Viper = viper.New() } func (c *Config) setViperOptions(fileName string, fileExtention string, filePath string) { c.Viper.SetConfigName(fileName) c.Viper.SetConfigType(fileExtention) c.Viper.AddConfigPath(filePath) } func (c *Config) viperReadYaml() { err := c.Viper.ReadInConfig() if err != nil { log.Fatalf("Error reading config file, %s", err) } } func (c *Config) viperUnmarshalYaml() { err := c.Viper.Unmarshal(&c) if err != nil { log.Fatalf("Unable to decode into struct, %v", err) } } func (eps *EntryPoints) ForEach(fn func(epName string, epConfig *EntryPoint)) { for epName, epConfig := range *eps { fn(epName, &epConfig) } } func (rp *ReverseProxies) ForEach(fn func(rpName string, rpConfig *ReverseProxy)) { for rpName, rpData := range *rp { fn(rpName, &rpData) } } func (rp *ReverseProxies) ForEachReturn(fn func(rpName string, rpConfig *ReverseProxy) (res ForEachReturn)) *ForEachReturn { res := ForEachReturn{} for rpName, rpData := range *rp { res = append(fn(rpName, &rpData), res) } return &res } // func (rp AuthMap) ForEach(fn func(authName string, authConfig Auth)) { // for authName, authConfig := range rp { // fn(authName, authConfig) // } // } func (c *Config) fetchEndPointsByAuthName(authName string) map[string]any { configURL := c.KeycloakWellknownURL(authName) resp := helper.IsFetchOK[EndPoints](configURL, "", http.Get) respByes, err := io.ReadAll(resp.Body) if err != nil { log.Println(err.Error()) } data := map[string]any{} json.Unmarshal(respByes, &data) return data } func (c *Config) initOpenIDEndPoints() { for authName := range c.AuthMap { data := c.fetchEndPointsByAuthName(authName) c.AuthMap[authName].OpenID.EndPoints = &EndPoints{ Issuer: data["issuer"].(string), Auth: data["authorization_endpoint"].(string), Introspection: data["introspection_endpoint"].(string), Token: data["token_endpoint"].(string), UserInfo: data["userinfo_endpoint"].(string), Logout: data["end_session_endpoint"].(string), JwksUri: data["jwks_uri"].(string), } } } func (c *Config) KeycloakWellknownURL(authName string) string { if _, ok := c.AuthMap[authName]; !ok { return "" } hostUrl := c.AuthMap[authName].OpenID.Host realm := c.AuthMap[authName].OpenID.Realm configPath := c.AuthMap[authName].OpenID.ConfigPath u := hostUrl u += strings.ReplaceAll(configPath, "<{{realm}}>", realm) return u } func (c *Config) GenerateDynamicRedirectUri(proxyName string, authName string) string { if _, ok := c.AuthMap[authName]; !ok { return "" } proto := "http" domain := c.ReverseProxies[proxyName].Domain tls := c.ReverseProxies[proxyName].TLS.Enabled if tls { proto = "https" } redirectURI := c.AuthMap[authName].OpenID.RedirectURI u := redirectURI u = strings.ReplaceAll(redirectURI, "<{{dynamic}}>", proto+"://"+domain) return u } func (c *Config) initDomainToProxyNameMap() { mp := make(map[string]string) c.ReverseProxies.ForEach(func(rpName string, rpConfig *ReverseProxy) { mp[rpConfig.Domain] = rpName }) c.DataMaps.DomainToProxy = mp } func (c *Config) initDomainToProxyAuthName() { authMap := map[string]int{} for authName := range c.AuthMap { authMap[authName] = 1 } mp := make(map[string]string) c.ReverseProxies.ForEach(func(rpName string, rpConfig *ReverseProxy) { if v, ok := authMap[rpConfig.Auth]; ok && v == 1 { mp[rpConfig.Domain] = rpConfig.Auth } }) c.DataMaps.DomainToAuth = mp } func (c *Config) initDomainToCertName() { crtMap := map[string]int{} for crtName := range c.TLS.Certs { crtMap[crtName] = 1 } mp := make(map[string]string) c.ReverseProxies.ForEach(func(rpName string, rpConfig *ReverseProxy) { if v, ok := crtMap[rpConfig.TLS.Certs]; ok && v == 1 { mp[rpConfig.Domain] = rpConfig.TLS.Certs } mp[rpConfig.Domain] = rpConfig.TLS.Certs }) c.DataMaps.DomainToCert = mp } func (c *Config) GetAuthNameByDomain(domain string) string { return c.DataMaps.DomainToAuth[domain] } func (c *Config) GetProxyNameByDomain(domain string) string { return c.DataMaps.DomainToProxy[domain] } func (c *Config) GetCertNameByDomain(domain string) string { return c.DataMaps.DomainToCert[domain] } func (c *Config) GetCertsPairByDomain(domain string) (string, string) { var crt string var key string certName := c.DataMaps.DomainToCert[domain] crt = c.TLS.Certs[certName].Cert key = c.TLS.Certs[certName].Key if !(certName != "" && crt == "" && key != "") { certName = "default" crt = c.TLS.Certs[certName].Cert key = c.TLS.Certs[certName].Key } return crt, key } func (c *Config) GetProxiesSliceByAuthName(authName string) []string { slc := &[]string{} c.ReverseProxies.ForEach(func(rpName string, rpConfig *ReverseProxy) { isOK := true if authName != rpConfig.Auth { isOK = false } if _, ok := c.AuthMap[authName]; !ok { isOK = false } if isOK { helper.AppendToPointer(slc, rpName) } }) return *slc } func (c *Config) GetAuthNameByProxyName(proxyName string) string { return c.ReverseProxies[proxyName].Auth }