Skip to content

Commit

Permalink
fix(permissionmap): fixes our permission map to actually run the redi…
Browse files Browse the repository at this point in the history
…al logic
  • Loading branch information
ale8k committed Dec 10, 2024
1 parent ad830a5 commit 914dc8f
Show file tree
Hide file tree
Showing 14 changed files with 127 additions and 96 deletions.
12 changes: 0 additions & 12 deletions internal/jimm/applicationoffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,6 @@ func (j *JIMM) GetApplicationOfferConsumeDetails(ctx context.Context, user *open
ctx,
&offer.Model.Controller,
names.ModelTag{},
permission{
resource: names.NewApplicationOfferTag(offer.UUID).String(),
relation: accessLevel,
},
)
if err != nil {
return errors.E(op, err)
Expand Down Expand Up @@ -366,10 +362,6 @@ func (j *JIMM) GetApplicationOffer(ctx context.Context, user *openfga.User, offe
ctx,
&offer.Model.Controller,
names.ModelTag{},
permission{
resource: names.NewApplicationOfferTag(offer.UUID).String(),
relation: accessLevel,
},
)
if err != nil {
return nil, errors.E(op, err)
Expand Down Expand Up @@ -706,10 +698,6 @@ func (j *JIMM) doApplicationOfferAdmin(ctx context.Context, user *openfga.User,
ctx,
&offer.Model.Controller,
names.ModelTag{},
permission{
resource: names.NewApplicationOfferTag(offer.UUID).String(),
relation: string(jujuparams.OfferAdminAccess),
},
)
if err != nil {
return errors.E(op, err)
Expand Down
21 changes: 6 additions & 15 deletions internal/jimm/jimm.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,27 +306,18 @@ func (j *JIMM) GetCredentialStore() credentials.CredentialStore {
return j.CredentialStore
}

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, permissons ...permission) (API, error) {
//
// It utilises a nil permission map to allow the controller to return
// the expected permissions back to the caller and then redials
// the controller with the correct permissions.
func (j *JIMM) dial(ctx context.Context, ctl *dbmodel.Controller, modelTag names.ModelTag) (API, error) {
if j == nil || j.Dialer == nil {
return nil, errors.E(errors.CodeConnectionFailed, "no dialer configured")
}
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)
return j.Dialer.Dial(ctx, ctl, modelTag, nil)
}

// A Dialer provides a connection to a controller.
Expand Down
5 changes: 0 additions & 5 deletions internal/jimm/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"strings"
"time"

jujupermission "github.com/juju/juju/core/permission"
jujuparams "github.com/juju/juju/rpc/params"
"github.com/juju/juju/state"
"github.com/juju/names/v5"
Expand Down Expand Up @@ -469,10 +468,6 @@ func (b *modelBuilder) CreateControllerModel() *modelBuilder {
b.ctx,
b.controller,
names.ModelTag{},
permission{
resource: b.cloud.ResourceTag().String(),
relation: string(jujupermission.AddModelAccess),
},
)
if err != nil {
b.err = errors.E(err)
Expand Down
6 changes: 3 additions & 3 deletions internal/jujuclient/allwatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
// WatchAllModels initialises a new AllModelWatcher. On success the watcher
// ID is returned. This uses the WatchAllModels method on the Controller
// facade.
func (c Connection) WatchAllModels(ctx context.Context) (string, error) {
func (c *Connection) WatchAllModels(ctx context.Context) (string, error) {
const op = errors.Op("jujuclient.WatchAllModels")
var resp jujuparams.SummaryWatcherID
if err := c.CallHighestFacadeVersion(ctx, "Controller", []int{11, 7}, "", "WatchAllModels", nil, &resp); err != nil {
Expand All @@ -26,7 +26,7 @@ func (c Connection) WatchAllModels(ctx context.Context) (string, error) {
// AllModelWatcherNext receives the next set of results from the all-model
// watcher with the given id. This uses the Next method on the
// AllModelWatcher facade.
func (c Connection) AllModelWatcherNext(ctx context.Context, id string) ([]jujuparams.Delta, error) {
func (c *Connection) AllModelWatcherNext(ctx context.Context, id string) ([]jujuparams.Delta, error) {
const op = errors.Op("jujuclient.AllModelWatcherNext")
var resp jujuparams.AllWatcherNextResults
if err := c.CallHighestFacadeVersion(ctx, "AllModelWatcher", []int{4, 2}, id, "Next", nil, &resp); err != nil {
Expand All @@ -37,7 +37,7 @@ func (c Connection) AllModelWatcherNext(ctx context.Context, id string) ([]jujup

// AllModelWatcherStop stops the all-model watcher with the given id. This
// uses the Stop method on the AllModelWatcher facade.
func (c Connection) AllModelWatcherStop(ctx context.Context, id string) error {
func (c *Connection) AllModelWatcherStop(ctx context.Context, id string) error {
const op = errors.Op("jujuclient.AllModelWatcherStop")
if err := c.CallHighestFacadeVersion(ctx, "AllModelWatcher", []int{4, 2}, id, "Stop", nil, nil); err != nil {
return errors.E(op, jujuerrors.Cause(err))
Expand Down
16 changes: 8 additions & 8 deletions internal/jujuclient/applicationoffers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

// Offer creates a new ApplicationOffer on the controller. Offer uses the
// Offer procedure on the ApplicationOffers facade.
func (c Connection) Offer(ctx context.Context, offerURL crossmodel.OfferURL, offer jujuparams.AddApplicationOffer) error {
func (c *Connection) Offer(ctx context.Context, offerURL crossmodel.OfferURL, offer jujuparams.AddApplicationOffer) error {
const op = errors.Op("jujuclient.Offer")
args := jujuparams.AddApplicationOffers{
Offers: []jujuparams.AddApplicationOffer{offer},
Expand Down Expand Up @@ -64,7 +64,7 @@ func (c Connection) Offer(ctx context.Context, offerURL crossmodel.OfferURL, off
// ListApplicationOffers lists ApplicationOffers on the controller matching
// the given filters. ListApplicationOffers uses the ListApplicationOffers
// procedure on the ApplicationOffers facade.
func (c Connection) ListApplicationOffers(ctx context.Context, filters []jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) {
func (c *Connection) ListApplicationOffers(ctx context.Context, filters []jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) {
const op = errors.Op("jujuclient.ListApplicationOffers")
args := jujuparams.OfferFilters{
Filters: filters,
Expand All @@ -81,7 +81,7 @@ func (c Connection) ListApplicationOffers(ctx context.Context, filters []jujupar
// FindApplicationOffers finds ApplicationOffers on the controller matching
// the given filters. FindApplicationOffers uses the FindApplicationOffers
// procedure on the ApplicationOffers facade.
func (c Connection) FindApplicationOffers(ctx context.Context, filters []jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) {
func (c *Connection) FindApplicationOffers(ctx context.Context, filters []jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) {
const op = errors.Op("jujuclient.FindApplicationOffers")
args := jujuparams.OfferFilters{
Filters: filters,
Expand All @@ -100,7 +100,7 @@ func (c Connection) FindApplicationOffers(ctx context.Context, filters []jujupar
// OfferURL the rest of the structure will be filled in by the API request.
// GetApplicationOffer uses the ApplicationOffers procedure on the
// ApplicationOffers facade.
func (c Connection) GetApplicationOffer(ctx context.Context, info *jujuparams.ApplicationOfferAdminDetailsV5) error {
func (c *Connection) GetApplicationOffer(ctx context.Context, info *jujuparams.ApplicationOfferAdminDetailsV5) error {
const op = errors.Op("jujuclient.GetApplicationOffer")
args := jujuparams.OfferURLs{
OfferURLs: []string{info.OfferURL},
Expand All @@ -123,7 +123,7 @@ func (c Connection) GetApplicationOffer(ctx context.Context, info *jujuparams.Ap
// GrantApplicationOfferAccess grants the specified permission to the
// given user on the given application offer. GrantApplicationOfferAccess
// uses the ModifyOfferAccess procedure on the ApplicationOffers facade..
func (c Connection) GrantApplicationOfferAccess(ctx context.Context, offerURL string, user names.UserTag, access jujuparams.OfferAccessPermission) error {
func (c *Connection) GrantApplicationOfferAccess(ctx context.Context, offerURL string, user names.UserTag, access jujuparams.OfferAccessPermission) error {
const op = errors.Op("jujuclient.GrantApplicationOfferAccess")
args := jujuparams.ModifyOfferAccessRequest{
Changes: []jujuparams.ModifyOfferAccess{{
Expand All @@ -150,7 +150,7 @@ func (c Connection) GrantApplicationOfferAccess(ctx context.Context, offerURL st
// RevokeApplicationOfferAccess revokes the specified permission from the
// given user on the given application offer. RevokeApplicationOfferAccess
// uses the ModifyOfferAccess procedure on the ApplicationOffers facade.
func (c Connection) RevokeApplicationOfferAccess(ctx context.Context, offerURL string, user names.UserTag, access jujuparams.OfferAccessPermission) error {
func (c *Connection) RevokeApplicationOfferAccess(ctx context.Context, offerURL string, user names.UserTag, access jujuparams.OfferAccessPermission) error {
const op = errors.Op("jujuclient.RevokeApplicationOfferAccess")
args := jujuparams.ModifyOfferAccessRequest{
Changes: []jujuparams.ModifyOfferAccess{{
Expand All @@ -177,7 +177,7 @@ func (c Connection) RevokeApplicationOfferAccess(ctx context.Context, offerURL s
// DestroyApplicationOffer destroys the given application offer.
// DestroyApplicationOffer uses the DestroyOffers procedure
// from the ApplicationOffers facade.
func (c Connection) DestroyApplicationOffer(ctx context.Context, offer string, force bool) error {
func (c *Connection) DestroyApplicationOffer(ctx context.Context, offer string, force bool) error {
const op = errors.Op("jujuclient.DestroyApplicationOffer")
args := jujuparams.DestroyApplicationOffers{
OfferURLs: []string{offer},
Expand All @@ -202,7 +202,7 @@ func (c Connection) DestroyApplicationOffer(ctx context.Context, offer string, f
// must include an Offer.OfferURL and the rest of the structure will be
// filled in by the API call. GetApplicationOfferConsumeDetails uses the
// GetConsumeDetails procedure on the ApplicationOffers facade.
func (c Connection) GetApplicationOfferConsumeDetails(ctx context.Context, user names.UserTag, info *jujuparams.ConsumeOfferDetails, v bakery.Version) error {
func (c *Connection) GetApplicationOfferConsumeDetails(ctx context.Context, user names.UserTag, info *jujuparams.ConsumeOfferDetails, v bakery.Version) error {
const op = errors.Op("jujuclient.GetApplicationOfferConsumeDetails")
args := jujuparams.ConsumeOfferDetailsArg{
OfferURLs: jujuparams.OfferURLs{
Expand Down
2 changes: 1 addition & 1 deletion internal/jujuclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

// Status returns the status of the juju model.
func (c Connection) Status(ctx context.Context, patterns []string) (*jujuparams.FullStatus, error) {
func (c *Connection) Status(ctx context.Context, patterns []string) (*jujuparams.FullStatus, error) {
const op = errors.Op("jujuclient.Status")

p := jujuparams.StatusParams{
Expand Down
24 changes: 12 additions & 12 deletions internal/jujuclient/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
// SupportsCheckCredentialModels reports whether the controller supports
// the Cloud.CheckCredentialsModels, Cloud.RevokeCredentialsCheckModels,
// and Cloud.UpdateCredentialsCheckModels methods.
func (c Connection) SupportsCheckCredentialModels() bool {
func (c *Connection) SupportsCheckCredentialModels() bool {
return c.hasFacadeVersion("Cloud", 3) || c.hasFacadeVersion("Cloud", 7)
}

Expand All @@ -26,7 +26,7 @@ func (c Connection) SupportsCheckCredentialModels() bool {
// credential. This method uses the CheckCredentialsModel procedure on
// the Cloud. Any error that represents a Juju API
// failure will be of type *APIError.
func (c Connection) CheckCredentialModels(ctx context.Context, cred jujuparams.TaggedCredential) ([]jujuparams.UpdateCredentialModelResult, error) {
func (c *Connection) CheckCredentialModels(ctx context.Context, cred jujuparams.TaggedCredential) ([]jujuparams.UpdateCredentialModelResult, error) {
const op = errors.Op("jujuclient.CheckCredentialModels")
in := jujuparams.TaggedCredentials{
Credentials: []jujuparams.TaggedCredential{cred},
Expand Down Expand Up @@ -57,7 +57,7 @@ func (c Connection) CheckCredentialModels(ctx context.Context, cred jujuparams.T
//
// Any error that represents a Juju API failure will be of type
// *APIError.
func (c Connection) UpdateCredential(ctx context.Context, cred jujuparams.TaggedCredential) ([]jujuparams.UpdateCredentialModelResult, error) {
func (c *Connection) UpdateCredential(ctx context.Context, cred jujuparams.TaggedCredential) ([]jujuparams.UpdateCredentialModelResult, error) {
const op = errors.Op("jujuclient.UpdateCredential")
creds := jujuparams.TaggedCredentials{
Credentials: []jujuparams.TaggedCredential{cred},
Expand Down Expand Up @@ -98,7 +98,7 @@ func (c Connection) UpdateCredential(ctx context.Context, cred jujuparams.Tagged
//
// Any error that represents a Juju API failure will be of type
// *APIError.
func (c Connection) RevokeCredential(ctx context.Context, cred names.CloudCredentialTag) error {
func (c *Connection) RevokeCredential(ctx context.Context, cred names.CloudCredentialTag) error {
const op = errors.Op("jujuclient.RevokeCredential")
out := jujuparams.ErrorResults{
Results: make([]jujuparams.ErrorResult, 1),
Expand Down Expand Up @@ -131,7 +131,7 @@ func (c Connection) RevokeCredential(ctx context.Context, cred names.CloudCreden

// Cloud retrieves information about the given cloud. Cloud uses the
// Cloud procedure on the Cloud facade.
func (c Connection) Cloud(ctx context.Context, tag names.CloudTag, cloud *jujuparams.Cloud) error {
func (c *Connection) Cloud(ctx context.Context, tag names.CloudTag, cloud *jujuparams.Cloud) error {
const op = errors.Op("jujuclient.Cloud")
args := jujuparams.Entities{
Entities: []jujuparams.Entity{{
Expand All @@ -154,7 +154,7 @@ func (c Connection) Cloud(ctx context.Context, tag names.CloudTag, cloud *jujupa

// Clouds retrieves information about all available clouds. Clouds uses the
// Clouds procedure on the Cloud facade.
func (c Connection) Clouds(ctx context.Context) (map[names.CloudTag]jujuparams.Cloud, error) {
func (c *Connection) Clouds(ctx context.Context) (map[names.CloudTag]jujuparams.Cloud, error) {
const op = errors.Op("jujuclient.Clouds")
var resp jujuparams.CloudsResult
if err := c.CallHighestFacadeVersion(ctx, "Cloud", []int{7, 1}, "", "Clouds", nil, &resp); err != nil {
Expand All @@ -175,7 +175,7 @@ func (c Connection) Clouds(ctx context.Context) (map[names.CloudTag]jujuparams.C

// AddCloud adds the given cloud to a controller with the given name.
// AddCloud uses the AddCloud procedure on the Cloud facade.
func (c Connection) AddCloud(ctx context.Context, tag names.CloudTag, cloud jujuparams.Cloud, force bool) error {
func (c *Connection) AddCloud(ctx context.Context, tag names.CloudTag, cloud jujuparams.Cloud, force bool) error {
const op = errors.Op("jujuclient.AddCloud")
args := jujuparams.AddCloudArgs{
Cloud: cloud,
Expand All @@ -190,7 +190,7 @@ func (c Connection) AddCloud(ctx context.Context, tag names.CloudTag, cloud juju

// RemoveCloud removes the given cloud from the controller. RemoveCloud
// uses the RemoveClouds procedure on the Cloud facade.
func (c Connection) RemoveCloud(ctx context.Context, tag names.CloudTag) error {
func (c *Connection) RemoveCloud(ctx context.Context, tag names.CloudTag) error {
const op = errors.Op("jujuclient.RemoveCloud")
args := jujuparams.Entities{
Entities: []jujuparams.Entity{{
Expand All @@ -212,7 +212,7 @@ func (c Connection) RemoveCloud(ctx context.Context, tag names.CloudTag) error {
// GrantCloudAccess gives the given user the given access level on the
// given cloud. GrantCloudAccess uses the ModifyCloudAccess procedure on
// the Cloud facade.
func (c Connection) GrantCloudAccess(ctx context.Context, cloudTag names.CloudTag, userTag names.UserTag, access string) error {
func (c *Connection) GrantCloudAccess(ctx context.Context, cloudTag names.CloudTag, userTag names.UserTag, access string) error {
const op = errors.Op("jujuclient.GrantCloudAccess")
args := jujuparams.ModifyCloudAccessRequest{
Changes: []jujuparams.ModifyCloudAccess{{
Expand All @@ -239,7 +239,7 @@ func (c Connection) GrantCloudAccess(ctx context.Context, cloudTag names.CloudTa
// RevokeCloudAccess revokes the given access level on the given cloud from
// the given user. RevokeCloudAccess uses the ModifyCloudAccess procedure
// on the Cloud facade.
func (c Connection) RevokeCloudAccess(ctx context.Context, cloudTag names.CloudTag, userTag names.UserTag, access string) error {
func (c *Connection) RevokeCloudAccess(ctx context.Context, cloudTag names.CloudTag, userTag names.UserTag, access string) error {
const op = errors.Op("jujuclient.RevokeCloudAccess")
args := jujuparams.ModifyCloudAccessRequest{
Changes: []jujuparams.ModifyCloudAccess{{
Expand All @@ -265,7 +265,7 @@ func (c Connection) RevokeCloudAccess(ctx context.Context, cloudTag names.CloudT

// CloudInfo retrieves information about the cloud with the given name.
// CloudInfo uses the CloudInfo procedure on the Cloud facade.
func (c Connection) CloudInfo(ctx context.Context, tag names.CloudTag, ci *jujuparams.CloudInfo) error {
func (c *Connection) CloudInfo(ctx context.Context, tag names.CloudTag, ci *jujuparams.CloudInfo) error {
const op = errors.Op("jujuclient.CloudInfo")
args := jujuparams.Entities{
Entities: []jujuparams.Entity{{Tag: tag.String()}},
Expand All @@ -288,7 +288,7 @@ func (c Connection) CloudInfo(ctx context.Context, tag names.CloudTag, ci *jujup

// UpdateCloud updates the given cloud with the given cloud definition.
// UpdateCloud uses the UpdateCloud procedure on the cloud facade.
func (c Connection) UpdateCloud(ctx context.Context, tag names.CloudTag, cloud jujuparams.Cloud) error {
func (c *Connection) UpdateCloud(ctx context.Context, tag names.CloudTag, cloud jujuparams.Cloud) error {
const op = errors.Op("jujuclient.UpdateCloud")

args := jujuparams.UpdateCloudArgs{
Expand Down
39 changes: 32 additions & 7 deletions internal/jujuclient/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/go-macaroon-bakery/macaroon-bakery/v3/httpbakery"
jujuhttp "github.com/juju/http/v2"
"github.com/juju/juju/api/base"
"github.com/juju/juju/core/permission"
jujuparams "github.com/juju/juju/rpc/params"
"github.com/juju/names/v5"
"github.com/juju/zaputil/zapctx"
Expand Down Expand Up @@ -54,15 +55,13 @@ type Dialer struct {
JWTService *jimmjwx.JWTService
}

// createLoginRequest formats a login request for Juju utilising a JWT.
func (d *Dialer) createLoginRequest(ctx context.Context, ctl *dbmodel.Controller, modelTag names.ModelTag, p map[string]string) (*jujuparams.LoginRequest, error) {
// JIMM is automatically given all required permissions
permissions := p
if permissions == nil {
permissions = make(map[string]string)
}
permissions[ctl.ResourceTag().String()] = "superuser"
if modelTag.Id() != "" {
permissions[modelTag.String()] = "admin"

permissions, err := d.maybeCorrectPermissionMap(p, ctl)
if err != nil {
return nil, err
}

jwt, err := d.JWTService.NewJWT(ctx, jimmjwx.JWTParams{
Expand All @@ -82,6 +81,30 @@ func (d *Dialer) createLoginRequest(ctx context.Context, ctl *dbmodel.Controller
}, nil
}

func (d *Dialer) maybeCorrectPermissionMap(permissions map[string]string, ctl *dbmodel.Controller) (map[string]string, error) {
if permissions == nil {
permissions = make(map[string]string)
}

// Check the access level for the controller.
access, ok := permissions[ctl.ResourceTag().String()]

// If a permission cannot be found set a required default.
if !ok {
// Minimum required permission to connect to a controller
// is login.
permissions[ctl.ResourceTag().String()] = string(permission.LoginAccess)
} else {
// If the permission was set ahead of time, we ensure it's a valid controller
// permission.
if err := permission.ValidateControllerAccess(permission.Access(access)); err != nil {
return nil, errors.E(err)
}
}

return permissions, nil
}

// Dial implements jimm.Dialer.
func (d *Dialer) Dial(ctx context.Context, ctl *dbmodel.Controller, modelTag names.ModelTag, requiredPermissions map[string]string) (jimm.API, error) {
const op = errors.Op("jujuclient.Dial")
Expand Down Expand Up @@ -126,6 +149,7 @@ func (d *Dialer) Dial(ctx context.Context, ctl *dbmodel.Controller, modelTag nam

monitorC := make(chan struct{})
broken := new(uint32)

go pinger(client, ct.Id(), monitorC, broken)
return &Connection{
ctx: ctx,
Expand Down Expand Up @@ -237,6 +261,7 @@ func (c *Connection) redial(ctx context.Context, requiredPermissions map[string]
if err = c.Close(); err != nil {
return errors.E(op, err)
}

conn := api.(*Connection)
c.client = conn.client
c.userTag = conn.userTag
Expand Down
Loading

0 comments on commit 914dc8f

Please sign in to comment.