diff --git a/internal/jimm/applicationoffer.go b/internal/jimm/applicationoffer.go index 6566422e2..678749f73 100644 --- a/internal/jimm/applicationoffer.go +++ b/internal/jimm/applicationoffer.go @@ -21,6 +21,7 @@ import ( "github.com/canonical/jimm/internal/errors" "github.com/canonical/jimm/internal/openfga" ofganames "github.com/canonical/jimm/internal/openfga/names" + jimmnames "github.com/canonical/jimm/pkg/names" ) // AddApplicationOfferParams holds parameters for the Offer method. @@ -202,7 +203,15 @@ func (j *JIMM) GetApplicationOfferConsumeDetails(ctx context.Context, user *open return errors.E(op, errors.CodeNotFound) } - api, err := j.dial(ctx, &offer.Model.Controller, names.ModelTag{}) + api, err := j.dial( + ctx, + &offer.Model.Controller, + names.ModelTag{}, + permission{ + resource: jimmnames.NewApplicationOfferTag(offer.UUID).String(), + relation: accessLevel, + }, + ) if err != nil { return errors.E(op, err) } @@ -312,7 +321,15 @@ func (j *JIMM) GetApplicationOffer(ctx context.Context, user *openfga.User, offe // controller. The all-watcher events do not include enough // information to reasonably keep the local database up-to-date, // and it would be non-trivial to make it do so. - api, err := j.dial(ctx, &offer.Model.Controller, names.ModelTag{}) + api, err := j.dial( + ctx, + &offer.Model.Controller, + names.ModelTag{}, + permission{ + resource: jimmnames.NewApplicationOfferTag(offer.UUID).String(), + relation: accessLevel, + }, + ) if err != nil { return nil, errors.E(op, err) } @@ -498,7 +515,15 @@ func (j *JIMM) UpdateApplicationOffer(ctx context.Context, controller *dbmodel.C return nil } - api, err := j.dial(ctx, controller, offer.Model.ResourceTag()) + api, err := j.dial( + ctx, + &offer.Model.Controller, + names.ModelTag{}, + permission{ + resource: jimmnames.NewApplicationOfferTag(offer.UUID).String(), + relation: "admin", + }, + ) if err != nil { return errors.E(op, err) } @@ -769,7 +794,16 @@ func (j *JIMM) doApplicationOfferAdmin(ctx context.Context, user *openfga.User, if !isOfferAdmin { return errors.E(op, errors.CodeUnauthorized, "unauthorized") } - api, err := j.dial(ctx, &offer.Model.Controller, names.ModelTag{}) + // add offer admin claim + api, err := j.dial( + ctx, + &offer.Model.Controller, + names.ModelTag{}, + permission{ + resource: jimmnames.NewApplicationOfferTag(offer.UUID).String(), + relation: "admin", + }, + ) if err != nil { return errors.E(op, err) } diff --git a/internal/jimm/jimm.go b/internal/jimm/jimm.go index ad4f5326b..445a84b9a 100644 --- a/internal/jimm/jimm.go +++ b/internal/jimm/jimm.go @@ -108,21 +108,34 @@ type Authenticator interface { Authenticate(ctx context.Context, req *jujuparams.LoginRequest) (*openfga.User, error) } +type permission struct { + resource string + relation string +} + // dial dials the controller and model specified by the given Controller // and ModelTag. If no Dialer has been configured then an error with a // code of CodeConnectionFailed will be returned. -func (j *JIMM) dial(ctx context.Context, ctl *dbmodel.Controller, modelTag names.ModelTag) (API, error) { +func (j *JIMM) dial(ctx context.Context, ctl *dbmodel.Controller, modelTag names.ModelTag, permissons ...permission) (API, error) { if j == nil || j.Dialer == nil { return nil, errors.E(errors.CodeConnectionFailed, "no dialer configured") } - return j.Dialer.Dial(ctx, ctl, modelTag, nil) + var permissionMap map[string]string + if len(permissons) > 0 { + permissionMap = make(map[string]string, len(permissons)) + for _, p := range permissons { + permissionMap[p.resource] = p.relation + } + } + + return j.Dialer.Dial(ctx, ctl, modelTag, permissionMap) } // A Dialer provides a connection to a controller. type Dialer interface { // Dial creates an API connection to a controller. If the given // model-tag is non-zero the connection will be to that model, - // otherwise the connection is to the controller. After sucessfully + // otherwise the connection is to the controller. After successfully // dialing the controller the UUID, AgentVersion and HostPorts fields // in the given controller should be updated to the values provided // by the controller. diff --git a/internal/jimm/model.go b/internal/jimm/model.go index ca0225ca0..2dbc8f6a9 100644 --- a/internal/jimm/model.go +++ b/internal/jimm/model.go @@ -428,7 +428,15 @@ func (b *modelBuilder) CreateControllerModel() *modelBuilder { return b } - api, err := b.jimm.dial(b.ctx, b.controller, names.ModelTag{}) + api, err := b.jimm.dial( + b.ctx, + b.controller, + names.ModelTag{}, + permission{ + resource: b.cloud.ResourceTag().String(), + relation: "add-model", + }, + ) if err != nil { b.err = errors.E(err) return b