diff --git a/domain/access/state/permission.go b/domain/access/state/permission.go index 683829f1c0f..a83b0e26435 100644 --- a/domain/access/state/permission.go +++ b/domain/access/state/permission.go @@ -7,7 +7,6 @@ import ( "context" "database/sql" "fmt" - "strings" "github.com/canonical/sqlair" "github.com/juju/collections/transform" @@ -348,25 +347,51 @@ func (st *PermissionState) ReadAllAccessForUserAndObjectType(ctx context.Context } var ( permissions []dbReadUserPermission - actualUser dbPermissionUser + actualUser []dbPermissionUser ) + var andClause string + switch objectType { + case corepermission.Controller: + andClause = `AND p.grant_on = ctrl.c` + case corepermission.Model: + andClause = `AND m.uuid NOT NULL` + case corepermission.Cloud: + andClause = `AND c.name NOT NULL` + case corepermission.Offer: + // TODO implement for offers + return nil, errors.NotImplementedf("ReadAllAccessForUserAndObjectType for offers") + default: + return nil, errors.NotValidf("object type %q", objectType) + } + readQuery := fmt.Sprintf(` +WITH ctrl AS (SELECT 'controller' AS c) +SELECT (p.uuid, p.grant_on, p.grant_to, p.access_type) AS (&dbReadUserPermission.*), + (u.uuid, u.name, u.display_name, u.created_at, u.disabled) AS (&dbPermissionUser.*), + creator.name AS &dbPermissionUser.created_by_name +FROM v_user_auth u + LEFT JOIN user AS creator ON u.created_by_uuid = creator.uuid + JOIN v_permission p ON u.uuid = p.grant_to + LEFT JOIN cloud c ON p.grant_on = c.name + LEFT JOIN model_list m on p.grant_on = m.uuid + LEFT JOIN ctrl ON p.grant_on = ctrl.c +WHERE u.name = $M.name +AND u.disabled = false +AND u.removed = false +%s +`, andClause) + + readStmt, err := st.Prepare(readQuery, dbReadUserPermission{}, dbPermissionUser{}, sqlair.M{}) + if err != nil { + return nil, errors.Trace(err) + } + err = db.Txn(ctx, func(ctx context.Context, tx *sqlair.TX) error { - // Get user uuid. - // Get all grantOn values possible for the given object type. - // Read all permissions with the union of the 2 values. - actualUser, err = st.findUserByName(ctx, tx, subject) - if err != nil { - return errors.Trace(err) - } - ids, err := st.allGrantOnForObjectType(ctx, tx, objectType) - if err != nil { - return errors.Trace(err) + err = tx.Query(ctx, readStmt, sqlair.M{"name": subject}).GetAll(&permissions, &actualUser) + if err != nil && errors.Is(err, sql.ErrNoRows) { + return errors.Annotatef(accesserrors.PermissionNotFound, "for %q on %q", subject, objectType) + } else if err != nil { + return errors.Annotatef(err, "getting permissions for %q on %q", subject, objectType) } - permissions, err = st.allPermissionsForUserAndType(ctx, tx, actualUser.UUID, ids) - if err != nil { - return errors.Trace(err) - } - return nil }) if err != nil { @@ -376,11 +401,10 @@ func (st *PermissionState) ReadAllAccessForUserAndObjectType(ctx context.Context userAccess := make([]corepermission.UserAccess, len(permissions)) for i, p := range permissions { p.ObjectType = string(objectType) - userAccess[i] = p.toUserAccess(actualUser) + userAccess[i] = p.toUserAccess(actualUser[i]) } return userAccess, nil - } // findUserByName finds the user provided exists, hasn't been removed and is not @@ -493,11 +517,11 @@ func objectAccessID( // Use access_type_id and object_type_id to validate row from permission_object_access objectAccessIDExists := ` SELECT at.id AS &M.access_type_id -FROM permission_object_access oa - LEFT JOIN permission_object_type ot ON ot.TYPE = $M.object_type - LEFT JOIN permission_access_type at ON at.TYPE = $M.access_type -WHERE oa.access_type_id = at.id - AND oa.object_type_id = ot.id +FROM permission_access_type at + INNER JOIN permission_object_access oa ON oa.access_type_id = at.id + INNER JOIN permission_object_type ot ON ot.id = oa.object_type_id +WHERE ot.type = $M.object_type +AND at.type = $M.access_type ` // Validate the access type is allowed for the target type. @@ -689,82 +713,3 @@ WHERE grant_on = $M.grant_on } return nil, errors.Annotatef(accesserrors.PermissionNotFound, "for %q", grantOn) } - -// allGrantOnForObjectType returns the grant_on values for -// a given object type. -func (st *PermissionState) allGrantOnForObjectType( - ctx context.Context, - tx *sqlair.TX, - objectType corepermission.ObjectType, -) ([]string, error) { - - var allGrantOnForType string - switch objectType { - case corepermission.Controller: - return []string{coredatabase.ControllerNS}, nil - case corepermission.Model: - allGrantOnForType = ` -SELECT uuid AS &ids.grant_on -FROM model_list -` - case corepermission.Cloud: - allGrantOnForType = ` -SELECT name AS &ids.grant_on -FROM cloud -` - case corepermission.Offer: - // TODO implement for offers - default: - return nil, errors.NotValidf("object type %q", objectType) - } - - type ids struct { - GrantOn string `db:"grant_on"` - } - allAccessTypeIDsForObjectTypeStmt, err := st.Prepare(allGrantOnForType, ids{}) - if err != nil { - return nil, errors.Trace(err) - } - - var result = []ids{} - err = tx.Query(ctx, allAccessTypeIDsForObjectTypeStmt).GetAll(&result) - if err != nil && errors.Is(err, sql.ErrNoRows) { - return nil, errors.Annotatef(err, "mismatch in %q", objectType) - } else if err != nil { - return nil, errors.Annotatef(err, "getting grant on values for %q", objectType) - } - results := make([]string, len(result)) - for i, value := range result { - results[i] = value.GrantOn - } - return results, nil -} - -// allPermissionsForUserAndType returns dbReadUserPermission for all -// grant_on in the list and the given user. -func (st *PermissionState) allPermissionsForUserAndType( - ctx context.Context, - tx *sqlair.TX, - grantTo string, - idsForType []string, -) ([]dbReadUserPermission, error) { - allAccessTypeIDsForObjectType := ` -SELECT (uuid, grant_on, grant_to, access_type) AS (&dbReadUserPermission.*) -FROM v_permission -WHERE grant_to = $M.grant_to AND grant_on IN ($S[:]) -` - permissionTypeIDsSlice := sqlair.S(transform.Slice(idsForType, func(s string) any { return any(s) })) - allPermissionsForUserAndTypeStmt, err := st.Prepare(allAccessTypeIDsForObjectType, sqlair.M{}, sqlair.S{}, dbReadUserPermission{}) - if err != nil { - return nil, errors.Trace(err) - } - - var result = []dbReadUserPermission{} - err = tx.Query(ctx, allPermissionsForUserAndTypeStmt, permissionTypeIDsSlice, sqlair.M{"grant_to": grantTo}).GetAll(&result) - if err != nil && errors.Is(err, sql.ErrNoRows) { - return nil, errors.Annotatef(accesserrors.PermissionNotFound, "for %q on %q", grantTo, strings.Join(idsForType, ", ")) - } else if err != nil { - return nil, errors.Annotatef(err, "getting permissions for %q on %q", grantTo, idsForType) - } - return result, nil -} diff --git a/domain/access/state/permission_test.go b/domain/access/state/permission_test.go index a25d9ea10c0..155a8ee237c 100644 --- a/domain/access/state/permission_test.go +++ b/domain/access/state/permission_test.go @@ -10,10 +10,10 @@ import ( "github.com/juju/names/v5" jc "github.com/juju/testing/checkers" - "github.com/juju/utils/v4" gc "gopkg.in/check.v1" corepermission "github.com/juju/juju/core/permission" + "github.com/juju/juju/core/user" accesserrors "github.com/juju/juju/domain/access/errors" schematesting "github.com/juju/juju/domain/schema/testing" "github.com/juju/juju/internal/uuid" @@ -22,18 +22,25 @@ import ( type permissionStateSuite struct { schematesting.ControllerSuite + + debug bool } var _ = gc.Suite(&permissionStateSuite{}) -func (s *permissionStateSuite) TestCreatePermissionModel(c *gc.C) { - st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - - // Setup to add permissions for user bob on the model - s.ensureModel(c, "model-uuid") +func (s *permissionStateSuite) SetUpTest(c *gc.C) { + s.ControllerSuite.SetUpTest(c) s.ensureUser(c, "42", "admin", "42") // model owner s.ensureUser(c, "123", "bob", "42") + s.ensureUser(c, "456", "sue", "42") s.ensureCloud(c, "987", "test-cloud") + s.ensureCloud(c, "654", "another-cloud") + s.ensureModel(c, "default-model") + s.ensureModel(c, "model-uuid") +} + +func (s *permissionStateSuite) TestCreatePermissionModel(c *gc.C) { + st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) userAccess, err := st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ User: "bob", @@ -61,12 +68,6 @@ func (s *permissionStateSuite) TestCreatePermissionModel(c *gc.C) { func (s *permissionStateSuite) TestCreatePermissionCloud(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureModel(c, "model-uuid") - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - s.ensureCloud(c, "987", "test-cloud") - userAccess, err := st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ User: "bob", AccessSpec: corepermission.AccessSpec{ @@ -93,10 +94,6 @@ func (s *permissionStateSuite) TestCreatePermissionCloud(c *gc.C) { func (s *permissionStateSuite) TestCreatePermissionController(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - userAccess, err := st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ User: "bob", AccessSpec: corepermission.AccessSpec{ @@ -123,10 +120,6 @@ func (s *permissionStateSuite) TestCreatePermissionController(c *gc.C) { func (s *permissionStateSuite) TestCreatePermissionForModelWithBadInfo(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user Bob on the model - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - _, err := st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ User: "bob", AccessSpec: corepermission.AccessSpec{ @@ -143,10 +136,6 @@ func (s *permissionStateSuite) TestCreatePermissionForModelWithBadInfo(c *gc.C) func (s *permissionStateSuite) TestCreatePermissionForControllerWithBadInfo(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user Bob on the model - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - _, err := st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ User: "bob", AccessSpec: corepermission.AccessSpec{ @@ -175,7 +164,7 @@ WHERE type = ? // Find the permission row := db.QueryRow(` -SELECT uuid, permission_type_id, grant_to, grant_on +SELECT uuid, permission_type_id, grant_to, grant_on FROM permission `) c.Assert(row.Err(), jc.ErrorIsNil) @@ -196,7 +185,7 @@ FROM permission func (s *permissionStateSuite) TestCreatePermissionErrorNoUser(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) _, err := st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ - User: "bob", + User: "testme", AccessSpec: corepermission.AccessSpec{ Target: corepermission.ID{ Key: "model-uuid", @@ -211,11 +200,6 @@ func (s *permissionStateSuite) TestCreatePermissionErrorNoUser(c *gc.C) { func (s *permissionStateSuite) TestCreatePermissionErrorDuplicate(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureModel(c, "model-uuid") - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - spec := corepermission.UserAccessSpec{ User: "bob", AccessSpec: corepermission.AccessSpec{ @@ -262,11 +246,6 @@ WHERE permission_type_id = 1 func (s *permissionStateSuite) TestDeletePermission(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureModel(c, "model-uuid") - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - target := corepermission.ID{ Key: "model-uuid", ObjectType: corepermission.Model, @@ -299,18 +278,13 @@ func (s *permissionStateSuite) TestDeletePermissionFailUserNotFound(c *gc.C) { Key: "model-uuid", ObjectType: corepermission.Model, } - err := st.DeletePermission(context.Background(), "bob", target) + err := st.DeletePermission(context.Background(), "testme", target) c.Assert(err, jc.ErrorIs, accesserrors.UserNotFound) } func (s *permissionStateSuite) TestDeletePermissionDoesNotExist(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureModel(c, "model-uuid") - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - target := corepermission.ID{ Key: "model-uuid", ObjectType: corepermission.Model, @@ -324,10 +298,6 @@ func (s *permissionStateSuite) TestDeletePermissionDoesNotExist(c *gc.C) { func (s *permissionStateSuite) TestReadUserAccessForTarget(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - target := corepermission.ID{ Key: "controller", ObjectType: corepermission.Controller, @@ -365,11 +335,6 @@ WHERE grant_to = 123 func (s *permissionStateSuite) TestReadUserAccessLevelForTarget(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - s.ensureCloud(c, "987", "test-cloud") - target := corepermission.ID{ Key: "test-cloud", ObjectType: corepermission.Cloud, @@ -392,17 +357,15 @@ func (s *permissionStateSuite) TestReadUserAccessLevelForTarget(c *gc.C) { func (s *permissionStateSuite) TestReadAllUserAccessForUser(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - modelUUID := utils.MustNewUUID().String() - _ = s.twoUsersACloudAndAModel(c, st, modelUUID) + s.setupForRead(c, st) userAccesses, err := st.ReadAllUserAccessForUser(context.Background(), "bob") c.Assert(err, jc.ErrorIsNil) - c.Assert(userAccesses, gc.HasLen, 2) + c.Assert(userAccesses, gc.HasLen, 4) for _, access := range userAccesses { c.Assert(access.UserName, gc.Equals, "bob") c.Assert(access.CreatedBy.Id(), gc.Equals, "admin") - } accessOne := userAccesses[0] c.Assert(accessOne.Access, gc.Equals, corepermission.AddModelAccess) @@ -411,9 +374,11 @@ func (s *permissionStateSuite) TestReadAllUserAccessForUser(c *gc.C) { func (s *permissionStateSuite) TestReadAllUserAccessForTarget(c *gc.C) { st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - modelUUID := utils.MustNewUUID().String() - targetCloud := s.twoUsersACloudAndAModel(c, st, modelUUID) - + s.setupForRead(c, st) + targetCloud := corepermission.ID{ + Key: "test-cloud", + ObjectType: corepermission.Cloud, + } userAccesses, err := st.ReadAllUserAccessForTarget(context.Background(), targetCloud) c.Assert(err, jc.ErrorIsNil) @@ -428,43 +393,14 @@ func (s *permissionStateSuite) TestReadAllUserAccessForTarget(c *gc.C) { c.Check(accessZero.UserID, gc.Not(gc.Equals), accessOne.UserID) } -func (s *permissionStateSuite) TestReadAllAccessForUserAndObjectType(c *gc.C) { - st := NewState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) +func (s *permissionStateSuite) TestReadAllAccessForUserAndObjectTypeCloud(c *gc.C) { + st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - s.ensureCloud(c, "987", "test-cloud") - s.ensureCloud(c, "654", "another-cloud") - - target := corepermission.ID{ - Key: "test-cloud", - ObjectType: corepermission.Cloud, - } - _, err := st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ - User: "bob", - AccessSpec: corepermission.AccessSpec{ - Target: target, - Access: corepermission.AddModelAccess, - }, - }) - c.Assert(err, jc.ErrorIsNil) - anotherTarget := corepermission.ID{ - Key: "another-cloud", - ObjectType: corepermission.Cloud, - } - _, err = st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ - User: "bob", - AccessSpec: corepermission.AccessSpec{ - Target: anotherTarget, - Access: corepermission.AddModelAccess, - }, - }) - c.Assert(err, jc.ErrorIsNil) + s.setupForRead(c, st) users, err := st.ReadAllAccessForUserAndObjectType(context.Background(), "bob", corepermission.Cloud) c.Assert(err, jc.ErrorIsNil) - c.Check(users, gc.HasLen, 2) + c.Assert(users, gc.HasLen, 2) var foundTestCloud, foundAnotherCloud bool for _, userAccess := range users { @@ -480,31 +416,117 @@ func (s *permissionStateSuite) TestReadAllAccessForUserAndObjectType(c *gc.C) { foundAnotherCloud = true } } + c.Check(foundTestCloud && foundAnotherCloud, jc.IsTrue, gc.Commentf("%+v", users)) +} + +func (s *permissionStateSuite) TestReadAllAccessForUserAndObjectTypeModel(c *gc.C) { + st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) + + s.setupForRead(c, st) + + users, err := st.ReadAllAccessForUserAndObjectType(context.Background(), "bob", corepermission.Model) + c.Assert(err, jc.ErrorIsNil) + c.Assert(users, gc.HasLen, 2) + + var admin, write bool + for _, userAccess := range users { + c.Check(userAccess.UserTag.Id(), gc.Equals, "bob") + c.Check(userAccess.UserName, gc.Equals, "bob") + c.Check(userAccess.CreatedBy.Id(), gc.Equals, "admin") + c.Check(userAccess.UserID, gc.Equals, "123") + if userAccess.Access == corepermission.WriteAccess { + write = true + c.Check(userAccess.Object.Id(), gc.Equals, "default-model") + } + if userAccess.Access == corepermission.AdminAccess { + admin = true + c.Check(userAccess.Object.Id(), gc.Equals, "model-uuid") + } + } + c.Assert(admin && write, jc.IsTrue, gc.Commentf("%+v", users)) +} + +func (s *permissionStateSuite) TestReadAllAccessForUserAndObjectTypeController(c *gc.C) { + st := NewPermissionState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) + + s.setupForRead(c, st) + + users, err := st.ReadAllAccessForUserAndObjectType(context.Background(), "admin", corepermission.Controller) + c.Assert(err, jc.ErrorIsNil) + c.Assert(users, gc.HasLen, 1) + userAccess := users[0] + c.Check(userAccess.UserTag.Id(), gc.Equals, "admin", gc.Commentf("%+v", users)) + c.Check(userAccess.UserName, gc.Equals, "admin", gc.Commentf("%+v", users)) + c.Check(userAccess.CreatedBy.Id(), gc.Equals, "admin", gc.Commentf("%+v", users)) + c.Check(userAccess.UserID, gc.Equals, "42", gc.Commentf("%+v", users)) + c.Check(userAccess.Access, gc.Equals, corepermission.SuperuserAccess, gc.Commentf("%+v", users)) +} + +func (s *permissionStateSuite) printPermissions(c *gc.C) { + rows, _ := s.DB().Query(` +SELECT uuid, permission_type_id, grant_to, grant_on +FROM permission +--WHERE grant_to = "bob" +`) + defer func() { _ = rows.Close() }() + var ( + userUuid, grantTo, grantOn string + permissionTypeId int + ) + + for rows.Next() { + err := rows.Scan(&userUuid, &permissionTypeId, &grantTo, &grantOn) + c.Assert(err, jc.ErrorIsNil) + c.Logf("%q, %d, %q, %q", userUuid, permissionTypeId, grantTo, grantOn) + } + +} + +func (s *permissionStateSuite) printUsers(c *gc.C) { + rows, _ := s.DB().Query(` +SELECT uuid, name, created_by_uuid +FROM user +`) + defer func() { _ = rows.Close() }() + var ( + rowUUID, name string + creatorUUID user.UUID + ) + + for rows.Next() { + err := rows.Scan(&rowUUID, &name, &creatorUUID) + c.Assert(err, jc.ErrorIsNil) + c.Logf("%q, %q, %q", rowUUID, name, creatorUUID) + } + +} + +func (s *permissionStateSuite) printClouds(c *gc.C) { + rows, _ := s.DB().Query(` +SELECT uuid, name +FROM cloud +`) + defer func() { _ = rows.Close() }() + var ( + rowUUID, name string + ) + + for rows.Next() { + err := rows.Scan(&rowUUID, &name) + c.Assert(err, jc.ErrorIsNil) + c.Logf("%q, %q", rowUUID, name) + } - c.Check(foundTestCloud && foundAnotherCloud, jc.IsTrue) } func (s *permissionStateSuite) TestReadAllAccessForUserAndObjectTypeNotFound(c *gc.C) { st := NewState(s.TxnRunnerFactory(), jujutesting.NewCheckLogger(c)) - // Setup to add permissions for user bob on the model - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "123", "bob", "42") - s.ensureCloud(c, "987", "test-cloud") - s.ensureCloud(c, "654", "another-cloud") - _, err := st.ReadAllAccessForUserAndObjectType(context.Background(), "bob", corepermission.Cloud) c.Assert(err, jc.ErrorIs, accesserrors.PermissionNotFound) } -func (s *permissionStateSuite) twoUsersACloudAndAModel(c *gc.C, st *PermissionState, modelUUID string) corepermission.ID { - // Setup to add permissions for user bob and sue on the model and a cloud - s.ensureUser(c, "42", "admin", "42") // model owner - s.ensureUser(c, "456", "sue", "42") - s.ensureUser(c, "123", "bob", "42") - s.ensureCloud(c, "987", "test-cloud") - s.ensureModel(c, modelUUID) - +func (s *permissionStateSuite) setupForRead(c *gc.C, st *PermissionState) { targetCloud := corepermission.ID{ Key: "test-cloud", ObjectType: corepermission.Cloud, @@ -525,27 +547,71 @@ func (s *permissionStateSuite) twoUsersACloudAndAModel(c *gc.C, st *PermissionSt }, }) c.Assert(err, jc.ErrorIsNil) - targetModel := corepermission.ID{ - Key: modelUUID, - ObjectType: corepermission.Model, - } _, err = st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ User: "bob", AccessSpec: corepermission.AccessSpec{ - Target: targetModel, + Target: corepermission.ID{ + Key: "model-uuid", + ObjectType: corepermission.Model, + }, Access: corepermission.AdminAccess, }, }) c.Assert(err, jc.ErrorIsNil) - return targetCloud + _, err = st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ + User: "bob", + AccessSpec: corepermission.AccessSpec{ + Target: corepermission.ID{ + Key: "another-cloud", + ObjectType: corepermission.Cloud, + }, + Access: corepermission.AddModelAccess, + }, + }) + c.Assert(err, jc.ErrorIsNil) + _, err = st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ + User: "bob", + AccessSpec: corepermission.AccessSpec{ + Target: corepermission.ID{ + Key: "default-model", + ObjectType: corepermission.Model, + }, + Access: corepermission.WriteAccess, + }, + }) + c.Assert(err, jc.ErrorIsNil) + _, err = st.CreatePermission(context.Background(), uuid.MustNewUUID(), corepermission.UserAccessSpec{ + User: "admin", + AccessSpec: corepermission.AccessSpec{ + Target: corepermission.ID{ + Key: "controller", + ObjectType: corepermission.Controller, + }, + Access: corepermission.SuperuserAccess, + }, + }) + c.Assert(err, jc.ErrorIsNil) + if s.debug { + s.printUsers(c) + s.printClouds(c) + s.printPermissions(c) + } } -func (s *permissionStateSuite) ensureUser(c *gc.C, uuid, name, createdByUUID string) { +func (s *permissionStateSuite) ensureUser(c *gc.C, userUUID, name, createdByUUID string) { err := s.TxnRunner().StdTxn(context.Background(), func(ctx context.Context, tx *sql.Tx) error { _, err := tx.ExecContext(ctx, ` INSERT INTO user (uuid, name, display_name, removed, created_by_uuid, created_at) VALUES (?, ?, ?, ?, ?, ?) - `, uuid, name, name, false, createdByUUID, time.Now()) + `, userUUID, name, name, false, createdByUUID, time.Now()) + return err + }) + c.Assert(err, jc.ErrorIsNil) + err = s.TxnRunner().StdTxn(context.Background(), func(ctx context.Context, tx *sql.Tx) error { + _, err := tx.ExecContext(ctx, ` + INSERT INTO user_authentication ( user_uuid, disabled) + VALUES (?, ?) + `, userUUID, false) return err }) c.Assert(err, jc.ErrorIsNil)