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