Skip to content

Commit

Permalink
Add ABAC support
Browse files Browse the repository at this point in the history
  • Loading branch information
nodece committed Nov 6, 2018
1 parent 0986052 commit b46bc27
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 45 deletions.
11 changes: 11 additions & 0 deletions examples/abac_model.conf
@@ -0,0 +1,11 @@
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == r.obj.Owner
96 changes: 96 additions & 0 deletions server/abac.go
@@ -0,0 +1,96 @@
// Copyright 2018 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 server

import (
"encoding/json"
"fmt"
"strconv"
"unicode"
)

type ABACModel struct {
V0 string
V1 string
V2 string
V3 string
V4 string
V5 string
V6 string
V7 string
V8 string
V9 string
V10 string
source map[string]string
}

func toUpperFirstChar(str string) string {
for i, v := range str {
return string(unicode.ToUpper(v)) + str[i+1:]
}
return ""
}

func MakeABAC(obj interface{}) (string, error) {
data, err := json.Marshal(&obj)
if err != nil {
return "", err
}
return "ABAC::" + string(data), nil
}

func resolveABAC(obj string) (ABACModel, error) {
var jsonMap map[string]interface{}
model := ABACModel{source: map[string]string{}}

err := json.Unmarshal([]byte(obj[len("ABAC::"):]), &jsonMap)
if err != nil {
return model, err
}

i := 0
for k, v := range jsonMap {
key := toUpperFirstChar(k)
value := fmt.Sprintf("%v", v)
model.source[key] = "V" + strconv.Itoa(i)
switch i {
case 0:
model.V0 = value
case 1:
model.V1 = value
case 2:
model.V2 = value
case 3:
model.V3 = value
case 4:
model.V4 = value
case 5:
model.V5 = value
case 6:
model.V6 = value
case 7:
model.V7 = value
case 8:
model.V8 = value
case 9:
model.V9 = value
case 10:
model.V10 = value
}
i++
}

return model, nil
}
2 changes: 1 addition & 1 deletion server/adapter.go
Expand Up @@ -16,14 +16,14 @@ package server

import (
"errors"

pb "github.com/casbin/casbin-server/proto"
"github.com/casbin/casbin/persist"
"github.com/casbin/casbin/persist/file-adapter"
"github.com/casbin/gorm-adapter"
_ "github.com/jinzhu/gorm/dialects/mssql"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)

var errDriverName = errors.New("invalid DriverName")
Expand Down
39 changes: 33 additions & 6 deletions server/enforcer.go
Expand Up @@ -16,6 +16,7 @@ package server

