1package sqlstore 2 3import ( 4 "context" 5 "fmt" 6 "sort" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/google/uuid" 12 "github.com/grafana/grafana/pkg/bus" 13 "github.com/grafana/grafana/pkg/events" 14 "github.com/grafana/grafana/pkg/models" 15 "github.com/grafana/grafana/pkg/setting" 16 "github.com/grafana/grafana/pkg/util" 17 "github.com/pkg/errors" 18) 19 20func (ss *SQLStore) addUserQueryAndCommandHandlers() { 21 ss.Bus.AddHandlerCtx(ss.GetSignedInUserWithCacheCtx) 22 23 bus.AddHandlerCtx("sql", GetUserById) 24 bus.AddHandlerCtx("sql", UpdateUser) 25 bus.AddHandlerCtx("sql", ChangeUserPassword) 26 bus.AddHandlerCtx("sql", ss.GetUserByLogin) 27 bus.AddHandlerCtx("sql", ss.GetUserByEmail) 28 bus.AddHandlerCtx("sql", SetUsingOrg) 29 bus.AddHandlerCtx("sql", UpdateUserLastSeenAt) 30 bus.AddHandlerCtx("sql", ss.GetUserProfile) 31 bus.AddHandlerCtx("sql", SearchUsers) 32 bus.AddHandlerCtx("sql", GetUserOrgList) 33 bus.AddHandlerCtx("sql", DisableUser) 34 bus.AddHandlerCtx("sql", BatchDisableUsers) 35 bus.AddHandlerCtx("sql", DeleteUser) 36 bus.AddHandlerCtx("sql", SetUserHelpFlag) 37} 38 39func getOrgIdForNewUser(sess *DBSession, cmd models.CreateUserCommand) (int64, error) { 40 if cmd.SkipOrgSetup { 41 return -1, nil 42 } 43 44 if setting.AutoAssignOrg && cmd.OrgId != 0 { 45 err := verifyExistingOrg(sess, cmd.OrgId) 46 if err != nil { 47 return -1, err 48 } 49 return cmd.OrgId, nil 50 } 51 52 orgName := cmd.OrgName 53 if len(orgName) == 0 { 54 orgName = util.StringsFallback2(cmd.Email, cmd.Login) 55 } 56 57 return getOrCreateOrg(sess, orgName) 58} 59 60type userCreationArgs struct { 61 Login string 62 Email string 63 Name string 64 Company string 65 Password string 66 IsAdmin bool 67 IsDisabled bool 68 EmailVerified bool 69 OrgID int64 70 OrgName string 71 DefaultOrgRole string 72} 73 74func (ss *SQLStore) getOrgIDForNewUser(sess *DBSession, args userCreationArgs) (int64, error) { 75 if ss.Cfg.AutoAssignOrg && args.OrgID != 0 { 76 if err := verifyExistingOrg(sess, args.OrgID); err != nil { 77 return -1, err 78 } 79 return args.OrgID, nil 80 } 81 82 orgName := args.OrgName 83 if orgName == "" { 84 orgName = util.StringsFallback2(args.Email, args.Login) 85 } 86 87 return ss.getOrCreateOrg(sess, orgName) 88} 89 90// createUser creates a user in the database. 91func (ss *SQLStore) createUser(ctx context.Context, sess *DBSession, args userCreationArgs, skipOrgSetup bool) (models.User, error) { 92 var user models.User 93 var orgID int64 = -1 94 if !skipOrgSetup { 95 var err error 96 orgID, err = ss.getOrgIDForNewUser(sess, args) 97 if err != nil { 98 return user, err 99 } 100 } 101 102 if args.Email == "" { 103 args.Email = args.Login 104 } 105 106 exists, err := sess.Where("email=? OR login=?", args.Email, args.Login).Get(&models.User{}) 107 if err != nil { 108 return user, err 109 } 110 if exists { 111 return user, models.ErrUserAlreadyExists 112 } 113 114 // create user 115 user = models.User{ 116 Email: args.Email, 117 Name: args.Name, 118 Login: args.Login, 119 Company: args.Company, 120 IsAdmin: args.IsAdmin, 121 IsDisabled: args.IsDisabled, 122 OrgId: orgID, 123 EmailVerified: args.EmailVerified, 124 Created: time.Now(), 125 Updated: time.Now(), 126 LastSeenAt: time.Now().AddDate(-10, 0, 0), 127 } 128 129 salt, err := util.GetRandomString(10) 130 if err != nil { 131 return user, err 132 } 133 user.Salt = salt 134 rands, err := util.GetRandomString(10) 135 if err != nil { 136 return user, err 137 } 138 user.Rands = rands 139 140 if len(args.Password) > 0 { 141 encodedPassword, err := util.EncodePassword(args.Password, user.Salt) 142 if err != nil { 143 return user, err 144 } 145 user.Password = encodedPassword 146 } 147 148 sess.UseBool("is_admin") 149 150 if _, err := sess.Insert(&user); err != nil { 151 return user, err 152 } 153 154 sess.publishAfterCommit(&events.UserCreated{ 155 Timestamp: user.Created, 156 Id: user.Id, 157 Name: user.Name, 158 Login: user.Login, 159 Email: user.Email, 160 }) 161 162 // create org user link 163 if !skipOrgSetup { 164 orgUser := models.OrgUser{ 165 OrgId: orgID, 166 UserId: user.Id, 167 Role: models.ROLE_ADMIN, 168 Created: time.Now(), 169 Updated: time.Now(), 170 } 171 172 if ss.Cfg.AutoAssignOrg && !user.IsAdmin { 173 if len(args.DefaultOrgRole) > 0 { 174 orgUser.Role = models.RoleType(args.DefaultOrgRole) 175 } else { 176 orgUser.Role = models.RoleType(ss.Cfg.AutoAssignOrgRole) 177 } 178 } 179 180 if _, err = sess.Insert(&orgUser); err != nil { 181 return user, err 182 } 183 } 184 185 return user, nil 186} 187 188func (ss *SQLStore) CloneUserToServiceAccount(ctx context.Context, siUser *models.SignedInUser) (*models.User, error) { 189 cmd := models.CreateUserCommand{ 190 Login: "Service-Account-" + uuid.New().String(), 191 Email: uuid.New().String(), 192 Password: "Password-" + uuid.New().String(), 193 Name: siUser.Name + "-Service-Account-" + uuid.New().String(), 194 OrgId: siUser.OrgId, 195 IsServiceAccount: true, 196 } 197 198 newuser, err := ss.CreateUser(ctx, cmd) 199 if err != nil { 200 return nil, errors.Errorf("Failed to create user: %v", err) 201 } 202 203 return newuser, err 204} 205 206func (ss *SQLStore) CreateUser(ctx context.Context, cmd models.CreateUserCommand) (*models.User, error) { 207 var user *models.User 208 err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { 209 orgId, err := getOrgIdForNewUser(sess, cmd) 210 if err != nil { 211 return err 212 } 213 214 if cmd.Email == "" { 215 cmd.Email = cmd.Login 216 } 217 218 exists, err := sess.Where("email=? OR login=?", cmd.Email, cmd.Login).Get(&models.User{}) 219 if err != nil { 220 return err 221 } 222 if exists { 223 return models.ErrUserAlreadyExists 224 } 225 226 // create user 227 user = &models.User{ 228 Email: cmd.Email, 229 Name: cmd.Name, 230 Login: cmd.Login, 231 Company: cmd.Company, 232 IsAdmin: cmd.IsAdmin, 233 IsDisabled: cmd.IsDisabled, 234 OrgId: orgId, 235 EmailVerified: cmd.EmailVerified, 236 Created: time.Now(), 237 Updated: time.Now(), 238 LastSeenAt: time.Now().AddDate(-10, 0, 0), 239 IsServiceAccount: cmd.IsServiceAccount, 240 } 241 242 salt, err := util.GetRandomString(10) 243 if err != nil { 244 return err 245 } 246 user.Salt = salt 247 rands, err := util.GetRandomString(10) 248 if err != nil { 249 return err 250 } 251 user.Rands = rands 252 253 if len(cmd.Password) > 0 { 254 encodedPassword, err := util.EncodePassword(cmd.Password, user.Salt) 255 if err != nil { 256 return err 257 } 258 user.Password = encodedPassword 259 } 260 261 sess.UseBool("is_admin") 262 263 if _, err := sess.Insert(user); err != nil { 264 return err 265 } 266 267 sess.publishAfterCommit(&events.UserCreated{ 268 Timestamp: user.Created, 269 Id: user.Id, 270 Name: user.Name, 271 Login: user.Login, 272 Email: user.Email, 273 }) 274 275 // create org user link 276 if !cmd.SkipOrgSetup { 277 orgUser := models.OrgUser{ 278 OrgId: orgId, 279 UserId: user.Id, 280 Role: models.ROLE_ADMIN, 281 Created: time.Now(), 282 Updated: time.Now(), 283 } 284 285 if setting.AutoAssignOrg && !user.IsAdmin { 286 if len(cmd.DefaultOrgRole) > 0 { 287 orgUser.Role = models.RoleType(cmd.DefaultOrgRole) 288 } else { 289 orgUser.Role = models.RoleType(setting.AutoAssignOrgRole) 290 } 291 } 292 293 if _, err = sess.Insert(&orgUser); err != nil { 294 return err 295 } 296 } 297 298 return nil 299 }) 300 301 return user, err 302} 303 304func GetUserById(ctx context.Context, query *models.GetUserByIdQuery) error { 305 return withDbSession(ctx, x, func(sess *DBSession) error { 306 user := new(models.User) 307 has, err := sess.ID(query.Id).Get(user) 308 309 if err != nil { 310 return err 311 } else if !has { 312 return models.ErrUserNotFound 313 } 314 315 query.Result = user 316 317 return nil 318 }) 319} 320 321func (ss *SQLStore) GetUserByLogin(ctx context.Context, query *models.GetUserByLoginQuery) error { 322 return ss.WithDbSession(ctx, func(sess *DBSession) error { 323 if query.LoginOrEmail == "" { 324 return models.ErrUserNotFound 325 } 326 327 // Try and find the user by login first. 328 // It's not sufficient to assume that a LoginOrEmail with an "@" is an email. 329 user := &models.User{Login: query.LoginOrEmail} 330 has, err := sess.Get(user) 331 332 if err != nil { 333 return err 334 } 335 336 if !has && strings.Contains(query.LoginOrEmail, "@") { 337 // If the user wasn't found, and it contains an "@" fallback to finding the 338 // user by email. 339 user = &models.User{Email: query.LoginOrEmail} 340 has, err = sess.Get(user) 341 } 342 343 if err != nil { 344 return err 345 } else if !has { 346 return models.ErrUserNotFound 347 } 348 349 query.Result = user 350 351 return nil 352 }) 353} 354 355func (ss *SQLStore) GetUserByEmail(ctx context.Context, query *models.GetUserByEmailQuery) error { 356 return ss.WithDbSession(ctx, func(sess *DBSession) error { 357 if query.Email == "" { 358 return models.ErrUserNotFound 359 } 360 361 user := &models.User{Email: query.Email} 362 has, err := sess.Get(user) 363 364 if err != nil { 365 return err 366 } else if !has { 367 return models.ErrUserNotFound 368 } 369 370 query.Result = user 371 372 return nil 373 }) 374} 375 376func UpdateUser(ctx context.Context, cmd *models.UpdateUserCommand) error { 377 return inTransaction(func(sess *DBSession) error { 378 user := models.User{ 379 Name: cmd.Name, 380 Email: cmd.Email, 381 Login: cmd.Login, 382 Theme: cmd.Theme, 383 Updated: time.Now(), 384 } 385 386 if _, err := sess.ID(cmd.UserId).Update(&user); err != nil { 387 return err 388 } 389 390 sess.publishAfterCommit(&events.UserUpdated{ 391 Timestamp: user.Created, 392 Id: user.Id, 393 Name: user.Name, 394 Login: user.Login, 395 Email: user.Email, 396 }) 397 398 return nil 399 }) 400} 401 402func ChangeUserPassword(ctx context.Context, cmd *models.ChangeUserPasswordCommand) error { 403 return inTransaction(func(sess *DBSession) error { 404 user := models.User{ 405 Password: cmd.NewPassword, 406 Updated: time.Now(), 407 } 408 409 _, err := sess.ID(cmd.UserId).Update(&user) 410 return err 411 }) 412} 413 414func UpdateUserLastSeenAt(ctx context.Context, cmd *models.UpdateUserLastSeenAtCommand) error { 415 return inTransaction(func(sess *DBSession) error { 416 user := models.User{ 417 Id: cmd.UserId, 418 LastSeenAt: time.Now(), 419 } 420 421 _, err := sess.ID(cmd.UserId).Update(&user) 422 return err 423 }) 424} 425 426func SetUsingOrg(ctx context.Context, cmd *models.SetUsingOrgCommand) error { 427 getOrgsForUserCmd := &models.GetUserOrgListQuery{UserId: cmd.UserId} 428 if err := GetUserOrgList(ctx, getOrgsForUserCmd); err != nil { 429 return err 430 } 431 432 valid := false 433 for _, other := range getOrgsForUserCmd.Result { 434 if other.OrgId == cmd.OrgId { 435 valid = true 436 } 437 } 438 if !valid { 439 return fmt.Errorf("user does not belong to org") 440 } 441 442 return inTransaction(func(sess *DBSession) error { 443 return setUsingOrgInTransaction(sess, cmd.UserId, cmd.OrgId) 444 }) 445} 446 447func setUsingOrgInTransaction(sess *DBSession, userID int64, orgID int64) error { 448 user := models.User{ 449 Id: userID, 450 OrgId: orgID, 451 } 452 453 _, err := sess.ID(userID).Update(&user) 454 return err 455} 456 457func (ss *SQLStore) GetUserProfile(ctx context.Context, query *models.GetUserProfileQuery) error { 458 return ss.WithDbSession(ctx, func(sess *DBSession) error { 459 var user models.User 460 has, err := sess.ID(query.UserId).Get(&user) 461 462 if err != nil { 463 return err 464 } else if !has { 465 return models.ErrUserNotFound 466 } 467 468 query.Result = models.UserProfileDTO{ 469 Id: user.Id, 470 Name: user.Name, 471 Email: user.Email, 472 Login: user.Login, 473 Theme: user.Theme, 474 IsGrafanaAdmin: user.IsAdmin, 475 IsDisabled: user.IsDisabled, 476 OrgId: user.OrgId, 477 UpdatedAt: user.Updated, 478 CreatedAt: user.Created, 479 } 480 481 return err 482 }) 483} 484 485type byOrgName []*models.UserOrgDTO 486 487// Len returns the length of an array of organisations. 488func (o byOrgName) Len() int { 489 return len(o) 490} 491 492// Swap swaps two indices of an array of organizations. 493func (o byOrgName) Swap(i, j int) { 494 o[i], o[j] = o[j], o[i] 495} 496 497// Less returns whether element i of an array of organizations is less than element j. 498func (o byOrgName) Less(i, j int) bool { 499 if strings.ToLower(o[i].Name) < strings.ToLower(o[j].Name) { 500 return true 501 } 502 503 return o[i].Name < o[j].Name 504} 505 506func GetUserOrgList(ctx context.Context, query *models.GetUserOrgListQuery) error { 507 query.Result = make([]*models.UserOrgDTO, 0) 508 sess := x.Table("org_user") 509 sess.Join("INNER", "org", "org_user.org_id=org.id") 510 sess.Where("org_user.user_id=?", query.UserId) 511 sess.Cols("org.name", "org_user.role", "org_user.org_id") 512 sess.OrderBy("org.name") 513 err := sess.Find(&query.Result) 514 sort.Sort(byOrgName(query.Result)) 515 return err 516} 517 518func newSignedInUserCacheKey(orgID, userID int64) string { 519 return fmt.Sprintf("signed-in-user-%d-%d", userID, orgID) 520} 521 522func (ss *SQLStore) GetSignedInUserWithCacheCtx(ctx context.Context, query *models.GetSignedInUserQuery) error { 523 cacheKey := newSignedInUserCacheKey(query.OrgId, query.UserId) 524 if cached, found := ss.CacheService.Get(cacheKey); found { 525 cachedUser := cached.(models.SignedInUser) 526 query.Result = &cachedUser 527 return nil 528 } 529 530 err := GetSignedInUser(ctx, query) 531 if err != nil { 532 return err 533 } 534 535 cacheKey = newSignedInUserCacheKey(query.Result.OrgId, query.UserId) 536 ss.CacheService.Set(cacheKey, *query.Result, time.Second*5) 537 return nil 538} 539 540func GetSignedInUser(ctx context.Context, query *models.GetSignedInUserQuery) error { 541 orgId := "u.org_id" 542 if query.OrgId > 0 { 543 orgId = strconv.FormatInt(query.OrgId, 10) 544 } 545 546 var rawSQL = `SELECT 547 u.id as user_id, 548 u.is_admin as is_grafana_admin, 549 u.email as email, 550 u.login as login, 551 u.name as name, 552 u.help_flags1 as help_flags1, 553 u.last_seen_at as last_seen_at, 554 (SELECT COUNT(*) FROM org_user where org_user.user_id = u.id) as org_count, 555 org.name as org_name, 556 org_user.role as org_role, 557 org.id as org_id 558 FROM ` + dialect.Quote("user") + ` as u 559 LEFT OUTER JOIN org_user on org_user.org_id = ` + orgId + ` and org_user.user_id = u.id 560 LEFT OUTER JOIN org on org.id = org_user.org_id ` 561 562 sess := x.Table("user") 563 sess = sess.Context(ctx) 564 switch { 565 case query.UserId > 0: 566 sess.SQL(rawSQL+"WHERE u.id=?", query.UserId) 567 case query.Login != "": 568 sess.SQL(rawSQL+"WHERE u.login=?", query.Login) 569 case query.Email != "": 570 sess.SQL(rawSQL+"WHERE u.email=?", query.Email) 571 } 572 573 var user models.SignedInUser 574 has, err := sess.Get(&user) 575 if err != nil { 576 return err 577 } else if !has { 578 return models.ErrUserNotFound 579 } 580 581 if user.OrgRole == "" { 582 user.OrgId = -1 583 user.OrgName = "Org missing" 584 } 585 586 getTeamsByUserQuery := &models.GetTeamsByUserQuery{OrgId: user.OrgId, UserId: user.UserId} 587 err = GetTeamsByUser(ctx, getTeamsByUserQuery) 588 if err != nil { 589 return err 590 } 591 592 user.Teams = make([]int64, len(getTeamsByUserQuery.Result)) 593 for i, t := range getTeamsByUserQuery.Result { 594 user.Teams[i] = t.Id 595 } 596 597 query.Result = &user 598 return err 599} 600 601func SearchUsers(ctx context.Context, query *models.SearchUsersQuery) error { 602 query.Result = models.SearchUserQueryResult{ 603 Users: make([]*models.UserSearchHitDTO, 0), 604 } 605 606 queryWithWildcards := "%" + query.Query + "%" 607 608 whereConditions := make([]string, 0) 609 whereParams := make([]interface{}, 0) 610 sess := x.Table("user").Alias("u") 611 612 // TODO: add to chore, for cleaning up after we have created 613 // service accounts table in the modelling 614 whereConditions = append(whereConditions, "u.is_service_account = false") 615 616 // Join with only most recent auth module 617 joinCondition := `( 618 SELECT id from user_auth 619 WHERE user_auth.user_id = u.id 620 ORDER BY user_auth.created DESC ` 621 joinCondition = "user_auth.id=" + joinCondition + dialect.Limit(1) + ")" 622 sess.Join("LEFT", "user_auth", joinCondition) 623 if query.OrgId > 0 { 624 whereConditions = append(whereConditions, "org_id = ?") 625 whereParams = append(whereParams, query.OrgId) 626 } 627 628 if query.Query != "" { 629 whereConditions = append(whereConditions, "(email "+dialect.LikeStr()+" ? OR name "+dialect.LikeStr()+" ? OR login "+dialect.LikeStr()+" ?)") 630 whereParams = append(whereParams, queryWithWildcards, queryWithWildcards, queryWithWildcards) 631 } 632 633 if query.IsDisabled != nil { 634 whereConditions = append(whereConditions, "is_disabled = ?") 635 whereParams = append(whereParams, query.IsDisabled) 636 } 637 638 if query.AuthModule != "" { 639 whereConditions = append(whereConditions, `auth_module=?`) 640 whereParams = append(whereParams, query.AuthModule) 641 } 642 643 if len(whereConditions) > 0 { 644 sess.Where(strings.Join(whereConditions, " AND "), whereParams...) 645 } 646 647 for _, filter := range query.Filters { 648 if jc := filter.JoinCondition(); jc != nil { 649 sess.Join(jc.Operator, jc.Table, jc.Params) 650 } 651 if ic := filter.InCondition(); ic != nil { 652 sess.In(ic.Condition, ic.Params) 653 } 654 if wc := filter.WhereCondition(); wc != nil { 655 sess.Where(wc.Condition, wc.Params) 656 } 657 } 658 659 if query.Limit > 0 { 660 offset := query.Limit * (query.Page - 1) 661 sess.Limit(query.Limit, offset) 662 } 663 664 sess.Cols("u.id", "u.email", "u.name", "u.login", "u.is_admin", "u.is_disabled", "u.last_seen_at", "user_auth.auth_module") 665 sess.Asc("u.login", "u.email") 666 if err := sess.Find(&query.Result.Users); err != nil { 667 return err 668 } 669 670 // get total 671 user := models.User{} 672 countSess := x.Table("user").Alias("u") 673 674 // Join with user_auth table if users filtered by auth_module 675 if query.AuthModule != "" { 676 countSess.Join("LEFT", "user_auth", joinCondition) 677 } 678 679 if len(whereConditions) > 0 { 680 countSess.Where(strings.Join(whereConditions, " AND "), whereParams...) 681 } 682 683 for _, filter := range query.Filters { 684 if jc := filter.JoinCondition(); jc != nil { 685 countSess.Join(jc.Operator, jc.Table, jc.Params) 686 } 687 if ic := filter.InCondition(); ic != nil { 688 countSess.In(ic.Condition, ic.Params) 689 } 690 if wc := filter.WhereCondition(); wc != nil { 691 countSess.Where(wc.Condition, wc.Params) 692 } 693 } 694 695 count, err := countSess.Count(&user) 696 query.Result.TotalCount = count 697 698 for _, user := range query.Result.Users { 699 user.LastSeenAtAge = util.GetAgeString(user.LastSeenAt) 700 } 701 702 return err 703} 704 705func DisableUser(ctx context.Context, cmd *models.DisableUserCommand) error { 706 user := models.User{} 707 sess := x.Table("user") 708 709 if has, err := sess.ID(cmd.UserId).Get(&user); err != nil { 710 return err 711 } else if !has { 712 return models.ErrUserNotFound 713 } 714 715 user.IsDisabled = cmd.IsDisabled 716 sess.UseBool("is_disabled") 717 718 _, err := sess.ID(cmd.UserId).Update(&user) 719 return err 720} 721 722func BatchDisableUsers(ctx context.Context, cmd *models.BatchDisableUsersCommand) error { 723 return inTransaction(func(sess *DBSession) error { 724 userIds := cmd.UserIds 725 726 if len(userIds) == 0 { 727 return nil 728 } 729 730 user_id_params := strings.Repeat(",?", len(userIds)-1) 731 disableSQL := "UPDATE " + dialect.Quote("user") + " SET is_disabled=? WHERE Id IN (?" + user_id_params + ")" 732 733 disableParams := []interface{}{disableSQL, cmd.IsDisabled} 734 for _, v := range userIds { 735 disableParams = append(disableParams, v) 736 } 737 738 _, err := sess.Exec(disableParams...) 739 if err != nil { 740 return err 741 } 742 743 return nil 744 }) 745} 746 747func DeleteUser(ctx context.Context, cmd *models.DeleteUserCommand) error { 748 return inTransaction(func(sess *DBSession) error { 749 return deleteUserInTransaction(sess, cmd) 750 }) 751} 752 753func deleteUserInTransaction(sess *DBSession, cmd *models.DeleteUserCommand) error { 754 // Check if user exists 755 user := models.User{Id: cmd.UserId} 756 has, err := sess.Get(&user) 757 if err != nil { 758 return err 759 } 760 if !has { 761 return models.ErrUserNotFound 762 } 763 for _, sql := range userDeletions() { 764 _, err := sess.Exec(sql, cmd.UserId) 765 if err != nil { 766 return err 767 } 768 } 769 return nil 770} 771 772func userDeletions() []string { 773 deletes := []string{ 774 "DELETE FROM star WHERE user_id = ?", 775 "DELETE FROM " + dialect.Quote("user") + " WHERE id = ?", 776 "DELETE FROM org_user WHERE user_id = ?", 777 "DELETE FROM dashboard_acl WHERE user_id = ?", 778 "DELETE FROM preferences WHERE user_id = ?", 779 "DELETE FROM team_member WHERE user_id = ?", 780 "DELETE FROM user_auth WHERE user_id = ?", 781 "DELETE FROM user_auth_token WHERE user_id = ?", 782 "DELETE FROM quota WHERE user_id = ?", 783 } 784 return deletes 785} 786 787func ServiceAccountDeletions() []string { 788 deletes := []string{ 789 "DELETE FROM api_key WHERE service_account_id = ?", 790 } 791 deletes = append(deletes, userDeletions()...) 792 return deletes 793} 794 795func (ss *SQLStore) UpdateUserPermissions(userID int64, isAdmin bool) error { 796 return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error { 797 var user models.User 798 if _, err := sess.ID(userID).Get(&user); err != nil { 799 return err 800 } 801 802 user.IsAdmin = isAdmin 803 sess.UseBool("is_admin") 804 805 _, err := sess.ID(user.Id).Update(&user) 806 if err != nil { 807 return err 808 } 809 810 // validate that after update there is at least one server admin 811 if err := validateOneAdminLeft(sess); err != nil { 812 return err 813 } 814 815 return nil 816 }) 817} 818 819func SetUserHelpFlag(ctx context.Context, cmd *models.SetUserHelpFlagCommand) error { 820 return inTransaction(func(sess *DBSession) error { 821 user := models.User{ 822 Id: cmd.UserId, 823 HelpFlags1: cmd.HelpFlags1, 824 Updated: time.Now(), 825 } 826 827 _, err := sess.ID(cmd.UserId).Cols("help_flags1").Update(&user) 828 return err 829 }) 830} 831 832func validateOneAdminLeft(sess *DBSession) error { 833 // validate that there is an admin user left 834 count, err := sess.Where("is_admin=?", true).Count(&models.User{}) 835 if err != nil { 836 return err 837 } 838 839 if count == 0 { 840 return models.ErrLastGrafanaAdmin 841 } 842 843 return nil 844} 845