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
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ jobs:
run: go test -v -coverprofile=profile.cov ./...

- name: Install goveralls
env:
GO111MODULE: off
run: go get github.com/mattn/goveralls
run: go install github.com/mattn/goveralls@v0.0.11

- name: Send coverage
env:
Expand Down
74 changes: 56 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,21 @@ Redis Adapter is the [Redis](https://redis.io/) adapter for [Casbin](https://git

go get github.com/casbin/redis-adapter/v3

## Simple Example
## Configuration Options

The `Config` struct supports the following options:

- `Network` (string): Network type, e.g., "tcp", "unix" (required when not using Pool)
- `Address` (string): Redis server address, e.g., "127.0.0.1:6379" (required when not using Pool)
- `Key` (string): Redis key to store Casbin rules (default: "casbin_rules")
- `Username` (string): Username for Redis authentication (optional)
- `Password` (string): Password for Redis authentication (optional)
- `TLSConfig` (*tls.Config): TLS configuration for secure connections (optional)
- `Pool` (*redis.Pool): Existing Redis connection pool (optional, if provided, other connection options are ignored)

## Usage Examples

### Basic Usage

```go
package main
Expand All @@ -26,28 +40,23 @@ import (
)

func main() {
// Direct Initialization:
// Initialize a Redis adapter and use it in a Casbin enforcer:
a, _ := redisadapter.NewAdapter("tcp", "127.0.0.1:6379") // Your Redis network and address.
// Recommended approach using Config
config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379"}
a, _ := redisadapter.NewAdapter(config)

// Use the following if Redis has password like "123"
// a, err := redisadapter.NewAdapterWithPassword("tcp", "127.0.0.1:6379", "123")
// With password authentication
// config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Password: "123"}
// a, _ := redisadapter.NewAdapter(config)

// Use the following if you use Redis with a specific user
// a, err := redisadapter.NewAdapterWithUser("tcp", "127.0.0.1:6379", "username", "password")
// With user credentials
// config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Username: "user", Password: "pass"}
// a, _ := redisadapter.NewAdapter(config)

// Use the following if you use Redis connections pool
// pool := &redis.Pool{}
// a, err := redisadapter.NewAdapterWithPool(pool)

// Initialization with different user options:
// Use the following if you use Redis with passowrd like "123":
// a, err := redisadapter.NewAdapterWithOption(redisadapter.WithNetwork("tcp"), redisadapter.WithAddress("127.0.0.1:6379"), redisadapter.WithPassword("123"))

// Use the following if you use Redis with username, password, and TLS option:
// With TLS configuration
// var clientTLSConfig tls.Config
// ...
// a, err := redisadapter.NewAdapterWithOption(redisadapter.WithNetwork("tcp"), redisadapter.WithAddress("127.0.0.1:6379"), redisadapter.WithUsername("testAccount"), redisadapter.WithPassword("123456"), redisadapter.WithTls(&clientTLSConfig))
// config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Username: "testAccount", Password: "123456", TLSConfig: &clientTLSConfig}
// a, _ := redisadapter.NewAdapter(config)
Comment thread
hsluoyz marked this conversation as resolved.

e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a)

Expand All @@ -66,6 +75,35 @@ func main() {
}
```

### With Connection Pool

```go
package main

import (
"github.com/casbin/casbin/v2"
"github.com/casbin/redis-adapter/v3"
"github.com/gomodule/redigo/redis"
)

func main() {
pool := &redis.Pool{Dial: func() (redis.Conn, error) { return redis.Dial("tcp", "127.0.0.1:6379") }}
config := &redisadapter.Config{Pool: pool, Key: "casbin_rules"}
a, _ := redisadapter.NewAdapter(config)

e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a)

// Load the policy from DB.
e.LoadPolicy()

// Check the permission.
e.Enforce("alice", "data1", "read")

// Save the policy back to DB.
e.SavePolicy()
}
```

## Getting Help

- [Casbin](https://github.com/casbin/casbin)
Expand Down
172 changes: 123 additions & 49 deletions adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ type CasbinRule struct {
V5 string
}

// Config represents the configuration for the Redis adapter.
type Config struct {
// Network is the network type, e.g., "tcp", "unix"
Network string
// Address is the Redis server address, e.g., "127.0.0.1:6379"
Address string
// Key is the Redis key to store Casbin rules (default: "casbin_rules")
Key string
// Username for Redis authentication (optional)
Username string
// Password for Redis authentication (optional)
Password string
// TLSConfig for secure connections (optional)
TLSConfig *tls.Config
// Pool is an existing Redis connection pool (optional)
// If provided, Network, Address, Username, Password, and TLSConfig are ignored
Pool *redis.Pool
}

// Adapter represents the Redis adapter for policy storage.
type Adapter struct {
network string
Expand Down Expand Up @@ -78,94 +97,148 @@ func finalizer(a *Adapter) {
}
}

func newAdapter(network string, address string, key string,
username string, password string) (*Adapter, error) {
// NewAdapter creates a new Redis adapter with the provided configuration.
func NewAdapter(config *Config) (*Adapter, error) {
if config == nil {
return nil, errors.New("config cannot be nil")
}

a := &Adapter{}
a.network = network
a.address = address
a.key = key
a.username = username
a.password = password

// Open the DB, create it if not existed.
err := a.open()
// Set default key if not provided
if config.Key == "" {
a.key = "casbin_rules"
} else {
a.key = config.Key
}

// If a pool is provided, use it
if config.Pool != nil {
a._pool = config.Pool
} else {
// Otherwise, create a new connection
if config.Network == "" {
return nil, errors.New("network is required when not using a pool")
}
if config.Address == "" {
return nil, errors.New("address is required when not using a pool")
}

a.network = config.Network
a.address = config.Address
a.username = config.Username
a.password = config.Password
a.tlsConfig = config.TLSConfig

// Open the DB connection
err := a.open()
if err != nil {
return nil, err
}
}

// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)

return a, err
return a, nil
}

// NewAdapter is the constructor for Adapter.
func NewAdapter(network string, address string) (*Adapter, error) {
return newAdapter(network, address, "casbin_rules", "", "")
// Legacy constructor functions (deprecated)
// These are kept for backward compatibility but should be avoided in new code

// NewAdapterBasic is the basic constructor for Adapter.
// Deprecated: Use NewAdapter with Config struct instead.
func NewAdapterBasic(network string, address string) (*Adapter, error) {
config := &Config{
Network: network,
Address: address,
}
return NewAdapter(config)
}

// NewAdapterWithUser creates adapter with user credentials.
// Deprecated: Use NewAdapter with Config struct instead.
func NewAdapterWithUser(network string, address string, username string, password string) (*Adapter, error) {
return newAdapter(network, address, "casbin_rules", username, password)
config := &Config{
Network: network,
Address: address,
Username: username,
Password: password,
}
return NewAdapter(config)
}

// NewAdapterWithPassword is the constructor for Adapter.
// NewAdapterWithPassword creates adapter with password authentication.
// Deprecated: Use NewAdapter with Config struct instead.
func NewAdapterWithPassword(network string, address string, password string) (*Adapter, error) {
return newAdapter(network, address, "casbin_rules", "", password)
config := &Config{
Network: network,
Address: address,
Password: password,
}
return NewAdapter(config)
}

// NewAdapterWithKey is the constructor for Adapter.
// NewAdapterWithKey creates adapter with custom key.
// Deprecated: Use NewAdapter with Config struct instead.
func NewAdapterWithKey(network string, address string, key string) (*Adapter, error) {
return newAdapter(network, address, key, "", "")
config := &Config{
Network: network,
Address: address,
Key: key,
}
return NewAdapter(config)
}

// NewAdapterWithPool is the constructor for Adapter.
// NewAdapterWithPool creates adapter with connection pool.
// Deprecated: Use NewAdapter with Config struct instead.
func NewAdapterWithPool(pool *redis.Pool) (*Adapter, error) {
a := &Adapter{}
a.key = "casbin_rules"

conn := pool.Get()
defer a.release(conn)

a._conn = conn
a._pool = pool

// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)

return a, nil
config := &Config{
Pool: pool,
}
return NewAdapter(config)
}

// NewAdapterWithPoolAndOptions is the constructor for Adapter.
// NewAdapterWithPoolAndOptions creates adapter with pool and options.
// Deprecated: Use NewAdapter with Config struct instead.
func NewAdapterWithPoolAndOptions(pool *redis.Pool, options ...Option) (*Adapter, error) {
a := &Adapter{}
a.key = "casbin_rules"
config := &Config{
Pool: pool,
}
a, err := NewAdapter(config)
if err != nil {
return nil, err
}

// Apply options for backward compatibility
for _, option := range options {
option(a)
}

conn := pool.Get()
defer a.release(conn)

a._conn = conn
a._pool = pool

// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)

return a, nil
}

type Option func(*Adapter)

// NewAdapterWithOption creates adapter with options pattern.
// Deprecated: Use NewAdapter with Config struct instead.
func NewAdapterWithOption(options ...Option) (*Adapter, error) {
a := &Adapter{}
for _, option := range options {
option(a)
}
// Open the DB, create it if not existed.
err := a.open()

// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)
// Convert to new config-based approach
config := &Config{
Network: a.network,
Address: a.address,
Key: a.key,
Username: a.username,
Password: a.password,
TLSConfig: a.tlsConfig,
}

return a, err
return NewAdapter(config)
}

func WithAddress(address string) Option {
Expand All @@ -191,6 +264,7 @@ func WithNetwork(network string) Option {
a.network = network
}
}

func WithKey(key string) Option {
return func(a *Adapter) {
a.key = key
Expand Down
Loading