github.com/greenpau/go-authcrunch@v1.1.4/pkg/authz/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 authz
    16  
    17  import (
    18  	"context"
    19  	"github.com/greenpau/go-authcrunch/pkg/acl"
    20  	"github.com/greenpau/go-authcrunch/pkg/authproxy"
    21  	"github.com/greenpau/go-authcrunch/pkg/authz/bypass"
    22  	"github.com/greenpau/go-authcrunch/pkg/authz/injector"
    23  	"github.com/greenpau/go-authcrunch/pkg/errors"
    24  	"github.com/greenpau/go-authcrunch/pkg/kms"
    25  	cfgutil "github.com/greenpau/go-authcrunch/pkg/util/cfg"
    26  	logutil "github.com/greenpau/go-authcrunch/pkg/util/log"
    27  	"strings"
    28  )
    29  
    30  // PolicyConfig is Gatekeeper configuration.
    31  type PolicyConfig struct {
    32  	Name                       string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"`
    33  	AuthURLPath                string `json:"auth_url_path,omitempty" xml:"auth_url_path,omitempty" yaml:"auth_url_path,omitempty"`
    34  	AuthRedirectDisabled       bool   `json:"disable_auth_redirect,omitempty" xml:"disable_auth_redirect,omitempty" yaml:"disable_auth_redirect,omitempty"`
    35  	AuthRedirectQueryDisabled  bool   `json:"disable_auth_redirect_query,omitempty" xml:"disable_auth_redirect_query,omitempty" yaml:"disable_auth_redirect_query,omitempty"`
    36  	AuthRedirectQueryParameter string `json:"auth_redirect_query_param,omitempty" xml:"auth_redirect_query_param,omitempty" yaml:"auth_redirect_query_param,omitempty"`
    37  	// The status code for the HTTP redirect for non-authorized users.
    38  	AuthRedirectStatusCode int `json:"auth_redirect_status_code,omitempty" xml:"auth_redirect_status_code,omitempty" yaml:"auth_redirect_status_code,omitempty"`
    39  	// Enable the redirect with Javascript, as opposed to HTTP redirect.
    40  	RedirectWithJavascript bool `json:"redirect_with_javascript,omitempty" xml:"redirect_with_javascript,omitempty" yaml:"redirect_with_javascript,omitempty"`
    41  	// The list of URI prefixes which bypass authorization.
    42  	BypassConfigs []*bypass.Config `json:"bypass_configs,omitempty" xml:"bypass_configs,omitempty" yaml:"bypass_configs,omitempty"`
    43  	// The list of mappings between header names and field names.
    44  	HeaderInjectionConfigs []*injector.Config       `json:"header_injection_configs,omitempty" xml:"header_injection_configs,omitempty" yaml:"header_injection_configs,omitempty"`
    45  	AccessListRules        []*acl.RuleConfiguration `json:"access_list_rules,omitempty" xml:"access_list_rules,omitempty" yaml:"access_list_rules,omitempty"`
    46  	CryptoKeyConfigs       []*kms.CryptoKeyConfig   `json:"crypto_key_configs,omitempty" xml:"crypto_key_configs,omitempty" yaml:"crypto_key_configs,omitempty"`
    47  	// CryptoKeyStoreConfig hold the default configuration for the keys, e.g. token name and lifetime.
    48  	CryptoKeyStoreConfig map[string]interface{} `json:"crypto_key_store_config,omitempty" xml:"crypto_key_store_config,omitempty" yaml:"crypto_key_store_config,omitempty"`
    49  	AuthProxyConfig      *authproxy.Config      `json:"auth_proxy_config,omitempty" xml:"auth_proxy_config,omitempty" yaml:"auth_proxy_config,omitempty"`
    50  	AllowedTokenSources  []string               `json:"allowed_token_sources,omitempty" xml:"allowed_token_sources,omitempty" yaml:"allowed_token_sources,omitempty"`
    51  	StripTokenEnabled    bool                   `json:"strip_token_enabled,omitempty" xml:"strip_token_enabled,omitempty" yaml:"strip_token_enabled,omitempty"`
    52  	ForbiddenURL         string                 `json:"forbidden_url,omitempty" xml:"forbidden_url,omitempty" yaml:"forbidden_url,omitempty"`
    53  	UserIdentityField    string                 `json:"user_identity_field,omitempty" xml:"user_identity_field,omitempty" yaml:"user_identity_field,omitempty"`
    54  	// Validate HTTP Authorization header.
    55  	ValidateBearerHeader bool `json:"validate_bearer_header,omitempty" xml:"validate_bearer_header,omitempty" yaml:"validate_bearer_header,omitempty"`
    56  	// Validate HTTP method and path.
    57  	ValidateMethodPath bool `json:"validate_method_path,omitempty" xml:"validate_method_path,omitempty" yaml:"validate_method_path,omitempty"`
    58  	// Validate HTTP path derived from JWT token.
    59  	ValidateAccessListPathClaim bool `json:"validate_access_list_path_claim,omitempty" xml:"validate_access_list_path_claim,omitempty" yaml:"validate_access_list_path_claim,omitempty"`
    60  	// Validate source address matches between HTTP request and JWT token.
    61  	ValidateSourceAddress bool `json:"validate_source_address,omitempty" xml:"validate_source_address,omitempty" yaml:"validate_source_address,omitempty"`
    62  	// Pass claims from JWT token via HTTP X- headers.
    63  	PassClaimsWithHeaders bool `json:"pass_claims_with_headers,omitempty" xml:"pass_claims_with_headers,omitempty" yaml:"pass_claims_with_headers,omitempty"`
    64  	// Validate the login hint which can be passed to the auth provider
    65  	LoginHintValidators []string `json:"login_hint_validators,omitempty" xml:"login_hint_validators,omitempty" yaml:"login_hint_validators,omitempty"`
    66  	// Allow to append scopes that come from the query parameter 'additionalScopes'
    67  	AdditionalScopes bool `json:"additional_scopes,omitempty" xml:"additional_scopes,omitempty" yaml:"additional_scopes,omitempty"`
    68  	// Holds raw crypto configuration.
    69  	cryptoRawConfigs []string
    70  	// Holds raw identity provider configuration.
    71  	authProxyRawConfig []string
    72  
    73  	// Indicated that the config was successfully validated.
    74  	validated bool
    75  }
    76  
    77  // AddRawCryptoConfigs adds raw crypto configs.
    78  func (cfg *PolicyConfig) AddRawCryptoConfigs(s string) {
    79  	cfg.cryptoRawConfigs = append(cfg.cryptoRawConfigs, s)
    80  }
    81  
    82  // AddRawIdpConfig add raw identity provider configs.
    83  func (cfg *PolicyConfig) AddRawIdpConfig(s string) {
    84  	cfg.authProxyRawConfig = append(cfg.authProxyRawConfig, s)
    85  }
    86  
    87  // parseRawCryptoConfigs parses raw crypto configs into CryptoKeyConfigs
    88  // and CryptoKeyStoreConfig.
    89  func (cfg *PolicyConfig) parseRawCryptoConfigs() error {
    90  	var cryptoKeyConfig, cryptoKeyStoreConfig []string
    91  	var cryptoKeyConfigFound, cryptoKeyStoreConfigFound bool
    92  	for _, encodedArgs := range cfg.cryptoRawConfigs {
    93  		args, err := cfgutil.DecodeArgs(encodedArgs)
    94  		if err != nil {
    95  			return errors.ErrConfigDirectiveFail.WithArgs("crypto", encodedArgs, err)
    96  		}
    97  		if len(args) < 3 {
    98  			return errors.ErrConfigDirectiveShort.WithArgs("crypto", args)
    99  		}
   100  		cryptoKeyConfig = append(cryptoKeyConfig, encodedArgs)
   101  		switch args[0] {
   102  		case "key":
   103  			cryptoKeyConfigFound = true
   104  		case "default":
   105  			cryptoKeyStoreConfig = append(cryptoKeyStoreConfig, encodedArgs)
   106  			cryptoKeyStoreConfigFound = true
   107  		default:
   108  			return errors.ErrConfigDirectiveValueUnsupported.WithArgs("crypto", args)
   109  		}
   110  	}
   111  
   112  	if cryptoKeyConfigFound {
   113  		configs, err := kms.ParseCryptoKeyConfigs(strings.Join(cryptoKeyConfig, "\n"))
   114  		if err != nil {
   115  			return errors.ErrConfigDirectiveFail.WithArgs("crypto.key", cryptoKeyConfig, err)
   116  		}
   117  		cfg.CryptoKeyConfigs = configs
   118  	}
   119  
   120  	if cryptoKeyStoreConfigFound {
   121  		configs, err := kms.ParseCryptoKeyStoreConfig(strings.Join(cryptoKeyStoreConfig, "\n"))
   122  		if err != nil {
   123  			return errors.ErrConfigDirectiveFail.WithArgs("crypto.keystore", cryptoKeyStoreConfig, err)
   124  		}
   125  		cfg.CryptoKeyStoreConfig = configs
   126  	}
   127  	return nil
   128  }
   129  
   130  // parseRawAuthProxyConfig parses raw auth proxy configs
   131  // into AuthProxyConfig.
   132  func (cfg *PolicyConfig) parseRawAuthProxyConfig() error {
   133  	if len(cfg.authProxyRawConfig) > 0 {
   134  		config, err := authproxy.ParseConfig(cfg.authProxyRawConfig)
   135  		if err != nil {
   136  			return errors.ErrConfigDirectiveFail.WithArgs("authproxy", cfg.authProxyRawConfig, err)
   137  		}
   138  		cfg.AuthProxyConfig = config
   139  	}
   140  	return nil
   141  }
   142  
   143  // Validate validates PolicyConfig.
   144  func (cfg *PolicyConfig) Validate() error {
   145  	if cfg.validated {
   146  		return nil
   147  	}
   148  	if cfg.Name == "" {
   149  		return errors.ErrPolicyConfigNameNotFound
   150  	}
   151  	if err := cfg.parseRawCryptoConfigs(); err != nil {
   152  		return err
   153  	}
   154  	if err := cfg.parseRawAuthProxyConfig(); err != nil {
   155  		return err
   156  	}
   157  
   158  	// Set authentication redirect URL.
   159  	if cfg.AuthURLPath == "" {
   160  		cfg.AuthURLPath = "/auth"
   161  	}
   162  
   163  	// Set authentication redirect URI parameter.
   164  	if cfg.AuthRedirectQueryParameter == "" {
   165  		cfg.AuthRedirectQueryParameter = "redirect_url"
   166  	}
   167  
   168  	// Set authentication redirect status code value.
   169  	if cfg.AuthRedirectStatusCode == 0 {
   170  		cfg.AuthRedirectStatusCode = 302
   171  	}
   172  
   173  	// Validate bypass URLs, if necessary.
   174  	for _, entry := range cfg.BypassConfigs {
   175  		if err := entry.Validate(); err != nil {
   176  			return errors.ErrInvalidConfiguration.WithArgs(cfg.Name, err)
   177  		}
   178  	}
   179  
   180  	// Validate header injection configs.
   181  	for _, entry := range cfg.HeaderInjectionConfigs {
   182  		if err := entry.Validate(); err != nil {
   183  			return errors.ErrInvalidConfiguration.WithArgs(cfg.Name, err)
   184  		}
   185  		// cfg.PassClaimsWithHeaders = true
   186  	}
   187  
   188  	if len(cfg.AccessListRules) == 0 {
   189  		return errors.ErrInvalidConfiguration.WithArgs(cfg.Name, "access list rule config not found")
   190  	}
   191  
   192  	accessList := acl.NewAccessList()
   193  	accessList.SetLogger(logutil.NewLogger())
   194  	if err := accessList.AddRules(context.Background(), cfg.AccessListRules); err != nil {
   195  		return errors.ErrInvalidConfiguration.WithArgs(cfg.Name, err)
   196  	}
   197  
   198  	cfg.validated = true
   199  	return nil
   200  }