github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/auth/auth.go (about)

     1  package auth
     2  
     3  import (
     4  	"context"
     5  	"crypto/x509"
     6  	"encoding/json"
     7  	"fmt"
     8  	"net/http"
     9  	"net/url"
    10  	"strings"
    11  	"sync"
    12  
    13  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets"
    14  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/servicetokens"
    15  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/urisearch"
    16  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/usertokens"
    17  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    18  	"go.uber.org/zap"
    19  )
    20  
    21  // CallbackResponse captures all the response data of the call back processing.
    22  type CallbackResponse struct {
    23  	Cookie    *http.Cookie
    24  	Status    int
    25  	OriginURL string
    26  	Data      string
    27  	Message   string
    28  }
    29  
    30  // Processor holds all the local data of the authorization engine. A processor
    31  // can handle authorization for multiple services. The goal is to authenticate
    32  // a request based on both service and user credentials.
    33  type Processor struct {
    34  	apis                  *urisearch.APICache
    35  	userTokenHandler      usertokens.Verifier
    36  	userTokenMappings     map[string]string
    37  	userAuthorizationType policy.UserAuthorizationTypeValues
    38  	aporetoJWT            *servicetokens.Verifier
    39  	sync.RWMutex
    40  }
    41  
    42  // NewProcessor creates an auth processor with PKI user tokens. The caller
    43  // must provide a valid secrets structure and an optional list of trustedCertificates
    44  // that can be used to validate tokens. If the list is empty, the CA from the secrets
    45  // will be used for token validation.
    46  func NewProcessor(s secrets.Secrets, trustedCertificate *x509.Certificate) *Processor {
    47  	return &Processor{
    48  		aporetoJWT: servicetokens.NewVerifier(s, trustedCertificate),
    49  	}
    50  }
    51  
    52  // UpdateSecrets will update the Aporeto secrets for the validation of the
    53  // Aporeto tokens.
    54  func (p *Processor) UpdateSecrets(s secrets.Secrets, trustedCertificate *x509.Certificate) {
    55  	p.aporetoJWT.UpdateSecrets(s, trustedCertificate)
    56  }
    57  
    58  // AddOrUpdateService adds or replaces a service in the authorization db.
    59  func (p *Processor) AddOrUpdateService(apis *urisearch.APICache, serviceType policy.UserAuthorizationTypeValues, handler usertokens.Verifier, mappings map[string]string) {
    60  	p.Lock()
    61  	defer p.Unlock()
    62  
    63  	p.apis = apis
    64  	p.userTokenMappings = mappings
    65  	p.userTokenHandler = handler
    66  	p.userAuthorizationType = serviceType
    67  }
    68  
    69  // UpdateServiceAPIs updates an existing service with a new API definition.
    70  func (p *Processor) UpdateServiceAPIs(apis *urisearch.APICache) error {
    71  	p.Lock()
    72  	defer p.Unlock()
    73  
    74  	p.apis = apis
    75  	return nil
    76  }
    77  
    78  // DecodeUserClaims decodes the user claims with the user authorization method.
    79  func (p *Processor) DecodeUserClaims(ctx context.Context, name, userToken string, certs []*x509.Certificate) ([]string, bool, string, error) {
    80  
    81  	switch p.userAuthorizationType {
    82  	case policy.UserAuthorizationMutualTLS, policy.UserAuthorizationJWT:
    83  		// First parse any incoming certificates and retrieve attributes from them.
    84  		// This is used in case of client authorization with certificates.
    85  		attributes := []string{}
    86  		for _, cert := range certs {
    87  			attributes = append(attributes, "CN="+cert.Subject.CommonName)
    88  			for _, email := range cert.EmailAddresses {
    89  				attributes = append(attributes, "Email="+email)
    90  			}
    91  			for _, org := range cert.Subject.Organization {
    92  				attributes = append(attributes, "O="+org)
    93  			}
    94  			for _, org := range cert.Subject.OrganizationalUnit {
    95  				attributes = append(attributes, "OU="+org)
    96  			}
    97  		}
    98  
    99  		if p.userAuthorizationType == policy.UserAuthorizationJWT && p.userTokenHandler != nil {
   100  			jwtAttributes, _, _, err := p.userTokenHandler.Validate(ctx, userToken)
   101  			if err != nil {
   102  				return attributes, false, userToken, fmt.Errorf("Unable to decode JWT: %s", err)
   103  			}
   104  			attributes = append(attributes, jwtAttributes...)
   105  		}
   106  
   107  		return attributes, false, userToken, nil
   108  
   109  	case policy.UserAuthorizationOIDC:
   110  		// Now we can parse the user claims.
   111  		if p.userTokenHandler == nil {
   112  			zap.L().Error("Internal Server Error: OIDC User Token Handler not configured")
   113  			return []string{}, false, userToken, nil
   114  		}
   115  		return p.userTokenHandler.Validate(ctx, userToken)
   116  	default:
   117  		return []string{}, false, userToken, nil
   118  	}
   119  }
   120  
   121  // DecodeAporetoClaims decodes the Aporeto claims
   122  func (p *Processor) DecodeAporetoClaims(aporetoToken string, publicKey string) (string, []string, *policy.PingPayload, error) {
   123  	if len(aporetoToken) == 0 || p.aporetoJWT == nil {
   124  		return "", []string{}, nil, nil
   125  	}
   126  
   127  	// Finally we can parse the Aporeto token.
   128  	id, scopes, profile, pingPayload, err := p.aporetoJWT.ParseToken(aporetoToken, publicKey)
   129  	if err != nil {
   130  		return "", []string{}, nil, fmt.Errorf("Invalid Aporeto Token: %s", err)
   131  	}
   132  
   133  	return id, append(profile, scopes...), pingPayload, nil
   134  }
   135  
   136  // Callback is function called by and IDP auth provider will exchange the provided
   137  // authorization code with a JWT token. This closes the Oauth loop.
   138  func (p *Processor) Callback(ctx context.Context, u *url.URL) (*CallbackResponse, error) {
   139  	p.RLock()
   140  	defer p.RUnlock()
   141  
   142  	c := &CallbackResponse{}
   143  
   144  	// Validate the JWT token through the handler.
   145  	token, originURL, status, err := p.userTokenHandler.Callback(ctx, u)
   146  	if err != nil {
   147  		c.Status = http.StatusUnauthorized
   148  		c.Message = fmt.Sprintf("Invalid code %s:", err)
   149  		return c, err
   150  	}
   151  
   152  	c.OriginURL = originURL
   153  	c.Status = status
   154  	c.Cookie = &http.Cookie{
   155  		Name:     "X-APORETO-AUTH",
   156  		Value:    token,
   157  		HttpOnly: true,
   158  		Secure:   true,
   159  		Path:     "/",
   160  	}
   161  
   162  	// We transmit the information in the return payload for applications
   163  	// that choose to use it directly without a cookie.
   164  	data, err := json.MarshalIndent(c.Cookie, " ", " ")
   165  	if err != nil {
   166  		c.Status = http.StatusInternalServerError
   167  		c.Message = "Unable to decode data"
   168  		return c, err
   169  	}
   170  
   171  	c.Data = string(data)
   172  
   173  	return c, nil
   174  }
   175  
   176  // Check is the main method that will search API cache and validate whether the call should
   177  // be allowed. It returns two values. If the access is allowed, and whether the access
   178  // public or not. This allows callers to decide what to do when there is a failure, and
   179  // potentially issue a redirect.
   180  func (p *Processor) Check(method, uri string, claims []string) (bool, bool) {
   181  	p.RLock()
   182  	defer p.RUnlock()
   183  
   184  	return p.apis.FindAndMatchScope(method, uri, claims)
   185  }
   186  
   187  // RedirectURI returns the redirect URI in order to start the authentication dance.
   188  func (p *Processor) RedirectURI(originURL string) string {
   189  	p.RLock()
   190  	defer p.RUnlock()
   191  
   192  	return p.userTokenHandler.IssueRedirect(originURL)
   193  }
   194  
   195  // UpdateRequestHeaders will update the request headers based on the user claims
   196  // and the corresponding mappings.
   197  func (p *Processor) UpdateRequestHeaders(h http.Header, claims []string) {
   198  	p.RLock()
   199  	defer p.RUnlock()
   200  
   201  	if len(p.userTokenMappings) == 0 {
   202  		return
   203  	}
   204  
   205  	for _, claim := range claims {
   206  		parts := strings.SplitN(claim, "=", 2)
   207  		if header, ok := p.userTokenMappings[parts[0]]; ok && len(parts) == 2 {
   208  			h.Add(header, parts[1])
   209  		}
   210  	}
   211  }
   212  
   213  // RetrieveServiceHandler will retrieve the service that is stored in the serviceMap
   214  func (p *Processor) RetrieveServiceHandler() (usertokens.Verifier, error) {
   215  	return p.userTokenHandler, nil
   216  }