From 4577e61ee7846448fdb4df3b979cae0ff375b7f8 Mon Sep 17 00:00:00 2001 From: Misi Date: Thu, 25 Jan 2024 11:13:24 +0100 Subject: [PATCH] Auth: Improve /admin/authentication permission checks and include new SSO pages (#81183) * Move evalAuthSettings to ssoutils * Improve permission check for auth page --- pkg/api/api.go | 18 +++++++------- pkg/services/accesscontrol/ssoutils/utils.go | 24 +++++++++++++++++++ pkg/services/navtree/navtreeimpl/admin.go | 11 +++------ .../auth-config/ProviderConfigForm.tsx | 2 +- 4 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 pkg/services/accesscontrol/ssoutils/utils.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 8ef843e5676..5ecc27e8029 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -35,6 +35,7 @@ import ( "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware/requestmeta" ac "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/ssoutils" "github.com/grafana/grafana/pkg/services/apikey" "github.com/grafana/grafana/pkg/services/auth" "github.com/grafana/grafana/pkg/services/correlations" @@ -220,7 +221,15 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/user/auth-tokens/rotate", routing.Wrap(hs.RotateUserAuthTokenRedirect)) } - r.Get("/admin/authentication/", authorize(evalAuthenticationSettings()), hs.Index) + adminAuthPageEvaluator := func() ac.Evaluator { + authnSettingsEval := ssoutils.EvalAuthenticationSettings(hs.Cfg) + if hs.Features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) { + return ac.EvalAny(authnSettingsEval, ssoutils.OauthSettingsEvaluator(hs.Cfg)) + } + return authnSettingsEval + } + + r.Get("/admin/authentication/", authorize(adminAuthPageEvaluator()), hs.Index) r.Get("/admin/authentication/ldap", authorize(ac.EvalPermission(ac.ActionLDAPStatusRead)), hs.Index) if hs.Features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) { providerParam := ac.Parameter("provider") @@ -633,10 +642,3 @@ func (hs *HTTPServer) registerRoutes() { r.Get("/api/snapshots-delete/:deleteKey", reqSnapshotPublicModeOrSignedIn, routing.Wrap(hs.DeleteDashboardSnapshotByDeleteKey)) r.Delete("/api/snapshots/:key", reqSignedIn, routing.Wrap(hs.DeleteDashboardSnapshot)) } - -func evalAuthenticationSettings() ac.Evaluator { - return ac.EvalAny(ac.EvalAll( - ac.EvalPermission(ac.ActionSettingsWrite, ac.ScopeSettingsSAML), - ac.EvalPermission(ac.ActionSettingsRead, ac.ScopeSettingsSAML), - ), ac.EvalPermission(ac.ActionLDAPStatusRead)) -} diff --git a/pkg/services/accesscontrol/ssoutils/utils.go b/pkg/services/accesscontrol/ssoutils/utils.go new file mode 100644 index 00000000000..857a2532af9 --- /dev/null +++ b/pkg/services/accesscontrol/ssoutils/utils.go @@ -0,0 +1,24 @@ +package ssoutils + +import ( + ac "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/setting" +) + +func EvalAuthenticationSettings(cfg *setting.Cfg) ac.Evaluator { + return ac.EvalAny( + ac.EvalAll( + ac.EvalPermission(ac.ActionSettingsWrite, ac.ScopeSettingsSAML), + ac.EvalPermission(ac.ActionSettingsRead, ac.ScopeSettingsSAML), + ), + ac.EvalPermission(ac.ActionLDAPStatusRead)) +} + +func OauthSettingsEvaluator(cfg *setting.Cfg) ac.Evaluator { + result := make([]ac.Evaluator, 0, len(cfg.SSOSettingsConfigurableProviders)) + for provider := range cfg.SSOSettingsConfigurableProviders { + result = append(result, ac.EvalPermission(ac.ActionSettingsRead, ac.ScopeSettingsOAuth(provider))) + result = append(result, ac.EvalPermission(ac.ActionSettingsWrite, ac.ScopeSettingsOAuth(provider))) + } + return ac.EvalAny(result...) +} diff --git a/pkg/services/navtree/navtreeimpl/admin.go b/pkg/services/navtree/navtreeimpl/admin.go index b50e171f8fd..ae85a68a9dd 100644 --- a/pkg/services/navtree/navtreeimpl/admin.go +++ b/pkg/services/navtree/navtreeimpl/admin.go @@ -2,6 +2,7 @@ package navtreeimpl import ( ac "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/ssoutils" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" "github.com/grafana/grafana/pkg/services/correlations" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -80,7 +81,8 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink }) } - if (authConfigUIAvailable && hasAccess(evalAuthenticationSettings())) || s.features.IsEnabled(ctx, featuremgmt.FlagSsoSettingsApi) { + if authConfigUIAvailable && hasAccess(ssoutils.EvalAuthenticationSettings(s.cfg)) || + (hasAccess(ssoutils.OauthSettingsEvaluator(s.cfg)) && s.features.IsEnabled(ctx, featuremgmt.FlagSsoSettingsApi)) { configNodes = append(configNodes, &navtree.NavLink{ Text: "Authentication", Id: "authentication", @@ -150,10 +152,3 @@ func enableServiceAccount(s *ServiceImpl, c *contextmodel.ReqContext) bool { hasAccess := ac.HasAccess(s.accessControl, c) return hasAccess(serviceaccounts.AccessEvaluator) } - -func evalAuthenticationSettings() ac.Evaluator { - return ac.EvalAny(ac.EvalAll( - ac.EvalPermission(ac.ActionSettingsWrite, ac.ScopeSettingsSAML), - ac.EvalPermission(ac.ActionSettingsRead, ac.ScopeSettingsSAML), - ), ac.EvalPermission(ac.ActionLDAPStatusRead)) -} diff --git a/public/app/features/auth-config/ProviderConfigForm.tsx b/public/app/features/auth-config/ProviderConfigForm.tsx index 3734ff36a05..97e235468c2 100644 --- a/public/app/features/auth-config/ProviderConfigForm.tsx +++ b/public/app/features/auth-config/ProviderConfigForm.tsx @@ -31,7 +31,7 @@ export const ProviderConfigForm = ({ config, provider, isLoading }: ProviderConf setValue, unregister, formState: { errors, dirtyFields, isSubmitted }, - } = useForm({ defaultValues: dataToDTO(config), reValidateMode: 'onSubmit' }); + } = useForm({ defaultValues: dataToDTO(config), mode: 'onChange', reValidateMode: 'onChange' }); const [isSaving, setIsSaving] = useState(false); const providerFields = fields[provider]; const [submitError, setSubmitError] = useState(false);