Inital commit for authz server

This commit is contained in:
shrey-rafay
2022-01-20 13:38:43 +05:30
parent 4fc8e39687
commit 4e3653cc65
13 changed files with 5317 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
package server
import (
"encoding/json"
"fmt"
"strconv"
"unicode"
)
type AbacAttrList struct {
V0 string
V1 string
V2 string
V3 string
V4 string
V5 string
V6 string
V7 string
V8 string
V9 string
V10 string
nameMap 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) (AbacAttrList, error) {
var jsonMap map[string]interface{}
attrList := AbacAttrList{nameMap: map[string]string{}}
err := json.Unmarshal([]byte(obj[len("ABAC::"):]), &jsonMap)
if err != nil {
return attrList, err
}
i := 0
for k, v := range jsonMap {
key := toUpperFirstChar(k)
value := fmt.Sprintf("%v", v)
attrList.nameMap[key] = "V" + strconv.Itoa(i)
switch i {
case 0:
attrList.V0 = value
case 1:
attrList.V1 = value
case 2:
attrList.V2 = value
case 3:
attrList.V3 = value
case 4:
attrList.V4 = value
case 5:
attrList.V5 = value
case 6:
attrList.V6 = value
case 7:
attrList.V7 = value
case 8:
attrList.V8 = value
case 9:
attrList.V9 = value
case 10:
attrList.V10 = value
}
i++
}
return attrList, nil
}
func (attr AbacAttrList) GetCacheKey() string {
res, _ := MakeABAC(&attr)
return res
}

View File

@@ -0,0 +1,99 @@
package server
import (
"encoding/json"
"errors"
"fmt"
"os"
"regexp"
"strings"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
"github.com/casbin/casbin/v2/persist"
fileadapter "github.com/casbin/casbin/v2/persist/file-adapter"
gormadapter "github.com/casbin/gorm-adapter/v2"
//_ "github.com/jinzhu/gorm/dialects/mssql"
//_ "github.com/jinzhu/gorm/dialects/mysql"
//_ "github.com/jinzhu/gorm/dialects/postgres"
)
var errDriverName = errors.New("currently supported DriverName: file | mysql | postgres | mssql")
func newAdapter(in *pb.NewAdapterRequest) (persist.Adapter, error) {
var a persist.Adapter
in = checkLocalConfig(in)
supportDriverNames := [...]string{"file", "mysql", "postgres", "mssql"}
switch in.DriverName {
case "file":
a = fileadapter.NewAdapter(in.ConnectString)
default:
var support = false
for _, driverName := range supportDriverNames {
if driverName == in.DriverName {
support = true
break
}
}
if !support {
return nil, errDriverName
}
var err error
a, err = gormadapter.NewAdapter(in.DriverName, in.ConnectString, in.DbSpecified)
if err != nil {
return nil, err
}
}
return a, nil
}
func checkLocalConfig(in *pb.NewAdapterRequest) *pb.NewAdapterRequest {
cfg := LoadConfiguration(getLocalConfigPath())
if in.ConnectString == "" || in.DriverName == "" {
in.DriverName = cfg.Driver
in.ConnectString = cfg.Connection
in.DbSpecified = cfg.DBSpecified
}
return in
}
const (
configFileDefaultPath = "config/connection_config.json"
configFilePathEnvironmentVariable = "CONNECTION_CONFIG_PATH"
)
func getLocalConfigPath() string {
configFilePath := os.Getenv(configFilePathEnvironmentVariable)
if configFilePath == "" {
configFilePath = configFileDefaultPath
}
return configFilePath
}
func LoadConfiguration(file string) Config {
//Loads a default config from adapter_config in case a custom adapter isn't provided by the client.
//DriverName, ConnectionString, and dbSpecified can be configured in the file. Defaults to 'file' mode.
configFile, err := os.Open(file)
if err != nil {
fmt.Println(err.Error())
}
decoder := json.NewDecoder(configFile)
config := Config{}
decoder.Decode(&config)
re := regexp.MustCompile(`\$\b((\w*))\b`)
config.Connection = re.ReplaceAllStringFunc(config.Connection, func(s string) string {
return os.Getenv(strings.TrimPrefix(s, `$`))
})
return config
}
type Config struct {
Driver string
Connection string
Enforcer string
DBSpecified bool
}

View File

@@ -0,0 +1,15 @@
package server
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetLocalConfig(t *testing.T) {
assert.Equal(t, configFileDefaultPath, getLocalConfigPath(), "read from default connection config path if environment variable is not set")
os.Setenv(configFilePathEnvironmentVariable, "dir/custom_path.json")
assert.Equal(t, "dir/custom_path.json", getLocalConfigPath())
}

View File

@@ -0,0 +1,181 @@
package server
import (
"context"
"errors"
"io/ioutil"
"strings"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
"github.com/casbin/casbin/v2/persist"
)
// Server is used to implement proto.CasbinServer.
type Server struct {
enforcerMap map[int]*casbin.Enforcer
adapterMap map[int]persist.Adapter
}
func NewServer() *Server {
s := Server{}
s.enforcerMap = map[int]*casbin.Enforcer{}
s.adapterMap = map[int]persist.Adapter{}
return &s
}
func (s *Server) getEnforcer(handle int) (*casbin.Enforcer, error) {
if _, ok := s.enforcerMap[handle]; ok {
return s.enforcerMap[handle], nil
} else {
return nil, errors.New("enforcer not found")
}
}
func (s *Server) getAdapter(handle int) (persist.Adapter, error) {
if _, ok := s.adapterMap[handle]; ok {
return s.adapterMap[handle], nil
} else {
return nil, errors.New("adapter not found")
}
}
func (s *Server) addEnforcer(e *casbin.Enforcer) int {
cnt := len(s.enforcerMap)
s.enforcerMap[cnt] = e
return cnt
}
func (s *Server) addAdapter(a persist.Adapter) int {
cnt := len(s.adapterMap)
s.adapterMap[cnt] = a
return cnt
}
func (s *Server) NewEnforcer(ctx context.Context, in *pb.NewEnforcerRequest) (*pb.NewEnforcerReply, error) {
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
}
}
if in.ModelText == "" {
cfg := LoadConfiguration(getLocalConfigPath())
data, err := ioutil.ReadFile(cfg.Enforcer)
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
}
in.ModelText = string(data)
}
if a == nil {
m, err := model.NewModelFromString(in.ModelText)
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
}
a, err = newAdapter(&pb.NewAdapterRequest{})
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
}
e, err = casbin.NewEnforcer(m, a)
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
}
} else {
m, err := model.NewModelFromString(in.ModelText)
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
}
e, err = casbin.NewEnforcer(m, a)
if err != nil {
return &pb.NewEnforcerReply{Handler: 0}, err
}
}
h := s.addEnforcer(e)
return &pb.NewEnforcerReply{Handler: int32(h)}, nil
}
func (s *Server) NewAdapter(ctx context.Context, in *pb.NewAdapterRequest) (*pb.NewAdapterReply, error) {
a, err := newAdapter(in)
if err != nil {
return nil, err
}
h := s.addAdapter(a)
return &pb.NewAdapterReply{Handler: int32(h)}, nil
}
func (s *Server) parseParam(param, matcher string) (interface{}, string) {
if strings.HasPrefix(param, "ABAC::") {
attrList, err := resolveABAC(param)
if err != nil {
panic(err)
}
for k, v := range attrList.nameMap {
old := "." + k
if strings.Contains(matcher, old) {
matcher = strings.Replace(matcher, old, "."+v, -1)
}
}
return attrList, matcher
} else {
return param, matcher
}
}
func (s *Server) Enforce(ctx context.Context, in *pb.EnforceRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{Res: false}, err
}
var param interface{}
params := make([]interface{}, 0, len(in.Params))
m := e.GetModel()["m"]["m"].Value
for index := range in.Params {
param, m = s.parseParam(in.Params[index], m)
params = append(params, param)
}
res, err := e.EnforceWithMatcher(m, params...)
if err != nil {
return &pb.BoolReply{Res: false}, err
}
return &pb.BoolReply{Res: res}, nil
}
func (s *Server) LoadPolicy(ctx context.Context, in *pb.EmptyRequest) (*pb.EmptyReply, error) {
e, err := s.getEnforcer(int(in.Handler))
if err != nil {
return &pb.EmptyReply{}, err
}
err = e.LoadPolicy()
return &pb.EmptyReply{}, err
}
func (s *Server) SavePolicy(ctx context.Context, in *pb.EmptyRequest) (*pb.EmptyReply, error) {
e, err := s.getEnforcer(int(in.Handler))
if err != nil {
return &pb.EmptyReply{}, err
}
err = e.SavePolicy()
return &pb.EmptyReply{}, err
}

