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 }