Auth: Decouple client and hook registration (#85084)

This commit is contained in:
Karl Persson 2024-04-04 09:33:00 +02:00 committed by GitHub
parent bdc492b96c
commit 504870f10a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 148 additions and 145 deletions

View File

@ -1,30 +0,0 @@
package service
import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
)
const (
ActionRead = "server.usagestats.report:read"
)
var (
usagestatsReaderRole = accesscontrol.RoleDTO{
Name: "fixed:usagestats:reader",
DisplayName: "Usage stats report reader",
Description: "View usage statistics report",
Group: "Statistics",
Permissions: []accesscontrol.Permission{
{Action: ActionRead},
},
}
)
func declareFixedRoles(ac accesscontrol.Service) error {
usagestatsReader := accesscontrol.RoleRegistration{
Role: usagestatsReaderRole,
Grants: []string{string(accesscontrol.RoleGrafanaAdmin)},
}
return ac.DeclareFixedRoles(usagestatsReader)
}

View File

@ -15,7 +15,7 @@ func (uss *UsageStats) registerAPIEndpoints() {
authorize := accesscontrol.Middleware(uss.accesscontrol)
uss.RouteRegister.Group(rootUrl, func(subrouter routing.RouteRegister) {
subrouter.Get("/usage-report-preview", authorize(accesscontrol.EvalPermission(ActionRead)), routing.Wrap(uss.getUsageReportPreview))
subrouter.Get("/usage-report-preview", authorize(accesscontrol.EvalPermission(accesscontrol.ActionUsageStatsRead)), routing.Wrap(uss.getUsageReportPreview))
})
}

View File