View File

@@ -0,0 +1,278 @@
package server
import (
"context"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
)
func (s *Server) wrapPlainPolicy(policy [][]string) *pb.Array2DReply {
if len(policy) == 0 {
return &pb.Array2DReply{}
}
policyReply := &pb.Array2DReply{}
policyReply.D2 = make([]*pb.Array2DReplyD, len(policy))
for e := range policy {
policyReply.D2[e] = &pb.Array2DReplyD{D1: policy[e]}
}
return policyReply
}
// GetAllSubjects gets the list of subjects that show up in the current policy.
func (s *Server) GetAllSubjects(ctx context.Context, in *pb.EmptyRequest) (*pb.ArrayReply, error) {
return s.GetAllNamedSubjects(ctx, &pb.SimpleGetRequest{EnforcerHandler: in.Handler, PType: "p"})
}
// GetAllNamedSubjects gets the list of subjects that show up in the current named policy.
func (s *Server) GetAllNamedSubjects(ctx context.Context, in *pb.SimpleGetRequest) (*pb.ArrayReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.ArrayReply{}, err
}
return &pb.ArrayReply{Array: e.GetModel().GetValuesForFieldInPolicy("p", in.PType, 0)}, nil
}
// GetAllObjects gets the list of objects that show up in the current policy.
func (s *Server) GetAllObjects(ctx context.Context, in *pb.EmptyRequest) (*pb.ArrayReply, error) {
return s.GetAllNamedObjects(ctx, &pb.SimpleGetRequest{EnforcerHandler: in.Handler, PType: "p"})
}
// GetAllNamedObjects gets the list of objects that show up in the current named policy.
func (s *Server) GetAllNamedObjects(ctx context.Context, in *pb.SimpleGetRequest) (*pb.ArrayReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.ArrayReply{}, err
}
return &pb.ArrayReply{Array: e.GetModel().GetValuesForFieldInPolicy("p", in.PType, 1)}, nil
}
// GetAllActions gets the list of actions that show up in the current policy.
func (s *Server) GetAllActions(ctx context.Context, in *pb.EmptyRequest) (*pb.ArrayReply, error) {
return s.GetAllNamedActions(ctx, &pb.SimpleGetRequest{EnforcerHandler: in.Handler, PType: "p"})
}
// GetAllNamedActions gets the list of actions that show up in the current named policy.
func (s *Server) GetAllNamedActions(ctx context.Context, in *pb.SimpleGetRequest) (*pb.ArrayReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.ArrayReply{}, err
}
return &pb.ArrayReply{Array: e.GetModel().GetValuesForFieldInPolicy("p", in.PType, 2)}, nil
}
// GetAllRoles gets the list of roles that show up in the current policy.
func (s *Server) GetAllRoles(ctx context.Context, in *pb.EmptyRequest) (*pb.ArrayReply, error) {
return s.GetAllNamedRoles(ctx, &pb.SimpleGetRequest{EnforcerHandler: in.Handler, PType: "g"})
}
// GetAllNamedRoles gets the list of roles that show up in the current named policy.
func (s *Server) GetAllNamedRoles(ctx context.Context, in *pb.SimpleGetRequest) (*pb.ArrayReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.ArrayReply{}, err
}
return &pb.ArrayReply{Array: e.GetModel().GetValuesForFieldInPolicy("g", in.PType, 1)}, nil
}
// GetPolicy gets all the authorization rules in the policy.
func (s *Server) GetPolicy(ctx context.Context, in *pb.EmptyRequest) (*pb.Array2DReply, error) {
return s.GetNamedPolicy(ctx, &pb.PolicyRequest{EnforcerHandler: in.Handler, PType: "p"})
}
// GetNamedPolicy gets all the authorization rules in the named policy.
func (s *Server) GetNamedPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.Array2DReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.Array2DReply{}, err
}
return s.wrapPlainPolicy(e.GetModel().GetPolicy("p", in.PType)), nil
}
// GetFilteredPolicy gets all the authorization rules in the policy, field filters can be specified.
func (s *Server) GetFilteredPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.Array2DReply, error) {
in.PType = "p"
return s.GetFilteredNamedPolicy(ctx, in)
}
// GetFilteredNamedPolicy gets all the authorization rules in the named policy, field filters can be specified.
func (s *Server) GetFilteredNamedPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.Array2DReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.Array2DReply{}, err
}
return s.wrapPlainPolicy(e.GetModel().GetFilteredPolicy("p", in.PType, int(in.FieldIndex), in.FieldValues...)), nil
}
// GetGroupingPolicy gets all the role inheritance rules in the policy.
func (s *Server) GetGroupingPolicy(ctx context.Context, in *pb.EmptyRequest) (*pb.Array2DReply, error) {
return s.GetNamedGroupingPolicy(ctx, &pb.PolicyRequest{EnforcerHandler: in.Handler, PType: "g"})
}
// GetNamedGroupingPolicy gets all the role inheritance rules in the policy.
func (s *Server) GetNamedGroupingPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.Array2DReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.Array2DReply{}, err
}
return s.wrapPlainPolicy(e.GetModel().GetPolicy("g", in.PType)), nil
}
// GetFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified.
func (s *Server) GetFilteredGroupingPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.Array2DReply, error) {
in.PType = "g"
return s.GetFilteredNamedGroupingPolicy(ctx, in)
}
// GetFilteredNamedGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified.
func (s *Server) GetFilteredNamedGroupingPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.Array2DReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.Array2DReply{}, err
}
return s.wrapPlainPolicy(e.GetModel().GetFilteredPolicy("g", in.PType, int(in.FieldIndex), in.FieldValues...)), nil
}
// HasPolicy determines whether an authorization rule exists.
func (s *Server) HasPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
return s.HasNamedPolicy(ctx, in)
}
// HasNamedPolicy determines whether a named authorization rule exists.
func (s *Server) HasNamedPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
return &pb.BoolReply{Res: e.GetModel().HasPolicy("p", in.PType, in.Params)}, nil
}
// HasGroupingPolicy determines whether a role inheritance rule exists.
func (s *Server) HasGroupingPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
in.PType = "g"
return s.HasNamedGroupingPolicy(ctx, in)
}
// HasNamedGroupingPolicy determines whether a named role inheritance rule exists.
func (s *Server) HasNamedGroupingPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
return &pb.BoolReply{Res: e.GetModel().HasPolicy("g", in.PType, in.Params)}, nil
}
func (s *Server) AddPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
in.PType = "p"
return s.AddNamedPolicy(ctx, in)
}
func (s *Server) AddNamedPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleAdded, err := e.AddNamedPolicy(in.PType, in.Params)
return &pb.BoolReply{Res: ruleAdded}, err
}
func (s *Server) RemovePolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
in.PType = "p"
return s.RemoveNamedPolicy(ctx, in)
}
func (s *Server) RemoveNamedPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveNamedPolicy(in.PType, in.Params)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// RemoveFilteredPolicy removes an authorization rule from the current policy, field filters can be specified.
func (s *Server) RemoveFilteredPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.BoolReply, error) {
in.PType = "p"
return s.RemoveFilteredNamedPolicy(ctx, in)
}
// RemoveFilteredNamedPolicy removes an authorization rule from the current named policy, field filters can be specified.
func (s *Server) RemoveFilteredNamedPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveFilteredNamedPolicy(in.PType, int(in.FieldIndex), in.FieldValues...)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// AddGroupingPolicy adds a role inheritance rule to the current policy.
// If the rule already exists, the function returns false and the rule will not be added.
// Otherwise the function returns true by adding the new rule.
func (s *Server) AddGroupingPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
in.PType = "g"
return s.AddNamedGroupingPolicy(ctx, in)
}
// AddNamedGroupingPolicy adds a named role inheritance rule to the current policy.
// If the rule already exists, the function returns false and the rule will not be added.
// Otherwise the function returns true by adding the new rule.
func (s *Server) AddNamedGroupingPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleAdded, err := e.AddNamedGroupingPolicy(in.PType, in.Params)
return &pb.BoolReply{Res: ruleAdded}, err
}
// RemoveGroupingPolicy removes a role inheritance rule from the current policy.
func (s *Server) RemoveGroupingPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
in.PType = "g"
return s.RemoveNamedGroupingPolicy(ctx, in)
}
// RemoveNamedGroupingPolicy removes a role inheritance rule from the current named policy.
func (s *Server) RemoveNamedGroupingPolicy(ctx context.Context, in *pb.PolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveNamedGroupingPolicy(in.PType, in.Params)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// RemoveFilteredGroupingPolicy removes a role inheritance rule from the current policy, field filters can be specified.
func (s *Server) RemoveFilteredGroupingPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.BoolReply, error) {
in.PType = "g"
return s.RemoveFilteredNamedGroupingPolicy(ctx, in)
}
// RemoveFilteredNamedGroupingPolicy removes a role inheritance rule from the current named policy, field filters can be specified.
func (s *Server) RemoveFilteredNamedGroupingPolicy(ctx context.Context, in *pb.FilteredPolicyRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveFilteredNamedGroupingPolicy(in.PType, int(in.FieldIndex), in.FieldValues...)
return &pb.BoolReply{Res: ruleRemoved}, err
}

