Replace casbin builtin keyMatch2 with custom match func

Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
He Weiwei 2019-01-29 01:26:38 +08:00
parent 8b5e68073d
commit 0ab7c93e16
3 changed files with 93 additions and 4 deletions

View File

@ -17,10 +17,13 @@ package rbac
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/casbin/casbin"
"github.com/casbin/casbin/model"
"github.com/casbin/casbin/persist"
"github.com/casbin/casbin/util"
)
var (
@ -50,6 +53,30 @@ e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && (r.act == p.act || p.act == '*')
`
// keyMatch2 determines whether key1 matches the pattern of key2, its behavior most likely the builtin KeyMatch2
// except that the match of ("/project/1/robot", "/project/1") will return false
func keyMatch2(key1 string, key2 string) bool {
key2 = strings.Replace(key2, "/*", "/.*", -1)
re := regexp.MustCompile(`(.*):[^/]+(.*)`)
for {
if !strings.Contains(key2, "/:") {
break
}
key2 = re.ReplaceAllString(key2, "$1[^/]+$2")
}
return util.RegexMatch(key1, "^"+key2+"$")
}
func keyMatch2Func(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return bool(keyMatch2(name1, name2)), nil
}
type userAdapter struct {
User
}
@ -134,5 +161,8 @@ func (a *userAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex
func enforcerForUser(user User) *casbin.Enforcer {
m := model.Model{}
m.LoadModelFromText(modelText)
return casbin.NewEnforcer(m, &userAdapter{User: user})
e := casbin.NewEnforcer(m, &userAdapter{User: user})
e.AddFunction("keyMatch2", keyMatch2Func)
return e
}

View File

@ -0,0 +1,59 @@
// Copyright Project Harbor Authors
//
// 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 rbac
import (
"testing"
)
func Test_keyMatch2(t *testing.T) {
type args struct {
key1 string
key2 string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "match /project/1/robot, /project/1",
args: args{"/project/1/robot", "/project/1"},
want: false,
},
{
name: "match /project/1/robot, /project/:pid",
args: args{"/project/1/robot", "/project/:pid"},
want: false,
},
{
name: "match /project/1/robot, /project/1/*",
args: args{"/project/1/robot", "/project/1/*"},
want: true,
},
{
name: "match /project/1/robot, /project/:pid/robot",
args: args{"/project/1/robot", "/project/:pid/robot"},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := keyMatch2(tt.args.key1, tt.args.key2); got != tt.want {
t.Errorf("keyMatch2() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -127,7 +127,7 @@ func TestHasPermissionUserWithoutRoles(t *testing.T) {
{
name: "project create for user without roles",
args: args{
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "project", Action: "create"}}},
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project", Action: "create"}}},
"/project",
"create",
},
@ -136,7 +136,7 @@ func TestHasPermissionUserWithoutRoles(t *testing.T) {
{
name: "project delete test for user without roles",
args: args{
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "project", Action: "create"}}},
&userWithoutRoles{Username: "user1", UserPolicies: []*Policy{{Resource: "/project", Action: "create"}}},
"/project",
"delete",
},
@ -168,7 +168,7 @@ func TestHasPermissionUsernameEmpty(t *testing.T) {
{
name: "project create for user without roles",
args: args{
&userWithoutRoles{Username: "", UserPolicies: []*Policy{{Resource: "project", Action: "create"}}},
&userWithoutRoles{Username: "", UserPolicies: []*Policy{{Resource: "/project", Action: "create"}}},
"/project",
"create",
},