github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/user.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // NOTE: the users that are being stored in the database here are only
     5  // the local users, like "admin" or "bob".  In the  world
     6  // where we have external user providers hooked up, there are no records
     7  // in the database for users that are authenticated elsewhere.
     8  
     9  package state
    10  
    11  import (
    12  	"crypto/rand"
    13  	"fmt"
    14  	"sort"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/juju/errors"
    19  	"github.com/juju/mgo/v3"
    20  	"github.com/juju/mgo/v3/bson"
    21  	"github.com/juju/mgo/v3/txn"
    22  	"github.com/juju/names/v5"
    23  	"github.com/juju/utils/v3"
    24  
    25  	"github.com/juju/juju/core/permission"
    26  )
    27  
    28  const userGlobalKeyPrefix = "us"
    29  
    30  func userGlobalKey(userID string) string {
    31  	return fmt.Sprintf("%s#%s", userGlobalKeyPrefix, userID)
    32  }
    33  
    34  func userIDFromGlobalKey(key string) string {
    35  	prefix := userGlobalKeyPrefix + "#"
    36  	return strings.TrimPrefix(key, prefix)
    37  }
    38  
    39  func (st *State) checkUserExists(name string) (bool, error) {
    40  	lowercaseName := strings.ToLower(name)
    41  
    42  	users, closer := st.db().GetCollection(usersC)
    43  	defer closer()
    44  
    45  	var count int
    46  	var err error
    47  	if count, err = users.FindId(lowercaseName).Count(); err != nil {
    48  		return false, err
    49  	}
    50  	return count > 0, nil
    51  }
    52  
    53  // AddUser adds a user to the database.
    54  func (st *State) AddUser(name, displayName, password, creator string) (*User, error) {
    55  	return st.addUser(name, displayName, password, creator, nil)
    56  }
    57  
    58  // AddUserWithSecretKey adds the user with the specified name, and assigns it
    59  // a randomly generated secret key. This secret key may be used for the user
    60  // and controller to mutually authenticate one another, without without relying
    61  // on TLS certificates.
    62  //
    63  // The new user will not have a password. A password must be set, clearing the
    64  // secret key in the process, before the user can login normally.
    65  func (st *State) AddUserWithSecretKey(name, displayName, creator string) (*User, error) {
    66  	secretKey, err := generateSecretKey()
    67  	if err != nil {
    68  		return nil, errors.Trace(err)
    69  	}
    70  	return st.addUser(name, displayName, "", creator, secretKey)
    71  }
    72  
    73  func (st *State) addUser(name, displayName, password, creator string, secretKey []byte) (*User, error) {
    74  
    75  	if !names.IsValidUserName(name) {
    76  		return nil, errors.Errorf("invalid user name %q", name)
    77  	}
    78  	lowercaseName := strings.ToLower(name)
    79  
    80  	foundUser := &User{st: st}
    81  	err := st.getUser(names.NewUserTag(name).Name(), &foundUser.doc)
    82  	// No error, the user is already there
    83  	if err == nil {
    84  		if foundUser.doc.Deleted {
    85  			// the user was deleted, we update it
    86  			return st.recreateExistingUser(foundUser, name, displayName, password, creator, secretKey)
    87  		} else {
    88  			return nil, errors.AlreadyExistsf("user %s", name)
    89  		}
    90  	}
    91  
    92  	// There is an error different from not found
    93  	if err != nil && !errors.IsNotFound(err) {
    94  		return nil, errors.Trace(err)
    95  	}
    96  
    97  	dateCreated := st.nowToTheSecond()
    98  	user := &User{
    99  		st: st,
   100  		doc: userDoc{
   101  			DocID:       lowercaseName,
   102  			Name:        name,
   103  			DisplayName: displayName,
   104  			SecretKey:   secretKey,
   105  			CreatedBy:   creator,
   106  			DateCreated: dateCreated,
   107  			Deleted:     false,
   108  			RemovalLog:  []userRemovedLogEntry{},
   109  		},
   110  	}
   111  
   112  	if password != "" {
   113  		salt, err := utils.RandomSalt()
   114  		if err != nil {
   115  			return nil, err
   116  		}
   117  		user.doc.PasswordHash = utils.UserPasswordHash(password, salt)
   118  		user.doc.PasswordSalt = salt
   119  	}
   120  
   121  	ops := []txn.Op{{
   122  		C:      usersC,
   123  		Id:     lowercaseName,
   124  		Assert: txn.DocMissing,
   125  		Insert: &user.doc,
   126  	}}
   127  	controllerUserOps := createControllerUserOps(st.ControllerUUID(),
   128  		names.NewUserTag(name),
   129  		names.NewUserTag(creator),
   130  		displayName,
   131  		dateCreated,
   132  		defaultControllerPermission)
   133  	ops = append(ops, controllerUserOps...)
   134  
   135  	err = st.db().RunTransaction(ops)
   136  	if err != nil {
   137  		if err == txn.ErrAborted {
   138  			err = errors.Errorf("username unavailable")
   139  		}
   140  		return nil, errors.Trace(err)
   141  	}
   142  	return user, nil
   143  }
   144  
   145  func (st *State) recreateExistingUser(u *User, name, displayName, password, creator string, secretKey []byte) (*User, error) {
   146  	dateCreated := st.nowToTheSecond()
   147  	buildTxn := func(attempt int) ([]txn.Op, error) {
   148  		if attempt > 0 {
   149  			err := u.Refresh()
   150  			if err != nil {
   151  				return nil, errors.Trace(err)
   152  			}
   153  			if !u.IsDeleted() {
   154  				return nil, errors.AlreadyExistsf("user %s", name)
   155  			}
   156  		}
   157  
   158  		updateUser := bson.D{{"$set", bson.D{
   159  			{"deleted", false},
   160  			{"name", name},
   161  			{"displayname", displayName},
   162  			{"createdby", creator},
   163  			{"datecreated", dateCreated},
   164  			{"secretkey", secretKey},
   165  		}}}
   166  
   167  		// update the password
   168  		if password != "" {
   169  			salt, err := utils.RandomSalt()
   170  			if err != nil {
   171  				return nil, err
   172  			}
   173  			updateUser = append(updateUser,
   174  				bson.DocElem{"$set", bson.D{
   175  					{"passwordhash", utils.UserPasswordHash(password, salt)},
   176  					{"passwordsalt", salt},
   177  				}},
   178  			)
   179  		}
   180  
   181  		var ops []txn.Op
   182  
   183  		// ensure models that were migrating at the time of the RemoveUser call are
   184  		// processed now.
   185  		modelQuery, closer, err := st.modelQueryForUser(u.UserTag(), false)
   186  		defer closer()
   187  		if err != nil {
   188  			return nil, errors.Trace(err)
   189  		}
   190  		var modelDocs []modelDoc
   191  		if err := modelQuery.All(&modelDocs); err != nil {
   192  			return nil, errors.Trace(err)
   193  		}
   194  		for _, model := range modelDocs {
   195  			// remove the permission for the model
   196  			ops = append(ops, removeModelUserOpsGlobal(model.UUID, u.UserTag())...)
   197  		}
   198  
   199  		// remove previous controller permissions
   200  		if _, err := u.st.controllerUser(u.UserTag()); err == nil {
   201  			ops = append(ops, removeControllerUserOps(st.ControllerUUID(), u.UserTag())...)
   202  		} else if err != nil && !errors.Is(err, errors.NotFound) {
   203  			return nil, errors.Trace(err)
   204  		}
   205  
   206  		// create default new ones
   207  		ops = append(ops, createControllerUserOps(st.ControllerUUID(),
   208  			u.UserTag(),
   209  			names.NewUserTag(creator),
   210  			displayName,
   211  			dateCreated,
   212  			defaultControllerPermission)...)
   213  
   214  		// update user doc
   215  		ops = append(ops, txn.Op{
   216  			C:  usersC,
   217  			Id: strings.ToLower(u.Name()),
   218  			Assert: bson.M{
   219  				"deleted": true,
   220  			},
   221  			Update: updateUser,
   222  		})
   223  
   224  		return ops, nil
   225  	}
   226  
   227  	if err := u.st.db().RunRaw(buildTxn); err != nil {
   228  		return nil, errors.Trace(err)
   229  	}
   230  
   231  	// recreate the user object
   232  	return st.User(u.UserTag())
   233  }
   234  
   235  // RemoveUser marks the user as deleted. This obviates the ability of a user
   236  // to function, but keeps the userDoc retaining provenance, i.e. auditing.
   237  func (st *State) RemoveUser(tag names.UserTag) error {
   238  	lowercaseName := strings.ToLower(tag.Name())
   239  
   240  	u, err := st.User(tag)
   241  	if err != nil {
   242  		return errors.Trace(err)
   243  	}
   244  	if u.IsDeleted() {
   245  		return nil
   246  	}
   247  
   248  	buildTxn := func(attempt int) ([]txn.Op, error) {
   249  		if attempt > 0 {
   250  			// If it is not our first attempt, refresh the user.
   251  			if err := u.Refresh(); err != nil {
   252  				return nil, errors.Trace(err)
   253  			}
   254  			if u.IsDeleted() {
   255  				return nil, nil
   256  			}
   257  		}
   258  
   259  		// remove the access to all the models and the current controller
   260  		// first query all the models for this user
   261  		modelQuery, closer, err := st.modelQueryForUser(tag, false)
   262  		defer closer()
   263  		if err != nil {
   264  			return nil, errors.Trace(err)
   265  		}
   266  		var modelDocs []modelDoc
   267  		if err := modelQuery.All(&modelDocs); err != nil {
   268  			return nil, errors.Trace(err)
   269  		}
   270  		var ops []txn.Op
   271  		for _, model := range modelDocs {
   272  			// remove the permission for the model
   273  			ops = append(ops, removeModelUserOpsGlobal(model.UUID, tag)...)
   274  		}
   275  
   276  		// remove the user from the controller
   277  		ops = append(ops, removeControllerUserOps(st.ControllerUUID(), tag)...)
   278  
   279  		// new entry in the removal log
   280  		newRemovalLogEntry := userRemovedLogEntry{
   281  			RemovedBy:   u.doc.CreatedBy,
   282  			DateCreated: u.doc.DateCreated,
   283  			DateRemoved: st.nowToTheSecond(),
   284  		}
   285  		ops = append(ops, txn.Op{
   286  			Id:     lowercaseName,
   287  			C:      usersC,
   288  			Assert: txn.DocExists,
   289  			Update: bson.M{
   290  				"$set": bson.M{
   291  					"deleted": true,
   292  				},
   293  				"$push": bson.M{
   294  					"removallog": bson.M{"$each": []userRemovedLogEntry{newRemovalLogEntry}},
   295  				},
   296  			},
   297  		})
   298  		return ops, nil
   299  	}
   300  
   301  	// Use raw transactions to avoid model filtering
   302  	return st.db().RunRaw(buildTxn)
   303  }
   304  
   305  func createInitialUserOps(controllerUUID string, user names.UserTag, password, salt string, dateCreated time.Time) []txn.Op {
   306  	lowercaseName := strings.ToLower(user.Name())
   307  	doc := userDoc{
   308  		DocID:        lowercaseName,
   309  		Name:         user.Name(),
   310  		DisplayName:  user.Name(),
   311  		PasswordHash: utils.UserPasswordHash(password, salt),
   312  		PasswordSalt: salt,
   313  		CreatedBy:    user.Name(),
   314  		DateCreated:  dateCreated,
   315  	}
   316  	ops := []txn.Op{{
   317  		C:      usersC,
   318  		Id:     lowercaseName,
   319  		Assert: txn.DocMissing,
   320  		Insert: &doc,
   321  	}}
   322  	controllerUserOps := createControllerUserOps(controllerUUID,
   323  		names.NewUserTag(user.Name()),
   324  		names.NewUserTag(user.Name()),
   325  		user.Name(),
   326  		dateCreated,
   327  		// first user is controller admin.
   328  		permission.SuperuserAccess)
   329  
   330  	ops = append(ops, controllerUserOps...)
   331  	return ops
   332  }
   333  
   334  // getUser fetches information about the user with the
   335  // given name into the provided userDoc.
   336  func (st *State) getUser(name string, udoc *userDoc) error {
   337  	users, closer := st.db().GetCollection(usersC)
   338  	defer closer()
   339  
   340  	name = strings.ToLower(name)
   341  	err := users.Find(bson.D{{"_id", name}}).One(udoc)
   342  	if err == mgo.ErrNotFound {
   343  		err = errors.NotFoundf("user %q", name)
   344  	}
   345  	// DateCreated is inserted as UTC, but read out as local time. So we
   346  	// convert it back to UTC here.
   347  	udoc.DateCreated = udoc.DateCreated.UTC()
   348  	return err
   349  }
   350  
   351  // User returns the state User for the given name.
   352  func (st *State) User(tag names.UserTag) (*User, error) {
   353  	if !tag.IsLocal() {
   354  		return nil, errors.NotFoundf("user %q", tag.Id())
   355  	}
   356  	user := &User{st: st}
   357  	if err := st.getUser(tag.Name(), &user.doc); err != nil {
   358  		return nil, errors.Trace(err)
   359  	}
   360  	if user.doc.Deleted {
   361  		// This error is returned to the apiserver and from there to the api
   362  		// client. So we don't annotate with information regarding deletion.
   363  		// TODO(redir): We'll return a deletedUserError in the future so we can
   364  		// return more appropriate errors, e.g. username not available.
   365  		return nil, newDeletedUserError(user.Name())
   366  	}
   367  	return user, nil
   368  }
   369  
   370  // AllUsers returns a slice of state.User. This includes all active users. If
   371  // includeDeactivated is true it also returns inactive users. At this point it
   372  // never returns deleted users.
   373  func (st *State) AllUsers(includeDeactivated bool) ([]*User, error) {
   374  	var result []*User
   375  
   376  	users, closer := st.db().GetCollection(usersC)
   377  	defer closer()
   378  
   379  	var query bson.D
   380  	// TODO(redir): Provide option to retrieve deleted users in future PR.
   381  	// e.g. if !includeDelted.
   382  	// Ensure the query checks for users without the deleted attribute, and
   383  	// also that if it does that the value is not true. fwereade wanted to be
   384  	// sure we cannot miss users that previously existed without the deleted
   385  	// attr. Since this will only be in 2.0 that should never happen, but...
   386  	// belt and suspenders.
   387  	query = append(query, bson.DocElem{
   388  		"deleted", bson.D{{"$ne", true}},
   389  	})
   390  
   391  	// As above, in the case that a user previously existed and doesn't have a
   392  	// deactivated attribute, we make sure the query checks for the existence
   393  	// of the attribute, and if it exists that it is not true.
   394  	if !includeDeactivated {
   395  		query = append(query, bson.DocElem{
   396  			"deactivated", bson.D{{"$ne", true}},
   397  		})
   398  	}
   399  	iter := users.Find(query).Iter()
   400  	defer iter.Close()
   401  
   402  	var doc userDoc
   403  	for iter.Next(&doc) {
   404  		result = append(result, &User{st: st, doc: doc})
   405  	}
   406  	if err := iter.Close(); err != nil {
   407  		return nil, errors.Trace(err)
   408  	}
   409  	// Always return a predictable order, sort by Name.
   410  	sort.Sort(userList(result))
   411  	return result, nil
   412  }
   413  
   414  // User represents a local user in the database.
   415  type User struct {
   416  	st           *State
   417  	doc          userDoc
   418  	lastLoginDoc userLastLoginDoc //nolint:unused
   419  }
   420  
   421  type userDoc struct {
   422  	DocID        string    `bson:"_id"`
   423  	Name         string    `bson:"name"`
   424  	DisplayName  string    `bson:"displayname"`
   425  	Deactivated  bool      `bson:"deactivated,omitempty"`
   426  	Deleted      bool      `bson:"deleted,omitempty"` // Deleted users are marked deleted but not removed.
   427  	SecretKey    []byte    `bson:"secretkey,omitempty"`
   428  	PasswordHash string    `bson:"passwordhash"`
   429  	PasswordSalt string    `bson:"passwordsalt"`
   430  	CreatedBy    string    `bson:"createdby"`
   431  	DateCreated  time.Time `bson:"datecreated"`
   432  	// RemovalLog keeps a track of removals for this user
   433  	RemovalLog []userRemovedLogEntry `bson:"removallog"`
   434  }
   435  
   436  type userLastLoginDoc struct {
   437  	DocID     string `bson:"_id"`
   438  	ModelUUID string `bson:"model-uuid"`
   439  	// LastLogin is updated by the apiserver whenever the user
   440  	// connects over the API. This update is not done using mgo.txn
   441  	// so this value could well change underneath a normal transaction
   442  	// and as such, it should NEVER appear in any transaction asserts.
   443  	// It is really informational only as far as everyone except the
   444  	// api server is concerned.
   445  	LastLogin time.Time `bson:"last-login"`
   446  }
   447  
   448  // userRemovedLog contains a log of entries added every time the user
   449  // doc has been removed
   450  type userRemovedLogEntry struct {
   451  	RemovedBy   string    `bson:"removedby"`
   452  	DateCreated time.Time `bson:"datecreated"`
   453  	DateRemoved time.Time `bson:"dateremoved"`
   454  }
   455  
   456  // String returns "<name>" where <name> is the Name of the user.
   457  func (u *User) String() string {
   458  	return u.UserTag().Id()
   459  }
   460  
   461  // Name returns the User name.
   462  func (u *User) Name() string {
   463  	return u.doc.Name
   464  }
   465  
   466  // DisplayName returns the display name of the User.
   467  func (u *User) DisplayName() string {
   468  	return u.doc.DisplayName
   469  }
   470  
   471  // CreatedBy returns the name of the User that created this User.
   472  func (u *User) CreatedBy() string {
   473  	return u.doc.CreatedBy
   474  }
   475  
   476  // DateCreated returns when this User was created in UTC.
   477  func (u *User) DateCreated() time.Time {
   478  	return u.doc.DateCreated.UTC()
   479  }
   480  
   481  // Tag returns the Tag for the User.
   482  func (u *User) Tag() names.Tag {
   483  	return u.UserTag()
   484  }
   485  
   486  // UserTag returns the Tag for the User.
   487  func (u *User) UserTag() names.UserTag {
   488  	name := u.doc.Name
   489  	return names.NewLocalUserTag(name)
   490  }
   491  
   492  // LastLogin returns when this User last connected through the API in UTC.
   493  // The resulting time will be nil if the user has never logged in.  In the
   494  // normal case, the LastLogin is the last time that the user connected through
   495  // the API server.
   496  func (u *User) LastLogin() (time.Time, error) {
   497  	lastLogins, closer := u.st.db().GetRawCollection(userLastLoginC)
   498  	defer closer()
   499  
   500  	var lastLogin userLastLoginDoc
   501  	err := lastLogins.FindId(u.doc.DocID).Select(bson.D{{"last-login", 1}}).One(&lastLogin)
   502  	if err != nil {
   503  		if err == mgo.ErrNotFound {
   504  			err = errors.Wrap(err, newNeverLoggedInError(u.UserTag().Name()))
   505  		}
   506  		return time.Time{}, errors.Trace(err)
   507  	}
   508  
   509  	return lastLogin.LastLogin.UTC(), nil
   510  }
   511  
   512  // UpdateLastLogin sets the LastLogin time of the user to be now (to the
   513  // nearest second).
   514  func (u *User) UpdateLastLogin() (err error) {
   515  	if err := u.ensureNotDeleted(); err != nil {
   516  		return errors.Annotate(err, "cannot update last login")
   517  	}
   518  	lastLogins, closer := u.st.db().GetCollection(userLastLoginC)
   519  	defer closer()
   520  
   521  	lastLoginsW := lastLogins.Writeable()
   522  
   523  	// Update the safe mode of the underlying session to not require
   524  	// write majority, nor sync to disk.
   525  	session := lastLoginsW.Underlying().Database.Session
   526  	session.SetSafe(&mgo.Safe{})
   527  
   528  	lastLogin := userLastLoginDoc{
   529  		DocID:     u.doc.DocID,
   530  		ModelUUID: u.st.ModelUUID(),
   531  		LastLogin: u.st.nowToTheSecond(),
   532  	}
   533  
   534  	_, err = lastLoginsW.UpsertId(lastLogin.DocID, lastLogin)
   535  	return errors.Trace(err)
   536  }
   537  
   538  // SecretKey returns the user's secret key, if any.
   539  func (u *User) SecretKey() []byte {
   540  	return u.doc.SecretKey
   541  }
   542  
   543  // SetPassword sets the password associated with the User.
   544  func (u *User) SetPassword(password string) error {
   545  	if err := u.ensureNotDeleted(); err != nil {
   546  		return errors.Annotate(err, "cannot set password")
   547  	}
   548  	salt, err := utils.RandomSalt()
   549  	if err != nil {
   550  		return err
   551  	}
   552  	return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt)
   553  }
   554  
   555  // SetPasswordHash stores the hash and the salt of the
   556  // password. If the User has a secret key set then it
   557  // will be cleared.
   558  func (u *User) SetPasswordHash(pwHash string, pwSalt string) error {
   559  	if err := u.ensureNotDeleted(); err != nil {
   560  		// If we do get a late set of the password this is fine b/c we have an
   561  		// explicit check before login.
   562  		return errors.Annotate(err, "cannot set password hash")
   563  	}
   564  	update := bson.D{{"$set", bson.D{
   565  		{"passwordhash", pwHash},
   566  		{"passwordsalt", pwSalt},
   567  	}}}
   568  	if u.doc.SecretKey != nil {
   569  		update = append(update,
   570  			bson.DocElem{"$unset", bson.D{{"secretkey", ""}}},
   571  		)
   572  	}
   573  	lowercaseName := strings.ToLower(u.Name())
   574  	ops := []txn.Op{{
   575  		C:      usersC,
   576  		Id:     lowercaseName,
   577  		Assert: txn.DocExists,
   578  		Update: update,
   579  	}}
   580  	if err := u.st.db().RunTransaction(ops); err != nil {
   581  		return errors.Annotatef(err, "cannot set password of user %q", u.Name())
   582  	}
   583  	u.doc.PasswordHash = pwHash
   584  	u.doc.PasswordSalt = pwSalt
   585  	u.doc.SecretKey = nil
   586  	return nil
   587  }
   588  
   589  // PasswordValid returns whether the given password is valid for the User. The
   590  // caller should call user.Refresh before calling this.
   591  func (u *User) PasswordValid(password string) bool {
   592  	// If the User is deactivated or deleted, there is no point in carrying on.
   593  	// Since any authentication checks are done very soon after the user is
   594  	// read from the database, there is a very small timeframe where a user
   595  	// could be disabled after it has been read but prior to being checked, but
   596  	// in practice, this isn't a problem.
   597  	if u.IsDisabled() || u.IsDeleted() {
   598  		return false
   599  	}
   600  	if u.doc.PasswordSalt != "" {
   601  		return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash
   602  	}
   603  	return false
   604  }
   605  
   606  // Refresh refreshes information about the User from the state.
   607  func (u *User) Refresh() error {
   608  	var udoc userDoc
   609  	if err := u.st.getUser(u.Name(), &udoc); err != nil {
   610  		return err
   611  	}
   612  	u.doc = udoc
   613  	return nil
   614  }
   615  
   616  // Disable deactivates the user.  Disabled identities cannot log in.
   617  func (u *User) Disable() error {
   618  	if err := u.ensureNotDeleted(); err != nil {
   619  		return errors.Annotate(err, "cannot disable")
   620  	}
   621  	owner, err := u.st.ControllerOwner()
   622  	if err != nil {
   623  		return errors.Trace(err)
   624  	}
   625  	if u.doc.Name == owner.Name() {
   626  		return errors.Unauthorizedf("cannot disable controller model owner")
   627  	}
   628  	return errors.Annotatef(u.setDeactivated(true), "cannot disable user %q", u.Name())
   629  }
   630  
   631  // Enable reactivates the user, setting disabled to false.
   632  func (u *User) Enable() error {
   633  	if err := u.ensureNotDeleted(); err != nil {
   634  		return errors.Annotate(err, "cannot enable")
   635  	}
   636  	return errors.Annotatef(u.setDeactivated(false), "cannot enable user %q", u.Name())
   637  }
   638  
   639  func (u *User) setDeactivated(value bool) error {
   640  	lowercaseName := strings.ToLower(u.Name())
   641  	ops := []txn.Op{{
   642  		C:      usersC,
   643  		Id:     lowercaseName,
   644  		Assert: txn.DocExists,
   645  		Update: bson.D{{"$set", bson.D{{"deactivated", value}}}},
   646  	}}
   647  	if err := u.st.db().RunTransaction(ops); err != nil {
   648  		if err == txn.ErrAborted {
   649  			err = fmt.Errorf("user no longer exists")
   650  		}
   651  		return err
   652  	}
   653  	u.doc.Deactivated = value
   654  	return nil
   655  }
   656  
   657  // IsDisabled returns whether the user is currently enabled.
   658  func (u *User) IsDisabled() bool {
   659  	// Yes, this is a cached value, but in practice the user object is
   660  	// never held around for a long time.
   661  	return u.doc.Deactivated
   662  }
   663  
   664  // IsDeleted returns whether the user is currently deleted.
   665  func (u *User) IsDeleted() bool {
   666  	return u.doc.Deleted
   667  }
   668  
   669  // ensureNotDeleted refreshes the user to ensure it wasn't deleted since we
   670  // acquired it.
   671  func (u *User) ensureNotDeleted() error {
   672  	if err := u.Refresh(); err != nil {
   673  		return errors.Trace(err)
   674  	}
   675  	if u.doc.Deleted {
   676  		return newDeletedUserError(u.Name())
   677  	}
   678  	return nil
   679  }
   680  
   681  // ResetPassword clears the user's password (if there is one),
   682  // and generates a new secret key for the user.
   683  // This must be an active user.
   684  func (u *User) ResetPassword() ([]byte, error) {
   685  	var key []byte
   686  	buildTxn := func(attempt int) ([]txn.Op, error) {
   687  		if err := u.ensureNotDeleted(); err != nil {
   688  			return nil, errors.Trace(err)
   689  		}
   690  		if u.IsDisabled() {
   691  			return nil, fmt.Errorf("user deactivated")
   692  		}
   693  		var err error
   694  		key, err = generateSecretKey()
   695  		if err != nil {
   696  			return nil, errors.Trace(err)
   697  		}
   698  		update := bson.D{
   699  			{
   700  				"$set", bson.D{
   701  					{"secretkey", key},
   702  				},
   703  			},
   704  			{
   705  				"$unset", bson.D{
   706  					{"passwordhash", ""},
   707  					{"passwordsalt", ""},
   708  				},
   709  			},
   710  		}
   711  		lowercaseName := strings.ToLower(u.Name())
   712  		return []txn.Op{{
   713  			C:      usersC,
   714  			Id:     lowercaseName,
   715  			Assert: txn.DocExists,
   716  			Update: update,
   717  		}}, nil
   718  	}
   719  	if err := u.st.db().Run(buildTxn); err != nil {
   720  		return nil, errors.Annotatef(err, "cannot reset password for user %q", u.Name())
   721  	}
   722  	u.doc.SecretKey = key
   723  	u.doc.PasswordHash = ""
   724  	u.doc.PasswordSalt = ""
   725  	return key, nil
   726  }
   727  
   728  // generateSecretKey generates a random, 32-byte secret key.
   729  func generateSecretKey() ([]byte, error) {
   730  	var secretKey [32]byte
   731  	if _, err := rand.Read(secretKey[:]); err != nil {
   732  		return nil, errors.Trace(err)
   733  	}
   734  	return secretKey[:], nil
   735  }
   736  
   737  // userList type is used to provide the methods for sorting.
   738  type userList []*User
   739  
   740  func (u userList) Len() int           { return len(u) }
   741  func (u userList) Swap(i, j int)      { u[i], u[j] = u[j], u[i] }
   742  func (u userList) Less(i, j int) bool { return u[i].Name() < u[j].Name() }