826 lines
28 KiB
Go
826 lines
28 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
|
|
"github.com/grafana/grafana/pkg/services/org"
|
|
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
|
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
|
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
|
|
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
|
"github.com/grafana/grafana/pkg/tests/testsuite"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
testsuite.Run(m)
|
|
}
|
|
|
|
// Service Account should not create an org on its own
|
|
func TestIntegrationStore_CreateServiceAccountOrgNonExistant(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
|
|
_, store := setupTestDatabase(t)
|
|
serviceAccountName := "new Service Account"
|
|
t.Run("create service account", func(t *testing.T) {
|
|
serviceAccountOrgId := int64(1)
|
|
serviceAccountRole := org.RoleAdmin
|
|
isDisabled := true
|
|
saForm := serviceaccounts.CreateServiceAccountForm{
|
|
Name: serviceAccountName,
|
|
Role: &serviceAccountRole,
|
|
IsDisabled: &isDisabled,
|
|
}
|
|
|
|
_, err := store.CreateServiceAccount(context.Background(), serviceAccountOrgId, &saForm)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestStore_CreateServiceAccount(t *testing.T) {
|
|
serviceAccountName := "new Service Account"
|
|
t.Run("create service account", func(t *testing.T) {
|
|
_, store := setupTestDatabase(t)
|
|
orgQuery := &org.CreateOrgCommand{Name: orgimpl.MainOrgName}
|
|
orgResult, err := store.orgService.CreateWithMember(context.Background(), orgQuery)
|
|
require.NoError(t, err)
|
|
serviceAccountOrgId := orgResult.ID
|
|
serviceAccountRole := org.RoleAdmin
|
|
isDisabled := true
|
|
saForm := serviceaccounts.CreateServiceAccountForm{
|
|
Name: serviceAccountName,
|
|
Role: &serviceAccountRole,
|
|
IsDisabled: &isDisabled,
|
|
}
|
|
|
|
saDTO, err := store.CreateServiceAccount(context.Background(), serviceAccountOrgId, &saForm)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, saDTO.Name)
|
|
assert.Equal(t, 0, int(saDTO.Tokens))
|
|
|
|
retrieved, err := store.RetrieveServiceAccount(context.Background(), &serviceaccounts.GetServiceAccountQuery{
|
|
OrgID: serviceAccountOrgId,
|
|
ID: saDTO.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, retrieved.Name)
|
|
assert.Equal(t, serviceAccountOrgId, retrieved.OrgId)
|
|
assert.Equal(t, string(serviceAccountRole), retrieved.Role)
|
|
assert.True(t, retrieved.IsDisabled)
|
|
|
|
retrievedId, err := store.RetrieveServiceAccountIdByName(context.Background(), serviceAccountOrgId, serviceAccountName)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, saDTO.Id, retrievedId)
|
|
})
|
|
|
|
t.Run("create service account twice same org, error", func(t *testing.T) {
|
|
_, store := setupTestDatabase(t)
|
|
orgQuery := &org.CreateOrgCommand{Name: orgimpl.MainOrgName}
|
|
orgResult, err := store.orgService.CreateWithMember(context.Background(), orgQuery)
|
|
require.NoError(t, err)
|
|
serviceAccountOrgId := orgResult.ID
|
|
serviceAccountRole := org.RoleAdmin
|
|
isDisabled := true
|
|
saForm := serviceaccounts.CreateServiceAccountForm{
|
|
Name: serviceAccountName,
|
|
Role: &serviceAccountRole,
|
|
IsDisabled: &isDisabled,
|
|
}
|
|
|
|
saDTO, err := store.CreateServiceAccount(context.Background(), serviceAccountOrgId, &saForm)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, saDTO.Name)
|
|
assert.Equal(t, 0, int(saDTO.Tokens))
|
|
|
|
retrieved, err := store.RetrieveServiceAccount(context.Background(), &serviceaccounts.GetServiceAccountQuery{
|
|
OrgID: serviceAccountOrgId,
|
|
ID: saDTO.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, retrieved.Name)
|
|
assert.Equal(t, serviceAccountOrgId, retrieved.OrgId)
|
|
assert.Equal(t, string(serviceAccountRole), retrieved.Role)
|
|
assert.True(t, retrieved.IsDisabled)
|
|
|
|
retrievedId, err := store.RetrieveServiceAccountIdByName(context.Background(), serviceAccountOrgId, serviceAccountName)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, saDTO.Id, retrievedId)
|
|
|
|
// should not b able to create the same service account twice in the same org
|
|
_, err = store.CreateServiceAccount(context.Background(), serviceAccountOrgId, &saForm)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("create service account twice different orgs should work", func(t *testing.T) {
|
|
_, store := setupTestDatabase(t)
|
|
orgQuery := &org.CreateOrgCommand{Name: orgimpl.MainOrgName}
|
|
orgResult, err := store.orgService.CreateWithMember(context.Background(), orgQuery)
|
|
require.NoError(t, err)
|
|
serviceAccountOrgId := orgResult.ID
|
|
serviceAccountRole := org.RoleAdmin
|
|
isDisabled := true
|
|
saForm := serviceaccounts.CreateServiceAccountForm{
|
|
Name: serviceAccountName,
|
|
Role: &serviceAccountRole,
|
|
IsDisabled: &isDisabled,
|
|
}
|
|
|
|
saDTO, err := store.CreateServiceAccount(context.Background(), serviceAccountOrgId, &saForm)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, saDTO.Name)
|
|
assert.Equal(t, 0, int(saDTO.Tokens))
|
|
|
|
retrieved, err := store.RetrieveServiceAccount(context.Background(), &serviceaccounts.GetServiceAccountQuery{
|
|
OrgID: serviceAccountOrgId,
|
|
ID: saDTO.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, retrieved.Name)
|
|
assert.Equal(t, serviceAccountOrgId, retrieved.OrgId)
|
|
assert.Equal(t, string(serviceAccountRole), retrieved.Role)
|
|
assert.True(t, retrieved.IsDisabled)
|
|
|
|
retrievedId, err := store.RetrieveServiceAccountIdByName(context.Background(), serviceAccountOrgId, serviceAccountName)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, saDTO.Id, retrievedId)
|
|
|
|
orgQuerySecond := &org.CreateOrgCommand{Name: "Second Org name"}
|
|
orgResultSecond, err := store.orgService.CreateWithMember(context.Background(), orgQuerySecond)
|
|
require.NoError(t, err)
|
|
serviceAccountOrgIdSecond := orgResultSecond.ID
|
|
// should not b able to create the same service account twice in the same org
|
|
saDTOSecond, err := store.CreateServiceAccount(context.Background(), serviceAccountOrgIdSecond, &saForm)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, saDTOSecond.Name)
|
|
assert.Equal(t, 0, int(saDTOSecond.Tokens))
|
|
})
|
|
}
|
|
|
|
func TestIntegrationStore_CreateServiceAccountRoleNone(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
|
|
_, store := setupTestDatabase(t)
|
|
orgQuery := &org.CreateOrgCommand{Name: orgimpl.MainOrgName}
|
|
orgResult, err := store.orgService.CreateWithMember(context.Background(), orgQuery)
|
|
require.NoError(t, err)
|
|
|
|
serviceAccountName := "new Service Account"
|
|
serviceAccountOrgId := orgResult.ID
|
|
serviceAccountRole := org.RoleNone
|
|
saForm := serviceaccounts.CreateServiceAccountForm{
|
|
Name: serviceAccountName,
|
|
Role: &serviceAccountRole,
|
|
IsDisabled: nil,
|
|
}
|
|
|
|
saDTO, err := store.CreateServiceAccount(context.Background(), serviceAccountOrgId, &saForm)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, saDTO.Name)
|
|
assert.Equal(t, 0, int(saDTO.Tokens))
|
|
|
|
retrieved, err := store.RetrieveServiceAccount(context.Background(), &serviceaccounts.GetServiceAccountQuery{
|
|
OrgID: serviceAccountOrgId,
|
|
ID: saDTO.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, serviceAccountName, retrieved.Name)
|
|
assert.Equal(t, serviceAccountOrgId, retrieved.OrgId)
|
|
assert.Equal(t, string(serviceAccountRole), retrieved.Role)
|
|
|
|
retrievedId, err := store.RetrieveServiceAccountIdByName(context.Background(), serviceAccountOrgId, serviceAccountName)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, saDTO.Id, retrievedId)
|
|
assert.Equal(t, saDTO.Role, string(org.RoleNone))
|
|
}
|
|
|
|
func TestIntegrationStore_DeleteServiceAccount(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
cases := []struct {
|
|
desc string
|
|
user tests.TestUser
|
|
expectedErr error
|
|
}{
|
|
{
|
|
desc: "service accounts should exist and get deleted",
|
|
user: tests.TestUser{Login: "servicetest1@admin", IsServiceAccount: true},
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
desc: "service accounts is false should not delete the user",
|
|
user: tests.TestUser{Login: "test1@admin", IsServiceAccount: false},
|
|
expectedErr: serviceaccounts.ErrServiceAccountNotFound,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.desc, func(t *testing.T) {
|
|
db, store := setupTestDatabase(t)
|
|
user := tests.SetupUserServiceAccount(t, db, store.cfg, c.user)
|
|
err := store.DeleteServiceAccount(context.Background(), user.OrgID, user.ID)
|
|
if c.expectedErr != nil {
|
|
require.ErrorIs(t, err, c.expectedErr)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func setupTestDatabase(t *testing.T) (db.DB, *ServiceAccountsStoreImpl) {
|
|
t.Helper()
|
|
db, cfg := db.InitTestDBWithCfg(t)
|
|
quotaService := quotatest.New(false, nil)
|
|
apiKeyService, err := apikeyimpl.ProvideService(db, cfg, quotaService)
|
|
require.NoError(t, err)
|
|
kvStore := kvstore.ProvideService(db)
|
|
orgService, err := orgimpl.ProvideService(db, cfg, quotaService)
|
|
require.NoError(t, err)
|
|
userSvc, err := userimpl.ProvideService(
|
|
db, orgService, cfg, nil, nil, tracing.InitializeTracerForTest(),
|
|
quotaService, supportbundlestest.NewFakeBundleService(),
|
|
)
|
|
require.NoError(t, err)
|
|
return db, ProvideServiceAccountsStore(cfg, db, apiKeyService, kvStore, userSvc, orgService)
|
|
}
|
|
|
|
func TestIntegrationStore_RetrieveServiceAccount(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
cases := []struct {
|
|
desc string
|
|
user tests.TestUser
|
|
retrieveByUID bool
|
|
expectedErr error
|
|
}{
|
|
{
|
|
desc: "service accounts should exist and get retrieved",
|
|
user: tests.TestUser{Login: "servicetest1@admin", IsServiceAccount: true},
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
desc: "service accounts should be able to be retrieved with uid",
|
|
user: tests.TestUser{Login: "test1@admin", IsServiceAccount: true},
|
|
expectedErr: nil,
|
|
retrieveByUID: true,
|
|
},
|
|
{
|
|
desc: "service accounts is false should not retrieve user",
|
|
user: tests.TestUser{Login: "test1@admin", IsServiceAccount: false},
|
|
expectedErr: serviceaccounts.ErrServiceAccountNotFound,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.desc, func(t *testing.T) {
|
|
var dto *serviceaccounts.ServiceAccountProfileDTO
|
|
var err error
|
|
db, store := setupTestDatabase(t)
|
|
user := tests.SetupUserServiceAccount(t, db, store.cfg, c.user)
|
|
if c.retrieveByUID {
|
|
dto, err = store.RetrieveServiceAccount(context.Background(), &serviceaccounts.GetServiceAccountQuery{
|
|
OrgID: user.OrgID,
|
|
UID: user.UID,
|
|
})
|
|
} else {
|
|
dto, err = store.RetrieveServiceAccount(context.Background(), &serviceaccounts.GetServiceAccountQuery{
|
|
OrgID: user.OrgID,
|
|
ID: user.ID,
|
|
})
|
|
}
|
|
if c.expectedErr != nil {
|
|
require.ErrorIs(t, err, c.expectedErr)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, c.user.Login, dto.Login)
|
|
require.Len(t, dto.Teams, 0)
|
|
if c.retrieveByUID {
|
|
require.Equal(t, user.UID, dto.UID)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIntegrationStore_MigrateApiKeys(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
cases := []struct {
|
|
desc string
|
|
serviceAccounts []user.CreateUserCommand
|
|
key tests.TestApiKey
|
|
expectedLogin string
|
|
expectedErr error
|
|
}{
|
|
{
|
|
desc: "api key should be migrated to service account token",
|
|
serviceAccounts: []user.CreateUserCommand{},
|
|
key: tests.TestApiKey{Name: "test1", Role: org.RoleEditor, OrgId: 1},
|
|
expectedLogin: "sa-autogen-1-test1",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
desc: "api key should be migrated to service account token on second attempt",
|
|
serviceAccounts: []user.CreateUserCommand{
|
|
{Login: "sa-autogen-1-test2"},
|
|
},
|
|
key: tests.TestApiKey{Name: "test2", Role: org.RoleEditor, OrgId: 1},
|
|
expectedLogin: "sa-autogen-1-test2-001",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
desc: "api key should be migrated to service account token on last attempt (the 10th)",
|
|
serviceAccounts: []user.CreateUserCommand{
|
|
{Login: "sa-autogen-1-test3"},
|
|
{Login: "sa-autogen-1-test3-001"},
|
|
{Login: "sa-autogen-1-test3-002"},
|
|
{Login: "sa-autogen-1-test3-003"},
|
|
{Login: "sa-autogen-1-test3-004"},
|
|
{Login: "sa-autogen-1-test3-005"},
|
|
{Login: "sa-autogen-1-test3-006"},
|
|
{Login: "sa-autogen-1-test3-007"},
|
|
{Login: "sa-autogen-1-test3-008"},
|
|
{Login: "sa-autogen-1-test3-009"},
|
|
},
|
|
key: tests.TestApiKey{Name: "test3", Role: org.RoleEditor, OrgId: 1},
|
|
expectedLogin: "sa-autogen-1-test3-010",
|
|
expectedErr: nil,
|
|
},
|
|
{
|
|
desc: "api key should not be migrated to service account token because all attempts failed",
|
|
serviceAccounts: []user.CreateUserCommand{
|
|
{Login: "sa-autogen-1-test4"},
|
|
{Login: "sa-autogen-1-test4-001"},
|
|
{Login: "sa-autogen-1-test4-002"},
|
|
{Login: "sa-autogen-1-test4-003"},
|
|
{Login: "sa-autogen-1-test4-004"},
|
|
{Login: "sa-autogen-1-test4-005"},
|
|
{Login: "sa-autogen-1-test4-006"},
|
|
{Login: "sa-autogen-1-test4-007"},
|
|
{Login: "sa-autogen-1-test4-008"},
|
|
{Login: "sa-autogen-1-test4-009"},
|
|
{Login: "sa-autogen-1-test4-010"},
|
|
},
|
|
key: tests.TestApiKey{Name: "test4", Role: org.RoleEditor, OrgId: 1},
|
|
expectedErr: serviceaccounts.ErrServiceAccountAlreadyExists,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.desc, func(t *testing.T) {
|
|
db, store := setupTestDatabase(t)
|
|
|
|
store.cfg.AutoAssignOrg = true
|
|
store.cfg.AutoAssignOrgId = 1
|
|
store.cfg.AutoAssignOrgRole = "Viewer"
|
|
_, err := store.orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: "main"})
|
|
require.NoError(t, err)
|
|
|
|
key := tests.SetupApiKey(t, db, store.cfg, c.key)
|
|
|
|
for _, sa := range c.serviceAccounts {
|
|
sa.IsServiceAccount = true
|
|
sa.OrgID = key.OrgID
|
|
_, err := store.userService.CreateServiceAccount(context.Background(), &sa)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
err = store.MigrateApiKey(context.Background(), key.OrgID, key.ID)
|
|
if c.expectedErr != nil {
|
|
require.ErrorIs(t, err, c.expectedErr)
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
q := serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: key.OrgID,
|
|
Query: c.expectedLogin,
|
|
Page: 1,
|
|
Limit: 50,
|
|
SignedInUser: &user.SignedInUser{
|
|
UserID: 1,
|
|
OrgID: 1,
|
|
Permissions: map[int64]map[string][]string{
|
|
key.OrgID: {
|
|
"serviceaccounts:read": {"serviceaccounts:id:*"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
serviceAccounts, err := store.SearchOrgServiceAccounts(context.Background(), &q)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), serviceAccounts.TotalCount)
|
|
saMigrated := serviceAccounts.ServiceAccounts[0]
|
|
require.Equal(t, string(key.Role), saMigrated.Role)
|
|
require.Equal(t, c.expectedLogin, saMigrated.Login)
|
|
|
|
tokens, err := store.ListTokens(context.Background(), &serviceaccounts.GetSATokensQuery{
|
|
OrgID: &key.OrgID,
|
|
ServiceAccountID: &saMigrated.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, tokens, 1)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIntegrationStore_MigrateAllApiKeys(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
cases := []struct {
|
|
desc string
|
|
keys []tests.TestApiKey
|
|
orgId int64
|
|
expectedServiceAccounts int64
|
|
expectedErr error
|
|
expectedMigratedResults *serviceaccounts.MigrationResult
|
|
ctxWithFastCancel bool
|
|
}{
|
|
{
|
|
desc: "api keys should be migrated to service account tokens within provided org",
|
|
keys: []tests.TestApiKey{
|
|
{Name: "test1", Role: org.RoleEditor, Key: "secret1", OrgId: 1},
|
|
{Name: "test2", Role: org.RoleEditor, Key: "secret2", OrgId: 1},
|
|
{Name: "test3", Role: org.RoleEditor, Key: "secret3", OrgId: 2},
|
|
},
|
|
orgId: 1,
|
|
expectedServiceAccounts: 2,
|
|
expectedErr: nil,
|
|
expectedMigratedResults: &serviceaccounts.MigrationResult{
|
|
Total: 2,
|
|
Migrated: 2,
|
|
Failed: 0,
|
|
FailedApikeyIDs: []int64{},
|
|
FailedDetails: []string{},
|
|
},
|
|
},
|
|
{
|
|
desc: "api keys from another orgs shouldn't be migrated",
|
|
keys: []tests.TestApiKey{
|
|
{Name: "test1", Role: org.RoleEditor, Key: "secret1", OrgId: 2},
|
|
{Name: "test2", Role: org.RoleEditor, Key: "secret2", OrgId: 2},
|
|
},
|
|
orgId: 1,
|
|
expectedServiceAccounts: 0,
|
|
expectedErr: nil,
|
|
expectedMigratedResults: &serviceaccounts.MigrationResult{
|
|
Total: 0,
|
|
Migrated: 0,
|
|
Failed: 0,
|
|
FailedApikeyIDs: []int64{},
|
|
FailedDetails: []string{},
|
|
},
|
|
},
|
|
{
|
|
desc: "expired api keys should be migrated",
|
|
keys: []tests.TestApiKey{
|
|
{Name: "test1", Role: org.RoleEditor, Key: "secret1", OrgId: 1},
|
|
{Name: "test2", Role: org.RoleEditor, Key: "secret2", OrgId: 1, IsExpired: true},
|
|
},
|
|
orgId: 1,
|
|
expectedServiceAccounts: 2,
|
|
expectedErr: nil,
|
|
expectedMigratedResults: &serviceaccounts.MigrationResult{
|
|
Total: 2,
|
|
Migrated: 2,
|
|
Failed: 0,
|
|
FailedApikeyIDs: []int64{},
|
|
FailedDetails: []string{},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.desc, func(t *testing.T) {
|
|
db, store := setupTestDatabase(t)
|
|
store.cfg.AutoAssignOrg = true
|
|
store.cfg.AutoAssignOrgId = 1
|
|
store.cfg.AutoAssignOrgRole = "Viewer"
|
|
_, err := store.orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: "main"})
|
|
require.NoError(t, err)
|
|
|
|
for _, key := range c.keys {
|
|
tests.SetupApiKey(t, db, store.cfg, key)
|
|
}
|
|
|
|
results, err := store.MigrateApiKeysToServiceAccounts(context.Background(), c.orgId)
|
|
if c.expectedErr != nil {
|
|
require.ErrorIs(t, err, c.expectedErr)
|
|
} else {
|
|
require.NoError(t, err)
|
|
|
|
q := serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: c.orgId,
|
|
Query: "",
|
|
Page: 1,
|
|
Limit: 50,
|
|
SignedInUser: &user.SignedInUser{
|
|
UserID: 1,
|
|
OrgID: 1,
|
|
Permissions: map[int64]map[string][]string{
|
|
c.orgId: {
|
|
"serviceaccounts:read": {"serviceaccounts:id:*"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
serviceAccounts, err := store.SearchOrgServiceAccounts(context.Background(), &q)
|
|
require.NoError(t, err)
|
|
require.Equal(t, c.expectedServiceAccounts, serviceAccounts.TotalCount)
|
|
if c.expectedServiceAccounts > 0 {
|
|
saMigrated := serviceAccounts.ServiceAccounts[0]
|
|
require.Equal(t, string(c.keys[0].Role), saMigrated.Role)
|
|
|
|
tokens, err := store.ListTokens(context.Background(), &serviceaccounts.GetSATokensQuery{
|
|
OrgID: &c.orgId,
|
|
ServiceAccountID: &saMigrated.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, tokens, 1)
|
|
}
|
|
require.Equal(t, c.expectedMigratedResults, results)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
func TestIntegrationServiceAccountsStoreImpl_SearchOrgServiceAccounts(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
|
|
db, store := setupTestDatabase(t)
|
|
|
|
initUsers := []tests.TestUser{
|
|
{Name: "extsvc-test-1", Role: string(org.RoleNone), Login: "sa-1-extsvc-test-1", IsServiceAccount: true},
|
|
{Name: "usertest-2", Role: string(org.RoleEditor), Login: "usertest-2", IsServiceAccount: false},
|
|
{Name: "extsvc-test-3", Role: string(org.RoleNone), Login: "sa-1-extsvc-test-3", IsServiceAccount: true},
|
|
{Name: "extsvc-test-4", Role: string(org.RoleNone), Login: "sa-1-extsvc-test-4", IsServiceAccount: true},
|
|
{Name: "extsvc-test-5", Role: string(org.RoleNone), Login: "sa-1-extsvc-test-5", IsServiceAccount: true},
|
|
{Name: "satest-6", Role: string(org.RoleViewer), Login: "sa-1-satest-6", IsServiceAccount: true},
|
|
{Name: "satest-7", Role: string(org.RoleEditor), Login: "sa-1-satest-7", IsServiceAccount: true},
|
|
{Name: "satest-8", Role: string(org.RoleAdmin), Login: "sa-1-satest-8", IsServiceAccount: true},
|
|
}
|
|
|
|
users, orgID := tests.SetupUsersServiceAccounts(t, db, store.cfg, initUsers)
|
|
|
|
apiKeys := []tests.TestApiKey{
|
|
{Name: "sa-01-apikey-01", OrgId: orgID, Key: "key01", IsExpired: false, ServiceAccountID: &users[0].ID},
|
|
{Name: "sa-01-apikey-02", OrgId: orgID, Key: "key02", IsExpired: false, ServiceAccountID: &users[0].ID},
|
|
{Name: "sa-01-apikey-03", OrgId: orgID, Key: "key03", IsExpired: false, ServiceAccountID: &users[0].ID},
|
|
{Name: "sa-02-apikey-01", OrgId: orgID, Key: "key04", IsExpired: false, ServiceAccountID: &users[2].ID},
|
|
{Name: "sa-02-apikey-02", OrgId: orgID, Key: "key05", IsExpired: false, ServiceAccountID: &users[2].ID},
|
|
{Name: "sa-03-apikey-01", OrgId: orgID, Key: "key06", IsExpired: false, ServiceAccountID: &users[3].ID},
|
|
}
|
|
|
|
tests.SetupApiKeys(t, db, store.cfg, apiKeys)
|
|
|
|
userWithPerm := &user.SignedInUser{
|
|
OrgID: orgID,
|
|
Permissions: map[int64]map[string][]string{orgID: {serviceaccounts.ActionRead: {serviceaccounts.ScopeAll}}},
|
|
}
|
|
|
|
expectedServiceAccount := func(i int, tokens int64) *serviceaccounts.ServiceAccountDTO {
|
|
return &serviceaccounts.ServiceAccountDTO{
|
|
Id: users[i].ID, UID: users[i].UID, Name: users[i].Name, Login: users[i].Login, OrgId: orgID, Role: "None", Tokens: tokens,
|
|
}
|
|
}
|
|
|
|
tt := []struct {
|
|
desc string
|
|
query *serviceaccounts.SearchOrgServiceAccountsQuery
|
|
expectedTotal int64 // Value of the result.TotalCount
|
|
expectedServiceAccounts []*serviceaccounts.ServiceAccountDTO
|
|
expectedErr error
|
|
}{
|
|
{
|
|
desc: "should list all service accounts with tokens count",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
SignedInUser: userWithPerm,
|
|
Filter: serviceaccounts.FilterIncludeAll,
|
|
CountTokens: true,
|
|
},
|
|
expectedTotal: 7,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
|
|
expectedServiceAccount(0, 3),
|
|
expectedServiceAccount(2, 2),
|
|
expectedServiceAccount(3, 1),
|
|
expectedServiceAccount(4, 0),
|
|
expectedServiceAccount(5, 0),
|
|
expectedServiceAccount(6, 0),
|
|
expectedServiceAccount(7, 0),
|
|
},
|
|
},
|
|
{
|
|
desc: "should list all service accounts with no tokens count",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
SignedInUser: userWithPerm,
|
|
Filter: serviceaccounts.FilterIncludeAll,
|
|
},
|
|
expectedTotal: 7,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
|
|
expectedServiceAccount(0, 0),
|
|
expectedServiceAccount(2, 0),
|
|
expectedServiceAccount(3, 0),
|
|
expectedServiceAccount(4, 0),
|
|
expectedServiceAccount(5, 0),
|
|
expectedServiceAccount(6, 0),
|
|
expectedServiceAccount(7, 0),
|
|
},
|
|
},
|
|
{
|
|
desc: "should list no service accounts without permissions",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
SignedInUser: &user.SignedInUser{
|
|
OrgID: orgID,
|
|
Permissions: map[int64]map[string][]string{orgID: {}},
|
|
},
|
|
Filter: serviceaccounts.FilterIncludeAll,
|
|
},
|
|
expectedTotal: 0,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{},
|
|
},
|
|
{
|
|
desc: "should list one service accounts with restricted permissions",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
SignedInUser: &user.SignedInUser{
|
|
OrgID: orgID,
|
|
Permissions: map[int64]map[string][]string{orgID: {serviceaccounts.ActionRead: {
|
|
ac.Scope("serviceaccounts", "id", "1"),
|
|
ac.Scope("serviceaccounts", "id", "7"),
|
|
}}},
|
|
},
|
|
Filter: serviceaccounts.FilterIncludeAll,
|
|
},
|
|
expectedTotal: 2,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
|
|
expectedServiceAccount(0, 0),
|
|
expectedServiceAccount(6, 0),
|
|
},
|
|
},
|
|
{
|
|
desc: "should list only external service accounts",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
SignedInUser: userWithPerm,
|
|
Filter: serviceaccounts.FilterOnlyExternal,
|
|
CountTokens: true,
|
|
},
|
|
expectedTotal: 4,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
|
|
expectedServiceAccount(0, 3),
|
|
expectedServiceAccount(2, 2),
|
|
expectedServiceAccount(3, 1),
|
|
expectedServiceAccount(4, 0),
|
|
},
|
|
},
|
|
{
|
|
desc: "should return service accounts with sa-1-satest login",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
Query: "sa-1-satest",
|
|
SignedInUser: userWithPerm,
|
|
Filter: serviceaccounts.FilterIncludeAll,
|
|
CountTokens: true,
|
|
},
|
|
expectedTotal: 3,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
|
|
expectedServiceAccount(5, 0),
|
|
expectedServiceAccount(6, 0),
|
|
expectedServiceAccount(7, 0),
|
|
},
|
|
},
|
|
{
|
|
desc: "should only count service accounts",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
SignedInUser: userWithPerm,
|
|
Filter: serviceaccounts.FilterIncludeAll,
|
|
CountOnly: true,
|
|
},
|
|
expectedTotal: 7,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{},
|
|
},
|
|
{
|
|
desc: "should paginate result",
|
|
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
|
OrgID: orgID,
|
|
Page: 4,
|
|
Limit: 2,
|
|
SignedInUser: userWithPerm,
|
|
Filter: serviceaccounts.FilterIncludeAll,
|
|
},
|
|
expectedTotal: 7,
|
|
expectedServiceAccounts: []*serviceaccounts.ServiceAccountDTO{
|
|
expectedServiceAccount(7, 0),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tt {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
got, err := store.SearchOrgServiceAccounts(ctx, tc.query)
|
|
if tc.expectedErr != nil {
|
|
require.ErrorIs(t, err, tc.expectedErr)
|
|
return
|
|
}
|
|
|
|
require.Equal(t, tc.expectedTotal, got.TotalCount)
|
|
require.Len(t, got.ServiceAccounts, len(tc.expectedServiceAccounts))
|
|
for i, sa := range got.ServiceAccounts {
|
|
require.EqualValues(t, tc.expectedServiceAccounts[i], sa)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIntegrationServiceAccountsStoreImpl_EnableServiceAccounts(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping test in short mode")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
initUsers := []tests.TestUser{
|
|
{Name: "satest-1", Role: string(org.RoleViewer), Login: "sa-satest-1", IsServiceAccount: true},
|
|
{Name: "satest-2", Role: string(org.RoleEditor), Login: "sa-satest-2", IsServiceAccount: true},
|
|
{Name: "usertest-3", Role: string(org.RoleEditor), Login: "usertest-3", IsServiceAccount: false},
|
|
}
|
|
|
|
db, store := setupTestDatabase(t)
|
|
_, orgID := tests.SetupUsersServiceAccounts(t, db, store.cfg, initUsers)
|
|
|
|
fetchStates := func() map[int64]bool {
|
|
sa1, err := store.RetrieveServiceAccount(ctx, &serviceaccounts.GetServiceAccountQuery{OrgID: orgID, ID: 1})
|
|
require.NoError(t, err)
|
|
sa2, err := store.RetrieveServiceAccount(ctx, &serviceaccounts.GetServiceAccountQuery{OrgID: orgID, ID: 2})
|
|
require.NoError(t, err)
|
|
user, err := store.userService.GetByID(ctx, &user.GetUserByIDQuery{ID: 3})
|
|
require.NoError(t, err)
|
|
return map[int64]bool{1: !sa1.IsDisabled, 2: !sa2.IsDisabled, 3: !user.IsDisabled}
|
|
}
|
|
|
|
tt := []struct {
|
|
desc string
|
|
id int64
|
|
enable bool
|
|
wantStates map[int64]bool
|
|
}{
|
|
{
|
|
desc: "should disable service account",
|
|
id: 1,
|
|
enable: false,
|
|
wantStates: map[int64]bool{1: false, 2: true, 3: true},
|
|
},
|
|
{
|
|
desc: "should disable service account again",
|
|
id: 1,
|
|
enable: false,
|
|
wantStates: map[int64]bool{1: false, 2: true, 3: true},
|
|
},
|
|
{
|
|
desc: "should enable service account",
|
|
id: 1,
|
|
enable: true,
|
|
wantStates: map[int64]bool{1: true, 2: true, 3: true},
|
|
},
|
|
{
|
|
desc: "should not disable user",
|
|
id: 3,
|
|
enable: false,
|
|
wantStates: map[int64]bool{1: true, 2: true, 3: true},
|
|
},
|
|
}
|
|
for _, tc := range tt {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
err := store.EnableServiceAccount(ctx, orgID, tc.id, tc.enable)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.wantStates, fetchStates())
|
|
})
|
|
}
|
|
}
|