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  }