mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AuthN: Add metrics to external service accounts management (#76789)
* AuthN: Add metrics to external service accounts management * Add a new metric to count stored external service accounts * Update variable names Co-authored-by: linoman <2051016+linoman@users.noreply.github.com> * Add test to SearchOrgServiceAccounts * Add feature flags checks before registering and using the metrics --------- Co-authored-by: linoman <2051016+linoman@users.noreply.github.com>
This commit is contained in:
@@ -270,9 +270,6 @@ func (s *ServiceAccountsStoreImpl) SearchOrgServiceAccounts(ctx context.Context,
|
||||
}
|
||||
|
||||
err := s.sqlStore.WithDbSession(ctx, func(dbSession *db.Session) error {
|
||||
sess := dbSession.Table("org_user")
|
||||
sess.Join("INNER", s.sqlStore.GetDialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", s.sqlStore.GetDialect().Quote("user")))
|
||||
|
||||
whereConditions := make([]string, 0)
|
||||
whereParams := make([]any, 0)
|
||||
|
||||
@@ -312,10 +309,38 @@ func (s *ServiceAccountsStoreImpl) SearchOrgServiceAccounts(ctx context.Context,
|
||||
whereConditions,
|
||||
"is_disabled = ?")
|
||||
whereParams = append(whereParams, s.sqlStore.GetDialect().BooleanStr(true))
|
||||
case serviceaccounts.FilterOnlyExternal:
|
||||
whereConditions = append(
|
||||
whereConditions,
|
||||
"login "+s.sqlStore.GetDialect().LikeStr()+" ?")
|
||||
whereParams = append(whereParams, serviceaccounts.ServiceAccountPrefix+serviceaccounts.ExtSvcPrefix+"%")
|
||||
default:
|
||||
s.log.Warn("Invalid filter user for service account filtering", "service account search filtering", query.Filter)
|
||||
}
|
||||
|
||||
// Count the number of accounts
|
||||
serviceaccount := serviceaccounts.ServiceAccountDTO{}
|
||||
countSess := dbSession.Table("org_user")
|
||||
countSess.Join("INNER", s.sqlStore.GetDialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", s.sqlStore.GetDialect().Quote("user")))
|
||||
|
||||
if len(whereConditions) > 0 {
|
||||
countSess.Where(strings.Join(whereConditions, " AND "), whereParams...)
|
||||
}
|
||||
count, err := countSess.Count(&serviceaccount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
searchResult.TotalCount = count
|
||||
|
||||
// Stop here if we only wanted to count the number of accounts
|
||||
if query.CountOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fetch service accounts
|
||||
sess := dbSession.Table("org_user")
|
||||
sess.Join("INNER", s.sqlStore.GetDialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", s.sqlStore.GetDialect().Quote("user")))
|
||||
|
||||
if len(whereConditions) > 0 {
|
||||
sess.Where(strings.Join(whereConditions, " AND "), whereParams...)
|
||||
}
|
||||
@@ -338,21 +363,6 @@ func (s *ServiceAccountsStoreImpl) SearchOrgServiceAccounts(ctx context.Context,
|
||||
if err := sess.Find(&searchResult.ServiceAccounts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get total
|
||||
serviceaccount := serviceaccounts.ServiceAccountDTO{}
|
||||
countSess := dbSession.Table("org_user")
|
||||
sess.Join("INNER", s.sqlStore.GetDialect().Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", s.sqlStore.GetDialect().Quote("user")))
|
||||
|
||||
if len(whereConditions) > 0 {
|
||||
countSess.Where(strings.Join(whereConditions, " AND "), whereParams...)
|
||||
}
|
||||
count, err := countSess.Count(&serviceaccount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
searchResult.TotalCount = count
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
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"
|
||||
@@ -368,3 +369,129 @@ func TestStore_MigrateAllApiKeys(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestServiceAccountsStoreImpl_SearchOrgServiceAccounts(t *testing.T) {
|
||||
initUsers := []tests.TestUser{
|
||||
{Name: "satest-1", Role: string(org.RoleViewer), Login: "sa-satest-1", IsServiceAccount: true},
|
||||
{Name: "usertest-2", Role: string(org.RoleEditor), Login: "usertest-2", IsServiceAccount: false},
|
||||
{Name: "satest-3", Role: string(org.RoleEditor), Login: "sa-satest-3", IsServiceAccount: true},
|
||||
{Name: "satest-4", Role: string(org.RoleAdmin), Login: "sa-satest-4", IsServiceAccount: true},
|
||||
{Name: "extsvc-test-5", Role: string(org.RoleNone), Login: "sa-extsvc-test-5", IsServiceAccount: true},
|
||||
{Name: "extsvc-test-6", Role: string(org.RoleNone), Login: "sa-extsvc-test-6", IsServiceAccount: true},
|
||||
{Name: "extsvc-test-7", Role: string(org.RoleNone), Login: "sa-extsvc-test-7", IsServiceAccount: true},
|
||||
{Name: "extsvc-test-8", Role: string(org.RoleNone), Login: "sa-extsvc-test-8", IsServiceAccount: true},
|
||||
}
|
||||
|
||||
db, store := setupTestDatabase(t)
|
||||
orgID := tests.SetupUsersServiceAccounts(t, db, initUsers)
|
||||
|
||||
userWithPerm := &user.SignedInUser{
|
||||
OrgID: orgID,
|
||||
Permissions: map[int64]map[string][]string{orgID: {serviceaccounts.ActionRead: {serviceaccounts.ScopeAll}}},
|
||||
}
|
||||
|
||||
tt := []struct {
|
||||
desc string
|
||||
query *serviceaccounts.SearchOrgServiceAccountsQuery
|
||||
expectedTotal int64 // Value of the result.TotalCount
|
||||
expectedCount int // Length of the result.ServiceAccounts slice
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
desc: "should list all service accounts",
|
||||
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
||||
OrgID: orgID,
|
||||
SignedInUser: userWithPerm,
|
||||
Filter: serviceaccounts.FilterIncludeAll,
|
||||
},
|
||||
expectedTotal: 7,
|
||||
expectedCount: 7,
|
||||
},
|
||||
{
|
||||
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,
|
||||
expectedCount: 0,
|
||||
},
|
||||
{
|
||||
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,
|
||||
expectedCount: 2,
|
||||
},
|
||||
{
|
||||
desc: "should list only external service accounts",
|
||||
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
||||
OrgID: orgID,
|
||||
SignedInUser: userWithPerm,
|
||||
Filter: serviceaccounts.FilterOnlyExternal,
|
||||
},
|
||||
expectedTotal: 4,
|
||||
expectedCount: 4,
|
||||
},
|
||||
{
|
||||
desc: "should return service accounts with sa-satest login",
|
||||
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
||||
OrgID: orgID,
|
||||
Query: "sa-satest",
|
||||
SignedInUser: userWithPerm,
|
||||
Filter: serviceaccounts.FilterIncludeAll,
|
||||
},
|
||||
expectedTotal: 3,
|
||||
expectedCount: 3,
|
||||
},
|
||||
{
|
||||
desc: "should only count service accounts",
|
||||
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
||||
OrgID: orgID,
|
||||
SignedInUser: userWithPerm,
|
||||
Filter: serviceaccounts.FilterIncludeAll,
|
||||
CountOnly: true,
|
||||
},
|
||||
expectedTotal: 7,
|
||||
expectedCount: 0,
|
||||
},
|
||||
{
|
||||
desc: "should paginate result",
|
||||
query: &serviceaccounts.SearchOrgServiceAccountsQuery{
|
||||
OrgID: orgID,
|
||||
Page: 4,
|
||||
Limit: 2,
|
||||
SignedInUser: userWithPerm,
|
||||
Filter: serviceaccounts.FilterIncludeAll,
|
||||
},
|
||||
expectedTotal: 7,
|
||||
expectedCount: 1,
|
||||
},
|
||||
}
|
||||
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, tc.expectedCount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user