github.com/greenpau/go-authcrunch@v1.1.4/pkg/authz/gatekeeper.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/options"
    22  	"github.com/greenpau/go-authcrunch/pkg/authz/validator"
    23  	"github.com/greenpau/go-authcrunch/pkg/errors"
    24  	"github.com/greenpau/go-authcrunch/pkg/kms"
    25  
    26  	"github.com/google/uuid"
    27  	"go.uber.org/zap"
    28  	"strings"
    29  )
    30  
    31  // Gatekeeper is an auth.
    32  type Gatekeeper struct {
    33  	id             string
    34  	config         *PolicyConfig
    35  	tokenValidator *validator.TokenValidator
    36  	opts           *options.TokenValidatorOptions
    37  	accessList     *acl.AccessList
    38  	authenticators []authproxy.Authenticator
    39  	// Enable authorization bypass for specific URIs.
    40  	bypassEnabled bool
    41  	// The names of the headers injected by an instance.
    42  	injectedHeaders map[string]bool
    43  	logger          *zap.Logger
    44  }
    45  
    46  // NewGatekeeper returns an instance of Gatekeeper.
    47  func NewGatekeeper(cfg *PolicyConfig, logger *zap.Logger) (*Gatekeeper, error) {
    48  	if logger == nil {
    49  		return nil, errors.ErrNewGatekeeperLoggerNil
    50  	}
    51  	if cfg == nil {
    52  		return nil, errors.ErrNewGatekeeperConfigNil
    53  	}
    54  	if err := cfg.Validate(); err != nil {
    55  		return nil, errors.ErrNewGatekeeper.WithArgs(err)
    56  	}
    57  	p := &Gatekeeper{
    58  		id:     uuid.New().String(),
    59  		config: cfg,
    60  		logger: logger,
    61  	}
    62  	if err := p.configure(); err != nil {
    63  		return nil, err
    64  	}
    65  	return p, nil
    66  }
    67  
    68  func (g *Gatekeeper) configure() error {
    69  	ctx := context.Background()
    70  
    71  	// Set bypass URLs, if necessary.
    72  	if len(g.config.BypassConfigs) > 0 {
    73  		g.bypassEnabled = true
    74  	}
    75  
    76  	// Configure header injection.
    77  	for _, entry := range g.config.HeaderInjectionConfigs {
    78  		if g.injectedHeaders == nil {
    79  			g.injectedHeaders = make(map[string]bool)
    80  		}
    81  		g.injectedHeaders[entry.Header] = true
    82  	}
    83  
    84  	// Initialize token validator and associated options.
    85  	g.tokenValidator = validator.NewTokenValidator()
    86  	g.opts = options.NewTokenValidatorOptions()
    87  	if g.config.ValidateMethodPath {
    88  		g.opts.ValidateMethodPath = true
    89  	}
    90  	if g.config.ValidateBearerHeader {
    91  		g.opts.ValidateBearerHeader = true
    92  	}
    93  	if g.config.ValidateAccessListPathClaim {
    94  		g.opts.ValidateAccessListPathClaim = true
    95  	}
    96  	if g.config.ValidateSourceAddress {
    97  		g.opts.ValidateSourceAddress = true
    98  	}
    99  
   100  	// Load token configuration into key managers, extract token verification
   101  	// keys and add them to token validator.
   102  	ks := kms.NewCryptoKeyStore()
   103  	if g.config.CryptoKeyStoreConfig != nil {
   104  		// Add default token name, lifetime, etc.
   105  		if err := ks.AddDefaults(g.config.CryptoKeyStoreConfig); err != nil {
   106  			return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   107  		}
   108  	}
   109  	if len(g.config.CryptoKeyConfigs) == 0 {
   110  		if err := ks.AutoGenerate("default", "ES512"); err != nil {
   111  			return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   112  		}
   113  	} else {
   114  		if err := ks.AddKeysWithConfigs(g.config.CryptoKeyConfigs); err != nil {
   115  			return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   116  		}
   117  		if err := ks.HasVerifyKeys(); err != nil {
   118  			return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   119  		}
   120  	}
   121  
   122  	// Load access list.
   123  	if len(g.config.AccessListRules) == 0 {
   124  		return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, "access list rule config not found")
   125  	}
   126  	accessList := acl.NewAccessList()
   127  	accessList.SetLogger(g.logger)
   128  	if err := accessList.AddRules(ctx, g.config.AccessListRules); err != nil {
   129  		return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   130  	}
   131  
   132  	// Configure token validator with keys and access list.
   133  	if err := g.tokenValidator.Configure(ctx, ks.GetVerifyKeys(), accessList, g.opts); err != nil {
   134  		return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   135  	}
   136  
   137  	// Set allow token sources and their priority.
   138  	if len(g.config.AllowedTokenSources) > 0 {
   139  		if err := g.tokenValidator.SetSourcePriority(g.config.AllowedTokenSources); err != nil {
   140  			return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   141  		}
   142  	}
   143  
   144  	g.logger.Debug(
   145  		"Configured gatekeeper",
   146  		zap.String("gatekeeper_name", g.config.Name),
   147  		zap.String("gatekeeper_id", g.id),
   148  		zap.String("auth_url_path", g.config.AuthURLPath),
   149  		zap.String("token_sources", strings.Join(g.tokenValidator.GetSourcePriority(), " ")),
   150  		zap.Any("token_validator_options", g.opts),
   151  		zap.Any("access_list_rules", g.config.AccessListRules),
   152  		zap.String("forbidden_path", g.config.ForbiddenURL),
   153  	)
   154  	return nil
   155  }
   156  
   157  // AddAuthenticators adds authproxy.Authenticator instances to Gatekeeper.
   158  func (g *Gatekeeper) AddAuthenticators(authenticators []authproxy.Authenticator) error {
   159  	g.authenticators = authenticators
   160  	if g.config.AuthProxyConfig != nil {
   161  		if err := g.tokenValidator.RegisterAuthProxy(g.config.AuthProxyConfig, g.authenticators); err != nil {
   162  			return errors.ErrInvalidConfiguration.WithArgs(g.config.Name, err)
   163  		}
   164  	}
   165  	return nil
   166  }