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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Alternatively, you can also [run it from an IDE](https://github.com/casbin/casbi

Similar to Casbin, Casbin-Server also uses adapters to provide policy storage. However, because Casbin-Server is a service instead of a library, the adapters have to be implemented inside Casbin-Server. As Golang is a static language, each adapter requires to import 3rd-party library for that database. We cannot import all those 3rd-party libraries inside Casbin-Server's code, as it causes dependency overhead.

For now, only [Gorm Adapter](https://github.com/casbin/casbin-server/blob/master/server/adapter.go) is built-in with ``mssql``, ``mysql``, ``postgres`` imports all commented. If you want to use ``Gorm Adapter`` with one of those databases, you should uncomment that import line, or add your own import, or even use another adapter by modifying Casbin-Server's source code.
For now, [Gorm Adapter](https://github.com/casbin/casbin-server/blob/master/server/adapter.go) (with ``mssql``, ``mysql``, ``postgres``), MongoDB und Redis Adapter are built-in imports all commented. If you want to use ``Gorm Adapter`` with one of those databases, you should uncomment that import line, or add your own import, or even use another adapter by modifying Casbin-Server's source code.

To allow Casbin-Server to be production-ready, the adapter configuration supports environment variables. For example, assume we created a ``postgres`` database for our RBAC model and want Casbin-Server to use it. Assuming that the environment in which the Casbin-Server runs contains the necessary variables, we can simply use the ``$ENV_VAR`` notation to provide these to the adapter.

Expand Down
5 changes: 5 additions & 0 deletions config/connection_config_redis_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"driver": "redis",
"connection": "localhost:6379",
"enforcer": "examples/rbac_model.conf"
}
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ module github.com/casbin/casbin-server
go 1.19

require (
github.com/alicebob/miniredis/v2 v2.35.0
github.com/casbin/casbin/v2 v2.100.0
github.com/casbin/gorm-adapter/v3 v3.14.0
github.com/casbin/mongodb-adapter/v3 v3.7.0
github.com/casbin/redis-adapter/v3 v3.6.0
github.com/stretchr/testify v1.8.0
google.golang.org/grpc v1.42.0
google.golang.org/protobuf v1.27.1
)

require (
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
github.com/casbin/govaluate v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -23,6 +24,7 @@ require (
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/protobuf v1.5.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/gomodule/redigo v1.8.9 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.13.0 // indirect
Expand All @@ -45,6 +47,7 @@ require (
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
go.mongodb.org/mongo-driver v1.12.0 // indirect
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
Expand Down
12 changes: 9 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSa
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/casbin/casbin/v2 v2.55.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.71.1 h1:LRHyqM0S1LzM/K59PmfUIN0ZJfLgcOjL4OhOQI/FNXU=
github.com/casbin/casbin/v2 v2.71.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.60.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.100.0 h1:aeugSNjjHfCrgA22nHkVvw2xsscboHv5r0a13ljQKGQ=
github.com/casbin/casbin/v2 v2.100.0/go.mod h1:LO7YPez4dX3LgoTCqSQAleQDo0S0BeZBDxYnPUl95Ng=
github.com/casbin/gorm-adapter/v3 v3.14.0 h1:zZ6AIiNHJZ3ntdf5RBrqD+0Cb4UO+uKFk79R9yJ7mpw=
Expand All @@ -23,6 +23,8 @@ github.com/casbin/govaluate v1.2.0 h1:wXCXFmqyY+1RwiKfYo3jMKyrtZmOL3kHwaqDyCPOYa
github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/casbin/mongodb-adapter/v3 v3.7.0 h1:w9c3bea1BGK4eZTAmk17JkY52yv/xSZDSHKji8q+z6E=
github.com/casbin/mongodb-adapter/v3 v3.7.0/go.mod h1:F1mu4ojoJVE/8VhIMxMedhjfwRDdIXgANYs6Sd0MgVA=
github.com/casbin/redis-adapter/v3 v3.6.0 h1:JY9eUJeF428e87s1HvNZxrgUHLGFcJqNiDg5JUftkas=
github.com/casbin/redis-adapter/v3 v3.6.0/go.mod h1:SGL+D0Gx7dQIR8frcnZeq8E0pT2WYuJ05gcEH4c2elY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down Expand Up @@ -89,6 +91,8 @@ github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand Down Expand Up @@ -234,6 +238,8 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE=
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
Expand Down
40 changes: 40 additions & 0 deletions server/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
"regexp"
"strings"
Expand All @@ -27,10 +28,31 @@ import (
fileadapter "github.com/casbin/casbin/v2/persist/file-adapter"
gormadapter "github.com/casbin/gorm-adapter/v3"
mongodbadapter "github.com/casbin/mongodb-adapter/v3"
redisadapter "github.com/casbin/redis-adapter/v3"
)

var errDriverName = errors.New("currently supported DriverName: file | mysql | postgres | mssql")

func parseRedisUrl(redisURL string) (host, port, username, password string, err error) {
if redisURL == "" {
return "", "", "", "", errors.New("redis URL cannot be empty")
}
if !strings.Contains(redisURL, "://") {
redisURL = "redis://" + redisURL
}
u, err := url.Parse(redisURL)
if err != nil {
return "", "", "", "", err
}
host = u.Hostname()
port = u.Port()
if u.User != nil {
username = u.User.Username()
password, _ = u.User.Password()
}
return host, port, username, password, nil
}

func newAdapter(in *pb.NewAdapterRequest) (persist.Adapter, error) {
var a persist.Adapter
in = checkLocalConfig(in)
Expand All @@ -45,6 +67,24 @@ func newAdapter(in *pb.NewAdapterRequest) (persist.Adapter, error) {
if err != nil {
return nil, err
}
case "redis":
var err error
host, port, username, password, err := parseRedisUrl(in.ConnectString)
if err != nil {
return nil, err
}
hostWithPort := fmt.Sprintf("%s:%s", host, port)

config := &redisadapter.Config{
Network: "tcp",
Address: hostWithPort,
Username: username,
Password: password,
}
a, err = redisadapter.NewAdapter(config)
if err != nil {
return nil, err
}
default:
var support = false
for _, driverName := range supportDriverNames {
Expand Down
89 changes: 89 additions & 0 deletions server/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"os"
"testing"

miniredis "github.com/alicebob/miniredis/v2"

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

Expand All @@ -13,3 +16,89 @@ func TestGetLocalConfig(t *testing.T) {
os.Setenv(configFilePathEnvironmentVariable, "dir/custom_path.json")
assert.Equal(t, "dir/custom_path.json", getLocalConfigPath())
}

func runFakeRedis(username string, password string) (host string, port string, err error) {
s, err := miniredis.Run()
if err != nil {
return "", "", err
}
if username != "" && password != "" {
s.RequireUserAuth(username, password)
}
return s.Host(), s.Port(), err
}

func TestRedisAdapterConfig(t *testing.T) {
os.Setenv(configFilePathEnvironmentVariable, "../config/connection_config.json")

host, port, err := runFakeRedis("", "")

in := &pb.NewAdapterRequest{
DriverName: "redis",
ConnectString: "redis://" + host + ":" + port,
}

a, err := newAdapter(in)
assert.NoError(t, err, "should create redis adapter without error")
assert.NotNil(t, a, "adapter should not be nil")
}

func TestRedisAdapterConfigWithUsernameAndPassword(t *testing.T) {
os.Setenv(configFilePathEnvironmentVariable, "../config/connection_config.json")

username, password := "foo", "bar"
host, port, err := runFakeRedis(username, password)

in := &pb.NewAdapterRequest{
DriverName: "redis",
ConnectString: "redis://" + username + ":" + password + "@" + host + ":" + port,
}

a, err := newAdapter(in)
assert.NoError(t, err, "should create redis adapter without error")
assert.NotNil(t, a, "adapter should not be nil")
}

func TestRedisAdapterConfigWithoutPrefix(t *testing.T) {
os.Setenv(configFilePathEnvironmentVariable, "../config/connection_config.json")

host, port, err := runFakeRedis("", "")

in := &pb.NewAdapterRequest{
DriverName: "redis",
ConnectString: host + ":" + port,
}

a, err := newAdapter(in)
assert.NoError(t, err, "should create redis adapter without error")
assert.NotNil(t, a, "adapter should not be nil")
}

func TestInvalidRedisAdapterConfig(t *testing.T) {
os.Setenv(configFilePathEnvironmentVariable, "../config/connection_config.json")

_, _, err := runFakeRedis("", "")

in := &pb.NewAdapterRequest{
DriverName: "redis",
ConnectString: "invalid-address",
}

a, err := newAdapter(in)
assert.Error(t, err, "should cause an redis adapter without error")
assert.ErrorContains(t, err, "dial tcp: lookup invalid-address")
assert.Nil(t, a, "adapter should be nil")
}

func TestRedisAdapterConfigReturnDefaultFallback(t *testing.T) {
os.Setenv(configFilePathEnvironmentVariable, "../config/connection_config.json")

in := &pb.NewAdapterRequest{
DriverName: "redis",
ConnectString: "",
}

a, err := newAdapter(in)
assert.NoError(t, err, "should create file default adapter without error")
assert.NotNil(t, a, "adapter should not be nil")
}