github.com/greenpau/go-authcrunch@v1.1.4/pkg/authz/validator/validator.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 validator 16 17 import ( 18 "context" 19 "net/http" 20 "strings" 21 22 "github.com/greenpau/go-authcrunch/pkg/acl" 23 "github.com/greenpau/go-authcrunch/pkg/authproxy" 24 "github.com/greenpau/go-authcrunch/pkg/authz/cache" 25 "github.com/greenpau/go-authcrunch/pkg/authz/options" 26 "github.com/greenpau/go-authcrunch/pkg/errors" 27 "github.com/greenpau/go-authcrunch/pkg/kms" 28 "github.com/greenpau/go-authcrunch/pkg/user" 29 addrutil "github.com/greenpau/go-authcrunch/pkg/util/addr" 30 ) 31 32 type guardian interface { 33 authorize(context.Context, *http.Request, *user.User) error 34 } 35 36 type guardianBase struct { 37 accessList *acl.AccessList 38 } 39 40 type guardianWithSrcAddr struct { 41 accessList *acl.AccessList 42 } 43 44 type guardianWithPathClaim struct { 45 accessList *acl.AccessList 46 } 47 48 type guardianWithMethodPath struct { 49 accessList *acl.AccessList 50 } 51 52 type guardianWithSrcAddrPathClaim struct { 53 accessList *acl.AccessList 54 } 55 56 type guardianWithMethodPathSrcAddr struct { 57 accessList *acl.AccessList 58 } 59 60 type guardianWithMethodPathPathClaim struct { 61 accessList *acl.AccessList 62 } 63 64 type guardianWithMethodPathSrcAddrPathClaim struct { 65 accessList *acl.AccessList 66 } 67 68 // TokenValidator validates tokens in http requests. 69 type TokenValidator struct { 70 keystore *kms.CryptoKeyStore 71 authHeaders map[string]interface{} 72 authCookies map[string]interface{} 73 authQueryParams map[string]interface{} 74 cache *cache.TokenCache 75 accessList *acl.AccessList 76 guardian guardian 77 tokenSources []string 78 opts *options.TokenValidatorOptions 79 basicAuthEnabled bool 80 apiKeyAuthEnabled bool 81 customAuthEnabled bool 82 authProxyConfig *authproxy.Config 83 authProxy authproxy.Authenticator 84 } 85 86 // NewTokenValidator returns an instance of TokenValidator 87 func NewTokenValidator() *TokenValidator { 88 v := &TokenValidator{ 89 keystore: kms.NewCryptoKeyStore(), 90 authHeaders: make(map[string]interface{}), 91 authCookies: make(map[string]interface{}), 92 authQueryParams: make(map[string]interface{}), 93 } 94 95 for _, name := range defaultTokenNames { 96 v.authHeaders[name] = true 97 v.authCookies[name] = true 98 v.authQueryParams[name] = true 99 } 100 101 v.cache = cache.NewTokenCache(0) 102 v.tokenSources = defaultTokenSources 103 return v 104 } 105 106 // GetAuthCookies returns auth cookies registered with TokenValidator. 107 func (v *TokenValidator) GetAuthCookies() map[string]interface{} { 108 return v.authCookies 109 } 110 111 func (v *TokenValidator) setAllowedTokenNames(arr []string) error { 112 m := make(map[string]bool) 113 for _, s := range arr { 114 s = strings.TrimSpace(s) 115 if s == "" { 116 return errors.ErrEmptyTokenName 117 } 118 if _, exists := m[s]; exists { 119 return errors.ErrDuplicateTokenName.WithArgs(s) 120 } 121 m[s] = true 122 } 123 v.clearAuthSources() 124 for _, s := range arr { 125 v.authHeaders[s] = true 126 v.authCookies[s] = true 127 v.authQueryParams[s] = true 128 } 129 return nil 130 } 131 132 // SetSourcePriority sets the order in which various token sources are being 133 // evaluated for the presence of keys. The default order is cookie, header, 134 // and query parameters. 135 func (v *TokenValidator) SetSourcePriority(arr []string) error { 136 if len(arr) == 0 || len(arr) > 3 { 137 return errors.ErrInvalidSourcePriority 138 } 139 m := make(map[string]bool) 140 for _, s := range arr { 141 s = strings.TrimSpace(s) 142 if s != tokenSourceHeader && s != tokenSourceCookie && s != tokenSourceQuery { 143 return errors.ErrInvalidSourceName.WithArgs(s) 144 } 145 if _, exists := m[s]; exists { 146 return errors.ErrDuplicateSourceName.WithArgs(s) 147 } 148 m[s] = true 149 } 150 v.tokenSources = arr 151 return nil 152 } 153 154 // GetSourcePriority returns the allowed token sources in their priority order. 155 func (v *TokenValidator) GetSourcePriority() []string { 156 return v.tokenSources 157 } 158 159 func (g *guardianBase) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 160 // Note: the cache was removed because authorize uses the same 161 // authorization endpoint. Previously, the endpoint was 162 // attached to a route. 163 // if usr.Cached { 164 // return nil 165 // } 166 if userAllowed := g.accessList.Allow(ctx, usr.GetData()); !userAllowed { 167 return errors.ErrAccessNotAllowed 168 } 169 return nil 170 } 171 172 func (g *guardianWithSrcAddr) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 173 if userAllowed := g.accessList.Allow(ctx, usr.GetData()); !userAllowed { 174 return errors.ErrAccessNotAllowed 175 } 176 if usr.Claims.Address == "" { 177 return errors.ErrSourceAddressNotFound 178 } 179 reqAddr := addrutil.GetSourceAddress(r) 180 if usr.Claims.Address != reqAddr { 181 return errors.ErrSourceAddressMismatch.WithArgs(usr.Claims.Address, reqAddr) 182 } 183 return nil 184 } 185 186 func (g *guardianWithPathClaim) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 187 if userAllowed := g.accessList.Allow(ctx, usr.GetData()); !userAllowed { 188 return errors.ErrAccessNotAllowed 189 } 190 if usr.Claims.AccessList == nil { 191 return errors.ErrAccessNotAllowedByPathACL 192 } 193 for path := range usr.Claims.AccessList.Paths { 194 if acl.MatchPathBasedACL(path, r.URL.Path) { 195 return nil 196 } 197 } 198 return errors.ErrAccessNotAllowedByPathACL 199 } 200 201 func (g *guardianWithSrcAddrPathClaim) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 202 if userAllowed := g.accessList.Allow(ctx, usr.GetData()); !userAllowed { 203 return errors.ErrAccessNotAllowed 204 } 205 if usr.Claims.Address == "" { 206 return errors.ErrSourceAddressNotFound 207 } 208 reqAddr := addrutil.GetSourceAddress(r) 209 if usr.Claims.Address != reqAddr { 210 return errors.ErrSourceAddressMismatch.WithArgs(usr.Claims.Address, reqAddr) 211 } 212 if usr.Claims.AccessList == nil { 213 return errors.ErrAccessNotAllowedByPathACL 214 } 215 for path := range usr.Claims.AccessList.Paths { 216 if acl.MatchPathBasedACL(path, r.URL.Path) { 217 return nil 218 } 219 } 220 return errors.ErrAccessNotAllowedByPathACL 221 } 222 223 func (g *guardianWithMethodPath) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 224 kv := make(map[string]interface{}) 225 for k, v := range usr.GetData() { 226 kv[k] = v 227 } 228 kv["method"] = r.Method 229 kv["path"] = r.URL.Path 230 if userAllowed := g.accessList.Allow(ctx, kv); !userAllowed { 231 return errors.ErrAccessNotAllowed 232 } 233 return nil 234 } 235 236 func (g *guardianWithMethodPathSrcAddr) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 237 kv := make(map[string]interface{}) 238 for k, v := range usr.GetData() { 239 kv[k] = v 240 } 241 kv["method"] = r.Method 242 kv["path"] = r.URL.Path 243 if userAllowed := g.accessList.Allow(ctx, kv); !userAllowed { 244 return errors.ErrAccessNotAllowed 245 } 246 if usr.Claims.Address == "" { 247 return errors.ErrSourceAddressNotFound 248 } 249 reqAddr := addrutil.GetSourceAddress(r) 250 if usr.Claims.Address != reqAddr { 251 return errors.ErrSourceAddressMismatch.WithArgs(usr.Claims.Address, reqAddr) 252 } 253 return nil 254 } 255 256 func (g *guardianWithMethodPathPathClaim) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 257 kv := make(map[string]interface{}) 258 for k, v := range usr.GetData() { 259 kv[k] = v 260 } 261 kv["method"] = r.Method 262 kv["path"] = r.URL.Path 263 if userAllowed := g.accessList.Allow(ctx, kv); !userAllowed { 264 return errors.ErrAccessNotAllowed 265 } 266 if usr.Claims.AccessList == nil { 267 return errors.ErrAccessNotAllowedByPathACL 268 } 269 for path := range usr.Claims.AccessList.Paths { 270 if acl.MatchPathBasedACL(path, r.URL.Path) { 271 return nil 272 } 273 } 274 return errors.ErrAccessNotAllowedByPathACL 275 } 276 277 func (g *guardianWithMethodPathSrcAddrPathClaim) authorize(ctx context.Context, r *http.Request, usr *user.User) error { 278 kv := make(map[string]interface{}) 279 for k, v := range usr.GetData() { 280 kv[k] = v 281 } 282 kv["method"] = r.Method 283 kv["path"] = r.URL.Path 284 if userAllowed := g.accessList.Allow(ctx, kv); !userAllowed { 285 return errors.ErrAccessNotAllowed 286 } 287 if usr.Claims.Address == "" { 288 return errors.ErrSourceAddressNotFound 289 } 290 reqAddr := addrutil.GetSourceAddress(r) 291 if usr.Claims.Address != reqAddr { 292 return errors.ErrSourceAddressMismatch.WithArgs(usr.Claims.Address, reqAddr) 293 } 294 295 if usr.Claims.AccessList == nil { 296 return errors.ErrAccessNotAllowedByPathACL 297 } 298 for path := range usr.Claims.AccessList.Paths { 299 if acl.MatchPathBasedACL(path, r.URL.Path) { 300 return nil 301 } 302 } 303 return errors.ErrAccessNotAllowedByPathACL 304 } 305 306 // Configure adds access list and keys for the verification of tokens. 307 func (v *TokenValidator) Configure(ctx context.Context, keys []*kms.CryptoKey, accessList *acl.AccessList, opts *options.TokenValidatorOptions) error { 308 if err := v.addKeys(ctx, keys); err != nil { 309 return err 310 } 311 if err := v.addAccessList(ctx, accessList); err != nil { 312 return err 313 } 314 if opts == nil { 315 return errors.ErrTokenValidatorOptionsNotFound 316 } 317 318 v.opts = opts 319 320 switch { 321 case opts.ValidateMethodPath && opts.ValidateSourceAddress && opts.ValidateAccessListPathClaim: 322 g := &guardianWithMethodPathSrcAddrPathClaim{accessList: accessList} 323 v.guardian = g 324 case opts.ValidateMethodPath && opts.ValidateAccessListPathClaim: 325 g := &guardianWithMethodPathPathClaim{accessList: accessList} 326 v.guardian = g 327 case opts.ValidateMethodPath && opts.ValidateSourceAddress: 328 g := &guardianWithMethodPathSrcAddr{accessList: accessList} 329 v.guardian = g 330 case opts.ValidateSourceAddress && opts.ValidateAccessListPathClaim: 331 g := &guardianWithSrcAddrPathClaim{accessList: accessList} 332 v.guardian = g 333 case opts.ValidateAccessListPathClaim: 334 g := &guardianWithPathClaim{accessList: accessList} 335 v.guardian = g 336 case opts.ValidateMethodPath: 337 g := &guardianWithMethodPath{accessList: accessList} 338 v.guardian = g 339 case opts.ValidateSourceAddress: 340 g := &guardianWithSrcAddr{accessList: accessList} 341 v.guardian = g 342 default: 343 g := &guardianBase{accessList: accessList} 344 v.guardian = g 345 } 346 return nil 347 } 348 349 func (v *TokenValidator) addAccessList(ctx context.Context, accessList *acl.AccessList) error { 350 if accessList == nil { 351 return errors.ErrNoAccessList 352 } 353 if len(accessList.GetRules()) == 0 { 354 return errors.ErrAccessListNoRules 355 } 356 357 v.accessList = accessList 358 return nil 359 } 360 361 func (v *TokenValidator) addKeys(ctx context.Context, keys []*kms.CryptoKey) error { 362 var tokenNames []string 363 tokenMap := make(map[string]bool) 364 if len(keys) == 0 { 365 return errors.ErrValidatorCryptoKeyStoreNoKeys 366 } 367 for _, k := range keys { 368 if !k.Verify.Token.Capable { 369 continue 370 } 371 if k.Verify.Token.Name == "" { 372 continue 373 } 374 if k.Verify.Token.MaxLifetime == 0 { 375 continue 376 } 377 v.keystore.AddKey(k) 378 tokenMap[k.Verify.Token.Name] = true 379 } 380 if len(tokenMap) == 0 { 381 return errors.ErrValidatorCryptoKeyStoreNoVerifyKeys 382 } 383 384 for k := range tokenMap { 385 tokenNames = append(tokenNames, k) 386 } 387 388 if err := v.setAllowedTokenNames(tokenNames); err != nil { 389 return err 390 } 391 392 return nil 393 } 394 395 // CacheUser adds a user to token validator cache. 396 func (v *TokenValidator) CacheUser(usr *user.User) error { 397 return v.cache.Add(usr) 398 } 399 400 // RegisterAuthProxy registers authproxy.Authenticator with TokenValidator. 401 func (v *TokenValidator) RegisterAuthProxy(cfg *authproxy.Config, authenticators []authproxy.Authenticator) error { 402 if cfg == nil { 403 return errors.ErrValidatorAuthProxy 404 } 405 if cfg.PortalName == "" { 406 return errors.ErrValidatorAuthProxyPortalName 407 } 408 409 if cfg.BasicAuth.Enabled { 410 v.basicAuthEnabled = true 411 v.customAuthEnabled = true 412 } 413 if cfg.APIKeyAuth.Enabled { 414 v.apiKeyAuthEnabled = true 415 v.customAuthEnabled = true 416 } 417 v.authProxyConfig = cfg 418 419 for _, authenticator := range authenticators { 420 if authenticator.GetName() != cfg.PortalName { 421 continue 422 } 423 v.authProxy = authenticator 424 return nil 425 } 426 427 return errors.ErrValidatorAuthProxyNotFound.WithArgs(cfg.PortalName) 428 }