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() }