Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions auth/casbin/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package casbin

import (
"context"
"errors"

stdcasbin "github.com/casbin/casbin"
"github.com/go-kit/kit/endpoint"
)

type contextKey string

const (
// CasbinModelContextKey holds the key to store the access control model
// in context, it can be a path to configuration file or a casbin/model
// Model.
CasbinModelContextKey contextKey = "CasbinModel"

// CasbinPolicyContextKey holds the key to store the access control policy
// in context, it can be a path to policy file or an implementation of
// casbin/persist Adapter interface.
CasbinPolicyContextKey contextKey = "CasbinPolicy"

// CasbinEnforcerContextKey holds the key to retrieve the active casbin
// Enforcer.
CasbinEnforcerContextKey contextKey = "CasbinEnforcer"
)

var (
// ErrModelContextMissing denotes a casbin model was not passed into
// the parsing of middleware's context.
ErrModelContextMissing = errors.New("CasbinModel is required in context")

// ErrPolicyContextMissing denotes a casbin policy was not passed into
// the parsing of middleware's context.
ErrPolicyContextMissing = errors.New("CasbinPolicy is required in context")

// ErrUnauthorized denotes the subject is not authorized to do the action
// intended on the given object, based on the context model and policy.
ErrUnauthorized = errors.New("Unauthorized Access")
)

// NewEnforcer checks whether the subject is authorized to do the specified
// action on the given object. If a valid access control model and policy
// is given, then the generated casbin Enforcer is stored in the context
// with CasbinEnforcer as the key.
func NewEnforcer(
subject string, object interface{}, action string,
) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (
response interface{}, err error,
) {
casbinModel := ctx.Value(CasbinModelContextKey)
casbinPolicy := ctx.Value(CasbinPolicyContextKey)

enforcer := stdcasbin.NewEnforcer(casbinModel, casbinPolicy)
ctx = context.WithValue(ctx, CasbinEnforcerContextKey, enforcer)
if !enforcer.Enforce(subject, object, action) {
return nil, ErrUnauthorized
}
return next(ctx, request)
}
}
}
55 changes: 55 additions & 0 deletions auth/casbin/middleware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package casbin

import (
"context"
"testing"

stdcasbin "github.com/casbin/casbin"
fileadapter "github.com/casbin/casbin/persist/file-adapter"
)

func TestStructBaseContext(t *testing.T) {
e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }

m := stdcasbin.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", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)")

a := fileadapter.NewAdapter("testdata/keymatch_policy.csv")

ctx := context.WithValue(context.Background(), CasbinModelContextKey, m)
ctx = context.WithValue(ctx, CasbinPolicyContextKey, a)

// positive case
middleware := NewEnforcer("alice", "/alice_data/resource1", "GET")(e)
ctx1, err := middleware(ctx, struct{}{})
if err != nil {
t.Fatalf("Enforcer returned error: %s", err)
}
_, ok := ctx1.(context.Context).Value(CasbinEnforcerContextKey).(*stdcasbin.Enforcer)
if !ok {
t.Fatalf("context should contains the active enforcer")
}

// negative case
middleware = NewEnforcer("alice", "/alice_data/resource2", "POST")(e)
_, err = middleware(ctx, struct{}{})
if err == nil {
t.Fatalf("Enforcer should return error")
}
}

func TestFileBaseContext(t *testing.T) {
e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }
ctx := context.WithValue(context.Background(), CasbinModelContextKey, "testdata/basic_model.conf")
ctx = context.WithValue(ctx, CasbinPolicyContextKey, "testdata/basic_policy.csv")

// positive case
middleware := NewEnforcer("alice", "data1", "read")(e)
_, err := middleware(ctx, struct{}{})
if err != nil {
t.Fatalf("Enforcer returned error: %s", err)
}
}
11 changes: 11 additions & 0 deletions auth/casbin/testdata/basic_model.conf
Original file line number Diff line number Diff line change
@@ -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 == p.sub && r.obj == p.obj && r.act == p.act
2 changes: 2 additions & 0 deletions auth/casbin/testdata/basic_policy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
p, alice, data1, read
p, bob, data2, write
7 changes: 7 additions & 0 deletions auth/casbin/testdata/keymatch_policy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
p, alice, /alice_data/*, GET
p, alice, /alice_data/resource1, POST

p, bob, /alice_data/resource2, GET
p, bob, /bob_data/*, POST

p, cathy, /cathy_data, (GET)|(POST)