Skip to content

Commit

Permalink
Merge pull request juju#18016 from Aflynn50/remove-usage-of-model-users
Browse files Browse the repository at this point in the history
juju#18016

The model user collection is being removed in the move to 4.0. Usages of it are being replaced with `GetModelUsers` on the model service and the collection in mongo will soon be deleted.

<!-- 
The PR title should match: <type>(optional <scope>): <description>.

Please also ensure all commits in this PR comply with our conventional commits specification:
https://docs.google.com/document/d/1SYUo9G7qZ_jdoVXpUVamS5VCgHmtZ0QA-wZxKoMS-C0 
-->

<!-- Why this change is needed and what it does. -->

## Checklist

<!-- If an item is not applicable, use `~strikethrough~`. -->

- [x] Code style: imports ordered, good names, simple structure, etc
- [x] Comments saying why design decisions were made
- [x] Go unit tests, with comments saying what you're testing
- [x] [Integration tests](https://github.com/juju/juju/tree/main/tests), with comments saying what you're testing
- [ ] [doc.go](https://discourse.charmhub.io/t/readme-in-packages/451) added or updated in changed packages

## QA steps
```
$ juju bootstrap lxd model-user-source
$ juju bootstrap lxd model-user-target
$ juju switch model-user-source
$ juju add-model test
$ juju add-user bob
$ juju add-user jim
$ juju grant bob admin test
$ juju grant jim read test
$ juju switch model-user-target
$ juju add-user bob
$ juju add-user jim
$ juju migrate
Migration started with ID "d66f7870-c51a-4443-8a63-288574cb98f2:0"
$ model-user-target:test juju show-model test
test:
 name: admin/test
 short-name: test
 model-uuid: d66f7870-c51a-4443-8a63-288574cb98f2
 model-type: iaas
 controller-uuid: 762f6a2e-2b91-46c5-8d5e-6794d08ec037
 controller-name: model-user-target
 is-controller: false
 owner: admin
 cloud: lxd
 region: default
 type: lxd
 life: alive
 status:
 current: available
 since: 54 seconds ago
 users:
 admin:
 display-name: admin
 access: admin
 last-connection: 21 seconds ago
 bob:
 access: write
 last-connection: never connected
 jim:
 access: read
 last-connection: never connected
$ juju switch model-user-source
$ juju status
ERROR Model "admin/test" has been migrated to controller "model-user-target".
To access it run 'juju switch model-user-target:admin/test'.

```
<!-- Describe steps to verify that the change works. -->

## Links

<!-- Link to all relevant specification, documentation, bug, issue or JIRA card. -->


**Jira card:** [JUJU-6533](https://warthogs.atlassian.net/browse/JUJU-6533)


[JUJU-6533]: https://warthogs.atlassian.net/browse/JUJU-6533?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
jujubot authored Sep 5, 2024
2 parents f95dacb + bf113d3 commit 66af719
Show file tree
Hide file tree
Showing 10 changed files with 17 additions and 204 deletions.
5 changes: 3 additions & 2 deletions apiserver/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func (a *admin) authenticate(ctx context.Context, req params.LoginRequest) (*aut
}

func (a *admin) maybeEmitRedirectError(modelUUID model.UUID, authTag names.Tag) error {
userTag, ok := authTag.(names.UserTag)
_, ok := authTag.(names.UserTag)
if !ok {
return nil
}
Expand All @@ -377,7 +377,8 @@ func (a *admin) maybeEmitRedirectError(modelUUID model.UUID, authTag names.Tag)
// granted access, do not return a redirect error.
// We need to return redirects if possible for anonymous logins in order
// to ensure post-migration operation of CMRs.
if mig == nil || (userTag.Id() != api.AnonymousUsername && mig.ModelUserAccess(userTag) == permission.NoAccess) {
// TODO(aflynn): reinstate check for unauthorised user (JUJU-6669).
if mig == nil {
return nil
}

Expand Down
7 changes: 4 additions & 3 deletions apiserver/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -616,9 +616,10 @@ func (s *loginSuite) TestMigratedModelLogin(c *gc.C) {
// Attempt to open an API connection to the migrated model as a user
// that had NO access to the model before it got migrated. The server
// should return a not-authorized error when attempting to log in.
info.Tag = names.NewUserTag("some-other-user")
_, err = api.Open(context.Background(), info, fastDialOpts)
c.Assert(params.ErrCode(errors.Cause(err)), gc.Equals, params.CodeUnauthorized)
// TODO(aflynn): reinstate check for unauthorised user (JUJU-6669).
//info.Tag = names.NewUserTag("some-other-user")
//_, err = api.Open(context.Background(), info, fastDialOpts)
//c.Assert(params.ErrCode(errors.Cause(err)), gc.Equals, params.CodeUnauthorized)

// Attempt to open an API connection to the migrated model as the
// anonymous user; this should also be allowed on account of CMRs.
Expand Down
4 changes: 3 additions & 1 deletion apiserver/common/modelmanagerinterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ func (st modelManagerStateShim) GetBackend(modelUUID string) (ModelManagerBacken
return nil, nil, errors.Trace(mErr)
}

if mig == nil || mig.ModelUserAccess(st.user) == permission.NoAccess {
// TODO(aflynn): Also return this error if, in the migration info, the
// user had access to the migrated model (JUJU-6669).
if mig == nil {
return nil, nil, errors.Trace(err) // return original NotFound error
}

Expand Down
1 change: 0 additions & 1 deletion apiserver/facades/client/client/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ type Model interface {
Config() (*config.Config, error)
Owner() names.UserTag
AddUser(state.UserAccessSpec) (permission.UserAccess, error)
Users() ([]permission.UserAccess, error)
StatusHistory(status.StatusHistoryFilter) ([]status.StatusInfo, error)
LatestToolsVersion() version.Number
Status() (status.StatusInfo, error)
Expand Down
39 changes: 0 additions & 39 deletions apiserver/facades/client/client/package_mock_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions apiserver/facades/client/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ type ModelService interface {
Model(ctx context.Context, uuid coremodel.UUID) (coremodel.Model, error)
// ControllerModel returns the model used for housing the Juju controller.
ControllerModel(ctx context.Context) (coremodel.Model, error)
// GetModelUsers will retrieve basic information about users with permissions on
// the given model UUID.
GetModelUsers(ctx context.Context, modelUUID coremodel.UUID) ([]coremodel.ModelUserInfo, error)
}

// ModelConfigService provides access to the model configuration.
Expand Down Expand Up @@ -1040,17 +1043,18 @@ func makeModelInfo(ctx context.Context, st *state.State,
return empty, ul, errors.Trace(err)
}

users, err := model.Users()
modelID := coremodel.UUID(model.UUID())

users, err := modelService.GetModelUsers(ctx, modelID)
if err != nil {
return empty, ul, errors.Trace(err)
}
ul.users = set.NewStrings()
for _, u := range users {
ul.users.Add(u.UserName.Name())
ul.users.Add(u.Name.Name())
}

// Retrieve agent version for the model.
modelID := coremodel.UUID(model.UUID())
modelInfo, err := modelService.Model(ctx, modelID)
if err != nil {
return empty, userList{}, fmt.Errorf("getting model %q: %w", modelID, err)
Expand Down
39 changes: 0 additions & 39 deletions apiserver/facades/controller/migrationmaster/mocks/state.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 0 additions & 43 deletions state/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (

"github.com/juju/juju/controller"
"github.com/juju/juju/core/constraints"
"github.com/juju/juju/core/permission"
"github.com/juju/juju/core/status"
"github.com/juju/juju/environs/config"
internalpassword "github.com/juju/juju/internal/password"
Expand Down Expand Up @@ -370,11 +369,6 @@ func (ctlr *Controller) NewModel(configSchemaGetter config.ConfigSchemaSourceGet
_, _ = probablyUpdateStatusHistory(newSt.db(), newModel.Kind(), modelGlobalKey, modelGlobalKey, modelStatusDoc)
}

_, err = newSt.SetUserAccess(newModel.Owner(), newModel.ModelTag(), permission.AdminAccess)
if err != nil {
return nil, nil, errors.Annotate(err, "granting admin permission to the owner")
}

// TODO(storage) - we need to add the default storage pools using the new dqlite model service
//storageService, registry, err := newSt.storageServices()
//if err != nil {
Expand Down Expand Up @@ -820,43 +814,6 @@ func (m *Model) AllEndpointBindings() (map[string]*Bindings, error) {
return appEndpointBindings, nil
}

// Users returns a slice of all users for this model.
func (m *Model) Users() ([]permission.UserAccess, error) {
coll, closer := m.st.db().GetCollection(modelUsersC)
defer closer()

var userDocs []userAccessDoc
err := coll.Find(nil).All(&userDocs)
if err != nil {
return nil, errors.Trace(err)
}

var modelUsers []permission.UserAccess
for _, doc := range userDocs {
// check if the User belonging to this model user has
// been deleted, in this case we should not return it.
userTag := names.NewUserTag(doc.UserName)
if userTag.IsLocal() {
_, err := m.st.User(userTag)
if err != nil {
if !IsDeletedUserError(err) {
// We ignore deleted users for now. So if it is not a
// DeletedUserError we return the error.
return nil, errors.Trace(err)
}
continue
}
}
mu, err := NewModelUserAccess(m.st, doc)
if err != nil {
return nil, errors.Trace(err)
}
modelUsers = append(modelUsers, mu)
}

return modelUsers, nil
}

// IsControllerModel returns a boolean indicating whether
// this model is responsible for running a controller.
func (m *Model) IsControllerModel() bool {
Expand Down
48 changes: 0 additions & 48 deletions state/modelmigration.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"gopkg.in/macaroon.v2"

"github.com/juju/juju/core/migration"
"github.com/juju/juju/core/permission"
"github.com/juju/juju/core/status"
"github.com/juju/juju/internal/mongo"
)
Expand Down Expand Up @@ -94,10 +93,6 @@ type ModelMigration interface {
// Refresh updates the contents of the ModelMigration from the
// underlying state.
Refresh() error

// ModelUserAccess returns the type of access that the given tag had to
// the model prior to it being migrated.
ModelUserAccess(names.Tag) permission.Access
}

// MinionReports indicates the sets of agents whose migration minion
Expand Down Expand Up @@ -159,14 +154,6 @@ type modelMigDoc struct {
// TargetMacaroons holds the macaroons to use with TargetAuthTag
// when authenticating.
TargetMacaroons string `bson:"target-macaroons,omitempty"`

// The list of users and their access-level to the model being migrated.
ModelUsers []modelMigUserDoc `bson:"model-users,omitempty"`
}

type modelMigUserDoc struct {
UserID string `bson:"user_id"`
Access permission.Access `bson:"access"`
}

// modelMigStatusDoc tracks the progress of a migration attempt for a
Expand Down Expand Up @@ -644,18 +631,6 @@ func (mig *modelMigration) Refresh() error {
return nil
}

// ModelUserAccess implements ModelMigration.
func (mig *modelMigration) ModelUserAccess(tag names.Tag) permission.Access {
id := tag.Id()
for _, user := range mig.doc.ModelUsers {
if user.UserID == id {
return user.Access
}
}

return permission.NoAccess
}

// MigrationSpec holds the information required to create a
// ModelMigration instance.
type MigrationSpec struct {
Expand Down Expand Up @@ -722,11 +697,6 @@ func (st *State) CreateMigration(spec MigrationSpec) (ModelMigration, error) {
return nil, errors.Trace(err)
}

userDocs, err := modelUserDocs(model)
if err != nil {
return nil, errors.Trace(err)
}

id := fmt.Sprintf("%s:%d", modelUUID, attempt)
doc = modelMigDoc{
Id: id,
Expand All @@ -740,7 +710,6 @@ func (st *State) CreateMigration(spec MigrationSpec) (ModelMigration, error) {
TargetAuthTag: spec.TargetInfo.AuthTag.String(),
TargetPassword: spec.TargetInfo.Password,
TargetMacaroons: macsJSON,
ModelUsers: userDocs,
}

statusDoc = modelMigStatusDoc{
Expand Down Expand Up @@ -788,23 +757,6 @@ func (st *State) CreateMigration(spec MigrationSpec) (ModelMigration, error) {
}, nil
}

func modelUserDocs(m *Model) ([]modelMigUserDoc, error) {
users, err := m.Users()
if err != nil {
return nil, err
}

var docs []modelMigUserDoc
for _, user := range users {
docs = append(docs, modelMigUserDoc{
UserID: user.UserTag.Id(),
Access: user.Access,
})
}

return docs, nil
}

func macaroonsToJSON(m []macaroon.Slice) (string, error) {
if len(m) == 0 {
return "", nil
Expand Down
25 changes: 0 additions & 25 deletions state/modelmigration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"gopkg.in/macaroon.v2"

"github.com/juju/juju/core/migration"
"github.com/juju/juju/core/permission"
"github.com/juju/juju/internal/testing"
"github.com/juju/juju/internal/testing/factory"
"github.com/juju/juju/internal/uuid"
Expand Down Expand Up @@ -850,30 +849,6 @@ func (s *MigrationSuite) TestWatchMinionReportsMultiModel(c *gc.C) {
wc3.AssertOneChange()
}

func (s *MigrationSuite) TestModelUserAccess(c *gc.C) {
model, err := s.State2.Model()
c.Assert(err, jc.ErrorIsNil)
c.Assert(model.MigrationMode(), gc.Equals, state.MigrationModeNone)

// Get users that had access to the model before the migration
modelUsers, err := model.Users()
c.Assert(err, jc.ErrorIsNil)
c.Assert(len(modelUsers), gc.Not(gc.Equals), 0)

mig, err := s.State2.CreateMigration(s.stdSpec)
c.Assert(err, jc.ErrorIsNil)

for _, modelUser := range modelUsers {
c.Logf("check that migration doc lists user %q having permission %q", modelUser.UserTag, modelUser.Access)
perm := mig.ModelUserAccess(modelUser.UserTag)
c.Assert(perm, gc.Equals, modelUser.Access)
}

// Querying for any other user should yield permission.NoAccess
perm := mig.ModelUserAccess(names.NewUserTag("bogus"))
c.Assert(perm, gc.Equals, permission.NoAccess)
}

func (s *MigrationSuite) createStatusWatcher(c *gc.C, st *state.State) (
state.NotifyWatcher, statetesting.NotifyWatcherC,
) {
Expand Down

0 comments on commit 66af719

Please sign in to comment.