Auth: Add empty role usage metrics for service and user accounts (#73108)
* Add tests for service accounts metrics usage * Add service account store implementation * Add service account service implementation * Add tests for org metrics usage * Add org implementation * Add service implementation
This commit is contained in:
@@ -40,6 +40,7 @@ type store interface {
|
||||
Search(context.Context, *user.SearchUsersQuery) (*user.SearchUserQueryResult, error)
|
||||
|
||||
Count(ctx context.Context) (int64, error)
|
||||
CountUserAccountsWithEmptyRole(ctx context.Context) (int64, error)
|
||||
}
|
||||
|
||||
type sqlStore struct {
|
||||
@@ -532,6 +533,27 @@ func (ss *sqlStore) Count(ctx context.Context) (int64, error) {
|
||||
return r.Count, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// validateOneAdminLeft validate that there is an admin user left
|
||||
func validateOneAdminLeft(ctx context.Context, sess *db.Session) error {
|
||||
count, err := sess.Where("is_admin=?", true).Count(&user.User{})
|
||||
|
||||
@@ -956,6 +956,53 @@ func updateDashboardACL(t *testing.T, sqlStore db.DB, dashboardID int64, items .
|
||||
return err
|
||||
}
|
||||
|
||||
func TestMetricsUsage(t *testing.T) {
|
||||
ss := db.InitTestDB(t)
|
||||
userStore := ProvideStore(ss, setting.NewCfg())
|
||||
quotaService := quotaimpl.ProvideService(ss, ss.Cfg)
|
||||
orgService, err := orgimpl.ProvideService(ss, ss.Cfg, quotaService)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, usrSvc := createOrgAndUserSvc(t, ss, ss.Cfg)
|
||||
|
||||
t.Run("", func(t *testing.T) {
|
||||
orgId := int64(1)
|
||||
|
||||
// create first user
|
||||
createFirtUserCmd := &user.CreateUserCommand{
|
||||
Login: "admin",
|
||||
Email: "admin@admin.com",
|
||||
Name: "admin",
|
||||
OrgID: orgId,
|
||||
}
|
||||
_, err := usrSvc.Create(context.Background(), createFirtUserCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
// create second user
|
||||
createSecondUserCmd := &user.CreateUserCommand{
|
||||
Login: "userWithoutRole",
|
||||
Email: "userWithoutRole@userWithoutRole.com",
|
||||
Name: "userWithoutRole",
|
||||
}
|
||||
secondUser, err := usrSvc.Create(context.Background(), createSecondUserCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
// assign the user to the org
|
||||
cmd := org.AddOrgUserCommand{
|
||||
OrgID: secondUser.OrgID,
|
||||
UserID: orgId,
|
||||
Role: org.RoleNone,
|
||||
}
|
||||
err = orgService.AddOrgUser(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
// get metric usage
|
||||
stats, err := userStore.CountUserAccountsWithEmptyRole(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), stats)
|
||||
})
|
||||
}
|
||||
|
||||
// This function was copied from pkg/services/dashboards/database to circumvent
|
||||
// import cycles. When this org-related code is refactored into a service the
|
||||
// tests can the real GetDashboardACLInfoList functions
|
||||
|
||||
@@ -72,6 +72,14 @@ func (s *Service) GetUsageStats(ctx context.Context) map[string]interface{} {
|
||||
}
|
||||
|
||||
stats["stats.case_insensitive_login.count"] = caseInsensitiveLoginVal
|
||||
|
||||
count, err := s.store.CountUserAccountsWithEmptyRole(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
stats["stats.user.role_none.count"] = count
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
|
||||
@@ -193,13 +193,40 @@ func TestUserService(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
userStore := newUserStoreFake()
|
||||
orgService := orgtest.NewOrgServiceFake()
|
||||
|
||||
userService := Service{
|
||||
store: userStore,
|
||||
orgService: orgService,
|
||||
cacheService: localcache.ProvideService(),
|
||||
teamService: &teamtest.FakeService{},
|
||||
}
|
||||
|
||||
t.Run("update user with role None", func(t *testing.T) {
|
||||
userStore.ExpectedCountUserAccountsWithEmptyRoles = int64(1)
|
||||
|
||||
userService.cfg = setting.NewCfg()
|
||||
userService.cfg.CaseInsensitiveLogin = true
|
||||
|
||||
stats := userService.GetUsageStats(context.Background())
|
||||
assert.NotEmpty(t, stats)
|
||||
|
||||
assert.Len(t, stats, 2, stats)
|
||||
assert.Equal(t, 1, stats["stats.case_insensitive_login.count"])
|
||||
assert.Equal(t, int64(1), stats["stats.user.role_none.count"])
|
||||
})
|
||||
}
|
||||
|
||||
type FakeUserStore struct {
|
||||
ExpectedUser *user.User
|
||||
ExpectedSignedInUser *user.SignedInUser
|
||||
ExpectedUserProfile *user.UserProfileDTO
|
||||
ExpectedSearchUserQueryResult *user.SearchUserQueryResult
|
||||
ExpectedError error
|
||||
ExpectedDeleteUserError error
|
||||
ExpectedUser *user.User
|
||||
ExpectedSignedInUser *user.SignedInUser
|
||||
ExpectedUserProfile *user.UserProfileDTO
|
||||
ExpectedSearchUserQueryResult *user.SearchUserQueryResult
|
||||
ExpectedError error
|
||||
ExpectedDeleteUserError error
|
||||
ExpectedCountUserAccountsWithEmptyRoles int64
|
||||
}
|
||||
|
||||
func newUserStoreFake() *FakeUserStore {
|
||||
@@ -290,6 +317,10 @@ func (f *FakeUserStore) Count(ctx context.Context) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (f *FakeUserStore) CountUserAccountsWithEmptyRole(ctx context.Context) (int64, error) {
|
||||
return f.ExpectedCountUserAccountsWithEmptyRoles, nil
|
||||
}
|
||||
|
||||
func TestUpdateLastSeenAt(t *testing.T) {
|
||||
userStore := newUserStoreFake()
|
||||
orgService := orgtest.NewOrgServiceFake()
|
||||
|
||||
Reference in New Issue
Block a user