github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/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 authn 16 17 import ( 18 "regexp" 19 "slices" 20 "strings" 21 22 "github.com/greenpau/go-authcrunch/pkg/acl" 23 "github.com/greenpau/go-authcrunch/pkg/authn/cookie" 24 "github.com/greenpau/go-authcrunch/pkg/authn/transformer" 25 "github.com/greenpau/go-authcrunch/pkg/authn/ui" 26 "github.com/greenpau/go-authcrunch/pkg/authz/options" 27 "github.com/greenpau/go-authcrunch/pkg/errors" 28 "github.com/greenpau/go-authcrunch/pkg/kms" 29 "github.com/greenpau/go-authcrunch/pkg/redirects" 30 cfgutil "github.com/greenpau/go-authcrunch/pkg/util/cfg" 31 ) 32 33 const ( 34 defaultGuestRoleName = "authp/guest" 35 defaultUserRoleName = "authp/user" 36 defaultAdminRoleName = "authp/admin" 37 ) 38 39 // PortalConfig represents Portal configuration. 40 type PortalConfig struct { 41 Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` 42 // UI holds the configuration for the user interface. 43 UI *ui.Parameters `json:"ui,omitempty" xml:"ui,omitempty" yaml:"ui,omitempty"` 44 // UserTransformerConfig holds the configuration for the user transformer. 45 UserTransformerConfigs []*transformer.Config `json:"user_transformer_configs,omitempty" xml:"user_transformer_configs,omitempty" yaml:"user_transformer_configs,omitempty"` 46 // CookieConfig holds the configuration for the cookies issues by Authenticator. 47 CookieConfig *cookie.Config `json:"cookie_config,omitempty" xml:"cookie_config,omitempty" yaml:"cookie_config,omitempty"` 48 // The names of identity stores. 49 IdentityStores []string `json:"identity_stores,omitempty" xml:"identity_stores,omitempty" yaml:"identity_stores,omitempty"` 50 // The names of identity providers. 51 IdentityProviders []string `json:"identity_providers,omitempty" xml:"identity_providers,omitempty" yaml:"identity_providers,omitempty"` 52 // The names of SSO providers. 53 SingleSignOnProviders []string `json:"sso_providers,omitempty" xml:"sso_providers,omitempty" yaml:"sso_providers,omitempty"` 54 // The names of user registries. 55 UserRegistries []string `json:"user_registries,omitempty" xml:"user_registries,omitempty" yaml:"user_registries,omitempty"` 56 // AccessListConfigs hold the configurations for the ACL of the token validator. 57 AccessListConfigs []*acl.RuleConfiguration `json:"access_list_configs,omitempty" xml:"access_list_configs,omitempty" yaml:"access_list_configs,omitempty"` 58 // TokenValidatorOptions holds the configuration for the token validator. 59 TokenValidatorOptions *options.TokenValidatorOptions `json:"token_validator_options,omitempty" xml:"token_validator_options,omitempty" yaml:"token_validator_options,omitempty"` 60 // CryptoKeyConfigs hold the configurations for the keys used to issue and validate user tokens. 61 CryptoKeyConfigs []*kms.CryptoKeyConfig `json:"crypto_key_configs,omitempty" xml:"crypto_key_configs,omitempty" yaml:"crypto_key_configs,omitempty"` 62 // CryptoKeyStoreConfig hold the default configuration for the keys, e.g. token name and lifetime. 63 CryptoKeyStoreConfig map[string]interface{} `json:"crypto_key_store_config,omitempty" xml:"crypto_key_store_config,omitempty" yaml:"crypto_key_store_config,omitempty"` 64 // TokenGrantorOptions holds the configuration for the tokens issues by Authenticator. 65 TokenGrantorOptions *options.TokenGrantorOptions `json:"token_grantor_options,omitempty" xml:"token_grantor_options,omitempty" yaml:"token_grantor_options,omitempty"` 66 // TrustedLogoutRedirectURIConfigs holds the configuration of trusted logout redirect URIs. 67 TrustedLogoutRedirectURIConfigs []*redirects.RedirectURIMatchConfig `json:"trusted_logout_redirect_uri_configs,omitempty" xml:"trusted_logout_redirect_uri_configs,omitempty" yaml:"trusted_logout_redirect_uri_configs,omitempty"` 68 69 // PortalAdminRoles holds the list of role names granted to do administrative tasks in the portal. 70 PortalAdminRoles map[string]interface{} `json:"portal_admin_roles,omitempty" xml:"portal_admin_roles,omitempty" yaml:"portal_admin_roles,omitempty"` 71 // PortalUserRoles holds the list of role names granted to do perform profile tasks in the portal. 72 PortalUserRoles map[string]interface{} `json:"portal_user_roles,omitempty" xml:"portal_user_roles,omitempty" yaml:"portal_user_roles,omitempty"` 73 // PortalGuestRoles holds the list of role names without admin or user privileges in the portal. 74 PortalGuestRoles map[string]interface{} `json:"portal_guest_roles,omitempty" xml:"portal_guest_roles,omitempty" yaml:"portal_guest_roles,omitempty"` 75 76 // PortalAdminRolePatterns holds the list of regular expressions for the role names granted to do administrative tasks in the portal. 77 PortalAdminRolePatterns []string `json:"portal_admin_role_patterns,omitempty" xml:"portal_admin_role_patterns,omitempty" yaml:"portal_admin_role_patterns,omitempty"` 78 adminRolePatterns []*regexp.Regexp 79 // PortalUserRolePatterns holds the list of regular expressions for the role names granted to do perform profile tasks in the portal. 80 PortalUserRolePatterns []string `json:"portal_user_role_patterns,omitempty" xml:"portal_user_role_patterns,omitempty" yaml:"portal_user_role_patterns,omitempty"` 81 userRolePatterns []*regexp.Regexp 82 // PortalGuestRolePatterns holds the list of regular expressions for the role names without admin or user privileges in the portal. 83 PortalGuestRolePatterns []string `json:"portal_guest_role_patterns,omitempty" xml:"portal_guest_role_patterns,omitempty" yaml:"portal_guest_role_patterns,omitempty"` 84 guestRolePatterns []*regexp.Regexp 85 reservedPortalRoles map[string]interface{} 86 guestPortalRoles []string 87 // API holds the configuration for API endpoints. 88 API *APIConfig `json:"api,omitempty" xml:"api,omitempty" yaml:"api,omitempty"` 89 90 // Holds raw crypto configuration. 91 cryptoRawConfigs []string 92 93 // Indicated that the config was successfully validated. 94 validated bool 95 } 96 97 // AddRawCryptoConfigs adds raw crypto configs. 98 func (cfg *PortalConfig) AddRawCryptoConfigs(s string) { 99 cfg.cryptoRawConfigs = append(cfg.cryptoRawConfigs, s) 100 } 101 102 // parseRawCryptoConfigs parses raw crypto configs into CryptoKeyConfigs 103 // and CryptoKeyStoreConfig. 104 func (cfg *PortalConfig) parseRawCryptoConfigs() error { 105 var cryptoKeyConfig, cryptoKeyStoreConfig []string 106 var cryptoKeyConfigFound, cryptoKeyStoreConfigFound bool 107 for _, encodedArgs := range cfg.cryptoRawConfigs { 108 args, err := cfgutil.DecodeArgs(encodedArgs) 109 if err != nil { 110 return errors.ErrConfigDirectiveFail.WithArgs("crypto", encodedArgs, err) 111 } 112 if len(args) < 3 { 113 return errors.ErrConfigDirectiveShort.WithArgs("crypto", args) 114 } 115 cryptoKeyConfig = append(cryptoKeyConfig, encodedArgs) 116 switch args[0] { 117 case "key": 118 cryptoKeyConfigFound = true 119 case "default": 120 cryptoKeyStoreConfig = append(cryptoKeyStoreConfig, encodedArgs) 121 cryptoKeyStoreConfigFound = true 122 default: 123 return errors.ErrConfigDirectiveValueUnsupported.WithArgs("crypto", args) 124 } 125 } 126 127 if cryptoKeyConfigFound { 128 configs, err := kms.ParseCryptoKeyConfigs(strings.Join(cryptoKeyConfig, "\n")) 129 if err != nil { 130 return errors.ErrConfigDirectiveFail.WithArgs("crypto.key", cryptoKeyConfig, err) 131 } 132 cfg.CryptoKeyConfigs = configs 133 } 134 135 if cryptoKeyStoreConfigFound { 136 configs, err := kms.ParseCryptoKeyStoreConfig(strings.Join(cryptoKeyStoreConfig, "\n")) 137 if err != nil { 138 return errors.ErrConfigDirectiveFail.WithArgs("crypto.keystore", cryptoKeyStoreConfig, err) 139 } 140 cfg.CryptoKeyStoreConfig = configs 141 } 142 return nil 143 } 144 145 // GetReservedPortalRoles returns the names of reserved portal roles. 146 func (cfg *PortalConfig) GetReservedPortalRoles() map[string]interface{} { 147 if cfg.reservedPortalRoles == nil { 148 cfg.parsePortalRoles() 149 } 150 return cfg.reservedPortalRoles 151 } 152 153 // GetGuestPortalRoles returns the names of guest portal roles. 154 func (cfg *PortalConfig) GetGuestPortalRoles() []string { 155 return cfg.guestPortalRoles 156 } 157 158 // parsePortalRoles validates the configuration of portal roles. 159 func (cfg *PortalConfig) parsePortalRoles() error { 160 if cfg.reservedPortalRoles == nil { 161 cfg.reservedPortalRoles = make(map[string]interface{}) 162 } 163 164 if cfg.PortalAdminRoles == nil { 165 cfg.PortalAdminRoles = make(map[string]interface{}) 166 } 167 if len(cfg.PortalAdminRoles) < 1 { 168 cfg.PortalAdminRoles[defaultAdminRoleName] = true 169 cfg.reservedPortalRoles[defaultAdminRoleName] = true 170 } 171 172 if cfg.PortalUserRoles == nil { 173 cfg.PortalUserRoles = make(map[string]interface{}) 174 } 175 if len(cfg.PortalUserRoles) < 1 { 176 cfg.PortalUserRoles[defaultUserRoleName] = true 177 cfg.reservedPortalRoles[defaultUserRoleName] = true 178 } 179 180 if cfg.PortalGuestRoles == nil { 181 cfg.PortalGuestRoles = make(map[string]interface{}) 182 cfg.reservedPortalRoles[defaultGuestRoleName] = true 183 } 184 if len(cfg.PortalGuestRoles) < 1 { 185 cfg.PortalGuestRoles[defaultGuestRoleName] = true 186 } 187 188 if slices.Contains(cfg.guestPortalRoles, defaultGuestRoleName) { 189 cfg.guestPortalRoles = append(cfg.guestPortalRoles, defaultGuestRoleName) 190 } 191 192 for _, ptrn := range cfg.PortalAdminRolePatterns { 193 if ptrn == "" { 194 return errors.ErrInvalidConfiguration.WithArgs("portal", "admin role pattern is empty") 195 } 196 r, err := regexp.Compile(ptrn) 197 if err != nil { 198 return errors.ErrInvalidConfiguration.WithArgs("portal admin role pattern", err) 199 } 200 cfg.adminRolePatterns = append(cfg.adminRolePatterns, r) 201 } 202 203 for _, ptrn := range cfg.PortalUserRolePatterns { 204 if ptrn == "" { 205 return errors.ErrInvalidConfiguration.WithArgs("portal", "user role pattern is empty") 206 } 207 r, err := regexp.Compile(ptrn) 208 if err != nil { 209 return errors.ErrInvalidConfiguration.WithArgs("portal user role pattern", err) 210 } 211 cfg.userRolePatterns = append(cfg.userRolePatterns, r) 212 } 213 214 for _, ptrn := range cfg.PortalGuestRolePatterns { 215 if ptrn == "" { 216 return errors.ErrInvalidConfiguration.WithArgs("portal", "guest role pattern is empty") 217 } 218 r, err := regexp.Compile(ptrn) 219 if err != nil { 220 return errors.ErrInvalidConfiguration.WithArgs("portal guest role pattern", err) 221 } 222 cfg.guestRolePatterns = append(cfg.guestRolePatterns, r) 223 } 224 225 return nil 226 } 227 228 // Validate validates PortalConfig. 229 func (cfg *PortalConfig) Validate() error { 230 if cfg.validated { 231 return nil 232 } 233 if cfg.Name == "" { 234 return errors.ErrPortalConfigNameNotFound 235 } 236 237 // if len(cfg.IdentityStores) == 0 && len(cfg.IdentityProviders) == 0 { 238 // return errors.ErrPortalConfigBackendsNotFound 239 // } 240 241 if err := cfg.parsePortalRoles(); err != nil { 242 return err 243 } 244 245 if err := cfg.parseRawCryptoConfigs(); err != nil { 246 return err 247 } 248 249 for _, redirURIConfig := range cfg.TrustedLogoutRedirectURIConfigs { 250 if err := redirURIConfig.Validate(); err != nil { 251 return err 252 } 253 } 254 255 // Inialize user interface settings 256 if cfg.UI == nil { 257 cfg.UI = &ui.Parameters{} 258 } 259 260 if cfg.UI.Templates == nil { 261 cfg.UI.Templates = make(map[string]string) 262 } 263 264 cfg.validated = true 265 return nil 266 }