181 lines
5.9 KiB
Go
181 lines
5.9 KiB
Go
// 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 casbin
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/casbin/casbin/v3/model"
|
|
fileadapter "github.com/casbin/casbin/v3/persist/file-adapter"
|
|
)
|
|
|
|
// TestBackslashHandlingConsistency tests that backslashes in string literals
|
|
// within matcher expressions are handled consistently with CSV-parsed values.
|
|
// This addresses the issue where govaluate interprets escape sequences in
|
|
// string literals, but CSV parsing treats backslashes as literal characters.
|
|
func TestBackslashHandlingConsistency(t *testing.T) {
|
|
// Test case 1: Literal string in matcher should match CSV-parsed request
|
|
t.Run("LiteralInMatcher", func(t *testing.T) {
|
|
m := model.NewModel()
|
|
m.AddDef("r", "r", "sub, obj, act")
|
|
m.AddDef("p", "p", "sub, obj, act")
|
|
m.AddDef("e", "e", "some(where (p.eft == allow))")
|
|
// User writes '\1\2' in matcher - should be treated as literal backslashes
|
|
m.AddDef("m", "m", "regexMatch('\\1\\2', p.obj)")
|
|
|
|
e, err := NewEnforcer(m, fileadapter.NewAdapter("examples/basic_policy.csv"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add a policy with a regex pattern containing backslashes
|
|
// CSV format: "\\[0-9]+\\" means literal string with 4 backslashes
|
|
_, err = e.AddPolicy("filename", "\\\\[0-9]+\\\\", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// This should match because '\1\2' after escaping becomes \1\2,
|
|
// and the pattern \\[0-9]+\\ matches strings like \1\ (which is a substring of \1\2)
|
|
result, err := e.Enforce("filename", "dummy", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !result {
|
|
t.Errorf("Expected true, got false - literal '\\1\\2' should match after escape processing")
|
|
}
|
|
})
|
|
|
|
// Test case 2: Request parameter should match policy with same backslash content
|
|
t.Run("RequestParameterVsPolicy", func(t *testing.T) {
|
|
m := model.NewModel()
|
|
m.AddDef("r", "r", "sub, obj, act")
|
|
m.AddDef("p", "p", "sub, obj, act")
|
|
m.AddDef("e", "e", "some(where (p.eft == allow))")
|
|
m.AddDef("m", "m", "regexMatch(r.obj, p.obj)")
|
|
|
|
e, err := NewEnforcer(m, fileadapter.NewAdapter("examples/basic_policy.csv"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add policy with regex pattern
|
|
_, err = e.AddPolicy("filename", "\\\\[0-9]+\\\\", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Request with backslashes - simulating CSV input "\1\2" which becomes \1\2
|
|
result, err := e.Enforce("filename", "\\1\\2", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !result {
|
|
t.Errorf("Expected true, got false - request \\1\\2 should match regex pattern")
|
|
}
|
|
})
|
|
|
|
// Test case 3: Both approaches should give the same result
|
|
t.Run("ConsistencyBetweenLiteralAndParameter", func(t *testing.T) {
|
|
// Create two enforcers with different matchers
|
|
m1 := model.NewModel()
|
|
m1.AddDef("r", "r", "sub, obj, act")
|
|
m1.AddDef("p", "p", "sub, obj, act")
|
|
m1.AddDef("e", "e", "some(where (p.eft == allow))")
|
|
m1.AddDef("m", "m", "regexMatch('\\1\\2', p.obj)")
|
|
|
|
m2 := model.NewModel()
|
|
m2.AddDef("r", "r", "sub, obj, act")
|
|
m2.AddDef("p", "p", "sub, obj, act")
|
|
m2.AddDef("e", "e", "some(where (p.eft == allow))")
|
|
m2.AddDef("m", "m", "regexMatch(r.obj, p.obj)")
|
|
|
|
e1, err := NewEnforcer(m1, fileadapter.NewAdapter("examples/basic_policy.csv"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
e2, err := NewEnforcer(m2, fileadapter.NewAdapter("examples/basic_policy.csv"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add same policy to both
|
|
pattern := "\\\\[0-9]+\\\\"
|
|
_, err = e1.AddPolicy("filename", pattern, "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = e2.AddPolicy("filename", pattern, "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Test with the same request
|
|
result1, err := e1.Enforce("filename", "dummy", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
result2, err := e2.Enforce("filename", "\\1\\2", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if result1 != result2 {
|
|
t.Errorf("Inconsistent results: literal in matcher gave %v, parameter gave %v", result1, result2)
|
|
}
|
|
})
|
|
|
|
// Test case 4: Simple equality check with backslashes
|
|
t.Run("SimpleEqualityWithBackslashes", func(t *testing.T) {
|
|
m := model.NewModel()
|
|
m.AddDef("r", "r", "sub, obj, act")
|
|
m.AddDef("p", "p", "sub, obj, act")
|
|
m.AddDef("e", "e", "some(where (p.eft == allow))")
|
|
// In Go source, '\test' (one backslash in the actual string) represents
|
|
// what would be typed in a web form. After escape processing, it will match
|
|
// the CSV-parsed value "\test" (one backslash).
|
|
// Note: In Go source, we write "r.obj == '\\test'" which is a Go string
|
|
// containing the text: r.obj == '\test' (with ONE backslash in the string content)
|
|
m.AddDef("m", "m", "r.obj == '\\test' && p.sub == r.sub")
|
|
|
|
e, err := NewEnforcer(m, fileadapter.NewAdapter("examples/basic_policy.csv"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = e.AddPolicy("alice", "any", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Request with literal backslash from CSV would be "\test"
|
|
// In Go source, we write "\\test" which represents the string \test (one backslash)
|
|
result, err := e.Enforce("alice", "\\test", "read")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !result {
|
|
t.Errorf("Expected true - literal '\\test' should equal request parameter \\test")
|
|
}
|
|
})
|
|
}
|