Repository: casbin/casbin (2025-08-04T13:40:09Z)

Issue:
[feature] support BLP model and add its test case
What is BLP: https://en.wikipedia.org/wiki/Bell%E2%80%93LaPadula_model

How to resolve this issue? Similar to the ReBAC model: https://github.com/casbin/casbin/pull/1501


Discussion:

Patch:
diff --git a/examples/blp_model.conf b/examples/blp_model.conf
new file mode 100644
index 000000000..bc919c8fc
--- /dev/null
+++ b/examples/blp_model.conf
@@ -0,0 +1,14 @@
+[request_definition]
+r = sub, sub_level, obj, obj_level, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = levelMatch(r.sub_level, r.obj_level, r.act) 
\ No newline at end of file
diff --git a/model/function.go b/model/function.go
index f1a8d0075..7c4268f7e 100644
--- a/model/function.go
+++ b/model/function.go
@@ -49,6 +49,7 @@ func LoadFunctionMap() FunctionMap {
 	fm.AddFunction("regexMatch", util.RegexMatchFunc)
 	fm.AddFunction("ipMatch", util.IPMatchFunc)
 	fm.AddFunction("globMatch", util.GlobMatchFunc)
+	fm.AddFunction("levelMatch", util.LevelMatchFunc)
 
 	return *fm
 }
diff --git a/util/builtin_operators_blp.go b/util/builtin_operators_blp.go
new file mode 100644
index 000000000..95588eb45
--- /dev/null
+++ b/util/builtin_operators_blp.go
@@ -0,0 +1,60 @@
+// Copyright 2025 The casbin Authors. All Rights Reserved.
+//
+// Licensed 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 util
+
+import (
+	"fmt"
+)
+
+// levelMatch enforces Bell-LaPadula security model.
+// Simple Security Property: no read-up.
+// Star Property: no write-down.
+func levelMatch(subjectLevel, objectLevel, action interface{}) bool {
+	subLevel := int(subjectLevel.(float64))
+	objLevel := int(objectLevel.(float64))
+	act := action.(string)
+
+	if act == "read" {
+		return subLevel >= objLevel
+	}
+	if act == "write" {
+		return subLevel <= objLevel
+	}
+	return false
+}
+
+// LevelMatchFunc is the wrapper for levelMatch.
+func LevelMatchFunc(args ...interface{}) (interface{}, error) {
+	if len(args) != 3 {
+		return false, fmt.Errorf("levelMatch: expected 3 arguments (subjectLevel, objectLevel, action), but got %d", len(args))
+	}
+
+	subjectLevel, ok := args[0].(float64)
+	if !ok {
+		return false, fmt.Errorf("levelMatch: subjectLevel argument must be a number")
+	}
+
+	objectLevel, ok := args[1].(float64)
+	if !ok {
+		return false, fmt.Errorf("levelMatch: objectLevel argument must be a number")
+	}
+
+	action, ok := args[2].(string)
+	if !ok {
+		return false, fmt.Errorf("levelMatch: action argument must be a string")
+	}
+
+	return levelMatch(subjectLevel, objectLevel, action), nil
+}


Test patch:
diff --git a/blp_test.go b/blp_test.go
new file mode 100644
index 000000000..7211505f5
--- /dev/null
+++ b/blp_test.go
@@ -0,0 +1,50 @@
+// Copyright 2025 The casbin Authors. All Rights Reserved.
+//
+// Licensed 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 casbin
+
+import (
+	"testing"
+)
+
+func testEnforceBLP(t *testing.T, e *Enforcer, sub string, subLevel float64, obj string, objLevel float64, act string, res bool) {
+	t.Helper()
+	if myRes, err := e.Enforce(sub, subLevel, obj, objLevel, act); err != nil {
+		t.Errorf("Enforce Error: %s", err)
+	} else if myRes != res {
+		t.Errorf("%s, %v, %s, %v, %s: %t, supposed to be %t", sub, subLevel, obj, objLevel, act, myRes, res)
+	}
+}
+
+func TestBLPModel(t *testing.T) {
+	e, _ := NewEnforcer("examples/blp_model.conf")
+
+	// Read operations: subject level >= object level
+	testEnforceBLP(t, e, "alice", 3, "data1", 1, "read", true)
+	testEnforceBLP(t, e, "bob", 2, "data2", 2, "read", true)
+	testEnforceBLP(t, e, "charlie", 1, "data1", 1, "read", true)
+
+	// Read violations: subject level < object level
+	testEnforceBLP(t, e, "bob", 2, "data3", 3, "read", false)
+	testEnforceBLP(t, e, "charlie", 1, "data2", 2, "read", false)
+
+	// Write operations: subject level <= object level
+	testEnforceBLP(t, e, "alice", 3, "data3", 3, "write", true)
+	testEnforceBLP(t, e, "bob", 2, "data3", 3, "write", true)
+	testEnforceBLP(t, e, "charlie", 1, "data2", 2, "write", true)
+
+	// Write violations: subject level > object level
+	testEnforceBLP(t, e, "alice", 3, "data1", 1, "write", false)
+	testEnforceBLP(t, e, "bob", 2, "data1", 1, "write", false)
+}