View File

@@ -0,0 +1,294 @@
package server
import (
"testing"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
"github.com/casbin/casbin/v2/util"
"github.com/stretchr/testify/assert"
)
func testStringList(t *testing.T, title string, f func() []string, res []string) {
t.Helper()
myRes := f()
t.Log(title+": ", myRes)
if !util.ArrayEquals(res, myRes) {
t.Error(title+": ", myRes, ", supposed to be ", res)
}
}
func extractFromArrayReply(reply *pb.ArrayReply) func() []string {
return func() []string {
return reply.Array
}
}
func TestGetList(t *testing.T) {
e := newTestEngine(t, "file", "../examples/rbac_policy.csv", "../examples/rbac_model.conf")
subjects, err := e.s.GetAllSubjects(e.ctx, &pb.EmptyRequest{Handler: e.h})
if err != nil {
t.Fatal(err)
}
testStringList(t, "Subjects", extractFromArrayReply(subjects), []string{"alice", "bob", "data2_admin", "data3_admin", "data4_admin"})
objects, err := e.s.GetAllObjects(e.ctx, &pb.EmptyRequest{Handler: e.h})
if err != nil {
t.Fatal(err)
}
testStringList(t, "Objects", extractFromArrayReply(objects), []string{"data1", "data2", "data3", "data4"})
actions, err := e.s.GetAllActions(e.ctx, &pb.EmptyRequest{Handler: e.h})
if err != nil {
t.Fatal(err)
}
testStringList(t, "Actions", extractFromArrayReply(actions), []string{"read", "write", "admin"})
roles, err := e.s.GetAllRoles(e.ctx, &pb.EmptyRequest{Handler: e.h})
if err != nil {
t.Fatal(err)
}
testStringList(t, "Roles", extractFromArrayReply(roles), []string{"data2_admin", "data3_admin", "data4_admin"})
}
func extractFromArray2DReply(reply *pb.Array2DReply) [][]string {
array2d := make([][]string, len(reply.D2))
for i := 0; i < len(reply.D2); i++ {
array2d[i] = reply.D2[i].D1
}
return array2d
}
func testGetPolicy(t *testing.T, e *testEngine, res [][]string) {
t.Helper()
req := &pb.EmptyRequest{Handler: e.h}
reply, err := e.s.GetPolicy(e.ctx, req)
assert.NoError(t, err)
myRes := extractFromArray2DReply(reply)
t.Log("Policy: ", myRes)
if !util.Array2DEquals(res, myRes) {
t.Error("Policy: ", myRes, ", supposed to be ", res)
}
}
func testGetFilteredPolicy(t *testing.T, e *testEngine, fieldIndex int, res [][]string, fieldValues ...string) {
t.Helper()
req := &pb.FilteredPolicyRequest{
EnforcerHandler: e.h, FieldIndex: int32(fieldIndex), FieldValues: fieldValues}
reply, err := e.s.GetFilteredPolicy(e.ctx, req)
assert.NoError(t, err)
myRes := extractFromArray2DReply(reply)
t.Log("Policy for ", util.ParamsToString(req.FieldValues...), ": ", myRes)
if !util.Array2DEquals(res, myRes) {
t.Error("Policy for ", util.ParamsToString(req.FieldValues...), ": ", myRes, ", supposed to be ", res)
}
}
func testGetGroupingPolicy(t *testing.T, e *testEngine, res [][]string) {
t.Helper()
req := &pb.EmptyRequest{Handler: e.h}
reply, err := e.s.GetGroupingPolicy(e.ctx, req)
assert.NoError(t, err)
myRes := extractFromArray2DReply(reply)
t.Log("Grouping policy: ", myRes)
if !util.Array2DEquals(res, myRes) {
t.Error("Grouping policy: ", myRes, ", supposed to be ", res)
}
}
func testGetFilteredGroupingPolicy(t *testing.T, e *testEngine, fieldIndex int, res [][]string, fieldValues ...string) {
t.Helper()
req := &pb.FilteredPolicyRequest{
EnforcerHandler: e.h, FieldIndex: int32(fieldIndex), FieldValues: fieldValues}
reply, err := e.s.GetFilteredGroupingPolicy(e.ctx, req)
assert.NoError(t, err)
myRes := extractFromArray2DReply(reply)
t.Log("Grouping policy for ", util.ParamsToString(fieldValues...), ": ", myRes)
if !util.Array2DEquals(res, myRes) {
t.Error("Grouping policy for ", util.ParamsToString(fieldValues...), ": ", myRes, ", supposed to be ", res)
}
}
func testHasPolicy(t *testing.T, e *testEngine, policy []string, res bool) {
t.Helper()
req := &pb.PolicyRequest{EnforcerHandler: e.h, PType: "p", Params: policy}
reply, err := e.s.HasPolicy(e.ctx, req)
assert.NoError(t, err)
myRes := reply.Res
t.Log("Has policy ", util.ArrayToString(policy), ": ", myRes)
if res != myRes {
t.Error("Has policy ", util.ArrayToString(policy), ": ", myRes, ", supposed to be ", res)
}
}
func testHasGroupingPolicy(t *testing.T, e *testEngine, policy []string, res bool) {
t.Helper()
req := &pb.PolicyRequest{EnforcerHandler: e.h, PType: "g", Params: policy}
reply, err := e.s.HasNamedGroupingPolicy(e.ctx, req)
assert.NoError(t, err)
myRes := reply.Res
t.Log("Has grouping policy ", util.ArrayToString(policy), ": ", myRes)
if res != myRes {
t.Error("Has grouping policy ", util.ArrayToString(policy), ": ", myRes, ", supposed to be ", res)
}
}
func TestGetPolicyAPI(t *testing.T) {
e := newTestEngine(t, "file", "../examples/rbac_policy.csv", "../examples/rbac_model.conf")
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
{"data3_admin", "data3", "admin"},
{"data4_admin", "data4", "read"}})
testGetFilteredPolicy(t, e, 0, [][]string{{"alice", "data1", "read"}}, "alice")
testGetFilteredPolicy(t, e, 0, [][]string{{"bob", "data2", "write"}}, "bob")
testGetFilteredPolicy(t, e, 0, [][]string{{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"}}, "data2_admin")
testGetFilteredPolicy(t, e, 1, [][]string{{"alice", "data1", "read"}}, "data1")
testGetFilteredPolicy(t, e, 1, [][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"}}, "data2")
testGetFilteredPolicy(t, e, 2, [][]string{{"alice", "data1", "read"}, {"data2_admin", "data2", "read"}, {"data4_admin", "data4", "read"}}, "read")
testGetFilteredPolicy(t, e, 2, [][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "write"}}, "write")
testGetFilteredPolicy(t, e, 0, [][]string{{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"}}, "data2_admin", "data2")
// Note: "" (empty string) in fieldValues means matching all values.
testGetFilteredPolicy(t, e, 0, [][]string{{"data2_admin", "data2", "read"}}, "data2_admin", "", "read")
testGetFilteredPolicy(t, e, 1, [][]string{{"bob", "data2", "write"},
{"data2_admin", "data2", "write"}}, "data2", "write")
testHasPolicy(t, e, []string{"alice", "data1", "read"}, true)
testHasPolicy(t, e, []string{"bob", "data2", "write"}, true)
testHasPolicy(t, e, []string{"alice", "data2", "read"}, false)
testHasPolicy(t, e, []string{"bob", "data3", "write"}, false)
testGetGroupingPolicy(t, e, [][]string{{"alice", "data2_admin"}, {"george", "data3_admin"}, {"data3_admin", "data4_admin"}})
testGetFilteredGroupingPolicy(t, e, 0, [][]string{{"alice", "data2_admin"}}, "alice")
testGetFilteredGroupingPolicy(t, e, 0, [][]string{}, "bob")
testGetFilteredGroupingPolicy(t, e, 1, [][]string{}, "data1_admin")
testGetFilteredGroupingPolicy(t, e, 1, [][]string{{"alice", "data2_admin"}}, "data2_admin")
// Note: "" (empty string) in fieldValues means matching all values.
testGetFilteredGroupingPolicy(t, e, 0, [][]string{{"alice", "data2_admin"}}, "", "data2_admin")
testHasGroupingPolicy(t, e, []string{"alice", "data2_admin"}, true)
testHasGroupingPolicy(t, e, []string{"bob", "data2_admin"}, false)
}
func TestModifyPolicyAPI(t *testing.T) {
e := newTestEngine(t, "file", "../examples/rbac_policy.csv", "../examples/rbac_model.conf")
testGetPolicy(t, e, [][]string{
{"alice", "data1", "read"},
{"bob", "data2", "write"},
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
{"data3_admin", "data3", "admin"},
{"data4_admin", "data4", "read"}})
_, err := e.s.RemovePolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"alice", "data1", "read"}})
assert.NoError(t, err)
_, err = e.s.RemovePolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"bob", "data2", "write"}})
assert.NoError(t, err)
_, err = e.s.RemovePolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"alice", "data1", "read"}})
assert.NoError(t, err)
_, err = e.s.AddPolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"eve", "data3", "read"}})
assert.NoError(t, err)
_, err = e.s.AddPolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"eve", "data3", "read"}})
assert.NoError(t, err)
namedPolicy := []string{"eve", "data3", "read"}
_, err = e.s.RemovePolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, PType: "p", Params: namedPolicy})
assert.NoError(t, err)
_, err = e.s.AddNamedPolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, PType: "p", Params: namedPolicy})
assert.NoError(t, err)
testGetPolicy(t, e, [][]string{
{"data2_admin", "data2", "read"},
{"data2_admin", "data2", "write"},
{"data3_admin", "data3", "admin"},
{"data4_admin", "data4", "read"},
{"eve", "data3", "read"}})
_, err = e.s.RemoveFilteredPolicy(e.ctx, &pb.FilteredPolicyRequest{EnforcerHandler: e.h, FieldIndex: 1, FieldValues: []string{"data2"}})
assert.NoError(t, err)
_, err = e.s.RemoveFilteredPolicy(e.ctx, &pb.FilteredPolicyRequest{EnforcerHandler: e.h, FieldIndex: 1, FieldValues: []string{"data4"}})
assert.NoError(t, err)
testGetPolicy(t, e, [][]string{{"data3_admin", "data3", "admin"}, {"eve", "data3", "read"}})
}
func TestModifyGroupingPolicyAPI(t *testing.T) {
e := newTestEngine(t, "file", "../examples/rbac_policy.csv", "../examples/rbac_model.conf")
testGetRoles(t, e, "alice", []string{"data2_admin"})
testGetRoles(t, e, "bob", []string{})
testGetRoles(t, e, "eve", []string{})
testGetRoles(t, e, "non_exist", []string{})
_, err := e.s.RemoveGroupingPolicy(e.ctx,
&pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"alice", "data2_admin"}})
assert.NoError(t, err)
_, err = e.s.AddGroupingPolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"bob", "data1_admin"}})
assert.NoError(t, err)
_, err = e.s.AddGroupingPolicy(e.ctx, &pb.PolicyRequest{EnforcerHandler: e.h, Params: []string{"eve", "data3_admin"}})
assert.NoError(t, err)
namedGroupingPolicy := []string{"alice", "data2_admin"}
testGetRoles(t, e, "alice", []string{})
_, err = e.s.AddNamedGroupingPolicy(e.ctx,
&pb.PolicyRequest{EnforcerHandler: e.h, PType: "g", Params: namedGroupingPolicy})
assert.NoError(t, err)
testGetRoles(t, e, "alice", []string{"data2_admin"})
_, err = e.s.RemoveNamedGroupingPolicy(e.ctx,
&pb.PolicyRequest{EnforcerHandler: e.h, PType: "g", Params: namedGroupingPolicy})
assert.NoError(t, err)
testGetRoles(t, e, "alice", []string{})
testGetRoles(t, e, "bob", []string{"data1_admin"})
testGetRoles(t, e, "eve", []string{"data3_admin"})
testGetRoles(t, e, "non_exist", []string{})
testGetUsers(t, e, "data1_admin", []string{"bob"})
testGetUsers(t, e, "data2_admin", []string{})
testGetUsers(t, e, "data3_admin", []string{"eve", "george"})
testGetUsers(t, e, "data4_admin", []string{"data3_admin"})
_, err = e.s.RemoveFilteredGroupingPolicy(e.ctx,
&pb.FilteredPolicyRequest{EnforcerHandler: e.h, FieldIndex: 0, FieldValues: []string{"bob"}})
assert.NoError(t, err)
testGetRoles(t, e, "alice", []string{})
testGetRoles(t, e, "bob", []string{})
testGetRoles(t, e, "eve", []string{"data3_admin"})
testGetRoles(t, e, "non_exist", []string{})
testGetUsers(t, e, "data1_admin", []string{})
testGetUsers(t, e, "data2_admin", []string{})
testGetUsers(t, e, "data3_admin", []string{"george", "eve"})
}

