github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 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/utils" 20 "gopkg.in/juju/names.v2" 21 "gopkg.in/mgo.v2" 22 "gopkg.in/mgo.v2/bson" 23 "gopkg.in/mgo.v2/txn" 24 25 "github.com/juju/juju/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 (st *State) checkUserExists(name string) (bool, error) { 35 users, closer := st.getCollection(usersC) 36 defer closer() 37 38 var count int 39 var err error 40 if count, err = users.FindId(name).Count(); err != nil { 41 return false, err 42 } 43 return count > 0, nil 44 } 45 46 // AddUser adds a user to the database. 47 func (st *State) AddUser(name, displayName, password, creator string) (*User, error) { 48 return st.addUser(name, displayName, password, creator, nil) 49 } 50 51 // AddUserWithSecretKey adds the user with the specified name, and assigns it 52 // a randomly generated secret key. This secret key may be used for the user 53 // and controller to mutually authenticate one another, without without relying 54 // on TLS certificates. 55 // 56 // The new user will not have a password. A password must be set, clearing the 57 // secret key in the process, before the user can login normally. 58 func (st *State) AddUserWithSecretKey(name, displayName, creator string) (*User, error) { 59 // Generate a random, 32-byte secret key. This can be used 60 // to obtain the controller's (self-signed) CA certificate 61 // and set the user's password. 62 var secretKey [32]byte 63 if _, err := rand.Read(secretKey[:]); err != nil { 64 return nil, errors.Trace(err) 65 } 66 return st.addUser(name, displayName, "", creator, secretKey[:]) 67 } 68 69 func (st *State) addUser(name, displayName, password, creator string, secretKey []byte) (*User, error) { 70 if !names.IsValidUserName(name) { 71 return nil, errors.Errorf("invalid user name %q", name) 72 } 73 nameToLower := strings.ToLower(name) 74 75 dateCreated := st.NowToTheSecond() 76 user := &User{ 77 st: st, 78 doc: userDoc{ 79 DocID: nameToLower, 80 Name: name, 81 DisplayName: displayName, 82 SecretKey: secretKey, 83 CreatedBy: creator, 84 DateCreated: dateCreated, 85 }, 86 } 87 88 if password != "" { 89 salt, err := utils.RandomSalt() 90 if err != nil { 91 return nil, err 92 } 93 user.doc.PasswordHash = utils.UserPasswordHash(password, salt) 94 user.doc.PasswordSalt = salt 95 } 96 97 ops := []txn.Op{{ 98 C: usersC, 99 Id: nameToLower, 100 Assert: txn.DocMissing, 101 Insert: &user.doc, 102 }} 103 controllerUserOps := createControllerUserOps(st.ControllerUUID(), 104 names.NewUserTag(name), 105 names.NewUserTag(creator), 106 displayName, 107 dateCreated, 108 defaultControllerPermission) 109 ops = append(ops, controllerUserOps...) 110 111 err := st.runTransaction(ops) 112 if err == txn.ErrAborted { 113 err = errors.AlreadyExistsf("user") 114 } 115 if err != nil { 116 return nil, errors.Trace(err) 117 } 118 return user, nil 119 } 120 121 // RemoveUser marks the user as deleted. This obviates the ability of a user 122 // to function, but keeps the userDoc retaining provenance, i.e. auditing. 123 func (st *State) RemoveUser(tag names.UserTag) error { 124 name := strings.ToLower(tag.Name()) 125 126 u, err := st.User(tag) 127 if err != nil { 128 return errors.Trace(err) 129 } 130 if u.IsDeleted() { 131 return nil 132 } 133 134 buildTxn := func(attempt int) ([]txn.Op, error) { 135 if attempt > 0 { 136 // If it is not our first attempt, refresh the user. 137 if err := u.Refresh(); err != nil { 138 return nil, errors.Trace(err) 139 } 140 } 141 ops := []txn.Op{{ 142 Id: name, 143 C: usersC, 144 Assert: txn.DocExists, 145 Update: bson.M{"$set": bson.M{"deleted": true}}, 146 }} 147 return ops, nil 148 } 149 return st.run(buildTxn) 150 } 151 152 func createInitialUserOps(controllerUUID string, user names.UserTag, password, salt string, dateCreated time.Time) []txn.Op { 153 nameToLower := strings.ToLower(user.Name()) 154 doc := userDoc{ 155 DocID: nameToLower, 156 Name: user.Name(), 157 DisplayName: user.Name(), 158 PasswordHash: utils.UserPasswordHash(password, salt), 159 PasswordSalt: salt, 160 CreatedBy: user.Name(), 161 DateCreated: dateCreated, 162 } 163 ops := []txn.Op{{ 164 C: usersC, 165 Id: nameToLower, 166 Assert: txn.DocMissing, 167 Insert: &doc, 168 }} 169 controllerUserOps := createControllerUserOps(controllerUUID, 170 names.NewUserTag(user.Name()), 171 names.NewUserTag(user.Name()), 172 user.Name(), 173 dateCreated, 174 // first user is controller admin. 175 permission.SuperuserAccess) 176 177 ops = append(ops, controllerUserOps...) 178 return ops 179 180 } 181 182 // getUser fetches information about the user with the 183 // given name into the provided userDoc. 184 func (st *State) getUser(name string, udoc *userDoc) error { 185 users, closer := st.getCollection(usersC) 186 defer closer() 187 188 name = strings.ToLower(name) 189 err := users.Find(bson.D{{"_id", name}}).One(udoc) 190 if err == mgo.ErrNotFound { 191 err = errors.NotFoundf("user %q", name) 192 } 193 // DateCreated is inserted as UTC, but read out as local time. So we 194 // convert it back to UTC here. 195 udoc.DateCreated = udoc.DateCreated.UTC() 196 return err 197 } 198 199 // User returns the state User for the given name. 200 func (st *State) User(tag names.UserTag) (*User, error) { 201 if !tag.IsLocal() { 202 return nil, errors.NotFoundf("user %q", tag.Canonical()) 203 } 204 user := &User{st: st} 205 if err := st.getUser(tag.Name(), &user.doc); err != nil { 206 return nil, errors.Trace(err) 207 } 208 if user.doc.Deleted { 209 // This error is returned to the apiserver and from there to the api 210 // client. So we don't annotate with information regarding deletion. 211 // TODO(redir): We'll return a deletedUserError in the future so we can 212 // return more appropriate errors, e.g. username not available. 213 return nil, errors.UserNotFoundf("%q", user.Name()) 214 } 215 return user, nil 216 } 217 218 // AllUsers returns a slice of state.User. This includes all active users. If 219 // includeDeactivated is true it also returns inactive users. At this point it 220 // never returns deleted users. 221 func (st *State) AllUsers(includeDeactivated bool) ([]*User, error) { 222 var result []*User 223 224 users, closer := st.getCollection(usersC) 225 defer closer() 226 227 var query bson.D 228 // TODO(redir): Provide option to retrieve deleted users in future PR. 229 // e.g. if !includeDelted. 230 // Ensure the query checks for users without the deleted attribute, and 231 // also that if it does that the value is not true. fwereade wanted to be 232 // sure we cannot miss users that previously existed without the deleted 233 // attr. Since this will only be in 2.0 that should never happen, but... 234 // belt and suspenders. 235 query = append(query, bson.DocElem{ 236 "deleted", bson.D{{"$ne", true}}, 237 }) 238 239 // As above, in the case that a user previously existed and doesn't have a 240 // deactivated attribute, we make sure the query checks for the existence 241 // of the attribute, and if it exists that it is not true. 242 if !includeDeactivated { 243 query = append(query, bson.DocElem{ 244 "deactivated", bson.D{{"$ne", true}}, 245 }) 246 } 247 iter := users.Find(query).Iter() 248 defer iter.Close() 249 250 var doc userDoc 251 for iter.Next(&doc) { 252 result = append(result, &User{st: st, doc: doc}) 253 } 254 if err := iter.Err(); err != nil { 255 return nil, errors.Trace(err) 256 } 257 // Always return a predictable order, sort by Name. 258 sort.Sort(userList(result)) 259 return result, nil 260 } 261 262 // User represents a local user in the database. 263 type User struct { 264 st *State 265 doc userDoc 266 lastLoginDoc userLastLoginDoc 267 } 268 269 type userDoc struct { 270 DocID string `bson:"_id"` 271 Name string `bson:"name"` 272 DisplayName string `bson:"displayname"` 273 Deactivated bool `bson:"deactivated,omitempty"` 274 Deleted bool `bson:"deleted,omitempty"` // Deleted users are marked deleted but not removed. 275 SecretKey []byte `bson:"secretkey,omitempty"` 276 PasswordHash string `bson:"passwordhash"` 277 PasswordSalt string `bson:"passwordsalt"` 278 CreatedBy string `bson:"createdby"` 279 DateCreated time.Time `bson:"datecreated"` 280 } 281 282 type userLastLoginDoc struct { 283 DocID string `bson:"_id"` 284 ModelUUID string `bson:"model-uuid"` 285 // LastLogin is updated by the apiserver whenever the user 286 // connects over the API. This update is not done using mgo.txn 287 // so this value could well change underneath a normal transaction 288 // and as such, it should NEVER appear in any transaction asserts. 289 // It is really informational only as far as everyone except the 290 // api server is concerned. 291 LastLogin time.Time `bson:"last-login"` 292 } 293 294 // String returns "<name>@local" where <name> is the Name of the user. 295 func (u *User) String() string { 296 return u.UserTag().Canonical() 297 } 298 299 // Name returns the User name. 300 func (u *User) Name() string { 301 return u.doc.Name 302 } 303 304 // DisplayName returns the display name of the User. 305 func (u *User) DisplayName() string { 306 return u.doc.DisplayName 307 } 308 309 // CreatedBy returns the name of the User that created this User. 310 func (u *User) CreatedBy() string { 311 return u.doc.CreatedBy 312 } 313 314 // DateCreated returns when this User was created in UTC. 315 func (u *User) DateCreated() time.Time { 316 return u.doc.DateCreated.UTC() 317 } 318 319 // Tag returns the Tag for the User. 320 func (u *User) Tag() names.Tag { 321 return u.UserTag() 322 } 323 324 // UserTag returns the Tag for the User. 325 func (u *User) UserTag() names.UserTag { 326 name := u.doc.Name 327 return names.NewLocalUserTag(name) 328 } 329 330 // LastLogin returns when this User last connected through the API in UTC. 331 // The resulting time will be nil if the user has never logged in. In the 332 // normal case, the LastLogin is the last time that the user connected through 333 // the API server. 334 func (u *User) LastLogin() (time.Time, error) { 335 lastLogins, closer := u.st.getRawCollection(userLastLoginC) 336 defer closer() 337 338 var lastLogin userLastLoginDoc 339 err := lastLogins.FindId(u.doc.DocID).Select(bson.D{{"last-login", 1}}).One(&lastLogin) 340 if err != nil { 341 if err == mgo.ErrNotFound { 342 err = errors.Wrap(err, NeverLoggedInError(u.UserTag().Name())) 343 } 344 return time.Time{}, errors.Trace(err) 345 } 346 347 return lastLogin.LastLogin.UTC(), nil 348 } 349 350 // NowToTheSecond returns the current time in UTC to the nearest second. We use 351 // this for a time source that is not more precise than we can handle. When 352 // serializing time in and out of mongo, we lose enough precision that it's 353 // misleading to store any more than precision to the second. 354 func (st *State) NowToTheSecond() time.Time { 355 return st.clock.Now().Round(time.Second).UTC() 356 } 357 358 // NeverLoggedInError is used to indicate that a user has never logged in. 359 type NeverLoggedInError string 360 361 // Error returns the error string for a user who has never logged 362 // in. 363 func (e NeverLoggedInError) Error() string { 364 return `never logged in: "` + string(e) + `"` 365 } 366 367 // IsNeverLoggedInError returns true if err is of type NeverLoggedInError. 368 func IsNeverLoggedInError(err error) bool { 369 _, ok := errors.Cause(err).(NeverLoggedInError) 370 return ok 371 } 372 373 // UpdateLastLogin sets the LastLogin time of the user to be now (to the 374 // nearest second). 375 func (u *User) UpdateLastLogin() (err error) { 376 if err := u.ensureNotDeleted(); err != nil { 377 return errors.Annotate(err, "cannot update last login") 378 } 379 lastLogins, closer := u.st.getCollection(userLastLoginC) 380 defer closer() 381 382 lastLoginsW := lastLogins.Writeable() 383 384 // Update the safe mode of the underlying session to not require 385 // write majority, nor sync to disk. 386 session := lastLoginsW.Underlying().Database.Session 387 session.SetSafe(&mgo.Safe{}) 388 389 lastLogin := userLastLoginDoc{ 390 DocID: u.doc.DocID, 391 ModelUUID: u.st.ModelUUID(), 392 LastLogin: u.st.NowToTheSecond(), 393 } 394 395 _, err = lastLoginsW.UpsertId(lastLogin.DocID, lastLogin) 396 return errors.Trace(err) 397 } 398 399 // SecretKey returns the user's secret key, if any. 400 func (u *User) SecretKey() []byte { 401 return u.doc.SecretKey 402 } 403 404 // SetPassword sets the password associated with the User. 405 func (u *User) SetPassword(password string) error { 406 if err := u.ensureNotDeleted(); err != nil { 407 return errors.Annotate(err, "cannot set password") 408 } 409 salt, err := utils.RandomSalt() 410 if err != nil { 411 return err 412 } 413 return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt) 414 } 415 416 // SetPasswordHash stores the hash and the salt of the 417 // password. If the User has a secret key set then it 418 // will be cleared. 419 func (u *User) SetPasswordHash(pwHash string, pwSalt string) error { 420 if err := u.ensureNotDeleted(); err != nil { 421 // If we do get a late set of the password this is fine b/c we have an 422 // explicit check before login. 423 return errors.Annotate(err, "cannot set password hash") 424 } 425 update := bson.D{{"$set", bson.D{ 426 {"passwordhash", pwHash}, 427 {"passwordsalt", pwSalt}, 428 }}} 429 if u.doc.SecretKey != nil { 430 update = append(update, 431 bson.DocElem{"$unset", bson.D{{"secretkey", ""}}}, 432 ) 433 } 434 ops := []txn.Op{{ 435 C: usersC, 436 Id: u.Name(), 437 Assert: txn.DocExists, 438 Update: update, 439 }} 440 if err := u.st.runTransaction(ops); err != nil { 441 return errors.Annotatef(err, "cannot set password of user %q", u.Name()) 442 } 443 u.doc.PasswordHash = pwHash 444 u.doc.PasswordSalt = pwSalt 445 u.doc.SecretKey = nil 446 return nil 447 } 448 449 // PasswordValid returns whether the given password is valid for the User. The 450 // caller should call user.Refresh before calling this. 451 func (u *User) PasswordValid(password string) bool { 452 // If the User is deactivated or deleted, there is no point in carrying on. 453 // Since any authentication checks are done very soon after the user is 454 // read from the database, there is a very small timeframe where an user 455 // could be disabled after it has been read but prior to being checked, but 456 // in practice, this isn't a problem. 457 if u.IsDisabled() || u.IsDeleted() { 458 return false 459 } 460 if u.doc.PasswordSalt != "" { 461 return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash 462 } 463 return false 464 } 465 466 // Refresh refreshes information about the User from the state. 467 func (u *User) Refresh() error { 468 var udoc userDoc 469 if err := u.st.getUser(u.Name(), &udoc); err != nil { 470 return err 471 } 472 u.doc = udoc 473 return nil 474 } 475 476 // Disable deactivates the user. Disabled identities cannot log in. 477 func (u *User) Disable() error { 478 if err := u.ensureNotDeleted(); err != nil { 479 return errors.Annotate(err, "cannot disable") 480 } 481 environment, err := u.st.ControllerModel() 482 if err != nil { 483 return errors.Trace(err) 484 } 485 if u.doc.Name == environment.Owner().Name() { 486 return errors.Unauthorizedf("cannot disable controller model owner") 487 } 488 return errors.Annotatef(u.setDeactivated(true), "cannot disable user %q", u.Name()) 489 } 490 491 // Enable reactivates the user, setting disabled to false. 492 func (u *User) Enable() error { 493 if err := u.ensureNotDeleted(); err != nil { 494 return errors.Annotate(err, "cannot enable") 495 } 496 return errors.Annotatef(u.setDeactivated(false), "cannot enable user %q", u.Name()) 497 } 498 499 func (u *User) setDeactivated(value bool) error { 500 ops := []txn.Op{{ 501 C: usersC, 502 Id: u.Name(), 503 Assert: txn.DocExists, 504 Update: bson.D{{"$set", bson.D{{"deactivated", value}}}}, 505 }} 506 if err := u.st.runTransaction(ops); err != nil { 507 if err == txn.ErrAborted { 508 err = fmt.Errorf("user no longer exists") 509 } 510 return err 511 } 512 u.doc.Deactivated = value 513 return nil 514 } 515 516 // IsDisabled returns whether the user is currently enabled. 517 func (u *User) IsDisabled() bool { 518 // Yes, this is a cached value, but in practice the user object is 519 // never held around for a long time. 520 return u.doc.Deactivated 521 } 522 523 // IsDeleted returns whether the user is currently deleted. 524 func (u *User) IsDeleted() bool { 525 return u.doc.Deleted 526 } 527 528 // DeletedUserError is used to indicate when an attempt to mutate a deleted 529 // user is attempted. 530 type DeletedUserError struct { 531 UserName string 532 } 533 534 // Error implements the error interface. 535 func (e DeletedUserError) Error() string { 536 return fmt.Sprintf("user %q deleted", e.UserName) 537 } 538 539 // ensureNotDeleted refreshes the user to ensure it wasn't deleted since we 540 // acquired it. 541 func (u *User) ensureNotDeleted() error { 542 if err := u.Refresh(); err != nil { 543 return errors.Trace(err) 544 } 545 if u.doc.Deleted { 546 return DeletedUserError{u.Name()} 547 } 548 return nil 549 } 550 551 // userList type is used to provide the methods for sorting. 552 type userList []*User 553 554 func (u userList) Len() int { return len(u) } 555 func (u userList) Swap(i, j int) { u[i], u[j] = u[j], u[i] } 556 func (u userList) Less(i, j int) bool { return u[i].Name() < u[j].Name() }