diff --git a/backend/server/api/middlewares.go b/backend/server/api/middlewares.go index 567e3a9f41f..b460c676450 100644 --- a/backend/server/api/middlewares.go +++ b/backend/server/api/middlewares.go @@ -93,7 +93,14 @@ func getBasicAuthUserInfo(c *gin.Context, basicRes context.BasicRes) (*common.Us func OAuth2ProxyAuthentication(basicRes context.BasicRes) gin.HandlerFunc { logger := basicRes.GetLogger() - forwardedUserSecret := strings.TrimSpace(basicRes.GetConfigReader().GetString("FORWARDED_USER_SECRET")) + cfg := basicRes.GetConfigReader() + forwardedUserSecret := strings.TrimSpace(cfg.GetString("FORWARDED_USER_SECRET")) + // A Basic header is only an identity nginx (config-ui) has already verified + // against ADMIN_USER/ADMIN_PASS; the lake never checks the password. With + // AUTH_ENABLED the lake is the authenticator (session cookie, API key, or + // forwarded headers with the shared secret), so an unverified Basic header + // must not become a user, or any username:password passes RequireAuth. + trustBasicAuth := !(cfg.IsSet("AUTH_ENABLED") && cfg.GetBool("AUTH_ENABLED")) return func(c *gin.Context) { _, exist := c.Get(common.USER) if !exist { @@ -101,7 +108,7 @@ func OAuth2ProxyAuthentication(basicRes context.BasicRes) gin.HandlerFunc { if err != nil { logger.Warn(err, "rejected forwarded user headers") } - if user == nil || user.Name == "" { + if (user == nil || user.Name == "") && trustBasicAuth { // fetch with basic auth header user, err = getBasicAuthUserInfo(c, basicRes) if err != nil { diff --git a/backend/server/api/middlewares_basicauth_test.go b/backend/server/api/middlewares_basicauth_test.go new file mode 100644 index 00000000000..40b1c253b7e --- /dev/null +++ b/backend/server/api/middlewares_basicauth_test.go @@ -0,0 +1,59 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "encoding/base64" + "testing" + + "github.com/spf13/viper" +) + +func basicAuthHeader(username, password string) map[string]string { + return map[string]string{ + "Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)), + } +} + +func TestOAuth2ProxyAuthenticationIgnoresBasicAuthWhenAuthEnabled(t *testing.T) { + cfg := viper.New() + cfg.Set("AUTH_ENABLED", true) + router := newProxyAuthRouterWithConfig(cfg) + body := performProxyAuthRequest(t, router, basicAuthHeader("anyone", "anything")) + if body.Authenticated { + t.Fatalf("expected Basic auth header to be ignored with AUTH_ENABLED=true, got %+v", body) + } +} + +func TestOAuth2ProxyAuthenticationTrustsBasicAuthWhenAuthDisabled(t *testing.T) { + cases := map[string]func(*viper.Viper){ + "AUTH_ENABLED unset": func(*viper.Viper) {}, + "AUTH_ENABLED explicit": func(cfg *viper.Viper) { cfg.Set("AUTH_ENABLED", false) }, + } + for name, configure := range cases { + t.Run(name, func(t *testing.T) { + cfg := viper.New() + configure(cfg) + router := newProxyAuthRouterWithConfig(cfg) + body := performProxyAuthRequest(t, router, basicAuthHeader("admin", "secret")) + if !body.Authenticated || body.Name != "admin" { + t.Fatalf("expected Basic auth user to be kept without AUTH_ENABLED, got %+v", body) + } + }) + } +} diff --git a/backend/server/api/middlewares_forwardsecret_test.go b/backend/server/api/middlewares_forwardsecret_test.go index 4b390aa526a..5b8063d40d5 100644 --- a/backend/server/api/middlewares_forwardsecret_test.go +++ b/backend/server/api/middlewares_forwardsecret_test.go @@ -57,9 +57,13 @@ type proxyAuthResponse struct { } func newProxyAuthRouter(secret string) *gin.Engine { - gin.SetMode(gin.TestMode) cfg := viper.New() cfg.Set("FORWARDED_USER_SECRET", secret) + return newProxyAuthRouterWithConfig(cfg) +} + +func newProxyAuthRouterWithConfig(cfg *viper.Viper) *gin.Engine { + gin.SetMode(gin.TestMode) basicRes := &proxyAuthTestBasicRes{ cfg: cfg, logger: logruslog.Global,