View File

@@ -0,0 +1,112 @@
package server
import (
"context"
"io/ioutil"
"testing"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
"github.com/stretchr/testify/assert"
)
func testEnforce(t *testing.T, e *testEngine, sub string, obj string, act string, res bool) {
t.Helper()
reply, err := e.s.Enforce(e.ctx, &pb.EnforceRequest{EnforcerHandler: e.h, 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)
} else {
t.Logf("Enforce for %s, %s, %s : %v", sub, obj, act, reply.Res)
}
}
func testEnforceWithoutUsers(t *testing.T, e *testEngine, obj string, act string, res bool) {
t.Helper()
reply, err := e.s.Enforce(e.ctx, &pb.EnforceRequest{EnforcerHandler: e.h, Params: []string{obj, act}})
assert.NoError(t, err)
if reply.Res != res {
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)
}
}

View File

@@ -0,0 +1,209 @@
package server
import (
"context"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
)
// GetRolesForUser gets the roles that a user has.
func (s *Server) GetRolesForUser(ctx context.Context, in *pb.UserRoleRequest) (*pb.ArrayReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.ArrayReply{}, err
}
res, _ := e.GetModel()["g"]["g"].RM.GetRoles(in.User)
return &pb.ArrayReply{Array: res}, nil
}
// GetImplicitPermissionsForUser gets all permissions(including children) for a user or role.
func (s *Server) GetImplicitRolesForUser(ctx context.Context, in *pb.UserRoleRequest) (*pb.ArrayReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.ArrayReply{}, err
}
res, err := e.GetImplicitRolesForUser(in.User)
return &pb.ArrayReply{Array: res}, err
}
// GetUsersForRole gets the users that has a role.
func (s *Server) GetUsersForRole(ctx context.Context, in *pb.UserRoleRequest) (*pb.ArrayReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.ArrayReply{}, err
}
res, _ := e.GetModel()["g"]["g"].RM.GetUsers(in.Role)
return &pb.ArrayReply{Array: res}, nil
}
// HasRoleForUser determines whether a user has a role.
func (s *Server) HasRoleForUser(ctx context.Context, in *pb.UserRoleRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
roles, err := e.GetRolesForUser(in.User)
if err != nil {
return &pb.BoolReply{}, err
}
for _, r := range roles {
if r == in.Role {
return &pb.BoolReply{Res: true}, nil
}
}
return &pb.BoolReply{}, nil
}
// AddRoleForUser adds a role for a user.
// Returns false if the user already has the role (aka not affected).
func (s *Server) AddRoleForUser(ctx context.Context, in *pb.UserRoleRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleAdded, err := e.AddGroupingPolicy(in.User, in.Role)
return &pb.BoolReply{Res: ruleAdded}, err
}
// DeleteRoleForUser deletes a role for a user.
// Returns false if the user does not have the role (aka not affected).
func (s *Server) DeleteRoleForUser(ctx context.Context, in *pb.UserRoleRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveGroupingPolicy(in.User, in.Role)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// DeleteRolesForUser deletes all roles for a user.
// Returns false if the user does not have any roles (aka not affected).
func (s *Server) DeleteRolesForUser(ctx context.Context, in *pb.UserRoleRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveFilteredGroupingPolicy(0, in.User)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// DeleteUser deletes a user.
// Returns false if the user does not exist (aka not affected).
func (s *Server) DeleteUser(ctx context.Context, in *pb.UserRoleRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveFilteredGroupingPolicy(0, in.User)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// DeleteRole deletes a role.
func (s *Server) DeleteRole(ctx context.Context, in *pb.UserRoleRequest) (*pb.EmptyReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.EmptyReply{}, err
}
_, err = e.DeleteRole(in.Role)
return &pb.EmptyReply{}, err
}
// DeletePermission deletes a permission.
// Returns false if the permission does not exist (aka not affected).
func (s *Server) DeletePermission(ctx context.Context, in *pb.PermissionRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveFilteredPolicy(1, in.Permissions...)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// AddPermissionForUser adds a permission for a user or role.
// Returns false if the user or role already has the permission (aka not affected).
func (s *Server) AddPermissionForUser(ctx context.Context, in *pb.PermissionRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleAdded, err := e.AddPolicy(s.convertPermissions(in.User, in.Permissions...)...)
return &pb.BoolReply{Res: ruleAdded}, err
}
// DeletePermissionForUser deletes a permission for a user or role.
// Returns false if the user or role does not have the permission (aka not affected).
func (s *Server) DeletePermissionForUser(ctx context.Context, in *pb.PermissionRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemovePolicy(s.convertPermissions(in.User, in.Permissions...)...)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// DeletePermissionsForUser deletes permissions for a user or role.
// Returns false if the user or role does not have any permissions (aka not affected).
func (s *Server) DeletePermissionsForUser(ctx context.Context, in *pb.PermissionRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
ruleRemoved, err := e.RemoveFilteredPolicy(0, in.User)
return &pb.BoolReply{Res: ruleRemoved}, err
}
// GetPermissionsForUser gets permissions for a user or role.
func (s *Server) GetPermissionsForUser(ctx context.Context, in *pb.PermissionRequest) (*pb.Array2DReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.Array2DReply{}, err
}
return s.wrapPlainPolicy(e.GetFilteredPolicy(0, in.User)), nil
}
// GetImplicitPermissionsForUser gets all permissions(including children) for a user or role.
func (s *Server) GetImplicitPermissionsForUser(ctx context.Context, in *pb.PermissionRequest) (*pb.Array2DReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.Array2DReply{}, err
}
resp, err := e.GetImplicitPermissionsForUser(in.User)
return s.wrapPlainPolicy(resp), err
}
// HasPermissionForUser determines whether a user has a permission.
func (s *Server) HasPermissionForUser(ctx context.Context, in *pb.PermissionRequest) (*pb.BoolReply, error) {
e, err := s.getEnforcer(int(in.EnforcerHandler))
if err != nil {
return &pb.BoolReply{}, err
}
return &pb.BoolReply{Res: e.HasPolicy(s.convertPermissions(in.User, in.Permissions...)...)}, nil
}
func (s *Server) convertPermissions(user string, permissions ...string) []interface{} {
params := make([]interface{}, 0, len(permissions)+1)
params = append(params, user)
for _, perm := range permissions {
params = append(params, perm)
}
return params
}

View File

@@ -0,0 +1,227 @@
package server
import (
"testing"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
"github.com/casbin/casbin/v2/util"
"github.com/stretchr/testify/assert"
)
func testGetRoles(t *testing.T, e *testEngine, name string, res []string) {
t.Helper()
reply, err := e.s.GetRolesForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: name})
assert.NoError(t, err)
t.Log("Roles for ", name, ": ", reply.Array)
if !util.SetEquals(res, reply.Array) {
t.Error("Roles for ", name, ": ", reply.Array, ", supposed to be ", res)
}
}
func testGetImplicitRoles(t *testing.T, e *testEngine, name string, res []string) {
t.Helper()
reply, err := e.s.GetImplicitRolesForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: name})
assert.NoError(t, err)
t.Log("Implicit Roles for ", name, ": ", reply.Array)
if !util.SetEquals(res, reply.Array) {
t.Error("Implicit Roles for ", name, ": ", reply.Array, ", supposed to be ", res)
}
}
func testGetUsers(t *testing.T, e *testEngine, name string, res []string) {
t.Helper()
reply, err := e.s.GetUsersForRole(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: name})
assert.NoError(t, err)
t.Log("Users for ", name, ": ", reply.Array)
if !util.SetEquals(res, reply.Array) {
t.Error("Users for ", name, ": ", reply.Array, ", supposed to be ", res)
}
}
func testHasRole(t *testing.T, e *testEngine, name string, role string, res bool) {
t.Helper()
reply, err := e.s.HasRoleForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: name, Role: role})
assert.NoError(t, err)
t.Log(name, " has role ", role, ": ", reply.Res)
if res != reply.Res {
t.Error(name, " has role ", role, ": ", reply.Res, ", supposed to be ", res)
}
}
func TestRoleAPI(t *testing.T) {
e := newTestEngine(t, "file", "../examples/rbac_policy.csv", "../examples/rbac_model.conf")
testGetRoles(t, e, "alice", []string{"data2_admin"})
testGetRoles(t, e, "bob", []string{})
testGetRoles(t, e, "data2_admin", []string{})
testGetRoles(t, e, "non_exist", []string{})
testHasRole(t, e, "alice", "data1_admin", false)
testHasRole(t, e, "alice", "data2_admin", true)
_, err := e.s.AddRoleForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: "alice", Role: "data1_admin"})
assert.NoError(t, err)
testGetRoles(t, e, "alice", []string{"data1_admin", "data2_admin"})
testGetRoles(t, e, "bob", []string{})
testGetRoles(t, e, "data2_admin", []string{})
_, err = e.s.DeleteRoleForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: "alice", Role: "data1_admin"})
assert.NoError(t, err)
testGetRoles(t, e, "alice", []string{"data2_admin"})
testGetRoles(t, e, "bob", []string{})
testGetRoles(t, e, "data2_admin", []string{})
testGetImplicitRoles(t, e, "alice", []string{"data2_admin"})
testGetImplicitRoles(t, e, "bob", []string{})
testGetImplicitRoles(t, e, "george", []string{"data3_admin", "data4_admin"})
_, err = e.s.DeleteRolesForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: "alice"})
assert.NoError(t, err)
testGetRoles(t, e, "alice", []string{})
testGetRoles(t, e, "bob", []string{})
testGetRoles(t, e, "data2_admin", []string{})
_, err = e.s.AddRoleForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: "alice", Role: "data1_admin"})
assert.NoError(t, err)
_, err = e.s.DeleteUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: "alice"})
assert.NoError(t, err)
testGetRoles(t, e, "alice", []string{})
testGetRoles(t, e, "bob", []string{})
testGetRoles(t, e, "data2_admin", []string{})
_, err = e.s.AddRoleForUser(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, User: "alice", Role: "data2_admin"})
assert.NoError(t, err)
testEnforce(t, e, "alice", "data1", "read", true)
testEnforce(t, e, "alice", "data1", "write", false)
testEnforce(t, e, "alice", "data2", "read", true)
testEnforce(t, e, "alice", "data2", "write", true)
testEnforce(t, e, "bob", "data1", "read", false)
testEnforce(t, e, "bob", "data1", "write", false)
testEnforce(t, e, "bob", "data2", "read", false)
testEnforce(t, e, "bob", "data2", "write", true)
testEnforce(t, e, "bob", "data4", "read", false)
testEnforce(t, e, "george", "data4", "write", false)
testEnforce(t, e, "george", "data4", "read", true)
_, err = e.s.DeleteRole(e.ctx, &pb.UserRoleRequest{EnforcerHandler: e.h, Role: "data2_admin"})
assert.NoError(t, err)
testEnforce(t, e, "alice", "data1", "read", true)
testEnforce(t, e, "alice", "data1", "write", false)
testEnforce(t, e, "alice", "data2", "read", false)
testEnforce(t, e, "alice", "data2", "write", false)
testEnforce(t, e, "bob", "data1", "read", false)
testEnforce(t, e, "bob", "data1", "write", false)
testEnforce(t, e, "bob", "data2", "read", false)
testEnforce(t, e, "bob", "data2", "write", true)
testGetPermissions(t, e, "alice", [][]string{{"alice", "data1", "read"}}) //Added these in this class as it's part of RBAC.
testGetPermissions(t, e, "bob", [][]string{{"bob", "data2", "write"}})
testGetPermissions(t, e, "george", [][]string{})
testGetPermissions(t, e, "data3_admin", [][]string{{"data3_admin", "data3", "admin"}})
testGetImplicitPermissions(t, e, "bob", [][]string{{"bob", "data2", "write"}})
testGetImplicitPermissions(t, e, "data3_admin", [][]string{{"data3_admin", "data3", "admin"}, {"data4_admin", "data4", "read"}})
}
func testGetPermissions(t *testing.T, e *testEngine, name string, res [][]string) {
t.Helper()
reply, err := e.s.GetPermissionsForUser(e.ctx, &pb.PermissionRequest{EnforcerHandler: e.h, User: name})
assert.NoError(t, err)
myRes := extractFromArray2DReply(reply)
t.Log("Permissions for ", name, ": ", myRes)
if !util.Array2DEquals(res, myRes) {
t.Error("Permissions for ", name, ": ", myRes, ", supposed to be ", res)
}
}
func testGetImplicitPermissions(t *testing.T, e *testEngine, name string, res [][]string) {
t.Helper()
reply, err := e.s.GetImplicitPermissionsForUser(e.ctx, &pb.PermissionRequest{EnforcerHandler: e.h, User: name})
assert.NoError(t, err)
myRes := extractFromArray2DReply(reply)
t.Log("Implicit Permissions for ", name, ": ", myRes)
if !util.Array2DEquals(res, myRes) {
t.Error("Implicit Permissions for ", name, ": ", myRes, ", supposed to be ", res)
}
}
func testHasPermission(t *testing.T, e *testEngine, name string, permission []string, res bool) {
t.Helper()
reply, err := e.s.HasPermissionForUser(e.ctx, &pb.PermissionRequest{EnforcerHandler: e.h, User: name, Permissions: permission})
assert.NoError(t, err)
t.Log(name, " has permission ", util.ArrayToString(permission), ": ", reply.Res)
if res != reply.Res {
t.Error(name, " has permission ", util.ArrayToString(permission), ": ", reply.Res, ", supposed to be ", res)
}
}
func TestPermissionAPI(t *testing.T) {
e := newTestEngine(t, "file", "../examples/basic_without_resources_policy.csv",
"../examples/basic_without_resources_model.conf")
testEnforceWithoutUsers(t, e, "alice", "read", true)
testEnforceWithoutUsers(t, e, "alice", "write", false)
testEnforceWithoutUsers(t, e, "bob", "read", false)
testEnforceWithoutUsers(t, e, "bob", "write", true)
testGetPermissions(t, e, "alice", [][]string{{"alice", "read"}})
testGetPermissions(t, e, "bob", [][]string{{"bob", "write"}})
testHasPermission(t, e, "alice", []string{"read"}, true)
testHasPermission(t, e, "alice", []string{"write"}, false)
testHasPermission(t, e, "bob", []string{"read"}, false)
testHasPermission(t, e, "bob", []string{"write"}, true)
_, err := e.s.DeletePermission(e.ctx, &pb.PermissionRequest{EnforcerHandler: e.h, Permissions: []string{"read"}})
assert.NoError(t, err)
testEnforceWithoutUsers(t, e, "alice", "read", false)
testEnforceWithoutUsers(t, e, "alice", "write", false)
testEnforceWithoutUsers(t, e, "bob", "read", false)
testEnforceWithoutUsers(t, e, "bob", "write", true)
_, err = e.s.AddPermissionForUser(e.ctx, &pb.PermissionRequest{EnforcerHandler: e.h, User: "bob", Permissions: []string{"read"}})
assert.NoError(t, err)
testEnforceWithoutUsers(t, e, "alice", "read", false)
testEnforceWithoutUsers(t, e, "alice", "write", false)
testEnforceWithoutUsers(t, e, "bob", "read", true)
testEnforceWithoutUsers(t, e, "bob", "write", true)
_, err = e.s.DeletePermissionForUser(e.ctx, &pb.PermissionRequest{EnforcerHandler: e.h, User: "bob", Permissions: []string{"read"}})
assert.NoError(t, err)
testEnforceWithoutUsers(t, e, "alice", "read", false)
testEnforceWithoutUsers(t, e, "alice", "write", false)
testEnforceWithoutUsers(t, e, "bob", "read", false)
testEnforceWithoutUsers(t, e, "bob", "write", true)
_, err = e.s.DeletePermissionsForUser(e.ctx, &pb.PermissionRequest{EnforcerHandler: e.h, User: "bob"})
assert.NoError(t, err)
testEnforceWithoutUsers(t, e, "alice", "read", false)
testEnforceWithoutUsers(t, e, "alice", "write", false)
testEnforceWithoutUsers(t, e, "bob", "read", false)
testEnforceWithoutUsers(t, e, "bob", "write", false)
}

