LDAP: Move to single package cluster (#63035)
* move multildap to ldap package * move LDAP api and tests to ldap package * register background service * lint
This commit is contained in:
@@ -523,7 +523,6 @@ lerna.json @grafana/frontend-ops
|
|||||||
/pkg/services/guardian/ @grafana/grafana-authnz-team
|
/pkg/services/guardian/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/ldap/ @grafana/grafana-authnz-team
|
/pkg/services/ldap/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/login/ @grafana/grafana-authnz-team
|
/pkg/services/login/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/multildap/ @grafana/grafana-authnz-team
|
|
||||||
/pkg/services/oauthtoken/ @grafana/grafana-authnz-team
|
/pkg/services/oauthtoken/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/teamguardian/ @grafana/grafana-authnz-team
|
/pkg/services/teamguardian/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/serviceaccounts/ @grafana/grafana-authnz-team
|
/pkg/services/serviceaccounts/ @grafana/grafana-authnz-team
|
||||||
|
|||||||
@@ -655,11 +655,6 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
adminRoute.Post("/provisioning/datasources/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersDatasources)), routing.Wrap(hs.AdminProvisioningReloadDatasources))
|
adminRoute.Post("/provisioning/datasources/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersDatasources)), routing.Wrap(hs.AdminProvisioningReloadDatasources))
|
||||||
adminRoute.Post("/provisioning/notifications/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersNotifications)), routing.Wrap(hs.AdminProvisioningReloadNotifications))
|
adminRoute.Post("/provisioning/notifications/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersNotifications)), routing.Wrap(hs.AdminProvisioningReloadNotifications))
|
||||||
adminRoute.Post("/provisioning/alerting/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersAlertRules)), routing.Wrap(hs.AdminProvisioningReloadAlerting))
|
adminRoute.Post("/provisioning/alerting/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersAlertRules)), routing.Wrap(hs.AdminProvisioningReloadAlerting))
|
||||||
|
|
||||||
adminRoute.Post("/ldap/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPConfigReload)), routing.Wrap(hs.ReloadLDAPCfg))
|
|
||||||
adminRoute.Post("/ldap/sync/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPUsersSync)), routing.Wrap(hs.PostSyncUserWithLDAP))
|
|
||||||
adminRoute.Get("/ldap/:username", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPUsersRead)), routing.Wrap(hs.GetUserFromLDAP))
|
|
||||||
adminRoute.Get("/ldap/status", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)), routing.Wrap(hs.GetLDAPStatus))
|
|
||||||
}, reqSignedIn)
|
}, reqSignedIn)
|
||||||
|
|
||||||
// Administering users
|
// Administering users
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/hooks"
|
"github.com/grafana/grafana/pkg/services/hooks"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||||
"github.com/grafana/grafana/pkg/services/licensing"
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
@@ -174,7 +173,6 @@ type HTTPServer struct {
|
|||||||
grafanaUpdateChecker *updatechecker.GrafanaService
|
grafanaUpdateChecker *updatechecker.GrafanaService
|
||||||
pluginsUpdateChecker *updatechecker.PluginsService
|
pluginsUpdateChecker *updatechecker.PluginsService
|
||||||
searchUsersService searchusers.Service
|
searchUsersService searchusers.Service
|
||||||
ldapGroups ldap.Groups
|
|
||||||
teamGuardian teamguardian.TeamGuardian
|
teamGuardian teamguardian.TeamGuardian
|
||||||
queryDataService *query.Service
|
queryDataService *query.Service
|
||||||
serviceAccountsService serviceaccounts.Service
|
serviceAccountsService serviceaccounts.Service
|
||||||
@@ -241,7 +239,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
encryptionService encryption.Internal, grafanaUpdateChecker *updatechecker.GrafanaService,
|
encryptionService encryption.Internal, grafanaUpdateChecker *updatechecker.GrafanaService,
|
||||||
pluginsUpdateChecker *updatechecker.PluginsService, searchUsersService searchusers.Service,
|
pluginsUpdateChecker *updatechecker.PluginsService, searchUsersService searchusers.Service,
|
||||||
dataSourcesService datasources.DataSourceService, queryDataService *query.Service,
|
dataSourcesService datasources.DataSourceService, queryDataService *query.Service,
|
||||||
ldapGroups ldap.Groups, teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service,
|
teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service,
|
||||||
authInfoService login.AuthInfoService, storageService store.StorageService, httpEntityStore httpentitystore.HTTPEntityStore,
|
authInfoService login.AuthInfoService, storageService store.StorageService, httpEntityStore httpentitystore.HTTPEntityStore,
|
||||||
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
|
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
|
||||||
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
|
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
|
||||||
@@ -325,7 +323,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
httpEntityStore: httpEntityStore,
|
httpEntityStore: httpEntityStore,
|
||||||
DataSourcesService: dataSourcesService,
|
DataSourcesService: dataSourcesService,
|
||||||
searchUsersService: searchUsersService,
|
searchUsersService: searchUsersService,
|
||||||
ldapGroups: ldapGroups,
|
|
||||||
teamGuardian: teamGuardian,
|
teamGuardian: teamGuardian,
|
||||||
queryDataService: queryDataService,
|
queryDataService: queryDataService,
|
||||||
serviceAccountsService: serviceaccountsService,
|
serviceAccountsService: serviceaccountsService,
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
|
ldapapi "github.com/grafana/grafana/pkg/services/ldap/api"
|
||||||
"github.com/grafana/grafana/pkg/services/live"
|
"github.com/grafana/grafana/pkg/services/live"
|
||||||
"github.com/grafana/grafana/pkg/services/live/pushhttp"
|
"github.com/grafana/grafana/pkg/services/live/pushhttp"
|
||||||
"github.com/grafana/grafana/pkg/services/login/authinfoservice"
|
"github.com/grafana/grafana/pkg/services/login/authinfoservice"
|
||||||
@@ -53,7 +54,7 @@ func ProvideBackgroundServiceRegistry(
|
|||||||
_ dashboardsnapshots.Service, _ *alerting.AlertNotificationService,
|
_ dashboardsnapshots.Service, _ *alerting.AlertNotificationService,
|
||||||
_ serviceaccounts.Service, _ *guardian.Provider,
|
_ serviceaccounts.Service, _ *guardian.Provider,
|
||||||
_ *plugindashboardsservice.DashboardUpdater, _ *sanitizer.Provider,
|
_ *plugindashboardsservice.DashboardUpdater, _ *sanitizer.Provider,
|
||||||
_ *grpcserver.HealthService, _ entity.EntityStoreServer, _ *grpcserver.ReflectionService,
|
_ *grpcserver.HealthService, _ entity.EntityStoreServer, _ *grpcserver.ReflectionService, _ *ldapapi.Service,
|
||||||
) *BackgroundServiceRegistry {
|
) *BackgroundServiceRegistry {
|
||||||
return NewBackgroundServiceRegistry(
|
return NewBackgroundServiceRegistry(
|
||||||
httpServer,
|
httpServer,
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/hooks"
|
"github.com/grafana/grafana/pkg/services/hooks"
|
||||||
|
ldapapi "github.com/grafana/grafana/pkg/services/ldap/api"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||||
"github.com/grafana/grafana/pkg/services/live"
|
"github.com/grafana/grafana/pkg/services/live"
|
||||||
@@ -248,6 +249,7 @@ var wireBasicSet = wire.NewSet(
|
|||||||
tracing.ProvideService,
|
tracing.ProvideService,
|
||||||
metrics.ProvideService,
|
metrics.ProvideService,
|
||||||
testdatasource.ProvideService,
|
testdatasource.ProvideService,
|
||||||
|
ldapapi.ProvideService,
|
||||||
opentsdb.ProvideService,
|
opentsdb.ProvideService,
|
||||||
social.ProvideService,
|
social.ProvideService,
|
||||||
influxdb.ProvideService,
|
influxdb.ProvideService,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/authn"
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/authn"
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
|
|||||||
@@ -9,23 +9,64 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
cfg *setting.Cfg
|
||||||
|
userService user.Service
|
||||||
|
authInfoService login.AuthInfoService
|
||||||
|
ldapGroupsService ldap.Groups
|
||||||
|
loginService login.Service
|
||||||
|
orgService org.Service
|
||||||
|
sessionService auth.UserTokenService
|
||||||
|
log log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideService(cfg *setting.Cfg, router routing.RouteRegister, accessControl ac.AccessControl,
|
||||||
|
userService user.Service, authInfoService login.AuthInfoService, ldapGroupsService ldap.Groups,
|
||||||
|
loginService login.Service, orgService org.Service, sessionService auth.UserTokenService) *Service {
|
||||||
|
s := &Service{
|
||||||
|
cfg: cfg,
|
||||||
|
userService: userService,
|
||||||
|
authInfoService: authInfoService,
|
||||||
|
ldapGroupsService: ldapGroupsService,
|
||||||
|
loginService: loginService,
|
||||||
|
orgService: orgService,
|
||||||
|
sessionService: sessionService,
|
||||||
|
log: log.New("ldap.api"),
|
||||||
|
}
|
||||||
|
|
||||||
|
authorize := ac.Middleware(accessControl)
|
||||||
|
reqGrafanaAdmin := middleware.ReqGrafanaAdmin
|
||||||
|
|
||||||
|
router.Group("/api/admin", func(adminRoute routing.RouteRegister) {
|
||||||
|
adminRoute.Post("/ldap/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPConfigReload)), routing.Wrap(s.ReloadLDAPCfg))
|
||||||
|
adminRoute.Post("/ldap/sync/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPUsersSync)), routing.Wrap(s.PostSyncUserWithLDAP))
|
||||||
|
adminRoute.Get("/ldap/:username", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPUsersRead)), routing.Wrap(s.GetUserFromLDAP))
|
||||||
|
adminRoute.Get("/ldap/status", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)), routing.Wrap(s.GetLDAPStatus))
|
||||||
|
}, middleware.ReqSignedIn)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
getLDAPConfig = multildap.GetConfig
|
getLDAPConfig = multildap.GetConfig
|
||||||
newLDAP = multildap.New
|
newLDAP = multildap.New
|
||||||
|
|
||||||
ldapLogger = log.New("LDAP.debug")
|
|
||||||
|
|
||||||
errOrganizationNotFound = func(orgId int64) error {
|
errOrganizationNotFound = func(orgId int64) error {
|
||||||
return fmt.Errorf("unable to find organization with ID '%d'", orgId)
|
return fmt.Errorf("unable to find organization with ID '%d'", orgId)
|
||||||
}
|
}
|
||||||
@@ -117,7 +158,7 @@ func (user *LDAPUserDTO) FetchOrgs(ctx context.Context, orga org.Service) error
|
|||||||
// 401: unauthorisedError
|
// 401: unauthorisedError
|
||||||
// 403: forbiddenError
|
// 403: forbiddenError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) ReloadLDAPCfg(c *contextmodel.ReqContext) response.Response {
|
func (s *Service) ReloadLDAPCfg(c *contextmodel.ReqContext) response.Response {
|
||||||
if !ldap.IsEnabled() {
|
if !ldap.IsEnabled() {
|
||||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||||
}
|
}
|
||||||
@@ -143,12 +184,12 @@ func (hs *HTTPServer) ReloadLDAPCfg(c *contextmodel.ReqContext) response.Respons
|
|||||||
// 401: unauthorisedError
|
// 401: unauthorisedError
|
||||||
// 403: forbiddenError
|
// 403: forbiddenError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) GetLDAPStatus(c *contextmodel.ReqContext) response.Response {
|
func (s *Service) GetLDAPStatus(c *contextmodel.ReqContext) response.Response {
|
||||||
if !ldap.IsEnabled() {
|
if !ldap.IsEnabled() {
|
||||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapConfig, err := getLDAPConfig(hs.Cfg)
|
ldapConfig, err := getLDAPConfig(s.cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
|
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
|
||||||
}
|
}
|
||||||
@@ -196,12 +237,12 @@ func (hs *HTTPServer) GetLDAPStatus(c *contextmodel.ReqContext) response.Respons
|
|||||||
// 401: unauthorisedError
|
// 401: unauthorisedError
|
||||||
// 403: forbiddenError
|
// 403: forbiddenError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.Response {
|
func (s *Service) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.Response {
|
||||||
if !ldap.IsEnabled() {
|
if !ldap.IsEnabled() {
|
||||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapConfig, err := getLDAPConfig(hs.Cfg)
|
ldapConfig, err := getLDAPConfig(s.cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
|
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
|
||||||
}
|
}
|
||||||
@@ -213,7 +254,7 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.
|
|||||||
|
|
||||||
query := user.GetUserByIDQuery{ID: userId}
|
query := user.GetUserByIDQuery{ID: userId}
|
||||||
|
|
||||||
usr, err := hs.userService.GetByID(c.Req.Context(), &query)
|
usr, err := s.userService.GetByID(c.Req.Context(), &query)
|
||||||
if err != nil { // validate the userId exists
|
if err != nil { // validate the userId exists
|
||||||
if errors.Is(err, user.ErrUserNotFound) {
|
if errors.Is(err, user.ErrUserNotFound) {
|
||||||
return response.Error(404, user.ErrUserNotFound.Error(), nil)
|
return response.Error(404, user.ErrUserNotFound.Error(), nil)
|
||||||
@@ -223,7 +264,7 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.
|
|||||||
}
|
}
|
||||||
|
|
||||||
authModuleQuery := &login.GetAuthInfoQuery{UserId: usr.ID, AuthModule: login.LDAPAuthModule}
|
authModuleQuery := &login.GetAuthInfoQuery{UserId: usr.ID, AuthModule: login.LDAPAuthModule}
|
||||||
if err := hs.authInfoService.GetAuthInfo(c.Req.Context(), authModuleQuery); err != nil { // validate the userId comes from LDAP
|
if err := s.authInfoService.GetAuthInfo(c.Req.Context(), authModuleQuery); err != nil { // validate the userId comes from LDAP
|
||||||
if errors.Is(err, user.ErrUserNotFound) {
|
if errors.Is(err, user.ErrUserNotFound) {
|
||||||
return response.Error(404, user.ErrUserNotFound.Error(), nil)
|
return response.Error(404, user.ErrUserNotFound.Error(), nil)
|
||||||
}
|
}
|
||||||
@@ -235,19 +276,19 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.
|
|||||||
userInfo, _, err := ldapServer.User(usr.Login)
|
userInfo, _, err := ldapServer.User(usr.Login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, multildap.ErrDidNotFindUser) { // User was not in the LDAP server - we need to take action:
|
if errors.Is(err, multildap.ErrDidNotFindUser) { // User was not in the LDAP server - we need to take action:
|
||||||
if hs.Cfg.AdminUser == usr.Login { // User is *the* Grafana Admin. We cannot disable it.
|
if s.cfg.AdminUser == usr.Login { // User is *the* Grafana Admin. We cannot disable it.
|
||||||
errMsg := fmt.Sprintf(`Refusing to sync grafana super admin "%s" - it would be disabled`, usr.Login)
|
errMsg := fmt.Sprintf(`Refusing to sync grafana super admin "%s" - it would be disabled`, usr.Login)
|
||||||
ldapLogger.Error(errMsg)
|
s.log.Error(errMsg)
|
||||||
return response.Error(http.StatusBadRequest, errMsg, err)
|
return response.Error(http.StatusBadRequest, errMsg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the user was not in the LDAP server. Let's disable it.
|
// Since the user was not in the LDAP server. Let's disable it.
|
||||||
err := hs.Login.DisableExternalUser(c.Req.Context(), usr.Login)
|
err := s.loginService.DisableExternalUser(c.Req.Context(), usr.Login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to disable the user", err)
|
return response.Error(http.StatusInternalServerError, "Failed to disable the user", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hs.AuthTokenService.RevokeAllUserTokens(c.Req.Context(), userId)
|
err = s.sessionService.RevokeAllUserTokens(c.Req.Context(), userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to remove session tokens for the user", err)
|
return response.Error(http.StatusInternalServerError, "Failed to remove session tokens for the user", err)
|
||||||
}
|
}
|
||||||
@@ -255,14 +296,14 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.
|
|||||||
return response.Error(http.StatusBadRequest, "User not found in LDAP. Disabled the user without updating information", nil) // should this be a success?
|
return response.Error(http.StatusBadRequest, "User not found in LDAP. Disabled the user without updating information", nil) // should this be a success?
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapLogger.Debug("Failed to sync the user with LDAP", "err", err)
|
s.log.Debug("Failed to sync the user with LDAP", "err", err)
|
||||||
return response.Error(http.StatusBadRequest, "Something went wrong while finding the user in LDAP", err)
|
return response.Error(http.StatusBadRequest, "Something went wrong while finding the user in LDAP", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
upsertCmd := &login.UpsertUserCommand{
|
upsertCmd := &login.UpsertUserCommand{
|
||||||
ReqContext: c,
|
ReqContext: c,
|
||||||
ExternalUser: userInfo,
|
ExternalUser: userInfo,
|
||||||
SignupAllowed: hs.Cfg.LDAPAllowSignup,
|
SignupAllowed: s.cfg.LDAPAllowSignup,
|
||||||
UserLookupParams: login.UserLookupParams{
|
UserLookupParams: login.UserLookupParams{
|
||||||
UserID: &usr.ID, // Upsert by ID only
|
UserID: &usr.ID, // Upsert by ID only
|
||||||
Email: nil,
|
Email: nil,
|
||||||
@@ -270,7 +311,7 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hs.Login.UpsertUser(c.Req.Context(), upsertCmd)
|
err = s.loginService.UpsertUser(c.Req.Context(), upsertCmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to update the user", err)
|
return response.Error(http.StatusInternalServerError, "Failed to update the user", err)
|
||||||
}
|
}
|
||||||
@@ -292,12 +333,12 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *contextmodel.ReqContext) response.
|
|||||||
// 401: unauthorisedError
|
// 401: unauthorisedError
|
||||||
// 403: forbiddenError
|
// 403: forbiddenError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
func (hs *HTTPServer) GetUserFromLDAP(c *contextmodel.ReqContext) response.Response {
|
func (s *Service) GetUserFromLDAP(c *contextmodel.ReqContext) response.Response {
|
||||||
if !ldap.IsEnabled() {
|
if !ldap.IsEnabled() {
|
||||||
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapConfig, err := getLDAPConfig(hs.Cfg)
|
ldapConfig, err := getLDAPConfig(s.cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration", err)
|
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration", err)
|
||||||
}
|
}
|
||||||
@@ -315,7 +356,7 @@ func (hs *HTTPServer) GetUserFromLDAP(c *contextmodel.ReqContext) response.Respo
|
|||||||
return response.Error(http.StatusNotFound, "No user was found in the LDAP server(s) with that username", err)
|
return response.Error(http.StatusNotFound, "No user was found in the LDAP server(s) with that username", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapLogger.Debug("user found", "user", user)
|
s.log.Debug("user found", "user", user)
|
||||||
|
|
||||||
name, surname := splitName(user.Name)
|
name, surname := splitName(user.Name)
|
||||||
|
|
||||||
@@ -354,12 +395,12 @@ func (hs *HTTPServer) GetUserFromLDAP(c *contextmodel.ReqContext) response.Respo
|
|||||||
u.OrgRoles = append(u.OrgRoles, LDAPRoleDTO{GroupDN: userGroup})
|
u.OrgRoles = append(u.OrgRoles, LDAPRoleDTO{GroupDN: userGroup})
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapLogger.Debug("mapping org roles", "orgsRoles", u.OrgRoles)
|
s.log.Debug("mapping org roles", "orgsRoles", u.OrgRoles)
|
||||||
if err := u.FetchOrgs(c.Req.Context(), hs.orgService); err != nil {
|
if err := u.FetchOrgs(c.Req.Context(), s.orgService); err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "An organization was not found - Please verify your LDAP configuration", err)
|
return response.Error(http.StatusBadRequest, "An organization was not found - Please verify your LDAP configuration", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Teams, err = hs.ldapGroups.GetTeams(user.Groups, orgIDs)
|
u.Teams, err = s.ldapGroupsService.GetTeams(user.Groups, orgIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "Unable to find the teams for this user", err)
|
return response.Error(http.StatusBadRequest, "Unable to find the teams for this user", err)
|
||||||
}
|
}
|
||||||
@@ -3,25 +3,22 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/auth/authtest"
|
"github.com/grafana/grafana/pkg/services/auth/authtest"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
|
||||||
"github.com/grafana/grafana/pkg/services/ldap"
|
"github.com/grafana/grafana/pkg/services/ldap"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ldap/multildap"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
|
||||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
"github.com/grafana/grafana/pkg/services/multildap"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
@@ -31,12 +28,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LDAPMock struct {
|
type LDAPMock struct {
|
||||||
Results []*login.ExternalUserInfo
|
Results []*login.ExternalUserInfo
|
||||||
|
UserSearchResult *login.ExternalUserInfo
|
||||||
|
UserSearchConfig ldap.ServerConfig
|
||||||
|
UserSearchError error
|
||||||
}
|
}
|
||||||
|
|
||||||
var userSearchResult *login.ExternalUserInfo
|
|
||||||
var userSearchConfig ldap.ServerConfig
|
|
||||||
var userSearchError error
|
|
||||||
var pingResult []*multildap.ServerStatus
|
var pingResult []*multildap.ServerStatus
|
||||||
var pingError error
|
var pingError error
|
||||||
|
|
||||||
@@ -54,59 +51,76 @@ func (m *LDAPMock) Users(logins []string) ([]*login.ExternalUserInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *LDAPMock) User(login string) (*login.ExternalUserInfo, ldap.ServerConfig, error) {
|
func (m *LDAPMock) User(login string) (*login.ExternalUserInfo, ldap.ServerConfig, error) {
|
||||||
return userSearchResult, userSearchConfig, userSearchError
|
return m.UserSearchResult, m.UserSearchConfig, m.UserSearchError
|
||||||
}
|
}
|
||||||
|
|
||||||
// ***
|
func setupAPITest(t *testing.T, opts ...func(a *Service)) (*Service, *webtest.Server) {
|
||||||
// GetUserFromLDAP tests
|
|
||||||
// ***
|
|
||||||
|
|
||||||
func getUserFromLDAPContext(t *testing.T, requestURL string, searchOrgRst []*org.OrgDTO) *scenarioContext {
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
router := routing.NewRouteRegister()
|
||||||
|
cfg := setting.NewCfg()
|
||||||
|
|
||||||
sc := setupScenarioContext(t, requestURL)
|
a := ProvideService(cfg,
|
||||||
|
router,
|
||||||
|
acimpl.ProvideAccessControl(cfg),
|
||||||
|
usertest.NewUserServiceFake(),
|
||||||
|
&logintest.AuthInfoServiceFake{},
|
||||||
|
ldap.ProvideGroupsService(),
|
||||||
|
&logintest.LoginServiceFake{},
|
||||||
|
&orgtest.FakeOrgService{},
|
||||||
|
authtest.NewFakeUserAuthTokenService(),
|
||||||
|
)
|
||||||
|
|
||||||
origLDAP := setting.LDAPEnabled
|
for _, o := range opts {
|
||||||
setting.LDAPEnabled = true
|
o(a)
|
||||||
t.Cleanup(func() { setting.LDAPEnabled = origLDAP })
|
}
|
||||||
|
|
||||||
hs := &HTTPServer{Cfg: setting.NewCfg(), ldapGroups: ldap.ProvideGroupsService(), orgService: &orgtest.FakeOrgService{ExpectedOrgs: searchOrgRst}}
|
server := webtest.NewServer(t, router)
|
||||||
|
|
||||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
return a, server
|
||||||
sc.context = c
|
|
||||||
return hs.GetUserFromLDAP(c)
|
|
||||||
})
|
|
||||||
|
|
||||||
sc.m.Get("/api/admin/ldap/:username", sc.defaultHandler)
|
|
||||||
|
|
||||||
sc.resp = httptest.NewRecorder()
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, requestURL, nil)
|
|
||||||
sc.req = req
|
|
||||||
sc.exec()
|
|
||||||
|
|
||||||
return sc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetUserFromLDAPAPIEndpoint_UserNotFound(t *testing.T) {
|
func TestGetUserFromLDAPAPIEndpoint_UserNotFound(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
return &ldap.Config{}, nil
|
return &ldap.Config{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
return &LDAPMock{}
|
return &LDAPMock{
|
||||||
|
UserSearchResult: nil,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userSearchResult = nil
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
a.orgService = &orgtest.FakeOrgService{
|
||||||
|
ExpectedOrgs: []*org.OrgDTO{},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
sc := getUserFromLDAPContext(t, "/api/admin/ldap/user-that-does-not-exist", []*org.OrgDTO{})
|
req := server.NewGetRequest("/api/admin/ldap/user-that-does-not-exist")
|
||||||
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:read": {"*"}}},
|
||||||
|
})
|
||||||
|
|
||||||
require.Equal(t, sc.resp.Code, http.StatusNotFound)
|
res, err := server.Send(req)
|
||||||
assert.JSONEq(t, "{\"message\":\"No user was found in the LDAP server(s) with that username\"}", sc.resp.Body.String())
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||||
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
assert.JSONEq(t, "{\"message\":\"No user was found in the LDAP server(s) with that username\"}", string(bodyBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetUserFromLDAPAPIEndpoint_OrgNotfound(t *testing.T) {
|
func TestGetUserFromLDAPAPIEndpoint_OrgNotfound(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
isAdmin := true
|
isAdmin := true
|
||||||
userSearchResult = &login.ExternalUserInfo{
|
userSearchResult := &login.ExternalUserInfo{
|
||||||
Name: "John Doe",
|
Name: "John Doe",
|
||||||
Email: "john.doe@example.com",
|
Email: "john.doe@example.com",
|
||||||
Login: "johndoe",
|
Login: "johndoe",
|
||||||
@@ -115,7 +129,7 @@ func TestGetUserFromLDAPAPIEndpoint_OrgNotfound(t *testing.T) {
|
|||||||
IsGrafanaAdmin: &isAdmin,
|
IsGrafanaAdmin: &isAdmin,
|
||||||
}
|
}
|
||||||
|
|
||||||
userSearchConfig = ldap.ServerConfig{
|
userSearchConfig := ldap.ServerConfig{
|
||||||
Attr: ldap.AttributeMap{
|
Attr: ldap.AttributeMap{
|
||||||
Name: "ldap-name",
|
Name: "ldap-name",
|
||||||
Surname: "ldap-surname",
|
Surname: "ldap-surname",
|
||||||
@@ -136,32 +150,54 @@ func TestGetUserFromLDAPAPIEndpoint_OrgNotfound(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
|
return &LDAPMock{
|
||||||
|
UserSearchResult: userSearchResult,
|
||||||
|
UserSearchConfig: userSearchConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mockOrgSearchResult := []*org.OrgDTO{
|
mockOrgSearchResult := []*org.OrgDTO{
|
||||||
{ID: 1, Name: "Main Org."},
|
{ID: 1, Name: "Main Org."},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
a.orgService = &orgtest.FakeOrgService{
|
||||||
|
ExpectedOrgs: mockOrgSearchResult,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
return &ldap.Config{}, nil
|
return &ldap.Config{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
req := server.NewGetRequest("/api/admin/ldap/johndoe")
|
||||||
return &LDAPMock{}
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
}
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:read": {"*"}}},
|
||||||
|
})
|
||||||
|
|
||||||
sc := getUserFromLDAPContext(t, "/api/admin/ldap/johndoe", mockOrgSearchResult)
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, http.StatusBadRequest, sc.resp.Code)
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
|
||||||
var res map[string]interface{}
|
var resMap map[string]interface{}
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &res)
|
err = json.Unmarshal(bodyBytes, &resMap)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "unable to find organization with ID '2'", res["error"])
|
assert.Equal(t, "unable to find organization with ID '2'", resMap["error"])
|
||||||
assert.Equal(t, "An organization was not found - Please verify your LDAP configuration", res["message"])
|
assert.Equal(t, "An organization was not found - Please verify your LDAP configuration", resMap["message"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetUserFromLDAPAPIEndpoint(t *testing.T) {
|
func TestGetUserFromLDAPAPIEndpoint(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
isAdmin := true
|
isAdmin := true
|
||||||
userSearchResult = &login.ExternalUserInfo{
|
userSearchResult := &login.ExternalUserInfo{
|
||||||
Name: "John Doe",
|
Name: "John Doe",
|
||||||
Email: "john.doe@example.com",
|
Email: "john.doe@example.com",
|
||||||
Login: "johndoe",
|
Login: "johndoe",
|
||||||
@@ -170,7 +206,7 @@ func TestGetUserFromLDAPAPIEndpoint(t *testing.T) {
|
|||||||
IsGrafanaAdmin: &isAdmin,
|
IsGrafanaAdmin: &isAdmin,
|
||||||
}
|
}
|
||||||
|
|
||||||
userSearchConfig = ldap.ServerConfig{
|
userSearchConfig := ldap.ServerConfig{
|
||||||
Attr: ldap.AttributeMap{
|
Attr: ldap.AttributeMap{
|
||||||
Name: "ldap-name",
|
Name: "ldap-name",
|
||||||
Surname: "ldap-surname",
|
Surname: "ldap-surname",
|
||||||
@@ -200,12 +236,30 @@ func TestGetUserFromLDAPAPIEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
return &LDAPMock{}
|
return &LDAPMock{
|
||||||
|
UserSearchResult: userSearchResult,
|
||||||
|
UserSearchConfig: userSearchConfig,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := getUserFromLDAPContext(t, "/api/admin/ldap/johndoe", mockOrgSearchResult)
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
a.orgService = &orgtest.FakeOrgService{
|
||||||
|
ExpectedOrgs: mockOrgSearchResult,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
assert.Equal(t, sc.resp.Code, http.StatusOK)
|
req := server.NewGetRequest("/api/admin/ldap/johndoe")
|
||||||
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:read": {"*"}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
|
||||||
expected := `
|
expected := `
|
||||||
{
|
{
|
||||||
@@ -231,12 +285,16 @@ func TestGetUserFromLDAPAPIEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
assert.JSONEq(t, expected, sc.resp.Body.String())
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
assert.JSONEq(t, expected, string(bodyBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetUserFromLDAPAPIEndpoint_WithTeamHandler(t *testing.T) {
|
func TestGetUserFromLDAPAPIEndpoint_WithTeamHandler(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
isAdmin := true
|
isAdmin := true
|
||||||
userSearchResult = &login.ExternalUserInfo{
|
userSearchResult := &login.ExternalUserInfo{
|
||||||
Name: "John Doe",
|
Name: "John Doe",
|
||||||
Email: "john.doe@example.com",
|
Email: "john.doe@example.com",
|
||||||
Login: "johndoe",
|
Login: "johndoe",
|
||||||
@@ -245,7 +303,7 @@ func TestGetUserFromLDAPAPIEndpoint_WithTeamHandler(t *testing.T) {
|
|||||||
IsGrafanaAdmin: &isAdmin,
|
IsGrafanaAdmin: &isAdmin,
|
||||||
}
|
}
|
||||||
|
|
||||||
userSearchConfig = ldap.ServerConfig{
|
userSearchConfig := ldap.ServerConfig{
|
||||||
Attr: ldap.AttributeMap{
|
Attr: ldap.AttributeMap{
|
||||||
Name: "ldap-name",
|
Name: "ldap-name",
|
||||||
Surname: "ldap-surname",
|
Surname: "ldap-surname",
|
||||||
@@ -270,12 +328,30 @@ func TestGetUserFromLDAPAPIEndpoint_WithTeamHandler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
return &LDAPMock{}
|
return &LDAPMock{
|
||||||
|
UserSearchResult: userSearchResult,
|
||||||
|
UserSearchConfig: userSearchConfig,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := getUserFromLDAPContext(t, "/api/admin/ldap/johndoe", mockOrgSearchResult)
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
a.orgService = &orgtest.FakeOrgService{
|
||||||
|
ExpectedOrgs: mockOrgSearchResult,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
require.Equal(t, sc.resp.Code, http.StatusOK)
|
req := server.NewGetRequest("/api/admin/ldap/johndoe")
|
||||||
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:read": {"*"}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
|
||||||
expected := `
|
expected := `
|
||||||
{
|
{
|
||||||
@@ -300,41 +376,14 @@ func TestGetUserFromLDAPAPIEndpoint_WithTeamHandler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
assert.JSONEq(t, expected, sc.resp.Body.String())
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
}
|
assert.JSONEq(t, expected, string(bodyBytes))
|
||||||
|
|
||||||
// ***
|
|
||||||
// GetLDAPStatus tests
|
|
||||||
// ***
|
|
||||||
|
|
||||||
func getLDAPStatusContext(t *testing.T) *scenarioContext {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
requestURL := "/api/admin/ldap/status"
|
|
||||||
sc := setupScenarioContext(t, requestURL)
|
|
||||||
|
|
||||||
ldap := setting.LDAPEnabled
|
|
||||||
setting.LDAPEnabled = true
|
|
||||||
t.Cleanup(func() { setting.LDAPEnabled = ldap })
|
|
||||||
|
|
||||||
hs := &HTTPServer{Cfg: setting.NewCfg()}
|
|
||||||
|
|
||||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
|
||||||
sc.context = c
|
|
||||||
return hs.GetLDAPStatus(c)
|
|
||||||
})
|
|
||||||
|
|
||||||
sc.m.Get("/api/admin/ldap/status", sc.defaultHandler)
|
|
||||||
|
|
||||||
sc.resp = httptest.NewRecorder()
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, requestURL, nil)
|
|
||||||
sc.req = req
|
|
||||||
sc.exec()
|
|
||||||
|
|
||||||
return sc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetLDAPStatusAPIEndpoint(t *testing.T) {
|
func TestGetLDAPStatusAPIEndpoint(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
pingResult = []*multildap.ServerStatus{
|
pingResult = []*multildap.ServerStatus{
|
||||||
{Host: "10.0.0.3", Port: 361, Available: true, Error: nil},
|
{Host: "10.0.0.3", Port: 361, Available: true, Error: nil},
|
||||||
{Host: "10.0.0.3", Port: 362, Available: true, Error: nil},
|
{Host: "10.0.0.3", Port: 362, Available: true, Error: nil},
|
||||||
@@ -349,9 +398,21 @@ func TestGetLDAPStatusAPIEndpoint(t *testing.T) {
|
|||||||
return &LDAPMock{}
|
return &LDAPMock{}
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := getLDAPStatusContext(t)
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
})
|
||||||
|
|
||||||
require.Equal(t, http.StatusOK, sc.resp.Code)
|
req := server.NewGetRequest("/api/admin/ldap/status")
|
||||||
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.status:read": {}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
|
||||||
expected := `
|
expected := `
|
||||||
[
|
[
|
||||||
@@ -360,70 +421,44 @@ func TestGetLDAPStatusAPIEndpoint(t *testing.T) {
|
|||||||
{ "host": "10.0.0.5", "port": 361, "available": false, "error": "something is awfully wrong" }
|
{ "host": "10.0.0.5", "port": 361, "available": false, "error": "something is awfully wrong" }
|
||||||
]
|
]
|
||||||
`
|
`
|
||||||
assert.JSONEq(t, expected, sc.resp.Body.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ***
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
// PostSyncUserWithLDAP tests
|
assert.JSONEq(t, expected, string(bodyBytes))
|
||||||
// ***
|
|
||||||
|
|
||||||
func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(*testing.T, *scenarioContext), userService user.Service) *scenarioContext {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
sc := setupScenarioContext(t, requestURL)
|
|
||||||
sc.authInfoService = &logintest.AuthInfoServiceFake{}
|
|
||||||
|
|
||||||
ldap := setting.LDAPEnabled
|
|
||||||
t.Cleanup(func() {
|
|
||||||
setting.LDAPEnabled = ldap
|
|
||||||
})
|
|
||||||
setting.LDAPEnabled = true
|
|
||||||
|
|
||||||
hs := &HTTPServer{
|
|
||||||
Cfg: sc.cfg,
|
|
||||||
AuthTokenService: authtest.NewFakeUserAuthTokenService(),
|
|
||||||
Login: loginservice.LoginServiceMock{},
|
|
||||||
authInfoService: sc.authInfoService,
|
|
||||||
userService: userService,
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
|
||||||
sc.context = c
|
|
||||||
return hs.PostSyncUserWithLDAP(c)
|
|
||||||
})
|
|
||||||
|
|
||||||
sc.m.Post("/api/admin/ldap/sync/:id", sc.defaultHandler)
|
|
||||||
|
|
||||||
sc.resp = httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest(http.MethodPost, requestURL, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
preHook(t, sc)
|
|
||||||
|
|
||||||
sc.req = req
|
|
||||||
sc.exec()
|
|
||||||
|
|
||||||
return sc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
|
func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
userServiceMock := usertest.NewUserServiceFake()
|
userServiceMock := usertest.NewUserServiceFake()
|
||||||
userServiceMock.ExpectedUser = &user.User{Login: "ldap-daniel", ID: 34}
|
userServiceMock.ExpectedUser = &user.User{Login: "ldap-daniel", ID: 34}
|
||||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
|
|
||||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
|
||||||
return &ldap.Config{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
return &LDAPMock{}
|
return &ldap.Config{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
userSearchResult = &login.ExternalUserInfo{
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
|
return &LDAPMock{UserSearchResult: &login.ExternalUserInfo{
|
||||||
Login: "ldap-daniel",
|
Login: "ldap-daniel",
|
||||||
}
|
}}
|
||||||
}, userServiceMock)
|
}
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, sc.resp.Code)
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
a.userService = userServiceMock
|
||||||
|
})
|
||||||
|
|
||||||
|
req := server.NewPostRequest("/api/admin/ldap/sync/34", nil)
|
||||||
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:sync": {}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
|
||||||
expected := `
|
expected := `
|
||||||
{
|
{
|
||||||
@@ -431,23 +466,40 @@ func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
assert.JSONEq(t, expected, sc.resp.Body.String())
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
assert.JSONEq(t, expected, string(bodyBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
|
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
userServiceMock := usertest.NewUserServiceFake()
|
userServiceMock := usertest.NewUserServiceFake()
|
||||||
userServiceMock.ExpectedError = user.ErrUserNotFound
|
userServiceMock.ExpectedError = user.ErrUserNotFound
|
||||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
|
|
||||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
|
||||||
return &ldap.Config{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
return &LDAPMock{}
|
return &ldap.Config{}, nil
|
||||||
}
|
}
|
||||||
}, userServiceMock)
|
|
||||||
|
|
||||||
assert.Equal(t, http.StatusNotFound, sc.resp.Code)
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
|
return &LDAPMock{}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
a.userService = userServiceMock
|
||||||
|
})
|
||||||
|
|
||||||
|
req := server.NewPostRequest("/api/admin/ldap/sync/34", nil)
|
||||||
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:sync": {}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||||
|
|
||||||
expected := `
|
expected := `
|
||||||
{
|
{
|
||||||
@@ -455,52 +507,83 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
assert.JSONEq(t, expected, sc.resp.Body.String())
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
assert.JSONEq(t, expected, string(bodyBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
|
func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
userServiceMock := usertest.NewUserServiceFake()
|
userServiceMock := usertest.NewUserServiceFake()
|
||||||
userServiceMock.ExpectedUser = &user.User{Login: "ldap-daniel", ID: 34}
|
userServiceMock.ExpectedUser = &user.User{Login: "ldap-daniel", ID: 34}
|
||||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
|
|
||||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
|
||||||
return &ldap.Config{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
return &LDAPMock{}
|
return &ldap.Config{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
userSearchError = multildap.ErrDidNotFindUser
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
|
return &LDAPMock{UserSearchError: multildap.ErrDidNotFindUser}
|
||||||
|
}
|
||||||
|
|
||||||
sc.cfg.AdminUser = "ldap-daniel"
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
}, userServiceMock)
|
a.userService = userServiceMock
|
||||||
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
|
a.cfg.AdminUser = "ldap-daniel"
|
||||||
|
})
|
||||||
|
|
||||||
var res map[string]interface{}
|
req := server.NewPostRequest("/api/admin/ldap/sync/34", nil)
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &res)
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:sync": {}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
|
||||||
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
var resMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(bodyBytes, &resMap)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "did not find a user", res["error"])
|
assert.Equal(t, "did not find a user", resMap["error"])
|
||||||
assert.Equal(t, "Refusing to sync grafana super admin \"ldap-daniel\" - it would be disabled", res["message"])
|
assert.Equal(t, "Refusing to sync grafana super admin \"ldap-daniel\" - it would be disabled", resMap["message"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
defer func() { setting.LDAPEnabled = false }()
|
||||||
|
|
||||||
userServiceMock := usertest.NewUserServiceFake()
|
userServiceMock := usertest.NewUserServiceFake()
|
||||||
userServiceMock.ExpectedUser = &user.User{Login: "ldap-daniel", ID: 34}
|
userServiceMock.ExpectedUser = &user.User{Login: "ldap-daniel", ID: 34}
|
||||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
|
|
||||||
sc.authInfoService.ExpectedExternalUser = &login.ExternalUserInfo{IsDisabled: true, UserId: 34}
|
|
||||||
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
|
||||||
return &ldap.Config{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
return &LDAPMock{}
|
return &ldap.Config{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
userSearchResult = nil
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
userSearchError = multildap.ErrDidNotFindUser
|
return &LDAPMock{UserSearchError: multildap.ErrDidNotFindUser}
|
||||||
}, userServiceMock)
|
}
|
||||||
|
|
||||||
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
|
a.userService = userServiceMock
|
||||||
|
a.authInfoService = &logintest.AuthInfoServiceFake{ExpectedExternalUser: &login.ExternalUserInfo{IsDisabled: true, UserId: 34}}
|
||||||
|
})
|
||||||
|
|
||||||
|
req := server.NewPostRequest("/api/admin/ldap/sync/34", nil)
|
||||||
|
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: {"ldap.user:sync": {}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
res, err := server.Send(req)
|
||||||
|
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
|
||||||
expected := `
|
expected := `
|
||||||
{
|
{
|
||||||
@@ -508,14 +591,46 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
assert.JSONEq(t, expected, sc.resp.Body.String())
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
assert.JSONEq(t, expected, string(bodyBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ***
|
|
||||||
// Access control tests for ldap endpoints
|
|
||||||
// ***
|
|
||||||
|
|
||||||
func TestLDAP_AccessControl(t *testing.T) {
|
func TestLDAP_AccessControl(t *testing.T) {
|
||||||
|
setting.LDAPEnabled = true
|
||||||
|
|
||||||
|
f, errC := os.CreateTemp("", "ldap.toml")
|
||||||
|
require.NoError(t, errC)
|
||||||
|
|
||||||
|
_, errF := f.WriteString(
|
||||||
|
`[[servers]]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 389
|
||||||
|
search_filter = "(cn=%s)"
|
||||||
|
search_base_dns = ["dc=grafana,dc=org"]`)
|
||||||
|
require.NoError(t, errF)
|
||||||
|
|
||||||
|
setting.LDAPConfigFile = f.Name()
|
||||||
|
|
||||||
|
errF = f.Close()
|
||||||
|
require.NoError(t, errF)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
setting.LDAPEnabled = false
|
||||||
|
setting.LDAPConfigFile = ""
|
||||||
|
}()
|
||||||
|
|
||||||
|
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
|
||||||
|
return &ldap.Config{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
||||||
|
return &LDAPMock{
|
||||||
|
UserSearchResult: &login.ExternalUserInfo{
|
||||||
|
Login: "ldap-daniel",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
desc string
|
desc string
|
||||||
method string
|
method string
|
||||||
@@ -600,41 +715,22 @@ func TestLDAP_AccessControl(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
enabled := setting.LDAPEnabled
|
_, server := setupAPITest(t, func(a *Service) {
|
||||||
configFile := setting.LDAPConfigFile
|
a.userService = &usertest.FakeUserService{ExpectedUser: &user.User{Login: "ldap-daniel", ID: 1}}
|
||||||
|
|
||||||
t.Cleanup(func() {
|
|
||||||
setting.LDAPEnabled = enabled
|
|
||||||
setting.LDAPConfigFile = configFile
|
|
||||||
})
|
|
||||||
|
|
||||||
setting.LDAPEnabled = true
|
|
||||||
path, err := filepath.Abs("../../conf/ldap.toml")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
setting.LDAPConfigFile = path
|
|
||||||
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
cfg.LDAPEnabled = true
|
|
||||||
hs.Cfg = cfg
|
|
||||||
hs.SQLStore = dbtest.NewFakeDB()
|
|
||||||
hs.orgService = orgtest.NewOrgServiceFake()
|
|
||||||
hs.userService = &usertest.FakeUserService{ExpectedUser: &user.User{}}
|
|
||||||
hs.ldapGroups = &ldap.OSSGroups{}
|
|
||||||
hs.Login = &loginservice.LoginServiceMock{}
|
|
||||||
hs.authInfoService = &logintest.AuthInfoServiceFake{}
|
|
||||||
})
|
})
|
||||||
// Add minimal setup to pass handler
|
// Add minimal setup to pass handler
|
||||||
userSearchResult = &login.ExternalUserInfo{}
|
res, err := server.Send(
|
||||||
userSearchError = nil
|
webtest.RequestWithSignedInUser(server.NewRequest(tt.method, tt.url, nil),
|
||||||
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
|
userWithPermissions(1, tt.permissions)))
|
||||||
return &LDAPMock{}
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewRequest(tt.method, tt.url, nil), userWithPermissions(1, tt.permissions)))
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, tt.expectedCode, res.StatusCode)
|
|
||||||
|
bodyBytes, _ := io.ReadAll(res.Body)
|
||||||
|
assert.Equal(t, tt.expectedCode, res.StatusCode, string(bodyBytes))
|
||||||
require.NoError(t, res.Body.Close())
|
require.NoError(t, res.Body.Close())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func userWithPermissions(orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
|
||||||
|
return &user.SignedInUser{OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user