import (
"errors"
"strings"

"github.com/casbin/casbin"
pb "github.com/casbin/casbin-server/proto"
Expand Down Expand Up @@ -67,12 +68,22 @@ func (s *Server) addAdapter(a persist.Adapter) int {
}

func (s *Server) NewEnforcer(ctx context.Context, in *pb.NewEnforcerRequest) (*pb.NewEnforcerReply, error) {
a, err := s.getAdapter(int(in.AdapterHandle))
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
var a persist.Adapter
var e *casbin.Enforcer

if in.AdapterHandle != -1 {
var err error
a, err = s.getAdapter(int(in.AdapterHandle))
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
}
}

e := casbin.NewEnforcer(casbin.NewModel(in.ModelText), a)
if a == nil {
e = casbin.NewEnforcer(casbin.NewModel(in.ModelText))
} else {
e = casbin.NewEnforcer(casbin.NewModel(in.ModelText), a)
}
h := s.addEnforcer(e)

return &pb.NewEnforcerReply{Handler: int32(h)}, nil
Expand All @@ -96,8 +107,24 @@ func (s *Server) Enforce(ctx context.Context, in *pb.EnforceRequest) (*pb.BoolRe
}

params := make([]interface{}, 0, len(in.Params))
for e := range in.Params {
params = append(params, in.Params[e])
for index := range in.Params {
param := in.Params[index]
if strings.HasPrefix(param, "ABAC::") == true {
model, err := resolveABAC(param)
if err != nil {
return &pb.BoolReply{Res: false}, err
}
m := e.GetModel()["m"]["m"]
for k, v := range model.source {
old := "." + k
if strings.Contains(m.Value, old) {
m.Value = strings.Replace(m.Value, old, "."+v, -1)
}
}
params = append(params, model)
continue
}
params = append(params, in.Params[index])
}

res := e.Enforce(params...)
Expand Down
36 changes: 0 additions & 36 deletions server/enforcer_test.go
Expand Up @@ -22,42 +22,6 @@ import (
pb "github.com/casbin/casbin-server/proto"
)

func TestRBACModel(t *testing.T) {
s := NewServer()
ctx := context.Background()

_, err := s.NewAdapter(ctx, &pb.NewAdapterRequest{DriverName: "file", ConnectString: "../examples/rbac_policy.csv"})
if err != nil {
t.Error(err)
}

modelText, err := ioutil.ReadFile("../examples/rbac_model.conf")
if err != nil {
t.Error(err)
}

resp, err := s.NewEnforcer(ctx, &pb.NewEnforcerRequest{ModelText: string(modelText), AdapterHandle: 0})
if err != nil {
t.Error(err)
}
e := resp.Handler

sub := "alice"
obj := "data1"
act := "read"
res := true

resp2, err := s.Enforce(ctx, &pb.EnforceRequest{EnforcerHandler: e, Params: []string{sub, obj, act}})
if err != nil {
t.Error(err)
}
myRes := resp2.Res

if myRes != res {
t.Errorf("%s, %s, %s: %t, supposed to be %t", sub, obj, act, myRes, res)
}
}

type testEngine struct {
s *Server
ctx context.Context
Expand Down
86 changes: 84 additions & 2 deletions server/model_test.go
Expand Up @@ -15,10 +15,12 @@
package server

import (
"io/ioutil"
"testing"

pb "github.com/casbin/casbin-server/proto"
"github.com/stretchr/testify/assert"

"testing"
"golang.org/x/net/context"
)

func testEnforce(t *testing.T, e *testEngine, sub string, obj string, act string, res bool) {
Expand All @@ -40,3 +42,83 @@ func testEnforceWithoutUsers(t *testing.T, e *testEngine, obj string, act string
t.Errorf("%s, %s: %t, supposed to be %t", obj, act, !res, res)
}
}

func TestRBACModel(t *testing.T) {
s := NewServer()
ctx := context.Background()

_, err := s.NewAdapter(ctx, &pb.NewAdapterRequest{DriverName: "file", ConnectString: "../examples/rbac_policy.csv"})
if err != nil {
t.Error(err)
}

modelText, err := ioutil.ReadFile("../examples/rbac_model.conf")
if err != nil {
t.Error(err)
}

resp, err := s.NewEnforcer(ctx, &pb.NewEnforcerRequest{ModelText: string(modelText), AdapterHandle: 0})
if err != nil {
t.Error(err)
}
e := resp.Handler

sub := "alice"
obj := "data1"
act := "read"
res := true

resp2, err := s.Enforce(ctx, &pb.EnforceRequest{EnforcerHandler: e, Params: []string{sub, obj, act}})
if err != nil {
t.Error(err)
}
myRes := resp2.Res

if myRes != res {
t.Errorf("%s, %s, %s: %t, supposed to be %t", sub, obj, act, myRes, res)
}
}

func TestABACModel(t *testing.T) {
s := NewServer()
ctx := context.Background()

modelText, err := ioutil.ReadFile("../examples/abac_model.conf")
if err != nil {
t.Error(err)
}

resp, err := s.NewEnforcer(ctx, &pb.NewEnforcerRequest{ModelText: string(modelText), AdapterHandle: -1})
if err != nil {
t.Error(err)
}
type ABACModel struct {
Name string
Owner string
}
e := resp.Handler

data1, _ := MakeABAC(ABACModel{Name:"data1",Owner:"alice"})
data2, _ := MakeABAC(ABACModel{Name:"data2",Owner:"bob"})

testModel(t, s, e, "alice", data1, "read", true)
testModel(t, s, e, "alice", data1, "write", true)
testModel(t, s, e, "alice", data2, "read", false)
testModel(t, s, e, "alice", data2, "write", false)
testModel(t, s, e, "bob", data1, "read", false)
testModel(t, s, e, "bob", data1, "write", false)
testModel(t, s, e, "bob", data2, "read", true)
testModel(t, s, e, "bob", data2, "write", true)

}

func testModel(t *testing.T, s *Server, enforcerHandler int32, sub string, obj string, act string, res bool) {
t.Helper()

reply, err := s.Enforce(nil, &pb.EnforceRequest{EnforcerHandler: enforcerHandler, Params: []string{sub, obj, act}})
assert.NoError(t, err)

if reply.Res != res {
t.Errorf("%s, %v, %s: %t, supposed to be %t", sub, obj, act, !res, res)
}
}

0 comments on commit b46bc27

Please sign in to comment.