mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			348 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
| package sqlstore
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 
 | |
| 	"github.com/grafana/grafana/pkg/models"
 | |
| 	ac "github.com/grafana/grafana/pkg/services/accesscontrol"
 | |
| 	"github.com/grafana/grafana/pkg/services/user"
 | |
| )
 | |
| 
 | |
| type getOrgUsersTestCase struct {
 | |
| 	desc             string
 | |
| 	query            *models.GetOrgUsersQuery
 | |
| 	expectedNumUsers int
 | |
| }
 | |
| 
 | |
| func TestSQLStore_GetOrgUsers(t *testing.T) {
 | |
| 	tests := []getOrgUsersTestCase{
 | |
| 		{
 | |
| 			desc: "should return all users",
 | |
| 			query: &models.GetOrgUsersQuery{
 | |
| 				OrgId: 1,
 | |
| 				User: &user.SignedInUser{
 | |
| 					OrgID:       1,
 | |
| 					Permissions: map[int64]map[string][]string{1: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}}},
 | |
| 				},
 | |
| 			},
 | |
| 			expectedNumUsers: 10,
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "should return no users",
 | |
| 			query: &models.GetOrgUsersQuery{
 | |
| 				OrgId: 1,
 | |
| 				User: &user.SignedInUser{
 | |
| 					OrgID:       1,
 | |
| 					Permissions: map[int64]map[string][]string{1: {ac.ActionOrgUsersRead: {""}}},
 | |
| 				},
 | |
| 			},
 | |
| 			expectedNumUsers: 0,
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "should return some users",
 | |
| 			query: &models.GetOrgUsersQuery{
 | |
| 				OrgId: 1,
 | |
| 				User: &user.SignedInUser{
 | |
| 					OrgID: 1,
 | |
| 					Permissions: map[int64]map[string][]string{1: {ac.ActionOrgUsersRead: {
 | |
| 						"users:id:1",
 | |
| 						"users:id:5",
 | |
| 						"users:id:9",
 | |
| 					}}},
 | |
| 				},
 | |
| 			},
 | |
| 			expectedNumUsers: 3,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	store := InitTestDB(t, InitTestDBOpt{})
 | |
| 	store.Cfg.IsEnterprise = true
 | |
| 	defer func() {
 | |
| 		store.Cfg.IsEnterprise = false
 | |
| 	}()
 | |
| 	seedOrgUsers(t, store, 10)
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.desc, func(t *testing.T) {
 | |
| 			err := store.GetOrgUsers(context.Background(), tt.query)
 | |
| 			require.NoError(t, err)
 | |
| 			require.Len(t, tt.query.Result, tt.expectedNumUsers)
 | |
| 
 | |
| 			if !hasWildcardScope(tt.query.User, ac.ActionOrgUsersRead) {
 | |
| 				for _, u := range tt.query.Result {
 | |
| 					assert.Contains(t, tt.query.User.Permissions[tt.query.User.OrgID][ac.ActionOrgUsersRead], fmt.Sprintf("users:id:%d", u.UserId))
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSQLStore_GetOrgUsers_PopulatesCorrectly(t *testing.T) {
 | |
| 	// The millisecond part is not stored in the DB
 | |
| 	constNow := time.Now().UTC().Truncate(time.Second)
 | |
| 	defer mockTimeNow(constNow)()
 | |
| 
 | |
| 	store := InitTestDB(t, InitTestDBOpt{})
 | |
| 	_, err := store.CreateUser(context.Background(), user.CreateUserCommand{
 | |
| 		Login: "Admin",
 | |
| 		Email: "admin@localhost",
 | |
| 		OrgID: 1,
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	newUser, err := store.CreateUser(context.Background(), user.CreateUserCommand{
 | |
| 		Login:      "Viewer",
 | |
| 		Email:      "viewer@localhost",
 | |
| 		OrgID:      1,
 | |
| 		IsDisabled: true,
 | |
| 		Name:       "Viewer Localhost",
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	err = store.AddOrgUser(context.Background(), &models.AddOrgUserCommand{
 | |
| 		Role:   "Viewer",
 | |
| 		OrgId:  1,
 | |
| 		UserId: newUser.ID,
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	query := &models.GetOrgUsersQuery{
 | |
| 		OrgId:  1,
 | |
| 		UserID: newUser.ID,
 | |
| 		User: &user.SignedInUser{
 | |
| 			OrgID:       1,
 | |
| 			Permissions: map[int64]map[string][]string{1: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}}},
 | |
| 		},
 | |
| 	}
 | |
| 	err = store.GetOrgUsers(context.Background(), query)
 | |
| 	require.NoError(t, err)
 | |
| 	require.Len(t, query.Result, 1)
 | |
| 
 | |
| 	actual := query.Result[0]
 | |
| 	assert.Equal(t, int64(1), actual.OrgId)
 | |
| 	assert.Equal(t, newUser.ID, actual.UserId)
 | |
| 	assert.Equal(t, "viewer@localhost", actual.Email)
 | |
| 	assert.Equal(t, "Viewer Localhost", actual.Name)
 | |
| 	assert.Equal(t, "Viewer", actual.Login)
 | |
| 	assert.Equal(t, "Viewer", actual.Role)
 | |
| 	assert.Equal(t, constNow.AddDate(-10, 0, 0), actual.LastSeenAt)
 | |
| 	assert.Equal(t, constNow, actual.Created)
 | |
| 	assert.Equal(t, constNow, actual.Updated)
 | |
| 	assert.Equal(t, true, actual.IsDisabled)
 | |
| }
 | |
| 
 | |
| type searchOrgUsersTestCase struct {
 | |
| 	desc             string
 | |
| 	query            *models.SearchOrgUsersQuery
 | |
| 	expectedNumUsers int
 | |
| }
 | |
| 
 | |
| func TestSQLStore_SearchOrgUsers(t *testing.T) {
 | |
| 	tests := []searchOrgUsersTestCase{
 | |
| 		{
 | |
| 			desc: "should return all users",
 | |
| 			query: &models.SearchOrgUsersQuery{
 | |
| 				OrgID: 1,
 | |
| 				User: &user.SignedInUser{
 | |
| 					OrgID:       1,
 | |
| 					Permissions: map[int64]map[string][]string{1: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}}},
 | |
| 				},
 | |
| 			},
 | |
| 			expectedNumUsers: 10,
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "should return no users",
 | |
| 			query: &models.SearchOrgUsersQuery{
 | |
| 				OrgID: 1,
 | |
| 				User: &user.SignedInUser{
 | |
| 					OrgID:       1,
 | |
| 					Permissions: map[int64]map[string][]string{1: {ac.ActionOrgUsersRead: {""}}},
 | |
| 				},
 | |
| 			},
 | |
| 			expectedNumUsers: 0,
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "should return some users",
 | |
| 			query: &models.SearchOrgUsersQuery{
 | |
| 				OrgID: 1,
 | |
| 				User: &user.SignedInUser{
 | |
| 					OrgID: 1,
 | |
| 					Permissions: map[int64]map[string][]string{1: {ac.ActionOrgUsersRead: {
 | |
| 						"users:id:1",
 | |
| 						"users:id:5",
 | |
| 						"users:id:9",
 | |
| 					}}},
 | |
| 				},
 | |
| 			},
 | |
| 			expectedNumUsers: 3,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	store := InitTestDB(t, InitTestDBOpt{})
 | |
| 	seedOrgUsers(t, store, 10)
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.desc, func(t *testing.T) {
 | |
| 			err := store.SearchOrgUsers(context.Background(), tt.query)
 | |
| 			require.NoError(t, err)
 | |
| 			assert.Len(t, tt.query.Result.OrgUsers, tt.expectedNumUsers)
 | |
| 
 | |
| 			if !hasWildcardScope(tt.query.User, ac.ActionOrgUsersRead) {
 | |
| 				for _, u := range tt.query.Result.OrgUsers {
 | |
| 					assert.Contains(t, tt.query.User.Permissions[tt.query.User.OrgID][ac.ActionOrgUsersRead], fmt.Sprintf("users:id:%d", u.UserId))
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSQLStore_AddOrgUser(t *testing.T) {
 | |
| 	var orgID int64 = 1
 | |
| 	store := InitTestDB(t)
 | |
| 
 | |
| 	// create org and admin
 | |
| 	_, err := store.CreateUser(context.Background(), user.CreateUserCommand{
 | |
| 		Login: "admin",
 | |
| 		OrgID: orgID,
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// create a service account with no org
 | |
| 	sa, err := store.CreateUser(context.Background(), user.CreateUserCommand{
 | |
| 		Login:            "sa-no-org",
 | |
| 		IsServiceAccount: true,
 | |
| 		SkipOrgSetup:     true,
 | |
| 	})
 | |
| 
 | |
| 	require.NoError(t, err)
 | |
| 	require.Equal(t, int64(-1), sa.OrgID)
 | |
| 
 | |
| 	// assign the sa to the org but without the override. should fail
 | |
| 	err = store.AddOrgUser(context.Background(), &models.AddOrgUserCommand{
 | |
| 		Role:   "Viewer",
 | |
| 		OrgId:  orgID,
 | |
| 		UserId: sa.ID,
 | |
| 	})
 | |
| 	require.Error(t, err)
 | |
| 
 | |
| 	// assign the sa to the org with the override. should succeed
 | |
| 	err = store.AddOrgUser(context.Background(), &models.AddOrgUserCommand{
 | |
| 		Role:                      "Viewer",
 | |
| 		OrgId:                     orgID,
 | |
| 		UserId:                    sa.ID,
 | |
| 		AllowAddingServiceAccount: true,
 | |
| 	})
 | |
| 
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// assert the org has been correctly set
 | |
| 	saFound := new(user.User)
 | |
| 	err = store.WithDbSession(context.Background(), func(sess *DBSession) error {
 | |
| 		has, err := sess.ID(sa.ID).Get(saFound)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		} else if !has {
 | |
| 			return user.ErrUserNotFound
 | |
| 		}
 | |
| 		return nil
 | |
| 	})
 | |
| 
 | |
| 	require.NoError(t, err)
 | |
| 	require.Equal(t, saFound.OrgID, orgID)
 | |
| }
 | |
| 
 | |
| func TestSQLStore_RemoveOrgUser(t *testing.T) {
 | |
| 	store := InitTestDB(t)
 | |
| 
 | |
| 	// create org and admin
 | |
| 	_, err := store.CreateUser(context.Background(), user.CreateUserCommand{
 | |
| 		Login: "admin",
 | |
| 		OrgID: 1,
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// create a user with no org
 | |
| 	_, err = store.CreateUser(context.Background(), user.CreateUserCommand{
 | |
| 		Login:        "user",
 | |
| 		OrgID:        1,
 | |
| 		SkipOrgSetup: true,
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// assign the user to the org
 | |
| 	err = store.AddOrgUser(context.Background(), &models.AddOrgUserCommand{
 | |
| 		Role:   "Viewer",
 | |
| 		OrgId:  1,
 | |
| 		UserId: 2,
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// assert the org has been assigned
 | |
| 	user := &models.GetUserByIdQuery{Id: 2}
 | |
| 	err = store.GetUserById(context.Background(), user)
 | |
| 	require.NoError(t, err)
 | |
| 	require.Equal(t, user.Result.OrgID, int64(1))
 | |
| 
 | |
| 	// remove the user org
 | |
| 	err = store.RemoveOrgUser(context.Background(), &models.RemoveOrgUserCommand{
 | |
| 		UserId:                   2,
 | |
| 		OrgId:                    1,
 | |
| 		ShouldDeleteOrphanedUser: false,
 | |
| 	})
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	// assert the org has been removed
 | |
| 	user = &models.GetUserByIdQuery{Id: 2}
 | |
| 	err = store.GetUserById(context.Background(), user)
 | |
| 	require.NoError(t, err)
 | |
| 	require.Equal(t, user.Result.OrgID, int64(0))
 | |
| }
 | |
| 
 | |
| func seedOrgUsers(t *testing.T, store *SQLStore, numUsers int) {
 | |
| 	t.Helper()
 | |
| 	// Seed users
 | |
| 	for i := 1; i <= numUsers; i++ {
 | |
| 		user, err := store.CreateUser(context.Background(), user.CreateUserCommand{
 | |
| 			Login: fmt.Sprintf("user-%d", i),
 | |
| 			OrgID: 1,
 | |
| 		})
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		if i != 1 {
 | |
| 			err = store.AddOrgUser(context.Background(), &models.AddOrgUserCommand{
 | |
| 				Role:   "Viewer",
 | |
| 				OrgId:  1,
 | |
| 				UserId: user.ID,
 | |
| 			})
 | |
| 			require.NoError(t, err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func hasWildcardScope(user *user.SignedInUser, action string) bool {
 | |
| 	for _, scope := range user.Permissions[user.OrgID][action] {
 | |
| 		if strings.HasSuffix(scope, ":*") {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func mockTimeNow(constTime time.Time) func() {
 | |
| 	timeNow = func() time.Time {
 | |
| 		return constTime.Truncate(time.Second)
 | |
| 	}
 | |
| 	return resetTimeNow
 | |
| }
 | |
| 
 | |
| func resetTimeNow() {
 | |
| 	timeNow = time.Now
 | |
| }
 |