github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/offerauth.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver
     5  
     6  import (
     7  	"context"
     8  	"net/http"
     9  
    10  	"github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
    11  	"github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
    12  	"github.com/go-macaroon-bakery/macaroon-bakery/v3/httpbakery"
    13  	"github.com/juju/errors"
    14  
    15  	"github.com/juju/juju/apiserver/apiserverhttp"
    16  	"github.com/juju/juju/apiserver/common/crossmodel"
    17  	"github.com/juju/juju/core/macaroon"
    18  	"github.com/juju/juju/state"
    19  )
    20  
    21  const (
    22  	localOfferAccessLocationPath = "/offeraccess"
    23  )
    24  
    25  type localOfferAuthHandler struct {
    26  	authCtx *crossmodel.AuthContext
    27  }
    28  
    29  func addOfferAuthHandlers(offerAuthCtxt *crossmodel.AuthContext, mux *apiserverhttp.Mux) {
    30  	appOfferHandler := &localOfferAuthHandler{authCtx: offerAuthCtxt}
    31  	appOfferDischargeMux := http.NewServeMux()
    32  
    33  	discharger := httpbakery.NewDischarger(
    34  		httpbakery.DischargerParams{
    35  			Key:     offerAuthCtxt.OfferThirdPartyKey(),
    36  			Checker: httpbakery.ThirdPartyCaveatCheckerFunc(appOfferHandler.checkThirdPartyCaveat),
    37  		})
    38  	discharger.AddMuxHandlers(appOfferDischargeMux, localOfferAccessLocationPath)
    39  
    40  	_ = mux.AddHandler("POST", localOfferAccessLocationPath+"/discharge", appOfferDischargeMux)
    41  	_ = mux.AddHandler("GET", localOfferAccessLocationPath+"/publickey", appOfferDischargeMux)
    42  }
    43  
    44  func newOfferAuthcontext(pool *state.StatePool) (*crossmodel.AuthContext, error) {
    45  	// Create a bakery service for discharging third-party caveats for
    46  	// local offer access authentication. This service does not persist keys;
    47  	// its macaroons should be very short-lived.
    48  	st, err := pool.SystemState()
    49  	if err != nil {
    50  		return nil, errors.Trace(err)
    51  	}
    52  	location := "juju model " + st.ModelUUID()
    53  	checker := checkers.New(macaroon.MacaroonNamespace)
    54  
    55  	// Create a bakery service for local offer access authentication. This service
    56  	// persists keys into MongoDB in a TTL collection.
    57  	store, err := st.NewBakeryStorage()
    58  	if err != nil {
    59  		return nil, errors.Trace(err)
    60  	}
    61  	bakeryConfig := st.NewBakeryConfig()
    62  	key, err := bakeryConfig.GetOffersThirdPartyKey()
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  
    67  	controllerConfig, err := st.ControllerConfig()
    68  	if err != nil {
    69  		return nil, errors.Annotate(err, "unable to get controller config")
    70  	}
    71  	loginTokenRefreshURL := controllerConfig.LoginTokenRefreshURL()
    72  	if loginTokenRefreshURL != "" {
    73  		offerBakery, err := crossmodel.NewJaaSOfferBakery(
    74  			loginTokenRefreshURL, location, bakeryConfig, store, checker,
    75  		)
    76  		if err != nil {
    77  			return nil, errors.Trace(err)
    78  		}
    79  		return crossmodel.NewAuthContext(crossmodel.GetBackend(st), key, offerBakery)
    80  	}
    81  	offerBakery, err := crossmodel.NewLocalOfferBakery(location, bakeryConfig, store, checker)
    82  	if err != nil {
    83  		return nil, errors.Trace(err)
    84  	}
    85  	return crossmodel.NewAuthContext(crossmodel.GetBackend(st), key, offerBakery)
    86  }
    87  
    88  func (h *localOfferAuthHandler) checkThirdPartyCaveat(stdctx context.Context, req *http.Request, cavInfo *bakery.ThirdPartyCaveatInfo, _ *httpbakery.DischargeToken) ([]checkers.Caveat, error) {
    89  	logger.Debugf("check offer third party caveat %q", cavInfo.Condition)
    90  	details, err := h.authCtx.CheckOfferAccessCaveat(string(cavInfo.Condition))
    91  	if err != nil {
    92  		return nil, errors.Trace(err)
    93  	}
    94  
    95  	firstPartyCaveats, err := h.authCtx.CheckLocalAccessRequest(details)
    96  	if err != nil {
    97  		return nil, errors.Trace(err)
    98  	}
    99  	return firstPartyCaveats, nil
   100  }