mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Add IsClientEnabled
and IsEnabled
for the authn.Service
and authn.Client
interfaces (#86034)
* Add `Service. IsClientEnabled` and `Client.IsEnabled` functions * Implement `IsEnabled` function for authn clients * Implement `IsClientEnabled` function for authn services
This commit is contained in:
parent
2b62167842
commit
51da96d94e
@ -20,6 +20,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginscdn"
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
@ -58,9 +59,10 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F
|
||||
}
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: cfg,
|
||||
Features: features,
|
||||
License: &licensing.OSSLicensingService{Cfg: cfg},
|
||||
authnService: &authntest.FakeService{},
|
||||
Cfg: cfg,
|
||||
Features: features,
|
||||
License: &licensing.OSSLicensingService{Cfg: cfg},
|
||||
RenderService: &rendering.RenderingService{
|
||||
Cfg: cfg,
|
||||
RendererPluginManager: &fakeRendererPluginManager{},
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/infra/network"
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/middleware/cookies"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
@ -329,7 +328,7 @@ func (hs *HTTPServer) redirectURLWithErrorCookie(c *contextmodel.ReqContext, err
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) samlEnabled() bool {
|
||||
return hs.SettingsProvider.KeyValue("auth.saml", "enabled").MustBool(false) && hs.License.FeatureEnabled(social.SAMLProviderName)
|
||||
return hs.authnService.IsClientEnabled(authn.ClientSAML)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) samlName() string {
|
||||
|
@ -491,6 +491,7 @@ func TestLoginOAuthRedirect(t *testing.T) {
|
||||
oAuthInfos: oAuthInfos,
|
||||
}
|
||||
hs := &HTTPServer{
|
||||
authnService: &authntest.FakeService{},
|
||||
Cfg: cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
@ -657,6 +658,7 @@ func TestLogoutSaml(t *testing.T) {
|
||||
license.On("FeatureEnabled", "saml").Return(true)
|
||||
|
||||
hs := &HTTPServer{
|
||||
authnService: &authntest.FakeService{},
|
||||
Cfg: sc.cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: sc.cfg},
|
||||
License: license,
|
||||
|
@ -58,6 +58,10 @@ func (a *Anonymous) Authenticate(ctx context.Context, r *authn.Request) (*authn.
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *Anonymous) IsEnabled() bool {
|
||||
return a.cfg.AnonymousEnabled
|
||||
}
|
||||
|
||||
func (a *Anonymous) Test(ctx context.Context, r *authn.Request) bool {
|
||||
// If anonymous client is register it can always be used for authentication
|
||||
return true
|
||||
|
@ -94,6 +94,19 @@ type Service interface {
|
||||
|
||||
// RegisterClient will register a new authn.Client that can be used for authentication
|
||||
RegisterClient(c Client)
|
||||
|
||||
// IsClientEnabled returns true if the client is enabled.
|
||||
//
|
||||
// The client lookup follows the same formats used by the `authn` package
|
||||
// constants.
|
||||
//
|
||||
// For OAuth clients, use the `authn.ClientWithPrefix(name)` to get the provider
|
||||
// name. Append the prefix `auth.client.{providerName}`.
|
||||
//
|
||||
// Example:
|
||||
// - "saml" = "auth.client.saml"
|
||||
// - "github" = "auth.client.github"
|
||||
IsClientEnabled(client string) bool
|
||||
}
|
||||
|
||||
type IdentitySynchronizer interface {
|
||||
@ -105,6 +118,8 @@ type Client interface {
|
||||
Name() string
|
||||
// Authenticate performs the authentication for the request
|
||||
Authenticate(ctx context.Context, r *Request) (*Identity, error)
|
||||
// IsEnabled returns the enabled status of the client
|
||||
IsEnabled() bool
|
||||
}
|
||||
|
||||
// ContextAwareClient is an optional interface that auth client can implement.
|
||||
|
@ -320,6 +320,15 @@ func (s *Service) RegisterClient(c authn.Client) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) IsClientEnabled(name string) bool {
|
||||
client, ok := s.clients[name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return client.IsEnabled()
|
||||
}
|
||||
|
||||
func (s *Service) SyncIdentity(ctx context.Context, identity *authn.Identity) error {
|
||||
r := &authn.Request{OrgID: identity.OrgID}
|
||||
// hack to not update last seen on external syncs
|
||||
|
@ -40,6 +40,10 @@ func (f *FakeService) Authenticate(ctx context.Context, r *authn.Request) (*auth
|
||||
return f.ExpectedIdentity, f.ExpectedErr
|
||||
}
|
||||
|
||||
func (f *FakeService) IsClientEnabled(name string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *FakeService) RegisterPostAuthHook(hook authn.PostAuthHookFn, priority uint) {}
|
||||
|
||||
func (f *FakeService) Login(ctx context.Context, client string, r *authn.Request) (*authn.Identity, error) {
|
||||
@ -119,6 +123,8 @@ func (f *FakeClient) Authenticate(ctx context.Context, r *authn.Request) (*authn
|
||||
return f.ExpectedIdentity, f.ExpectedErr
|
||||
}
|
||||
|
||||
func (f FakeClient) IsEnabled() bool { return true }
|
||||
|
||||
func (f *FakeClient) Test(ctx context.Context, r *authn.Request) bool {
|
||||
return f.ExpectedTest
|
||||
}
|
||||
@ -161,6 +167,8 @@ func (f FakeRedirectClient) Authenticate(ctx context.Context, r *authn.Request)
|
||||
return f.ExpectedIdentity, f.ExpectedErr
|
||||
}
|
||||
|
||||
func (f FakeRedirectClient) IsEnabled() bool { return true }
|
||||
|
||||
func (f FakeRedirectClient) RedirectURL(ctx context.Context, r *authn.Request) (*authn.Redirect, error) {
|
||||
return f.ExpectedRedirect, f.ExpectedErr
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ func (m *MockService) Authenticate(ctx context.Context, r *authn.Request) (*auth
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (m *MockService) IsClientEnabled(name string) bool {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (m *MockService) Login(ctx context.Context, client string, r *authn.Request) (*authn.Identity, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
@ -87,6 +91,10 @@ func (m MockClient) Authenticate(ctx context.Context, r *authn.Request) (*authn.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m MockClient) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m MockClient) Test(ctx context.Context, r *authn.Request) bool {
|
||||
if m.TestFunc != nil {
|
||||
return m.TestFunc(ctx, r)
|
||||
|
@ -70,6 +70,10 @@ func (s *APIKey) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide
|
||||
return newServiceAccountIdentity(key), nil
|
||||
}
|
||||
|
||||
func (s *APIKey) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *APIKey) getAPIKey(ctx context.Context, token string) (*apikey.APIKey, error) {
|
||||
fn := s.getFromToken
|
||||
if !strings.HasPrefix(token, satokengen.GrafanaPrefix) {
|
||||
|
@ -38,6 +38,10 @@ func (c *Basic) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden
|
||||
return c.client.AuthenticatePassword(ctx, r, username, password)
|
||||
}
|
||||
|
||||
func (c *Basic) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Basic) Test(ctx context.Context, r *authn.Request) bool {
|
||||
if r.HTTPRequest == nil {
|
||||
return false
|
||||
|
@ -91,6 +91,10 @@ func (s *ExtendedJWT) Authenticate(ctx context.Context, r *authn.Request) (*auth
|
||||
return s.authenticateAsService(claims)
|
||||
}
|
||||
|
||||
func (s *ExtendedJWT) IsEnabled() bool {
|
||||
return s.cfg.ExtJWTAuth.Enabled
|
||||
}
|
||||
|
||||
func (s *ExtendedJWT) authenticateAsUser(idTokenClaims,
|
||||
accessTokenClaims *ExtendedJWTClaims) (*authn.Identity, error) {
|
||||
// Only allow access policies to impersonate
|
||||
|
@ -38,3 +38,7 @@ func (c *Form) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ident
|
||||
}
|
||||
return c.client.AuthenticatePassword(ctx, r, form.Username, form.Password)
|
||||
}
|
||||
|
||||
func (c *Form) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ func (i *IdentityClient) Name() string {
|
||||
return "identity"
|
||||
}
|
||||
|
||||
func (i *IdentityClient) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *IdentityClient) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) {
|
||||
return i.identity, nil
|
||||
}
|
||||
|
@ -139,6 +139,10 @@ func (s *JWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identi
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *JWT) IsEnabled() bool {
|
||||
return s.cfg.JWTAuth.Enabled
|
||||
}
|
||||
|
||||
// remove sensitive query param
|
||||
// avoid JWT URL login passing auth_token in URL
|
||||
func (s *JWT) stripSensitiveParam(httpRequest *http.Request) {
|
||||
|
@ -199,6 +199,15 @@ func (c *OAuth) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *OAuth) IsEnabled() bool {
|
||||
provider := c.socialService.GetOAuthInfoProvider(c.providerName)
|
||||
if provider == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return provider.Enabled
|
||||
}
|
||||
|
||||
func (c *OAuth) RedirectURL(ctx context.Context, r *authn.Request) (*authn.Redirect, error) {
|
||||
var opts []oauth2.AuthCodeOption
|
||||
|
||||
|
@ -507,6 +507,49 @@ func TestGenPKCECodeVerifier(t *testing.T) {
|
||||
assert.Len(t, verifier, 128)
|
||||
}
|
||||
|
||||
func TestIsEnabled(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
oauthCfg *social.OAuthInfo
|
||||
expected bool
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
desc: "should return false when client is not enabled",
|
||||
oauthCfg: &social.OAuthInfo{Enabled: false},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "should return false when client doesnt exists",
|
||||
oauthCfg: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "should return true when client is enabled",
|
||||
oauthCfg: &social.OAuthInfo{Enabled: true},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
fakeSocialSvc := &socialtest.FakeSocialService{
|
||||
ExpectedAuthInfoProvider: tt.oauthCfg,
|
||||
}
|
||||
cfg := setting.NewCfg()
|
||||
c := ProvideOAuth(
|
||||
social.GitHubProviderName,
|
||||
cfg,
|
||||
nil,
|
||||
fakeSocialSvc,
|
||||
&setting.OSSImpl{Cfg: cfg},
|
||||
featuremgmt.WithFeatures())
|
||||
assert.Equal(t, tt.expected, c.IsEnabled())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockConnector struct {
|
||||
AuthCodeURLFunc func(state string, opts ...oauth2.AuthCodeOption) string
|
||||
social.SocialConnector
|
||||
|
@ -107,6 +107,10 @@ func (c *Proxy) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden
|
||||
return nil, clientErr
|
||||
}
|
||||
|
||||
func (c *Proxy) IsEnabled() bool {
|
||||
return c.cfg.AuthProxy.Enabled
|
||||
}
|
||||
|
||||
// See if we have cached the user id, in that case we can fetch the signed-in user and skip sync.
|
||||
// Error here means that we could not find anything in cache, so we can proceed as usual
|
||||
func (c *Proxy) retrieveIDFromCache(ctx context.Context, cacheKey string, r *authn.Request) (*authn.Identity, error) {
|
||||
|
@ -59,6 +59,10 @@ func (c *Render) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Render) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Render) Test(ctx context.Context, r *authn.Request) bool {
|
||||
if r.HTTPRequest == nil {
|
||||
return false
|
||||
|
@ -78,6 +78,10 @@ func (s *Session) Authenticate(ctx context.Context, r *authn.Request) (*authn.Id
|
||||
return ident, nil
|
||||
}
|
||||
|
||||
func (s *Session) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Session) Test(ctx context.Context, r *authn.Request) bool {
|
||||
if s.cfg.LoginCookieName == "" {
|
||||
return false
|
||||
|
Loading…
Reference in New Issue
Block a user