View File

@@ -0,0 +1,37 @@
package server
import (
"context"
"io/ioutil"
"testing"
pb "github.com/RafaySystems/rcloud-base/components/authz/proto/rpc"
)
type testEngine struct {
s *Server
ctx context.Context
h int32
}
func newTestEngine(t *testing.T, from, connectStr string, modelLoc string) *testEngine {
s := NewServer()
ctx := context.Background()
_, err := s.NewAdapter(ctx, &pb.NewAdapterRequest{DriverName: from, ConnectString: connectStr})
if err != nil {
t.Fatal(err)
}
modelText, err := ioutil.ReadFile(modelLoc)
if err != nil {
t.Fatal(err)
}
resp, err := s.NewEnforcer(ctx, &pb.NewEnforcerRequest{ModelText: string(modelText), AdapterHandle: 0})
if err != nil {
t.Fatal(err)
}
return &testEngine{s: s, ctx: ctx, h: resp.Handler}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
syntax = "proto3";
package rafay.dev.rpc.authz;
service Authz {
rpc NewEnforcer (NewEnforcerRequest) returns (NewEnforcerReply) {}
rpc NewAdapter (NewAdapterRequest) returns (NewAdapterReply) {}
rpc Enforce (EnforceRequest) returns (BoolReply) {}
rpc LoadPolicy (EmptyRequest) returns (EmptyReply) {}
rpc SavePolicy (EmptyRequest) returns (EmptyReply) {}
rpc AddPolicy (PolicyRequest) returns (BoolReply) {}
rpc AddNamedPolicy (PolicyRequest) returns (BoolReply) {}
rpc RemovePolicy (PolicyRequest) returns (BoolReply) {}
rpc RemoveNamedPolicy (PolicyRequest) returns (BoolReply) {}
rpc RemoveFilteredPolicy (FilteredPolicyRequest) returns (BoolReply) {}
rpc RemoveFilteredNamedPolicy (FilteredPolicyRequest) returns (BoolReply) {}
rpc GetPolicy (EmptyRequest) returns (Array2DReply) {}
rpc GetNamedPolicy (PolicyRequest) returns (Array2DReply) {}
rpc GetFilteredPolicy (FilteredPolicyRequest) returns (Array2DReply) {}
rpc GetFilteredNamedPolicy (FilteredPolicyRequest) returns (Array2DReply) {}
rpc AddGroupingPolicy (PolicyRequest) returns (BoolReply) {}
rpc AddNamedGroupingPolicy (PolicyRequest) returns (BoolReply) {}
rpc RemoveGroupingPolicy (PolicyRequest) returns (BoolReply) {}
rpc RemoveNamedGroupingPolicy (PolicyRequest) returns (BoolReply) {}
rpc RemoveFilteredGroupingPolicy (FilteredPolicyRequest) returns (BoolReply) {}
rpc RemoveFilteredNamedGroupingPolicy (FilteredPolicyRequest) returns (BoolReply) {}
rpc GetGroupingPolicy (EmptyRequest) returns (Array2DReply) {}
rpc GetNamedGroupingPolicy(PolicyRequest) returns (Array2DReply) {}
rpc GetFilteredGroupingPolicy (FilteredPolicyRequest) returns (Array2DReply) {}
rpc GetFilteredNamedGroupingPolicy (FilteredPolicyRequest) returns (Array2DReply) {}
rpc GetAllSubjects (EmptyRequest) returns (ArrayReply) {}
rpc GetAllNamedSubjects (SimpleGetRequest) returns (ArrayReply) {}
rpc GetAllObjects (EmptyRequest) returns (ArrayReply) {}
rpc GetAllNamedObjects (SimpleGetRequest) returns (ArrayReply) {}
rpc GetAllActions (EmptyRequest) returns (ArrayReply) {}
rpc GetAllNamedActions (SimpleGetRequest) returns (ArrayReply) {}
rpc GetAllRoles (EmptyRequest) returns (ArrayReply) {}
rpc GetAllNamedRoles (SimpleGetRequest) returns (ArrayReply) {}
rpc HasPolicy (PolicyRequest) returns (BoolReply) {}
rpc HasNamedPolicy (PolicyRequest) returns (BoolReply) {}
rpc HasGroupingPolicy (PolicyRequest) returns (BoolReply) {}
rpc HasNamedGroupingPolicy (PolicyRequest) returns (BoolReply) {}
rpc GetRolesForUser (UserRoleRequest) returns (ArrayReply) {}
rpc GetImplicitRolesForUser (UserRoleRequest) returns (ArrayReply) {}
rpc GetUsersForRole (UserRoleRequest) returns (ArrayReply) {}
rpc HasRoleForUser (UserRoleRequest) returns (BoolReply) {}
rpc AddRoleForUser (UserRoleRequest) returns (BoolReply) {}
rpc DeleteRoleForUser (UserRoleRequest) returns (BoolReply) {}
rpc DeleteRolesForUser (UserRoleRequest) returns (BoolReply) {}
rpc DeleteUser (UserRoleRequest) returns (BoolReply) {}
rpc DeleteRole (UserRoleRequest) returns (EmptyReply) {}
rpc GetPermissionsForUser (PermissionRequest) returns (Array2DReply) {}
rpc GetImplicitPermissionsForUser (PermissionRequest) returns (Array2DReply) {}
rpc DeletePermission (PermissionRequest) returns (BoolReply) {}
rpc AddPermissionForUser (PermissionRequest) returns (BoolReply) {}
rpc DeletePermissionForUser (PermissionRequest) returns (BoolReply) {}
rpc DeletePermissionsForUser (PermissionRequest) returns (BoolReply) {}
rpc HasPermissionForUser (PermissionRequest) returns (BoolReply) {}
}
message NewEnforcerRequest {
string modelText = 1;
int32 adapterHandle = 2;
}
message NewEnforcerReply {
int32 handler = 1;
}
message NewAdapterRequest {
string adapterName = 1;
string driverName = 2;
string connectString = 3;
bool dbSpecified = 4;
}
message NewAdapterReply {
int32 handler = 1;
}
message EnforceRequest {
int32 enforcerHandler = 1;
repeated string params = 2;
}
message BoolReply {
bool res = 1;
}
message EmptyRequest {
int32 handler = 1;
}
message EmptyReply {
}
message PolicyRequest {
int32 enforcerHandler = 1;
string pType = 2;
repeated string params = 3;
}
message SimpleGetRequest {
int32 enforcerHandler = 1;
string pType = 2;
}
message ArrayReply {
repeated string array = 1;
}
message FilteredPolicyRequest {
int32 enforcerHandler = 1;
string pType = 2;
int32 fieldIndex = 3;
repeated string fieldValues = 4;
}
message UserRoleRequest {
int32 enforcerHandler = 1;
string user = 2;
string role = 3;
}
message PermissionRequest {
int32 enforcerHandler = 1;
string user = 2;
repeated string permissions = 3;
}
message Array2DReply {
message d {
repeated string d1 = 1;
}
repeated d d2 = 1;
}

File diff suppressed because it is too large Load Diff