diff --git a/cmd/jimmctl/cmd/addcloudtocontroller_test.go b/cmd/jimmctl/cmd/addcloudtocontroller_test.go index c347cda4d..e99d92393 100644 --- a/cmd/jimmctl/cmd/addcloudtocontroller_test.go +++ b/cmd/jimmctl/cmd/addcloudtocontroller_test.go @@ -31,9 +31,9 @@ func (s *addCloudToControllerSuite) SetUpTest(c *gc.C) { s.jimmSuite.SetUpTest(c) // We add user bob, who is a JIMM administrator. - err := s.JIMM.Database.UpdateUser(context.Background(), &dbmodel.User{ + err := s.JIMM.Database.UpdateIdentity(context.Background(), &dbmodel.Identity{ DisplayName: "Bob", - Username: "bob@external", + Name: "bob@external", }) c.Assert(err, gc.IsNil) @@ -51,8 +51,8 @@ func (s *addCloudToControllerSuite) SetUpTest(c *gc.C) { // We grant user bob administrator access to JIMM and the added // test-cloud. bob := openfga.NewUser( - &dbmodel.User{ - Username: "bob@external", + &dbmodel.Identity{ + Name: "bob@external", }, s.JIMM.OpenFGAClient, ) diff --git a/cmd/jimmctl/cmd/importcloudcredentials_test.go b/cmd/jimmctl/cmd/importcloudcredentials_test.go index da7793fc4..69407397e 100644 --- a/cmd/jimmctl/cmd/importcloudcredentials_test.go +++ b/cmd/jimmctl/cmd/importcloudcredentials_test.go @@ -67,27 +67,27 @@ func (s *importCloudCredentialsSuite) TestImportCloudCredentials(c *gc.C) { c.Assert(err, gc.IsNil) cred1 := dbmodel.CloudCredential{ - CloudName: "aws", - OwnerUsername: "alice@external", - Name: "test1", + CloudName: "aws", + OwnerIdentityName: "alice@external", + Name: "test1", } err = s.JIMM.Database.GetCloudCredential(context.Background(), &cred1) c.Assert(err, gc.IsNil) c.Check(cred1.AuthType, gc.Equals, "access-key") cred2 := dbmodel.CloudCredential{ - CloudName: "aws", - OwnerUsername: "bob@external", - Name: "test1", + CloudName: "aws", + OwnerIdentityName: "bob@external", + Name: "test1", } err = s.JIMM.Database.GetCloudCredential(context.Background(), &cred2) c.Assert(err, gc.IsNil) c.Check(cred2.AuthType, gc.Equals, "access-key") cred3 := dbmodel.CloudCredential{ - CloudName: "gce", - OwnerUsername: "charlie@external", - Name: "test1", + CloudName: "gce", + OwnerIdentityName: "charlie@external", + Name: "test1", } err = s.JIMM.Database.GetCloudCredential(context.Background(), &cred3) c.Assert(err, gc.IsNil) diff --git a/cmd/jimmctl/cmd/importmodel_test.go b/cmd/jimmctl/cmd/importmodel_test.go index 81222ab5d..fcea71caa 100644 --- a/cmd/jimmctl/cmd/importmodel_test.go +++ b/cmd/jimmctl/cmd/importmodel_test.go @@ -50,7 +50,7 @@ func (s *importModelSuite) TestImportModelSuperuser(c *gc.C) { model2.SetTag(names.NewModelTag(m.ModelUUID())) err = s.JIMM.Database.GetModel(context.Background(), &model2) c.Assert(err, gc.Equals, nil) - c.Check(model2.OwnerUsername, gc.Equals, "charlie@external") + c.Check(model2.OwnerIdentityName, gc.Equals, "charlie@external") } func (s *importModelSuite) TestImportModelFromLocalUser(c *gc.C) { @@ -78,7 +78,7 @@ func (s *importModelSuite) TestImportModelFromLocalUser(c *gc.C) { err = s.JIMM.Database.GetModel(context.Background(), &model2) c.Assert(err, gc.Equals, nil) c.Check(model2.CreatedAt.After(model.CreatedAt), gc.Equals, true) - c.Check(model2.OwnerUsername, gc.Equals, "alice@external") + c.Check(model2.OwnerIdentityName, gc.Equals, "alice@external") } func (s *importModelSuite) TestImportModelUnauthorized(c *gc.C) { diff --git a/cmd/jimmctl/cmd/jimmsuite_test.go b/cmd/jimmctl/cmd/jimmsuite_test.go index f3fc0c813..9fc1c5b0a 100644 --- a/cmd/jimmctl/cmd/jimmsuite_test.go +++ b/cmd/jimmctl/cmd/jimmsuite_test.go @@ -39,7 +39,7 @@ type jimmSuite struct { Params service.Params HTTP *httptest.Server Service *service.Service - AdminUser *dbmodel.User + AdminUser *dbmodel.Identity ClientStore func() *jjclient.MemStore JIMM *jimm.JIMM cancel context.CancelFunc @@ -103,11 +103,11 @@ func (s *jimmSuite) SetUpTest(c *gc.C) { s.ControllerAdmins = []string{"controller-admin"} s.JujuConnSuite.SetUpTest(c) - s.AdminUser = &dbmodel.User{ - Username: "alice@external", + s.AdminUser = &dbmodel.Identity{ + Name: "alice@external", LastLogin: db.Now(), } - err = s.JIMM.Database.GetUser(ctx, s.AdminUser) + err = s.JIMM.Database.GetIdentity(ctx, s.AdminUser) c.Assert(err, gc.Equals, nil) alice := openfga.NewUser(s.AdminUser, ofgaClient) @@ -183,12 +183,12 @@ func (s *jimmSuite) userBakeryClient(username string) *httpbakery.Client { func (s *jimmSuite) AddController(c *gc.C, name string, info *api.Info) { ctl := &dbmodel.Controller{ - UUID: info.ControllerUUID, - Name: name, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - CACertificate: info.CACert, - Addresses: nil, + UUID: info.ControllerUUID, + Name: name, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + CACertificate: info.CACert, + Addresses: nil, } ctl.Addresses = make(dbmodel.HostPorts, 0, len(info.Addrs)) for _, addr := range info.Addrs { @@ -207,11 +207,11 @@ func (s *jimmSuite) AddController(c *gc.C, name string, info *api.Info) { func (s *jimmSuite) UpdateCloudCredential(c *gc.C, tag names.CloudCredentialTag, cred jujuparams.CloudCredential) { ctx := context.Background() - u := dbmodel.User{ - Username: tag.Owner().Id(), + u := dbmodel.Identity{ + Name: tag.Owner().Id(), } user := openfga.NewUser(&u, s.JIMM.OpenFGAClient) - err := s.JIMM.Database.GetUser(ctx, &u) + err := s.JIMM.Database.GetIdentity(ctx, &u) c.Assert(err, gc.Equals, nil) _, err = s.JIMM.UpdateCloudCredential(ctx, user, jimm.UpdateCloudCredentialArgs{ CredentialTag: tag, @@ -224,12 +224,12 @@ func (s *jimmSuite) UpdateCloudCredential(c *gc.C, tag names.CloudCredentialTag, func (s *jimmSuite) AddModel(c *gc.C, owner names.UserTag, name string, cloud names.CloudTag, region string, cred names.CloudCredentialTag) names.ModelTag { ctx := context.Background() u := openfga.NewUser( - &dbmodel.User{ - Username: owner.Id(), + &dbmodel.Identity{ + Name: owner.Id(), }, s.OFGAClient, ) - err := s.JIMM.Database.GetUser(ctx, u.User) + err := s.JIMM.Database.GetIdentity(ctx, u.Identity) c.Assert(err, gc.Equals, nil) mi, err := s.JIMM.AddModel(ctx, u, &jimm.ModelCreateArgs{ Name: name, diff --git a/cmd/jimmctl/cmd/purge_logs_test.go b/cmd/jimmctl/cmd/purge_logs_test.go index d41425ca4..49268816a 100644 --- a/cmd/jimmctl/cmd/purge_logs_test.go +++ b/cmd/jimmctl/cmd/purge_logs_test.go @@ -59,16 +59,16 @@ func (s *purgeLogsSuite) TestPurgeLogsFromDb(c *gc.C) { ctx := context.Background() relativeNow := time.Now().AddDate(-1, 0, 0) ale := dbmodel.AuditLogEntry{ - Time: relativeNow.UTC().Round(time.Millisecond), - UserTag: names.NewUserTag("alice@external").String(), + Time: relativeNow.UTC().Round(time.Millisecond), + IdentityTag: names.NewUserTag("alice@external").String(), } ale_past := dbmodel.AuditLogEntry{ - Time: relativeNow.AddDate(0, 0, -1).UTC().Round(time.Millisecond), - UserTag: names.NewUserTag("alice@external").String(), + Time: relativeNow.AddDate(0, 0, -1).UTC().Round(time.Millisecond), + IdentityTag: names.NewUserTag("alice@external").String(), } ale_future := dbmodel.AuditLogEntry{ - Time: relativeNow.AddDate(0, 0, 5).UTC().Round(time.Millisecond), - UserTag: names.NewUserTag("alice@external").String(), + Time: relativeNow.AddDate(0, 0, 5).UTC().Round(time.Millisecond), + IdentityTag: names.NewUserTag("alice@external").String(), } err := s.JIMM.Database.Migrate(context.Background(), false) diff --git a/cmd/jimmctl/cmd/relation_test.go b/cmd/jimmctl/cmd/relation_test.go index 1ace9b81f..be79fe3a0 100644 --- a/cmd/jimmctl/cmd/relation_test.go +++ b/cmd/jimmctl/cmd/relation_test.go @@ -255,7 +255,7 @@ func (s *relationSuite) TestRemoveRelation(c *gc.C) { } type environment struct { - users []dbmodel.User + users []dbmodel.Identity clouds []dbmodel.Cloud credentials []dbmodel.CloudCredential controllers []dbmodel.Controller @@ -263,15 +263,15 @@ type environment struct { applicationOffers []dbmodel.ApplicationOffer } -func initializeEnvironment(c *gc.C, ctx context.Context, db *db.Database, u dbmodel.User) *environment { +func initializeEnvironment(c *gc.C, ctx context.Context, db *db.Database, u dbmodel.Identity) *environment { env := environment{} - u1 := dbmodel.User{ - Username: "eve@external", + u1 := dbmodel.Identity{ + Name: "eve@external", } c.Assert(db.DB.Create(&u1).Error, gc.IsNil) - env.users = []dbmodel.User{u, u1} + env.users = []dbmodel.Identity{u, u1} cloud := dbmodel.Cloud{ Name: "test-cloud", @@ -300,10 +300,10 @@ func initializeEnvironment(c *gc.C, ctx context.Context, db *db.Database, u dbmo env.controllers = []dbmodel.Controller{controller} cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, gc.Equals, nil) @@ -315,7 +315,7 @@ func initializeEnvironment(c *gc.C, ctx context.Context, db *db.Database, u dbmo String: "acdbf3e5-67e1-42a2-a2dc-64505265c030", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -327,7 +327,7 @@ func initializeEnvironment(c *gc.C, ctx context.Context, db *db.Database, u dbmo offer := dbmodel.ApplicationOffer{ ID: 1, UUID: "436b2264-d8f8-4e24-b16f-dd43c4116528", - URL: env.controllers[0].Name + ":" + env.models[0].OwnerUsername + "/" + env.models[0].Name + ".testoffer1", + URL: env.controllers[0].Name + ":" + env.models[0].OwnerIdentityName + "/" + env.models[0].Name + ".testoffer1", Name: "testoffer1", ModelID: model.ID, Model: model, @@ -353,11 +353,11 @@ func (s *relationSuite) TestListRelations(c *gc.C) { } relations := []apiparams.RelationshipTuple{{ - Object: "user-" + env.users[0].Username, + Object: "user-" + env.users[0].Name, Relation: "member", TargetObject: "group-group-1", }, { - Object: "user-" + env.users[1].Username, + Object: "user-" + env.users[1].Name, Relation: "member", TargetObject: "group-group-2", }, { @@ -371,11 +371,11 @@ func (s *relationSuite) TestListRelations(c *gc.C) { }, { Object: "group-group-1#member", Relation: "administrator", - TargetObject: "model-" + env.controllers[0].Name + ":" + env.models[0].OwnerUsername + "/" + env.models[0].Name, + TargetObject: "model-" + env.controllers[0].Name + ":" + env.models[0].OwnerIdentityName + "/" + env.models[0].Name, }, { - Object: "user-" + env.users[1].Username, + Object: "user-" + env.users[1].Name, Relation: "administrator", - TargetObject: "applicationoffer-" + env.controllers[0].Name + ":" + env.applicationOffers[0].Model.OwnerUsername + "/" + env.applicationOffers[0].Model.Name + "." + env.applicationOffers[0].Name, + TargetObject: "applicationoffer-" + env.controllers[0].Name + ":" + env.applicationOffers[0].Model.OwnerIdentityName + "/" + env.applicationOffers[0].Model.Name + "." + env.applicationOffers[0].Name, }} for _, relation := range relations { _, err := cmdtesting.RunCommand(c, cmd.NewAddRelationCommandForTesting(s.ClientStore(), bClient), relation.Object, relation.Relation, relation.TargetObject) @@ -448,8 +448,8 @@ func (s *relationSuite) TestCheckRelationViaSuperuser(c *gc.C) { err = db.GetGroup(ctx, &group) c.Assert(err, gc.IsNil) - u := dbmodel.User{ - Username: petname.Generate(2, "-") + "@external", + u := dbmodel.Identity{ + Name: petname.Generate(2, "-") + "@external", } c.Assert(db.DB.Create(&u).Error, gc.IsNil) @@ -476,10 +476,10 @@ func (s *relationSuite) TestCheckRelationViaSuperuser(c *gc.C) { c.Assert(err, gc.IsNil) cred := dbmodel.CloudCredential{ - Name: petname.Generate(2, "-"), - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: petname.Generate(2, "-"), + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, gc.IsNil) @@ -490,7 +490,7 @@ func (s *relationSuite) TestCheckRelationViaSuperuser(c *gc.C) { String: id.String(), Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -522,8 +522,8 @@ func (s *relationSuite) TestCheckRelationViaSuperuser(c *gc.C) { c.Assert(err, gc.IsNil) // Test reader is OK - userToCheck := "user-" + u.Username - modelToCheck := "model-" + controller.Name + ":" + u.Username + "/" + model.Name + userToCheck := "user-" + u.Name + modelToCheck := "model-" + controller.Name + ":" + u.Name + "/" + model.Name cmdCtx, err := cmdtesting.RunCommand( c, cmd.NewCheckRelationCommandForTesting(s.ClientStore(), bClient), diff --git a/discharger.go b/discharger.go index de5de78ca..e3ce30274 100644 --- a/discharger.go +++ b/discharger.go @@ -106,8 +106,8 @@ func (md *macaroonDischarger) checkThirdPartyCaveat(ctx context.Context, req *ht offerTag := jimmnames.NewApplicationOfferTag(offerUUID) user := openfga.NewUser( - &dbmodel.User{ - Username: userTag.Id(), + &dbmodel.Identity{ + Name: userTag.Id(), }, md.ofgaClient, ) @@ -124,6 +124,6 @@ func (md *macaroonDischarger) checkThirdPartyCaveat(ctx context.Context, req *ht checkers.TimeBeforeCaveat(time.Now().Add(defaultDischargeExpiry)), }, nil } - zapctx.Debug(ctx, "macaroon dishcharge denied", zap.String("user", user.Username), zap.String("offer", offerUUID)) + zapctx.Debug(ctx, "macaroon dishcharge denied", zap.String("user", user.Name), zap.String("offer", offerUUID)) return nil, httpbakery.ErrPermissionDenied } diff --git a/internal/auth/jujuauth.go b/internal/auth/jujuauth.go index e58f201ba..97e0fac60 100644 --- a/internal/auth/jujuauth.go +++ b/internal/auth/jujuauth.go @@ -71,8 +71,8 @@ func (a JujuAuthenticator) Authenticate(ctx context.Context, req *jujuparams.Log if ut.IsLocal() { ut = ut.WithDomain("external") } - u := &dbmodel.User{ - Username: ut.Id(), + u := &dbmodel.Identity{ + Name: ut.Id(), DisplayName: ut.Name(), } // Note: Previously here we would grant a user superuser permission if they were part of diff --git a/internal/auth/jujuauth_test.go b/internal/auth/jujuauth_test.go index 98abdc701..73620ce50 100644 --- a/internal/auth/jujuauth_test.go +++ b/internal/auth/jujuauth_test.go @@ -59,8 +59,8 @@ func TestAuthenticateLogin(t *testing.T) { c.Assert(err, qt.IsNil) c.Check(u.LastLogin.Valid, qt.Equals, false) u.LastLogin = sql.NullTime{} - c.Check(u.User, qt.DeepEquals, &dbmodel.User{ - Username: "alice@external", + c.Check(u.Identity, qt.DeepEquals, &dbmodel.Identity{ + Name: "alice@external", DisplayName: "alice", }) } @@ -102,8 +102,8 @@ func TestAuthenticateLoginWithDomain(t *testing.T) { c.Assert(err, qt.IsNil) c.Check(u.LastLogin.Valid, qt.Equals, false) u.LastLogin = sql.NullTime{} - c.Check(u.User, qt.DeepEquals, &dbmodel.User{ - Username: "alice@mydomain", + c.Check(u.Identity, qt.DeepEquals, &dbmodel.Identity{ + Name: "alice@mydomain", DisplayName: "alice", }) } @@ -146,8 +146,8 @@ func TestAuthenticateLoginSuperuser(t *testing.T) { c.Assert(err, qt.IsNil) c.Check(u.LastLogin.Valid, qt.Equals, false) u.LastLogin = sql.NullTime{} - c.Check(u.User, qt.DeepEquals, &dbmodel.User{ - Username: "bob@external", + c.Check(u.Identity, qt.DeepEquals, &dbmodel.Identity{ + Name: "bob@external", DisplayName: "bob", }) } diff --git a/internal/db/applicationoffer_test.go b/internal/db/applicationoffer_test.go index d70603a0d..752c30dd6 100644 --- a/internal/db/applicationoffer_test.go +++ b/internal/db/applicationoffer_test.go @@ -27,7 +27,7 @@ func TestAddApplicationOfferUnconfiguredDatabase(t *testing.T) { } type testEnvironment struct { - u dbmodel.User + u dbmodel.Identity cloud dbmodel.Cloud cred dbmodel.CloudCredential controller dbmodel.Controller @@ -40,8 +40,8 @@ func initTestEnvironment(c *qt.C, db *db.Database) testEnvironment { env := testEnvironment{} - env.u = dbmodel.User{ - Username: "bob@external", + env.u = dbmodel.Identity{ + Name: "bob@external", } c.Assert(db.DB.Create(&env.u).Error, qt.IsNil) @@ -241,8 +241,8 @@ func (s *dbSuite) TestFindApplicationOffers(c *qt.C) { err := s.Database.AddApplicationOffer(context.Background(), &offer1) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) diff --git a/internal/db/audit.go b/internal/db/audit.go index 27a0b3413..92a037a16 100644 --- a/internal/db/audit.go +++ b/internal/db/audit.go @@ -36,9 +36,9 @@ type AuditLogFilter struct { // found. End time.Time - // UserTag defines the user-tag on the audit log entry to match, if - // this is empty all user-tags are matched. - UserTag string + // IdentityTag defines the identity-tag on the audit log entry to match, if + // this is empty all identity-tags are matched. + IdentityTag string // Model is used to filter the event log to only contain events that // were performed against a specific model. @@ -77,8 +77,8 @@ func (d *Database) ForEachAuditLogEntry(ctx context.Context, filter AuditLogFilt if !filter.End.IsZero() { db = db.Where("time <= ?", filter.End) } - if filter.UserTag != "" { - db = db.Where("user_tag = ?", filter.UserTag) + if filter.IdentityTag != "" { + db = db.Where("identity_tag = ?", filter.IdentityTag) } if filter.Model != "" { db = db.Where("model = ?", filter.Model) diff --git a/internal/db/auditlog_test.go b/internal/db/auditlog_test.go index bbcfa60e4..55d2606c4 100644 --- a/internal/db/auditlog_test.go +++ b/internal/db/auditlog_test.go @@ -28,8 +28,8 @@ func (s *dbSuite) TestAddAuditLogEntry(c *qt.C) { ctx := context.Background() ale := dbmodel.AuditLogEntry{ - Time: time.Now().UTC().Round(time.Millisecond), - UserTag: names.NewUserTag("alice@external").String(), + Time: time.Now().UTC().Round(time.Millisecond), + IdentityTag: names.NewUserTag("alice@external").String(), } err := s.Database.AddAuditLogEntry(ctx, &ale) @@ -63,17 +63,17 @@ func TestForEachAuditLogEntryUnconfiguredDatabase(t *testing.T) { } var testAuditLogEntries = []dbmodel.AuditLogEntry{{ - Time: time.Date(2020, time.February, 20, 20, 2, 20, 0, time.UTC), - UserTag: names.NewUserTag("alice@external").String(), + Time: time.Date(2020, time.February, 20, 20, 2, 20, 0, time.UTC), + IdentityTag: names.NewUserTag("alice@external").String(), }, { - Time: time.Date(2020, time.February, 20, 20, 2, 21, 0, time.UTC), - UserTag: names.NewUserTag("alice@external").String(), + Time: time.Date(2020, time.February, 20, 20, 2, 21, 0, time.UTC), + IdentityTag: names.NewUserTag("alice@external").String(), }, { - Time: time.Date(2020, time.February, 20, 20, 2, 21, 0, time.UTC), - UserTag: names.NewUserTag("bob@external").String(), + Time: time.Date(2020, time.February, 20, 20, 2, 21, 0, time.UTC), + IdentityTag: names.NewUserTag("bob@external").String(), }, { - Time: time.Date(2020, time.February, 20, 20, 2, 23, 0, time.UTC), - UserTag: names.NewUserTag("alice@external").String(), + Time: time.Date(2020, time.February, 20, 20, 2, 23, 0, time.UTC), + IdentityTag: names.NewUserTag("alice@external").String(), }} var forEachAuditLogEntryTests = []struct { @@ -106,7 +106,7 @@ var forEachAuditLogEntryTests = []struct { }, { name: "UserTagFilter", filter: db.AuditLogFilter{ - UserTag: names.NewUserTag("alice@external").String(), + IdentityTag: names.NewUserTag("alice@external").String(), }, expectEntries: []int{0, 1, 3}, }} @@ -209,16 +209,16 @@ func (s *dbSuite) TestPurgeLogsFromDb(c *qt.C) { ctx := context.Background() relativeNow := time.Now().AddDate(-1, 0, 0) ale := dbmodel.AuditLogEntry{ - Time: relativeNow.UTC().Round(time.Millisecond), - UserTag: names.NewUserTag("alice@external").String(), + Time: relativeNow.UTC().Round(time.Millisecond), + IdentityTag: names.NewUserTag("alice@external").String(), } ale_past := dbmodel.AuditLogEntry{ - Time: relativeNow.AddDate(0, 0, -1).UTC().Round(time.Millisecond), - UserTag: names.NewUserTag("alice@external").String(), + Time: relativeNow.AddDate(0, 0, -1).UTC().Round(time.Millisecond), + IdentityTag: names.NewUserTag("alice@external").String(), } ale_future := dbmodel.AuditLogEntry{ - Time: relativeNow.AddDate(0, 0, 5).UTC().Round(time.Millisecond), - UserTag: names.NewUserTag("alice@external").String(), + Time: relativeNow.AddDate(0, 0, 5).UTC().Round(time.Millisecond), + IdentityTag: names.NewUserTag("alice@external").String(), } err := s.Database.Migrate(context.Background(), false) diff --git a/internal/db/cloudcredential.go b/internal/db/cloudcredential.go index bb04f4b02..bd5aa0794 100644 --- a/internal/db/cloudcredential.go +++ b/internal/db/cloudcredential.go @@ -19,15 +19,15 @@ func (d *Database) SetCloudCredential(ctx context.Context, cred *dbmodel.CloudCr return errors.E(op, err) } - if cred.CloudName == "" || cred.OwnerUsername == "" || cred.Name == "" { - return errors.E(op, errors.CodeBadRequest, fmt.Sprintf("invalid cloudcredential tag %q", cred.CloudName+"/"+cred.OwnerUsername+"/"+cred.Name)) + if cred.CloudName == "" || cred.OwnerIdentityName == "" || cred.Name == "" { + return errors.E(op, errors.CodeBadRequest, fmt.Sprintf("invalid cloudcredential tag %q", cred.CloudName+"/"+cred.OwnerIdentityName+"/"+cred.Name)) } db := d.DB.WithContext(ctx) if err := db.Clauses(clause.OnConflict{ Columns: []clause.Column{ {Name: "cloud_name"}, - {Name: "owner_username"}, + {Name: "owner_identity_name"}, {Name: "name"}, }, DoUpdates: clause.AssignmentColumns([]string{"auth_type", "label", "attributes_in_vault", "attributes", "valid"}), @@ -44,16 +44,16 @@ func (d *Database) GetCloudCredential(ctx context.Context, cred *dbmodel.CloudCr if err := d.ready(); err != nil { return errors.E(op, err) } - if cred.CloudName == "" || cred.OwnerUsername == "" || cred.Name == "" { - return errors.E(op, errors.CodeNotFound, fmt.Sprintf("cloudcredential %q not found", cred.CloudName+"/"+cred.OwnerUsername+"/"+cred.Name)) + if cred.CloudName == "" || cred.OwnerIdentityName == "" || cred.Name == "" { + return errors.E(op, errors.CodeNotFound, fmt.Sprintf("cloudcredential %q not found", cred.CloudName+"/"+cred.OwnerIdentityName+"/"+cred.Name)) } db := d.DB.WithContext(ctx) db = db.Preload("Cloud") db = db.Preload("Models") - if err := db.Where("cloud_name = ? AND owner_username = ? AND name = ?", cred.CloudName, cred.OwnerUsername, cred.Name).First(&cred).Error; err != nil { + if err := db.Where("cloud_name = ? AND owner_identity_name = ? AND name = ?", cred.CloudName, cred.OwnerIdentityName, cred.Name).First(&cred).Error; err != nil { err := dbError(err) if errors.ErrorCode(err) == errors.CodeNotFound { - return errors.E(op, errors.CodeNotFound, fmt.Sprintf("cloudcredential %q not found", cred.CloudName+"/"+cred.OwnerUsername+"/"+cred.Name), err) + return errors.E(op, errors.CodeNotFound, fmt.Sprintf("cloudcredential %q not found", cred.CloudName+"/"+cred.OwnerIdentityName+"/"+cred.Name), err) } return errors.E(op, err) } @@ -61,10 +61,10 @@ func (d *Database) GetCloudCredential(ctx context.Context, cred *dbmodel.CloudCr } // ForEachCloudCredential iterates through all cloud credentials owned by -// the given user calling the given function with each one. If cloud is +// the given identity calling the given function with each one. If cloud is // specified then the cloud-credentials are filtered to only return // credentials for that cloud. -func (d *Database) ForEachCloudCredential(ctx context.Context, username, cloud string, f func(*dbmodel.CloudCredential) error) error { +func (d *Database) ForEachCloudCredential(ctx context.Context, identityName, cloud string, f func(*dbmodel.CloudCredential) error) error { const op = errors.Op("db.ForEachCloudCredential") if err := d.ready(); err != nil { @@ -74,9 +74,9 @@ func (d *Database) ForEachCloudCredential(ctx context.Context, username, cloud s db := d.DB.WithContext(ctx) mdb := db.Model(dbmodel.CloudCredential{}) if cloud == "" { - mdb = mdb.Where("owner_username = ?", username) + mdb = mdb.Where("owner_identity_name = ?", identityName) } else { - mdb = mdb.Where("cloud_name = ? AND owner_username = ?", cloud, username) + mdb = mdb.Where("cloud_name = ? AND owner_identity_name = ?", cloud, identityName) } rows, err := mdb.Rows() if err != nil { diff --git a/internal/db/cloudcredential_test.go b/internal/db/cloudcredential_test.go index 98434a335..0ad8f039e 100644 --- a/internal/db/cloudcredential_test.go +++ b/internal/db/cloudcredential_test.go @@ -30,8 +30,8 @@ func (s *dbSuite) TestSetCloudCredentialInvalidTag(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -45,12 +45,12 @@ func (s *dbSuite) TestSetCloudCredentialInvalidTag(c *qt.C) { c.Assert(s.Database.DB.Create(&cloud).Error, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-cred", - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-cred", + OwnerIdentityName: u.Name, + AuthType: "empty", } err = s.Database.SetCloudCredential(context.Background(), &cred) - c.Check(err, qt.ErrorMatches, fmt.Sprintf("invalid cloudcredential tag %q", cred.CloudName+"/"+cred.OwnerUsername+"/"+cred.Name)) + c.Check(err, qt.ErrorMatches, fmt.Sprintf("invalid cloudcredential tag %q", cred.CloudName+"/"+cred.OwnerIdentityName+"/"+cred.Name)) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeBadRequest) } @@ -58,8 +58,8 @@ func (s *dbSuite) TestSetCloudCredential(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -73,17 +73,17 @@ func (s *dbSuite) TestSetCloudCredential(c *qt.C) { c.Assert(s.Database.DB.Create(&cloud).Error, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-cred", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-cred", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } c1 := cred err = s.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) var dbCred dbmodel.CloudCredential - result := s.Database.DB.Where("cloud_name = ? AND owner_username = ? AND name = ?", cloud.Name, u.Username, cred.Name).First(&dbCred) + result := s.Database.DB.Where("cloud_name = ? AND owner_identity_name = ? AND name = ?", cloud.Name, u.Name, cred.Name).First(&dbCred) c.Assert(result.Error, qt.Equals, nil) c.Assert(dbCred, qt.DeepEquals, cred) @@ -95,8 +95,8 @@ func (s *dbSuite) TestSetCloudCredentialUpdate(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -110,10 +110,10 @@ func (s *dbSuite) TestSetCloudCredentialUpdate(c *qt.C) { c.Assert(s.Database.DB.Create(&cloud).Error, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-cred", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-cred", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = s.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -135,9 +135,9 @@ func (s *dbSuite) TestSetCloudCredentialUpdate(c *qt.C) { c.Assert(err, qt.Equals, nil) dbCred := dbmodel.CloudCredential{ - CloudName: cloud.Name, - OwnerUsername: u.Username, - Name: cred.Name, + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + Name: cred.Name, } err = s.Database.GetCloudCredential(context.Background(), &dbCred) c.Assert(err, qt.Equals, nil) @@ -165,8 +165,8 @@ func (s *dbSuite) TestGetCloudCredential(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -180,10 +180,10 @@ func (s *dbSuite) TestGetCloudCredential(c *qt.C) { c.Assert(s.Database.DB.Create(&cloud).Error, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-cred", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-cred", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } cred.Cloud.Regions = nil err = s.Database.SetCloudCredential(context.Background(), &cred) @@ -193,9 +193,9 @@ func (s *dbSuite) TestGetCloudCredential(c *qt.C) { cred.Cloud.Regions = nil dbCred := dbmodel.CloudCredential{ - CloudName: cloud.Name, - OwnerUsername: u.Username, - Name: cred.Name, + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + Name: cred.Name, } err = s.Database.GetCloudCredential(context.Background(), &dbCred) c.Assert(err, qt.Equals, nil) diff --git a/internal/db/clouddefaults.go b/internal/db/clouddefaults.go index eb31bf026..49c6d3e30 100644 --- a/internal/db/clouddefaults.go +++ b/internal/db/clouddefaults.go @@ -20,8 +20,8 @@ func (d *Database) SetCloudDefaults(ctx context.Context, defaults *dbmodel.Cloud db := d.DB.WithContext(ctx) dbDefaults := dbmodel.CloudDefaults{ - Username: defaults.Username, - CloudID: defaults.CloudID, + IdentityName: defaults.IdentityName, + CloudID: defaults.CloudID, Cloud: dbmodel.Cloud{ Name: defaults.Cloud.Name, }, @@ -46,7 +46,7 @@ func (d *Database) SetCloudDefaults(ctx context.Context, defaults *dbmodel.Cloud } if err := db.Clauses(clause.OnConflict{ Columns: []clause.Column{ - {Name: "username"}, + {Name: "identity_name"}, {Name: "cloud_id"}, {Name: "region"}, }, @@ -70,8 +70,8 @@ func (d *Database) UnsetCloudDefaults(ctx context.Context, defaults *dbmodel.Clo db := d.DB.WithContext(ctx) dbDefaults := dbmodel.CloudDefaults{ - Username: defaults.Username, - CloudID: defaults.CloudID, + IdentityName: defaults.IdentityName, + CloudID: defaults.CloudID, Cloud: dbmodel.Cloud{ Name: defaults.Cloud.Name, }, @@ -89,7 +89,7 @@ func (d *Database) UnsetCloudDefaults(ctx context.Context, defaults *dbmodel.Clo } if err := db.Clauses(clause.OnConflict{ Columns: []clause.Column{ - {Name: "username"}, + {Name: "identity_name"}, {Name: "cloud_id"}, {Name: "region"}, }, @@ -114,7 +114,7 @@ func (d *Database) CloudDefaults(ctx context.Context, defaults *dbmodel.CloudDef } db := d.DB.WithContext(ctx) - db = db.Where("username = ?", defaults.Username) + db = db.Where("identity_name = ?", defaults.IdentityName) db = db.Joins("JOIN clouds ON clouds.id = cloud_defaults.cloud_id") if defaults.CloudID != 0 { db = db.Where("clouds.id = ?", defaults.CloudID) @@ -123,7 +123,7 @@ func (d *Database) CloudDefaults(ctx context.Context, defaults *dbmodel.CloudDef } db = db.Where("region = ?", defaults.Region) - result := db.Preload("User").Preload("Cloud").First(&defaults) + result := db.Preload("Identity").Preload("Cloud").First(&defaults) if result.Error != nil { err := dbError(result.Error) if errors.ErrorCode(err) == errors.CodeNotFound { @@ -135,7 +135,7 @@ func (d *Database) CloudDefaults(ctx context.Context, defaults *dbmodel.CloudDef } // ModelDefaultsForCloud returns the default config values for the specified cloud. -func (d *Database) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.User, cloud names.CloudTag) ([]dbmodel.CloudDefaults, error) { +func (d *Database) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloud names.CloudTag) ([]dbmodel.CloudDefaults, error) { const op = errors.Op("db.ModelDefaultsForCloud") if err := d.ready(); err != nil { @@ -143,12 +143,12 @@ func (d *Database) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.User } db := d.DB.WithContext(ctx) - db = db.Where("username = ?", user.Username) + db = db.Where("identity_name = ?", user.Name) db = db.Joins("JOIN clouds ON clouds.id = cloud_defaults.cloud_id") db = db.Where("clouds.name = ?", cloud.Id()) var defaults []dbmodel.CloudDefaults - result := db.Preload("User").Preload("Cloud").Find(&defaults) + result := db.Preload("Identity").Preload("Cloud").Find(&defaults) if result.Error != nil { return nil, errors.E(op, dbError(result.Error)) } diff --git a/internal/db/clouddefaults_test.go b/internal/db/clouddefaults_test.go index eac5058ee..dfbbb9b2c 100644 --- a/internal/db/clouddefaults_test.go +++ b/internal/db/clouddefaults_test.go @@ -22,8 +22,8 @@ func (s *dbSuite) TestModelDefaults(c *qt.C) { err := s.Database.Migrate(ctx, true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -48,11 +48,11 @@ func (s *dbSuite) TestModelDefaults(c *qt.C) { cloud := cloud1 cloud.Regions = nil defaults := dbmodel.CloudDefaults{ - Username: u.Username, - User: u, - CloudID: cloud.ID, - Cloud: cloud, - Region: cloud1.Regions[0].Name, + IdentityName: u.Name, + Identity: u, + CloudID: cloud.ID, + Cloud: cloud, + Region: cloud1.Regions[0].Name, Defaults: map[string]interface{}{ "key1": float64(17), "key2": "some other data", @@ -81,20 +81,20 @@ func (s *dbSuite) TestModelDefaults(c *qt.C) { }) dbDefaults := dbmodel.CloudDefaults{ - Username: u.Username, - CloudID: cloud2.ID, - Cloud: cloud2, - Region: cloud2.Regions[0].Name, + IdentityName: u.Name, + CloudID: cloud2.ID, + Cloud: cloud2, + Region: cloud2.Regions[0].Name, } err = s.Database.CloudDefaults(ctx, &dbDefaults) c.Assert(err, qt.ErrorMatches, "cloudregiondefaults not found") c.Assert(errors.ErrorCode(err), qt.Equals, errors.CodeNotFound) dbDefaults = dbmodel.CloudDefaults{ - Username: u.Username, - CloudID: cloud1.ID, - Cloud: cloud1, - Region: cloud1.Regions[0].Name, + IdentityName: u.Name, + CloudID: cloud1.ID, + Cloud: cloud1, + Region: cloud1.Regions[0].Name, } err = s.Database.CloudDefaults(ctx, &dbDefaults) c.Assert(err, qt.Equals, nil) @@ -106,20 +106,20 @@ func (s *dbSuite) TestModelDefaults(c *qt.C) { err = s.Database.CloudDefaults(ctx, &dbDefaults) c.Assert(err, qt.Equals, nil) c.Assert(dbDefaults, qt.CmpEquals(cmpopts.IgnoreTypes([]dbmodel.CloudRegion{}, gorm.Model{})), dbmodel.CloudDefaults{ - Username: u.Username, - User: u, - CloudID: cloud1.ID, - Cloud: cloud1, - Region: cloud1.Regions[0].Name, + IdentityName: u.Name, + Identity: u, + CloudID: cloud1.ID, + Cloud: cloud1, + Region: cloud1.Regions[0].Name, Defaults: map[string]interface{}{ "key3": "more data", }, }) err = s.Database.UnsetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: u.Username, - CloudID: cloud2.ID, - Region: "no-such-region", + IdentityName: u.Name, + CloudID: cloud2.ID, + Region: "no-such-region", }, []string{"key1", "key2", "unknown-key"}) c.Assert(err, qt.ErrorMatches, "cloudregiondefaults not found") } @@ -155,7 +155,7 @@ func TestModelDefaultsForCloudUnconfiguredDatabase(t *testing.T) { c := qt.New(t) var d db.Database - _, err := d.ModelDefaultsForCloud(context.Background(), &dbmodel.User{}, names.NewCloudTag("test-cloud")) + _, err := d.ModelDefaultsForCloud(context.Background(), &dbmodel.Identity{}, names.NewCloudTag("test-cloud")) c.Check(err, qt.ErrorMatches, `database not configured`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeServerConfiguration) } diff --git a/internal/db/controller_test.go b/internal/db/controller_test.go index 3466c53e2..df6caea9f 100644 --- a/internal/db/controller_test.go +++ b/internal/db/controller_test.go @@ -122,8 +122,8 @@ func (s *dbSuite) TestGetControllerWithModels(c *qt.C) { CloudName: "test-cloud", CloudRegion: "test-region", } - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) diff --git a/internal/db/db_test.go b/internal/db/db_test.go index 9d6777b78..0bb490e56 100644 --- a/internal/db/db_test.go +++ b/internal/db/db_test.go @@ -62,7 +62,7 @@ func (s *dbSuite) TestTransaction(c *qt.C) { err = s.Database.Transaction(func(d *db.Database) error { c.Check(d, qt.Not(qt.Equals), s.Database) - return d.GetUser(context.Background(), &dbmodel.User{Username: "bob@external"}) + return d.GetIdentity(context.Background(), &dbmodel.Identity{Name: "bob@external"}) }) c.Assert(err, qt.IsNil) diff --git a/internal/db/identity.go b/internal/db/identity.go new file mode 100644 index 000000000..eaae49d85 --- /dev/null +++ b/internal/db/identity.go @@ -0,0 +1,101 @@ +// Copyright 2020 Canonical Ltd. + +package db + +import ( + "context" + + "github.com/canonical/jimm/internal/dbmodel" + "github.com/canonical/jimm/internal/errors" +) + +// GetIdentity loads the details for the identity identified by name. If +// necessary the identity record will be created, in which case the identity will +// have access to no resources and the default add-model access on JIMM. +// +// GetIdentity does not fill out the identity's ApplicationOffers, Clouds, +// CloudCredentials, or Models associations. See GetIdentityApplicationOffers, +// GetIdentityClouds, GetIdentityCloudCredentials, and GetIdentityModels to retrieve +// this information. +// +// GetIdentity returns an error with CodeNotFound if the identity name is invalid. +func (d *Database) GetIdentity(ctx context.Context, u *dbmodel.Identity) error { + const op = errors.Op("db.GetIdentity") + if err := d.ready(); err != nil { + return errors.E(op, err) + } + + if u.Name == "" { + return errors.E(op, errors.CodeNotFound, `invalid identity name ""`) + } + + db := d.DB.WithContext(ctx) + if err := db.Where("name = ?", u.Name).FirstOrCreate(&u).Error; err != nil { + return errors.E(op, err) + } + return nil +} + +// FetchIdentity loads the details for the identity identified by name. It +// will not create an identity if the identity cannot be found. +// +// FetchIdentity returns an error with CodeNotFound if the identity name is invalid. +func (d *Database) FetchIdentity(ctx context.Context, u *dbmodel.Identity) error { + const op = errors.Op("db.FetchIdentity") + if err := d.ready(); err != nil { + return errors.E(op, err) + } + + if u.Name == "" { + return errors.E(op, errors.CodeNotFound, `invalid identity name ""`) + } + + db := d.DB.WithContext(ctx) + if err := db.Where("name = ?", u.Name).First(&u).Error; err != nil { + return errors.E(op, err) + } + return nil +} + +// UpdateIdentity updates the given identity record. UpdateIdentity will not store any +// changes to an identity's ApplicationOffers, Clouds, CloudCredentials, or +// Models. These should be updated through the object in question. +// +// UpdateIdentity returns an error with CodeNotFound if the identity name is +// invalid. +func (d *Database) UpdateIdentity(ctx context.Context, u *dbmodel.Identity) error { + const op = errors.Op("db.UpdateIdentity") + if err := d.ready(); err != nil { + return errors.E(op, err) + } + + if u.Name == "" { + return errors.E(op, errors.CodeNotFound, `invalid identity name ""`) + } + + db := d.DB.WithContext(ctx) + db = db.Omit("ApplicationOffers").Omit("Clouds").Omit("CloudCredentials").Omit("Models") + if err := db.Save(u).Error; err != nil { + return errors.E(op) + } + return nil +} + +// GetIdentityCloudCredentials fetches identity's cloud credentials for the specified cloud. +func (d *Database) GetIdentityCloudCredentials(ctx context.Context, u *dbmodel.Identity, cloud string) ([]dbmodel.CloudCredential, error) { + const op = errors.Op("db.GetIdentityCloudCredentials") + if err := d.ready(); err != nil { + return nil, errors.E(op, err) + } + + if u.Name == "" || cloud == "" { + return nil, errors.E(op, errors.CodeNotFound, `cloudcredential not found`) + } + + var credentials []dbmodel.CloudCredential + db := d.DB.WithContext(ctx) + if err := db.Model(u).Where("cloud_name = ?", cloud).Association("CloudCredentials").Find(&credentials); err != nil { + return nil, errors.E(op, err) + } + return credentials, nil +} diff --git a/internal/db/user_test.go b/internal/db/identity_test.go similarity index 56% rename from internal/db/user_test.go rename to internal/db/identity_test.go index 4dce0925f..d23c61437 100644 --- a/internal/db/user_test.go +++ b/internal/db/identity_test.go @@ -13,107 +13,107 @@ import ( "github.com/canonical/jimm/internal/errors" ) -func TestGetUserUnconfiguredDatabase(t *testing.T) { +func TestGetIdentityUnconfiguredDatabase(t *testing.T) { c := qt.New(t) var d db.Database - err := d.GetUser(context.Background(), &dbmodel.User{}) + err := d.GetIdentity(context.Background(), &dbmodel.Identity{}) c.Check(err, qt.ErrorMatches, `database not configured`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeServerConfiguration) } -func (s *dbSuite) TestGetUser(c *qt.C) { +func (s *dbSuite) TestGetIdentity(c *qt.C) { ctx := context.Background() - err := s.Database.GetUser(ctx, &dbmodel.User{}) + err := s.Database.GetIdentity(ctx, &dbmodel.Identity{}) c.Check(err, qt.ErrorMatches, `upgrade in progress`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeUpgradeInProgress) err = s.Database.Migrate(ctx, false) c.Assert(err, qt.IsNil) - err = s.Database.GetUser(ctx, &dbmodel.User{}) - c.Check(err, qt.ErrorMatches, `invalid username ""`) + err = s.Database.GetIdentity(ctx, &dbmodel.Identity{}) + c.Check(err, qt.ErrorMatches, `invalid identity name ""`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeNotFound) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } - err = s.Database.GetUser(ctx, &u) + err = s.Database.GetIdentity(ctx, &u) c.Assert(err, qt.IsNil) - u2 := dbmodel.User{ - Username: u.Username, + u2 := dbmodel.Identity{ + Name: u.Name, } - err = s.Database.GetUser(ctx, &u2) + err = s.Database.GetIdentity(ctx, &u2) c.Assert(err, qt.IsNil) c.Check(u2, qt.DeepEquals, u) } -func TestUpdateUserUnconfiguredDatabase(t *testing.T) { +func TestUpdateIdentityUnconfiguredDatabase(t *testing.T) { c := qt.New(t) var d db.Database - err := d.UpdateUser(context.Background(), &dbmodel.User{}) + err := d.UpdateIdentity(context.Background(), &dbmodel.Identity{}) c.Check(err, qt.ErrorMatches, `database not configured`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeServerConfiguration) } -func (s *dbSuite) TestUpdateUser(c *qt.C) { +func (s *dbSuite) TestUpdateIdentity(c *qt.C) { ctx := context.Background() - err := s.Database.UpdateUser(ctx, &dbmodel.User{}) + err := s.Database.UpdateIdentity(ctx, &dbmodel.Identity{}) c.Check(err, qt.ErrorMatches, `upgrade in progress`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeUpgradeInProgress) err = s.Database.Migrate(ctx, false) c.Assert(err, qt.IsNil) - err = s.Database.UpdateUser(ctx, &dbmodel.User{}) - c.Check(err, qt.ErrorMatches, `invalid username ""`) + err = s.Database.UpdateIdentity(ctx, &dbmodel.Identity{}) + c.Check(err, qt.ErrorMatches, `invalid identity name ""`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeNotFound) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } - err = s.Database.GetUser(ctx, &u) + err = s.Database.GetIdentity(ctx, &u) c.Assert(err, qt.IsNil) - err = s.Database.UpdateUser(ctx, &u) + err = s.Database.UpdateIdentity(ctx, &u) c.Assert(err, qt.IsNil) - u2 := dbmodel.User{ - Username: u.Username, + u2 := dbmodel.Identity{ + Name: u.Name, } - err = s.Database.GetUser(ctx, &u2) + err = s.Database.GetIdentity(ctx, &u2) c.Assert(err, qt.IsNil) c.Check(u2, qt.DeepEquals, u) } -func TestGetUserCloudCredentialsUnconfiguredDatabase(t *testing.T) { +func TestGetIdentityCloudCredentialsUnconfiguredDatabase(t *testing.T) { c := qt.New(t) var d db.Database - _, err := d.GetUserCloudCredentials(context.Background(), &dbmodel.User{}, "") + _, err := d.GetIdentityCloudCredentials(context.Background(), &dbmodel.Identity{}, "") c.Check(err, qt.ErrorMatches, `database not configured`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeServerConfiguration) } -func (s *dbSuite) TestGetUserCloudCredentials(c *qt.C) { +func (s *dbSuite) TestGetIdentityCloudCredentials(c *qt.C) { ctx := context.Background() err := s.Database.Migrate(ctx, false) c.Assert(err, qt.IsNil) - _, err = s.Database.GetUserCloudCredentials(ctx, &dbmodel.User{}, "") + _, err = s.Database.GetIdentityCloudCredentials(ctx, &dbmodel.Identity{}, "") c.Check(err, qt.ErrorMatches, `cloudcredential not found`) c.Check(errors.ErrorCode(err), qt.Equals, errors.CodeNotFound) - _, err = s.Database.GetUserCloudCredentials(ctx, &dbmodel.User{ - Username: "test", + _, err = s.Database.GetIdentityCloudCredentials(ctx, &dbmodel.Identity{ + Name: "test", }, "ec2") c.Check(err, qt.IsNil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -127,24 +127,24 @@ func (s *dbSuite) TestGetUserCloudCredentials(c *qt.C) { c.Assert(s.Database.DB.Create(&cloud).Error, qt.IsNil) cred1 := dbmodel.CloudCredential{ - Name: "test-cred-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-cred-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = s.Database.SetCloudCredential(context.Background(), &cred1) c.Assert(err, qt.Equals, nil) cred2 := dbmodel.CloudCredential{ - Name: "test-cred-2", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-cred-2", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = s.Database.SetCloudCredential(context.Background(), &cred2) c.Assert(err, qt.Equals, nil) - credentials, err := s.Database.GetUserCloudCredentials(ctx, &u, cloud.Name) + credentials, err := s.Database.GetIdentityCloudCredentials(ctx, &u, cloud.Name) c.Check(err, qt.IsNil) c.Assert(credentials, qt.DeepEquals, []dbmodel.CloudCredential{cred1, cred2}) } diff --git a/internal/db/usermodeldefaults.go b/internal/db/identitymodeldefaults.go similarity index 60% rename from internal/db/usermodeldefaults.go rename to internal/db/identitymodeldefaults.go index 67fd81e73..5f139b27f 100644 --- a/internal/db/usermodeldefaults.go +++ b/internal/db/identitymodeldefaults.go @@ -11,18 +11,18 @@ import ( "github.com/canonical/jimm/internal/errors" ) -// SetUserModelDefaults sets default model setting values for the controller. -func (d *Database) SetUserModelDefaults(ctx context.Context, defaults *dbmodel.UserModelDefaults) error { - const op = errors.Op("db.SetUserModelDefaults") +// SetIdentityModelDefaults sets default model setting values for the controller. +func (d *Database) SetIdentityModelDefaults(ctx context.Context, defaults *dbmodel.IdentityModelDefaults) error { + const op = errors.Op("db.SetIdentityModelDefaults") err := d.Transaction(func(d *Database) error { db := d.DB.WithContext(ctx) - dbDefaults := dbmodel.UserModelDefaults{ - Username: defaults.Username, + dbDefaults := dbmodel.IdentityModelDefaults{ + IdentityName: defaults.IdentityName, } // try to fetch cloud defaults from the db - err := d.UserModelDefaults(ctx, &dbDefaults) + err := d.IdentityModelDefaults(ctx, &dbDefaults) if err != nil { if errors.ErrorCode(err) == errors.CodeNotFound { // if defaults do not exist, we create them @@ -40,7 +40,7 @@ func (d *Database) SetUserModelDefaults(ctx context.Context, defaults *dbmodel.U } if err := db.Clauses(clause.OnConflict{ Columns: []clause.Column{ - {Name: "username"}, + {Name: "identity_name"}, }, DoUpdates: clause.AssignmentColumns([]string{"defaults"}), }).Create(&dbDefaults).Error; err != nil { @@ -54,22 +54,22 @@ func (d *Database) SetUserModelDefaults(ctx context.Context, defaults *dbmodel.U return nil } -// UserModelDefaults fetches user defaults. -func (d *Database) UserModelDefaults(ctx context.Context, defaults *dbmodel.UserModelDefaults) error { - const op = errors.Op("db.UserModelDefaults") +// IdentityModelDefaults fetches identities defaults. +func (d *Database) IdentityModelDefaults(ctx context.Context, defaults *dbmodel.IdentityModelDefaults) error { + const op = errors.Op("db.IdentityModelDefaults") if err := d.ready(); err != nil { return errors.E(op, err) } db := d.DB.WithContext(ctx) - db = db.Where("username = ?", defaults.Username) + db = db.Where("identity_name = ?", defaults.IdentityName) - result := db.Preload("User").First(&defaults) + result := db.Preload("Identity").First(&defaults) if result.Error != nil { err := dbError(result.Error) if errors.ErrorCode(err) == errors.CodeNotFound { - return errors.E(op, errors.CodeNotFound, "usermodeldefaults not found", err) + return errors.E(op, errors.CodeNotFound, "identitymodeldefaults not found", err) } return errors.E(op, err) } diff --git a/internal/db/usermodeldefaults_test.go b/internal/db/identitymodeldefaults_test.go similarity index 66% rename from internal/db/usermodeldefaults_test.go rename to internal/db/identitymodeldefaults_test.go index 0d5f6f25c..df3e6af4c 100644 --- a/internal/db/usermodeldefaults_test.go +++ b/internal/db/identitymodeldefaults_test.go @@ -17,17 +17,17 @@ import ( "github.com/canonical/jimm/internal/jimmtest" ) -func TestSetUserModelDefaults(t *testing.T) { +func TestSetIdentityModelDefaults(t *testing.T) { c := qt.New(t) ctx := context.Background() now := time.Now() type testConfig struct { - user *dbmodel.User + identity *dbmodel.Identity defaults map[string]interface{} expectedError string - expectedDefaults *dbmodel.UserModelDefaults + expectedDefaults *dbmodel.IdentityModelDefaults } tests := []struct { @@ -37,24 +37,24 @@ func TestSetUserModelDefaults(t *testing.T) { }{{ about: "defaults do not exist yet - defaults created", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) defaults := map[string]interface{}{ "key1": float64(42), "key2": "a test string", } - expectedDefaults := dbmodel.UserModelDefaults{ - Username: user.Username, - User: user, - Defaults: defaults, + expectedDefaults := dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Identity: identity, + Defaults: defaults, } return testConfig{ - user: &user, + identity: &identity, defaults: defaults, expectedDefaults: &expectedDefaults, } @@ -62,14 +62,14 @@ func TestSetUserModelDefaults(t *testing.T) { }, { about: "defaults already exist - defaults updated", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) - j.Database.SetUserModelDefaults(ctx, &dbmodel.UserModelDefaults{ - Username: user.Username, - User: user, + j.Database.SetIdentityModelDefaults(ctx, &dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Identity: identity, Defaults: map[string]interface{}{ "key1": float64(17), "key2": "a test string", @@ -82,23 +82,23 @@ func TestSetUserModelDefaults(t *testing.T) { "key3": "a new value", } - expectedDefaults := dbmodel.UserModelDefaults{ - Username: user.Username, - User: user, - Defaults: defaults, + expectedDefaults := dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Identity: identity, + Defaults: defaults, } return testConfig{ - user: &user, + identity: &identity, defaults: defaults, expectedDefaults: &expectedDefaults, } }, }, { - about: "user does not exist", + about: "identity does not exist", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } defaults := map[string]interface{}{ @@ -108,7 +108,7 @@ func TestSetUserModelDefaults(t *testing.T) { } return testConfig{ - user: &user, + identity: &identity, defaults: defaults, expectedError: `.*violates foreign key constraint.*`, } @@ -116,10 +116,10 @@ func TestSetUserModelDefaults(t *testing.T) { }, { about: "cannot set agent-version", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) defaults := map[string]interface{}{ "agent-version": "2.0", @@ -128,7 +128,7 @@ func TestSetUserModelDefaults(t *testing.T) { } return testConfig{ - user: &user, + identity: &identity, defaults: defaults, expectedError: `agent-version cannot have a default value`, } @@ -147,13 +147,13 @@ func TestSetUserModelDefaults(t *testing.T) { testConfig := test.setup(c, j) - err = j.SetUserModelDefaults(ctx, testConfig.user, testConfig.defaults) + err = j.SetIdentityModelDefaults(ctx, testConfig.identity, testConfig.defaults) if testConfig.expectedError == "" { c.Assert(err, qt.Equals, nil) - dbDefaults := dbmodel.UserModelDefaults{ - Username: testConfig.expectedDefaults.Username, + dbDefaults := dbmodel.IdentityModelDefaults{ + IdentityName: testConfig.expectedDefaults.IdentityName, } - err = j.Database.UserModelDefaults(ctx, &dbDefaults) + err = j.Database.IdentityModelDefaults(ctx, &dbDefaults) c.Assert(err, qt.Equals, nil) c.Assert(&dbDefaults, qt.CmpEquals(cmpopts.IgnoreTypes(gorm.Model{})), testConfig.expectedDefaults) } else { diff --git a/internal/db/model.go b/internal/db/model.go index 4b22cd493..69c4e58e5 100644 --- a/internal/db/model.go +++ b/internal/db/model.go @@ -42,8 +42,8 @@ func (d *Database) GetModel(ctx context.Context, model *dbmodel.Model) error { } } else if model.ID != 0 { db = db.Where("id = ?", model.ID) - } else if model.OwnerUsername != "" && model.Name != "" { - db = db.Where("owner_username = ? AND name = ?", model.OwnerUsername, model.Name) + } else if model.OwnerIdentityName != "" && model.Name != "" { + db = db.Where("owner_identity_name = ? AND name = ?", model.OwnerIdentityName, model.Name) } else if model.ControllerID != 0 { // TODO(ales): fix ordering of where fields and handle error to represent what is *actually* required. db = db.Where("controller_id = ?", model.ControllerID) diff --git a/internal/db/model_test.go b/internal/db/model_test.go index 89cf1a7d5..62b5a60e4 100644 --- a/internal/db/model_test.go +++ b/internal/db/model_test.go @@ -31,8 +31,8 @@ func (s *dbSuite) TestAddModel(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -69,7 +69,7 @@ func (s *dbSuite) TestAddModel(c *qt.C) { String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -104,8 +104,8 @@ func (s *dbSuite) TestGetModel(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -142,7 +142,7 @@ func (s *dbSuite) TestGetModel(c *qt.C) { String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, Owner: u, ControllerID: controller.ID, Controller: controller, @@ -162,7 +162,7 @@ func (s *dbSuite) TestGetModel(c *qt.C) { }, } model.CloudCredential.Cloud = dbmodel.Cloud{} - model.CloudCredential.Owner = dbmodel.User{} + model.CloudCredential.Owner = dbmodel.Identity{} err = s.Database.AddModel(context.Background(), &model) c.Assert(err, qt.Equals, nil) @@ -189,8 +189,8 @@ func (s *dbSuite) TestGetModel(c *qt.C) { c.Assert(eError.Code, qt.Equals, errors.CodeNotFound) dbModel = dbmodel.Model{ - Name: model.Name, - OwnerUsername: model.OwnerUsername, + Name: model.Name, + OwnerIdentityName: model.OwnerIdentityName, } err = s.Database.GetModel(context.Background(), &dbModel) c.Assert(err, qt.IsNil) @@ -204,8 +204,8 @@ func (s *dbSuite) TestUpdateModel(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -238,7 +238,7 @@ func (s *dbSuite) TestUpdateModel(c *qt.C) { model := dbmodel.Model{ Name: "test-model-1", - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -282,8 +282,8 @@ func (s *dbSuite) TestDeleteModel(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -316,7 +316,7 @@ func (s *dbSuite) TestDeleteModel(c *qt.C) { model := dbmodel.Model{ Name: "test-model-1", - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, Controller: controller, CloudRegionID: cloud.Regions[0].ID, @@ -356,8 +356,8 @@ func (s *dbSuite) TestGetModelsUsingCredential(c *qt.C) { err := s.Database.Migrate(context.Background(), true) c.Assert(err, qt.Equals, nil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(s.Database.DB.Create(&u).Error, qt.IsNil) @@ -401,7 +401,7 @@ func (s *dbSuite) TestGetModelsUsingCredential(c *qt.C) { String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred1.ID, @@ -425,7 +425,7 @@ func (s *dbSuite) TestGetModelsUsingCredential(c *qt.C) { String: "00000001-0000-0000-0000-0000-000000000002", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred2.ID, @@ -454,7 +454,7 @@ func (s *dbSuite) TestGetModelsUsingCredential(c *qt.C) { String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, Controller: controller, CloudRegionID: cloud.Regions[0].ID, diff --git a/internal/db/pgx_test.go b/internal/db/pgx_test.go index 269138aaf..79df6eaf5 100644 --- a/internal/db/pgx_test.go +++ b/internal/db/pgx_test.go @@ -23,6 +23,10 @@ import ( "github.com/canonical/jimm/internal/jimmtest" ) +const ( + defaultDSN = "postgresql://jimm:jimm@127.0.0.1:5432/jimm" +) + func TestPostgres(t *testing.T) { c := qt.New(t) @@ -34,8 +38,10 @@ type postgresSuite struct { } func (s *postgresSuite) Init(c *qt.C) { - dsn, exists := os.LookupEnv("JIMM_TEST_PGXDSN") - c.Assert(exists, qt.IsTrue, qt.Commentf("env var JIMM_TEST_PGXDSN is not assigned")) + dsn := defaultDSN + if envTestDSN, exists := os.LookupEnv("JIMM_TEST_PGXDSN"); exists { + dsn = envTestDSN + } connCfg, err := pgx.ParseConfig(dsn) c.Assert(err, qt.IsNil) diff --git a/internal/db/user.go b/internal/db/user.go deleted file mode 100644 index ae074d3a4..000000000 --- a/internal/db/user.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2020 Canonical Ltd. - -package db - -import ( - "context" - - "github.com/canonical/jimm/internal/dbmodel" - "github.com/canonical/jimm/internal/errors" -) - -// GetUser loads the user details for the user identified by username. If -// necessary the user record will be created, in which case the user will -// have access to no resources and the default add-model access on JIMM. -// -// GetUser does not fill out the user's ApplicationOffers, Clouds, -// CloudCredentials, or Models associations. See GetUserApplicationOffers, -// GetUserClouds, GetUserCloudCredentials, and GetUserModels to retrieve -// this information. -// -// GetUser returns an error with CodeNotFound if the username is invalid. -func (d *Database) GetUser(ctx context.Context, u *dbmodel.User) error { - const op = errors.Op("db.GetUser") - if err := d.ready(); err != nil { - return errors.E(op, err) - } - - if u.Username == "" { - return errors.E(op, errors.CodeNotFound, `invalid username ""`) - } - - db := d.DB.WithContext(ctx) - if err := db.Where("username = ?", u.Username).FirstOrCreate(&u).Error; err != nil { - return errors.E(op, err) - } - return nil -} - -// FetchUser loads the user details for the user identified by username. It -// will not create a user if the user cannot be found. -// -// FetchUser returns an error with CodeNotFound if the username is invalid. -func (d *Database) FetchUser(ctx context.Context, u *dbmodel.User) error { - const op = errors.Op("db.FetchUser") - if err := d.ready(); err != nil { - return errors.E(op, err) - } - - if u.Username == "" { - return errors.E(op, errors.CodeNotFound, `invalid username ""`) - } - - db := d.DB.WithContext(ctx) - if err := db.Where("username = ?", u.Username).First(&u).Error; err != nil { - return errors.E(op, err) - } - return nil -} - -// UpdateUser updates the given user record. UpdateUser will not store any -// changes to a user's ApplicationOffers, Clouds, CloudCredentials, or -// Models. These should be updated through the object in question. -// -// UpdateUser returns an error with CodeNotFound if the username is -// invalid. -func (d *Database) UpdateUser(ctx context.Context, u *dbmodel.User) error { - const op = errors.Op("db.UpdateUser") - if err := d.ready(); err != nil { - return errors.E(op, err) - } - - if u.Username == "" { - return errors.E(op, errors.CodeNotFound, `invalid username ""`) - } - - db := d.DB.WithContext(ctx) - db = db.Omit("ApplicationOffers").Omit("Clouds").Omit("CloudCredentials").Omit("Models") - if err := db.Save(u).Error; err != nil { - return errors.E(op) - } - return nil -} - -// GetUserCloudCredentials fetches user cloud credentials for the specified cloud. -func (d *Database) GetUserCloudCredentials(ctx context.Context, u *dbmodel.User, cloud string) ([]dbmodel.CloudCredential, error) { - const op = errors.Op("db.GetUserCloudCredentials") - if err := d.ready(); err != nil { - return nil, errors.E(op, err) - } - - if u.Username == "" || cloud == "" { - return nil, errors.E(op, errors.CodeNotFound, `cloudcredential not found`) - } - - var credentials []dbmodel.CloudCredential - db := d.DB.WithContext(ctx) - if err := db.Model(u).Where("cloud_name = ?", cloud).Association("CloudCredentials").Find(&credentials); err != nil { - return nil, errors.E(op, err) - } - return credentials, nil -} diff --git a/internal/dbmodel/applicationoffer.go b/internal/dbmodel/applicationoffer.go index 2c2e6d8f2..419d5901b 100644 --- a/internal/dbmodel/applicationoffer.go +++ b/internal/dbmodel/applicationoffer.go @@ -104,7 +104,7 @@ func (o *ApplicationOffer) FromJujuApplicationOfferAdminDetails(offerDetails juj o.Connections[i] = ApplicationOfferConnection{ SourceModelTag: connection.SourceModelTag, RelationID: connection.RelationId, - Username: connection.Username, + IdentityName: connection.Username, Endpoint: connection.Endpoint, IngressSubnets: connection.IngressSubnets, } @@ -137,7 +137,7 @@ func (o *ApplicationOffer) ToJujuApplicationOfferDetails() jujuparams.Applicatio connections[i] = jujuparams.OfferConnection{ SourceModelTag: connection.SourceModelTag, RelationId: connection.RelationID, - Username: connection.Username, + Username: connection.IdentityName, Endpoint: connection.Endpoint, IngressSubnets: connection.IngressSubnets, } @@ -199,7 +199,7 @@ type ApplicationOfferConnection struct { SourceModelTag string RelationID int - Username string + IdentityName string Endpoint string IngressSubnets Strings } diff --git a/internal/dbmodel/audit.go b/internal/dbmodel/audit.go index ff705fa16..00b744510 100644 --- a/internal/dbmodel/audit.go +++ b/internal/dbmodel/audit.go @@ -40,8 +40,8 @@ type AuditLogEntry struct { // ObjectId contains the object id to act on, only used by certain facades. ObjectId string - // UserTag is the tag of the user the performed the action. - UserTag string `gorm:"index"` + // IdentityTag is the tag of the identity that performed the action. + IdentityTag string `gorm:"index"` // IsResponse indicates whether the action was a Response/Request. IsResponse bool @@ -69,7 +69,7 @@ func (e AuditLogEntry) ToAPIAuditEvent() apiparams.AuditEvent { ale.FacadeName = e.FacadeName ale.FacadeVersion = e.FacadeVersion ale.ObjectId = e.ObjectId - ale.UserTag = e.UserTag + ale.UserTag = e.IdentityTag ale.Model = e.Model ale.IsResponse = e.IsResponse ale.Errors = nil diff --git a/internal/dbmodel/audit_test.go b/internal/dbmodel/audit_test.go index 08221907b..6c52bacec 100644 --- a/internal/dbmodel/audit_test.go +++ b/internal/dbmodel/audit_test.go @@ -30,7 +30,7 @@ func TestAuditLogEntry(t *testing.T) { FacadeMethod: "AddController", FacadeVersion: 1, ObjectId: "1", - UserTag: names.NewUserTag("bob@external").String(), + IdentityTag: names.NewUserTag("bob@external").String(), IsResponse: false, Params: paramsJSON, Errors: nil, @@ -57,7 +57,7 @@ func TestToAPIAuditEvent(t *testing.T) { FacadeMethod: "AddController", FacadeVersion: 1, ObjectId: "1", - UserTag: names.NewUserTag("bob@external").String(), + IdentityTag: names.NewUserTag("bob@external").String(), IsResponse: false, Params: paramsJSON, Errors: nil, diff --git a/internal/dbmodel/cloudcredential.go b/internal/dbmodel/cloudcredential.go index f8adf20f3..cc6d0d524 100644 --- a/internal/dbmodel/cloudcredential.go +++ b/internal/dbmodel/cloudcredential.go @@ -21,9 +21,9 @@ type CloudCredential struct { CloudName string Cloud Cloud `gorm:"foreignKey:CloudName;references:Name;constraint:OnDelete:CASCADE"` - // Owner is the user that owns this credential. - OwnerUsername string - Owner User `gorm:"foreignKey:OwnerUsername;references:Username"` + // Owner is the identity that owns this credential. + OwnerIdentityName string + Owner Identity `gorm:"foreignKey:OwnerIdentityName;references:Name"` // AuthType is the type of the credential. AuthType string @@ -55,17 +55,17 @@ func (c CloudCredential) Tag() names.Tag { // a concrete type names.CloudCredentialTag instead of the // names.Tag interface. func (c CloudCredential) ResourceTag() names.CloudCredentialTag { - return names.NewCloudCredentialTag(fmt.Sprintf("%s/%s/%s", c.CloudName, c.OwnerUsername, c.Name)) + return names.NewCloudCredentialTag(fmt.Sprintf("%s/%s/%s", c.CloudName, c.OwnerIdentityName, c.Name)) } // SetTag sets the Name, CloudName and Username fields from the given tag. func (c *CloudCredential) SetTag(t names.CloudCredentialTag) { c.CloudName = t.Cloud().Id() c.Name = t.Name() - c.OwnerUsername = t.Owner().Id() + c.OwnerIdentityName = t.Owner().Id() } // Path returns a juju style cloud credential path. func (c CloudCredential) Path() string { - return fmt.Sprintf("%s/%s/%s", c.CloudName, c.OwnerUsername, c.Name) + return fmt.Sprintf("%s/%s/%s", c.CloudName, c.OwnerIdentityName, c.Name) } diff --git a/internal/dbmodel/cloudcredential_test.go b/internal/dbmodel/cloudcredential_test.go index 804d207f8..651e89817 100644 --- a/internal/dbmodel/cloudcredential_test.go +++ b/internal/dbmodel/cloudcredential_test.go @@ -15,9 +15,9 @@ func TestCloudCredentialTag(t *testing.T) { c := qt.New(t) cred := dbmodel.CloudCredential{ - Name: "test-credential", - CloudName: "test-cloud", - OwnerUsername: "test-user", + Name: "test-credential", + CloudName: "test-cloud", + OwnerIdentityName: "test-user", } tag := cred.Tag() c.Check(tag.String(), qt.Equals, "cloudcred-test-cloud_test-user_test-credential") @@ -36,8 +36,8 @@ func TestCloudCredential(t *testing.T) { Cloud: dbmodel.Cloud{ Name: "test-cloud", }, - Owner: dbmodel.User{ - Username: "bob@external", + Owner: dbmodel.Identity{ + Name: "bob@external", }, AuthType: "empty", Label: "test label", @@ -49,7 +49,7 @@ func TestCloudCredential(t *testing.T) { result := db.Create(&cred) c.Assert(result.Error, qt.IsNil) c.Check(cred.CloudName, qt.Equals, cred.Cloud.Name) - c.Check(cred.OwnerUsername, qt.Equals, cred.Owner.Username) + c.Check(cred.OwnerIdentityName, qt.Equals, cred.Owner.Name) } // TestCloudCredentialsCascadeOnDelete As of database version 1.3 (see migrations), @@ -69,15 +69,15 @@ func TestCloudCredentialsCascadeOnDelete(t *testing.T) { cred := dbmodel.CloudCredential{ Name: "test-credential", Cloud: cloud, - Owner: dbmodel.User{ - Username: "bob@external", + Owner: dbmodel.Identity{ + Name: "bob@external", }, } result = db.Create(&cred) c.Assert(result.Error, qt.IsNil) c.Check(result.RowsAffected, qt.Equals, int64(1)) c.Check(cred.CloudName, qt.Equals, "test-cloud") - c.Check(cred.OwnerUsername, qt.Equals, "bob@external") + c.Check(cred.OwnerIdentityName, qt.Equals, "bob@external") result = db.Delete(&cloud) c.Assert(result.Error, qt.IsNil) diff --git a/internal/dbmodel/clouddefaults.go b/internal/dbmodel/clouddefaults.go index 54fb7d6a1..3c52814b9 100644 --- a/internal/dbmodel/clouddefaults.go +++ b/internal/dbmodel/clouddefaults.go @@ -8,8 +8,8 @@ import "gorm.io/gorm" type CloudDefaults struct { gorm.Model - Username string - User User `gorm:"foreignKey:Username;references:Username"` + IdentityName string + Identity Identity `gorm:"foreignKey:IdentityName;references:Name"` CloudID uint Cloud Cloud diff --git a/internal/dbmodel/controller.go b/internal/dbmodel/controller.go index 941b4ec41..3ceacca81 100644 --- a/internal/dbmodel/controller.go +++ b/internal/dbmodel/controller.go @@ -28,9 +28,9 @@ type Controller struct { // purposes. UUID string `gorm:"not null"` - // AdminUser is the username that JIMM uses to connect to the + // AdminIdentityName is the identity name that JIMM uses to connect to the // controller. - AdminUser string + AdminIdentityName string // AdminPassword is the password that JIMM uses to connect to the // controller. @@ -104,7 +104,7 @@ func (c Controller) ToAPIControllerInfo() apiparams.ControllerInfo { var ci apiparams.ControllerInfo ci.Name = c.Name ci.UUID = c.UUID - ci.Username = c.AdminUser + ci.Username = c.AdminIdentityName ci.PublicAddress = c.PublicAddress for _, hps := range c.Addresses { for _, hp := range hps { @@ -114,7 +114,7 @@ func (c Controller) ToAPIControllerInfo() apiparams.ControllerInfo { ci.CACertificate = c.CACertificate ci.CloudTag = names.NewCloudTag(c.CloudName).String() ci.CloudRegion = c.CloudRegion - ci.Username = c.AdminUser + ci.Username = c.AdminIdentityName ci.AgentVersion = c.AgentVersion if c.UnavailableSince.Valid { ci.Status = jujuparams.EntityStatus{ diff --git a/internal/dbmodel/controller_test.go b/internal/dbmodel/controller_test.go index 9ca0cae6f..720abb730 100644 --- a/internal/dbmodel/controller_test.go +++ b/internal/dbmodel/controller_test.go @@ -39,13 +39,13 @@ func TestController(t *testing.T) { c.Assert(result.Error, qt.IsNil) ctl := dbmodel.Controller{ - Name: "test-controller", - UUID: "00000000-0000-0000-0000-000000000001", - AdminUser: "admin", - AdminPassword: "pw", - CACertificate: "ca-cert", - PublicAddress: "controller.example.com:443", - CloudName: "test-cloud", + Name: "test-controller", + UUID: "00000000-0000-0000-0000-000000000001", + AdminIdentityName: "admin", + AdminPassword: "pw", + CACertificate: "ca-cert", + PublicAddress: "controller.example.com:443", + CloudName: "test-cloud", Addresses: dbmodel.HostPorts([][]jujuparams.HostPort{{{ Address: jujuparams.Address{ Value: "1.1.1.1", @@ -91,8 +91,8 @@ func TestControllerModels(t *testing.T) { } c.Assert(db.Create(&m1).Error, qt.IsNil) - u2 := dbmodel.User{ - Username: "charlie@external", + u2 := dbmodel.Identity{ + Name: "charlie@external", } c.Assert(db.Create(&u2).Error, qt.IsNil) @@ -119,7 +119,7 @@ func TestControllerModels(t *testing.T) { UpdatedAt: m1.UpdatedAt, Name: m1.Name, UUID: m1.UUID, - OwnerUsername: m1.OwnerUsername, + OwnerIdentityName: m1.OwnerIdentityName, ControllerID: m1.ControllerID, CloudRegionID: m1.CloudRegionID, CloudCredentialID: m1.CloudCredentialID, @@ -129,7 +129,7 @@ func TestControllerModels(t *testing.T) { UpdatedAt: m2.UpdatedAt, Name: m2.Name, UUID: m2.UUID, - OwnerUsername: m2.OwnerUsername, + OwnerIdentityName: m2.OwnerIdentityName, ControllerID: m2.ControllerID, CloudRegionID: m2.CloudRegionID, CloudCredentialID: m2.CloudCredentialID, @@ -166,7 +166,7 @@ func TestToAPIControllerInfo(t *testing.T) { CloudRegion: cl.Regions[0], Priority: dbmodel.CloudRegionControllerPriorityDeployed, }} - ctl.AdminUser = "admin" + ctl.AdminIdentityName = "admin" ctl.AgentVersion = "1.2.3" ci := ctl.ToAPIControllerInfo() diff --git a/internal/dbmodel/identity.go b/internal/dbmodel/identity.go new file mode 100644 index 000000000..56ebe0bf6 --- /dev/null +++ b/internal/dbmodel/identity.go @@ -0,0 +1,68 @@ +// Copyright 2020 Canonical Ltd. + +package dbmodel + +import ( + "database/sql" + + jujuparams "github.com/juju/juju/rpc/params" + "github.com/juju/names/v4" + "gorm.io/gorm" +) + +// Identity represents a JIMM identity, which can be a user or a service account. +type Identity struct { + gorm.Model + + // Name is the name of the identity. This is the user name when + // representing a Juju user (i.e. with an @external suffix), or the client + // ID for a service account. The Name will have originated at an + // external identity provider in JAAS deployments. + Name string `gorm:"not null;uniqueIndex"` + + // DisplayName is the display name of the identity. + DisplayName string `gorm:"not null"` + + // LastLogin is the time the identity last authenticated to the JIMM + // server. LastLogin will only be a valid time if the identity has + // authenticated at least once. + LastLogin sql.NullTime + + // Disabled records whether the identity has been disabled or not, disabled + // identities are not allowed to authenticate. + Disabled bool `gorm:"not null;default:FALSE"` + + // CloudCredentials are the cloud credentials owned by this identity. + CloudCredentials []CloudCredential `gorm:"foreignKey:OwnerIdentityName;references:Name"` +} + +// Tag returns a names.Tag for the identity. +func (u Identity) Tag() names.Tag { + return u.ResourceTag() +} + +// ResourceTag returns a tag for the user. This method +// is intended to be used in places where we expect to see +// a concrete type names.UserTag instead of the +// names.Tag interface. +func (i Identity) ResourceTag() names.UserTag { + return names.NewUserTag(i.Name) +} + +// SetTag sets the identity name of the identity to the value from the given tag. +func (i *Identity) SetTag(t names.UserTag) { + i.Name = t.Id() +} + +// ToJujuUserInfo converts an Identity into a juju UserInfo value. +func (i Identity) ToJujuUserInfo() jujuparams.UserInfo { + var ui jujuparams.UserInfo + ui.Username = i.Name + ui.DisplayName = i.DisplayName + ui.Access = "" //TODO(Kian) CSS-6040 Handle merging OpenFGA and Postgres information + ui.DateCreated = i.CreatedAt + if i.LastLogin.Valid { + ui.LastConnection = &i.LastLogin.Time + } + return ui +} diff --git a/internal/dbmodel/user_test.go b/internal/dbmodel/identity_test.go similarity index 69% rename from internal/dbmodel/user_test.go rename to internal/dbmodel/identity_test.go index d953e0099..d9b0132df 100644 --- a/internal/dbmodel/user_test.go +++ b/internal/dbmodel/identity_test.go @@ -15,24 +15,24 @@ import ( "github.com/canonical/jimm/internal/dbmodel" ) -func TestUser(t *testing.T) { +func TestIdentity(t *testing.T) { c := qt.New(t) db := gormDB(c) - var u0 dbmodel.User - result := db.Where("username = ?", "bob@external").First(&u0) + var u0 dbmodel.Identity + result := db.Where("name = ?", "bob@external").First(&u0) c.Check(result.Error, qt.Equals, gorm.ErrRecordNotFound) - u1 := dbmodel.User{ - Username: "bob@external", + u1 := dbmodel.Identity{ + Name: "bob@external", DisplayName: "bob", } result = db.Create(&u1) c.Assert(result.Error, qt.IsNil) c.Check(result.RowsAffected, qt.Equals, int64(1)) - var u2 dbmodel.User - result = db.Where("username = ?", "bob@external").First(&u2) + var u2 dbmodel.Identity + result = db.Where("name = ?", "bob@external").First(&u2) c.Assert(result.Error, qt.IsNil) c.Check(u2, qt.DeepEquals, u1) @@ -40,33 +40,33 @@ func TestUser(t *testing.T) { u2.LastLogin.Valid = true result = db.Save(&u2) c.Assert(result.Error, qt.IsNil) - var u3 dbmodel.User - result = db.Where("username = ?", "bob@external").First(&u3) + var u3 dbmodel.Identity + result = db.Where("name = ?", "bob@external").First(&u3) c.Assert(result.Error, qt.IsNil) c.Check(u3, qt.DeepEquals, u2) - u4 := dbmodel.User{ - Username: "bob@external", + u4 := dbmodel.Identity{ + Name: "bob@external", DisplayName: "bob", } result = db.Create(&u4) - c.Check(result.Error, qt.ErrorMatches, `.*violates unique constraint "users_username_key".*`) + c.Check(result.Error, qt.ErrorMatches, `.*violates unique constraint "identities_name_key".*`) } func TestUserTag(t *testing.T) { c := qt.New(t) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } tag := u.Tag() c.Check(tag.String(), qt.Equals, "user-bob@external") - var u2 dbmodel.User + var u2 dbmodel.Identity u2.SetTag(tag.(names.UserTag)) c.Check(u2, qt.DeepEquals, u) } -func TestUserCloudCredentials(t *testing.T) { +func TestIdentityCloudCredentials(t *testing.T) { c := qt.New(t) db := gormDB(c) @@ -76,8 +76,8 @@ func TestUserCloudCredentials(t *testing.T) { result := db.Create(&cl) c.Assert(result.Error, qt.IsNil) - u := dbmodel.User{ - Username: "bob@external", + u := dbmodel.Identity{ + Name: "bob@external", } result = db.Create(&u) c.Assert(result.Error, qt.IsNil) @@ -104,28 +104,28 @@ func TestUserCloudCredentials(t *testing.T) { err := db.Model(u).Association("CloudCredentials").Find(&creds) c.Assert(err, qt.IsNil) c.Check(creds, qt.DeepEquals, []dbmodel.CloudCredential{{ - Model: cred1.Model, - Name: cred1.Name, - CloudName: cred1.CloudName, - OwnerUsername: cred1.OwnerUsername, - AuthType: cred1.AuthType, + Model: cred1.Model, + Name: cred1.Name, + CloudName: cred1.CloudName, + OwnerIdentityName: cred1.OwnerIdentityName, + AuthType: cred1.AuthType, }, { - Model: cred2.Model, - Name: cred2.Name, - CloudName: cred2.CloudName, - OwnerUsername: cred2.OwnerUsername, - AuthType: cred2.AuthType, + Model: cred2.Model, + Name: cred2.Name, + CloudName: cred2.CloudName, + OwnerIdentityName: cred2.OwnerIdentityName, + AuthType: cred2.AuthType, }}) } -func TestUserToJujuUserInfo(t *testing.T) { +func TestIdentityToJujuUserInfo(t *testing.T) { c := qt.New(t) - u := dbmodel.User{ + u := dbmodel.Identity{ Model: gorm.Model{ CreatedAt: time.Now(), }, - Username: "alice@external", + Name: "alice@external", DisplayName: "Alice", } ui := u.ToJujuUserInfo() diff --git a/internal/dbmodel/identitymodeldefaults.go b/internal/dbmodel/identitymodeldefaults.go new file mode 100644 index 000000000..04c966840 --- /dev/null +++ b/internal/dbmodel/identitymodeldefaults.go @@ -0,0 +1,15 @@ +// Copyright 2021 Canonical Ltd. + +package dbmodel + +import "gorm.io/gorm" + +// IdentityModelDefaults holds identities's model defaults. +type IdentityModelDefaults struct { + gorm.Model + + IdentityName string + Identity Identity `gorm:"foreignKey:IdentityName;references:Name"` + + Defaults Map +} diff --git a/internal/dbmodel/model.go b/internal/dbmodel/model.go index bedd8643f..740810dc1 100644 --- a/internal/dbmodel/model.go +++ b/internal/dbmodel/model.go @@ -29,9 +29,9 @@ type Model struct { // UUID is the UUID of the model. UUID sql.NullString - // Owner is user that owns the model. - OwnerUsername string - Owner User `gorm:"foreignkey:OwnerUsername;references:Username"` + // Owner is identity that owns the model. + OwnerIdentityName string + Owner Identity `gorm:"foreignkey:OwnerIdentityName;references:Name"` // Controller is the controller that is hosting the model. ControllerID uint @@ -103,8 +103,8 @@ func (m *Model) SetTag(t names.ModelTag) { } // FromModelUpdate updates the model from the given ModelUpdate. -func (m *Model) SwitchOwner(u *User) { - m.OwnerUsername = u.Username +func (m *Model) SwitchOwner(u *Identity) { + m.OwnerIdentityName = u.Name m.Owner = *u } @@ -120,7 +120,7 @@ func (m *Model) FromJujuModelInfo(info jujuparams.ModelInfo) error { if err != nil { return errors.E(err) } - m.OwnerUsername = ut.Id() + m.OwnerIdentityName = ut.Id() } m.Life = string(info.Life) m.Status.FromJujuEntityStatus(info.Status) @@ -140,7 +140,7 @@ func (m *Model) FromJujuModelInfo(info jujuparams.ModelInfo) error { } m.CloudCredential.Name = cct.Name() m.CloudCredential.CloudName = cct.Cloud().Id() - m.CloudCredential.Owner.Username = cct.Owner().Id() + m.CloudCredential.Owner.Name = cct.Owner().Id() } if info.SLA != nil { @@ -167,7 +167,7 @@ func (m Model) ToJujuModel() jujuparams.Model { jm.Name = m.Name jm.UUID = m.UUID.String jm.Type = m.Type - jm.OwnerTag = names.NewUserTag(m.OwnerUsername).String() + jm.OwnerTag = names.NewUserTag(m.OwnerIdentityName).String() return jm } diff --git a/internal/dbmodel/model_test.go b/internal/dbmodel/model_test.go index 1e1127f92..59d740c65 100644 --- a/internal/dbmodel/model_test.go +++ b/internal/dbmodel/model_test.go @@ -57,7 +57,7 @@ func TestRecreateDeletedModel(t *testing.T) { CloudRegion: cl.Regions[0], CloudCredential: cred, } - c.Check(db.Create(&m2).Error, qt.ErrorMatches, `.*violates unique constraint "models_controller_id_owner_username_name_key".*`) + c.Check(db.Create(&m2).Error, qt.ErrorMatches, `.*violates unique constraint "models_controller_id_owner_identity_name_name_key".*`) c.Assert(db.Delete(&m1).Error, qt.IsNil) c.Check(db.First(&m1).Error, qt.Equals, gorm.ErrRecordNotFound) @@ -214,15 +214,15 @@ func TestToJujuModel(t *testing.T) { String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - OwnerUsername: u.Username, - Owner: u, - Controller: ctl, - CloudRegion: cl.Regions[0], - CloudCredential: cred, - Type: "iaas", - IsController: false, - DefaultSeries: "warty", - Life: constants.ALIVE.String(), + OwnerIdentityName: u.Name, + Owner: u, + Controller: ctl, + CloudRegion: cl.Regions[0], + CloudCredential: cred, + Type: "iaas", + IsController: false, + DefaultSeries: "warty", + Life: constants.ALIVE.String(), Status: dbmodel.Status{ Status: "available", Since: sql.NullTime{ @@ -316,9 +316,9 @@ func TestToJujuModelSummary(t *testing.T) { // initModelEnv initialises a controller, cloud and cloud-credential so // that a model can be created. -func initModelEnv(c *qt.C, db *gorm.DB) (dbmodel.Cloud, dbmodel.CloudCredential, dbmodel.Controller, dbmodel.User) { - u := dbmodel.User{ - Username: "bob@external", +func initModelEnv(c *qt.C, db *gorm.DB) (dbmodel.Cloud, dbmodel.CloudCredential, dbmodel.Controller, dbmodel.Identity) { + u := dbmodel.Identity{ + Name: "bob@external", } c.Assert(db.Create(&u).Error, qt.IsNil) @@ -414,15 +414,15 @@ func TestModelFromJujuModelInfo(t *testing.T) { CloudCredential: dbmodel.CloudCredential{ Name: "test-cred", CloudName: "test-cloud", - Owner: dbmodel.User{ - Username: "bob@external", + Owner: dbmodel.Identity{ + Name: "bob@external", }, }, - OwnerUsername: "bob@external", - Type: "iaas", - IsController: false, - DefaultSeries: "warty", - Life: constants.ALIVE.String(), + OwnerIdentityName: "bob@external", + Type: "iaas", + IsController: false, + DefaultSeries: "warty", + Life: constants.ALIVE.String(), Status: dbmodel.Status{ Status: "available", Since: sql.NullTime{ diff --git a/internal/dbmodel/sql/postgres/1_6.sql b/internal/dbmodel/sql/postgres/1_6.sql new file mode 100644 index 000000000..b93ff7530 --- /dev/null +++ b/internal/dbmodel/sql/postgres/1_6.sql @@ -0,0 +1,37 @@ +-- 1_6.sql is a migration that renames `user` to `identity`. + +-- Note that we don't need to rename underlying indexes/constraints. As Postgres +-- docs states: +-- "When renaming a constraint that has an underlying index, the index is renamed as well." +-- (See https://www.postgresql.org/docs/current/sql-altertable.html) + +-- Renaming tables/columns. +ALTER TABLE IF EXISTS users RENAME TO identities; +ALTER TABLE IF EXISTS identities RENAME COLUMN username TO name; +ALTER TABLE IF EXISTS cloud_credentials RENAME COLUMN owner_username TO owner_identity_name; +ALTER TABLE IF EXISTS cloud_defaults RENAME COLUMN username TO identity_name; +ALTER TABLE IF EXISTS models RENAME COLUMN owner_username TO owner_identity_name; +ALTER TABLE IF EXISTS application_offer_connections RENAME COLUMN username TO identity_name; +ALTER TABLE IF EXISTS user_model_defaults RENAME TO identity_model_defaults; +ALTER TABLE IF EXISTS identity_model_defaults RENAME COLUMN username TO identity_name; +ALTER TABLE IF EXISTS controllers RENAME COLUMN admin_user TO admin_identity_name; +ALTER TABLE IF EXISTS audit_log RENAME COLUMN user_tag TO identity_tag; + +-- Renaming indexes: +ALTER INDEX IF EXISTS users_username_key RENAME TO identities_name_key; +ALTER INDEX IF EXISTS users_pkey RENAME TO identities_pkey; +ALTER INDEX IF EXISTS idx_users_deleted_at RENAME TO idx_identities_deleted_at; +ALTER INDEX IF EXISTS models_controller_id_owner_username_name_key RENAME TO models_controller_id_owner_identity_name_name_key; +ALTER INDEX IF EXISTS user_model_defaults_username_key RENAME TO user_model_defaults_identity_name_key; +ALTER INDEX IF EXISTS user_model_defaults_username_fkey RENAME TO user_model_defaults_identity_name_fkey; +ALTER INDEX IF EXISTS cloud_credentials_cloud_name_owner_username_name_key RENAME TO cloud_credentials_cloud_name_owner_identity_name_name_key; +ALTER INDEX IF EXISTS cloud_defaults_username_cloud_id_region_key RENAME TO cloud_defaults_identity_name_cloud_id_region_key; +ALTER INDEX IF EXISTS idx_audit_log_user_tag RENAME TO idx_audit_log_identity_tag; + +-- We don't need to rename columns in these tables, because they're already +-- dropped in an earlier migration: +-- - user_application_offer_access +-- - user_cloud_access +-- - user_model_access + +UPDATE versions SET major=1, minor=6 WHERE component='jimmdb'; diff --git a/internal/dbmodel/user.go b/internal/dbmodel/user.go deleted file mode 100644 index 8ade75c92..000000000 --- a/internal/dbmodel/user.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2020 Canonical Ltd. - -package dbmodel - -import ( - "database/sql" - - jujuparams "github.com/juju/juju/rpc/params" - "github.com/juju/names/v4" - "gorm.io/gorm" -) - -// A User represents a JIMM user. -type User struct { - gorm.Model - - // Username is the username for the user. This is the juju - // representation of the username (i.e. with an @external suffix). The - // username will have originated at an external identity provider in - // JAAS deployments. - Username string `gorm:"not null;uniqueIndex"` - - // DisplayName is the displayname of the user. - DisplayName string `gorm:"not null"` - - // LastLogin is the time the user last authenticated to the JIMM - // server. LastLogin will only be a valid time if the user has - // authenticated at least once. - LastLogin sql.NullTime - - // Disabled records whether the user has been disabled or not, disabled - // users are not allowed to authenticate. - Disabled bool `gorm:"not null;default:FALSE"` - - // CloudCredentials are the cloud credentials owned by this user. - CloudCredentials []CloudCredential `gorm:"foreignKey:OwnerUsername;references:Username"` -} - -// Tag returns a names.Tag for the user. -func (u User) Tag() names.Tag { - return u.ResourceTag() -} - -// ResourceTag returns a tag for the user. This method -// is intended to be used in places where we expect to see -// a concrete type names.UserTag instead of the -// names.Tag interface. -func (u User) ResourceTag() names.UserTag { - return names.NewUserTag(u.Username) -} - -// SetTag sets the username of the user to the value from the given tag. -func (u *User) SetTag(t names.UserTag) { - u.Username = t.Id() -} - -// ToJujuUserInfo converts a User into a juju UserInfo value. -func (u User) ToJujuUserInfo() jujuparams.UserInfo { - var ui jujuparams.UserInfo - ui.Username = u.Username - ui.DisplayName = u.DisplayName - ui.Access = "" //TODO(Kian) CSS-6040 Handle merging OpenFGA and Postgres information - ui.DateCreated = u.CreatedAt - if u.LastLogin.Valid { - ui.LastConnection = &u.LastLogin.Time - } - return ui -} diff --git a/internal/dbmodel/usermodeldefaults.go b/internal/dbmodel/usermodeldefaults.go deleted file mode 100644 index a7d0b9214..000000000 --- a/internal/dbmodel/usermodeldefaults.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2021 Canonical Ltd. - -package dbmodel - -import "gorm.io/gorm" - -// UserModelDefaults holds user's model defaults. -type UserModelDefaults struct { - gorm.Model - - Username string - User User `gorm:"foreignKey:Username;references:Username"` - - Defaults Map -} diff --git a/internal/dbmodel/version.go b/internal/dbmodel/version.go index 5c3ee485c..56730709f 100644 --- a/internal/dbmodel/version.go +++ b/internal/dbmodel/version.go @@ -20,7 +20,7 @@ const ( // Minor is the minor version of the model described in the dbmodel // package. It should be incremented for any change made to the // database model from database model in a released JIMM. - Minor = 5 + Minor = 6 ) type Version struct { diff --git a/internal/jimm/access.go b/internal/jimm/access.go index e1422852d..8803cb34e 100644 --- a/internal/jimm/access.go +++ b/internal/jimm/access.go @@ -317,9 +317,9 @@ func (j *JIMM) GrantAuditLogAccess(ctx context.Context, user *openfga.User, targ return errors.E(op, errors.CodeUnauthorized, "unauthorized") } - targetUser := &dbmodel.User{} + targetUser := &dbmodel.Identity{} targetUser.SetTag(targetUserTag) - err := j.Database.GetUser(ctx, targetUser) + err := j.Database.GetIdentity(ctx, targetUser) if err != nil { return errors.E(op, err) } @@ -340,9 +340,9 @@ func (j *JIMM) RevokeAuditLogAccess(ctx context.Context, user *openfga.User, tar return errors.E(op, errors.CodeUnauthorized, "unauthorized") } - targetUser := &dbmodel.User{} + targetUser := &dbmodel.Identity{} targetUser.SetTag(targetUserTag) - err := j.Database.GetUser(ctx, targetUser) + err := j.Database.GetIdentity(ctx, targetUser) if err != nil { return errors.E(op, err) } diff --git a/internal/jimm/access_test.go b/internal/jimm/access_test.go index 5bea4814d..cc7f263e0 100644 --- a/internal/jimm/access_test.go +++ b/internal/jimm/access_test.go @@ -34,8 +34,8 @@ func (ta *testAuthenticator) Authenticate(ctx context.Context, req *jujuparams.L return nil, ta.err } return &openfga.User{ - User: &dbmodel.User{ - Username: ta.username, + Identity: &dbmodel.Identity{ + Name: ta.username, }, }, nil } @@ -142,11 +142,11 @@ func TestAuditLogAccess(t *testing.T) { err = j.Database.Migrate(ctx, false) c.Assert(err, qt.IsNil) - adminUser := openfga.NewUser(&dbmodel.User{Username: "alice"}, j.OpenFGAClient) + adminUser := openfga.NewUser(&dbmodel.Identity{Name: "alice"}, j.OpenFGAClient) err = adminUser.SetControllerAccess(ctx, j.ResourceTag(), ofganames.AdministratorRelation) c.Assert(err, qt.IsNil) - user := openfga.NewUser(&dbmodel.User{Username: "bob"}, j.OpenFGAClient) + user := openfga.NewUser(&dbmodel.Identity{Name: "bob"}, j.OpenFGAClient) // admin user can grant other users audit log access. err = j.GrantAuditLogAccess(ctx, adminUser, user.ResourceTag()) diff --git a/internal/jimm/applicationoffer.go b/internal/jimm/applicationoffer.go index 294369d53..c1ebfd1bf 100644 --- a/internal/jimm/applicationoffer.go +++ b/internal/jimm/applicationoffer.go @@ -61,7 +61,7 @@ func (j *JIMM) Offer(ctx context.Context, user *openfga.User, offer AddApplicati } offerURL := crossmodel.OfferURL{ - User: model.OwnerUsername, + User: model.OwnerIdentityName, ModelName: model.Name, // Confusingly the application name in the offer URL is // actually the offer name. @@ -141,8 +141,8 @@ func (j *JIMM) Offer(ctx context.Context, user *openfga.User, offer AddApplicati ownerId = user.Tag().Id() } owner := openfga.NewUser( - &dbmodel.User{ - Username: ownerId, + &dbmodel.Identity{ + Name: ownerId, }, j.OpenFGAClient, ) @@ -155,8 +155,8 @@ func (j *JIMM) Offer(ctx context.Context, user *openfga.User, offer AddApplicati } everyone := openfga.NewUser( - &dbmodel.User{ - Username: ofganames.EveryoneUser, + &dbmodel.Identity{ + Name: ofganames.EveryoneUser, }, j.OpenFGAClient, ) @@ -223,7 +223,7 @@ func (j *JIMM) GetApplicationOfferConsumeDetails(ctx context.Context, user *open // Fix the consume details from the controller to be correct for JAAS. // Filter out any juju local users. - users, err := j.listApplicationOfferUsers(ctx, offer.ResourceTag(), user.User, accessLevel) + users, err := j.listApplicationOfferUsers(ctx, offer.ResourceTag(), user.Identity, accessLevel) if err != nil { return errors.E(op, err) } @@ -251,7 +251,7 @@ func (j *JIMM) GetApplicationOfferConsumeDetails(ctx context.Context, user *open // only see themselves. // TODO(Kian) CSS-6040 Consider changing wherever this function is used to // better encapsulate transforming Postgres/OpenFGA objects into Juju objects. -func (j *JIMM) listApplicationOfferUsers(ctx context.Context, offer names.ApplicationOfferTag, user *dbmodel.User, accessLevel string) ([]jujuparams.OfferUserDetails, error) { +func (j *JIMM) listApplicationOfferUsers(ctx context.Context, offer names.ApplicationOfferTag, user *dbmodel.Identity, accessLevel string) ([]jujuparams.OfferUserDetails, error) { users := make(map[string]string) // we loop through relations in a decreasing order of access for _, relation := range []openfga.Relation{ @@ -266,10 +266,10 @@ func (j *JIMM) listApplicationOfferUsers(ctx context.Context, offer names.Applic for _, user := range usersWithRelation { // if the user is in the users map, it must already have a higher // access level - we skip this user - if users[user.Username] != "" { + if users[user.Name] != "" { continue } - users[user.Username] = ToOfferAccessString(relation) + users[user.Name] = ToOfferAccessString(relation) } } @@ -278,7 +278,7 @@ func (j *JIMM) listApplicationOfferUsers(ctx context.Context, offer names.Applic // non-admin users should only see their own access level // and access level of "everyone@external" - meaning the access // level everybody has. - if accessLevel != string(jujuparams.OfferAdminAccess) && username != ofganames.EveryoneUser && username != user.Username { + if accessLevel != string(jujuparams.OfferAdminAccess) && username != ofganames.EveryoneUser && username != user.Name { continue } userDetails = append(userDetails, jujuparams.OfferUserDetails{ @@ -344,7 +344,7 @@ func (j *JIMM) GetApplicationOffer(ctx context.Context, user *openfga.User, offe if accessLevel != string(jujuparams.OfferAdminAccess) { offerDetails.Connections = nil } - users, err := j.listApplicationOfferUsers(ctx, offer.ResourceTag(), user.User, accessLevel) + users, err := j.listApplicationOfferUsers(ctx, offer.ResourceTag(), user.Identity, accessLevel) if err != nil { return nil, errors.E(op, err) } @@ -358,7 +358,7 @@ func (j *JIMM) GrantOfferAccess(ctx context.Context, user *openfga.User, offerUR const op = errors.Op("jimm.GrantOfferAccess") err := j.doApplicationOfferAdmin(ctx, user, offerURL, func(offer *dbmodel.ApplicationOffer, api API) error { - tUser := openfga.NewUser(&dbmodel.User{Username: ut.Id()}, j.OpenFGAClient) + tUser := openfga.NewUser(&dbmodel.Identity{Name: ut.Id()}, j.OpenFGAClient) currentRelation := tUser.GetApplicationOfferAccess(ctx, offer.ResourceTag()) currentAccessLevel := ToOfferAccessString(currentRelation) targetAccessLevel := determineAccessLevelAfterGrant(currentAccessLevel, string(access)) @@ -415,7 +415,7 @@ func (j *JIMM) RevokeOfferAccess(ctx context.Context, user *openfga.User, offerU const op = errors.Op("jimm.RevokeOfferAccess") err = j.doApplicationOfferAdmin(ctx, user, offerURL, func(offer *dbmodel.ApplicationOffer, api API) error { - tUser := openfga.NewUser(&dbmodel.User{Username: ut.Id()}, j.OpenFGAClient) + tUser := openfga.NewUser(&dbmodel.Identity{Name: ut.Id()}, j.OpenFGAClient) targetRelation, err := ToOfferRelation(string(access)) if err != nil { return errors.E(op, err) @@ -614,7 +614,7 @@ func (j *JIMM) FindApplicationOffers(ctx context.Context, user *openfga.User, fi if accessLevel != "admin" { offerDetails[i].Connections = nil } - users, err := j.listApplicationOfferUsers(ctx, offer.ResourceTag(), user.User, accessLevel) + users, err := j.listApplicationOfferUsers(ctx, offer.ResourceTag(), user.Identity, accessLevel) if err != nil { return nil, errors.E(op, err) } @@ -664,8 +664,8 @@ func (j *JIMM) applicationOfferFilters(ctx context.Context, jujuFilters ...jujup } if len(f.AllowedConsumerTags) > 0 { for _, u := range f.AllowedConsumerTags { - dbUser := dbmodel.User{ - Username: u, + dbUser := dbmodel.Identity{ + Name: u, } ofgaUser := openfga.NewUser(&dbUser, j.OpenFGAClient) offerUUIDs, err := ofgaUser.ListApplicationOffers(ctx, ofganames.ConsumerRelation) @@ -702,7 +702,7 @@ func (j *JIMM) ListApplicationOffers(ctx context.Context, user *openfga.User, fi return nil, errors.E(op, "application offer filter must specify a model name") } if f.OwnerName == "" { - f.OwnerName = user.Username + f.OwnerName = user.Name } m := modelKey{ name: f.ModelName, @@ -726,15 +726,15 @@ func (j *JIMM) ListApplicationOffers(ctx context.Context, user *openfga.User, fi for _, k := range keys { m := dbmodel.Model{ - Name: k.name, - OwnerUsername: k.ownerUsername, + Name: k.name, + OwnerIdentityName: k.ownerUsername, } offerDetails, err := j.listApplicationOffersForModel(ctx, user, &m, modelFilters[k]) if err != nil { return nil, errors.E(op, err) } for _, offer := range offerDetails { - users, err := j.listApplicationOfferUsers(ctx, names.NewApplicationOfferTag(offer.OfferUUID), user.User, "admin") + users, err := j.listApplicationOfferUsers(ctx, names.NewApplicationOfferTag(offer.OfferUUID), user.Identity, "admin") if err != nil { return nil, errors.E(op, err) } diff --git a/internal/jimm/applicationoffer_test.go b/internal/jimm/applicationoffer_test.go index f9011bc92..ee481a3b4 100644 --- a/internal/jimm/applicationoffer_test.go +++ b/internal/jimm/applicationoffer_test.go @@ -31,7 +31,7 @@ import ( ) type environment struct { - users []dbmodel.User + users []dbmodel.Identity clouds []dbmodel.Cloud credentials []dbmodel.CloudCredential controllers []dbmodel.Controller @@ -43,47 +43,47 @@ var initializeEnvironment = func(c *qt.C, ctx context.Context, db *db.Database, env := environment{} // Alice is a model admin, but not a superuser or offer admin. - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) - u1 := dbmodel.User{ - Username: "eve@external", + u1 := dbmodel.Identity{ + Name: "eve@external", } c.Assert(db.DB.Create(&u1).Error, qt.IsNil) - u2 := dbmodel.User{ - Username: "bob@external", + u2 := dbmodel.Identity{ + Name: "bob@external", } c.Assert(db.DB.Create(&u2).Error, qt.IsNil) - u3 := dbmodel.User{ - Username: "fred@external", + u3 := dbmodel.Identity{ + Name: "fred@external", } c.Assert(db.DB.Create(&u3).Error, qt.IsNil) - u4 := dbmodel.User{ - Username: "grant@external", + u4 := dbmodel.Identity{ + Name: "grant@external", } c.Assert(db.DB.Create(&u4).Error, qt.IsNil) // Jane is an offer admin, but not a superuser or model admin. - u5 := dbmodel.User{ - Username: "jane@external", + u5 := dbmodel.Identity{ + Name: "jane@external", } c.Assert(db.DB.Create(&u5).Error, qt.IsNil) // Joe is a superuser, but not a model or offer admin. - u6 := dbmodel.User{ - Username: "joe@external", + u6 := dbmodel.Identity{ + Name: "joe@external", } c.Assert(db.DB.Create(&u6).Error, qt.IsNil) err := openfga.NewUser(&u6, client).SetControllerAccess(ctx, names.NewControllerTag(jimmUUID), ofganames.AdministratorRelation) c.Assert(err, qt.IsNil) - env.users = []dbmodel.User{u, u1, u2, u3, u4, u5, u6} + env.users = []dbmodel.Identity{u, u1, u2, u3, u4, u5, u6} cloud := dbmodel.Cloud{ Name: "test-cloud", @@ -122,10 +122,10 @@ var initializeEnvironment = func(c *qt.C, ctx context.Context, db *db.Database, c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -137,7 +137,7 @@ var initializeEnvironment = func(c *qt.C, ctx context.Context, db *db.Database, String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -197,117 +197,117 @@ func TestRevokeOfferAccess(t *testing.T) { tests := []struct { about string - parameterFunc func(*environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) + parameterFunc func(*environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) setup func(*environment, *openfga.OFGAClient) expectedError string expectedAccessLevel string expectedAccessLevelOnError string // This expectation is meant to ensure there'll be no unpredicted behavior (like changing existing relations) after an error has occurred }{{ about: "admin revokes a model's admin user's admin access - an error returns (relation is indirect)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[1], env.users[0], "test-offer-url", jujuparams.OfferAdminAccess }, expectedError: "failed to unset given access", expectedAccessLevelOnError: "admin", }, { about: "model admin revokes an admin user admin access - user has no access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[1], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "", }, { about: "admin revokes an admin user admin access - user has no access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[5], env.users[1], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "", }, { about: "superuser revokes an admin user admin access - user has no access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[6], env.users[1], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "", }, { about: "admin revokes an admin user consume access - an error returns (no direct relation to remove)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[1], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedError: "failed to unset given access", expectedAccessLevelOnError: "admin", }, { about: "admin revokes an admin user read access - an error returns (no direct relation to remove)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[1], "test-offer-url", jujuparams.OfferReadAccess }, expectedError: "failed to unset given access", expectedAccessLevelOnError: "admin", }, { about: "admin revokes a consume user admin access - an error returns (no direct relation to remove)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[2], "test-offer-url", jujuparams.OfferAdminAccess }, expectedError: "failed to unset given access", expectedAccessLevelOnError: "consume", }, { about: "admin revokes a consume user consume access - user has no access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[2], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedAccessLevel: "", }, { about: "admin revokes a consume user read access - an error returns (no direct relation to remove)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[2], "test-offer-url", jujuparams.OfferReadAccess }, expectedError: "failed to unset given access", expectedAccessLevelOnError: "consume", }, { about: "admin revokes a read user admin access - an error returns (no direct relation to remove)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[3], "test-offer-url", jujuparams.OfferAdminAccess }, expectedError: "failed to unset given access", expectedAccessLevelOnError: "read", }, { about: "admin revokes a read user consume access - an error returns (no direct relation to remove)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[3], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedError: "failed to unset given access", expectedAccessLevelOnError: "read", }, { about: "admin revokes a read user read access - user has no access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[3], "test-offer-url", jujuparams.OfferReadAccess }, expectedAccessLevel: "", }, { about: "admin tries to revoke access to user that does not have access - an error returns", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[4], "test-offer-url", jujuparams.OfferReadAccess }, expectedError: "failed to unset given access", }, { about: "user with consume access cannot revoke access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[2], env.users[3], "test-offer-url", jujuparams.OfferReadAccess }, expectedError: "unauthorized", }, { about: "user with read access cannot revoke access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[3], env.users[3], "test-offer-url", jujuparams.OfferReadAccess }, expectedError: "unauthorized", }, { about: "no such offer", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[3], env.users[3], "no-such-offer", jujuparams.OfferReadAccess }, expectedError: "application offer not found", }, { about: "admin revokes another user (who is direct admin+consumer) their consume access - an error returns (saying user still has access; hinting to use 'jimmctl' for advanced cases)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[1], env.users[4], env.applicationOffers[0].URL, jujuparams.OfferConsumeAccess }, setup: func(env *environment, client *openfga.OFGAClient) { @@ -318,7 +318,7 @@ func TestRevokeOfferAccess(t *testing.T) { expectedAccessLevelOnError: "admin", }, { about: "admin revokes another user (who is direct admin+reader) their read access - an error returns (saying user still has access; hinting to use 'jimmctl' for advanced cases)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[1], env.users[4], env.applicationOffers[0].URL, jujuparams.OfferReadAccess }, setup: func(env *environment, client *openfga.OFGAClient) { @@ -329,7 +329,7 @@ func TestRevokeOfferAccess(t *testing.T) { expectedAccessLevelOnError: "admin", }, { about: "admin revokes another user (who is direct consumer+reader) their read access - an error returns (saying user still has access; hinting to use 'jimmctl' for advanced cases)", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[1], env.users[4], env.applicationOffers[0].URL, jujuparams.OfferReadAccess }, setup: func(env *environment, client *openfga.OFGAClient) { @@ -402,108 +402,108 @@ func TestGrantOfferAccess(t *testing.T) { tests := []struct { about string - parameterFunc func(*environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) + parameterFunc func(*environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) expectedError string expectedAccessLevel string }{{ about: "model admin grants an admin user admin access - admin user keeps admin", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[1], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "admin", }, { about: "model admin grants an admin user consume access - admin user keeps admin", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[1], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedAccessLevel: "admin", }, { about: "model admin grants an admin user read access - admin user keeps admin", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[1], "test-offer-url", jujuparams.OfferReadAccess }, expectedAccessLevel: "admin", }, { about: "model admin grants a consume user admin access - user gets admin access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[2], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "admin", }, { about: "admin grants a consume user admin access - user gets admin access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[5], env.users[2], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "admin", }, { about: "superuser grants a consume user admin access - user gets admin access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[6], env.users[2], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "admin", }, { about: "admin grants a consume user consume access - user keeps consume access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[2], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedAccessLevel: "consume", }, { about: "admin grants a consume user read access - use keeps consume access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[2], "test-offer-url", jujuparams.OfferReadAccess }, expectedAccessLevel: "consume", }, { about: "admin grants a read user admin access - user gets admin access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[3], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "admin", }, { about: "admin grants a read user consume access - user gets consume access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[3], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedAccessLevel: "consume", }, { about: "admin grants a read user read access - user keeps read access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[3], "test-offer-url", jujuparams.OfferReadAccess }, expectedAccessLevel: "read", }, { about: "no such offer", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[3], "no-such-offer", jujuparams.OfferReadAccess }, expectedError: "application offer not found", }, { about: "user with consume rights cannot grant any rights", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[2], env.users[4], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedError: "unauthorized", }, { about: "user with read rights cannot grant any rights", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[3], env.users[4], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedError: "unauthorized", }, { about: "admin grants new user admin access - new user has admin access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[4], "test-offer-url", jujuparams.OfferAdminAccess }, expectedAccessLevel: "admin", }, { about: "admin grants new user consume access - new user has consume access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[4], "test-offer-url", jujuparams.OfferConsumeAccess }, expectedAccessLevel: "consume", }, { about: "admin grants new user read access - new user has read access", - parameterFunc: func(env *environment) (dbmodel.User, dbmodel.User, string, jujuparams.OfferAccessPermission) { + parameterFunc: func(env *environment) (dbmodel.Identity, dbmodel.Identity, string, jujuparams.OfferAccessPermission) { return env.users[0], env.users[4], "test-offer-url", jujuparams.OfferReadAccess }, expectedAccessLevel: "read", @@ -568,18 +568,18 @@ func TestGetApplicationOfferConsumeDetails(t *testing.T) { err = db.Migrate(ctx, false) c.Assert(err, qt.IsNil) - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) - u1 := dbmodel.User{ - Username: "eve@external", + u1 := dbmodel.Identity{ + Name: "eve@external", } c.Assert(db.DB.Create(&u1).Error, qt.IsNil) - u2 := dbmodel.User{ - Username: "bob@external", + u2 := dbmodel.Identity{ + Name: "bob@external", } c.Assert(db.DB.Create(&u2).Error, qt.IsNil) @@ -612,10 +612,10 @@ func TestGetApplicationOfferConsumeDetails(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -626,7 +626,7 @@ func TestGetApplicationOfferConsumeDetails(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -659,8 +659,8 @@ func TestGetApplicationOfferConsumeDetails(t *testing.T) { c.Assert(err, qt.IsNil) everyoneTag := names.NewUserTag(ofganames.EveryoneUser) - uAll := dbmodel.User{ - Username: everyoneTag.Id(), + uAll := dbmodel.Identity{ + Name: everyoneTag.Id(), } c.Assert(db.DB.Create(&uAll).Error, qt.IsNil) // user uAll is reader of the test offer @@ -723,7 +723,7 @@ func TestGetApplicationOfferConsumeDetails(t *testing.T) { tests := []struct { about string - user *dbmodel.User + user *dbmodel.Identity details jujuparams.ConsumeOfferDetails expectedOfferDetails jujuparams.ConsumeOfferDetails expectedError string @@ -942,18 +942,18 @@ func TestGetApplicationOffer(t *testing.T) { err = j.Database.Migrate(ctx, false) c.Assert(err, qt.IsNil) - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) - u1 := dbmodel.User{ - Username: "eve@external", + u1 := dbmodel.Identity{ + Name: "eve@external", } c.Assert(j.Database.DB.Create(&u1).Error, qt.IsNil) - u2 := dbmodel.User{ - Username: "bob@external", + u2 := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&u2).Error, qt.IsNil) @@ -980,10 +980,10 @@ func TestGetApplicationOffer(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -994,7 +994,7 @@ func TestGetApplicationOffer(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1036,7 +1036,7 @@ func TestGetApplicationOffer(t *testing.T) { ApplicationOfferID: 1, SourceModelTag: "test-model-src", RelationID: 1, - Username: "unknown", + IdentityName: "unknown", Endpoint: "test-endpoint", }}, } @@ -1053,7 +1053,7 @@ func TestGetApplicationOffer(t *testing.T) { tests := []struct { about string - user *dbmodel.User + user *dbmodel.Identity offerURL string expectedOfferDetails jujuparams.ApplicationOfferAdminDetails expectedError string @@ -1187,7 +1187,7 @@ func TestOffer(t *testing.T) { getApplicationOffer func(context.Context, *jujuparams.ApplicationOfferAdminDetails) error grantApplicationOfferAccess func(context.Context, string, names.UserTag, jujuparams.OfferAccessPermission) error offer func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error - createEnv func(*qt.C, db.Database, *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) + createEnv func(*qt.C, db.Database, *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) }{{ about: "all ok", getApplicationOffer: func(_ context.Context, details *jujuparams.ApplicationOfferAdminDetails) error { @@ -1241,11 +1241,11 @@ func TestOffer(t *testing.T) { offer: func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error { return nil }, - createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) @@ -1276,10 +1276,10 @@ func TestOffer(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -1290,7 +1290,7 @@ func TestOffer(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1345,7 +1345,7 @@ func TestOffer(t *testing.T) { ApplicationOfferID: 1, SourceModelTag: "test-model-src", RelationID: 1, - Username: "unknown", + IdentityName: "unknown", Endpoint: "test-endpoint", }}, } @@ -1363,11 +1363,11 @@ func TestOffer(t *testing.T) { offer: func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error { return errors.E("a silly error") }, - createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) @@ -1398,10 +1398,10 @@ func TestOffer(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -1412,7 +1412,7 @@ func TestOffer(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1451,9 +1451,9 @@ func TestOffer(t *testing.T) { offer: func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error { return nil }, - createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) @@ -1484,11 +1484,11 @@ func TestOffer(t *testing.T) { offer: func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error { return errors.E(errors.CodeNotFound, "application test-app") }, - createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) @@ -1519,10 +1519,10 @@ func TestOffer(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -1533,7 +1533,7 @@ func TestOffer(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1573,16 +1573,16 @@ func TestOffer(t *testing.T) { offer: func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error { return nil }, - createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) - u1 := dbmodel.User{ - Username: "eve@external", + u1 := dbmodel.Identity{ + Name: "eve@external", } c.Assert(db.DB.Create(&u1).Error, qt.IsNil) @@ -1613,10 +1613,10 @@ func TestOffer(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -1627,7 +1627,7 @@ func TestOffer(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1666,11 +1666,11 @@ func TestOffer(t *testing.T) { offer: func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error { return nil }, - createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) @@ -1701,10 +1701,10 @@ func TestOffer(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -1715,7 +1715,7 @@ func TestOffer(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1754,11 +1754,11 @@ func TestOffer(t *testing.T) { offer: func(context.Context, crossmodel.OfferURL, jujuparams.AddApplicationOffer) error { return errors.E("application offer already exists") }, - createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + createEnv: func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) @@ -1789,10 +1789,10 @@ func TestOffer(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -1803,7 +1803,7 @@ func TestOffer(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1884,11 +1884,11 @@ func TestOfferAssertOpenFGARelationsExist(t *testing.T) { ctx := context.Background() now := time.Now().UTC().Round(time.Millisecond) - createEnv := func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.User, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { + createEnv := func(c *qt.C, db db.Database, client *openfga.OFGAClient) (dbmodel.Identity, jimm.AddApplicationOfferParams, dbmodel.ApplicationOffer, func(*qt.C, error)) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(db.DB.Create(&u).Error, qt.IsNil) @@ -1919,10 +1919,10 @@ func TestOfferAssertOpenFGARelationsExist(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) @@ -1933,7 +1933,7 @@ func TestOfferAssertOpenFGARelationsExist(t *testing.T) { String: "00000000-0000-0000-0000-0000-0000000000003", Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1988,7 +1988,7 @@ func TestOfferAssertOpenFGARelationsExist(t *testing.T) { ApplicationOfferID: 1, SourceModelTag: "test-model-src", RelationID: 1, - Username: "unknown", + IdentityName: "unknown", Endpoint: "test-endpoint", }}, } @@ -2195,42 +2195,42 @@ func TestDestroyOffer(t *testing.T) { tests := []struct { about string - parameterFunc func(*environment) (dbmodel.User, string) + parameterFunc func(*environment) (dbmodel.Identity, string) destroyError string expectedError string }{{ about: "admin allowed to destroy an offer", - parameterFunc: func(env *environment) (dbmodel.User, string) { + parameterFunc: func(env *environment) (dbmodel.Identity, string) { return env.users[0], "test-offer-url" }, }, { about: "user with consume access not allowed to destroy an offer", - parameterFunc: func(env *environment) (dbmodel.User, string) { + parameterFunc: func(env *environment) (dbmodel.Identity, string) { return env.users[2], "test-offer-url" }, expectedError: "unauthorized", }, { about: "user with read access not allowed to destroy an offer", - parameterFunc: func(env *environment) (dbmodel.User, string) { + parameterFunc: func(env *environment) (dbmodel.Identity, string) { return env.users[3], "test-offer-url" }, expectedError: "unauthorized", }, { about: "user without access not allowed to destroy an offer", - parameterFunc: func(env *environment) (dbmodel.User, string) { + parameterFunc: func(env *environment) (dbmodel.Identity, string) { return env.users[4], "test-offer-url" }, expectedError: "unauthorized", }, { about: "offer not found", - parameterFunc: func(env *environment) (dbmodel.User, string) { + parameterFunc: func(env *environment) (dbmodel.Identity, string) { return env.users[0], "no-such-offer" }, expectedError: "application offer not found", }, { about: "controller returns an error", destroyError: "a silly error", - parameterFunc: func(env *environment) (dbmodel.User, string) { + parameterFunc: func(env *environment) (dbmodel.Identity, string) { return env.users[0], "test-offer-url" }, expectedError: "a silly error", @@ -2334,7 +2334,7 @@ func TestUpdateOffer(t *testing.T) { ApplicationOfferID: 1, SourceModelTag: "test-model-src", RelationID: 1, - Username: "unknown", + IdentityName: "unknown", Endpoint: "test-endpoint", }}, Endpoints: []dbmodel.ApplicationOfferRemoteEndpoint{{ @@ -2484,12 +2484,12 @@ func TestFindApplicationOffers(t *testing.T) { tests := []struct { about string - parameterFunc func(*environment) (dbmodel.User, string, []jujuparams.OfferFilter) + parameterFunc func(*environment) (dbmodel.Identity, string, []jujuparams.OfferFilter) expectedError string expectedOffer *dbmodel.ApplicationOffer }{{ about: "find an offer as an offer consumer", - parameterFunc: func(env *environment) (dbmodel.User, string, []jujuparams.OfferFilter) { + parameterFunc: func(env *environment) (dbmodel.Identity, string, []jujuparams.OfferFilter) { return env.users[2], "consume", []jujuparams.OfferFilter{{ OfferName: "test-offer", }} @@ -2497,7 +2497,7 @@ func TestFindApplicationOffers(t *testing.T) { expectedOffer: &expectedOffer, }, { about: "find an offer as model admin", - parameterFunc: func(env *environment) (dbmodel.User, string, []jujuparams.OfferFilter) { + parameterFunc: func(env *environment) (dbmodel.Identity, string, []jujuparams.OfferFilter) { return env.users[0], "admin", []jujuparams.OfferFilter{{ OfferName: "test-offer", }} @@ -2505,7 +2505,7 @@ func TestFindApplicationOffers(t *testing.T) { expectedOffer: &expectedOffer, }, { about: "find an offer as offer admin", - parameterFunc: func(env *environment) (dbmodel.User, string, []jujuparams.OfferFilter) { + parameterFunc: func(env *environment) (dbmodel.Identity, string, []jujuparams.OfferFilter) { return env.users[5], "admin", []jujuparams.OfferFilter{{ OfferName: "test-offer", }} @@ -2513,7 +2513,7 @@ func TestFindApplicationOffers(t *testing.T) { expectedOffer: &expectedOffer, }, { about: "find an offer as superuser", - parameterFunc: func(env *environment) (dbmodel.User, string, []jujuparams.OfferFilter) { + parameterFunc: func(env *environment) (dbmodel.Identity, string, []jujuparams.OfferFilter) { return env.users[6], "admin", []jujuparams.OfferFilter{{ OfferName: "test-offer", }} @@ -2521,14 +2521,14 @@ func TestFindApplicationOffers(t *testing.T) { expectedOffer: &expectedOffer, }, { about: "offer not found", - parameterFunc: func(env *environment) (dbmodel.User, string, []jujuparams.OfferFilter) { + parameterFunc: func(env *environment) (dbmodel.Identity, string, []jujuparams.OfferFilter) { return env.users[0], "admin", []jujuparams.OfferFilter{{ OfferName: "no-such-offer", }} }, }, { about: "user without access cannot find offers", - parameterFunc: func(env *environment) (dbmodel.User, string, []jujuparams.OfferFilter) { + parameterFunc: func(env *environment) (dbmodel.Identity, string, []jujuparams.OfferFilter) { return env.users[4], "", []jujuparams.OfferFilter{{ OfferName: "test-offer", }} @@ -2568,7 +2568,7 @@ func TestFindApplicationOffers(t *testing.T) { details := test.expectedOffer.ToJujuApplicationOfferDetails() if accessLevel != string(jujuparams.OfferAdminAccess) { details.Users = []jujuparams.OfferUserDetails{{ - UserName: user.Username, + UserName: user.Name, Access: accessLevel, }} } else { diff --git a/internal/jimm/audit_log.go b/internal/jimm/audit_log.go index 314768b04..286a3fb9d 100644 --- a/internal/jimm/audit_log.go +++ b/internal/jimm/audit_log.go @@ -45,7 +45,7 @@ func (r DbAuditLogger) newAuditLogEntry(header *rpc.Header) dbmodel.AuditLogEntr ale := dbmodel.AuditLogEntry{ Time: time.Now().UTC().Round(time.Millisecond), MessageId: header.RequestId, - UserTag: r.getUser().String(), + IdentityTag: r.getUser().String(), ConversationId: r.conversationId, } return ale diff --git a/internal/jimm/cloud.go b/internal/jimm/cloud.go index 52abbe0a3..435c13fad 100644 --- a/internal/jimm/cloud.go +++ b/internal/jimm/cloud.go @@ -26,8 +26,8 @@ func (j *JIMM) GetUserCloudAccess(ctx context.Context, user *openfga.User, cloud if accessLevel == ofganames.NoRelation { everyoneTag := names.NewUserTag(ofganames.EveryoneUser) everyone := openfga.NewUser( - &dbmodel.User{ - Username: everyoneTag.Id(), + &dbmodel.Identity{ + Name: everyoneTag.Id(), }, j.OpenFGAClient, ) @@ -99,8 +99,8 @@ func (j *JIMM) ForEachUserCloud(ctx context.Context, user *openfga.User, f func( } // Also include "public" clouds - everyoneDB := dbmodel.User{ - Username: ofganames.EveryoneUser, + everyoneDB := dbmodel.Identity{ + Name: ofganames.EveryoneUser, } everyone := openfga.NewUser(&everyoneDB, j.OpenFGAClient) @@ -396,7 +396,7 @@ func (j *JIMM) AddHostedCloud(ctx context.Context, user *openfga.User, tag names zapctx.Error( ctx, "failed to add user as cloud admin", - zap.String("user", user.Username), + zap.String("user", user.Name), zap.String("cloud", dbCloud.ResourceTag().Id()), zap.Error(err), ) @@ -528,9 +528,9 @@ func (j *JIMM) GrantCloudAccess(ctx context.Context, user *openfga.User, ct name } err = j.doCloudAdmin(ctx, user, ct, func(_ *dbmodel.Cloud, _ API) error { - targetUser := &dbmodel.User{} + targetUser := &dbmodel.Identity{} targetUser.SetTag(ut) - if err := j.Database.GetUser(ctx, targetUser); err != nil { + if err := j.Database.GetIdentity(ctx, targetUser); err != nil { return err } targetOfgaUser := openfga.NewUser(targetUser, j.OpenFGAClient) @@ -593,9 +593,9 @@ func (j *JIMM) RevokeCloudAccess(ctx context.Context, user *openfga.User, ct nam } err = j.doCloudAdmin(ctx, user, ct, func(_ *dbmodel.Cloud, _ API) error { - targetUser := &dbmodel.User{} + targetUser := &dbmodel.Identity{} targetUser.SetTag(ut) - if err := j.Database.GetUser(ctx, targetUser); err != nil { + if err := j.Database.GetIdentity(ctx, targetUser); err != nil { return err } targetOfgaUser := openfga.NewUser(targetUser, j.OpenFGAClient) diff --git a/internal/jimm/cloud_test.go b/internal/jimm/cloud_test.go index 4ba9f2039..426ae6794 100644 --- a/internal/jimm/cloud_test.go +++ b/internal/jimm/cloud_test.go @@ -41,23 +41,23 @@ func TestGetCloud(t *testing.T) { c.Assert(err, qt.IsNil) alice := openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) bob := openfga.NewUser( - &dbmodel.User{ - Username: "bob@external", + &dbmodel.Identity{ + Name: "bob@external", }, client, ) - charlie := openfga.NewUser(&dbmodel.User{Username: "charlie@external"}, client) + charlie := openfga.NewUser(&dbmodel.Identity{Name: "charlie@external"}, client) // daphne is a jimm administrator daphne := openfga.NewUser( - &dbmodel.User{ - Username: "daphne@external", + &dbmodel.Identity{ + Name: "daphne@external", }, client, ) @@ -69,8 +69,8 @@ func TestGetCloud(t *testing.T) { c.Assert(err, qt.IsNil) everyone := openfga.NewUser( - &dbmodel.User{ - Username: ofganames.EveryoneUser, + &dbmodel.Identity{ + Name: ofganames.EveryoneUser, }, client, ) @@ -169,26 +169,26 @@ func TestForEachCloud(t *testing.T) { c.Assert(err, qt.IsNil) alice := openfga.NewUser( - &dbmodel.User{Username: "alice@external"}, + &dbmodel.Identity{Name: "alice@external"}, client, ) bob := openfga.NewUser( - &dbmodel.User{Username: "bob@external"}, + &dbmodel.Identity{Name: "bob@external"}, client, ) charlie := openfga.NewUser( - &dbmodel.User{Username: "charlie@external"}, + &dbmodel.Identity{Name: "charlie@external"}, client, ) daphne := openfga.NewUser( - &dbmodel.User{Username: "daphne@external"}, + &dbmodel.Identity{Name: "daphne@external"}, client, ) daphne.JimmAdmin = true everyone := openfga.NewUser( - &dbmodel.User{ - Username: ofganames.EveryoneUser, + &dbmodel.Identity{ + Name: ofganames.EveryoneUser, }, client, ) diff --git a/internal/jimm/cloudcredential.go b/internal/jimm/cloudcredential.go index c9c6edc03..9e28394ac 100644 --- a/internal/jimm/cloudcredential.go +++ b/internal/jimm/cloudcredential.go @@ -30,7 +30,7 @@ import ( func (j *JIMM) GetCloudCredential(ctx context.Context, user *openfga.User, tag names.CloudCredentialTag) (*dbmodel.CloudCredential, error) { const op = errors.Op("jimm.GetCloudCredential") - if !user.JimmAdmin && user.Username != tag.Owner().Id() { + if !user.JimmAdmin && user.Name != tag.Owner().Id() { return nil, errors.E(op, errors.CodeUnauthorized, "unauthorized") } @@ -48,10 +48,10 @@ func (j *JIMM) GetCloudCredential(ctx context.Context, user *openfga.User, tag n // RevokeCloudCredential checks that the credential with the given path // can be revoked and revokes the credential. -func (j *JIMM) RevokeCloudCredential(ctx context.Context, user *dbmodel.User, tag names.CloudCredentialTag, force bool) error { +func (j *JIMM) RevokeCloudCredential(ctx context.Context, user *dbmodel.Identity, tag names.CloudCredentialTag, force bool) error { const op = errors.Op("jimm.RevokeCloudCredential") - if user.Username != tag.Owner().Id() { + if user.Name != tag.Owner().Id() { return errors.E(op, errors.CodeUnauthorized, "unauthorized") } @@ -140,9 +140,9 @@ func (j *JIMM) UpdateCloudCredential(ctx context.Context, user *openfga.User, ar return result, errors.E(op, errors.CodeUnauthorized, "unauthorized") } // ensure the user we are adding the credential for exists. - var u2 dbmodel.User + var u2 dbmodel.Identity u2.SetTag(args.CredentialTag.Owner()) - if err := j.Database.GetUser(ctx, &u2); err != nil { + if err := j.Database.GetIdentity(ctx, &u2); err != nil { return result, errors.E(op, err) } } @@ -284,7 +284,7 @@ func (j *JIMM) updateControllerCloudCredential( // calling the function will not contain any attributes, // GetCloudCredentialAttributes should be used to retrive the credential // attributes if needed. The given function should not update the database. -func (j *JIMM) ForEachUserCloudCredential(ctx context.Context, u *dbmodel.User, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error { +func (j *JIMM) ForEachUserCloudCredential(ctx context.Context, u *dbmodel.Identity, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error { const op = errors.Op("jimm.ForEachUserCloudCredential") var cloud string @@ -294,7 +294,7 @@ func (j *JIMM) ForEachUserCloudCredential(ctx context.Context, u *dbmodel.User, errStop := errors.E("stop") var iterErr error - err := j.Database.ForEachCloudCredential(ctx, u.Username, cloud, func(cred *dbmodel.CloudCredential) error { + err := j.Database.ForEachCloudCredential(ctx, u.Name, cloud, func(cred *dbmodel.CloudCredential) error { cred.Attributes = nil iterErr = f(cred) if iterErr != nil { @@ -321,11 +321,11 @@ func (j *JIMM) GetCloudCredentialAttributes(ctx context.Context, user *openfga.U if hidden { // Controller superusers cannot read hidden credential attributes. - if user.Username != cred.OwnerUsername { + if user.Name != cred.OwnerIdentityName { return nil, nil, errors.E(op, errors.CodeUnauthorized, "unauthorized") } } else { - if !user.JimmAdmin && user.Username != cred.OwnerUsername { + if !user.JimmAdmin && user.Name != cred.OwnerIdentityName { return nil, nil, errors.E(op, errors.CodeUnauthorized, "unauthorized") } } diff --git a/internal/jimm/cloudcredential_test.go b/internal/jimm/cloudcredential_test.go index 4fff4fe9b..164e22dc4 100644 --- a/internal/jimm/cloudcredential_test.go +++ b/internal/jimm/cloudcredential_test.go @@ -39,13 +39,13 @@ func TestUpdateCloudCredential(t *testing.T) { checkCredentialErrors []error updateCredentialErrors []error jimmAdmin bool - createEnv func(*qt.C, *jimm.JIMM, *openfga.OFGAClient) (*dbmodel.User, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) + createEnv func(*qt.C, *jimm.JIMM, *openfga.OFGAClient) (*dbmodel.Identity, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) }{{ about: "all ok", jimmAdmin: true, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -94,10 +94,10 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -109,7 +109,7 @@ func TestUpdateCloudCredential(t *testing.T) { mi, err := j.AddModel(context.Background(), user, &jimm.ModelCreateArgs{ Name: "test-model", - Owner: names.NewUserTag(u.Username), + Owner: names.NewUserTag(u.Name), Cloud: names.NewCloudTag(cloud.Name), CloudRegion: "test-region-1", CloudCredential: names.NewCloudCredentialTag("test-cloud/alice@external/test-credential-1"), @@ -144,7 +144,7 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.IsNil) // Clear some fields we don't need. // TODO(mhilton) don't fetch these in the first place. - m.Owner = dbmodel.User{} + m.Owner = dbmodel.Identity{} m.Controller = dbmodel.Controller{} m.CloudCredential = dbmodel.CloudCredential{} m.CloudRegion = dbmodel.CloudRegion{} @@ -157,9 +157,9 @@ func TestUpdateCloudCredential(t *testing.T) { about: "update credential error returned by controller", jimmAdmin: true, updateCredentialErrors: []error{nil, errors.E("test error")}, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -209,10 +209,10 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -224,7 +224,7 @@ func TestUpdateCloudCredential(t *testing.T) { _, err = j.AddModel(context.Background(), user, &jimm.ModelCreateArgs{ Name: "test-model", - Owner: names.NewUserTag(u.Username), + Owner: names.NewUserTag(u.Name), Cloud: names.NewCloudTag(cloud.Name), CloudRegion: "test-region-1", CloudCredential: names.NewCloudCredentialTag("test-cloud/alice@external/test-credential-1"), @@ -248,9 +248,9 @@ func TestUpdateCloudCredential(t *testing.T) { jimmAdmin: true, checkCredentialErrors: []error{errors.E("test error")}, updateCredentialErrors: []error{nil}, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -300,17 +300,17 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) _, err = j.AddModel(context.Background(), user, &jimm.ModelCreateArgs{ Name: "test-model", - Owner: names.NewUserTag(u.Username), + Owner: names.NewUserTag(u.Name), Cloud: names.NewCloudTag(cloud.Name), CloudRegion: "test-region-1", CloudCredential: names.NewCloudCredentialTag("test-cloud/alice@external/test-credential-1"), @@ -332,9 +332,9 @@ func TestUpdateCloudCredential(t *testing.T) { }, { about: "user is controller superuser", jimmAdmin: true, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -344,8 +344,8 @@ func TestUpdateCloudCredential(t *testing.T) { err := alice.SetControllerAccess(context.Background(), j.ResourceTag(), ofganames.AdministratorRelation) c.Assert(err, qt.IsNil) - eve := dbmodel.User{ - Username: "eve@external", + eve := dbmodel.Identity{ + Name: "eve@external", } c.Assert(j.Database.DB.Create(&eve).Error, qt.IsNil) @@ -394,10 +394,10 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: eve.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: eve.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -431,7 +431,7 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.IsNil) // Clear some fields we don't need. // TODO(mhilton) don't fetch these in the first place. - m.Owner = dbmodel.User{} + m.Owner = dbmodel.Identity{} m.Controller = dbmodel.Controller{} m.CloudCredential = dbmodel.CloudCredential{} m.CloudRegion = dbmodel.CloudRegion{} @@ -443,7 +443,7 @@ func TestUpdateCloudCredential(t *testing.T) { Name: cloud.Name, Type: cloud.Type, }, - OwnerUsername: eve.Username, + OwnerIdentityName: eve.Name, Attributes: map[string]string{ "key1": "value1", "key2": "value2", @@ -456,9 +456,9 @@ func TestUpdateCloudCredential(t *testing.T) { about: "skip check, which would return an error", checkCredentialErrors: []error{errors.E("test error")}, jimmAdmin: true, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -508,10 +508,10 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -523,7 +523,7 @@ func TestUpdateCloudCredential(t *testing.T) { mi, err := j.AddModel(context.Background(), user, &jimm.ModelCreateArgs{ Name: "test-model", - Owner: names.NewUserTag(u.Username), + Owner: names.NewUserTag(u.Name), Cloud: names.NewCloudTag(cloud.Name), CloudRegion: "test-region-1", CloudCredential: names.NewCloudCredentialTag("test-cloud/alice@external/test-credential-1"), @@ -558,7 +558,7 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.IsNil) // Clear some fields we don't need. // TODO(mhilton) don't fetch these in the first place. - m.Owner = dbmodel.User{} + m.Owner = dbmodel.Identity{} m.Controller = dbmodel.Controller{} m.CloudCredential = dbmodel.CloudCredential{} m.CloudRegion = dbmodel.CloudRegion{} @@ -569,9 +569,9 @@ func TestUpdateCloudCredential(t *testing.T) { }, { about: "skip update", jimmAdmin: true, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, jimm.UpdateCloudCredentialArgs, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -621,10 +621,10 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -635,7 +635,7 @@ func TestUpdateCloudCredential(t *testing.T) { } mi, err := j.AddModel(context.Background(), user, &jimm.ModelCreateArgs{ Name: "test-model", - Owner: names.NewUserTag(u.Username), + Owner: names.NewUserTag(u.Name), Cloud: names.NewCloudTag(cloud.Name), CloudRegion: "test-region-1", CloudCredential: names.NewCloudCredentialTag("test-cloud/alice@external/test-credential-1"), @@ -664,7 +664,7 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(err, qt.IsNil) // Clear some fields we don't need. // TODO(mhilton) don't fetch these in the first place. - m.Owner = dbmodel.User{} + m.Owner = dbmodel.Identity{} m.Controller = dbmodel.Controller{} m.CloudCredential = dbmodel.CloudCredential{} m.CloudRegion = dbmodel.CloudRegion{} @@ -805,9 +805,9 @@ func TestUpdateCloudCredential(t *testing.T) { c.Assert(result[0].ModelName, qt.Equals, "test-model") c.Assert(result[0].ModelUUID, qt.Equals, "00000001-0000-0000-0000-0000-000000000001") credential := dbmodel.CloudCredential{ - Name: expectedCredential.Name, - CloudName: expectedCredential.CloudName, - OwnerUsername: expectedCredential.OwnerUsername, + Name: expectedCredential.Name, + CloudName: expectedCredential.CloudName, + OwnerIdentityName: expectedCredential.OwnerIdentityName, } err = j.Database.GetCloudCredential(ctx, &credential) c.Assert(err, qt.Equals, nil) @@ -872,12 +872,12 @@ func TestRevokeCloudCredential(t *testing.T) { tests := []struct { about string revokeCredentialErrors []error - createEnv func(*qt.C, *jimm.JIMM, *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, string) + createEnv func(*qt.C, *jimm.JIMM, *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, string) }{{ about: "credential revoked", - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -927,10 +927,10 @@ func TestRevokeCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -949,9 +949,9 @@ func TestRevokeCloudCredential(t *testing.T) { Message: "credential not found", Code: jujuparams.CodeNotFound, }}, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -1001,10 +1001,10 @@ func TestRevokeCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -1019,9 +1019,9 @@ func TestRevokeCloudCredential(t *testing.T) { }, }, { about: "credential still used by a model", - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -1071,17 +1071,17 @@ func TestRevokeCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) _, err = j.AddModel(context.Background(), alice, &jimm.ModelCreateArgs{ Name: "test-model", - Owner: names.NewUserTag(u.Username), + Owner: names.NewUserTag(u.Name), Cloud: names.NewCloudTag(cloud.Name), CloudRegion: "test-region-1", CloudCredential: names.NewCloudCredentialTag("test-cloud/alice@external/test-credential-1"), @@ -1094,9 +1094,9 @@ func TestRevokeCloudCredential(t *testing.T) { }, }, { about: "user not owner of credentials - unauthorizer error", - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -1112,9 +1112,9 @@ func TestRevokeCloudCredential(t *testing.T) { }, { about: "error revoking credential on controller", revokeCredentialErrors: []error{errors.E("test error")}, - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -1164,10 +1164,10 @@ func TestRevokeCloudCredential(t *testing.T) { c.Assert(err, qt.Equals, nil) cred := dbmodel.CloudCredential{ - Name: "test-credential-1", - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: "test-credential-1", + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -1281,12 +1281,12 @@ func TestGetCloudCredential(t *testing.T) { tests := []struct { about string revokeCredentialErrors []error - createEnv func(*qt.C, *jimm.JIMM, *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, dbmodel.CloudCredential, string) + createEnv func(*qt.C, *jimm.JIMM, *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, dbmodel.CloudCredential, string) }{{ about: "all ok", - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -1342,8 +1342,8 @@ func TestGetCloudCredential(t *testing.T) { Name: cloud.Name, Type: cloud.Type, }, - OwnerUsername: u.Username, - AuthType: "empty", + OwnerIdentityName: u.Name, + AuthType: "empty", } err = j.Database.SetCloudCredential(context.Background(), &cred) c.Assert(err, qt.Equals, nil) @@ -1354,9 +1354,9 @@ func TestGetCloudCredential(t *testing.T) { }, }, { about: "credential not found", - createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.User, names.CloudCredentialTag, dbmodel.CloudCredential, string) { - u := dbmodel.User{ - Username: "alice@external", + createEnv: func(c *qt.C, j *jimm.JIMM, client *openfga.OFGAClient) (*dbmodel.Identity, names.CloudCredentialTag, dbmodel.CloudCredential, string) { + u := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&u).Error, qt.IsNil) @@ -1690,17 +1690,17 @@ func TestCloudCredentialAttributeStore(t *testing.T) { c.Assert(err, qt.IsNil) cred := dbmodel.CloudCredential{ - OwnerUsername: "alice@external", - Name: "cred-1", - CloudName: "test", + OwnerIdentityName: "alice@external", + Name: "cred-1", + CloudName: "test", } err = j.Database.GetCloudCredential(ctx, &cred) c.Assert(err, qt.IsNil) c.Check(cred, jimmtest.DBObjectEquals, dbmodel.CloudCredential{ - OwnerUsername: "alice@external", - Name: "cred-1", - CloudName: "test", - AuthType: "userpass", + OwnerIdentityName: "alice@external", + Name: "cred-1", + CloudName: "test", + AuthType: "userpass", Cloud: dbmodel.Cloud{ Name: "test", Type: "test-provider", diff --git a/internal/jimm/clouddefaults.go b/internal/jimm/clouddefaults.go index 59420a15b..47c93cd51 100644 --- a/internal/jimm/clouddefaults.go +++ b/internal/jimm/clouddefaults.go @@ -16,7 +16,7 @@ const ( ) // SetModelDefaults writes new default model setting values for the specified cloud/region. -func (j *JIMM) SetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, configs map[string]interface{}) error { +func (j *JIMM) SetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error { const op = errors.Op("jimm.SetModelDefaults") var keys strings.Builder @@ -54,10 +54,10 @@ func (j *JIMM) SetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTa } } err = j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - CloudID: cloud.ID, - Region: region, - Defaults: configs, + IdentityName: user.Name, + CloudID: cloud.ID, + Region: region, + Defaults: configs, }) if err != nil { return errors.E(op, err) @@ -66,11 +66,11 @@ func (j *JIMM) SetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTa } // UnsetModelDefaults resets default model setting values for the specified cloud/region. -func (j *JIMM) UnsetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, keys []string) error { +func (j *JIMM) UnsetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error { const op = errors.Op("jimm.UnsetModelDefaults") defaults := dbmodel.CloudDefaults{ - Username: user.Username, + IdentityName: user.Name, Cloud: dbmodel.Cloud{ Name: cloudTag.Id(), }, @@ -84,7 +84,7 @@ func (j *JIMM) UnsetModelDefaults(ctx context.Context, user *dbmodel.User, cloud } // ModelDefaultsForCloud returns the default config values for the specified cloud. -func (j *JIMM) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) { +func (j *JIMM) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) { const op = errors.Op("jimm.ModelDefaultsForCloud") result := jujuparams.ModelDefaultsResult{ Config: make(map[string]jujuparams.ModelDefaults), diff --git a/internal/jimm/clouddefaults_test.go b/internal/jimm/clouddefaults_test.go index dc97f33f8..56b131a88 100644 --- a/internal/jimm/clouddefaults_test.go +++ b/internal/jimm/clouddefaults_test.go @@ -26,7 +26,7 @@ func TestSetCloudDefaults(t *testing.T) { now := time.Now() type testConfig struct { - user *dbmodel.User + user *dbmodel.Identity cloud names.CloudTag region string defaults map[string]interface{} @@ -41,8 +41,8 @@ func TestSetCloudDefaults(t *testing.T) { }{{ about: "defaults do not exist yet - defaults created", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -62,12 +62,12 @@ func TestSetCloudDefaults(t *testing.T) { cloud.Regions = nil expectedDefaults := dbmodel.CloudDefaults{ - Username: user.Username, - User: user, - CloudID: cloud.ID, - Cloud: cloud, - Region: "test-region", - Defaults: defaults, + IdentityName: user.Name, + Identity: user, + CloudID: cloud.ID, + Cloud: cloud, + Region: "test-region", + Defaults: defaults, } return testConfig{ @@ -81,8 +81,8 @@ func TestSetCloudDefaults(t *testing.T) { }, { about: "set defaults without region - defaults created", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -102,11 +102,11 @@ func TestSetCloudDefaults(t *testing.T) { cloud.Regions = nil expectedDefaults := dbmodel.CloudDefaults{ - Username: user.Username, - User: user, - CloudID: cloud.ID, - Cloud: cloud, - Defaults: defaults, + IdentityName: user.Name, + Identity: user, + CloudID: cloud.ID, + Cloud: cloud, + Defaults: defaults, } return testConfig{ @@ -120,8 +120,8 @@ func TestSetCloudDefaults(t *testing.T) { }, { about: "defaults already exist - defaults updated", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -135,11 +135,11 @@ func TestSetCloudDefaults(t *testing.T) { c.Assert(j.Database.DB.Create(&cloud).Error, qt.IsNil) j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - User: user, - CloudID: cloud.ID, - Cloud: cloud, - Region: cloud.Regions[0].Name, + IdentityName: user.Name, + Identity: user, + CloudID: cloud.ID, + Cloud: cloud, + Region: cloud.Regions[0].Name, Defaults: map[string]interface{}{ "key1": float64(17), "key2": "a test string", @@ -154,12 +154,12 @@ func TestSetCloudDefaults(t *testing.T) { cloud.Regions = nil expectedDefaults := dbmodel.CloudDefaults{ - Username: user.Username, - User: user, - CloudID: cloud.ID, - Cloud: cloud, - Region: "test-region", - Defaults: defaults, + IdentityName: user.Name, + Identity: user, + CloudID: cloud.ID, + Cloud: cloud, + Region: "test-region", + Defaults: defaults, } return testConfig{ @@ -173,8 +173,8 @@ func TestSetCloudDefaults(t *testing.T) { }, { about: "cloudregion does not exist", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -203,8 +203,8 @@ func TestSetCloudDefaults(t *testing.T) { }, { about: "cannot set agent-version", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -248,7 +248,7 @@ func TestSetCloudDefaults(t *testing.T) { if testConfig.expectedError == "" { c.Assert(err, qt.Equals, nil) dbDefaults := dbmodel.CloudDefaults{ - Username: testConfig.expectedDefaults.Username, + IdentityName: testConfig.expectedDefaults.IdentityName, Cloud: dbmodel.Cloud{ Name: testConfig.expectedDefaults.Cloud.Name, }, @@ -271,7 +271,7 @@ func TestUnsetCloudDefaults(t *testing.T) { now := time.Now() type testConfig struct { - user *dbmodel.User + user *dbmodel.Identity cloud names.CloudTag region string keys []string @@ -286,8 +286,8 @@ func TestUnsetCloudDefaults(t *testing.T) { }{{ about: "all ok - keys removed from the defaults map", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -301,9 +301,9 @@ func TestUnsetCloudDefaults(t *testing.T) { c.Assert(j.Database.DB.Create(&cloud).Error, qt.IsNil) err := j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - CloudID: cloud.ID, - Region: cloud.Regions[0].Name, + IdentityName: user.Name, + CloudID: cloud.ID, + Region: cloud.Regions[0].Name, Defaults: map[string]interface{}{ "key1": float64(17), "key2": "a test string", @@ -320,11 +320,11 @@ func TestUnsetCloudDefaults(t *testing.T) { cloud.Regions = nil expectedDefaults := dbmodel.CloudDefaults{ - Username: user.Username, - User: user, - CloudID: cloud.ID, - Cloud: cloud, - Region: "test-region", + IdentityName: user.Name, + Identity: user, + CloudID: cloud.ID, + Cloud: cloud, + Region: "test-region", Defaults: map[string]interface{}{ "key2": "a test string", }, @@ -341,8 +341,8 @@ func TestUnsetCloudDefaults(t *testing.T) { }, { about: "unset without region - keys removed from the defaults map", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -356,8 +356,8 @@ func TestUnsetCloudDefaults(t *testing.T) { c.Assert(j.Database.DB.Create(&cloud).Error, qt.IsNil) err := j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - CloudID: cloud.ID, + IdentityName: user.Name, + CloudID: cloud.ID, Defaults: map[string]interface{}{ "key1": float64(17), "key2": "a test string", @@ -374,11 +374,11 @@ func TestUnsetCloudDefaults(t *testing.T) { cloud.Regions = nil expectedDefaults := dbmodel.CloudDefaults{ - Username: user.Username, - User: user, - CloudID: cloud.ID, - Cloud: cloud, - Region: "", + IdentityName: user.Name, + Identity: user, + CloudID: cloud.ID, + Cloud: cloud, + Region: "", Defaults: map[string]interface{}{ "key2": "a test string", }, @@ -395,8 +395,8 @@ func TestUnsetCloudDefaults(t *testing.T) { }, { about: "cloudregiondefaults not found", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) @@ -441,7 +441,7 @@ func TestUnsetCloudDefaults(t *testing.T) { if testConfig.expectedError == "" { c.Assert(err, qt.Equals, nil) dbDefaults := dbmodel.CloudDefaults{ - Username: testConfig.expectedDefaults.Username, + IdentityName: testConfig.expectedDefaults.IdentityName, Cloud: dbmodel.Cloud{ Name: testConfig.cloud.Id(), }, @@ -471,13 +471,13 @@ func TestModelDefaultsForCloud(t *testing.T) { err := j.Database.Migrate(ctx, true) c.Assert(err, qt.Equals, nil) - user := dbmodel.User{ - Username: "bob@external", + user := dbmodel.Identity{ + Name: "bob@external", } c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) - user1 := dbmodel.User{ - Username: "alice@external", + user1 := dbmodel.Identity{ + Name: "alice@external", } c.Assert(j.Database.DB.Create(&user1).Error, qt.IsNil) @@ -502,9 +502,9 @@ func TestModelDefaultsForCloud(t *testing.T) { c.Assert(j.Database.DB.Create(&cloud2).Error, qt.IsNil) err = j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - CloudID: cloud1.ID, - Region: cloud1.Regions[0].Name, + IdentityName: user.Name, + CloudID: cloud1.ID, + Region: cloud1.Regions[0].Name, Defaults: map[string]interface{}{ "key1": float64(17), "key2": "a test string", @@ -514,9 +514,9 @@ func TestModelDefaultsForCloud(t *testing.T) { c.Assert(err, qt.Equals, nil) err = j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - CloudID: cloud1.ID, - Region: cloud1.Regions[1].Name, + IdentityName: user.Name, + CloudID: cloud1.ID, + Region: cloud1.Regions[1].Name, Defaults: map[string]interface{}{ "key2": "a different string", "key4": float64(42), @@ -525,9 +525,9 @@ func TestModelDefaultsForCloud(t *testing.T) { c.Assert(err, qt.Equals, nil) err = j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - CloudID: cloud2.ID, - Region: cloud2.Regions[0].Name, + IdentityName: user.Name, + CloudID: cloud2.ID, + Region: cloud2.Regions[0].Name, Defaults: map[string]interface{}{ "key2": "a different string", "key4": float64(42), @@ -537,9 +537,9 @@ func TestModelDefaultsForCloud(t *testing.T) { c.Assert(err, qt.Equals, nil) err = j.Database.SetCloudDefaults(ctx, &dbmodel.CloudDefaults{ - Username: user.Username, - CloudID: cloud2.ID, - Region: "", + IdentityName: user.Name, + CloudID: cloud2.ID, + Region: "", Defaults: map[string]interface{}{ "key1": "value", "key4": float64(37), diff --git a/internal/jimm/controller.go b/internal/jimm/controller.go index 505c21165..1b637fa33 100644 --- a/internal/jimm/controller.go +++ b/internal/jimm/controller.go @@ -92,7 +92,7 @@ func (j *JIMM) AddController(ctx context.Context, user *openfga.User, ctl *dbmod credentialsStored := false if j.CredentialStore != nil { - err := j.CredentialStore.PutControllerCredentials(ctx, ctl.Name, ctl.AdminUser, ctl.AdminPassword) + err := j.CredentialStore.PutControllerCredentials(ctx, ctl.Name, ctl.AdminIdentityName, ctl.AdminPassword) if err != nil { return errors.E(op, err, "failed to store controller credentials") } @@ -145,7 +145,7 @@ func (j *JIMM) AddController(ctx context.Context, user *openfga.User, ctl *dbmod // if we already stored controller credentials in CredentialStore // we should not store them plain text in JIMM's DB. if credentialsStored { - ctl.AdminUser = "" + ctl.AdminIdentityName = "" ctl.AdminPassword = "" } if err := tx.AddController(ctx, ctl); err != nil { @@ -169,8 +169,8 @@ func (j *JIMM) AddController(ctx context.Context, user *openfga.User, ctl *dbmod if cloud.ResourceTag().String() == ms.CloudTag { everyoneTag := names.NewUserTag(ofganames.EveryoneUser) everyone := openfga.NewUser( - &dbmodel.User{ - Username: everyoneTag.Id(), + &dbmodel.Identity{ + Name: everyoneTag.Id(), }, j.OpenFGAClient, ) @@ -287,7 +287,7 @@ func (j *JIMM) GetJimmControllerAccess(ctx context.Context, user *openfga.User, // for him/her-self then we return that - either the user // is a JIMM admin (aka "superuser"), or they have a "login" // access level. - if user.Username == tag.Id() { + if user.Name == tag.Id() { if user.JimmAdmin { return "superuser", nil } @@ -300,7 +300,7 @@ func (j *JIMM) GetJimmControllerAccess(ctx context.Context, user *openfga.User, return "", errors.E(op, errors.CodeUnauthorized, "unauthorized") } - var targetUser dbmodel.User + var targetUser dbmodel.Identity targetUser.SetTag(tag) targetUserTag := openfga.NewUser(&targetUser, j.OpenFGAClient) @@ -378,9 +378,9 @@ func (j *JIMM) ImportModel(ctx context.Context, user *openfga.User, controllerNa if ownerTag.IsLocal() { return errors.E(op, "cannot import model from local user, try --owner to switch the model owner") } - ownerUser := dbmodel.User{} + ownerUser := dbmodel.Identity{} ownerUser.SetTag(ownerTag) - err = j.Database.GetUser(ctx, &ownerUser) + err = j.Database.GetIdentity(ctx, &ownerUser) if err != nil { return errors.E(op, err) } @@ -393,7 +393,7 @@ func (j *JIMM) ImportModel(ctx context.Context, user *openfga.User, controllerNa zapctx.Error( ctx, "failed to set model admin", - zap.String("owner", ownerUser.Username), + zap.String("owner", ownerUser.Name), zap.String("model", modelTag.String()), zap.Error(err), ) @@ -412,12 +412,12 @@ func (j *JIMM) ImportModel(ctx context.Context, user *openfga.User, controllerNa // credential against the cloud the model is deployed against. Even using the correct cloud for the // credential is not strictly necessary, but will help prevent the user thinking they can create new // models on the incoming cloud. - allCredentials, err := j.Database.GetUserCloudCredentials(ctx, &ownerUser, cloudTag.Id()) + allCredentials, err := j.Database.GetIdentityCloudCredentials(ctx, &ownerUser, cloudTag.Id()) if err != nil { return errors.E(op, err) } if len(allCredentials) == 0 { - return errors.E(op, errors.CodeNotFound, fmt.Sprintf("Failed to find cloud credential for user %s on cloud %s", ownerUser.Username, cloudTag.Id())) + return errors.E(op, errors.CodeNotFound, fmt.Sprintf("Failed to find cloud credential for user %s on cloud %s", ownerUser.Name, cloudTag.Id())) } cloudCredential := allCredentials[0] @@ -527,7 +527,7 @@ func (j *JIMM) SetControllerConfig(ctx context.Context, user *openfga.User, args } // GetControllerConfig returns jimm's controller config. -func (j *JIMM) GetControllerConfig(ctx context.Context, u *dbmodel.User) (*dbmodel.ControllerConfig, error) { +func (j *JIMM) GetControllerConfig(ctx context.Context, u *dbmodel.Identity) (*dbmodel.ControllerConfig, error) { const op = errors.Op("jimm.GetControllerConfig") config := dbmodel.ControllerConfig{ Name: "jimm", diff --git a/internal/jimm/controller_test.go b/internal/jimm/controller_test.go index ad01ce806..3d94c0e7a 100644 --- a/internal/jimm/controller_test.go +++ b/internal/jimm/controller_test.go @@ -147,8 +147,8 @@ func TestAddController(t *testing.T) { err = j.Database.Migrate(ctx, false) c.Assert(err, qt.IsNil) - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } alice := openfga.NewUser(&u, client) @@ -157,10 +157,10 @@ func TestAddController(t *testing.T) { c.Assert(err, qt.IsNil) ctl1 := dbmodel.Controller{ - Name: "test-controller", - AdminUser: "admin", - AdminPassword: "5ecret", - PublicAddress: "example.com:443", + Name: "test-controller", + AdminIdentityName: "admin", + AdminPassword: "5ecret", + PublicAddress: "example.com:443", } err = j.AddController(context.Background(), alice, &ctl1) c.Assert(err, qt.IsNil) @@ -173,10 +173,10 @@ func TestAddController(t *testing.T) { c.Check(ctl2, qt.CmpEquals(cmpopts.EquateEmpty(), cmpopts.IgnoreTypes(dbmodel.CloudRegion{})), ctl1) ctl3 := dbmodel.Controller{ - Name: "test-controller-2", - AdminUser: "admin", - AdminPassword: "5ecret", - PublicAddress: "example.com:443", + Name: "test-controller-2", + AdminIdentityName: "admin", + AdminPassword: "5ecret", + PublicAddress: "example.com:443", } err = j.AddController(context.Background(), alice, &ctl3) c.Assert(err, qt.IsNil) @@ -315,8 +315,8 @@ func TestAddControllerWithVault(t *testing.T) { err = j.Database.Migrate(ctx, false) c.Assert(err, qt.IsNil) - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } alice := openfga.NewUser(&u, ofgaClient) alice.JimmAdmin = true @@ -325,14 +325,14 @@ func TestAddControllerWithVault(t *testing.T) { c.Assert(err, qt.IsNil) ctl1 := dbmodel.Controller{ - Name: "test-controller", - AdminUser: "admin", - AdminPassword: "5ecret", - PublicAddress: "example.com:443", + Name: "test-controller", + AdminIdentityName: "admin", + AdminPassword: "5ecret", + PublicAddress: "example.com:443", } err = j.AddController(context.Background(), alice, &ctl1) c.Assert(err, qt.IsNil) - c.Assert(ctl1.AdminUser, qt.Equals, "") + c.Assert(ctl1.AdminIdentityName, qt.Equals, "") c.Assert(ctl1.AdminPassword, qt.Equals, "") ctl2 := dbmodel.Controller{ @@ -348,14 +348,14 @@ func TestAddControllerWithVault(t *testing.T) { c.Assert(password, qt.Equals, "5ecret") ctl3 := dbmodel.Controller{ - Name: "test-controller-2", - AdminUser: "admin", - AdminPassword: "5ecretToo", - PublicAddress: "example.com:443", + Name: "test-controller-2", + AdminIdentityName: "admin", + AdminPassword: "5ecretToo", + PublicAddress: "example.com:443", } err = j.AddController(context.Background(), alice, &ctl3) c.Assert(err, qt.IsNil) - c.Assert(ctl3.AdminUser, qt.Equals, "") + c.Assert(ctl3.AdminIdentityName, qt.Equals, "") c.Assert(ctl3.AdminPassword, qt.Equals, "") ctl4 := dbmodel.Controller{ @@ -602,8 +602,8 @@ func TestImportModel(t *testing.T) { String: "00000002-0000-0000-0000-000000000001", Valid: true, }, - Owner: dbmodel.User{ - Username: "alice@external", + Owner: dbmodel.Identity{ + Name: "alice@external", DisplayName: "Alice", }, Controller: dbmodel.Controller{ @@ -690,8 +690,8 @@ func TestImportModel(t *testing.T) { String: "00000002-0000-0000-0000-000000000001", Valid: true, }, - Owner: dbmodel.User{ - Username: "alice@external", + Owner: dbmodel.Identity{ + Name: "alice@external", DisplayName: "Alice", }, Controller: dbmodel.Controller{ @@ -1121,7 +1121,7 @@ func TestGetControllerConfig(t *testing.T) { }) c.Assert(err, qt.Equals, nil) - cfg, err := j.GetControllerConfig(ctx, user.User) + cfg, err := j.GetControllerConfig(ctx, user.Identity) if test.expectedError == "" { c.Assert(err, qt.IsNil) c.Assert(cfg, jimmtest.DBObjectEquals, &test.expectedConfig) @@ -1436,8 +1436,8 @@ func TestInitiateMigration(t *testing.T) { about: "model migration initiated successfully", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) @@ -1461,8 +1461,8 @@ func TestInitiateMigration(t *testing.T) { about: "model not found", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) @@ -1481,8 +1481,8 @@ func TestInitiateMigration(t *testing.T) { about: "InitiateMigration call fails", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) @@ -1502,8 +1502,8 @@ func TestInitiateMigration(t *testing.T) { about: "non-admin-user gets unauthorized error", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "bob@external", + &dbmodel.Identity{ + Name: "bob@external", }, client, ) @@ -1521,8 +1521,8 @@ func TestInitiateMigration(t *testing.T) { about: "invalid model tag", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) @@ -1540,8 +1540,8 @@ func TestInitiateMigration(t *testing.T) { about: "invalid target controller tag", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) @@ -1559,8 +1559,8 @@ func TestInitiateMigration(t *testing.T) { about: "invalid target user tag", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) @@ -1578,8 +1578,8 @@ func TestInitiateMigration(t *testing.T) { about: "invalid macaroon data", user: func(client *openfga.OFGAClient) *openfga.User { return openfga.NewUser( - &dbmodel.User{ - Username: "alice@external", + &dbmodel.Identity{ + Name: "alice@external", }, client, ) diff --git a/internal/jimm/export_test.go b/internal/jimm/export_test.go index b302efcd7..4c52cde7a 100644 --- a/internal/jimm/export_test.go +++ b/internal/jimm/export_test.go @@ -43,6 +43,6 @@ func NewWatcherWithDeltaProcessedChannel(db db.Database, dialer Dialer, pubsub P } } -func (j *JIMM) ListApplicationOfferUsers(ctx context.Context, offer names.ApplicationOfferTag, user *dbmodel.User, accessLevel string) ([]jujuparams.OfferUserDetails, error) { +func (j *JIMM) ListApplicationOfferUsers(ctx context.Context, offer names.ApplicationOfferTag, user *dbmodel.Identity, accessLevel string) ([]jujuparams.OfferUserDetails, error) { return j.listApplicationOfferUsers(ctx, offer, user, accessLevel) } diff --git a/internal/jimm/identitymodeldefaults.go b/internal/jimm/identitymodeldefaults.go new file mode 100644 index 000000000..af2c64e4a --- /dev/null +++ b/internal/jimm/identitymodeldefaults.go @@ -0,0 +1,45 @@ +// Copyright 2020 Canonical Ltd. + +package jimm + +import ( + "context" + + "github.com/canonical/jimm/internal/dbmodel" + "github.com/canonical/jimm/internal/errors" +) + +// SetIdentityModelDefaults writes new default model setting values for the user. +func (j *JIMM) SetIdentityModelDefaults(ctx context.Context, identity *dbmodel.Identity, configs map[string]interface{}) error { + const op = errors.Op("jimm.SetIdentityModelDefaults") + + for k := range configs { + if k == agentVersionKey { + return errors.E(op, errors.CodeBadRequest, `agent-version cannot have a default value`) + } + } + + err := j.Database.SetIdentityModelDefaults(ctx, &dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Defaults: configs, + }) + if err != nil { + return errors.E(op, err) + } + return nil +} + +// IdnetityModelDefaults returns the default config values for the identity. +func (j *JIMM) IdentityModelDefaults(ctx context.Context, identity *dbmodel.Identity) (map[string]interface{}, error) { + const op = errors.Op("jimm.UserModelDefaults") + + defaults := dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + } + err := j.Database.IdentityModelDefaults(ctx, &defaults) + if err != nil { + return nil, errors.E(op, err) + } + + return defaults.Defaults, nil +} diff --git a/internal/jimm/usermodeldefaults_test.go b/internal/jimm/identitymodeldefaults_test.go similarity index 66% rename from internal/jimm/usermodeldefaults_test.go rename to internal/jimm/identitymodeldefaults_test.go index 24b22a18d..c6495e46f 100644 --- a/internal/jimm/usermodeldefaults_test.go +++ b/internal/jimm/identitymodeldefaults_test.go @@ -17,17 +17,17 @@ import ( "github.com/canonical/jimm/internal/jimmtest" ) -func TestSetUserModelDefaults(t *testing.T) { +func TestSetIdentityModelDefaults(t *testing.T) { c := qt.New(t) ctx := context.Background() now := time.Now() type testConfig struct { - user *dbmodel.User + identity *dbmodel.Identity defaults map[string]interface{} expectedError string - expectedDefaults *dbmodel.UserModelDefaults + expectedDefaults *dbmodel.IdentityModelDefaults } tests := []struct { @@ -37,24 +37,24 @@ func TestSetUserModelDefaults(t *testing.T) { }{{ about: "defaults do not exist yet - defaults created", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) defaults := map[string]interface{}{ "key1": float64(42), "key2": "a test string", } - expectedDefaults := dbmodel.UserModelDefaults{ - Username: user.Username, - User: user, - Defaults: defaults, + expectedDefaults := dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Identity: identity, + Defaults: defaults, } return testConfig{ - user: &user, + identity: &identity, defaults: defaults, expectedDefaults: &expectedDefaults, } @@ -62,14 +62,14 @@ func TestSetUserModelDefaults(t *testing.T) { }, { about: "defaults already exist - defaults updated", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) - j.Database.SetUserModelDefaults(ctx, &dbmodel.UserModelDefaults{ - Username: user.Username, - User: user, + j.Database.SetIdentityModelDefaults(ctx, &dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Identity: identity, Defaults: map[string]interface{}{ "key1": float64(17), "key2": "a test string", @@ -82,14 +82,14 @@ func TestSetUserModelDefaults(t *testing.T) { "key3": "a new value", } - expectedDefaults := dbmodel.UserModelDefaults{ - Username: user.Username, - User: user, - Defaults: defaults, + expectedDefaults := dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Identity: identity, + Defaults: defaults, } return testConfig{ - user: &user, + identity: &identity, defaults: defaults, expectedDefaults: &expectedDefaults, } @@ -97,10 +97,10 @@ func TestSetUserModelDefaults(t *testing.T) { }, { about: "cannot set agent-version", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) defaults := map[string]interface{}{ "agent-version": "2.0", @@ -109,7 +109,7 @@ func TestSetUserModelDefaults(t *testing.T) { } return testConfig{ - user: &user, + identity: &identity, defaults: defaults, expectedError: `agent-version cannot have a default value`, } @@ -128,13 +128,13 @@ func TestSetUserModelDefaults(t *testing.T) { testConfig := test.setup(c, j) - err = j.SetUserModelDefaults(ctx, testConfig.user, testConfig.defaults) + err = j.SetIdentityModelDefaults(ctx, testConfig.identity, testConfig.defaults) if testConfig.expectedError == "" { c.Assert(err, qt.Equals, nil) - dbDefaults := dbmodel.UserModelDefaults{ - Username: testConfig.expectedDefaults.Username, + dbDefaults := dbmodel.IdentityModelDefaults{ + IdentityName: testConfig.expectedDefaults.IdentityName, } - err = j.Database.UserModelDefaults(ctx, &dbDefaults) + err = j.Database.IdentityModelDefaults(ctx, &dbDefaults) c.Assert(err, qt.Equals, nil) c.Assert(&dbDefaults, qt.CmpEquals(cmpopts.IgnoreTypes(gorm.Model{})), testConfig.expectedDefaults) } else { @@ -144,14 +144,14 @@ func TestSetUserModelDefaults(t *testing.T) { } } -func TestUserModelDefaults(t *testing.T) { +func TestIdentityModelDefaults(t *testing.T) { c := qt.New(t) ctx := context.Background() now := time.Now() type testConfig struct { - user *dbmodel.User + identity *dbmodel.Identity expectedError string expectedDefaults map[string]interface{} } @@ -163,27 +163,27 @@ func TestUserModelDefaults(t *testing.T) { }{{ about: "defaults do not exist", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) return testConfig{ - user: &user, - expectedError: "usermodeldefaults not found", + identity: &identity, + expectedError: "identitymodeldefaults not found", } }, }, { about: "defaults exist", setup: func(c *qt.C, j *jimm.JIMM) testConfig { - user := dbmodel.User{ - Username: "bob@external", + identity := dbmodel.Identity{ + Name: "bob@external", } - c.Assert(j.Database.DB.Create(&user).Error, qt.IsNil) + c.Assert(j.Database.DB.Create(&identity).Error, qt.IsNil) - j.Database.SetUserModelDefaults(ctx, &dbmodel.UserModelDefaults{ - Username: user.Username, - User: user, + j.Database.SetIdentityModelDefaults(ctx, &dbmodel.IdentityModelDefaults{ + IdentityName: identity.Name, + Identity: identity, Defaults: map[string]interface{}{ "key1": float64(42), "key2": "a changed string", @@ -192,7 +192,7 @@ func TestUserModelDefaults(t *testing.T) { }) return testConfig{ - user: &user, + identity: &identity, expectedDefaults: map[string]interface{}{ "key1": float64(42), "key2": "a changed string", @@ -214,7 +214,7 @@ func TestUserModelDefaults(t *testing.T) { testConfig := test.setup(c, j) - defaults, err := j.UserModelDefaults(ctx, testConfig.user) + defaults, err := j.IdentityModelDefaults(ctx, testConfig.identity) if testConfig.expectedError == "" { c.Assert(err, qt.Equals, nil) c.Assert(defaults, qt.CmpEquals(cmpopts.IgnoreTypes(gorm.Model{})), testConfig.expectedDefaults) diff --git a/internal/jimm/jimm.go b/internal/jimm/jimm.go index 60fb94de3..742a389b0 100644 --- a/internal/jimm/jimm.go +++ b/internal/jimm/jimm.go @@ -517,7 +517,7 @@ func fillMigrationTarget(db db.Database, credStore credentials.CredentialStore, if err != nil { return jujuparams.MigrationTargetInfo{}, 0, err } - adminUser := dbController.AdminUser + adminUser := dbController.AdminIdentityName adminPass := dbController.AdminPassword if adminPass == "" { u, p, err := credStore.GetControllerCredentials(ctx, controllerName) diff --git a/internal/jimm/jimm_test.go b/internal/jimm/jimm_test.go index f55d330f0..4a91813c7 100644 --- a/internal/jimm/jimm_test.go +++ b/internal/jimm/jimm_test.go @@ -45,32 +45,32 @@ func TestFindAuditEvents(t *testing.T) { err = j.Database.Migrate(ctx, true) c.Assert(err, qt.Equals, nil) - admin := openfga.NewUser(&dbmodel.User{Username: "alice@external"}, client) + admin := openfga.NewUser(&dbmodel.Identity{Name: "alice@external"}, client) err = admin.SetControllerAccess(ctx, j.ResourceTag(), ofganames.AdministratorRelation) c.Assert(err, qt.IsNil) - privileged := openfga.NewUser(&dbmodel.User{Username: "bob@external"}, client) + privileged := openfga.NewUser(&dbmodel.Identity{Name: "bob@external"}, client) err = privileged.SetControllerAccess(ctx, j.ResourceTag(), ofganames.AuditLogViewerRelation) c.Assert(err, qt.IsNil) - unprivileged := openfga.NewUser(&dbmodel.User{Username: "eve@external"}, client) + unprivileged := openfga.NewUser(&dbmodel.Identity{Name: "eve@external"}, client) events := []dbmodel.AuditLogEntry{{ Time: now, - UserTag: admin.User.Tag().String(), + IdentityTag: admin.Identity.Tag().String(), FacadeMethod: "Login", }, { Time: now.Add(time.Hour), - UserTag: admin.User.Tag().String(), + IdentityTag: admin.Identity.Tag().String(), FacadeMethod: "AddModel", }, { Time: now.Add(2 * time.Hour), - UserTag: privileged.User.Tag().String(), + IdentityTag: privileged.Identity.Tag().String(), Model: "TestModel", FacadeMethod: "Deploy", }, { Time: now.Add(3 * time.Hour), - UserTag: privileged.User.Tag().String(), + IdentityTag: privileged.Identity.Tag().String(), Model: "TestModel", FacadeMethod: "DestroyModel", }} @@ -98,7 +98,7 @@ func TestFindAuditEvents(t *testing.T) { about: "admin/privileged user is allowed to find audit events by user", users: []*openfga.User{admin, privileged}, filter: db.AuditLogFilter{ - UserTag: admin.Tag().String(), + IdentityTag: admin.Tag().String(), }, expectedEvents: []dbmodel.AuditLogEntry{events[0], events[1]}, }, { @@ -135,13 +135,13 @@ func TestFindAuditEvents(t *testing.T) { about: "admin/privileged user - no events found", users: []*openfga.User{admin, privileged}, filter: db.AuditLogFilter{ - UserTag: "no-such-user", + IdentityTag: "no-such-user", }, }, { about: "unprivileged user is not allowed to access audit events", users: []*openfga.User{unprivileged}, filter: db.AuditLogFilter{ - UserTag: admin.Tag().String(), + IdentityTag: admin.Tag().String(), }, expectedError: "unauthorized", }} @@ -220,7 +220,7 @@ func TestListControllers(t *testing.T) { tests := []struct { about string - user dbmodel.User + user dbmodel.Identity jimmAdmin bool expectedControllers []dbmodel.Controller expectedError string @@ -308,7 +308,7 @@ func TestSetControllerDeprecated(t *testing.T) { tests := []struct { about string - user dbmodel.User + user dbmodel.Identity jimmAdmin bool deprecated bool expectedError string diff --git a/internal/jimm/model.go b/internal/jimm/model.go index 7e9b07d5d..cb17d0817 100644 --- a/internal/jimm/model.go +++ b/internal/jimm/model.go @@ -102,7 +102,7 @@ type modelBuilder struct { name string config map[string]interface{} - owner *dbmodel.User + owner *dbmodel.Identity credential *dbmodel.CloudCredential controller *dbmodel.Controller cloud *dbmodel.Cloud @@ -146,7 +146,7 @@ func (b *modelBuilder) jujuModelCreateArgs() (*jujuparams.ModelCreateArgs, error } // WithOwner returns a builder with the specified owner. -func (b *modelBuilder) WithOwner(owner *dbmodel.User) *modelBuilder { +func (b *modelBuilder) WithOwner(owner *dbmodel.Identity) *modelBuilder { if b.err != nil { return b } @@ -247,9 +247,9 @@ func (b *modelBuilder) WithCloudCredential(credentialTag names.CloudCredentialTa return b } credential := dbmodel.CloudCredential{ - Name: credentialTag.Name(), - CloudName: credentialTag.Cloud().Id(), - OwnerUsername: credentialTag.Owner().Id(), + Name: credentialTag.Name(), + CloudName: credentialTag.Cloud().Id(), + OwnerIdentityName: credentialTag.Owner().Id(), } err := b.jimm.Database.GetCloudCredential(b.ctx, &credential) if err != nil { @@ -315,7 +315,7 @@ func (b *modelBuilder) CreateDatabaseModel() *modelBuilder { err := b.jimm.Database.AddModel(b.ctx, b.model) if err != nil { if errors.ErrorCode(err) == errors.CodeAlreadyExists { - b.err = errors.E(err, fmt.Sprintf("model %s/%s already exists", b.owner.Username, b.name)) + b.err = errors.E(err, fmt.Sprintf("model %s/%s already exists", b.owner.Name, b.name)) return b } else { zapctx.Error(b.ctx, "failed to store model information", zaputil.Error(err)) @@ -339,7 +339,7 @@ func (b *modelBuilder) Cleanup() { // context expiration ctx := context.Background() if derr := b.jimm.Database.DeleteModel(ctx, b.model); derr != nil { - zapctx.Error(ctx, "failed to delete model", zap.String("model", b.model.Name), zap.String("owner", b.model.Owner.Username), zaputil.Error(derr)) + zapctx.Error(ctx, "failed to delete model", zap.String("model", b.model.Name), zap.String("owner", b.model.Owner.Name), zaputil.Error(derr)) } } @@ -403,7 +403,7 @@ func (b *modelBuilder) selectCloudCredentials() error { if b.cloud == nil { return errors.E("cloud not specified") } - credentials, err := b.jimm.Database.GetUserCloudCredentials(b.ctx, b.owner, b.cloud.Name) + credentials, err := b.jimm.Database.GetIdentityCloudCredentials(b.ctx, b.owner, b.cloud.Name) if err != nil { return errors.E(err, "failed to fetch user cloud credentials") } @@ -519,16 +519,16 @@ func (b *modelBuilder) JujuModelInfo() *jujuparams.ModelInfo { func (j *JIMM) AddModel(ctx context.Context, user *openfga.User, args *ModelCreateArgs) (_ *jujuparams.ModelInfo, err error) { const op = errors.Op("jimm.AddModel") - owner := &dbmodel.User{ - Username: args.Owner.Id(), + owner := &dbmodel.Identity{ + Name: args.Owner.Id(), } - err = j.Database.GetUser(ctx, owner) + err = j.Database.GetIdentity(ctx, owner) if err != nil { return nil, errors.E(op, err) } // Only JIMM admins are able to add models on behalf of other users. - if owner.Username != user.Username && !user.JimmAdmin { + if owner.Name != user.Name && !user.JimmAdmin { return nil, errors.E(op, errors.CodeUnauthorized, "unauthorized") } @@ -540,7 +540,7 @@ func (j *JIMM) AddModel(ctx context.Context, user *openfga.User, args *ModelCrea } // fetch user model defaults - userConfig, err := j.UserModelDefaults(ctx, user.User) + userConfig, err := j.IdentityModelDefaults(ctx, user.Identity) if err != nil && errors.ErrorCode(err) != errors.CodeNotFound { return nil, errors.E(op, "failed to fetch cloud defaults") } @@ -549,7 +549,7 @@ func (j *JIMM) AddModel(ctx context.Context, user *openfga.User, args *ModelCrea // fetch cloud defaults if args.Cloud != (names.CloudTag{}) { cloudDefaults := dbmodel.CloudDefaults{ - Username: user.Username, + IdentityName: user.Name, Cloud: dbmodel.Cloud{ Name: args.Cloud.Id(), }, @@ -585,7 +585,7 @@ func (j *JIMM) AddModel(ctx context.Context, user *openfga.User, args *ModelCrea // fetch cloud region defaults if args.Cloud != (names.CloudTag{}) && builder.cloudRegion != "" { cloudRegionDefaults := dbmodel.CloudDefaults{ - Username: user.Username, + IdentityName: user.Name, Cloud: dbmodel.Cloud{ Name: args.Cloud.Id(), }, @@ -706,8 +706,8 @@ func (j *JIMM) ModelInfo(ctx context.Context, user *openfga.User, mt names.Model // Since we are checking user relations in decreasing level of // access privilege, we want to make sure the user has not // already been recorded with a higher access level. - if _, ok := userAccess[u.Username]; !ok { - userAccess[u.Username] = ToModelAccessString(relation) + if _, ok := userAccess[u.Name]; !ok { + userAccess[u.Name] = ToModelAccessString(relation) } } } @@ -721,7 +721,7 @@ func (j *JIMM) ModelInfo(ctx context.Context, user *openfga.User, mt names.Model if !strings.Contains(username, "@") { continue } - if modelAccess == "admin" || username == user.Username || username == ofganames.EveryoneUser { + if modelAccess == "admin" || username == user.Name || username == ofganames.EveryoneUser { users = append(users, jujuparams.ModelUserInfo{ UserName: username, Access: jujuparams.UserAccessPermission(access), @@ -850,9 +850,9 @@ func (j *JIMM) GrantModelAccess(ctx context.Context, user *openfga.User, mt name } err = j.doModelAdmin(ctx, user, mt, func(_ *dbmodel.Model, _ API) error { - targetUser := &dbmodel.User{} + targetUser := &dbmodel.Identity{} targetUser.SetTag(ut) - if err := j.Database.GetUser(ctx, targetUser); err != nil { + if err := j.Database.GetIdentity(ctx, targetUser); err != nil { return err } targetOfgaUser := openfga.NewUser(targetUser, j.OpenFGAClient) @@ -928,9 +928,9 @@ func (j *JIMM) RevokeModelAccess(ctx context.Context, user *openfga.User, mt nam } err = j.doModel(ctx, user, mt, requiredAccess, func(_ *dbmodel.Model, _ API) error { - targetUser := &dbmodel.User{} + targetUser := &dbmodel.Identity{} targetUser.SetTag(ut) - if err := j.Database.GetUser(ctx, targetUser); err != nil { + if err := j.Database.GetIdentity(ctx, targetUser); err != nil { return err } targetOfgaUser := openfga.NewUser(targetUser, j.OpenFGAClient) diff --git a/internal/jimm/model_test.go b/internal/jimm/model_test.go index 5f479878c..79ee6a319 100644 --- a/internal/jimm/model_test.go +++ b/internal/jimm/model_test.go @@ -237,8 +237,8 @@ users: String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - Owner: dbmodel.User{ - Username: "alice@external", + Owner: dbmodel.Identity{ + Name: "alice@external", }, Controller: dbmodel.Controller{ Name: "controller-2", @@ -351,8 +351,8 @@ users: String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - Owner: dbmodel.User{ - Username: "alice@external", + Owner: dbmodel.Identity{ + Name: "alice@external", }, Controller: dbmodel.Controller{ Name: "controller-2", @@ -444,8 +444,8 @@ users: String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - Owner: dbmodel.User{ - Username: "alice@external", + Owner: dbmodel.Identity{ + Name: "alice@external", }, Controller: dbmodel.Controller{ Name: "controller-2", @@ -544,8 +544,8 @@ users: String: "00000001-0000-0000-0000-0000-000000000001", Valid: true, }, - Owner: dbmodel.User{ - Username: "bob@external", + Owner: dbmodel.Identity{ + Name: "bob@external", }, Controller: dbmodel.Controller{ Name: "controller-2", @@ -945,8 +945,8 @@ func TestAddModel(t *testing.T) { isModelAdmin, err := openfga.IsAdministrator( context.Background(), openfga.NewUser( - &dbmodel.User{ - Username: ownerId, + &dbmodel.Identity{ + Name: ownerId, }, client, ), @@ -1296,8 +1296,8 @@ func TestModelInfo(t *testing.T) { env := jimmtest.ParseEnvironment(c, test.env) env.PopulateDBAndPermissions(c, j.ResourceTag(), j.Database, client) - dbUser := &dbmodel.User{ - Username: test.username, + dbUser := &dbmodel.Identity{ + Name: test.username, } user := openfga.NewUser(dbUser, client) @@ -3353,8 +3353,8 @@ var updateModelCredentialTests = []struct { String: "00000002-0000-0000-0000-000000000001", Valid: true, }, - Owner: dbmodel.User{ - Username: "alice@external", + Owner: dbmodel.Identity{ + Name: "alice@external", }, Controller: dbmodel.Controller{ Name: "controller-1", diff --git a/internal/jimm/service_account_test.go b/internal/jimm/service_account_test.go index c86103e4e..924696d60 100644 --- a/internal/jimm/service_account_test.go +++ b/internal/jimm/service_account_test.go @@ -25,8 +25,8 @@ func TestAddServiceAccount(t *testing.T) { } c.Assert(err, qt.IsNil) user := openfga.NewUser( - &dbmodel.User{ - Username: "bob@external", + &dbmodel.Identity{ + Name: "bob@external", DisplayName: "Bob", }, client, @@ -37,8 +37,8 @@ func TestAddServiceAccount(t *testing.T) { err = j.AddServiceAccount(ctx, user, clientID) c.Assert(err, qt.IsNil) userAlice := openfga.NewUser( - &dbmodel.User{ - Username: "alive@external", + &dbmodel.Identity{ + Name: "alive@external", DisplayName: "Alice", }, client, diff --git a/internal/jimm/user.go b/internal/jimm/user.go index e88061a27..43c1708a9 100644 --- a/internal/jimm/user.go +++ b/internal/jimm/user.go @@ -32,10 +32,10 @@ func (j *JIMM) Authenticate(ctx context.Context, req *jujuparams.LoginRequest) ( } err = j.Database.Transaction(func(tx *db.Database) error { - pu := dbmodel.User{ - Username: u.Username, + pu := dbmodel.Identity{ + Name: u.Name, } - if err := tx.GetUser(ctx, &pu); err != nil { + if err := tx.GetIdentity(ctx, &pu); err != nil { return err } u.Model = pu.Model @@ -47,7 +47,7 @@ func (j *JIMM) Authenticate(ctx context.Context, req *jujuparams.LoginRequest) ( } pu.LastLogin.Time = j.Database.DB.Config.NowFunc() pu.LastLogin.Valid = true - return tx.UpdateUser(ctx, &pu) + return tx.UpdateIdentity(ctx, &pu) }) if err != nil { return nil, errors.E(op, err) diff --git a/internal/jimm/user_test.go b/internal/jimm/user_test.go index a085b3bf7..f6ef6c934 100644 --- a/internal/jimm/user_test.go +++ b/internal/jimm/user_test.go @@ -52,15 +52,15 @@ func TestAuthenticate(t *testing.T) { c.Assert(err, qt.IsNil) auth.User = openfga.NewUser( - &dbmodel.User{ - Username: "bob@external", + &dbmodel.Identity{ + Name: "bob@external", DisplayName: "Bob", }, client, ) u, err := j.Authenticate(ctx, nil) c.Assert(err, qt.IsNil) - c.Check(u.Username, qt.Equals, "bob@external") + c.Check(u.Name, qt.Equals, "bob@external") c.Check(u.JimmAdmin, qt.IsFalse) err = auth.User.SetControllerAccess( @@ -72,18 +72,18 @@ func TestAuthenticate(t *testing.T) { u, err = j.Authenticate(ctx, nil) c.Assert(err, qt.IsNil) - c.Check(u.Username, qt.Equals, "bob@external") + c.Check(u.Name, qt.Equals, "bob@external") c.Check(u.JimmAdmin, qt.IsTrue) - u2 := dbmodel.User{ - Username: "bob@external", + u2 := dbmodel.Identity{ + Name: "bob@external", } - err = j.Database.GetUser(ctx, &u2) + err = j.Database.GetIdentity(ctx, &u2) c.Assert(err, qt.IsNil) - c.Check(u2, qt.DeepEquals, dbmodel.User{ + c.Check(u2, qt.DeepEquals, dbmodel.Identity{ Model: u.Model, - Username: "bob@external", + Name: "bob@external", DisplayName: "Bob", LastLogin: sql.NullTime{ Time: now, diff --git a/internal/jimm/usermodeldefaults.go b/internal/jimm/usermodeldefaults.go deleted file mode 100644 index acd334307..000000000 --- a/internal/jimm/usermodeldefaults.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 Canonical Ltd. - -package jimm - -import ( - "context" - - "github.com/canonical/jimm/internal/dbmodel" - "github.com/canonical/jimm/internal/errors" -) - -// SetUserModelDefaults writes new default model setting values for the user. -func (j *JIMM) SetUserModelDefaults(ctx context.Context, user *dbmodel.User, configs map[string]interface{}) error { - const op = errors.Op("jimm.SetUserModelDefaults") - - for k := range configs { - if k == agentVersionKey { - return errors.E(op, errors.CodeBadRequest, `agent-version cannot have a default value`) - } - } - - err := j.Database.SetUserModelDefaults(ctx, &dbmodel.UserModelDefaults{ - Username: user.Username, - Defaults: configs, - }) - if err != nil { - return errors.E(op, err) - } - return nil -} - -// UserModelDefaults returns the default config values for the user. -func (j *JIMM) UserModelDefaults(ctx context.Context, user *dbmodel.User) (map[string]interface{}, error) { - const op = errors.Op("jimm.UserModelDefaults") - - defaults := dbmodel.UserModelDefaults{ - Username: user.Username, - } - err := j.Database.UserModelDefaults(ctx, &defaults) - if err != nil { - return nil, errors.E(op, err) - } - - return defaults.Defaults, nil -} diff --git a/internal/jimm/watcher_test.go b/internal/jimm/watcher_test.go index aeef8e1ce..f917dbcd4 100644 --- a/internal/jimm/watcher_test.go +++ b/internal/jimm/watcher_test.go @@ -386,7 +386,7 @@ var watcherTests = []struct { c.Assert(err, qt.IsNil) // zero any uninteresting associations // TODO(mhilton) don't fetch these in the first place. - model.Owner = dbmodel.User{} + model.Owner = dbmodel.Identity{} model.CloudCredential = dbmodel.CloudCredential{} model.CloudRegion = dbmodel.CloudRegion{} model.Controller = dbmodel.Controller{} @@ -482,7 +482,7 @@ var watcherTests = []struct { c.Assert(err, qt.IsNil) // zero any uninteresting associations // TODO(mhilton) don't fetch these in the first place. - model.Owner = dbmodel.User{} + model.Owner = dbmodel.Identity{} model.CloudCredential = dbmodel.CloudCredential{} model.CloudRegion = dbmodel.CloudRegion{} model.Controller = dbmodel.Controller{} diff --git a/internal/jimmtest/cmp.go b/internal/jimmtest/cmp.go index 8ef28ee08..404bef2fb 100644 --- a/internal/jimmtest/cmp.go +++ b/internal/jimmtest/cmp.go @@ -32,7 +32,7 @@ func ControllerTagCompare(a, b dbmodel.Controller) bool { // determines if two database user objects are equal based on comparing // the Tag values. This is often sufficient where the objects are embedded // in another database object. -func UserTagCompare(a, b dbmodel.User) bool { +func UserTagCompare(a, b dbmodel.Identity) bool { return a.Tag() == b.Tag() } @@ -42,11 +42,11 @@ var DBObjectEquals = qt.CmpEquals( cmpopts.EquateEmpty(), cmpopts.IgnoreTypes(gorm.Model{}), cmpopts.IgnoreFields(dbmodel.Cloud{}, "ID", "CreatedAt", "UpdatedAt"), - cmpopts.IgnoreFields(dbmodel.CloudCredential{}, "CloudName", "OwnerUsername"), + cmpopts.IgnoreFields(dbmodel.CloudCredential{}, "CloudName", "OwnerIdentityName"), cmpopts.IgnoreFields(dbmodel.CloudRegion{}, "CloudName"), cmpopts.IgnoreFields(dbmodel.CloudRegionControllerPriority{}, "CloudRegionID", "ControllerID"), cmpopts.IgnoreFields(dbmodel.Controller{}, "ID"), - cmpopts.IgnoreFields(dbmodel.Model{}, "ID", "CreatedAt", "UpdatedAt", "OwnerUsername", "ControllerID", "CloudRegionID", "CloudCredentialID"), + cmpopts.IgnoreFields(dbmodel.Model{}, "ID", "CreatedAt", "UpdatedAt", "OwnerIdentityName", "ControllerID", "CloudRegionID", "CloudCredentialID"), ) // CmpEquals uses cmp.Diff (see http://godoc.org/github.com/google/go-cmp/cmp#Diff) diff --git a/internal/jimmtest/env.go b/internal/jimmtest/env.go index d1bfcdf5a..4d5e2133c 100644 --- a/internal/jimmtest/env.go +++ b/internal/jimmtest/env.go @@ -167,7 +167,7 @@ func (m Model) addModelRelations(c *qt.C, jimmTag names.ControllerTag, db db.Dat case "read": relation = ofganames.ReaderRelation default: - c.Fatalf("unknown model access: %s %s", dbUser.Username, u.Access) + c.Fatalf("unknown model access: %s %s", dbUser.Name, u.Access) } user := openfga.NewUser(&dbUser, client) err := user.SetModelAccess(context.Background(), m.dbo.ResourceTag(), relation) @@ -180,9 +180,9 @@ func (m Model) addModelRelations(c *qt.C, jimmTag names.ControllerTag, db db.Dat // addControllerRelations adds permissions the model should have and adds permissions for users to the controller. func (ctl Controller) addControllerRelations(c *qt.C, jimmTag names.ControllerTag, db db.Database, client *openfga.OFGAClient) { - if ctl.dbo.AdminUser != "" { - user := openfga.NewUser(&dbmodel.User{ - Username: ctl.dbo.AdminUser, + if ctl.dbo.AdminIdentityName != "" { + user := openfga.NewUser(&dbmodel.Identity{ + Name: ctl.dbo.AdminIdentityName, }, client) err := user.SetControllerAccess(context.Background(), ctl.dbo.ResourceTag(), ofganames.AdministratorRelation) c.Assert(err, qt.IsNil) @@ -253,18 +253,18 @@ type UserDefaults struct { Defaults map[string]interface{} `json:"defaults"` env *Environment - dbo dbmodel.UserModelDefaults + dbo dbmodel.IdentityModelDefaults } -func (cd *UserDefaults) DBObject(c *qt.C, db db.Database) dbmodel.UserModelDefaults { +func (cd *UserDefaults) DBObject(c *qt.C, db db.Database) dbmodel.IdentityModelDefaults { if cd.dbo.ID != 0 { return cd.dbo } - cd.dbo.User = cd.env.User(cd.User).DBObject(c, db) + cd.dbo.Identity = cd.env.User(cd.User).DBObject(c, db) cd.dbo.Defaults = cd.Defaults - err := db.SetUserModelDefaults(context.Background(), &cd.dbo) + err := db.SetIdentityModelDefaults(context.Background(), &cd.dbo) c.Assert(err, qt.IsNil) return cd.dbo } @@ -285,7 +285,7 @@ func (cd *CloudDefaults) DBObject(c *qt.C, db db.Database) dbmodel.CloudDefaults return cd.dbo } - cd.dbo.User = cd.env.User(cd.User).DBObject(c, db) + cd.dbo.Identity = cd.env.User(cd.User).DBObject(c, db) cd.dbo.Cloud = cd.env.Cloud(cd.Cloud).DBObject(c, db) cd.dbo.Region = cd.Region cd.dbo.Defaults = cd.Defaults @@ -355,7 +355,7 @@ func (cc *CloudCredential) DBObject(c *qt.C, db db.Database) dbmodel.CloudCreden cc.dbo.Cloud = cc.env.Cloud(cc.Cloud).DBObject(c, db) cc.dbo.CloudName = cc.dbo.Cloud.Name cc.dbo.Owner = cc.env.User(cc.Owner).DBObject(c, db) - cc.dbo.OwnerUsername = cc.dbo.Owner.Username + cc.dbo.OwnerIdentityName = cc.dbo.Owner.Name cc.dbo.AuthType = cc.AuthType cc.dbo.Attributes = cc.Attributes @@ -387,7 +387,7 @@ func (ctl *Controller) DBObject(c *qt.C, db db.Database) dbmodel.Controller { ctl.dbo.Name = ctl.Name ctl.dbo.UUID = ctl.UUID ctl.dbo.AgentVersion = ctl.AgentVersion - ctl.dbo.AdminUser = ctl.AdminUser + ctl.dbo.AdminIdentityName = ctl.AdminUser ctl.dbo.AdminPassword = ctl.AdminPassword ctl.dbo.CloudName = ctl.Cloud ctl.dbo.CloudRegion = ctl.CloudRegion @@ -484,17 +484,17 @@ type User struct { ControllerAccess string `json:"controller-access"` env *Environment - dbo dbmodel.User + dbo dbmodel.Identity } -func (u *User) DBObject(c *qt.C, db db.Database) dbmodel.User { +func (u *User) DBObject(c *qt.C, db db.Database) dbmodel.Identity { if u.dbo.ID != 0 { return u.dbo } - u.dbo.Username = u.Username + u.dbo.Name = u.Username u.dbo.DisplayName = u.DisplayName - err := db.UpdateUser(context.Background(), &u.dbo) + err := db.UpdateIdentity(context.Background(), &u.dbo) c.Assert(err, qt.IsNil) return u.dbo } diff --git a/internal/jimmtest/jimm_mock.go b/internal/jimmtest/jimm_mock.go index 373ce895d..d8c415107 100644 --- a/internal/jimmtest/jimm_mock.go +++ b/internal/jimmtest/jimm_mock.go @@ -50,7 +50,7 @@ type JIMM struct { ForEachCloud_ func(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error ForEachModel_ func(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error ForEachUserCloud_ func(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error - ForEachUserCloudCredential_ func(ctx context.Context, u *dbmodel.User, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error + ForEachUserCloudCredential_ func(ctx context.Context, u *dbmodel.Identity, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error ForEachUserModel_ func(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error FullModelStatus_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) GetApplicationOffer_ func(ctx context.Context, user *openfga.User, offerURL string) (*jujuparams.ApplicationOfferAdminDetails, error) @@ -58,7 +58,7 @@ type JIMM struct { GetCloud_ func(ctx context.Context, u *openfga.User, tag names.CloudTag) (dbmodel.Cloud, error) GetCloudCredential_ func(ctx context.Context, user *openfga.User, tag names.CloudCredentialTag) (*dbmodel.CloudCredential, error) GetCloudCredentialAttributes_ func(ctx context.Context, u *openfga.User, cred *dbmodel.CloudCredential, hidden bool) (attrs map[string]string, redacted []string, err error) - GetControllerConfig_ func(ctx context.Context, u *dbmodel.User) (*dbmodel.ControllerConfig, error) + GetControllerConfig_ func(ctx context.Context, u *dbmodel.Identity) (*dbmodel.ControllerConfig, error) GetJimmControllerAccess_ func(ctx context.Context, user *openfga.User, tag names.UserTag) (string, error) GetUserCloudAccess_ func(ctx context.Context, user *openfga.User, cloud names.CloudTag) (string, error) GetUserControllerAccess_ func(ctx context.Context, user *openfga.User, controller names.ControllerTag) (string, error) @@ -71,7 +71,7 @@ type JIMM struct { InitiateMigration_ func(ctx context.Context, user *openfga.User, spec jujuparams.MigrationSpec, targetControllerID uint) (jujuparams.InitiateMigrationResult, error) ListApplicationOffers_ func(ctx context.Context, user *openfga.User, filters ...jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetails, error) ListControllers_ func(ctx context.Context, user *openfga.User) ([]dbmodel.Controller, error) - ModelDefaultsForCloud_ func(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) + ModelDefaultsForCloud_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) ModelInfo_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) ModelStatus_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) Offer_ func(ctx context.Context, user *openfga.User, offer jimm.AddApplicationOfferParams) error @@ -84,19 +84,19 @@ type JIMM struct { ResourceTag_ func() names.ControllerTag RevokeAuditLogAccess_ func(ctx context.Context, user *openfga.User, targetUserTag names.UserTag) error RevokeCloudAccess_ func(ctx context.Context, user *openfga.User, ct names.CloudTag, ut names.UserTag, access string) error - RevokeCloudCredential_ func(ctx context.Context, user *dbmodel.User, tag names.CloudCredentialTag, force bool) error + RevokeCloudCredential_ func(ctx context.Context, user *dbmodel.Identity, tag names.CloudCredentialTag, force bool) error RevokeModelAccess_ func(ctx context.Context, user *openfga.User, mt names.ModelTag, ut names.UserTag, access jujuparams.UserAccessPermission) error RevokeOfferAccess_ func(ctx context.Context, user *openfga.User, offerURL string, ut names.UserTag, access jujuparams.OfferAccessPermission) (err error) SetControllerConfig_ func(ctx context.Context, u *openfga.User, args jujuparams.ControllerConfigSet) error SetControllerDeprecated_ func(ctx context.Context, user *openfga.User, controllerName string, deprecated bool) error - SetModelDefaults_ func(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, configs map[string]interface{}) error - SetUserModelDefaults_ func(ctx context.Context, user *dbmodel.User, configs map[string]interface{}) error - UnsetModelDefaults_ func(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, keys []string) error + SetModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error + SetUserModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, configs map[string]interface{}) error + UnsetModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error UpdateApplicationOffer_ func(ctx context.Context, controller *dbmodel.Controller, offerUUID string, removed bool) error UpdateCloud_ func(ctx context.Context, u *openfga.User, ct names.CloudTag, cloud jujuparams.Cloud) error UpdateCloudCredential_ func(ctx context.Context, u *openfga.User, args jimm.UpdateCloudCredentialArgs) ([]jujuparams.UpdateCredentialModelResult, error) UpdateMigratedModel_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error - UserModelDefaults_ func(ctx context.Context, user *dbmodel.User) (map[string]interface{}, error) + UserModelDefaults_ func(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) ValidateModelUpgrade_ func(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error WatchAllModelSummaries_ func(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) } @@ -229,7 +229,7 @@ func (j *JIMM) ForEachUserCloud(ctx context.Context, user *openfga.User, f func( } return j.ForEachUserCloud_(ctx, user, f) } -func (j *JIMM) ForEachUserCloudCredential(ctx context.Context, u *dbmodel.User, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error { +func (j *JIMM) ForEachUserCloudCredential(ctx context.Context, u *dbmodel.Identity, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error { if j.ForEachUserCloudCredential_ == nil { return errors.E(errors.CodeNotImplemented) } @@ -277,7 +277,7 @@ func (j *JIMM) GetCloudCredentialAttributes(ctx context.Context, u *openfga.User } return j.GetCloudCredentialAttributes_(ctx, u, cred, hidden) } -func (j *JIMM) GetControllerConfig(ctx context.Context, u *dbmodel.User) (*dbmodel.ControllerConfig, error) { +func (j *JIMM) GetControllerConfig(ctx context.Context, u *dbmodel.Identity) (*dbmodel.ControllerConfig, error) { if j.GetControllerConfig_ == nil { return nil, errors.E(errors.CodeNotImplemented) } @@ -355,7 +355,7 @@ func (j *JIMM) ListControllers(ctx context.Context, user *openfga.User) ([]dbmod } return j.ListControllers_(ctx, user) } -func (j *JIMM) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) { +func (j *JIMM) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) { if j.ModelDefaultsForCloud_ == nil { return jujuparams.ModelDefaultsResult{}, errors.E(errors.CodeNotImplemented) } @@ -433,7 +433,7 @@ func (j *JIMM) RevokeCloudAccess(ctx context.Context, user *openfga.User, ct nam } return j.RevokeCloudAccess_(ctx, user, ct, ut, access) } -func (j *JIMM) RevokeCloudCredential(ctx context.Context, user *dbmodel.User, tag names.CloudCredentialTag, force bool) error { +func (j *JIMM) RevokeCloudCredential(ctx context.Context, user *dbmodel.Identity, tag names.CloudCredentialTag, force bool) error { if j.RevokeCloudCredential_ == nil { return errors.E(errors.CodeNotImplemented) } @@ -463,19 +463,19 @@ func (j *JIMM) SetControllerDeprecated(ctx context.Context, user *openfga.User, } return j.SetControllerDeprecated_(ctx, user, controllerName, deprecated) } -func (j *JIMM) SetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, configs map[string]interface{}) error { +func (j *JIMM) SetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error { if j.SetModelDefaults_ == nil { return errors.E(errors.CodeNotImplemented) } return j.SetModelDefaults_(ctx, user, cloudTag, region, configs) } -func (j *JIMM) SetUserModelDefaults(ctx context.Context, user *dbmodel.User, configs map[string]interface{}) error { +func (j *JIMM) SetUserModelDefaults(ctx context.Context, user *dbmodel.Identity, configs map[string]interface{}) error { if j.SetUserModelDefaults_ == nil { return errors.E(errors.CodeNotImplemented) } return j.SetUserModelDefaults_(ctx, user, configs) } -func (j *JIMM) UnsetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, keys []string) error { +func (j *JIMM) UnsetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error { if j.UnsetModelDefaults_ == nil { return errors.E(errors.CodeNotImplemented) } @@ -505,7 +505,7 @@ func (j *JIMM) UpdateMigratedModel(ctx context.Context, user *openfga.User, mode } return j.UpdateMigratedModel_(ctx, user, modelTag, targetControllerName) } -func (j *JIMM) UserModelDefaults(ctx context.Context, user *dbmodel.User) (map[string]interface{}, error) { +func (j *JIMM) UserModelDefaults(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) { if j.UserModelDefaults_ == nil { return nil, errors.E(errors.CodeNotImplemented) } diff --git a/internal/jimmtest/suite.go b/internal/jimmtest/suite.go index 76c909d78..4f3602e1b 100644 --- a/internal/jimmtest/suite.go +++ b/internal/jimmtest/suite.go @@ -58,7 +58,7 @@ type JIMMSuite struct { // Authenticator configured. JIMM *jimm.JIMM - AdminUser *dbmodel.User + AdminUser *dbmodel.Identity OFGAClient *openfga.OFGAClient COFGAClient *cofga.Client COFGAParams *cofga.OpenFGAParams @@ -88,11 +88,11 @@ func (s *JIMMSuite) SetUpTest(c *gc.C) { err = s.JIMM.Database.Migrate(ctx, false) c.Assert(err, gc.Equals, nil) - s.AdminUser = &dbmodel.User{ - Username: "alice@external", + s.AdminUser = &dbmodel.Identity{ + Name: "alice@external", LastLogin: db.Now(), } - err = s.JIMM.Database.GetUser(ctx, s.AdminUser) + err = s.JIMM.Database.GetIdentity(ctx, s.AdminUser) c.Assert(err, gc.Equals, nil) adminUser := openfga.NewUser(s.AdminUser, s.OFGAClient) @@ -145,18 +145,18 @@ func (s *JIMMSuite) TearDownTest(c *gc.C) { } } -func (s *JIMMSuite) NewUser(u *dbmodel.User) *openfga.User { +func (s *JIMMSuite) NewUser(u *dbmodel.Identity) *openfga.User { return openfga.NewUser(u, s.OFGAClient) } func (s *JIMMSuite) AddController(c *gc.C, name string, info *api.Info) { ctl := &dbmodel.Controller{ - UUID: info.ControllerUUID, - Name: name, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - CACertificate: info.CACert, - Addresses: nil, + UUID: info.ControllerUUID, + Name: name, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + CACertificate: info.CACert, + Addresses: nil, } ctl.Addresses = make(dbmodel.HostPorts, 0, len(info.Addrs)) for _, addr := range info.Addrs { @@ -175,11 +175,11 @@ func (s *JIMMSuite) AddController(c *gc.C, name string, info *api.Info) { func (s *JIMMSuite) UpdateCloudCredential(c *gc.C, tag names.CloudCredentialTag, cred jujuparams.CloudCredential) { ctx := context.Background() - u := dbmodel.User{ - Username: tag.Owner().Id(), + u := dbmodel.Identity{ + Name: tag.Owner().Id(), } user := openfga.NewUser(&u, s.JIMM.OpenFGAClient) - err := s.JIMM.Database.GetUser(ctx, &u) + err := s.JIMM.Database.GetIdentity(ctx, &u) c.Assert(err, gc.Equals, nil) _, err = s.JIMM.UpdateCloudCredential(ctx, user, jimm.UpdateCloudCredentialArgs{ CredentialTag: tag, @@ -191,10 +191,10 @@ func (s *JIMMSuite) UpdateCloudCredential(c *gc.C, tag names.CloudCredentialTag, func (s *JIMMSuite) AddModel(c *gc.C, owner names.UserTag, name string, cloud names.CloudTag, region string, cred names.CloudCredentialTag) names.ModelTag { ctx := context.Background() - u := dbmodel.User{ - Username: owner.Id(), + u := dbmodel.Identity{ + Name: owner.Id(), } - err := s.JIMM.Database.GetUser(ctx, &u) + err := s.JIMM.Database.GetIdentity(ctx, &u) c.Assert(err, gc.Equals, nil) mi, err := s.JIMM.AddModel(ctx, s.NewUser(&u), &jimm.ModelCreateArgs{ Name: name, diff --git a/internal/jujuapi/access_control.go b/internal/jujuapi/access_control.go index d7757a038..85117f604 100644 --- a/internal/jujuapi/access_control.go +++ b/internal/jujuapi/access_control.go @@ -251,7 +251,7 @@ func resolveTag(jimmUUID string, db *db.Database, tag string) (*ofganames.Tag, e return nil, errors.E("controller not found") } model.ControllerID = controller.ID - model.OwnerUsername = userName + model.OwnerIdentityName = userName model.Name = modelName } @@ -363,7 +363,7 @@ func (r *controllerRoot) CheckRelation(ctx context.Context, req apiparams.CheckR return checkResp, errors.E(op, errors.CodeFailedToParseTupleKey, err) } - userCheckingSelf := parsedTuple.Object.Kind == openfga.UserType && parsedTuple.Object.ID == r.user.Username + userCheckingSelf := parsedTuple.Object.Kind == openfga.UserType && parsedTuple.Object.ID == r.user.Name // Admins can check any relation, non-admins can only check their own. if !(r.user.JimmAdmin || userCheckingSelf) { return checkResp, errors.E(op, errors.CodeUnauthorized, "unauthorized") @@ -469,7 +469,7 @@ func (r *controllerRoot) toJAASTag(ctx context.Context, tag *ofganames.Tag) (str if err != nil { return "", errors.E(err, "failed to fetch model information") } - modelString := names.ModelTagKind + "-" + model.Controller.Name + ":" + model.OwnerUsername + "/" + model.Name + modelString := names.ModelTagKind + "-" + model.Controller.Name + ":" + model.OwnerIdentityName + "/" + model.Name if tag.Relation.String() != "" { modelString = modelString + "#" + tag.Relation.String() } @@ -482,7 +482,7 @@ func (r *controllerRoot) toJAASTag(ctx context.Context, tag *ofganames.Tag) (str if err != nil { return "", errors.E(err, "failed to fetch application offer information") } - aoString := names.ApplicationOfferTagKind + "-" + ao.Model.Controller.Name + ":" + ao.Model.OwnerUsername + "/" + ao.Model.Name + "." + ao.Name + aoString := names.ApplicationOfferTagKind + "-" + ao.Model.Controller.Name + ":" + ao.Model.OwnerIdentityName + "/" + ao.Model.Name + "." + ao.Name if tag.Relation.String() != "" { aoString = aoString + "#" + tag.Relation.String() } diff --git a/internal/jujuapi/access_control_test.go b/internal/jujuapi/access_control_test.go index fda65a63f..f29309f2e 100644 --- a/internal/jujuapi/access_control_test.go +++ b/internal/jujuapi/access_control_test.go @@ -272,9 +272,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { tagTests := []tagTest{ // Test user -> controller by name { - input: tuple{"user-" + user.Username, "administrator", "controller-" + controller.Name}, + input: tuple{"user-" + user.Name, "administrator", "controller-" + controller.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "administrator", "controller:"+controller.UUID, ), @@ -283,9 +283,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, // Test user -> controller jimm { - input: tuple{"user-" + user.Username, "administrator", "controller-jimm"}, + input: tuple{"user-" + user.Name, "administrator", "controller-jimm"}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "administrator", "controller:"+s.JIMM.UUID, ), @@ -294,9 +294,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, // Test user -> controller by UUID { - input: tuple{"user-" + user.Username, "administrator", "controller-" + controller.UUID}, + input: tuple{"user-" + user.Name, "administrator", "controller-" + controller.UUID}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "administrator", "controller:"+controller.UUID, ), @@ -305,9 +305,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, //Test user -> group { - input: tuple{"user-" + user.Username, "member", "group-" + group.Name}, + input: tuple{"user-" + user.Name, "member", "group-" + group.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "member", "group:"+stringGroupID(group.ID), ), @@ -338,9 +338,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, //Test user -> model by name { - input: tuple{"user-" + user.Username, "writer", "model-" + controller.Name + ":" + user.Username + "/" + model.Name}, + input: tuple{"user-" + user.Name, "writer", "model-" + controller.Name + ":" + user.Name + "/" + model.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "writer", "model:"+model.UUID.String, ), @@ -349,9 +349,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, // Test user -> model by UUID { - input: tuple{"user-" + user.Username, "writer", "model-" + model.UUID.String}, + input: tuple{"user-" + user.Name, "writer", "model-" + model.UUID.String}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "writer", "model:"+model.UUID.String, ), @@ -360,9 +360,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, // Test user -> applicationoffer by name { - input: tuple{"user-" + user.Username, "consumer", "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name}, + input: tuple{"user-" + user.Name, "consumer", "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "consumer", "applicationoffer:"+offer.UUID, ), @@ -371,9 +371,9 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, // Test user -> applicationoffer by UUID { - input: tuple{"user-" + user.Username, "consumer", "applicationoffer-" + offer.UUID}, + input: tuple{"user-" + user.Name, "consumer", "applicationoffer-" + offer.UUID}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "consumer", "applicationoffer:"+offer.UUID, ), @@ -404,7 +404,7 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, // Test group -> model by name { - input: tuple{"group-" + group.Name + "#member", "writer", "model-" + controller.Name + ":" + user.Username + "/" + model.Name}, + input: tuple{"group-" + group.Name + "#member", "writer", "model-" + controller.Name + ":" + user.Name + "/" + model.Name}, want: createTuple( "group:"+stringGroupID(group.ID)+"#member", "writer", @@ -426,7 +426,7 @@ func (s *accessControlSuite) TestAddRelation(c *gc.C) { }, // Test group -> applicationoffer by name { - input: tuple{"group-" + group.Name + "#member", "consumer", "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name}, + input: tuple{"group-" + group.Name + "#member", "consumer", "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name}, want: createTuple( "group:"+stringGroupID(group.ID)+"#member", "consumer", @@ -532,9 +532,9 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "administrator", Target: ofganames.ConvertTag(controller.ResourceTag()), }, - toRemove: tuple{"user-" + user.Username, "administrator", "controller-" + controller.Name}, + toRemove: tuple{"user-" + user.Name, "administrator", "controller-" + controller.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "administrator", "controller:"+controller.UUID, ), @@ -548,9 +548,9 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "administrator", Target: ofganames.ConvertTag(controller.ResourceTag()), }, - toRemove: tuple{"user-" + user.Username, "administrator", "controller-" + controller.UUID}, + toRemove: tuple{"user-" + user.Name, "administrator", "controller-" + controller.UUID}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "administrator", "controller:"+controller.UUID, ), @@ -564,9 +564,9 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "member", Target: ofganames.ConvertTag(group.ResourceTag()), }, - toRemove: tuple{"user-" + user.Username, "member", "group-" + group.Name}, + toRemove: tuple{"user-" + user.Name, "member", "group-" + group.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "member", "group:"+stringGroupID(group.ID), ), @@ -596,9 +596,9 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "writer", Target: ofganames.ConvertTag(model.ResourceTag()), }, - toRemove: tuple{"user-" + user.Username, "writer", "model-" + controller.Name + ":" + user.Username + "/" + model.Name}, + toRemove: tuple{"user-" + user.Name, "writer", "model-" + controller.Name + ":" + user.Name + "/" + model.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "writer", "model:"+model.UUID.String, ), @@ -612,9 +612,9 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "writer", Target: ofganames.ConvertTag(model.ResourceTag()), }, - toRemove: tuple{"user-" + user.Username, "writer", "model-" + model.UUID.String}, + toRemove: tuple{"user-" + user.Name, "writer", "model-" + model.UUID.String}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "writer", "model:"+model.UUID.String, ), @@ -628,9 +628,9 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "consumer", Target: ofganames.ConvertTag(offer.ResourceTag()), }, - toRemove: tuple{"user-" + user.Username, "consumer", "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name}, + toRemove: tuple{"user-" + user.Name, "consumer", "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "consumer", "applicationoffer:"+offer.UUID, ), @@ -644,9 +644,9 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "consumer", Target: ofganames.ConvertTag(offer.ResourceTag()), }, - toRemove: tuple{"user-" + user.Username, "consumer", "applicationoffer-" + offer.UUID}, + toRemove: tuple{"user-" + user.Name, "consumer", "applicationoffer-" + offer.UUID}, want: createTuple( - "user:"+user.Username, + "user:"+user.Name, "consumer", "applicationoffer:"+offer.UUID, ), @@ -692,7 +692,7 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "writer", Target: ofganames.ConvertTag(model.ResourceTag()), }, - toRemove: tuple{"group-" + group.Name + "#member", "writer", "model-" + controller.Name + ":" + user.Username + "/" + model.Name}, + toRemove: tuple{"group-" + group.Name + "#member", "writer", "model-" + controller.Name + ":" + user.Name + "/" + model.Name}, want: createTuple( "group:"+stringGroupID(group.ID)+"#member", "writer", @@ -724,7 +724,7 @@ func (s *accessControlSuite) TestRemoveRelation(c *gc.C) { Relation: "consumer", Target: ofganames.ConvertTag(offer.ResourceTag()), }, - toRemove: tuple{"group-" + group.Name + "#member", "consumer", "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name}, + toRemove: tuple{"group-" + group.Name + "#member", "consumer", "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name}, want: createTuple( "group:"+stringGroupID(group.ID)+"#member", "consumer", @@ -803,16 +803,16 @@ func (s *accessControlSuite) TestJAASTag(c *gc.C) { expectedError string }{{ tag: ofganames.ConvertTag(user.ResourceTag()), - expectedJAASTag: "user-" + user.Username, + expectedJAASTag: "user-" + user.Name, }, { tag: ofganames.ConvertTag(controller.ResourceTag()), expectedJAASTag: "controller-" + controller.Name, }, { tag: ofganames.ConvertTag(model.ResourceTag()), - expectedJAASTag: "model-" + controller.Name + ":" + user.Username + "/" + model.Name, + expectedJAASTag: "model-" + controller.Name + ":" + user.Name + "/" + model.Name, }, { tag: ofganames.ConvertTag(applicationOffer.ResourceTag()), - expectedJAASTag: "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + applicationOffer.Name, + expectedJAASTag: "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + applicationOffer.Name, }, { tag: &ofganames.Tag{}, expectedError: "unexpected tag kind: ", @@ -846,7 +846,7 @@ func (s *accessControlSuite) TestListRelationshipTuples(c *gc.C) { Relation: "member", TargetObject: "group-yellow", }, { - Object: "user-" + user.Username, + Object: "user-" + user.Name, Relation: "member", TargetObject: "group-orange", }, { @@ -856,7 +856,7 @@ func (s *accessControlSuite) TestListRelationshipTuples(c *gc.C) { }, { Object: "group-orange#member", Relation: "administrator", - TargetObject: "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + applicationOffer.Name, + TargetObject: "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + applicationOffer.Name, }} err = client.AddRelation(&apiparams.AddRelationRequest{Tuples: tuples}) @@ -869,7 +869,7 @@ func (s *accessControlSuite) TestListRelationshipTuples(c *gc.C) { response, err = client.ListRelationshipTuples(&apiparams.ListRelationshipTuplesRequest{ Tuple: apiparams.RelationshipTuple{ - TargetObject: "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + applicationOffer.Name, + TargetObject: "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + applicationOffer.Name, }, }) c.Assert(err, jc.ErrorIsNil) @@ -918,8 +918,8 @@ func (s *accessControlSuite) TestCheckRelationOfferReaderFlow(c *gc.C) { offerTag := ofganames.ConvertTag(offer.ResourceTag()) // JAAS style keys, to be translated and checked against UUIDs/users/groups - userJAASKey := "user-" + user.Username - offerJAASKey := "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name + userJAASKey := "user-" + user.Name + offerJAASKey := "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name // Test direct relation to an applicationoffer from a user of a group via "reader" relation @@ -989,8 +989,8 @@ func (s *accessControlSuite) TestCheckRelationOfferConsumerFlow(c *gc.C) { offerTag := ofganames.ConvertTag(offer.ResourceTag()) // JAAS style keys, to be translated and checked against UUIDs/users/groups - userJAASKey := "user-" + user.Username - offerJAASKey := "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name + userJAASKey := "user-" + user.Name + offerJAASKey := "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name // Test direct relation to an applicationoffer from a user of a group via "consumer" relation userToGroupMember := openfga.Tuple{ @@ -1060,8 +1060,8 @@ func (s *accessControlSuite) TestCheckRelationModelReaderFlow(c *gc.C) { // Test direct relation to a model from a user of a group via "writer" relation // JAAS style keys, to be translated and checked against UUIDs/users/groups - userJAASKey := "user-" + user.Username - modelJAASKey := "model-" + controller.Name + ":" + user.Username + "/" + model.Name + userJAASKey := "user-" + user.Name + modelJAASKey := "model-" + controller.Name + ":" + user.Name + "/" + model.Name // Test direct relation to a model from a user of a group via "reader" relation userToGroupMember := openfga.Tuple{ @@ -1141,8 +1141,8 @@ func (s *accessControlSuite) TestCheckRelationModelWriterFlow(c *gc.C) { } // Make group members writer of model via member union // JAAS style keys, to be translated and checked against UUIDs/users/groups - userJAASKey := "user-" + user.Username - modelJAASKey := "model-" + controller.Name + ":" + user.Username + "/" + model.Name + userJAASKey := "user-" + user.Name + modelJAASKey := "model-" + controller.Name + ":" + user.Name + "/" + model.Name err := ofgaClient.AddRelation( ctx, @@ -1200,11 +1200,11 @@ func (s *accessControlSuite) TestCheckRelationControllerAdministratorFlow(c *gc. offerTag := ofganames.ConvertTag(offer.ResourceTag()) // JAAS style keys, to be translated and checked against UUIDs/users/groups - userJAASKey := "user-" + user.Username + userJAASKey := "user-" + user.Name groupJAASKey := "group-" + group.Name controllerJAASKey := "controller-" + controller.Name - modelJAASKey := "model-" + controller.Name + ":" + user.Username + "/" + model.Name - offerJAASKey := "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name + modelJAASKey := "model-" + controller.Name + ":" + user.Name + "/" + model.Name + offerJAASKey := "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name // Test the administrator flow of a group user being related to a controller via administrator relation userToGroup := openfga.Tuple{ @@ -1412,7 +1412,7 @@ func (s *accessControlSuite) TestResolveTupleObjectMapsModelUUIDs(c *gc.C) { user, _, controller, model, _, _, _, _, closeClient := createTestControllerEnvironment(ctx, c, s) defer closeClient() - jimmTag := "model-" + controller.Name + ":" + user.Username + "/" + model.Name + "#administrator" + jimmTag := "model-" + controller.Name + ":" + user.Name + "/" + model.Name + "#administrator" tag, err := jujuapi.ResolveTag(s.JIMM.UUID, s.JIMM.DB(), jimmTag) c.Assert(err, gc.IsNil) @@ -1426,7 +1426,7 @@ func (s *accessControlSuite) TestResolveTupleObjectMapsApplicationOffersUUIDs(c user, _, controller, model, offer, _, _, _, closeClient := createTestControllerEnvironment(ctx, c, s) closeClient() - jimmTag := "applicationoffer-" + controller.Name + ":" + user.Username + "/" + model.Name + "." + offer.Name + "#administrator" + jimmTag := "applicationoffer-" + controller.Name + ":" + user.Name + "/" + model.Name + "." + offer.Name + "#administrator" jujuTag, err := jujuapi.ResolveTag(s.JIMM.UUID, s.JIMM.DB(), jimmTag) c.Assert(err, gc.IsNil) @@ -1447,7 +1447,7 @@ func (s *accessControlSuite) TestParseTag(c *gc.C) { user, _, controller, model, _, _, _, _, closeClient := createTestControllerEnvironment(ctx, c, s) defer closeClient() - jimmTag := "model-" + controller.Name + ":" + user.Username + "/" + model.Name + "#administrator" + jimmTag := "model-" + controller.Name + ":" + user.Name + "/" + model.Name + "#administrator" // JIMM tag syntax for models tag, err := jujuapi.ParseTag(ctx, s.JIMM.UUID, s.JIMM.DB(), jimmTag) @@ -1484,7 +1484,7 @@ func (s *accessControlSuite) TestParseTag(c *gc.C) { // TODO(ale8k): Make this an implicit thing on the JIMM suite per test & refactor the current state. // and make the suite argument an interface of the required calls we use here. func createTestControllerEnvironment(ctx context.Context, c *gc.C, s *accessControlSuite) ( - dbmodel.User, + dbmodel.Identity, dbmodel.GroupEntry, dbmodel.Controller, dbmodel.Model, @@ -1501,8 +1501,8 @@ func createTestControllerEnvironment(ctx context.Context, c *gc.C, s *accessCont err = db.GetGroup(ctx, &group) c.Assert(err, gc.IsNil) - u := dbmodel.User{ - Username: petname.Generate(2, "-") + "@external", + u := dbmodel.Identity{ + Name: petname.Generate(2, "-") + "@external", } c.Assert(db.DB.Create(&u).Error, gc.IsNil) @@ -1529,10 +1529,10 @@ func createTestControllerEnvironment(ctx context.Context, c *gc.C, s *accessCont c.Assert(err, gc.IsNil) cred := dbmodel.CloudCredential{ - Name: petname.Generate(2, "-"), - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: petname.Generate(2, "-"), + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } err = db.SetCloudCredential(ctx, &cred) c.Assert(err, gc.IsNil) @@ -1543,7 +1543,7 @@ func createTestControllerEnvironment(ctx context.Context, c *gc.C, s *accessCont String: id.String(), Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -1561,7 +1561,7 @@ func createTestControllerEnvironment(ctx context.Context, c *gc.C, s *accessCont c.Assert(err, gc.IsNil) offerName := petname.Generate(2, "-") - offerURL, err := crossmodel.ParseOfferURL(controller.Name + ":" + u.Username + "/" + model.Name + "." + offerName) + offerURL, err := crossmodel.ParseOfferURL(controller.Name + ":" + u.Name + "/" + model.Name + "." + offerName) c.Assert(err, gc.IsNil) offer := dbmodel.ApplicationOffer{ diff --git a/internal/jujuapi/applicationoffers.go b/internal/jujuapi/applicationoffers.go index 70930a00f..ce95ae114 100644 --- a/internal/jujuapi/applicationoffers.go +++ b/internal/jujuapi/applicationoffers.go @@ -110,7 +110,7 @@ func (r *controllerRoot) getConsumeDetails(ctx context.Context, user *openfga.Us // Ensure the path is normalised. if ourl.User == "" { // If the model owner is not specified use the specified user. - ourl.User = user.Username + ourl.User = user.Name } details := jujuparams.ConsumeOfferDetails{ diff --git a/internal/jujuapi/applicationoffers_test.go b/internal/jujuapi/applicationoffers_test.go index 0d8c52816..01ad65bb9 100644 --- a/internal/jujuapi/applicationoffers_test.go +++ b/internal/jujuapi/applicationoffers_test.go @@ -349,7 +349,7 @@ func (s *applicationOffersSuite) TestDestroyOffers(c *gc.C) { } err = s.JIMM.Database.GetApplicationOffer(context.Background(), &offer) c.Assert(err, gc.Equals, nil) - charlie := openfga.NewUser(&dbmodel.User{Username: "charlie@external"}, s.OFGAClient) + charlie := openfga.NewUser(&dbmodel.Identity{Name: "charlie@external"}, s.OFGAClient) err = charlie.SetApplicationOfferAccess(context.Background(), offer.ResourceTag(), ofganames.ReaderRelation) c.Assert(err, gc.Equals, nil) @@ -452,7 +452,7 @@ func (s *applicationOffersSuite) TestFindApplicationOffers(c *gc.C) { client2 := applicationoffers.NewClient(conn2) offers, err = client2.FindApplicationOffers(crossmodel.ApplicationOfferFilter{ - OwnerName: s.Model.OwnerUsername, + OwnerName: s.Model.OwnerIdentityName, ModelName: s.Model.Name, ApplicationName: "test-app", OfferName: "test-offer1", diff --git a/internal/jujuapi/cloud.go b/internal/jujuapi/cloud.go index 14b523cf0..225a0f9b8 100644 --- a/internal/jujuapi/cloud.go +++ b/internal/jujuapi/cloud.go @@ -133,7 +133,7 @@ func (r *controllerRoot) UserCredentials(ctx context.Context, userclouds jujupar results[i].Error = mapError(errors.E(op, err, errors.CodeBadRequest)) continue } - err = r.jimm.ForEachUserCloudCredential(ctx, user.User, cld, func(c *dbmodel.CloudCredential) error { + err = r.jimm.ForEachUserCloudCredential(ctx, user.Identity, cld, func(c *dbmodel.CloudCredential) error { results[i].Result = append(results[i].Result, c.Tag().String()) return nil }) @@ -169,7 +169,7 @@ func (r *controllerRoot) revokeCredential(ctx context.Context, tag string, force if err != nil { return errors.E(op, err, errors.CodeBadRequest) } - if err := r.jimm.RevokeCloudCredential(ctx, r.user.User, ct, force); err != nil { + if err := r.jimm.RevokeCloudCredential(ctx, r.user.Identity, ct, force); err != nil { return errors.E(op, err) } return nil @@ -333,7 +333,7 @@ func (r *controllerRoot) CredentialContents(ctx context.Context, args jujuparams results := make([]jujuparams.CredentialContentResult, len(args.Credentials)) for i, arg := range args.Credentials { - cct := names.NewCloudCredentialTag(fmt.Sprintf("%s/%s/%s", arg.CloudName, r.user.Username, arg.CredentialName)) + cct := names.NewCloudCredentialTag(fmt.Sprintf("%s/%s/%s", arg.CloudName, r.user.Name, arg.CredentialName)) cred, err := r.jimm.GetCloudCredential(ctx, r.user, cct) if err != nil { results[i].Error = mapError(errors.E(op, err)) @@ -348,7 +348,7 @@ func (r *controllerRoot) CredentialContents(ctx context.Context, args jujuparams return jujuparams.CredentialContentResults{Results: results}, nil } - err := r.jimm.ForEachUserCloudCredential(ctx, r.user.User, names.CloudTag{}, func(c *dbmodel.CloudCredential) error { + err := r.jimm.ForEachUserCloudCredential(ctx, r.user.Identity, names.CloudTag{}, func(c *dbmodel.CloudCredential) error { var result jujuparams.CredentialContentResult var err error result.Result, err = credentialContents(c) diff --git a/internal/jujuapi/cloud_test.go b/internal/jujuapi/cloud_test.go index 878ea0ab8..6105a414f 100644 --- a/internal/jujuapi/cloud_test.go +++ b/internal/jujuapi/cloud_test.go @@ -946,13 +946,13 @@ func (s *cloudSuite) TestListCloudInfo(c *gc.C) { err = client.GrantCloud("bob@external", "add-model", "test-cloud") c.Assert(err, gc.Equals, nil) */ - bob := openfga.NewUser(&dbmodel.User{Username: "bob@external"}, s.OFGAClient) + bob := openfga.NewUser(&dbmodel.Identity{Name: "bob@external"}, s.OFGAClient) err = bob.SetCloudAccess(context.Background(), names.NewCloudTag("test-cloud"), ofganames.CanAddModelRelation) c.Assert(err, gc.Equals, nil) err = bob.SetCloudAccess(context.Background(), names.NewCloudTag(jimmtest.TestCloudName), ofganames.CanAddModelRelation) c.Assert(err, gc.Equals, nil) - alice := openfga.NewUser(&dbmodel.User{Username: "alice@external"}, s.OFGAClient) + alice := openfga.NewUser(&dbmodel.Identity{Name: "alice@external"}, s.OFGAClient) err = alice.SetCloudAccess(context.Background(), names.NewCloudTag(jimmtest.TestCloudName), ofganames.CanAddModelRelation) c.Assert(err, gc.Equals, nil) diff --git a/internal/jujuapi/controller.go b/internal/jujuapi/controller.go index 422d8722c..9db1e2299 100644 --- a/internal/jujuapi/controller.go +++ b/internal/jujuapi/controller.go @@ -237,7 +237,7 @@ func (r *controllerRoot) ControllerConfig(ctx context.Context) (jujuparams.Contr }, nil } - cfg, err := r.jimm.GetControllerConfig(ctx, r.user.User) + cfg, err := r.jimm.GetControllerConfig(ctx, r.user.Identity) if err != nil { return jujuparams.ControllerConfigResult{}, errors.E(op, err) } diff --git a/internal/jujuapi/controllerroot.go b/internal/jujuapi/controllerroot.go index b68115a07..fd3a09578 100644 --- a/internal/jujuapi/controllerroot.go +++ b/internal/jujuapi/controllerroot.go @@ -44,7 +44,7 @@ type JIMM interface { ForEachCloud(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error ForEachModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error ForEachUserCloud(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error - ForEachUserCloudCredential(ctx context.Context, u *dbmodel.User, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error + ForEachUserCloudCredential(ctx context.Context, u *dbmodel.Identity, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error ForEachUserModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error FullModelStatus(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) GetApplicationOffer(ctx context.Context, user *openfga.User, offerURL string) (*jujuparams.ApplicationOfferAdminDetails, error) @@ -52,7 +52,7 @@ type JIMM interface { GetCloud(ctx context.Context, u *openfga.User, tag names.CloudTag) (dbmodel.Cloud, error) GetCloudCredential(ctx context.Context, user *openfga.User, tag names.CloudCredentialTag) (*dbmodel.CloudCredential, error) GetCloudCredentialAttributes(ctx context.Context, u *openfga.User, cred *dbmodel.CloudCredential, hidden bool) (attrs map[string]string, redacted []string, err error) - GetControllerConfig(ctx context.Context, u *dbmodel.User) (*dbmodel.ControllerConfig, error) + GetControllerConfig(ctx context.Context, u *dbmodel.Identity) (*dbmodel.ControllerConfig, error) GetJimmControllerAccess(ctx context.Context, user *openfga.User, tag names.UserTag) (string, error) GetUserCloudAccess(ctx context.Context, user *openfga.User, cloud names.CloudTag) (string, error) GetUserControllerAccess(ctx context.Context, user *openfga.User, controller names.ControllerTag) (string, error) @@ -61,11 +61,12 @@ type JIMM interface { GrantCloudAccess(ctx context.Context, user *openfga.User, ct names.CloudTag, ut names.UserTag, access string) error GrantModelAccess(ctx context.Context, user *openfga.User, mt names.ModelTag, ut names.UserTag, access jujuparams.UserAccessPermission) error GrantOfferAccess(ctx context.Context, u *openfga.User, offerURL string, ut names.UserTag, access jujuparams.OfferAccessPermission) error + IdentityModelDefaults(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) ImportModel(ctx context.Context, user *openfga.User, controllerName string, modelTag names.ModelTag, newOwner string) error - InitiateMigration(ctx context.Context, user *openfga.User, spec jujuparams.MigrationSpec, targetControllerID uint) (jujuparams.InitiateMigrationResult, error) InitiateInternalMigration(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetController string) (jujuparams.InitiateMigrationResult, error) + InitiateMigration(ctx context.Context, user *openfga.User, spec jujuparams.MigrationSpec, targetControllerID uint) (jujuparams.InitiateMigrationResult, error) ListApplicationOffers(ctx context.Context, user *openfga.User, filters ...jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetails, error) - ModelDefaultsForCloud(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) + ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) ModelInfo(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) ModelStatus(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) Offer(ctx context.Context, user *openfga.User, offer jimm.AddApplicationOfferParams) error @@ -78,19 +79,18 @@ type JIMM interface { ResourceTag() names.ControllerTag RevokeAuditLogAccess(ctx context.Context, user *openfga.User, targetUserTag names.UserTag) error RevokeCloudAccess(ctx context.Context, user *openfga.User, ct names.CloudTag, ut names.UserTag, access string) error - RevokeCloudCredential(ctx context.Context, user *dbmodel.User, tag names.CloudCredentialTag, force bool) error + RevokeCloudCredential(ctx context.Context, user *dbmodel.Identity, tag names.CloudCredentialTag, force bool) error RevokeModelAccess(ctx context.Context, user *openfga.User, mt names.ModelTag, ut names.UserTag, access jujuparams.UserAccessPermission) error RevokeOfferAccess(ctx context.Context, user *openfga.User, offerURL string, ut names.UserTag, access jujuparams.OfferAccessPermission) (err error) SetControllerConfig(ctx context.Context, u *openfga.User, args jujuparams.ControllerConfigSet) error SetControllerDeprecated(ctx context.Context, user *openfga.User, controllerName string, deprecated bool) error - SetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, configs map[string]interface{}) error - SetUserModelDefaults(ctx context.Context, user *dbmodel.User, configs map[string]interface{}) error - UnsetModelDefaults(ctx context.Context, user *dbmodel.User, cloudTag names.CloudTag, region string, keys []string) error + SetIdentityModelDefaults(ctx context.Context, user *dbmodel.Identity, configs map[string]interface{}) error + SetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error + UnsetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error UpdateApplicationOffer(ctx context.Context, controller *dbmodel.Controller, offerUUID string, removed bool) error UpdateCloud(ctx context.Context, u *openfga.User, ct names.CloudTag, cloud jujuparams.Cloud) error UpdateCloudCredential(ctx context.Context, u *openfga.User, args jimm.UpdateCloudCredentialArgs) ([]jujuparams.UpdateCredentialModelResult, error) UpdateMigratedModel(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error - UserModelDefaults(ctx context.Context, user *dbmodel.User) (map[string]interface{}, error) ValidateModelUpgrade(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error WatchAllModelSummaries(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) } @@ -147,10 +147,10 @@ func (r *controllerRoot) masquerade(ctx context.Context, userTag string) (*openf if !r.user.JimmAdmin { return nil, errors.E(errors.CodeUnauthorized, "unauthorized") } - user := dbmodel.User{ - Username: ut.Id(), + user := dbmodel.Identity{ + Name: ut.Id(), } - if err := r.jimm.DB().GetUser(ctx, &user); err != nil { + if err := r.jimm.DB().GetIdentity(ctx, &user); err != nil { return nil, err } return openfga.NewUser(&user, r.jimm.AuthorizationClient()), nil diff --git a/internal/jujuapi/jimm.go b/internal/jujuapi/jimm.go index 338bf47c9..3b8b30f37 100644 --- a/internal/jujuapi/jimm.go +++ b/internal/jujuapi/jimm.go @@ -168,12 +168,12 @@ func (r *controllerRoot) AddController(ctx context.Context, req apiparams.AddCon } ctl := dbmodel.Controller{ - UUID: req.UUID, - Name: req.Name, - PublicAddress: req.PublicAddress, - CACertificate: req.CACertificate, - AdminUser: req.Username, - AdminPassword: req.Password, + UUID: req.UUID, + Name: req.Name, + PublicAddress: req.PublicAddress, + CACertificate: req.CACertificate, + AdminIdentityName: req.Username, + AdminPassword: req.Password, } nphps, err := network.ParseProviderHostPorts(req.APIAddresses...) if err != nil { @@ -293,7 +293,7 @@ func auditParamsToFilter(req apiparams.FindAuditEventsRequest) (db.AuditLogFilte if err != nil { return filter, errors.E(err, errors.CodeBadRequest, `invalid "user-tag" filter`) } - filter.UserTag = tag.String() + filter.IdentityTag = tag.String() } limit := int(req.Limit) diff --git a/internal/jujuapi/jimm_test.go b/internal/jujuapi/jimm_test.go index a73164034..08b1b62e0 100644 --- a/internal/jujuapi/jimm_test.go +++ b/internal/jujuapi/jimm_test.go @@ -415,14 +415,14 @@ func TestAuditLogAPIParamsConversion(t *testing.T) { SortTime: false, }, result: db.AuditLogFilter{ - Start: time.Date(2023, 8, 14, 0, 0, 0, 0, time.UTC), - End: time.Date(2023, 8, 14, 0, 0, 0, 0, time.UTC), - UserTag: "user-alice", - Model: "123", - Method: "Deploy", - Offset: 10, - Limit: 10, - SortTime: false, + Start: time.Date(2023, 8, 14, 0, 0, 0, 0, time.UTC), + End: time.Date(2023, 8, 14, 0, 0, 0, 0, time.UTC), + IdentityTag: "user-alice", + Model: "123", + Method: "Deploy", + Offset: 10, + Limit: 10, + SortTime: false, }, }, { about: "Test limit lower bound", @@ -567,10 +567,10 @@ func (s *jimmSuite) TestImportModel(c *gc.C) { func (s *jimmSuite) TestAddCloudToController(c *gc.C) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } - err := s.JIMM.Database.GetUser(ctx, &u) + err := s.JIMM.Database.GetIdentity(ctx, &u) c.Assert(err, gc.IsNil) conn := s.open(c, nil, "alice@external") @@ -604,10 +604,10 @@ func (s *jimmSuite) TestAddCloudToController(c *gc.C) { func (s *jimmSuite) TestAddExistingCloudToController(c *gc.C) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } - err := s.JIMM.Database.GetUser(ctx, &u) + err := s.JIMM.Database.GetIdentity(ctx, &u) c.Assert(err, gc.IsNil) conn := s.open(c, nil, "alice@external") @@ -652,10 +652,10 @@ func (s *jimmSuite) TestAddExistingCloudToController(c *gc.C) { func (s *jimmSuite) TestRemoveCloudFromController(c *gc.C) { ctx := context.Background() - u := dbmodel.User{ - Username: "alice@external", + u := dbmodel.Identity{ + Name: "alice@external", } - err := s.JIMM.Database.GetUser(ctx, &u) + err := s.JIMM.Database.GetIdentity(ctx, &u) c.Assert(err, gc.IsNil) conn := s.open(c, nil, "alice@external") diff --git a/internal/jujuapi/modelmanager.go b/internal/jujuapi/modelmanager.go index c4096e83a..31f9aef39 100644 --- a/internal/jujuapi/modelmanager.go +++ b/internal/jujuapi/modelmanager.go @@ -318,7 +318,7 @@ func (r *controllerRoot) SetModelDefaults(ctx context.Context, args jujuparams.S results[i].Error = mapError(errors.E(op, err)) continue } - results[i].Error = mapError(r.jimm.SetModelDefaults(ctx, r.user.User, cloudTag, config.CloudRegion, config.Config)) + results[i].Error = mapError(r.jimm.SetModelDefaults(ctx, r.user.Identity, cloudTag, config.CloudRegion, config.Config)) } return jujuparams.ErrorResults{ @@ -335,7 +335,7 @@ func (r *controllerRoot) UnsetModelDefaults(ctx context.Context, args jujuparams results[i].Error = mapError(err) continue } - results[i].Error = mapError(r.jimm.UnsetModelDefaults(ctx, r.user.User, cloudTag, key.CloudRegion, key.Keys)) + results[i].Error = mapError(r.jimm.UnsetModelDefaults(ctx, r.user.Identity, cloudTag, key.CloudRegion, key.Keys)) } return jujuparams.ErrorResults{ @@ -356,7 +356,7 @@ func (r *controllerRoot) ModelDefaultsForClouds(ctx context.Context, args jujupa result.Results[i].Error = mapError(errors.E(op, err)) continue } - defaults, err := r.jimm.ModelDefaultsForCloud(ctx, r.user.User, cloudTag) + defaults, err := r.jimm.ModelDefaultsForCloud(ctx, r.user.Identity, cloudTag) if err != nil { result.Results[i].Error = mapError(errors.E(op, err)) continue diff --git a/internal/jujuapi/modelmanager_test.go b/internal/jujuapi/modelmanager_test.go index 4135653bf..35667e163 100644 --- a/internal/jujuapi/modelmanager_test.go +++ b/internal/jujuapi/modelmanager_test.go @@ -264,7 +264,7 @@ func (s *modelManagerSuite) TestModelInfo(c *gc.C) { //client := modelmanager.NewClient(conn) //err := client.GrantModel("bob@external", "write", mt4.Id()) //c.Assert(err, gc.Equals, nil) - bob := openfga.NewUser(&dbmodel.User{Username: "bob@external"}, s.OFGAClient) + bob := openfga.NewUser(&dbmodel.Identity{Name: "bob@external"}, s.OFGAClient) err := bob.SetModelAccess(context.Background(), mt4, ofganames.WriterRelation) c.Assert(err, gc.Equals, nil) @@ -521,7 +521,7 @@ func (s *modelManagerSuite) TestModelInfoDisableControllerUUIDMasking(c *gc.C) { s.Candid.AddUser("bob", "controller-admin") // we make bob a jimm administrator - bob := openfga.NewUser(&dbmodel.User{Username: "bob@external"}, s.OFGAClient) + bob := openfga.NewUser(&dbmodel.Identity{Name: "bob@external"}, s.OFGAClient) err = bob.SetControllerAccess(context.Background(), s.JIMM.ResourceTag(), ofganames.AdministratorRelation) c.Assert(err, gc.Equals, nil) diff --git a/internal/jujuapi/usermanager.go b/internal/jujuapi/usermanager.go index dfd574f70..c31491e80 100644 --- a/internal/jujuapi/usermanager.go +++ b/internal/jujuapi/usermanager.go @@ -83,7 +83,7 @@ func (r *controllerRoot) userInfo(ctx context.Context, entity string) (*jujupara if err != nil { return nil, errors.E(op, err, errors.CodeBadRequest) } - if r.user.Username != user.Id() { + if r.user.Name != user.Id() { return nil, errors.E(op, errors.CodeUnauthorized) } ui := r.user.ToJujuUserInfo() diff --git a/internal/jujuapi/websocket_test.go b/internal/jujuapi/websocket_test.go index 6fab801d1..0b7aa1d23 100644 --- a/internal/jujuapi/websocket_test.go +++ b/internal/jujuapi/websocket_test.go @@ -87,8 +87,8 @@ func (s *websocketSuite) SetUpTest(c *gc.C) { c.Assert(err, gc.Equals, nil) bob := openfga.NewUser( - &dbmodel.User{ - Username: "bob@external", + &dbmodel.Identity{ + Name: "bob@external", }, s.OFGAClient, ) @@ -175,7 +175,7 @@ func (s *proxySuite) TestConnectToModel(c *gc.C) { func (s *proxySuite) TestConnectToModelAndLogin(c *gc.C) { ctx := context.Background() alice := names.NewUserTag("alice") - aliceUser := openfga.NewUser(&dbmodel.User{Username: alice.Id()}, s.JIMM.OpenFGAClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: alice.Id()}, s.JIMM.OpenFGAClient) err := aliceUser.SetControllerAccess(ctx, s.Model.Controller.ResourceTag(), ofganames.AdministratorRelation) c.Assert(err, gc.IsNil) conn, err := s.openNoAssert(c, &api.Info{ diff --git a/internal/jujuclient/client_test.go b/internal/jujuclient/client_test.go index 5abec889d..243bc0880 100644 --- a/internal/jujuclient/client_test.go +++ b/internal/jujuclient/client_test.go @@ -38,12 +38,12 @@ func (s *clientSuite) TestStatus(c *gc.C) { info := s.APIInfo(c) ctl := dbmodel.Controller{ - UUID: info.ControllerUUID, - Name: s.ControllerConfig.ControllerName(), - CACertificate: info.CACert, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - PublicAddress: info.Addrs[0], + UUID: info.ControllerUUID, + Name: s.ControllerConfig.ControllerName(), + CACertificate: info.CACert, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + PublicAddress: info.Addrs[0], } models, err := s.API.UpdateCredential(ctx, cred) diff --git a/internal/jujuclient/dial_test.go b/internal/jujuclient/dial_test.go index cdf7db353..7ba5bc3f8 100644 --- a/internal/jujuclient/dial_test.go +++ b/internal/jujuclient/dial_test.go @@ -71,12 +71,12 @@ var _ = gc.Suite(&dialSuite{}) func (s *dialSuite) TestDial(c *gc.C) { info := s.APIInfo(c) ctl := dbmodel.Controller{ - UUID: s.ControllerConfig.ControllerUUID(), - Name: s.ControllerConfig.ControllerName(), - CACertificate: info.CACert, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - PublicAddress: info.Addrs[0], + UUID: s.ControllerConfig.ControllerUUID(), + Name: s.ControllerConfig.ControllerName(), + CACertificate: info.CACert, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + PublicAddress: info.Addrs[0], } api, err := s.Dialer.Dial(context.Background(), &ctl, names.ModelTag{}, nil) c.Assert(err, gc.Equals, nil) diff --git a/internal/jujuclient/modelwatcher_test.go b/internal/jujuclient/modelwatcher_test.go index b2797846d..81f11d518 100644 --- a/internal/jujuclient/modelwatcher_test.go +++ b/internal/jujuclient/modelwatcher_test.go @@ -42,12 +42,12 @@ func (s *modelWatcherSuite) SetUpTest(c *gc.C) { }}) } ctl := dbmodel.Controller{ - UUID: s.ControllerConfig.ControllerUUID(), - Name: s.ControllerConfig.ControllerName(), - CACertificate: info.CACert, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - Addresses: hpss, + UUID: s.ControllerConfig.ControllerUUID(), + Name: s.ControllerConfig.ControllerName(), + CACertificate: info.CACert, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + Addresses: hpss, } s.API, err = s.Dialer.Dial(context.Background(), &ctl, s.Model.ModelTag(), nil) diff --git a/internal/jujuclient/ping_test.go b/internal/jujuclient/ping_test.go index 8e80b5cdb..54ab09c61 100644 --- a/internal/jujuclient/ping_test.go +++ b/internal/jujuclient/ping_test.go @@ -21,12 +21,12 @@ func (s *pingSuite) TestPing(c *gc.C) { info := s.APIInfo(c) ctl := dbmodel.Controller{ - UUID: s.ControllerConfig.ControllerUUID(), - Name: s.ControllerConfig.ControllerName(), - CACertificate: info.CACert, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - PublicAddress: info.Addrs[0], + UUID: s.ControllerConfig.ControllerUUID(), + Name: s.ControllerConfig.ControllerName(), + CACertificate: info.CACert, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + PublicAddress: info.Addrs[0], } api, err := s.Dialer.Dial(ctx, &ctl, names.ModelTag{}, nil) c.Assert(err, gc.Equals, nil) diff --git a/internal/jujuclient/storage_test.go b/internal/jujuclient/storage_test.go index 20ec0a983..6f449bc64 100644 --- a/internal/jujuclient/storage_test.go +++ b/internal/jujuclient/storage_test.go @@ -35,12 +35,12 @@ func (s *storageSuite) TestListFilesystems(c *gc.C) { info := s.APIInfo(c) ctl := dbmodel.Controller{ - UUID: s.ControllerConfig.ControllerUUID(), - Name: s.ControllerConfig.ControllerName(), - CACertificate: info.CACert, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - PublicAddress: info.Addrs[0], + UUID: s.ControllerConfig.ControllerUUID(), + Name: s.ControllerConfig.ControllerName(), + CACertificate: info.CACert, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + PublicAddress: info.Addrs[0], } models, err := s.API.UpdateCredential(ctx, cred) @@ -81,12 +81,12 @@ func (s *storageSuite) TestListVolumes(c *gc.C) { info := s.APIInfo(c) ctl := dbmodel.Controller{ - UUID: s.ControllerConfig.ControllerUUID(), - Name: s.ControllerConfig.ControllerName(), - CACertificate: info.CACert, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - PublicAddress: info.Addrs[0], + UUID: s.ControllerConfig.ControllerUUID(), + Name: s.ControllerConfig.ControllerName(), + CACertificate: info.CACert, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + PublicAddress: info.Addrs[0], } models, err := s.API.UpdateCredential(ctx, cred) @@ -127,12 +127,12 @@ func (s *storageSuite) TestListStorageDetails(c *gc.C) { info := s.APIInfo(c) ctl := dbmodel.Controller{ - UUID: s.ControllerConfig.ControllerUUID(), - Name: s.ControllerConfig.ControllerName(), - CACertificate: info.CACert, - AdminUser: info.Tag.Id(), - AdminPassword: info.Password, - PublicAddress: info.Addrs[0], + UUID: s.ControllerConfig.ControllerUUID(), + Name: s.ControllerConfig.ControllerName(), + CACertificate: info.CACert, + AdminIdentityName: info.Tag.Id(), + AdminPassword: info.Password, + PublicAddress: info.Addrs[0], } models, err := s.API.UpdateCredential(ctx, cred) diff --git a/internal/openfga/user.go b/internal/openfga/user.go index af531c49c..9de3d3f6c 100644 --- a/internal/openfga/user.go +++ b/internal/openfga/user.go @@ -18,17 +18,17 @@ import ( // NewUser returns a new user structure that can be used to check // user's access rights to various resources. -func NewUser(u *dbmodel.User, client *OFGAClient) *User { +func NewUser(u *dbmodel.Identity, client *OFGAClient) *User { return &User{ - User: u, - client: client, + Identity: u, + client: client, } } // User wraps dbmodel.User and implements methods that enable us // to check user's access rights to various resources. type User struct { - *dbmodel.User + *dbmodel.Identity client *OFGAClient JimmAdmin bool } @@ -318,7 +318,7 @@ func IsAdministrator[T administratorT](ctx context.Context, u *User, resource T) ctx, "openfga administrator check failed", zap.Error(err), - zap.String("user", u.Username), + zap.String("user", u.Name), zap.String("resource", resource.String()), ) return false, errors.E(err) @@ -440,7 +440,7 @@ func ListUsersWithAccess[T ofganames.ResourceTagger](ctx context.Context, client if entity.ID == "*" { entity.ID = ofganames.EveryoneUser } - users[i] = NewUser(&dbmodel.User{Username: entity.ID}, client) + users[i] = NewUser(&dbmodel.Identity{Name: entity.ID}, client) } return users, nil } diff --git a/internal/openfga/user_test.go b/internal/openfga/user_test.go index 8b9b4f2b8..57e55df83 100644 --- a/internal/openfga/user_test.go +++ b/internal/openfga/user_test.go @@ -54,8 +54,8 @@ func (s *userTestSuite) TestIsAdministrator(c *gc.C) { c.Assert(err, gc.IsNil) u := openfga.NewUser( - &dbmodel.User{ - Username: user.Id(), + &dbmodel.Identity{ + Name: user.Id(), }, s.ofgaClient, ) @@ -102,9 +102,9 @@ func (s *userTestSuite) TestModelAccess(c *gc.C) { err = s.ofgaClient.AddRelation(ctx, tuples...) c.Assert(err, gc.IsNil) - adamUser := openfga.NewUser(&dbmodel.User{Username: "adam"}, s.ofgaClient) - eveUser := openfga.NewUser(&dbmodel.User{Username: eve.Id()}, s.ofgaClient) - aliceUser := openfga.NewUser(&dbmodel.User{Username: alice.Id()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: "adam"}, s.ofgaClient) + eveUser := openfga.NewUser(&dbmodel.Identity{Name: eve.Id()}, s.ofgaClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: alice.Id()}, s.ofgaClient) relation := eveUser.GetModelAccess(ctx, model) c.Assert(relation, gc.DeepEquals, ofganames.AdministratorRelation) @@ -137,9 +137,9 @@ func (s *userTestSuite) TestSetModelAccess(c *gc.C) { eve := names.NewUserTag("eve") alice := names.NewUserTag("alice") - adamUser := openfga.NewUser(&dbmodel.User{Username: "adam"}, s.ofgaClient) - eveUser := openfga.NewUser(&dbmodel.User{Username: eve.Id()}, s.ofgaClient) - aliceUser := openfga.NewUser(&dbmodel.User{Username: alice.Id()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: "adam"}, s.ofgaClient) + eveUser := openfga.NewUser(&dbmodel.Identity{Name: eve.Id()}, s.ofgaClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: alice.Id()}, s.ofgaClient) err = eveUser.SetModelAccess(ctx, model, ofganames.AdministratorRelation) c.Assert(err, gc.IsNil) @@ -197,9 +197,9 @@ func (s *userTestSuite) TestCloudAccess(c *gc.C) { err = s.ofgaClient.AddRelation(ctx, tuples...) c.Assert(err, gc.IsNil) - adamUser := openfga.NewUser(&dbmodel.User{Username: "adam"}, s.ofgaClient) - eveUser := openfga.NewUser(&dbmodel.User{Username: eve.Id()}, s.ofgaClient) - aliceUser := openfga.NewUser(&dbmodel.User{Username: alice.Id()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: "adam"}, s.ofgaClient) + eveUser := openfga.NewUser(&dbmodel.Identity{Name: eve.Id()}, s.ofgaClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: alice.Id()}, s.ofgaClient) relation := eveUser.GetCloudAccess(ctx, cloud) c.Assert(relation, gc.DeepEquals, ofganames.AdministratorRelation) @@ -220,9 +220,9 @@ func (s *userTestSuite) TestSetCloudAccess(c *gc.C) { eve := names.NewUserTag("eve") alice := names.NewUserTag("alice") - adamUser := openfga.NewUser(&dbmodel.User{Username: "adam"}, s.ofgaClient) - eveUser := openfga.NewUser(&dbmodel.User{Username: eve.Id()}, s.ofgaClient) - aliceUser := openfga.NewUser(&dbmodel.User{Username: alice.Id()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: "adam"}, s.ofgaClient) + eveUser := openfga.NewUser(&dbmodel.Identity{Name: eve.Id()}, s.ofgaClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: alice.Id()}, s.ofgaClient) err = eveUser.SetCloudAccess(ctx, cloud, ofganames.AdministratorRelation) c.Assert(err, gc.IsNil) @@ -273,9 +273,9 @@ func (s *userTestSuite) TestControllerAccess(c *gc.C) { err = s.ofgaClient.AddRelation(ctx, tuples...) c.Assert(err, gc.IsNil) - adamUser := openfga.NewUser(&dbmodel.User{Username: "adam"}, s.ofgaClient) - eveUser := openfga.NewUser(&dbmodel.User{Username: eve.Id()}, s.ofgaClient) - aliceUser := openfga.NewUser(&dbmodel.User{Username: alice.Id()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: "adam"}, s.ofgaClient) + eveUser := openfga.NewUser(&dbmodel.Identity{Name: eve.Id()}, s.ofgaClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: alice.Id()}, s.ofgaClient) relation := eveUser.GetControllerAccess(ctx, controller) c.Assert(relation, gc.DeepEquals, ofganames.AdministratorRelation) @@ -302,9 +302,9 @@ func (s *userTestSuite) TestSetControllerAccess(c *gc.C) { eve := names.NewUserTag("eve") alice := names.NewUserTag("alice") - adamUser := openfga.NewUser(&dbmodel.User{Username: "adam"}, s.ofgaClient) - eveUser := openfga.NewUser(&dbmodel.User{Username: eve.Id()}, s.ofgaClient) - aliceUser := openfga.NewUser(&dbmodel.User{Username: alice.Id()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: "adam"}, s.ofgaClient) + eveUser := openfga.NewUser(&dbmodel.Identity{Name: eve.Id()}, s.ofgaClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: alice.Id()}, s.ofgaClient) err = eveUser.SetControllerAccess(ctx, controller, ofganames.AdministratorRelation) c.Assert(err, gc.IsNil) @@ -339,10 +339,10 @@ func (s *userTestSuite) TestUnsetAuditLogViewerAccess(c *gc.C) { c.Assert(err, gc.IsNil) controller := names.NewControllerTag(controllerUUID.String()) - aliceUser := openfga.NewUser(&dbmodel.User{Username: "alice"}, s.ofgaClient) + aliceUser := openfga.NewUser(&dbmodel.Identity{Name: "alice"}, s.ofgaClient) tuples := []openfga.Tuple{{ - Object: ofganames.ConvertTag(aliceUser.User.ResourceTag()), + Object: ofganames.ConvertTag(aliceUser.Identity.ResourceTag()), Relation: ofganames.AuditLogViewerRelation, Target: ofganames.ConvertTag(controller), }} @@ -425,7 +425,7 @@ func (s *userTestSuite) TestListRelatedUsers(c *gc.C) { err = s.ofgaClient.AddRelation(ctx, tuples...) c.Assert(err, gc.IsNil) - eveUser := openfga.NewUser(&dbmodel.User{Username: "eve"}, s.ofgaClient) + eveUser := openfga.NewUser(&dbmodel.Identity{Name: "eve"}, s.ofgaClient) isAdministrator, err := openfga.IsAdministrator(ctx, eveUser, offer) c.Assert(err, gc.IsNil) c.Assert(isAdministrator, gc.Equals, true) @@ -473,7 +473,7 @@ func (s *userTestSuite) TestListModels(c *gc.C) { err = s.ofgaClient.AddRelation(ctx, tuples...) c.Assert(err, gc.IsNil) - adamUser := openfga.NewUser(&dbmodel.User{Username: adam.Name()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: adam.Name()}, s.ofgaClient) modelUUIDs, err := adamUser.ListModels(ctx, ofganames.ReaderRelation) c.Assert(err, gc.IsNil) wantUUIDs := []string{model1UUID.String(), model2UUID.String(), model3UUID.String()} @@ -515,7 +515,7 @@ func (s *userTestSuite) TestListApplicationOffers(c *gc.C) { err = s.ofgaClient.AddRelation(ctx, tuples...) c.Assert(err, gc.IsNil) - adamUser := openfga.NewUser(&dbmodel.User{Username: adam.Name()}, s.ofgaClient) + adamUser := openfga.NewUser(&dbmodel.Identity{Name: adam.Name()}, s.ofgaClient) offerUUIDs, err := adamUser.ListApplicationOffers(ctx, ofganames.ReaderRelation) c.Assert(err, gc.IsNil) wantUUIDs := []string{offer1UUID.String(), offer2UUID.String(), offer3UUID.String()} diff --git a/internal/rpc/client_test.go b/internal/rpc/client_test.go index ae203ee93..96383013e 100644 --- a/internal/rpc/client_test.go +++ b/internal/rpc/client_test.go @@ -380,7 +380,7 @@ func TestProxySocketsAuditLogs(t *testing.T) { FacadeMethod: "TestReq", FacadeVersion: 0, ObjectId: "", - UserTag: "user-testUser", + IdentityTag: "user-testUser", IsResponse: false, Params: dbmodel.JSON(p), Errors: nil, @@ -394,7 +394,7 @@ func TestProxySocketsAuditLogs(t *testing.T) { FacadeMethod: "", FacadeVersion: 0, ObjectId: "", - UserTag: "user-testUser", + IdentityTag: "user-testUser", IsResponse: true, Params: nil, Errors: auditLogs[1].Errors, diff --git a/internal/rpc/proxy.go b/internal/rpc/proxy.go index c7e056ebd..2e02a841d 100644 --- a/internal/rpc/proxy.go +++ b/internal/rpc/proxy.go @@ -124,7 +124,7 @@ func (p *modelProxy) auditLogMessage(msg *message, isResponse bool) error { ale := dbmodel.AuditLogEntry{ Time: time.Now().UTC().Round(time.Millisecond), MessageId: msg.RequestID, - UserTag: p.tokenGen.GetUser().String(), + IdentityTag: p.tokenGen.GetUser().String(), Model: p.modelName, ConversationId: p.conversationId, FacadeName: msg.Type, diff --git a/local/candid/config.yaml b/local/candid/config.yaml index 53c53dcfc..5beede576 100644 --- a/local/candid/config.yaml +++ b/local/candid/config.yaml @@ -1,6 +1,6 @@ storage: type: postgres - connection-string: postgresql://jimm:jimm@db:5432/jimm?sslmode=disable + connection-string: postgresql://jimm:jimm@db:5432/postgres?sslmode=disable logging-config: "DEBUG" auth-username: admin auth-password: password diff --git a/local/seed_db/main.go b/local/seed_db/main.go index ddef8052f..b3f16703f 100644 --- a/local/seed_db/main.go +++ b/local/seed_db/main.go @@ -47,8 +47,8 @@ func main() { os.Exit(1) } - u := dbmodel.User{ - Username: petname.Generate(2, "-") + "@external", + u := dbmodel.Identity{ + Name: petname.Generate(2, "-") + "@external", } if err = db.DB.Create(&u).Error; err != nil { fmt.Println("failed to add user to db ", err) @@ -84,10 +84,10 @@ func main() { } cred := dbmodel.CloudCredential{ - Name: petname.Generate(2, "-"), - CloudName: cloud.Name, - OwnerUsername: u.Username, - AuthType: "empty", + Name: petname.Generate(2, "-"), + CloudName: cloud.Name, + OwnerIdentityName: u.Name, + AuthType: "empty", } if err = db.SetCloudCredential(ctx, &cred); err != nil { fmt.Println("failed to add cloud credential to db ", err) @@ -100,7 +100,7 @@ func main() { String: id.String(), Valid: true, }, - OwnerUsername: u.Username, + OwnerIdentityName: u.Name, ControllerID: controller.ID, CloudRegionID: cloud.Regions[0].ID, CloudCredentialID: cred.ID, @@ -119,7 +119,7 @@ func main() { } offerName := petname.Generate(2, "-") - offerURL, _ := crossmodel.ParseOfferURL(controller.Name + ":" + u.Username + "/" + model.Name + "." + offerName) + offerURL, _ := crossmodel.ParseOfferURL(controller.Name + ":" + u.Name + "/" + model.Name + "." + offerName) offer := dbmodel.ApplicationOffer{ UUID: id.String(), Name: offerName, diff --git a/service.go b/service.go index 02c6facfd..66a972d26 100644 --- a/service.go +++ b/service.go @@ -557,7 +557,7 @@ func ensureControllerAdministrators(ctx context.Context, client *openfga.OFGACli tuples := []openfga.Tuple{} for _, username := range admins { userTag := names.NewUserTag(username) - user := openfga.NewUser(&dbmodel.User{Username: userTag.Id()}, client) + user := openfga.NewUser(&dbmodel.Identity{Name: userTag.Id()}, client) isAdmin, err := openfga.IsAdministrator(ctx, user, controller) if err != nil { return errors.E(err) diff --git a/service_test.go b/service_test.go index 5827e6a16..c3319dff6 100644 --- a/service_test.go +++ b/service_test.go @@ -252,7 +252,7 @@ func TestOpenFGA(t *testing.T) { // assert controller admins have been created in openfga for _, username := range []string{"alice", "eve"} { user := openfga.NewUser( - &dbmodel.User{Username: username}, + &dbmodel.Identity{Name: username}, client, ) allowed, err := openfga.IsAdministrator(context.Background(), user, names.NewControllerTag(p.ControllerUUID)) @@ -296,15 +296,15 @@ func TestThirdPartyCaveatDischarge(t *testing.T) { UUID: "7e4e7ffb-5116-4544-a400-f584d08c410e", Name: "test-application-offer", } - user := dbmodel.User{ - Username: "alice@external", + user := dbmodel.Identity{ + Name: "alice@external", } ctx := context.Background() tests := []struct { about string - setup func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.User) + setup func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.Identity) caveats []string expectDeclared map[string]string expectedError string @@ -314,7 +314,7 @@ func TestThirdPartyCaveatDischarge(t *testing.T) { expectedError: ".*third party refused discharge: cannot discharge: caveat not recognized", }, { about: "user is an offer reader", - setup: func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.User) { + setup: func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.Identity) { u := openfga.NewUser(user, ofgaClient) err := u.SetApplicationOfferAccess(ctx, offer.ResourceTag(), ofganames.ReaderRelation) c.Assert(err, qt.IsNil) @@ -327,7 +327,7 @@ func TestThirdPartyCaveatDischarge(t *testing.T) { expectedError: ".*cannot discharge: permission denied", }, { about: "user is an offer consumer", - setup: func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.User) { + setup: func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.Identity) { u := openfga.NewUser(user, ofgaClient) err := u.SetApplicationOfferAccess(ctx, offer.ResourceTag(), ofganames.ConsumerRelation) c.Assert(err, qt.IsNil) @@ -336,7 +336,7 @@ func TestThirdPartyCaveatDischarge(t *testing.T) { expectDeclared: map[string]string{"offer-uuid": offer.UUID}, }, { about: "user is an offer administrator", - setup: func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.User) { + setup: func(c *qt.C, ofgaClient *openfga.OFGAClient, user *dbmodel.Identity) { u := openfga.NewUser(user, ofgaClient) err := u.SetApplicationOfferAccess(ctx, offer.ResourceTag(), ofganames.AdministratorRelation) c.Assert(err, qt.IsNil)