diff --git a/cmd/roles/roles.go b/cmd/roles/roles.go index 135d18706a..0e90c25324 100644 --- a/cmd/roles/roles.go +++ b/cmd/roles/roles.go @@ -53,7 +53,6 @@ import ( "github.com/milvus-io/milvus/pkg/v3/tracer" "github.com/milvus-io/milvus/pkg/v3/util/conc" "github.com/milvus-io/milvus/pkg/v3/util/etcd" - "github.com/milvus-io/milvus/pkg/v3/util/expr" "github.com/milvus-io/milvus/pkg/v3/util/gc" "github.com/milvus-io/milvus/pkg/v3/util/logutil" "github.com/milvus-io/milvus/pkg/v3/util/metricsinfo" @@ -467,8 +466,6 @@ func (mr *MilvusRoles) Run() { }) healthz.SetComponentNum(len(enableComponents)) - expr.Init() - expr.Register("param", paramtable.Get()) mr.setupLogger() defer mlog.Cleanup() diff --git a/configs/milvus.yaml b/configs/milvus.yaml index 323dbc7d67..9ae0fb4709 100644 --- a/configs/milvus.yaml +++ b/configs/milvus.yaml @@ -1181,8 +1181,6 @@ common: defaultRootPassword: Milvus rootShouldBindRole: false # Whether the root user should bind a role when the authorization is enabled. enablePublicPrivilege: true # Whether to enable public privilege - exprEnabled: false # Whether to enable the /expr endpoint for debugging. - exprAuthMode: rootOnly # Authentication mode for the /expr endpoint. Valid values: rootOnly, rbac. rootOnly accepts only the root credentials via HTTP Basic Auth. rbac requires common.security.authorizationEnabled=true and grants access to any user holding the Expr privilege. internaltlsEnabled: false tlsMode: 0 session: diff --git a/go.mod b/go.mod index 5cfe10924e..85b2e03500 100644 --- a/go.mod +++ b/go.mod @@ -157,7 +157,6 @@ require ( github.com/ebitengine/purego v0.9.0 // indirect github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect - github.com/expr-lang/expr v1.15.7 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect @@ -314,7 +313,6 @@ require ( replace ( github.com/apache/arrow/go/v17 => github.com/milvus-io/arrow/go/v17 v17.0.2 github.com/bketelsen/crypt => github.com/bketelsen/crypt v0.0.4 // Fix security alert for core-os/etcd - github.com/expr-lang/expr => github.com/SimFG/expr v0.0.0-20250513112851-9b981e8400b9 github.com/go-kit/kit => github.com/go-kit/kit v0.1.0 github.com/greatroar/blobloom => github.com/milvus-io/blobloom v0.0.0-20240603110411-471ae49f3b93 github.com/ianlancetaylor/cgosymbolizer => github.com/milvus-io/cgosymbolizer v0.0.0-20260810014817-f3b338cf6824 diff --git a/go.sum b/go.sum index 352b448e19..1ffbf78820 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,6 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/RoaringBitmap/roaring/v2 v2.8.0 h1:y1rdtixfXvaITKzkfiKvScI0hlBJHe9sfzJp8cgeM7w= github.com/RoaringBitmap/roaring/v2 v2.8.0/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/SimFG/expr v0.0.0-20250513112851-9b981e8400b9 h1:eXnmJhsHt8m6NU3IJ19UthXJ8JK6e3tmfN07nym3BXs= -github.com/SimFG/expr v0.0.0-20250513112851-9b981e8400b9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/actgardner/gogen-avro/v10 v10.1.0/go.mod h1:o+ybmVjEa27AAr35FRqU98DJu1fXES56uXniYFv4yDA= diff --git a/internal/datacoord/server.go b/internal/datacoord/server.go index 09ed0485b3..66c3d20ef8 100644 --- a/internal/datacoord/server.go +++ b/internal/datacoord/server.go @@ -53,7 +53,6 @@ import ( "github.com/milvus-io/milvus/pkg/v3/proto/datapb" "github.com/milvus-io/milvus/pkg/v3/proto/querypb" "github.com/milvus-io/milvus/pkg/v3/util" - "github.com/milvus-io/milvus/pkg/v3/util/expr" "github.com/milvus-io/milvus/pkg/v3/util/lock" "github.com/milvus-io/milvus/pkg/v3/util/logutil" "github.com/milvus-io/milvus/pkg/v3/util/merr" @@ -221,7 +220,6 @@ func CreateServer(ctx context.Context, factory dependency.Factory, opts ...Optio for _, opt := range opts { opt(s) } - expr.Register("datacoord", s) return s } diff --git a/internal/datanode/data_node.go b/internal/datanode/data_node.go index 5153a8c465..5cdbc41b48 100644 --- a/internal/datanode/data_node.go +++ b/internal/datanode/data_node.go @@ -45,7 +45,6 @@ import ( "github.com/milvus-io/milvus/pkg/v3/metrics" "github.com/milvus-io/milvus/pkg/v3/mlog" "github.com/milvus-io/milvus/pkg/v3/util/conc" - "github.com/milvus-io/milvus/pkg/v3/util/expr" "github.com/milvus-io/milvus/pkg/v3/util/lifetime" "github.com/milvus-io/milvus/pkg/v3/util/merr" "github.com/milvus-io/milvus/pkg/v3/util/metricsinfo" @@ -129,7 +128,6 @@ func NewDataNode(ctx context.Context) *DataNode { node.taskManager = index.NewTaskManager(ctx2) node.externalCollectionManager = external.NewExternalCollectionManager(ctx2, 8) node.UpdateStateCode(commonpb.StateCode_Abnormal) - expr.Register("datanode", node) return node } diff --git a/internal/http/rbac.go b/internal/http/rbac.go index b3bf7d15a4..1ac3ef5bdb 100644 --- a/internal/http/rbac.go +++ b/internal/http/rbac.go @@ -20,7 +20,6 @@ import ( "context" "fmt" "net/http" - "strings" "github.com/cockroachdb/errors" @@ -32,11 +31,6 @@ import ( "github.com/milvus-io/milvus/pkg/v3/util/paramtable" ) -const ( - ExprAuthModeRootOnly = "rootOnly" - ExprAuthModeRBAC = "rbac" -) - // getUserRoleFunc is a callback function to get user roles. // This is set by the proxy package to avoid circular dependency. var getUserRoleFunc func(username string) ([]string, error) @@ -80,49 +74,6 @@ func parseHTTPAuth(req *http.Request) (username, password string, ok bool) { return req.BasicAuth() } -// CheckExprAuth authenticates and authorizes access to the /expr endpoint. -func CheckExprAuth(ctx context.Context, req *http.Request) error { - mode := paramtable.Get().CommonCfg.ExprAuthMode.GetValue() - switch strings.ToLower(mode) { - case "", strings.ToLower(ExprAuthModeRootOnly): - return checkExprRootAuth(ctx, req) - case ExprAuthModeRBAC: - if !paramtable.Get().CommonCfg.AuthorizationEnabled.GetAsBool() { - return &ErrPermissionDenied{msg: "authorization must be enabled when common.security.exprAuthMode is rbac"} - } - return CheckPrivilege( - ctx, - req, - commonpb.ObjectType_Global, - util.PrivilegeExpr, - util.AnyWord, - util.DefaultDBName, - ) - default: - return &ErrPermissionDenied{msg: fmt.Sprintf("invalid expr auth mode %q", mode)} - } -} - -func checkExprRootAuth(ctx context.Context, req *http.Request) error { - username, password, ok := parseHTTPAuth(req) - if !ok || username == "" || password == "" { - return &ErrAuthentication{msg: "authentication required. Use HTTP Basic Auth with root credentials"} - } - if username != util.UserRoot { - mlog.Warn(ctx, "non-root user attempted to access /expr", mlog.String("username", username)) - return &ErrPermissionDenied{msg: "only root user can access /expr endpoint"} - } - if passwordVerifyFunc == nil { - return &ErrServiceUnavailable{msg: "password verification not available"} - } - if !passwordVerifyFunc(ctx, username, password) { - mlog.Warn(ctx, "invalid root password for /expr access") - return &ErrAuthentication{msg: "invalid root password"} - } - mlog.Info(ctx, "root user authenticated for /expr access") - return nil -} - // CheckPrivilege checks if the authenticated user has the specified privilege. func CheckPrivilege(ctx context.Context, req *http.Request, objectType commonpb.ObjectType, objectPrivilege string, objectName string, dbName string, diff --git a/internal/http/rbac_test.go b/internal/http/rbac_test.go index 41a43c0175..eb82a7bb88 100644 --- a/internal/http/rbac_test.go +++ b/internal/http/rbac_test.go @@ -107,48 +107,6 @@ func TestEnforceErrorMapsToInternalServerError(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, HTTPStatusFromPrivilegeError(err)) } -func TestCheckExprAuth(t *testing.T) { - paramtable.Init() - originalPasswordVerify := passwordVerifyFunc - originalGetUserRole := getUserRoleFunc - defer func() { - passwordVerifyFunc = originalPasswordVerify - getUserRoleFunc = originalGetUserRole - paramtable.Get().Reset(paramtable.Get().CommonCfg.AuthorizationEnabled.Key) - paramtable.Get().Reset(paramtable.Get().CommonCfg.RootShouldBindRole.Key) - paramtable.Get().Reset(paramtable.Get().CommonCfg.ExprAuthMode.Key) - }() - - t.Run("root_only_rejects_non_root_with_valid_credentials", func(t *testing.T) { - paramtable.Get().Save(paramtable.Get().CommonCfg.ExprAuthMode.Key, ExprAuthModeRootOnly) - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return username == "admin" && password == "admin123" - }) - req := httptest.NewRequest(http.MethodGet, "/expr", nil) - req.SetBasicAuth("admin", "admin123") - - err := CheckExprAuth(context.Background(), req) - - assert.Error(t, err) - assert.True(t, IsPermissionDeniedError(err)) - assert.Equal(t, http.StatusForbidden, HTTPStatusFromPrivilegeError(err)) - }) - - t.Run("rbac_fails_closed_when_authorization_disabled", func(t *testing.T) { - paramtable.Get().Save(paramtable.Get().CommonCfg.ExprAuthMode.Key, ExprAuthModeRBAC) - paramtable.Get().Save(paramtable.Get().CommonCfg.AuthorizationEnabled.Key, "false") - req := httptest.NewRequest(http.MethodGet, "/expr", nil) - req.SetBasicAuth(util.UserRoot, "Milvus") - - err := CheckExprAuth(context.Background(), req) - - assert.Error(t, err) - assert.True(t, IsPermissionDeniedError(err)) - assert.Contains(t, err.Error(), "authorization must be enabled") - }) -} - -// CheckPrivilegeTestSuite tests the CheckPrivilege function type CheckPrivilegeTestSuite struct { suite.Suite ctx context.Context @@ -174,7 +132,6 @@ func (s *CheckPrivilegeTestSuite) TearDownTest() { // Reset paramtable settings paramtable.Get().Reset(paramtable.Get().CommonCfg.AuthorizationEnabled.Key) paramtable.Get().Reset(paramtable.Get().CommonCfg.RootShouldBindRole.Key) - paramtable.Get().Reset(paramtable.Get().CommonCfg.ExprAuthMode.Key) } func (s *CheckPrivilegeTestSuite) TestAuthorizationDisabledFailsClosed() { @@ -419,7 +376,6 @@ func (s *CheckPrivilegeWithEnforcerTestSuite) TearDownTest() { getUserRoleFunc = s.originalGetUserRole paramtable.Get().Reset(paramtable.Get().CommonCfg.AuthorizationEnabled.Key) paramtable.Get().Reset(paramtable.Get().CommonCfg.RootShouldBindRole.Key) - paramtable.Get().Reset(paramtable.Get().CommonCfg.ExprAuthMode.Key) privilege.CleanPrivilegeCache() } diff --git a/internal/http/router.go b/internal/http/router.go index 7eb06939d4..a348b1396c 100644 --- a/internal/http/router.go +++ b/internal/http/router.go @@ -28,12 +28,6 @@ const LogLevelRouterPath = "/log/level" // EventLogRouterPath is path for eventlog control. const EventLogRouterPath = "/eventlog" -// ExprPath is path for expression. -const ExprPath = "/expr" - -// StaticPath is path for the static view. -const StaticPath = "/static/" - const RootPath = "/" // Prometheus restful api path diff --git a/internal/http/server.go b/internal/http/server.go index 1c56d28d2b..6eb003e825 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -31,7 +31,6 @@ import ( "github.com/milvus-io/milvus/internal/http/healthz" "github.com/milvus-io/milvus/pkg/v3/eventlog" "github.com/milvus-io/milvus/pkg/v3/mlog" - "github.com/milvus-io/milvus/pkg/v3/util/expr" "github.com/milvus-io/milvus/pkg/v3/util/paramtable" ) @@ -100,50 +99,6 @@ func registerDefaults() { Path: EventLogRouterPath, Handler: eventlog.Handler(), }) - Register(&Handler{ - Path: ExprPath, - Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - // Check if expr endpoint is enabled - if !paramtable.Get().CommonCfg.ExprEnabled.GetAsBool() { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte(`{"msg": "expr endpoint is disabled. Set common.security.exprEnabled to true to enable it."}`)) - return - } - - code := req.URL.Query().Get("code") - var auth string - - // Only Proxy nodes can access /expr endpoint - if !expr.HasRegistered("proxy") || passwordVerifyFunc == nil { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte(`{"msg": "/expr endpoint is only available on Proxy nodes"}`)) - return - } - - if err := CheckExprAuth(req.Context(), req); err != nil { - writeJSONError(w, HTTPStatusFromPrivilegeError(err), err.Error()) - return - } - // Use bypass since we've already authenticated - auth = expr.AuthBypass - - output, err := expr.Exec(code, auth) - if err != nil { - writeJSONError(w, http.StatusInternalServerError, - fmt.Sprintf("failed to execute expression, %s", err.Error())) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - resp := make(map[string]string) - resp["output"] = output - json.NewEncoder(w).Encode(resp) - }), - }) - Register(&Handler{ - Path: StaticPath, - Handler: GetStaticHandler(), - }) if paramtable.Get().HTTPCfg.EnableWebUI.GetAsBool() { RegisterWebUIHandler() diff --git a/internal/http/server_test.go b/internal/http/server_test.go index 6a5f905627..4553d430bf 100644 --- a/internal/http/server_test.go +++ b/internal/http/server_test.go @@ -35,11 +35,7 @@ import ( "github.com/milvus-io/milvus-proto/go-api/v3/commonpb" "github.com/milvus-io/milvus/internal/http/healthz" "github.com/milvus-io/milvus/internal/json" - "github.com/milvus-io/milvus/internal/proxy/privilege" "github.com/milvus-io/milvus/pkg/v3/mlog" - "github.com/milvus-io/milvus/pkg/v3/util" - "github.com/milvus-io/milvus/pkg/v3/util/expr" - "github.com/milvus-io/milvus/pkg/v3/util/funcutil" "github.com/milvus-io/milvus/pkg/v3/util/paramtable" ) @@ -202,303 +198,6 @@ func (suite *HTTPServerTestSuite) TestPprofHandler() { } } -func (suite *HTTPServerTestSuite) TestExprHandler() { - expr.Init() - expr.Register("foo", "hello") - - suite.Run("disabled_by_default", func() { - // By default, exprEnabled is false, should return 403 - paramtable.Get().Save("common.security.exprEnabled", "false") - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo&auth=by-dev" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusForbidden, resp.StatusCode) - body, _ := io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "expr endpoint is disabled")) - }) - - suite.Run("disabled_on_non_proxy_nodes", func() { - // When enabled but not on Proxy node (no proxy registered, no passwordVerifyFunc), - // it should return 403 Forbidden - paramtable.Get().Save("common.security.exprEnabled", "true") - - // Should be forbidden on non-Proxy nodes - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo&auth=by-dev" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusForbidden, resp.StatusCode) - body, _ := io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "only available on Proxy nodes")) - }) - - suite.Run("root_only_requires_root_when_authorization_disabled", func() { - paramtable.Get().Save("common.security.exprEnabled", "true") - paramtable.Get().Save("common.security.exprAuthMode", ExprAuthModeRootOnly) - paramtable.Get().Save("common.security.authorizationEnabled", "false") - expr.Register("proxy", "mock_proxy") - - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return (username == "root" && password == "Milvus") || - (username == "admin" && password == "admin123") - }) - - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("admin", "admin123") - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusForbidden, resp.StatusCode) - body, _ := io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "only root user can access")) - - req, _ = http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("root", "Milvus") - resp, err = client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusOK, resp.StatusCode) - body, _ = io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "hello")) - }) - - suite.Run("rbac_mode_requires_authorization_enabled", func() { - paramtable.Get().Save("common.security.exprEnabled", "true") - paramtable.Get().Save("common.security.exprAuthMode", ExprAuthModeRBAC) - paramtable.Get().Save("common.security.authorizationEnabled", "false") - expr.Register("proxy", "mock_proxy") - - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return username == "root" && password == "Milvus" - }) - - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("root", "Milvus") - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusForbidden, resp.StatusCode) - body, _ := io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "authorization must be enabled")) - }) - - suite.Run("enabled_on_proxy_with_rbac_root_bypass", func() { - // When authorization is enabled but RootShouldBindRole is false, - // root user should be able to access via bypass - paramtable.Get().Save("common.security.exprEnabled", "true") - paramtable.Get().Save("common.security.exprAuthMode", ExprAuthModeRBAC) - paramtable.Get().Save("common.security.authorizationEnabled", "true") - paramtable.Get().Save("common.security.rootShouldBindRole", "false") - expr.Register("proxy", "mock_proxy") - - // Register mock functions - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return (username == "root" && password == "Milvus") || - (username == "admin" && password == "admin123") - }) - - // Without auth header - should fail with 401 - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusUnauthorized, resp.StatusCode) - body, _ := io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "authentication required")) - - // With non-root user (without Expr privilege) - should fail with 401 (invalid credentials) - url = "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - req, _ = http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("admin", "wrong_password") - resp, err = client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusUnauthorized, resp.StatusCode) - body, _ = io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "invalid credentials")) - - // With root user but wrong password - should fail with 401 - url = "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - req, _ = http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("root", "wrong_password") - resp, err = client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusUnauthorized, resp.StatusCode) - body, _ = io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "invalid credentials")) - - // With correct root credentials - should succeed via root bypass - url = "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - req, _ = http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("root", "Milvus") - resp, err = client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusOK, resp.StatusCode) - body, _ = io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "hello")) - }) - - suite.Run("invalid_auth_mode_returns_valid_json", func() { - paramtable.Get().Save("common.security.exprEnabled", "true") - paramtable.Get().Save("common.security.exprAuthMode", "rbca") - paramtable.Get().Save("common.security.authorizationEnabled", "true") - paramtable.Get().Save("common.security.rootShouldBindRole", "false") - expr.Register("proxy", "mock_proxy") - - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return (username == "root" && password == "Milvus") || - (username == "admin" && password == "admin123") - }) - - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("root", "Milvus") - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusForbidden, resp.StatusCode) - - body, _ := io.ReadAll(resp.Body) - var parsed map[string]string - err = json.Unmarshal(body, &parsed) - suite.NoError(err) - suite.Contains(parsed["msg"], "rbca") - }) - - suite.Run("exec_error_returns_valid_json", func() { - paramtable.Get().Save("common.security.exprEnabled", "true") - paramtable.Get().Save("common.security.exprAuthMode", ExprAuthModeRootOnly) - paramtable.Get().Save("common.security.authorizationEnabled", "false") - paramtable.Get().Save("common.security.rootShouldBindRole", "false") - expr.Register("proxy", "mock_proxy") - - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return (username == "root" && password == "Milvus") || - (username == "admin" && password == "admin123") - }) - - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=1%2B" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("root", "Milvus") - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusInternalServerError, resp.StatusCode) - - body, _ := io.ReadAll(resp.Body) - var parsed map[string]string - err = json.Unmarshal(body, &parsed) - suite.NoError(err) - suite.Contains(parsed["msg"], "failed to execute expression") - suite.Contains(parsed["msg"], "unexpected token EOF") - }) - - suite.Run("enabled_on_proxy_non_root_user_without_privilege", func() { - // Non-root user with valid credentials but without PrivilegeExpr privilege - // should get 403 Forbidden - paramtable.Get().Save("common.security.exprEnabled", "true") - paramtable.Get().Save("common.security.exprAuthMode", ExprAuthModeRBAC) - paramtable.Get().Save("common.security.authorizationEnabled", "true") - paramtable.Get().Save("common.security.rootShouldBindRole", "false") - expr.Register("proxy", "mock_proxy") - privilege.InitPrivilegeGroups() - - // Register mock password verify function - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return (username == "root" && password == "Milvus") || - (username == "admin" && password == "admin123") - }) - - // Register mock getUserRoleFunc - admin has role1 but no PrivilegeExpr - RegisterGetUserRoleFunc(func(username string) ([]string, error) { - if username == "admin" { - return []string{"role1"}, nil - } - return []string{}, nil - }) - - loadPrivilegePoliciesForTest(suite.T(), []string{ - funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "col1", commonpb.ObjectPrivilege_PrivilegeLoad.String(), "default"), - }) - defer privilege.CleanPrivilegeCache() - - // Non-root user with valid credentials but without PrivilegeExpr - should fail with 403 - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("admin", "admin123") - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusForbidden, resp.StatusCode) - body, _ := io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "permission denied")) - }) - - suite.Run("enabled_on_proxy_non_root_user_with_privilege", func() { - // Non-root user with valid credentials and PrivilegeExpr privilege - // should get 200 OK - paramtable.Get().Save("common.security.exprEnabled", "true") - paramtable.Get().Save("common.security.exprAuthMode", ExprAuthModeRBAC) - paramtable.Get().Save("common.security.authorizationEnabled", "true") - paramtable.Get().Save("common.security.rootShouldBindRole", "false") - expr.Register("proxy", "mock_proxy") - privilege.InitPrivilegeGroups() - - // Register mock password verify function - RegisterPasswordVerifyFunc(func(ctx context.Context, username, password string) bool { - return (username == "root" && password == "Milvus") || - (username == "admin" && password == "admin123") - }) - - // Register mock getUserRoleFunc - admin has role1 with PrivilegeExpr - RegisterGetUserRoleFunc(func(username string) ([]string, error) { - if username == "admin" { - return []string{"role1"}, nil - } - return []string{}, nil - }) - - loadPrivilegePoliciesForTest(suite.T(), []string{ - funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Global.String(), "*", util.PrivilegeExpr, "default"), - }) - defer privilege.CleanPrivilegeCache() - - // Non-root user with valid credentials and PrivilegeExpr - should succeed with 200 - url := "http://localhost:" + DefaultListenPort + ExprPath + "?code=foo" - client := http.Client{} - req, _ := http.NewRequest(http.MethodGet, url, nil) - req.SetBasicAuth("admin", "admin123") - resp, err := client.Do(req) - suite.Nil(err) - defer resp.Body.Close() - suite.Equal(http.StatusOK, resp.StatusCode) - body, _ := io.ReadAll(resp.Body) - suite.True(strings.Contains(string(body), "hello")) - }) - - // Reset config - paramtable.Get().Save("common.security.exprEnabled", "false") - paramtable.Get().Save("common.security.exprAuthMode", ExprAuthModeRootOnly) - paramtable.Get().Save("common.security.authorizationEnabled", "false") - paramtable.Get().Save("common.security.rootShouldBindRole", "false") -} - func TestHTTPServerSuite(t *testing.T) { suite.Run(t, new(HTTPServerTestSuite)) } diff --git a/internal/http/static/index.html b/internal/http/static/index.html deleted file mode 100644 index 49551383c9..0000000000 --- a/internal/http/static/index.html +++ /dev/null @@ -1,314 +0,0 @@ - - -
- - -Parameter meaning:
-The auth parameter is etcd root path, and the code parameter is expr execution expression.
-Injection object:
-Currently, the objects injected by expr include: param, proxy, rootcoord, querycoord, datacoord, quernode, datanode. You can use this tool to get the running value of the object.
-Usage example:
-1. Get a configuration: param.CommonCfg.GracefulTime.GetValue()
-2. Get a property value in the proxy object: proxy.address
-3. Determine whether a graph exists in the datanode: datanode.flowgraphManager.HasFlowgraph("aaa")
-Limitations:
-1. Variables cannot be modified.
-2. Methods with non-basic type parameters cannot be executed.
-3. Functions with multiple return values cannot be chained.