github.com/greenpau/go-authcrunch@v1.1.4/pkg/authz/validator/auth.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  	"github.com/greenpau/go-authcrunch/pkg/authproxy"
    20  	"github.com/greenpau/go-authcrunch/pkg/errors"
    21  	"github.com/greenpau/go-authcrunch/pkg/requests"
    22  	addrutil "github.com/greenpau/go-authcrunch/pkg/util/addr"
    23  	"net/http"
    24  	"strings"
    25  )
    26  
    27  // parseCustomAuthHeader authorizes HTTP requests based on the presence and the
    28  // content of HTTP Authorization or X-API-Key headers.
    29  func (v *TokenValidator) parseCustomAuthHeader(ctx context.Context, r *http.Request, ar *requests.AuthorizationRequest) error {
    30  	if v.basicAuthEnabled {
    31  		if err := v.parseCustomBasicAuthHeader(ctx, r, ar); err != nil {
    32  			return err
    33  		}
    34  	}
    35  	if !ar.Token.Found && v.apiKeyAuthEnabled {
    36  		return v.parseCustomAPIKeyAuthHeader(ctx, r, ar)
    37  	}
    38  	return nil
    39  }
    40  
    41  func (v *TokenValidator) parseCustomBasicAuthHeader(ctx context.Context, r *http.Request, ar *requests.AuthorizationRequest) error {
    42  	var tokenSecret, tokenRealm string
    43  	hdr := r.Header.Get("Authorization")
    44  	if hdr == "" {
    45  		return nil
    46  	}
    47  	entries := strings.Split(hdr, ",")
    48  	for _, entry := range entries {
    49  		entry = strings.TrimSpace(entry)
    50  		if !strings.HasPrefix(entry, "Basic") {
    51  			continue
    52  		}
    53  		entry = strings.TrimPrefix(entry, "Basic")
    54  		entry = strings.TrimSpace(entry)
    55  
    56  		ar.Token.Source = "basicauth"
    57  		ar.Token.Name = "Basic"
    58  		ar.Token.Found = true
    59  
    60  		sep := strings.Index(entry, " ")
    61  		if sep < 0 {
    62  			tokenSecret = entry
    63  		} else {
    64  			tokenSecret = entry[:sep]
    65  			directives := parseAuthHeaderDirectives(entry[sep+1:])
    66  			if directives != nil {
    67  				if realm, exists := directives["realm"]; exists {
    68  					tokenRealm = realm
    69  				}
    70  			}
    71  		}
    72  		break
    73  	}
    74  
    75  	if ar.Token.Found {
    76  		if tokenRealm != "" {
    77  			// Check if the realm is registered.
    78  			if _, exists := v.authProxyConfig.BasicAuth.Realms[tokenRealm]; !exists {
    79  				return errors.ErrBasicAuthFailed
    80  			}
    81  		}
    82  
    83  		apr := &authproxy.Request{
    84  			Address: addrutil.GetSourceAddress(r),
    85  			Realm:   tokenRealm,
    86  			Secret:  tokenSecret,
    87  		}
    88  
    89  		if err := v.authProxy.BasicAuth(apr); err != nil {
    90  			return err
    91  		}
    92  
    93  		ar.Token.Name = apr.Response.Name
    94  		ar.Token.Payload = apr.Response.Payload
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func (v *TokenValidator) parseCustomAPIKeyAuthHeader(ctx context.Context, r *http.Request, ar *requests.AuthorizationRequest) error {
   101  	var tokenSecret, tokenRealm string
   102  	hdr := r.Header.Get("X-API-Key")
   103  	if hdr == "" {
   104  		return nil
   105  	}
   106  	entry := strings.TrimSpace(hdr)
   107  
   108  	ar.Token.Source = "apikey"
   109  	ar.Token.Name = "X-API-Key"
   110  	ar.Token.Found = true
   111  
   112  	sep := strings.Index(entry, " ")
   113  	if sep < 0 {
   114  		tokenSecret = entry
   115  	} else {
   116  		tokenSecret = entry[:sep]
   117  		directives := parseAuthHeaderDirectives(entry[sep+1:])
   118  		if directives != nil {
   119  			if realm, exists := directives["realm"]; exists {
   120  				tokenRealm = realm
   121  			}
   122  		}
   123  	}
   124  
   125  	if tokenRealm != "" {
   126  		// Check if the realm is registered.
   127  		if _, exists := v.authProxyConfig.APIKeyAuth.Realms[tokenRealm]; !exists {
   128  			return errors.ErrAPIKeyAuthFailed
   129  		}
   130  	}
   131  
   132  	apr := &authproxy.Request{
   133  		Address: addrutil.GetSourceAddress(r),
   134  		Realm:   tokenRealm,
   135  		Secret:  tokenSecret,
   136  	}
   137  
   138  	if err := v.authProxy.APIKeyAuth(apr); err != nil {
   139  		return err
   140  	}
   141  	ar.Token.Name = apr.Response.Name
   142  	ar.Token.Payload = apr.Response.Payload
   143  	return nil
   144  }
   145  
   146  func parseAuthHeaderDirectives(s string) map[string]string {
   147  	m := make(map[string]string)
   148  	for _, entry := range strings.Split(s, ",") {
   149  		kv := strings.SplitN(strings.TrimSpace(entry), "=", 2)
   150  		if len(kv) != 2 {
   151  			continue
   152  		}
   153  		m[kv[0]] = strings.Trim(kv[1], `"'`)
   154  	}
   155  	return m
   156  }