github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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" (@local).  In the  world
     6  // where we have external user providers hooked up, there are no records
     7  // in the databse 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/names"
    20  	"github.com/juju/utils"
    21  	"gopkg.in/mgo.v2"
    22  	"gopkg.in/mgo.v2/bson"
    23  	"gopkg.in/mgo.v2/txn"
    24  )
    25  
    26  func (st *State) checkUserExists(name string) (bool, error) {
    27  	users, closer := st.getCollection(usersC)
    28  	defer closer()
    29  
    30  	var count int
    31  	var err error
    32  	if count, err = users.FindId(name).Count(); err != nil {
    33  		return false, err
    34  	}
    35  	return count > 0, nil
    36  }
    37  
    38  // AddUser adds a user to the database.
    39  func (st *State) AddUser(name, displayName, password, creator string) (*User, error) {
    40  	return st.addUser(name, displayName, password, creator, nil)
    41  }
    42  
    43  // AddUserWithSecretKey adds the user with the specified name, and assigns it
    44  // a randomly generated secret key. This secret key may be used for the user
    45  // and controller to mutually authenticate one another, without without relying
    46  // on TLS certificates.
    47  //
    48  // The new user will not have a password. A password must be set, clearing the
    49  // secret key in the process, before the user can login normally.
    50  func (st *State) AddUserWithSecretKey(name, displayName, creator string) (*User, error) {
    51  	// Generate a random, 32-byte secret key. This can be used
    52  	// to obtain the controller's (self-signed) CA certificate
    53  	// and set the user's password.
    54  	var secretKey [32]byte
    55  	if _, err := rand.Read(secretKey[:]); err != nil {
    56  		return nil, errors.Trace(err)
    57  	}
    58  	return st.addUser(name, displayName, "", creator, secretKey[:])
    59  }
    60  
    61  func (st *State) addUser(name, displayName, password, creator string, secretKey []byte) (*User, error) {
    62  	if !names.IsValidUserName(name) {
    63  		return nil, errors.Errorf("invalid user name %q", name)
    64  	}
    65  	nameToLower := strings.ToLower(name)
    66  
    67  	user := &User{
    68  		st: st,
    69  		doc: userDoc{
    70  			DocID:       nameToLower,
    71  			Name:        name,
    72  			DisplayName: displayName,
    73  			SecretKey:   secretKey,
    74  			CreatedBy:   creator,
    75  			DateCreated: nowToTheSecond(),
    76  		},
    77  	}
    78  
    79  	if password != "" {
    80  		salt, err := utils.RandomSalt()
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  		user.doc.PasswordHash = utils.UserPasswordHash(password, salt)
    85  		user.doc.PasswordSalt = salt
    86  	}
    87  
    88  	ops := []txn.Op{{
    89  		C:      usersC,
    90  		Id:     nameToLower,
    91  		Assert: txn.DocMissing,
    92  		Insert: &user.doc,
    93  	}}
    94  	err := st.runTransaction(ops)
    95  	if err == txn.ErrAborted {
    96  		err = errors.AlreadyExistsf("user")
    97  	}
    98  	if err != nil {
    99  		return nil, errors.Trace(err)
   100  	}
   101  	return user, nil
   102  }
   103  
   104  func createInitialUserOp(st *State, user names.UserTag, password, salt string) txn.Op {
   105  	nameToLower := strings.ToLower(user.Name())
   106  	doc := userDoc{
   107  		DocID:        nameToLower,
   108  		Name:         user.Name(),
   109  		DisplayName:  user.Name(),
   110  		PasswordHash: utils.UserPasswordHash(password, salt),
   111  		PasswordSalt: salt,
   112  		CreatedBy:    user.Name(),
   113  		DateCreated:  nowToTheSecond(),
   114  	}
   115  	return txn.Op{
   116  		C:      usersC,
   117  		Id:     nameToLower,
   118  		Assert: txn.DocMissing,
   119  		Insert: &doc,
   120  	}
   121  }
   122  
   123  // getUser fetches information about the user with the
   124  // given name into the provided userDoc.
   125  func (st *State) getUser(name string, udoc *userDoc) error {
   126  	users, closer := st.getCollection(usersC)
   127  	defer closer()
   128  
   129  	name = strings.ToLower(name)
   130  	err := users.Find(bson.D{{"_id", name}}).One(udoc)
   131  	if err == mgo.ErrNotFound {
   132  		err = errors.NotFoundf("user %q", name)
   133  	}
   134  	// DateCreated is inserted as UTC, but read out as local time. So we
   135  	// convert it back to UTC here.
   136  	udoc.DateCreated = udoc.DateCreated.UTC()
   137  	return err
   138  }
   139  
   140  // User returns the state User for the given name.
   141  func (st *State) User(tag names.UserTag) (*User, error) {
   142  	if !tag.IsLocal() {
   143  		return nil, errors.NotFoundf("user %q", tag.Canonical())
   144  	}
   145  	user := &User{st: st}
   146  	if err := st.getUser(tag.Name(), &user.doc); err != nil {
   147  		return nil, errors.Trace(err)
   148  	}
   149  	return user, nil
   150  }
   151  
   152  // User returns the state User for the given name,
   153  func (st *State) AllUsers(includeDeactivated bool) ([]*User, error) {
   154  	var result []*User
   155  
   156  	users, closer := st.getCollection(usersC)
   157  	defer closer()
   158  
   159  	var query bson.D
   160  	if !includeDeactivated {
   161  		query = append(query, bson.DocElem{"deactivated", false})
   162  	}
   163  	iter := users.Find(query).Iter()
   164  	defer iter.Close()
   165  
   166  	var doc userDoc
   167  	for iter.Next(&doc) {
   168  		result = append(result, &User{st: st, doc: doc})
   169  	}
   170  	if err := iter.Err(); err != nil {
   171  		return nil, errors.Trace(err)
   172  	}
   173  	// Always return a predictable order, sort by Name.
   174  	sort.Sort(userList(result))
   175  	return result, nil
   176  }
   177  
   178  // User represents a local user in the database.
   179  type User struct {
   180  	st           *State
   181  	doc          userDoc
   182  	lastLoginDoc userLastLoginDoc
   183  }
   184  
   185  type userDoc struct {
   186  	DocID       string `bson:"_id"`
   187  	Name        string `bson:"name"`
   188  	DisplayName string `bson:"displayname"`
   189  	// Removing users means they still exist, but are marked deactivated
   190  	Deactivated  bool      `bson:"deactivated"`
   191  	SecretKey    []byte    `bson:"secretkey,omitempty"`
   192  	PasswordHash string    `bson:"passwordhash"`
   193  	PasswordSalt string    `bson:"passwordsalt"`
   194  	CreatedBy    string    `bson:"createdby"`
   195  	DateCreated  time.Time `bson:"datecreated"`
   196  }
   197  
   198  type userLastLoginDoc struct {
   199  	DocID     string `bson:"_id"`
   200  	ModelUUID string `bson:"model-uuid"`
   201  	// LastLogin is updated by the apiserver whenever the user
   202  	// connects over the API. This update is not done using mgo.txn
   203  	// so this value could well change underneath a normal transaction
   204  	// and as such, it should NEVER appear in any transaction asserts.
   205  	// It is really informational only as far as everyone except the
   206  	// api server is concerned.
   207  	LastLogin time.Time `bson:"last-login"`
   208  }
   209  
   210  // String returns "<name>@local" where <name> is the Name of the user.
   211  func (u *User) String() string {
   212  	return u.UserTag().Canonical()
   213  }
   214  
   215  // Name returns the User name.
   216  func (u *User) Name() string {
   217  	return u.doc.Name
   218  }
   219  
   220  // DisplayName returns the display name of the User.
   221  func (u *User) DisplayName() string {
   222  	return u.doc.DisplayName
   223  }
   224  
   225  // CreatedBy returns the name of the User that created this User.
   226  func (u *User) CreatedBy() string {
   227  	return u.doc.CreatedBy
   228  }
   229  
   230  // DateCreated returns when this User was created in UTC.
   231  func (u *User) DateCreated() time.Time {
   232  	return u.doc.DateCreated.UTC()
   233  }
   234  
   235  // Tag returns the Tag for the User.
   236  func (u *User) Tag() names.Tag {
   237  	return u.UserTag()
   238  }
   239  
   240  // UserTag returns the Tag for the User.
   241  func (u *User) UserTag() names.UserTag {
   242  	name := u.doc.Name
   243  	if name == "" {
   244  		// TODO(waigani) This is a hack for upgrades to 1.23. Once we are no
   245  		// longer tied to 1.23, we can confidently always use u.doc.Name.
   246  		name = u.doc.DocID
   247  	}
   248  	return names.NewLocalUserTag(name)
   249  }
   250  
   251  // LastLogin returns when this User last connected through the API in UTC.
   252  // The resulting time will be nil if the user has never logged in.  In the
   253  // normal case, the LastLogin is the last time that the user connected through
   254  // the API server.
   255  func (u *User) LastLogin() (time.Time, error) {
   256  	lastLogins, closer := u.st.getRawCollection(userLastLoginC)
   257  	defer closer()
   258  
   259  	var lastLogin userLastLoginDoc
   260  	err := lastLogins.FindId(u.doc.DocID).Select(bson.D{{"last-login", 1}}).One(&lastLogin)
   261  	if err != nil {
   262  		if err == mgo.ErrNotFound {
   263  			err = errors.Wrap(err, NeverLoggedInError(u.UserTag().Name()))
   264  		}
   265  		return time.Time{}, errors.Trace(err)
   266  	}
   267  
   268  	return lastLogin.LastLogin.UTC(), nil
   269  }
   270  
   271  // nowToTheSecond returns the current time in UTC to the nearest second.
   272  // We use this for a time source that is not more precise than we can
   273  // handle. When serializing time in and out of mongo, we lose enough
   274  // precision that it's misleading to store any more than precision to
   275  // the second.
   276  // TODO(fwereade): 2016-03-17 lp:1558657
   277  var nowToTheSecond = func() time.Time { return time.Now().Round(time.Second).UTC() }
   278  
   279  // NeverLoggedInError is used to indicate that a user has never logged in.
   280  type NeverLoggedInError string
   281  
   282  // Error returns the error string for a user who has never logged
   283  // in.
   284  func (e NeverLoggedInError) Error() string {
   285  	return `never logged in: "` + string(e) + `"`
   286  }
   287  
   288  // IsNeverLoggedInError returns true if err is of type NeverLoggedInError.
   289  func IsNeverLoggedInError(err error) bool {
   290  	_, ok := errors.Cause(err).(NeverLoggedInError)
   291  	return ok
   292  }
   293  
   294  // UpdateLastLogin sets the LastLogin time of the user to be now (to the
   295  // nearest second).
   296  func (u *User) UpdateLastLogin() (err error) {
   297  	lastLogins, closer := u.st.getCollection(userLastLoginC)
   298  	defer closer()
   299  
   300  	lastLoginsW := lastLogins.Writeable()
   301  
   302  	// Update the safe mode of the underlying session to not require
   303  	// write majority, nor sync to disk.
   304  	session := lastLoginsW.Underlying().Database.Session
   305  	session.SetSafe(&mgo.Safe{})
   306  
   307  	lastLogin := userLastLoginDoc{
   308  		DocID:     u.doc.DocID,
   309  		ModelUUID: u.st.ModelUUID(),
   310  		LastLogin: nowToTheSecond(),
   311  	}
   312  
   313  	_, err = lastLoginsW.UpsertId(lastLogin.DocID, lastLogin)
   314  	return errors.Trace(err)
   315  }
   316  
   317  // SecretKey returns the user's secret key, if any.
   318  func (u *User) SecretKey() []byte {
   319  	return u.doc.SecretKey
   320  }
   321  
   322  // SetPassword sets the password associated with the User.
   323  func (u *User) SetPassword(password string) error {
   324  	salt, err := utils.RandomSalt()
   325  	if err != nil {
   326  		return err
   327  	}
   328  	return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt)
   329  }
   330  
   331  // SetPasswordHash stores the hash and the salt of the
   332  // password. If the User has a secret key set then it
   333  // will be cleared.
   334  func (u *User) SetPasswordHash(pwHash string, pwSalt string) error {
   335  	update := bson.D{{"$set", bson.D{
   336  		{"passwordhash", pwHash},
   337  		{"passwordsalt", pwSalt},
   338  	}}}
   339  	if u.doc.SecretKey != nil {
   340  		update = append(update,
   341  			bson.DocElem{"$unset", bson.D{{"secretkey", ""}}},
   342  		)
   343  	}
   344  	ops := []txn.Op{{
   345  		C:      usersC,
   346  		Id:     u.Name(),
   347  		Assert: txn.DocExists,
   348  		Update: update,
   349  	}}
   350  	if err := u.st.runTransaction(ops); err != nil {
   351  		return errors.Annotatef(err, "cannot set password of user %q", u.Name())
   352  	}
   353  	u.doc.PasswordHash = pwHash
   354  	u.doc.PasswordSalt = pwSalt
   355  	u.doc.SecretKey = nil
   356  	return nil
   357  }
   358  
   359  // PasswordValid returns whether the given password is valid for the User.
   360  func (u *User) PasswordValid(password string) bool {
   361  	// If the User is deactivated, no point in carrying on. Since any
   362  	// authentication checks are done very soon after the user is read
   363  	// from the database, there is a very small timeframe where an user
   364  	// could be disabled after it has been read but prior to being checked,
   365  	// but in practice, this isn't a problem.
   366  	if u.IsDisabled() {
   367  		return false
   368  	}
   369  	if u.doc.PasswordSalt != "" {
   370  		return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash
   371  	}
   372  	return false
   373  }
   374  
   375  // Refresh refreshes information about the User from the state.
   376  func (u *User) Refresh() error {
   377  	var udoc userDoc
   378  	if err := u.st.getUser(u.Name(), &udoc); err != nil {
   379  		return err
   380  	}
   381  	u.doc = udoc
   382  	return nil
   383  }
   384  
   385  // Disable deactivates the user.  Disabled identities cannot log in.
   386  func (u *User) Disable() error {
   387  	environment, err := u.st.ControllerModel()
   388  	if err != nil {
   389  		return errors.Trace(err)
   390  	}
   391  	if u.doc.Name == environment.Owner().Name() {
   392  		return errors.Unauthorizedf("cannot disable controller model owner")
   393  	}
   394  	return errors.Annotatef(u.setDeactivated(true), "cannot disable user %q", u.Name())
   395  }
   396  
   397  // Enable reactivates the user, setting disabled to false.
   398  func (u *User) Enable() error {
   399  	return errors.Annotatef(u.setDeactivated(false), "cannot enable user %q", u.Name())
   400  }
   401  
   402  func (u *User) setDeactivated(value bool) error {
   403  	ops := []txn.Op{{
   404  		C:      usersC,
   405  		Id:     u.Name(),
   406  		Assert: txn.DocExists,
   407  		Update: bson.D{{"$set", bson.D{{"deactivated", value}}}},
   408  	}}
   409  	if err := u.st.runTransaction(ops); err != nil {
   410  		if err == txn.ErrAborted {
   411  			err = fmt.Errorf("user no longer exists")
   412  		}
   413  		return err
   414  	}
   415  	u.doc.Deactivated = value
   416  	return nil
   417  }
   418  
   419  // IsDisabled returns whether the user is currently enabled.
   420  func (u *User) IsDisabled() bool {
   421  	// Yes, this is a cached value, but in practice the user object is
   422  	// never held around for a long time.
   423  	return u.doc.Deactivated
   424  }
   425  
   426  // userList type is used to provide the methods for sorting.
   427  type userList []*User
   428  
   429  func (u userList) Len() int           { return len(u) }
   430  func (u userList) Swap(i, j int)      { u[i], u[j] = u[j], u[i] }
   431  func (u userList) Less(i, j int) bool { return u[i].Name() < u[j].Name() }