// NewModelManagerAPI creates a new api server endpoint for managing // models. func NewModelManagerAPI( st common.ModelManagerBackend, configGetter environs.EnvironConfigGetter, authorizer facade.Authorizer, ) (*ModelManagerAPI, error) { if !authorizer.AuthClient() { return nil, common.ErrPerm } // Since we know this is a user tag (because AuthClient is true), // we just do the type assertion to the UserTag. apiUser, _ := authorizer.GetAuthTag().(names.UserTag) // Pretty much all of the user manager methods have special casing for admin // users, so look once when we start and remember if the user is an admin. isAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, st.ControllerTag()) if err != nil { return nil, errors.Trace(err) } urlGetter := common.NewToolsURLGetter(st.ModelUUID(), st) return &ModelManagerAPI{ ModelStatusAPI: common.NewModelStatusAPI(st, authorizer, apiUser), state: st, check: common.NewBlockChecker(st), authorizer: authorizer, toolsFinder: common.NewToolsFinder(configGetter, st, urlGetter), apiUser: apiUser, isAdmin: isAdmin, }, nil }
func userAuthorizedToChangeAccess(st common.ModelManagerBackend, userIsAdmin bool, userTag names.UserTag) error { if userIsAdmin { // Just confirm that the model that has been given is a valid model. _, err := st.Model() if err != nil { return errors.Trace(err) } return nil } // Get the current user's ModelUser for the Model to see if the user has // permission to grant or revoke permissions on the model. currentUser, err := st.UserAccess(userTag, st.ModelTag()) if err != nil { if errors.IsNotFound(err) { // No, this user doesn't have permission. return common.ErrPerm } return errors.Annotate(err, "could not retrieve user") } if currentUser.Access != permission.AdminAccess { return common.ErrPerm } return nil }
// changeModelAccess performs the requested access grant or revoke action for the // specified user on the specified model. func changeModelAccess(accessor common.ModelManagerBackend, modelTag names.ModelTag, apiUser, targetUserTag names.UserTag, action params.ModelAction, access permission.Access, userIsAdmin bool) error { st, err := accessor.ForModel(modelTag) if err != nil { return errors.Annotate(err, "could not lookup model") } defer st.Close() if err := userAuthorizedToChangeAccess(st, userIsAdmin, apiUser); err != nil { return errors.Trace(err) } switch action { case params.GrantModelAccess: _, err = st.AddModelUser(modelTag.Id(), state.UserAccessSpec{User: targetUserTag, CreatedBy: apiUser, Access: access}) if errors.IsAlreadyExists(err) { modelUser, err := st.UserAccess(targetUserTag, modelTag) if errors.IsNotFound(err) { // Conflicts with prior check, must be inconsistent state. err = txn.ErrExcessiveContention } if err != nil { return errors.Annotate(err, "could not look up model access for user") } // Only set access if greater access is being granted. if modelUser.Access.EqualOrGreaterModelAccessThan(access) { return errors.Errorf("user already has %q access or greater", access) } if _, err = st.SetUserAccess(modelUser.UserTag, modelUser.Object, access); err != nil { return errors.Annotate(err, "could not set model access for user") } return nil } return errors.Annotate(err, "could not grant model access") case params.RevokeModelAccess: switch access { case permission.ReadAccess: // Revoking read access removes all access. err := st.RemoveUserAccess(targetUserTag, modelTag) return errors.Annotate(err, "could not revoke model access") case permission.WriteAccess: // Revoking write access sets read-only. modelUser, err := st.UserAccess(targetUserTag, modelTag) if err != nil { return errors.Annotate(err, "could not look up model access for user") } _, err = st.SetUserAccess(modelUser.UserTag, modelUser.Object, permission.ReadAccess) return errors.Annotate(err, "could not set model access to read-only") case permission.AdminAccess: // Revoking admin access sets read-write. modelUser, err := st.UserAccess(targetUserTag, modelTag) if err != nil { return errors.Annotate(err, "could not look up model access for user") } _, err = st.SetUserAccess(modelUser.UserTag, modelUser.Object, permission.WriteAccess) return errors.Annotate(err, "could not set model access to read-write") default: return errors.Errorf("don't know how to revoke %q access", access) } default: return errors.Errorf("unknown action %q", action) } }