AuthProxy: Allow disabling Auth Proxy cache (#83755)

* extract auth proxy settings

* simplify auth proxy methods

* add doc mentions
This commit is contained in:
Jo 2024-03-01 11:31:06 +01:00 committed by GitHub
parent 1cec975a66
commit 36a19bfa83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 145 additions and 110 deletions

View File

@ -832,7 +832,7 @@ enabled = false
header_name = X-WEBAUTH-USER
header_property = username
auto_sign_up = true
sync_ttl = 60
sync_ttl = 15
whitelist =
headers =
headers_encoded = false
@ -1305,7 +1305,7 @@ loki_basic_auth_password =
# mylabelkey = mylabelvalue
[unified_alerting.state_history.annotations]
# Controls retention of annotations automatically created while evaluating alert rules.
# Controls retention of annotations automatically created while evaluating alert rules.
# Alert state history backend must be configured to be annotations (see setting [unified_alerting.state_history].backend).
# Configures how long alert annotations are stored for. Default is 0, which keeps them forever.

View File

@ -36,7 +36,8 @@ header_property = username
auto_sign_up = true
# Define cache time to live in minutes
# If combined with Grafana LDAP integration it is also the sync interval
sync_ttl = 60
# Set to 0 to always fetch and sync the latest user data
sync_ttl = 15
# Limit where auth proxy requests come from by configuring a list of IP addresses.
# This can be used to prevent users spoofing the X-WEBAUTH-USER header.
# Example `whitelist = 192.168.1.1, 192.168.1.0/24, 2001::23, 2001::0/120`
@ -231,6 +232,10 @@ ProxyPassReverse / http://grafana:3000/
With our Grafana and Apache containers running, you can now connect to http://localhost/ and log in using the username/password we created in the htpasswd file.
{{% admonition type="note" %}}
If the user is deleted from Grafana, the user will be not be able to login and resync until after the `sync_ttl` has expired.
{{% /admonition %}}
### Team Sync (Enterprise only)
> Only available in Grafana Enterprise v6.3+

View File

@ -344,11 +344,11 @@ func validateJSONData(ctx context.Context, jsonData *simplejson.Json, cfg *setti
return nil
}
if cfg.AuthProxyEnabled {
if cfg.AuthProxy.Enabled {
for key, value := range jsonData.MustMap() {
if strings.HasPrefix(key, datasources.CustomHeaderName) {
header := fmt.Sprint(value)
if http.CanonicalHeaderKey(header) == http.CanonicalHeaderKey(cfg.AuthProxyHeaderName) {
if http.CanonicalHeaderKey(header) == http.CanonicalHeaderKey(cfg.AuthProxy.HeaderName) {
datasourcesLogger.Error("Forbidden to add a data source header with a name equal to auth proxy header name", "headerName", key)
return errors.New("validation error, invalid header name specified")
}

View File

@ -147,10 +147,10 @@ func TestAddDataSource_InvalidJSONData(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources")
hs.Cfg = setting.NewCfg()
hs.Cfg.AuthProxyEnabled = true
hs.Cfg.AuthProxyHeaderName = "X-AUTH-PROXY-HEADER"
hs.Cfg.AuthProxy.Enabled = true
hs.Cfg.AuthProxy.HeaderName = "X-AUTH-PROXY-HEADER"
jsonData := simplejson.New()
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxyHeaderName)
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxy.HeaderName)
sc.m.Post(sc.url, routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
@ -201,10 +201,10 @@ func TestUpdateDataSource_InvalidJSONData(t *testing.T) {
}
sc := setupScenarioContext(t, "/api/datasources/1234")
hs.Cfg.AuthProxyEnabled = true
hs.Cfg.AuthProxyHeaderName = "X-AUTH-PROXY-HEADER"
hs.Cfg.AuthProxy.Enabled = true
hs.Cfg.AuthProxy.HeaderName = "X-AUTH-PROXY-HEADER"
jsonData := simplejson.New()
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxyHeaderName)
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxy.HeaderName)
sc.m.Put(sc.url, routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
@ -297,7 +297,7 @@ func TestUpdateDataSourceTeamHTTPHeaders_InvalidJSONData(t *testing.T) {
},
}
sc := setupScenarioContext(t, fmt.Sprintf("/api/datasources/%s", tenantID))
hs.Cfg.AuthProxyEnabled = true
hs.Cfg.AuthProxy.Enabled = true
jsonData := simplejson.New()
jsonData.Set("teamHttpHeaders", tc.data)

View File

@ -171,7 +171,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
AppUrl: hs.Cfg.AppURL,
AppSubUrl: hs.Cfg.AppSubURL,
AllowOrgCreate: (hs.Cfg.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin,
AuthProxyEnabled: hs.Cfg.AuthProxyEnabled,
AuthProxyEnabled: hs.Cfg.AuthProxy.Enabled,
LdapEnabled: hs.Cfg.LDAPAuthEnabled,
JwtHeaderName: hs.Cfg.JWTAuth.HeaderName,
JwtUrlLogin: hs.Cfg.JWTAuth.URLLogin,
@ -322,7 +322,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
oauthProviders := hs.SocialService.GetOAuthInfoProviders()
frontendSettings.Auth = dtos.FrontendSettingsAuthDTO{
AuthProxyEnableLoginToken: hs.Cfg.AuthProxyEnableLoginToken,
AuthProxyEnableLoginToken: hs.Cfg.AuthProxy.EnableLoginToken,
OAuthSkipOrgRoleUpdateSync: hs.Cfg.OAuthSkipOrgRoleUpdateSync,
SAMLSkipOrgRoleSync: hs.Cfg.SAMLSkipOrgRoleSync,
LDAPSkipOrgRoleSync: hs.Cfg.LDAPSkipOrgRoleSync,

View File

@ -125,8 +125,8 @@ func (hs *HTTPServer) LoginView(c *contextmodel.ReqContext) {
if c.IsSignedIn {
// Assign login token to auth proxy users if enable_login_token = true
if hs.Cfg.AuthProxyEnabled &&
hs.Cfg.AuthProxyEnableLoginToken &&
if hs.Cfg.AuthProxy.Enabled &&
hs.Cfg.AuthProxy.EnableLoginToken &&
c.SignedInUser.AuthenticatedBy == loginservice.AuthProxyAuthModule {
user := &user.User{ID: c.SignedInUser.UserID, Email: c.SignedInUser.Email, Login: c.SignedInUser.Login}
err := hs.loginUserWithUser(user, c)

View File

@ -600,8 +600,8 @@ func TestAuthProxyLoginWithEnableLoginTokenAndEnabledOauthAutoLogin(t *testing.T
return response.Empty(http.StatusOK)
})
sc.cfg.AuthProxyEnabled = true
sc.cfg.AuthProxyEnableLoginToken = true
sc.cfg.AuthProxy.Enabled = true
sc.cfg.AuthProxy.EnableLoginToken = true
sc.m.Get(sc.url, sc.defaultHandler)
sc.fakeReqNoAssertions("GET", sc.url).exec()
@ -640,8 +640,8 @@ func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioConte
return response.Empty(http.StatusOK)
})
sc.cfg.AuthProxyEnabled = true
sc.cfg.AuthProxyEnableLoginToken = enableLoginToken
sc.cfg.AuthProxy.Enabled = true
sc.cfg.AuthProxy.EnableLoginToken = enableLoginToken
sc.m.Get(sc.url, sc.defaultHandler)
sc.fakeReqNoAssertions("GET", sc.url).exec()

View File

@ -147,11 +147,11 @@ func (hs *HTTPServer) UpdateSignedInUser(c *contextmodel.ReqContext) response.Re
return errResponse
}
if hs.Cfg.AuthProxyEnabled {
if hs.Cfg.AuthProxyHeaderProperty == "email" && cmd.Email != c.SignedInUser.GetEmail() {
if hs.Cfg.AuthProxy.Enabled {
if hs.Cfg.AuthProxy.HeaderProperty == "email" && cmd.Email != c.SignedInUser.GetEmail() {
return response.Error(http.StatusBadRequest, "Not allowed to change email when auth proxy is using email property", nil)
}
if hs.Cfg.AuthProxyHeaderProperty == "username" && cmd.Login != c.SignedInUser.GetLogin() {
if hs.Cfg.AuthProxy.HeaderProperty == "username" && cmd.Login != c.SignedInUser.GetLogin() {
return response.Error(http.StatusBadRequest, "Not allowed to change username when auth proxy is using username property", nil)
}
}

View File

@ -85,7 +85,7 @@ func TestMetrics(t *testing.T) {
AnonymousEnabled: true,
BasicAuthEnabled: true,
LDAPAuthEnabled: true,
AuthProxyEnabled: true,
AuthProxy: setting.AuthProxySettings{Enabled: true},
Packaging: "deb",
ReportingDistributor: "hosted-grafana",
}

View File

@ -145,7 +145,7 @@ func TestCollectingUsageStats(t *testing.T) {
AnonymousEnabled: true,
BasicAuthEnabled: true,
LDAPAuthEnabled: true,
AuthProxyEnabled: true,
AuthProxy: setting.AuthProxySettings{Enabled: true},
Packaging: "deb",
ReportingDistributor: "hosted-grafana",
RemoteCacheOptions: &setting.RemoteCacheOptions{

View File

@ -122,7 +122,7 @@ func ProvideService(
}
}
if s.cfg.AuthProxyEnabled && len(proxyClients) > 0 {
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)

View File

@ -13,7 +13,7 @@ func (s *Service) getUsageStats(ctx context.Context) (map[string]any, error) {
authTypes := map[string]bool{}
authTypes["basic_auth"] = s.cfg.BasicAuthEnabled
authTypes["ldap"] = s.cfg.LDAPAuthEnabled
authTypes["auth_proxy"] = s.cfg.AuthProxyEnabled
authTypes["auth_proxy"] = s.cfg.AuthProxy.Enabled
authTypes["anonymous"] = s.cfg.AnonymousEnabled
authTypes["jwt"] = s.cfg.JWTAuth.Enabled
authTypes["grafana_password"] = !s.cfg.DisableLogin

View File

@ -20,7 +20,7 @@ func TestService_getUsageStats(t *testing.T) {
svc.cfg.DisableLoginForm = false
svc.cfg.DisableLogin = false
svc.cfg.BasicAuthEnabled = true
svc.cfg.AuthProxyEnabled = true
svc.cfg.AuthProxy.Enabled = true
svc.cfg.JWTAuth.Enabled = true
svc.cfg.LDAPAuthEnabled = true
svc.cfg.EditorsCanAdmin = true

View File

@ -40,11 +40,11 @@ func (c *Grafana) AuthenticateProxy(ctx context.Context, r *authn.Request, usern
FetchSyncedUser: true,
SyncOrgRoles: true,
SyncPermissions: true,
AllowSignUp: c.cfg.AuthProxyAutoSignUp,
AllowSignUp: c.cfg.AuthProxy.AutoSignUp,
},
}
switch c.cfg.AuthProxyHeaderProperty {
switch c.cfg.AuthProxy.HeaderProperty {
case "username":
identity.Login = username
addr, err := mail.ParseAddress(username)
@ -55,7 +55,7 @@ func (c *Grafana) AuthenticateProxy(ctx context.Context, r *authn.Request, usern
identity.Login = username
identity.Email = username
default:
return nil, errInvalidProxyHeader.Errorf("invalid auth proxy header property, expected username or email but got: %s", c.cfg.AuthProxyHeaderProperty)
return nil, errInvalidProxyHeader.Errorf("invalid auth proxy header property, expected username or email but got: %s", c.cfg.AuthProxy.HeaderProperty)
}
if v, ok := additional[proxyFieldName]; ok {

View File

@ -94,8 +94,8 @@ func TestGrafana_AuthenticateProxy(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
cfg := setting.NewCfg()
cfg.AuthProxyAutoSignUp = true
cfg.AuthProxyHeaderProperty = tt.proxyProperty
cfg.AuthProxy.AutoSignUp = true
cfg.AuthProxy.HeaderProperty = tt.proxyProperty
c := ProvideGrafana(cfg, usertest.NewUserServiceFake())
identity, err := c.AuthenticateProxy(context.Background(), tt.req, tt.username, tt.additional)

View File

@ -3,6 +3,7 @@ package clients
import (
"context"
"encoding/hex"
"errors"
"fmt"
"hash/fnv"
"net"
@ -12,6 +13,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
authidentity "github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/login"
@ -43,7 +45,7 @@ var (
)
func ProvideProxy(cfg *setting.Cfg, cache proxyCache, clients ...authn.ProxyClient) (*Proxy, error) {
list, err := parseAcceptList(cfg.AuthProxyWhitelist)
list, err := parseAcceptList(cfg.AuthProxy.Whitelist)
if err != nil {
return nil, err
}
@ -73,34 +75,22 @@ func (c *Proxy) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden
return nil, errNotAcceptedIP.Errorf("request ip is not in the configured accept list")
}
username := getProxyHeader(r, c.cfg.AuthProxyHeaderName, c.cfg.AuthProxyHeadersEncoded)
username := getProxyHeader(r, c.cfg.AuthProxy.HeaderName, c.cfg.AuthProxy.HeadersEncoded)
if len(username) == 0 {
return nil, errEmptyProxyHeader.Errorf("no username provided in auth proxy header")
}
additional := getAdditionalProxyHeaders(r, c.cfg)
cacheKey, ok := getProxyCacheKey(username, additional)
if ok {
// 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
if entry, err := c.cache.Get(ctx, cacheKey); err == nil {
uid, err := strconv.ParseInt(string(entry), 10, 64)
if err != nil {
c.log.FromContext(ctx).Warn("Failed to parse user id from cache", "error", err, "userId", string(entry))
} else {
return &authn.Identity{
ID: authn.NamespacedID(authn.NamespaceUser, uid),
OrgID: r.OrgID,
// FIXME: This does not match the actual auth module used, but should not have any impact
// Maybe caching the auth module used with the user ID would be a good idea
AuthenticatedBy: login.AuthProxyAuthModule,
ClientParams: authn.ClientParams{
FetchSyncedUser: true,
SyncPermissions: true,
},
}, nil
}
if c.cfg.AuthProxy.SyncTTL != 0 && ok {
identity, errCache := c.retrieveIDFromCache(ctx, cacheKey, r)
if errCache == nil {
return identity, nil
}
if !errors.Is(errCache, remotecache.ErrCacheItemNotFound) {
c.log.FromContext(ctx).Warn("Failed to fetch auth proxy info from cache", "error", errCache)
}
}
@ -117,8 +107,34 @@ func (c *Proxy) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden
return nil, clientErr
}
// 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) {
entry, err := c.cache.Get(ctx, cacheKey)
if err != nil {
return nil, err
}
uid, err := strconv.ParseInt(string(entry), 10, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse user id from cache: %w - entry: %s", err, string(entry))
}
return &authn.Identity{
ID: authn.NamespacedID(authn.NamespaceUser, uid),
OrgID: r.OrgID,
// FIXME: This does not match the actual auth module used, but should not have any impact
// Maybe caching the auth module used with the user ID would be a good idea
AuthenticatedBy: login.AuthProxyAuthModule,
ClientParams: authn.ClientParams{
FetchSyncedUser: true,
SyncPermissions: true,
},
}, nil
}
func (c *Proxy) Test(ctx context.Context, r *authn.Request) bool {
return len(getProxyHeader(r, c.cfg.AuthProxyHeaderName, c.cfg.AuthProxyHeadersEncoded)) != 0
return len(getProxyHeader(r, c.cfg.AuthProxy.HeaderName, c.cfg.AuthProxy.HeadersEncoded)) != 0
}
func (c *Proxy) Priority() uint {
@ -147,7 +163,7 @@ func (c *Proxy) Hook(ctx context.Context, identity *authn.Identity, r *authn.Req
// 3. Name = x; Role = Admin # cache hit with key Name=x;Role=Admin, no update, the user stays with Role=Editor
// To avoid such a problem we also cache the key used using `prefix:[username]`.
// Then whenever we get a cache miss due to changes in any header we use it to invalidate the previous item.
username := getProxyHeader(r, c.cfg.AuthProxyHeaderName, c.cfg.AuthProxyHeadersEncoded)
username := getProxyHeader(r, c.cfg.AuthProxy.HeaderName, c.cfg.AuthProxy.HeadersEncoded)
userKey := fmt.Sprintf("%s:%s", proxyCachePrefix, username)
// invalidate previously cached user id
@ -159,7 +175,7 @@ func (c *Proxy) Hook(ctx context.Context, identity *authn.Identity, r *authn.Req
c.log.FromContext(ctx).Debug("Cache proxy user", "userId", id)
bytes := []byte(strconv.FormatInt(id, 10))
duration := time.Duration(c.cfg.AuthProxySyncTTL) * time.Minute
duration := time.Duration(c.cfg.AuthProxy.SyncTTL) * time.Minute
if err := c.cache.Set(ctx, identity.ClientParams.CacheAuthProxyKey, bytes, duration); err != nil {
c.log.Warn("Failed to cache proxy user", "error", err, "userId", id)
}
@ -232,7 +248,7 @@ func getProxyHeader(r *authn.Request, headerName string, encoded bool) string {
func getAdditionalProxyHeaders(r *authn.Request, cfg *setting.Cfg) map[string]string {
additional := make(map[string]string, len(proxyFields))
for _, k := range proxyFields {
if v := getProxyHeader(r, cfg.AuthProxyHeaders[k], cfg.AuthProxyHeadersEncoded); v != "" {
if v := getProxyHeader(r, cfg.AuthProxy.Headers[k], cfg.AuthProxy.HeadersEncoded); v != "" {
additional[k] = v
}
}

View File

@ -100,9 +100,9 @@ func TestProxy_Authenticate(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
cfg := setting.NewCfg()
cfg.AuthProxyHeaderName = "X-Username"
cfg.AuthProxyHeaders = tt.proxyHeaders
cfg.AuthProxyWhitelist = tt.ips
cfg.AuthProxy.HeaderName = "X-Username"
cfg.AuthProxy.Headers = tt.proxyHeaders
cfg.AuthProxy.Whitelist = tt.ips
calledUsername := ""
var calledAdditional map[string]string
@ -166,7 +166,7 @@ func TestProxy_Test(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
cfg := setting.NewCfg()
cfg.AuthProxyHeaderName = "Proxy-Header"
cfg.AuthProxy.HeaderName = "Proxy-Header"
c, _ := ProvideProxy(cfg, nil, nil, nil)
assert.Equal(t, tt.expectedOK, c.Test(context.Background(), tt.req))
@ -197,8 +197,8 @@ func (f fakeCache) Delete(ctx context.Context, key string) error {
func TestProxy_Hook(t *testing.T) {
cfg := setting.NewCfg()
cfg.AuthProxyHeaderName = "X-Username"
cfg.AuthProxyHeaders = map[string]string{
cfg.AuthProxy.HeaderName = "X-Username"
cfg.AuthProxy.Headers = map[string]string{
proxyFieldRole: "X-Role",
}
cache := &fakeCache{data: make(map[string][]byte)}

View File

@ -195,9 +195,9 @@ func WithAuthHTTPHeaders(ctx context.Context, cfg *setting.Cfg) context.Context
}
// if auth proxy is enabled add the main proxy header and all configured headers
if cfg.AuthProxyEnabled {
list.Items = append(list.Items, cfg.AuthProxyHeaderName)
for _, header := range cfg.AuthProxyHeaders {
if cfg.AuthProxy.Enabled {
list.Items = append(list.Items, cfg.AuthProxy.HeaderName)
for _, header := range cfg.AuthProxy.Headers {
if header != "" {
list.Items = append(list.Items, header)
}

View File

@ -114,9 +114,9 @@ func TestContextHandler(t *testing.T) {
cfg := setting.NewCfg()
cfg.JWTAuth.Enabled = true
cfg.JWTAuth.HeaderName = "jwt-header"
cfg.AuthProxyEnabled = true
cfg.AuthProxyHeaderName = "proxy-header"
cfg.AuthProxyHeaders = map[string]string{
cfg.AuthProxy.Enabled = true
cfg.AuthProxy.HeaderName = "proxy-header"
cfg.AuthProxy.Headers = map[string]string{
"name": "proxy-header-name",
}

View File

@ -675,7 +675,7 @@ func (s *Service) getCustomHeaders(jsonData *simplejson.Json, decryptedValues ma
// skip a header with name that corresponds to auth proxy header's name
// to make sure that data source proxy isn't used to circumvent auth proxy.
// For more context take a look at CVE-2022-35957
if s.cfg.AuthProxyEnabled && http.CanonicalHeaderKey(key) == http.CanonicalHeaderKey(s.cfg.AuthProxyHeaderName) {
if s.cfg.AuthProxy.Enabled && http.CanonicalHeaderKey(key) == http.CanonicalHeaderKey(s.cfg.AuthProxy.HeaderName) {
continue
}

View File

@ -256,15 +256,7 @@ type Cfg struct {
Azure *azsettings.AzureSettings
// Auth proxy settings
AuthProxyEnabled bool
AuthProxyHeaderName string
AuthProxyHeaderProperty string
AuthProxyAutoSignUp bool
AuthProxyEnableLoginToken bool
AuthProxyWhitelist string
AuthProxyHeaders map[string]string
AuthProxyHeadersEncoded bool
AuthProxySyncTTL int
AuthProxy AuthProxySettings
// OAuth
OAuthAutoLogin bool
@ -1197,6 +1189,7 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error {
cfg.handleAWSConfig()
cfg.readAzureSettings()
cfg.readAuthJWTSettings()
cfg.readAuthProxySettings()
cfg.readSessionConfig()
if err := cfg.readSmtpSettings(); err != nil {
return err
@ -1617,31 +1610,6 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
cfg.ExtendedJWTExpectAudience = authExtendedJWT.Key("expect_audience").MustString("")
cfg.ExtendedJWTExpectIssuer = authExtendedJWT.Key("expect_issuer").MustString("")
// Auth Proxy
authProxy := iniFile.Section("auth.proxy")
cfg.AuthProxyEnabled = authProxy.Key("enabled").MustBool(false)
cfg.AuthProxyHeaderName = valueAsString(authProxy, "header_name", "")
cfg.AuthProxyHeaderProperty = valueAsString(authProxy, "header_property", "")
cfg.AuthProxyAutoSignUp = authProxy.Key("auto_sign_up").MustBool(true)
cfg.AuthProxyEnableLoginToken = authProxy.Key("enable_login_token").MustBool(false)
cfg.AuthProxySyncTTL = authProxy.Key("sync_ttl").MustInt()
cfg.AuthProxyWhitelist = valueAsString(authProxy, "whitelist", "")
cfg.AuthProxyHeaders = make(map[string]string)
headers := valueAsString(authProxy, "headers", "")
for _, propertyAndHeader := range util.SplitString(headers) {
split := strings.SplitN(propertyAndHeader, ":", 2)
if len(split) == 2 {
cfg.AuthProxyHeaders[split[0]] = split[1]
}
}
cfg.AuthProxyHeadersEncoded = authProxy.Key("headers_encoded").MustBool(false)
// SSO Settings
ssoSettings := iniFile.Section("sso_settings")
cfg.SSOSettingsReloadInterval = ssoSettings.Key("reload_interval").MustDuration(1 * time.Minute)

View File

@ -0,0 +1,45 @@
package setting
import (
"strings"
"github.com/grafana/grafana/pkg/util"
)
type AuthProxySettings struct {
// Auth Proxy
Enabled bool
HeaderName string
HeaderProperty string
AutoSignUp bool
EnableLoginToken bool
Whitelist string
Headers map[string]string
HeadersEncoded bool
SyncTTL int
}
func (cfg *Cfg) readAuthProxySettings() {
authProxySettings := AuthProxySettings{}
authProxy := cfg.Raw.Section("auth.proxy")
authProxySettings.Enabled = authProxy.Key("enabled").MustBool(false)
authProxySettings.HeaderName = valueAsString(authProxy, "header_name", "")
authProxySettings.HeaderProperty = valueAsString(authProxy, "header_property", "")
authProxySettings.AutoSignUp = authProxy.Key("auto_sign_up").MustBool(true)
authProxySettings.EnableLoginToken = authProxy.Key("enable_login_token").MustBool(false)
authProxySettings.SyncTTL = authProxy.Key("sync_ttl").MustInt(15)
authProxySettings.Whitelist = valueAsString(authProxy, "whitelist", "")
authProxySettings.Headers = make(map[string]string)
headers := valueAsString(authProxy, "headers", "")
for _, propertyAndHeader := range util.SplitString(headers) {
split := strings.SplitN(propertyAndHeader, ":", 2)
if len(split) == 2 {
authProxySettings.Headers[split[0]] = split[1]
}
}
authProxySettings.HeadersEncoded = authProxy.Key("headers_encoded").MustBool(false)
cfg.AuthProxy = authProxySettings
}

View File

@ -13,11 +13,12 @@ import (
"testing"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util/osutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util/osutil"
)
const (
@ -274,7 +275,7 @@ func TestLoadingSettings(t *testing.T) {
})
require.Nil(t, err)
require.Equal(t, 2, cfg.AuthProxySyncTTL)
require.Equal(t, 2, cfg.AuthProxy.SyncTTL)
})
t.Run("Test reading string values from .ini file", func(t *testing.T) {