2022-06-28 20:32:25 +08:00
package userimpl
import (
"context"
2022-07-19 22:01:05 +08:00
"fmt"
2022-10-04 18:17:55 +08:00
"strconv"
2022-09-28 19:18:19 +08:00
"strings"
2022-09-28 22:17:09 +08:00
"time"
2022-06-28 20:32:25 +08:00
"github.com/grafana/grafana/pkg/events"
2022-10-19 21:02:15 +08:00
"github.com/grafana/grafana/pkg/infra/db"
2022-09-27 19:58:49 +08:00
"github.com/grafana/grafana/pkg/infra/log"
2022-10-05 15:34:36 +08:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
2022-07-19 22:01:05 +08:00
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
2022-06-28 20:32:25 +08:00
"github.com/grafana/grafana/pkg/services/user"
2022-09-27 19:58:49 +08:00
"github.com/grafana/grafana/pkg/setting"
2022-10-05 15:34:36 +08:00
"github.com/grafana/grafana/pkg/util"
2022-06-28 20:32:25 +08:00
)
type store interface {
Insert ( context . Context , * user . User ) ( int64 , error )
Get ( context . Context , * user . User ) ( * user . User , error )
2022-08-02 22:58:05 +08:00
GetByID ( context . Context , int64 ) ( * user . User , error )
2022-07-19 22:01:05 +08:00
GetNotServiceAccount ( context . Context , int64 ) ( * user . User , error )
Delete ( context . Context , int64 ) error
2022-12-08 00:03:22 +08:00
LoginConflict ( ctx context . Context , login , email string , caseInsensitive bool ) error
2022-08-02 22:58:05 +08:00
CaseInsensitiveLoginConflict ( context . Context , string , string ) error
2022-09-28 19:18:19 +08:00
GetByLogin ( context . Context , * user . GetUserByLoginQuery ) ( * user . User , error )
GetByEmail ( context . Context , * user . GetUserByEmailQuery ) ( * user . User , error )
2022-09-28 22:17:09 +08:00
Update ( context . Context , * user . UpdateUserCommand ) error
ChangePassword ( context . Context , * user . ChangeUserPasswordCommand ) error
UpdateLastSeenAt ( context . Context , * user . UpdateUserLastSeenAtCommand ) error
2022-10-04 18:17:55 +08:00
GetSignedInUser ( context . Context , * user . GetSignedInUserQuery ) ( * user . SignedInUser , error )
UpdateUser ( context . Context , * user . User ) error
2022-10-04 20:14:32 +08:00
GetProfile ( context . Context , * user . GetUserProfileQuery ) ( * user . UserProfileDTO , error )
SetHelpFlag ( context . Context , * user . SetUserHelpFlagCommand ) error
UpdatePermissions ( context . Context , int64 , bool ) error
2022-10-05 15:34:36 +08:00
BatchDisableUsers ( context . Context , * user . BatchDisableUsersCommand ) error
Disable ( context . Context , * user . DisableUserCommand ) error
Search ( context . Context , * user . SearchUsersQuery ) ( * user . SearchUserQueryResult , error )
2022-11-15 03:08:10 +08:00
Count ( ctx context . Context ) ( int64 , error )
2023-08-16 16:56:47 +08:00
CountUserAccountsWithEmptyRole ( ctx context . Context ) ( int64 , error )
2022-06-28 20:32:25 +08:00
}
type sqlStore struct {
2022-07-19 22:01:05 +08:00
db db . DB
dialect migrator . Dialect
2022-09-27 19:58:49 +08:00
logger log . Logger
cfg * setting . Cfg
}
func ProvideStore ( db db . DB , cfg * setting . Cfg ) sqlStore {
return sqlStore {
db : db ,
dialect : db . GetDialect ( ) ,
cfg : cfg ,
logger : log . New ( "user.store" ) ,
}
2022-06-28 20:32:25 +08:00
}
func ( ss * sqlStore ) Insert ( ctx context . Context , cmd * user . User ) ( int64 , error ) {
var err error
2022-10-19 21:02:15 +08:00
err = ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-06-28 20:32:25 +08:00
sess . UseBool ( "is_admin" )
2022-12-08 00:03:22 +08:00
if _ , err = sess . Insert ( cmd ) ; err != nil {
2022-06-28 20:32:25 +08:00
return err
}
sess . PublishAfterCommit ( & events . UserCreated {
Timestamp : cmd . Created ,
Id : cmd . ID ,
Name : cmd . Name ,
Login : cmd . Login ,
Email : cmd . Email ,
} )
return nil
} )
if err != nil {
return 0 , err
}
2022-12-08 01:33:40 +08:00
// verify that user was created and cmd.ID was updated with the actual new userID
_ , err = ss . getAnyUserType ( ctx , cmd . ID )
if err != nil {
return 0 , err
}
2022-12-08 00:03:22 +08:00
return cmd . ID , nil
2022-06-28 20:32:25 +08:00
}
2022-07-19 22:01:05 +08:00
func ( ss * sqlStore ) Get ( ctx context . Context , usr * user . User ) ( * user . User , error ) {
2023-04-28 23:38:58 +08:00
ret := & user . User { }
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2023-04-28 23:38:58 +08:00
where := "email=? OR login=?"
login := usr . Login
email := usr . Email
if ss . cfg . CaseInsensitiveLogin {
where = "LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)"
}
exists , err := sess . Where ( where , email , login ) . Get ( ret )
2022-06-28 20:32:25 +08:00
if ! exists {
2022-07-20 20:50:06 +08:00
return user . ErrUserNotFound
2022-06-28 20:32:25 +08:00
}
if err != nil {
return err
}
return nil
} )
if err != nil {
return nil , err
}
2023-04-28 23:38:58 +08:00
return ret , nil
2022-06-28 20:32:25 +08:00
}
2022-07-19 22:01:05 +08:00
func ( ss * sqlStore ) Delete ( ctx context . Context , userID int64 ) error {
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-07-19 22:01:05 +08:00
var rawSQL = "DELETE FROM " + ss . dialect . Quote ( "user" ) + " WHERE id = ?"
_ , err := sess . Exec ( rawSQL , userID )
return err
} )
if err != nil {
return err
}
return nil
}
func ( ss * sqlStore ) GetNotServiceAccount ( ctx context . Context , userID int64 ) ( * user . User , error ) {
2022-07-20 20:50:06 +08:00
usr := user . User { ID : userID }
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-07-20 20:50:06 +08:00
has , err := sess . Where ( ss . notServiceAccountFilter ( ) ) . Get ( & usr )
2022-07-19 22:01:05 +08:00
if err != nil {
return err
}
if ! has {
2022-07-20 20:50:06 +08:00
return user . ErrUserNotFound
2022-07-19 22:01:05 +08:00
}
return nil
} )
2022-07-20 20:50:06 +08:00
return & usr , err
2022-07-19 22:01:05 +08:00
}
2022-08-02 22:58:05 +08:00
func ( ss * sqlStore ) GetByID ( ctx context . Context , userID int64 ) ( * user . User , error ) {
var usr user . User
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-08-02 22:58:05 +08:00
has , err := sess . ID ( & userID ) .
Where ( ss . notServiceAccountFilter ( ) ) .
Get ( & usr )
if err != nil {
return err
} else if ! has {
return user . ErrUserNotFound
}
return nil
} )
return & usr , err
}
2022-07-19 22:01:05 +08:00
func ( ss * sqlStore ) notServiceAccountFilter ( ) string {
return fmt . Sprintf ( "%s.is_service_account = %s" ,
ss . dialect . Quote ( "user" ) ,
ss . dialect . BooleanStr ( false ) )
}
2022-08-02 22:58:05 +08:00
func ( ss * sqlStore ) CaseInsensitiveLoginConflict ( ctx context . Context , login , email string ) error {
users := make ( [ ] user . User , 0 )
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-08-02 22:58:05 +08:00
if err := sess . Where ( "LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)" ,
email , login ) . Find ( & users ) ; err != nil {
return err
}
if len ( users ) > 1 {
return & user . ErrCaseInsensitiveLoginConflict { Users : users }
}
return nil
} )
return err
}
2022-09-28 19:18:19 +08:00
func ( ss * sqlStore ) GetByLogin ( ctx context . Context , query * user . GetUserByLoginQuery ) ( * user . User , error ) {
usr := & user . User { }
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-09-28 19:18:19 +08:00
if query . LoginOrEmail == "" {
return user . ErrUserNotFound
}
2022-10-21 21:21:21 +08:00
var where string
var has bool
var err error
2022-09-28 19:18:19 +08:00
2022-10-21 21:21:21 +08:00
// Since username can be an email address, attempt login with email address
// first if the login field has the "@" symbol.
if strings . Contains ( query . LoginOrEmail , "@" ) {
2022-09-28 19:18:19 +08:00
where = "email=?"
if ss . cfg . CaseInsensitiveLogin {
where = "LOWER(email)=LOWER(?)"
}
2022-10-21 21:21:21 +08:00
has , err = sess . Where ( ss . notServiceAccountFilter ( ) ) . Where ( where , query . LoginOrEmail ) . Get ( usr )
if err != nil {
return err
}
}
// Look for the login field instead of email
if ! has {
where = "login=?"
if ss . cfg . CaseInsensitiveLogin {
where = "LOWER(login)=LOWER(?)"
}
2022-09-28 19:18:19 +08:00
has , err = sess . Where ( ss . notServiceAccountFilter ( ) ) . Where ( where , query . LoginOrEmail ) . Get ( usr )
}
if err != nil {
return err
} else if ! has {
return user . ErrUserNotFound
}
if ss . cfg . CaseInsensitiveLogin {
if err := ss . userCaseInsensitiveLoginConflict ( ctx , sess , usr . Login , usr . Email ) ; err != nil {
return err
}
}
return nil
} )
2022-10-21 21:21:21 +08:00
2023-01-10 22:08:52 +08:00
if err != nil {
return nil , err
}
return usr , nil
2022-09-28 19:18:19 +08:00
}
func ( ss * sqlStore ) GetByEmail ( ctx context . Context , query * user . GetUserByEmailQuery ) ( * user . User , error ) {
usr := & user . User { }
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-09-28 19:18:19 +08:00
if query . Email == "" {
return user . ErrUserNotFound
}
where := "email=?"
if ss . cfg . CaseInsensitiveLogin {
where = "LOWER(email)=LOWER(?)"
}
has , err := sess . Where ( ss . notServiceAccountFilter ( ) ) . Where ( where , query . Email ) . Get ( usr )
if err != nil {
return err
} else if ! has {
return user . ErrUserNotFound
}
if ss . cfg . CaseInsensitiveLogin {
if err := ss . userCaseInsensitiveLoginConflict ( ctx , sess , usr . Login , usr . Email ) ; err != nil {
return err
}
}
return nil
} )
if err != nil {
return nil , err
}
return usr , nil
}
2022-10-19 21:02:15 +08:00
func ( ss * sqlStore ) userCaseInsensitiveLoginConflict ( ctx context . Context , sess * db . Session , login , email string ) error {
2022-09-28 19:18:19 +08:00
users := make ( [ ] user . User , 0 )
if err := sess . Where ( "LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)" ,
email , login ) . Find ( & users ) ; err != nil {
return err
}
if len ( users ) > 1 {
return & user . ErrCaseInsensitiveLoginConflict { Users : users }
}
return nil
}
2022-09-28 22:17:09 +08:00
2022-12-08 00:03:22 +08:00
// LoginConflict returns an error if the provided email or login are already
// associated with a user. If caseInsensitive is true the search is not case
// sensitive.
func ( ss * sqlStore ) LoginConflict ( ctx context . Context , login , email string , caseInsensitive bool ) error {
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
return ss . loginConflict ( ctx , sess , login , email , caseInsensitive )
} )
return err
}
func ( ss * sqlStore ) loginConflict ( ctx context . Context , sess * db . Session , login , email string , caseInsensitive bool ) error {
users := make ( [ ] user . User , 0 )
where := "email=? OR login=?"
if caseInsensitive {
where = "LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)"
login = strings . ToLower ( login )
email = strings . ToLower ( email )
}
exists , err := sess . Where ( where , email , login ) . Get ( & user . User { } )
if err != nil {
return err
}
if exists {
return user . ErrUserAlreadyExists
}
if err := sess . Where ( "LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)" ,
email , login ) . Find ( & users ) ; err != nil {
return err
}
if len ( users ) > 1 {
return & user . ErrCaseInsensitiveLoginConflict { Users : users }
}
return nil
}
2022-09-28 22:17:09 +08:00
func ( ss * sqlStore ) Update ( ctx context . Context , cmd * user . UpdateUserCommand ) error {
if ss . cfg . CaseInsensitiveLogin {
cmd . Login = strings . ToLower ( cmd . Login )
cmd . Email = strings . ToLower ( cmd . Email )
}
2022-10-19 21:02:15 +08:00
return ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-09-28 22:17:09 +08:00
user := user . User {
Name : cmd . Name ,
Email : cmd . Email ,
Login : cmd . Login ,
Theme : cmd . Theme ,
Updated : time . Now ( ) ,
}
if _ , err := sess . ID ( cmd . UserID ) . Where ( ss . notServiceAccountFilter ( ) ) . Update ( & user ) ; err != nil {
return err
}
if ss . cfg . CaseInsensitiveLogin {
if err := ss . userCaseInsensitiveLoginConflict ( ctx , sess , user . Login , user . Email ) ; err != nil {
return err
}
}
sess . PublishAfterCommit ( & events . UserUpdated {
Timestamp : user . Created ,
Id : user . ID ,
Name : user . Name ,
Login : user . Login ,
Email : user . Email ,
} )
return nil
} )
}
func ( ss * sqlStore ) ChangePassword ( ctx context . Context , cmd * user . ChangeUserPasswordCommand ) error {
2022-10-19 21:02:15 +08:00
return ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-09-28 22:17:09 +08:00
user := user . User {
Password : cmd . NewPassword ,
Updated : time . Now ( ) ,
}
_ , err := sess . ID ( cmd . UserID ) . Where ( ss . notServiceAccountFilter ( ) ) . Update ( & user )
return err
} )
}
func ( ss * sqlStore ) UpdateLastSeenAt ( ctx context . Context , cmd * user . UpdateUserLastSeenAtCommand ) error {
2023-08-03 14:26:02 +08:00
if cmd . UserID <= 0 {
return user . ErrUpdateInvalidID
}
2022-10-19 21:02:15 +08:00
return ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-09-28 22:17:09 +08:00
user := user . User {
ID : cmd . UserID ,
LastSeenAt : time . Now ( ) ,
}
_ , err := sess . ID ( cmd . UserID ) . Update ( & user )
return err
} )
}
2022-10-04 18:17:55 +08:00
func ( ss * sqlStore ) GetSignedInUser ( ctx context . Context , query * user . GetSignedInUserQuery ) ( * user . SignedInUser , error ) {
var signedInUser user . SignedInUser
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( dbSess * db . Session ) error {
2022-10-04 18:17:55 +08:00
orgId := "u.org_id"
if query . OrgID > 0 {
orgId = strconv . FormatInt ( query . OrgID , 10 )
}
var rawSQL = ` SELECT
u . id as user_id ,
u . is_admin as is_grafana_admin ,
u . email as email ,
u . login as login ,
u . name as name ,
u . is_disabled as is_disabled ,
u . help_flags1 as help_flags1 ,
u . last_seen_at as last_seen_at ,
org . name as org_name ,
org_user . role as org_role ,
2022-11-04 20:39:54 +08:00
org . id as org_id ,
u . is_service_account as is_service_account
2022-10-04 18:17:55 +08:00
FROM ` + ss.dialect.Quote("user") + ` as u
LEFT OUTER JOIN org_user on org_user . org_id = ` + orgId + ` and org_user . user_id = u . id
LEFT OUTER JOIN org on org . id = org_user . org_id `
sess := dbSess . Table ( "user" )
sess = sess . Context ( ctx )
switch {
case query . UserID > 0 :
sess . SQL ( rawSQL + "WHERE u.id=?" , query . UserID )
case query . Login != "" :
if ss . cfg . CaseInsensitiveLogin {
sess . SQL ( rawSQL + "WHERE LOWER(u.login)=LOWER(?)" , query . Login )
} else {
sess . SQL ( rawSQL + "WHERE u.login=?" , query . Login )
}
case query . Email != "" :
if ss . cfg . CaseInsensitiveLogin {
sess . SQL ( rawSQL + "WHERE LOWER(u.email)=LOWER(?)" , query . Email )
} else {
sess . SQL ( rawSQL + "WHERE u.email=?" , query . Email )
}
2023-08-03 14:26:02 +08:00
default :
return user . ErrNoUniqueID
2022-10-04 18:17:55 +08:00
}
has , err := sess . Get ( & signedInUser )
if err != nil {
return err
} else if ! has {
return user . ErrUserNotFound
}
if signedInUser . OrgRole == "" {
signedInUser . OrgID = - 1
signedInUser . OrgName = "Org missing"
}
return nil
} )
return & signedInUser , err
}
func ( ss * sqlStore ) UpdateUser ( ctx context . Context , user * user . User ) error {
2022-10-19 21:02:15 +08:00
return ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-10-04 18:17:55 +08:00
_ , err := sess . ID ( user . ID ) . Update ( user )
return err
} )
}
2022-10-04 20:14:32 +08:00
func ( ss * sqlStore ) GetProfile ( ctx context . Context , query * user . GetUserProfileQuery ) ( * user . UserProfileDTO , error ) {
var usr user . User
var userProfile user . UserProfileDTO
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-10-04 20:14:32 +08:00
has , err := sess . ID ( query . UserID ) . Where ( ss . notServiceAccountFilter ( ) ) . Get ( & usr )
if err != nil {
return err
} else if ! has {
return user . ErrUserNotFound
}
userProfile = user . UserProfileDTO {
ID : usr . ID ,
Name : usr . Name ,
Email : usr . Email ,
Login : usr . Login ,
Theme : usr . Theme ,
IsGrafanaAdmin : usr . IsAdmin ,
IsDisabled : usr . IsDisabled ,
OrgID : usr . OrgID ,
UpdatedAt : usr . Updated ,
CreatedAt : usr . Created ,
}
return err
} )
return & userProfile , err
}
func ( ss * sqlStore ) SetHelpFlag ( ctx context . Context , cmd * user . SetUserHelpFlagCommand ) error {
2022-10-19 21:02:15 +08:00
return ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-10-04 20:14:32 +08:00
user := user . User {
ID : cmd . UserID ,
HelpFlags1 : cmd . HelpFlags1 ,
2022-10-19 21:02:15 +08:00
Updated : time . Now ( ) ,
2022-10-04 20:14:32 +08:00
}
_ , err := sess . ID ( cmd . UserID ) . Cols ( "help_flags1" ) . Update ( & user )
return err
} )
}
// UpdatePermissions sets the user Server Admin flag
func ( ss * sqlStore ) UpdatePermissions ( ctx context . Context , userID int64 , isAdmin bool ) error {
2022-10-19 21:02:15 +08:00
return ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-10-04 20:14:32 +08:00
var user user . User
if _ , err := sess . ID ( userID ) . Where ( ss . notServiceAccountFilter ( ) ) . Get ( & user ) ; err != nil {
return err
}
user . IsAdmin = isAdmin
sess . UseBool ( "is_admin" )
_ , err := sess . ID ( user . ID ) . Update ( & user )
if err != nil {
return err
}
// validate that after update there is at least one server admin
if err := validateOneAdminLeft ( ctx , sess ) ; err != nil {
return err
}
return nil
} )
}
2022-11-15 03:08:10 +08:00
func ( ss * sqlStore ) Count ( ctx context . Context ) ( int64 , error ) {
type result struct {
Count int64
}
r := result { }
2022-12-08 00:03:22 +08:00
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-11-15 03:08:10 +08:00
rawSQL := fmt . Sprintf ( "SELECT COUNT(*) as count from %s WHERE is_service_account=%s" , ss . db . GetDialect ( ) . Quote ( "user" ) , ss . db . GetDialect ( ) . BooleanStr ( false ) )
if _ , err := sess . SQL ( rawSQL ) . Get ( & r ) ; err != nil {
return err
}
return nil
} )
return r . Count , err
}
2023-08-16 16:56:47 +08:00
func ( ss * sqlStore ) CountUserAccountsWithEmptyRole ( ctx context . Context ) ( int64 , error ) {
sb := & db . SQLBuilder { }
sb . Write ( "SELECT " )
sb . Write ( ` (SELECT COUNT (*) from ` + ss . dialect . Quote ( "org_user" ) + ` AS ou ` +
` LEFT JOIN ` + ss . dialect . Quote ( "user" ) + ` AS u ON u.id = ou.user_id ` +
` WHERE ou.role =? ` +
` AND u.is_service_account = ` + ss . dialect . BooleanStr ( false ) + ` ` +
` AND u.is_disabled = ` + ss . dialect . BooleanStr ( false ) + ` ) AS user_accounts_with_no_role ` )
sb . AddParams ( "None" )
var countStats int64
if err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
_ , err := sess . SQL ( sb . GetSQLString ( ) , sb . GetParams ( ) ... ) . Get ( & countStats )
return err
} ) ; err != nil {
return - 1 , err
}
return countStats , nil
}
2022-10-04 20:14:32 +08:00
// validateOneAdminLeft validate that there is an admin user left
2022-10-19 21:02:15 +08:00
func validateOneAdminLeft ( ctx context . Context , sess * db . Session ) error {
2022-10-04 20:14:32 +08:00
count , err := sess . Where ( "is_admin=?" , true ) . Count ( & user . User { } )
if err != nil {
return err
}
if count == 0 {
return user . ErrLastGrafanaAdmin
}
return nil
}
2022-10-05 15:34:36 +08:00
func ( ss * sqlStore ) BatchDisableUsers ( ctx context . Context , cmd * user . BatchDisableUsersCommand ) error {
2022-10-19 21:02:15 +08:00
return ss . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-10-05 15:34:36 +08:00
userIds := cmd . UserIDs
if len ( userIds ) == 0 {
return nil
}
user_id_params := strings . Repeat ( ",?" , len ( userIds ) - 1 )
disableSQL := "UPDATE " + ss . dialect . Quote ( "user" ) + " SET is_disabled=? WHERE Id IN (?" + user_id_params + ")"
2023-08-30 23:46:47 +08:00
disableParams := [ ] any { disableSQL , cmd . IsDisabled }
2022-10-05 15:34:36 +08:00
for _ , v := range userIds {
disableParams = append ( disableParams , v )
}
_ , err := sess . Where ( ss . notServiceAccountFilter ( ) ) . Exec ( disableParams ... )
return err
} )
}
func ( ss * sqlStore ) Disable ( ctx context . Context , cmd * user . DisableUserCommand ) error {
2022-10-19 21:02:15 +08:00
return ss . db . WithDbSession ( ctx , func ( dbSess * db . Session ) error {
2022-10-05 15:34:36 +08:00
usr := user . User { }
sess := dbSess . Table ( "user" )
if has , err := sess . ID ( cmd . UserID ) . Where ( ss . notServiceAccountFilter ( ) ) . Get ( & usr ) ; err != nil {
return err
} else if ! has {
return user . ErrUserNotFound
}
usr . IsDisabled = cmd . IsDisabled
sess . UseBool ( "is_disabled" )
_ , err := sess . ID ( cmd . UserID ) . Update ( & usr )
return err
} )
}
func ( ss * sqlStore ) Search ( ctx context . Context , query * user . SearchUsersQuery ) ( * user . SearchUserQueryResult , error ) {
result := user . SearchUserQueryResult {
Users : make ( [ ] * user . UserSearchHitDTO , 0 ) ,
}
2022-10-19 21:02:15 +08:00
err := ss . db . WithDbSession ( ctx , func ( dbSess * db . Session ) error {
2022-10-05 15:34:36 +08:00
queryWithWildcards := "%" + query . Query + "%"
whereConditions := make ( [ ] string , 0 )
2023-08-30 23:46:47 +08:00
whereParams := make ( [ ] any , 0 )
2022-10-05 15:34:36 +08:00
sess := dbSess . Table ( "user" ) . Alias ( "u" )
whereConditions = append ( whereConditions , "u.is_service_account = ?" )
whereParams = append ( whereParams , ss . dialect . BooleanStr ( false ) )
// Join with only most recent auth module
joinCondition := ` (
SELECT id from user_auth
WHERE user_auth . user_id = u . id
ORDER BY user_auth . created DESC `
joinCondition = "user_auth.id=" + joinCondition + ss . dialect . Limit ( 1 ) + ")"
sess . Join ( "LEFT" , "user_auth" , joinCondition )
if query . OrgID > 0 {
whereConditions = append ( whereConditions , "org_id = ?" )
whereParams = append ( whereParams , query . OrgID )
}
// user only sees the users for which it has read permissions
2023-08-25 21:19:58 +08:00
acFilter , err := accesscontrol . Filter ( query . SignedInUser , "u.id" , "global.users:id:" , accesscontrol . ActionUsersRead )
if err != nil {
return err
2022-10-05 15:34:36 +08:00
}
2023-08-25 21:19:58 +08:00
whereConditions = append ( whereConditions , acFilter . Where )
whereParams = append ( whereParams , acFilter . Args ... )
2022-10-05 15:34:36 +08:00
if query . Query != "" {
whereConditions = append ( whereConditions , "(email " + ss . dialect . LikeStr ( ) + " ? OR name " + ss . dialect . LikeStr ( ) + " ? OR login " + ss . dialect . LikeStr ( ) + " ?)" )
whereParams = append ( whereParams , queryWithWildcards , queryWithWildcards , queryWithWildcards )
}
if query . IsDisabled != nil {
whereConditions = append ( whereConditions , "is_disabled = ?" )
whereParams = append ( whereParams , query . IsDisabled )
}
if query . AuthModule != "" {
whereConditions = append ( whereConditions , ` auth_module=? ` )
whereParams = append ( whereParams , query . AuthModule )
}
if len ( whereConditions ) > 0 {
sess . Where ( strings . Join ( whereConditions , " AND " ) , whereParams ... )
}
for _ , filter := range query . Filters {
if jc := filter . JoinCondition ( ) ; jc != nil {
sess . Join ( jc . Operator , jc . Table , jc . Params )
}
if ic := filter . InCondition ( ) ; ic != nil {
sess . In ( ic . Condition , ic . Params )
}
if wc := filter . WhereCondition ( ) ; wc != nil {
sess . Where ( wc . Condition , wc . Params )
}
}
if query . Limit > 0 {
offset := query . Limit * ( query . Page - 1 )
sess . Limit ( query . Limit , offset )
}
sess . Cols ( "u.id" , "u.email" , "u.name" , "u.login" , "u.is_admin" , "u.is_disabled" , "u.last_seen_at" , "user_auth.auth_module" )
2023-09-28 16:16:18 +08:00
if len ( query . SortOpts ) > 0 {
for i := range query . SortOpts {
for j := range query . SortOpts [ i ] . Filter {
sess . OrderBy ( query . SortOpts [ i ] . Filter [ j ] . OrderBy ( ) )
}
}
} else {
sess . Asc ( "u.login" , "u.email" )
}
2022-10-05 15:34:36 +08:00
if err := sess . Find ( & result . Users ) ; err != nil {
return err
}
// get total
user := user . User { }
countSess := dbSess . Table ( "user" ) . Alias ( "u" )
// Join with user_auth table if users filtered by auth_module
if query . AuthModule != "" {
countSess . Join ( "LEFT" , "user_auth" , joinCondition )
}
if len ( whereConditions ) > 0 {
countSess . Where ( strings . Join ( whereConditions , " AND " ) , whereParams ... )
}
for _ , filter := range query . Filters {
if jc := filter . JoinCondition ( ) ; jc != nil {
countSess . Join ( jc . Operator , jc . Table , jc . Params )
}
if ic := filter . InCondition ( ) ; ic != nil {
countSess . In ( ic . Condition , ic . Params )
}
if wc := filter . WhereCondition ( ) ; wc != nil {
countSess . Where ( wc . Condition , wc . Params )
}
}
count , err := countSess . Count ( & user )
result . TotalCount = count
for _ , user := range result . Users {
user . LastSeenAtAge = util . GetAgeString ( user . LastSeenAt )
}
return err
} )
return & result , err
}
2022-12-08 01:33:40 +08:00
// getAnyUserType searches for a user record by ID. The user account may be a service account.
func ( ss * sqlStore ) getAnyUserType ( ctx context . Context , userID int64 ) ( * user . User , error ) {
usr := user . User { ID : userID }
err := ss . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
has , err := sess . Get ( & usr )
if err != nil {
return err
}
if ! has {
return user . ErrUserNotFound
}
return nil
} )
return & usr , err
}