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 }