mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 13:39:19 -06:00
9ab210a7d7
* Org: use constants for status codes * ServiceAccounts: Avoid creating new orgs for service accounts * Document createUserBehavior * Update pkg/services/sqlstore/org_users_test.go * add doc string to flag
282 lines
6.9 KiB
Go
282 lines
6.9 KiB
Go
package sqlstore
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"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: &models.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: &models.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: &models.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))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
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: &models.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: &models.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: &models.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 models.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 *models.SignedInUser, action string) bool {
|
|
for _, scope := range user.Permissions[user.OrgId][action] {
|
|
if strings.HasSuffix(scope, ":*") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|