@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/infra/db/dbtest"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/stats"
@ -28,13 +29,13 @@ func TestApi_getUsageStats(t *testing.T) {
{
desc: "expect usage stats",
enabled: true,
permissions: map[string][]string{ActionRead: {}},
permissions: map[string][]string{accesscontrol.ActionUsageStatsRead: {}},
expectedStatus: 200,
},
{
desc: "expect usage stat preview still there after disabling",
enabled: false,
permissions: map[string][]string{ActionRead: {}},
permissions: map[string][]string{accesscontrol.ActionUsageStatsRead: {}},
expectedStatus: 200,
},
{

View File

@ -35,7 +35,6 @@ func ProvideService(cfg *setting.Cfg,
routeRegister routing.RouteRegister,
tracer tracing.Tracer,
accesscontrol ac.AccessControl,
accesscontrolService ac.Service,
bundleRegistry supportbundles.Service,
) (*UsageStats, error) {
s := &UsageStats{
@ -47,10 +46,6 @@ func ProvideService(cfg *setting.Cfg,
accesscontrol: accesscontrol,
}
if err := declareFixedRoles(accesscontrolService); err != nil {
return nil, err
}
s.registerAPIEndpoints()
bundleRegistry.RegisterSupportItemCollector(s.supportBundleCollector())

View File

@ -23,7 +23,6 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tests/testsuite"
@ -248,7 +247,6 @@ func createService(t *testing.T, sqlStore db.DB, withDB bool) *UsageStats {
routing.NewRouteRegister(),
tracing.InitializeTracerForTest(),
acimpl.ProvideAccessControl(cfg),
actest.FakeService{},
supportbundlestest.NewFakeBundleService(),
)

View File

@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/services/anonymous/anonimpl"
grafanaapiserver "github.com/grafana/grafana/pkg/services/apiserver"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/authn/authnimpl"
"github.com/grafana/grafana/pkg/services/cleanup"
"github.com/grafana/grafana/pkg/services/cloudmigration"
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
@ -68,7 +69,7 @@ func ProvideBackgroundServiceRegistry(
_ *plugindashboardsservice.DashboardUpdater, _ *sanitizer.Provider,
_ *grpcserver.HealthService, _ entity.EntityStoreServer, _ *grpcserver.ReflectionService, _ *ldapapi.Service,
_ *apiregistry.Service, _ auth.IDService, _ *teamapi.TeamAPI, _ ssosettings.Service,
_ cloudmigration.Service,
_ cloudmigration.Service, _ authnimpl.Registration,
) *BackgroundServiceRegistry {
return NewBackgroundServiceRegistry(
httpServer,

View File

@ -356,6 +356,7 @@ var wireBasicSet = wire.NewSet(
authnimpl.ProvideService,
authnimpl.ProvideIdentitySynchronizer,
authnimpl.ProvideAuthnService,
authnimpl.ProvideRegistration,
supportbundlesimpl.ProvideService,
extsvcaccounts.ProvideExtSvcAccountsService,
wire.Bind(new(serviceaccounts.ExtSvcAccountsService), new(*extsvcaccounts.ExtSvcAccountsService)),

View File

@ -481,6 +481,9 @@ const (
ActionLibraryPanelsRead = "library.panels:read"
ActionLibraryPanelsWrite = "library.panels:write"
ActionLibraryPanelsDelete = "library.panels:delete"
// Usage stats actions
ActionUsageStatsRead = "server.usagestats.report:read"
)
var (

View File

@ -280,6 +280,16 @@ var (
},
},
}
usagestatsReaderRole = RoleDTO{
Name: "fixed:usagestats:reader",
DisplayName: "Usage stats report reader",
Description: "View usage statistics report",
Group: "Statistics",
Permissions: []Permission{
{Action: ActionUsageStatsRead},
},
}
)
// Declare OSS roles to the accesscontrol service
@ -320,15 +330,22 @@ func DeclareFixedRoles(service Service, cfg *setting.Cfg) error {
Role: generalAuthConfigWriterRole,
Grants: []string{RoleGrafanaAdmin},
}
// TODO: Move to own service when implemented
authenticationConfigWriter := RoleRegistration{
Role: authenticationConfigWriterRole,
Grants: []string{RoleGrafanaAdmin},
}
return service.DeclareFixedRoles(ldapReader, ldapWriter, orgUsersReader, orgUsersWriter,
settingsReader, statsReader, usersReader, usersWriter, authenticationConfigWriter, generalAuthConfigWriter)
usageStatsReader := RoleRegistration{
Role: usagestatsReaderRole,
Grants: []string{RoleGrafanaAdmin},
}
return service.DeclareFixedRoles(
ldapReader, ldapWriter, orgUsersReader, orgUsersWriter,
settingsReader, statsReader, usersReader, usersWriter,
authenticationConfigWriter, generalAuthConfigWriter, usageStatsReader,
)
}
func ConcatPermissions(permissions ...[]Permission) []Permission {

View File

@ -0,0 +1,116 @@
package authnimpl
import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/authn/authnimpl/sync"
"github.com/grafana/grafana/pkg/services/authn/clients"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/loginattempt"
"github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/signingkeys"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
)
type Registration struct{}
func ProvideRegistration(
cfg *setting.Cfg, authnSvc authn.Service,
orgService org.Service, sessionService auth.UserTokenService,
accessControlService accesscontrol.Service,
apikeyService apikey.Service, userService user.Service,
jwtService auth.JWTVerifierService, userProtectionService login.UserProtectionService,
loginAttempts loginattempt.Service, quotaService quota.Service,
authInfoService login.AuthInfoService, renderService rendering.Service,
features *featuremgmt.FeatureManager, oauthTokenService oauthtoken.OAuthTokenService,
socialService social.Service, cache *remotecache.RemoteCache, signingKeysService signingkeys.Service,
ldapService service.LDAP, settingsProviderService setting.Provider,
) Registration {
logger := log.New("authn.registration")
authnSvc.RegisterClient(clients.ProvideRender(renderService))
authnSvc.RegisterClient(clients.ProvideAPIKey(apikeyService))
if cfg.LoginCookieName != "" {
authnSvc.RegisterClient(clients.ProvideSession(cfg, sessionService))
}
var proxyClients []authn.ProxyClient
var passwordClients []authn.PasswordClient
if cfg.LDAPAuthEnabled {
ldap := clients.ProvideLDAP(cfg, ldapService, userService, authInfoService)
proxyClients = append(proxyClients, ldap)
passwordClients = append(passwordClients, ldap)
}
if !cfg.DisableLogin {
grafana := clients.ProvideGrafana(cfg, userService)
proxyClients = append(proxyClients, grafana)
passwordClients = append(passwordClients, grafana)
}
// if we have password clients configure check if basic auth or form auth is enabled
if len(passwordClients) > 0 {
passwordClient := clients.ProvidePassword(loginAttempts, passwordClients...)
if cfg.BasicAuthEnabled {
authnSvc.RegisterClient(clients.ProvideBasic(passwordClient))
}
if !cfg.DisableLoginForm {
authnSvc.RegisterClient(clients.ProvideForm(passwordClient))
}
}
if cfg.AuthProxy.Enabled && len(proxyClients) > 0 {
proxy, err := clients.ProvideProxy(cfg, cache, proxyClients...)
if err != nil {
logger.Error("Failed to configure auth proxy", "err", err)
} else {
authnSvc.RegisterClient(proxy)
}
}
if cfg.JWTAuth.Enabled {
authnSvc.RegisterClient(clients.ProvideJWT(jwtService, cfg))
}
if cfg.ExtJWTAuth.Enabled && features.IsEnabledGlobally(featuremgmt.FlagAuthAPIAccessTokenAuth) {
authnSvc.RegisterClient(clients.ProvideExtendedJWT(userService, cfg, signingKeysService))
}
for name := range socialService.GetOAuthProviders() {
clientName := authn.ClientWithPrefix(name)
authnSvc.RegisterClient(clients.ProvideOAuth(clientName, cfg, oauthTokenService, socialService, settingsProviderService, features))
}
// FIXME (jguer): move to User package
userSync := sync.ProvideUserSync(userService, userProtectionService, authInfoService, quotaService)
orgSync := sync.ProvideOrgSync(userService, orgService, accessControlService, cfg)
authnSvc.RegisterPostAuthHook(userSync.SyncUserHook, 10)
authnSvc.RegisterPostAuthHook(userSync.EnableUserHook, 20)
authnSvc.RegisterPostAuthHook(orgSync.SyncOrgRolesHook, 30)
authnSvc.RegisterPostAuthHook(userSync.SyncLastSeenHook, 130)
authnSvc.RegisterPostAuthHook(sync.ProvideOAuthTokenSync(oauthTokenService, sessionService, socialService).SyncOauthTokenHook, 60)
authnSvc.RegisterPostAuthHook(userSync.FetchSyncedUserHook, 100)
rbacSync := sync.ProvideRBACSync(accessControlService)
if features.IsEnabledGlobally(featuremgmt.FlagCloudRBACRoles) {
authnSvc.RegisterPostAuthHook(rbacSync.SyncCloudRoles, 110)
}
authnSvc.RegisterPostAuthHook(rbacSync.SyncPermissionsHook, 120)
authnSvc.RegisterPostAuthHook(orgSync.SetDefaultOrgHook, 130)
return Registration{}
}

View File

@ -13,26 +13,13 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/network"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/authn/authnimpl/sync"
"github.com/grafana/grafana/pkg/services/authn/clients"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap/service"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/loginattempt"
"github.com/grafana/grafana/pkg/services/oauthtoken"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/signingkeys"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
@ -60,19 +47,8 @@ func ProvideIdentitySynchronizer(s *Service) authn.IdentitySynchronizer {
func ProvideService(
cfg *setting.Cfg, tracer tracing.Tracer,
orgService org.Service, sessionService auth.UserTokenService,
accessControlService accesscontrol.Service,
apikeyService apikey.Service, userService user.Service,
jwtService auth.JWTVerifierService,
usageStats usagestats.Service,
userProtectionService login.UserProtectionService,
loginAttempts loginattempt.Service, quotaService quota.Service,
authInfoService login.AuthInfoService, renderService rendering.Service,
features *featuremgmt.FeatureManager, oauthTokenService oauthtoken.OAuthTokenService,
socialService social.Service, cache *remotecache.RemoteCache,
ldapService service.LDAP, registerer prometheus.Registerer,
signingKeysService signingkeys.Service,
settingsProviderService setting.Provider,
sessionService auth.UserTokenService, usageStats usagestats.Service,
authInfoService login.AuthInfoService, registerer prometheus.Registerer,
) *Service {
s := &Service{
log: log.New("authn.service"),
@ -88,81 +64,6 @@ func ProvideService(
}
usageStats.RegisterMetricsFunc(s.getUsageStats)
s.RegisterClient(clients.ProvideRender(renderService))
s.RegisterClient(clients.ProvideAPIKey(apikeyService))
if cfg.LoginCookieName != "" {
s.RegisterClient(clients.ProvideSession(cfg, sessionService))
}
var proxyClients []authn.ProxyClient
var passwordClients []authn.PasswordClient
if s.cfg.LDAPAuthEnabled {
ldap := clients.ProvideLDAP(cfg, ldapService, userService, authInfoService)
proxyClients = append(proxyClients, ldap)
passwordClients = append(passwordClients, ldap)
}
if !s.cfg.DisableLogin {
grafana := clients.ProvideGrafana(cfg, userService)
proxyClients = append(proxyClients, grafana)
passwordClients = append(passwordClients, grafana)
}
// if we have password clients configure check if basic auth or form auth is enabled
if len(passwordClients) > 0 {
passwordClient := clients.ProvidePassword(loginAttempts, passwordClients...)
if s.cfg.BasicAuthEnabled {
s.RegisterClient(clients.ProvideBasic(passwordClient))
}
if !s.cfg.DisableLoginForm {
s.RegisterClient(clients.ProvideForm(passwordClient))
}
}
if s.cfg.AuthProxy.Enabled && len(proxyClients) > 0 {
proxy, err := clients.ProvideProxy(cfg, cache, proxyClients...)
if err != nil {
s.log.Error("Failed to configure auth proxy", "err", err)
} else {
s.RegisterClient(proxy)
}
}
if s.cfg.JWTAuth.Enabled {
s.RegisterClient(clients.ProvideJWT(jwtService, cfg))
}
if s.cfg.ExtJWTAuth.Enabled && features.IsEnabledGlobally(featuremgmt.FlagAuthAPIAccessTokenAuth) {
s.RegisterClient(clients.ProvideExtendedJWT(userService, cfg, signingKeysService))
}
for name := range socialService.GetOAuthProviders() {
clientName := authn.ClientWithPrefix(name)
s.RegisterClient(clients.ProvideOAuth(clientName, cfg, oauthTokenService, socialService, settingsProviderService, features))
}
// FIXME (jguer): move to User package
userSyncService := sync.ProvideUserSync(userService, userProtectionService, authInfoService, quotaService)
orgUserSyncService := sync.ProvideOrgSync(userService, orgService, accessControlService, cfg)
s.RegisterPostAuthHook(userSyncService.SyncUserHook, 10)
s.RegisterPostAuthHook(userSyncService.EnableUserHook, 20)
s.RegisterPostAuthHook(orgUserSyncService.SyncOrgRolesHook, 30)
s.RegisterPostAuthHook(userSyncService.SyncLastSeenHook, 130)
s.RegisterPostAuthHook(sync.ProvideOAuthTokenSync(oauthTokenService, sessionService, socialService).SyncOauthTokenHook, 60)
s.RegisterPostAuthHook(userSyncService.FetchSyncedUserHook, 100)
rbacSync := sync.ProvideRBACSync(accessControlService)
if features.IsEnabledGlobally(featuremgmt.FlagCloudRBACRoles) {
s.RegisterPostAuthHook(rbacSync.SyncCloudRoles, 110)
}
s.RegisterPostAuthHook(rbacSync.SyncPermissionsHook, 120)
s.RegisterPostAuthHook(orgUserSyncService.SetDefaultOrgHook, 130)
return s
}