github.com/greenpau/go-authcrunch@v1.1.4/config.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package authcrunch 16 17 import ( 18 "fmt" 19 "github.com/greenpau/go-authcrunch/pkg/authn" 20 "github.com/greenpau/go-authcrunch/pkg/authz" 21 "github.com/greenpau/go-authcrunch/pkg/credentials" 22 "github.com/greenpau/go-authcrunch/pkg/errors" 23 "github.com/greenpau/go-authcrunch/pkg/idp" 24 "github.com/greenpau/go-authcrunch/pkg/ids" 25 "github.com/greenpau/go-authcrunch/pkg/messaging" 26 "github.com/greenpau/go-authcrunch/pkg/registry" 27 "github.com/greenpau/go-authcrunch/pkg/sso" 28 ) 29 30 // Config is a configuration of Server. 31 type Config struct { 32 Credentials *credentials.Config `json:"credentials,omitempty" xml:"credentials,omitempty" yaml:"credentials,omitempty"` 33 Messaging *messaging.Config `json:"messaging,omitempty" xml:"messaging,omitempty" yaml:"messaging,omitempty"` 34 AuthenticationPortals []*authn.PortalConfig `json:"authentication_portals,omitempty" xml:"authentication_portals,omitempty" yaml:"authentication_portals,omitempty"` 35 AuthorizationPolicies []*authz.PolicyConfig `json:"authorization_policies,omitempty" xml:"authorization_policies,omitempty" yaml:"authorization_policies,omitempty"` 36 IdentityStores []*ids.IdentityStoreConfig `json:"identity_stores,omitempty" xml:"identity_stores,omitempty" yaml:"identity_stores,omitempty"` 37 IdentityProviders []*idp.IdentityProviderConfig `json:"identity_providers,omitempty" xml:"identity_providers,omitempty" yaml:"identity_providers,omitempty"` 38 SingleSignOnProviders []*sso.SingleSignOnProviderConfig `json:"sso_providers,omitempty" xml:"sso_providers,omitempty" yaml:"sso_providers,omitempty"` 39 disabledIdentityStores map[string]interface{} 40 disabledIdentityProviders map[string]interface{} 41 UserRegistries []*registry.UserRegistryConfig `json:"user_registries,omitempty" xml:"user_registries,omitempty" yaml:"user_registries,omitempty"` 42 } 43 44 // NewConfig returns an instance of Config. 45 func NewConfig() *Config { 46 return &Config{} 47 } 48 49 // AddCredential adds a credential configuration. 50 func (cfg *Config) AddCredential(c credentials.Credential) error { 51 if cfg.Credentials == nil { 52 cfg.Credentials = &credentials.Config{} 53 } 54 return cfg.Credentials.Add(c) 55 } 56 57 // AddMessagingProvider adds a messaging provider configuration. 58 func (cfg *Config) AddMessagingProvider(p messaging.Provider) error { 59 if cfg.Messaging == nil { 60 cfg.Messaging = &messaging.Config{} 61 } 62 return cfg.Messaging.Add(p) 63 } 64 65 // AddIdentityStore adds an identity store configuration. 66 func (cfg *Config) AddIdentityStore(name, kind string, data map[string]interface{}) error { 67 store, err := ids.NewIdentityStoreConfig(name, kind, data) 68 if err != nil { 69 return err 70 } 71 cfg.IdentityStores = append(cfg.IdentityStores, store) 72 return nil 73 } 74 75 // AddIdentityProvider adds an identity provider configuration. 76 func (cfg *Config) AddIdentityProvider(name, kind string, data map[string]interface{}) error { 77 provider, err := idp.NewIdentityProviderConfig(name, kind, data) 78 if err != nil { 79 return err 80 } 81 cfg.IdentityProviders = append(cfg.IdentityProviders, provider) 82 return nil 83 } 84 85 // AddSingleSignOnProvider adds a single sign-on provider configuration. 86 func (cfg *Config) AddSingleSignOnProvider(data map[string]interface{}) error { 87 provider, err := sso.NewSingleSignOnProviderConfig(data) 88 if err != nil { 89 return err 90 } 91 cfg.SingleSignOnProviders = append(cfg.SingleSignOnProviders, provider) 92 return nil 93 } 94 95 // AddAuthenticationPortal adds an authentication portal configuration. 96 func (cfg *Config) AddAuthenticationPortal(p *authn.PortalConfig) error { 97 if err := p.Validate(); err != nil { 98 return err 99 } 100 cfg.AuthenticationPortals = append(cfg.AuthenticationPortals, p) 101 return nil 102 } 103 104 // AddAuthorizationPolicy adds an authorization policy configuration. 105 func (cfg *Config) AddAuthorizationPolicy(p *authz.PolicyConfig) error { 106 if err := p.Validate(); err != nil { 107 return err 108 } 109 cfg.AuthorizationPolicies = append(cfg.AuthorizationPolicies, p) 110 return nil 111 } 112 113 // Validate validates Config. 114 func (cfg *Config) Validate() error { 115 if len(cfg.AuthenticationPortals) < 1 && len(cfg.AuthorizationPolicies) < 1 { 116 return fmt.Errorf("no portals and gatekeepers found") 117 } 118 119 identityStoreUserRegistry := make(map[string]string) 120 for _, userRegistry := range cfg.UserRegistries { 121 userRegistry.SetCredentials(cfg.Credentials) 122 userRegistry.SetMessaging(cfg.Messaging) 123 if err := userRegistry.ValidateMessaging(); err != nil { 124 return err 125 } 126 var identityStoreFound bool 127 for _, identityStore := range cfg.IdentityStores { 128 if identityStore.Name == userRegistry.IdentityStore { 129 identityStoreFound = true 130 identityStoreUserRegistry[identityStore.Name] = userRegistry.IdentityStore 131 break 132 } 133 } 134 if !identityStoreFound { 135 return fmt.Errorf( 136 "identity store %q referenced in %q user registry not found", 137 userRegistry.IdentityStore, userRegistry.Name, 138 ) 139 } 140 } 141 142 // Validate auth portal configurations. 143 for _, portalCfg := range cfg.AuthenticationPortals { 144 // If there are no excplicitly specified identity stores and providers in a portal, add all of them. 145 if len(portalCfg.IdentityStores) == 0 && len(portalCfg.IdentityProviders) == 0 { 146 for _, entry := range cfg.IdentityStores { 147 portalCfg.IdentityStores = append(portalCfg.IdentityStores, entry.Name) 148 } 149 for _, entry := range cfg.IdentityProviders { 150 portalCfg.IdentityProviders = append(portalCfg.IdentityProviders, entry.Name) 151 } 152 } 153 154 if len(portalCfg.IdentityStores) == 0 && len(portalCfg.IdentityProviders) == 0 { 155 return errors.ErrPortalConfigBackendsNotFound 156 } 157 158 // Filter out disabled identity store names. 159 portalCfg.IdentityStores = cfg.filterDisabledIdentityStores(portalCfg.IdentityStores) 160 161 // Vealidate that there are no duplicate or overlapping identity store and providers. 162 authByName := make(map[string]string) 163 authByRealm := make(map[string]string) 164 165 for _, storeName := range portalCfg.IdentityStores { 166 if v, exists := authByName[storeName]; exists { 167 return fmt.Errorf( 168 "identity store %q has the same name as %s", 169 storeName, v, 170 ) 171 } 172 173 authByName[storeName] = "another identity store" 174 175 var storeConfig *ids.IdentityStoreConfig 176 for _, entry := range cfg.IdentityStores { 177 if entry.Name == storeName { 178 storeConfig = entry 179 break 180 } 181 } 182 if storeConfig == nil { 183 continue 184 } 185 if storeConfig.Params == nil { 186 continue 187 } 188 if v, exists := storeConfig.Params["realm"]; exists { 189 realmName := v.(string) 190 if prevStoreName, exists := authByRealm[realmName]; exists { 191 return fmt.Errorf( 192 "identity provider %q has the same %q realm as %q", 193 storeName, realmName, prevStoreName, 194 ) 195 } 196 authByRealm[realmName] = storeName 197 authByName[storeName] = "identity store in " + realmName + " realm" 198 } 199 200 // Add registry store if configured. 201 if v, exists := identityStoreUserRegistry[storeName]; exists { 202 storeConfig.Params["registration_enabled"] = true 203 portalCfg.UserRegistries = append(portalCfg.UserRegistries, v) 204 } 205 } 206 207 // Filter out disabled identity store names. 208 portalCfg.IdentityProviders = cfg.filterDisabledIdentityProviders(portalCfg.IdentityProviders) 209 210 for _, providerName := range portalCfg.IdentityProviders { 211 if v, exists := authByName[providerName]; exists { 212 return fmt.Errorf( 213 "identity provider %q has the same name as %s", 214 providerName, v, 215 ) 216 } 217 218 authByName[providerName] = "another identity provider" 219 220 var providerConfig *idp.IdentityProviderConfig 221 for _, entry := range cfg.IdentityProviders { 222 providerConfig = entry 223 if entry.Name == providerName { 224 break 225 } 226 } 227 if providerConfig == nil { 228 continue 229 } 230 if providerConfig.Params == nil { 231 continue 232 } 233 if v, exists := providerConfig.Params["realm"]; exists { 234 realmName := v.(string) 235 if prevProviderName, exists := authByRealm[realmName]; exists { 236 return fmt.Errorf( 237 "identity provider %q has the same %q realm as %q", 238 providerName, realmName, prevProviderName, 239 ) 240 } 241 authByRealm[realmName] = providerName 242 authByName[providerName] = "identity provider in " + realmName + " realm" 243 } 244 } 245 246 // Iterate over SSO providers. 247 for _, providerName := range portalCfg.SingleSignOnProviders { 248 var providerFound bool 249 for _, entry := range cfg.SingleSignOnProviders { 250 if providerName == entry.Name { 251 providerFound = true 252 break 253 } 254 } 255 if !providerFound { 256 return fmt.Errorf("sso provider %q configuration not found", providerName) 257 } 258 } 259 } 260 261 return nil 262 } 263 264 // AddDisabledIdentityStore adds the names of disabled identity stores. 265 func (cfg *Config) AddDisabledIdentityStore(s string) { 266 if cfg.disabledIdentityStores == nil { 267 cfg.disabledIdentityStores = map[string]interface{}{ 268 s: true, 269 } 270 return 271 } 272 cfg.disabledIdentityStores[s] = true 273 } 274 275 // AddDisabledIdentityProvider adds the names of disabled identity providers. 276 func (cfg *Config) AddDisabledIdentityProvider(s string) { 277 if cfg.disabledIdentityProviders == nil { 278 cfg.disabledIdentityProviders = map[string]interface{}{ 279 s: true, 280 } 281 return 282 } 283 cfg.disabledIdentityProviders[s] = true 284 } 285 286 func (cfg *Config) filterDisabledIdentityStores(arr []string) []string { 287 var output []string 288 if len(arr) == 0 || cfg.disabledIdentityStores == nil { 289 return arr 290 } 291 for _, s := range arr { 292 if _, exists := cfg.disabledIdentityStores[s]; exists { 293 continue 294 } 295 output = append(output, s) 296 } 297 return output 298 } 299 300 func (cfg *Config) filterDisabledIdentityProviders(arr []string) []string { 301 var output []string 302 if len(arr) == 0 || cfg.disabledIdentityProviders == nil { 303 return arr 304 } 305 for _, s := range arr { 306 if _, exists := cfg.disabledIdentityProviders[s]; exists { 307 continue 308 } 309 output = append(output, s) 310 } 311 return output 312 } 313 314 // AddUserRegistry adds a user registry configuration. 315 func (cfg *Config) AddUserRegistry(r *registry.UserRegistryConfig) error { 316 if err := r.Validate(); err != nil { 317 return err 318 } 319 cfg.UserRegistries = append(cfg.UserRegistries, r) 320 return nil 321 }