arangoadapter

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2025 License: MIT Imports: 13 Imported by: 0

README

ArangoDB Adapter for Casbin

A persistent adapter for Casbin that uses ArangoDB as the storage backend. Store your authorization policies in ArangoDB instead of flat files, with support for clusters, TLS, and flexible configuration.

Why ArangoDB?

ArangoDB is a multi-model database that excels at storing graph data, documents, and key-value pairs. If you're already using ArangoDB or need the flexibility it provides, this adapter makes it easy to persist Casbin policies there.

Features

  • Simple configuration - Functional options pattern for easy setup
  • TLS support - Secure connections with custom certificate configuration
  • Cluster support - Multiple endpoints with round-robin load balancing
  • Auto-creation - Automatically creates database and collection if they don't exist
  • Thread-safe - Safe for concurrent use
  • Context-aware - All major operations support context for timeouts and cancellation
  • Backward compatible - Still supports direct client usage if needed

Installation

go get github.com/denisbytes/arango-adapter

Quick Start

package main

import (
    "log"

    arangoadapter "github.com/denisbytes/arango-adapter"
    "github.com/casbin/casbin/v2"
)

func main() {
    // Create adapter with simple configuration
    adapter, err := arangoadapter.NewAdapter(
        arangoadapter.WithEndpoints("http://localhost:8529"),
        arangoadapter.WithAuthentication("root", "password"),
        arangoadapter.WithDatabase("casbin"),
        arangoadapter.WithCollection("casbin_rule"),
    )
    if err != nil {
        log.Fatal(err)
    }

    // Create the enforcer
    enforcer, err := casbin.NewEnforcer("model.conf", adapter)
    if err != nil {
        log.Fatal(err)
    }

    // Use Casbin as normal
    enforcer.AddPolicy("alice", "data1", "read")
    enforcer.SavePolicy()
}

Configuration Options

The adapter uses the functional options pattern for flexible configuration:

Basic Options
// Set ArangoDB endpoints (single server or cluster)
WithEndpoints("http://localhost:8529")

// Set authentication
WithAuthentication("username", "password")

// Set database name (default: "casbin")
WithDatabase("my_database")

// Set collection name (default: "casbin_rule")
WithCollection("my_collection")
TLS Configuration
// Enable TLS with a CA certificate file
adapter, err := arangoadapter.NewAdapter(
    arangoadapter.WithEndpoints("https://localhost:8529"),
    arangoadapter.WithAuthentication("root", "password"),
    arangoadapter.WithTLS("/path/to/ca-cert.pem"),
)

Or use a custom TLS configuration:

import (
    "crypto/tls"
    "crypto/x509"
    "os"
)

// Load your certificates
caCert, _ := os.ReadFile("/path/to/ca-cert.pem")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

tlsConfig := &tls.Config{
    MinVersion: tls.VersionTLS12,
    RootCAs:    caCertPool,
}

adapter, err := arangoadapter.NewAdapter(
    arangoadapter.WithEndpoints("https://localhost:8529"),
    arangoadapter.WithAuthentication("root", "password"),
    arangoadapter.WithTLSConfig(tlsConfig),
)
Cluster Configuration
// Connect to an ArangoDB cluster with multiple coordinators
adapter, err := arangoadapter.NewAdapter(
    arangoadapter.WithEndpoints(
        "http://coordinator1:8529",
        "http://coordinator2:8529",
        "http://coordinator3:8529",
    ),
    arangoadapter.WithAuthentication("root", "password"),
)

The adapter uses round-robin load balancing across all endpoints.

Using an Existing Client

If you already have an ArangoDB client configured, you can use it directly:

import (
    "github.com/arangodb/go-driver/v2/arangodb"
    "github.com/arangodb/go-driver/v2/connection"
)

// Your existing client setup
endpoint := connection.NewRoundRobinEndpoints([]string{"http://localhost:8529"})
config := connection.DefaultHTTP2ConfigurationWrapper(endpoint, false)
config.Authentication = connection.NewBasicAuth("root", "password")
conn := connection.NewHttp2Connection(config)
client := arangodb.NewClient(conn)

// Create adapter from existing client
adapter, err := arangoadapter.NewAdapterFromClient(client, "casbin", "casbin_rule")

API Reference

Adapter Methods

The adapter implements all required Casbin interfaces:

Loading and Saving
  • LoadPolicy(model) - Load all policies from database
  • LoadPolicyCtx(ctx, model) - Load with context support
  • SavePolicy(model) - Save all policies (replaces existing)
  • SavePolicyCtx(ctx, model) - Save with context support
Single Policy Operations
  • AddPolicy(sec, ptype, rule) - Add a single policy
  • AddPolicyCtx(ctx, sec, ptype, rule) - Add with context
  • RemovePolicy(sec, ptype, rule) - Remove a single policy
  • RemovePolicyCtx(ctx, sec, ptype, rule) - Remove with context
  • UpdatePolicy(sec, ptype, oldRule, newRule) - Update a policy
Batch Operations
  • AddPolicies(sec, ptype, rules) - Add multiple policies
  • RemovePolicies(sec, ptype, rules) - Remove multiple policies
  • UpdatePolicies(sec, ptype, oldRules, newRules) - Update multiple policies
Filtered Operations
  • RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) - Remove policies matching a filter
  • RemoveFilteredPolicyCtx(ctx, sec, ptype, fieldIndex, fieldValues...) - Remove with context
  • UpdateFilteredPolicies(sec, ptype, newPolicies, fieldIndex, fieldValues...) - Update policies matching a filter

Data Structure

Policies are stored as documents in ArangoDB:

{
  "_key": "auto-generated-by-arangodb",
  "ptype": "p",
  "v0": "alice",
  "v1": "data1",
  "v2": "read",
  "v3": "",
  "v4": "",
  "v5": ""
}
  • ptype: Policy type (p, g, p2, g2, etc.)
  • v0-v5: Up to 6 values per rule (Casbin's limit)

Example Policy Model

Here's a simple RBAC model:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

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

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

Save this as model.conf and you're ready to go.

Running with Docker

Need a quick ArangoDB instance for testing?

docker run -e ARANGO_ROOT_PASSWORD=password -p 8529:8529 arangodb/arangodb:latest

Then connect to http://localhost:8529 with username root and password password.

Examples

Check out the examples directory for more:

  • basic - Simple usage with RBAC
  • tls - Secure connection with TLS
  • cluster - Multi-coordinator cluster setup

Performance Tips

  1. Use batch operations - AddPolicies() is much faster than multiple AddPolicy() calls
  2. Use context timeouts - Always set reasonable timeouts with the *Ctx() methods
  3. Consider indexes - For large policy sets, add indexes on ptype and frequently queried v* fields in ArangoDB

Thread Safety

The adapter uses a mutex to protect concurrent writes, so it's safe to use from multiple goroutines.

License

MIT License - see LICENSE file for details.

Contributing

Found a bug or want to add a feature? Pull requests are welcome!

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Documentation

Overview

Package arangoadapter provides a Casbin adapter for ArangoDB. It allows you to persist authorization policies in ArangoDB instead of local files.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Adapter

type Adapter struct {
	// contains filtered or unexported fields
}

Adapter is the main struct that connects Casbin to ArangoDB. It handles all the CRUD operations for policy rules.

func NewAdapter

func NewAdapter(opts ...Option) (*Adapter, error)

NewAdapter creates a new ArangoDB adapter using functional options. It automatically creates the database and collection if they don't exist.

Example:

adapter, err := NewAdapter(
    WithEndpoints("http://localhost:8529"),
    WithAuthentication("root", "password"),
    WithDatabase("casbin"),
    WithCollection("casbin_rule"),
)

func NewAdapterFromClient

func NewAdapterFromClient(client arangodb.Client, databaseName string, collectionName string) (*Adapter, error)

NewAdapterFromClient creates a new ArangoDB adapter from an existing client. This is useful when you already have an ArangoDB client configured. It'll automatically create the database and collection if they don't exist.

func NewFilteredAdapter

func NewFilteredAdapter(opts ...Option) (*Adapter, error)

NewFilteredAdapter creates a filtered adapter that won't auto-load all policies. Casbin won't automatically call LoadPolicy() for filtered adapters. You'll need to manually call LoadFilteredPolicy() with your filter criteria.

func (*Adapter) AddPolicies

func (a *Adapter) AddPolicies(sec string, ptype string, rules [][]string) error

AddPolicies adds multiple policy rules at once.

func (*Adapter) AddPoliciesCtx

func (a *Adapter) AddPoliciesCtx(ctx context.Context, sec string, ptype string, rules [][]string) error

AddPoliciesCtx adds multiple policy rules with context support.

func (*Adapter) AddPolicy

func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error

AddPolicy adds a single policy rule to the database.

func (*Adapter) AddPolicyCtx

func (a *Adapter) AddPolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error

AddPolicyCtx is like AddPolicy but with context support.

func (*Adapter) BeginTransaction

func (a *Adapter) BeginTransaction(ctx context.Context) (persist.TransactionContext, error)

BeginTransaction starts a new database transaction. Returns a context you can use to commit or rollback.

func (*Adapter) Close

func (a *Adapter) Close() error

Close shuts down the adapter. ArangoDB handles connections internally, so this is mostly a no-op. We have it to satisfy the adapter interface.

func (*Adapter) Copy

func (a *Adapter) Copy() *Adapter

Copy creates a shallow copy of the adapter. Useful for transaction handling where we need separate adapter instances.

func (*Adapter) IsFiltered

func (a *Adapter) IsFiltered() bool

IsFiltered returns true if the loaded policy has been filtered.

func (*Adapter) LoadFilteredPolicy

func (a *Adapter) LoadFilteredPolicy(model model.Model, filter interface{}) error

LoadFilteredPolicy loads only policies that match the filter. Useful when you have millions of policies and don't want to load them all.

func (*Adapter) LoadFilteredPolicyCtx

func (a *Adapter) LoadFilteredPolicyCtx(ctx context.Context, model model.Model, filter interface{}) error

LoadFilteredPolicyCtx loads filtered policies with context support.

func (*Adapter) LoadPolicy

func (a *Adapter) LoadPolicy(model model.Model) error

LoadPolicy loads all policies from the database into the Casbin model. This is called when Casbin initializes.

func (*Adapter) LoadPolicyCtx

func (a *Adapter) LoadPolicyCtx(ctx context.Context, model model.Model) error

LoadPolicyCtx is like LoadPolicy but with context support for cancellation and timeouts.

func (*Adapter) Preview

func (a *Adapter) Preview(rules *[]CasbinRule, model model.Model) error

Preview checks which rules are valid for the model. Filters out rules that don't match, so you don't get partial load failures.

func (*Adapter) RemoveFilteredPolicy

func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error

RemoveFilteredPolicy removes policies that match a partial filter.

func (*Adapter) RemoveFilteredPolicyCtx

func (a *Adapter) RemoveFilteredPolicyCtx(ctx context.Context, sec string, ptype string, fieldIndex int, fieldValues ...string) error

RemoveFilteredPolicyCtx is like RemoveFilteredPolicy but with context support.

func (*Adapter) RemovePolicies

func (a *Adapter) RemovePolicies(sec string, ptype string, rules [][]string) error

RemovePolicies removes multiple policy rules at once.

func (*Adapter) RemovePoliciesCtx

func (a *Adapter) RemovePoliciesCtx(ctx context.Context, sec string, ptype string, rules [][]string) error

RemovePoliciesCtx removes multiple policy rules with context support.

func (*Adapter) RemovePolicy

func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error

RemovePolicy removes a single policy rule from the database.

func (*Adapter) RemovePolicyCtx

func (a *Adapter) RemovePolicyCtx(ctx context.Context, sec string, ptype string, rule []string) error

RemovePolicyCtx is like RemovePolicy but with context support. It builds a query to match the exact rule and removes it.

func (*Adapter) SavePolicy

func (a *Adapter) SavePolicy(model model.Model) error

SavePolicy saves all policies from the Casbin model back to the database. Warning: This wipes out the entire collection and replaces it with the current policy set.

func (*Adapter) SavePolicyCtx

func (a *Adapter) SavePolicyCtx(ctx context.Context, model model.Model) error

SavePolicyCtx is like SavePolicy but with context support. Uses batching to handle large policy sets efficiently.

func (*Adapter) Transaction

func (a *Adapter) Transaction(e casbin.IEnforcer, fc func(casbin.IEnforcer) error) error

Transaction executes a function within a database transaction. This is the old-style transaction interface for backward compatibility.

func (*Adapter) UpdateFilteredPolicies

func (a *Adapter) UpdateFilteredPolicies(sec string, ptype string, newPolicies [][]string, fieldIndex int, fieldValues ...string) ([][]string, error)

UpdateFilteredPolicies updates policies that match a filter. Right now it just adds the new policies - doesn't remove old ones.

func (*Adapter) UpdatePolicies

func (a *Adapter) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) error

UpdatePolicies updates multiple policy rules at once.

func (*Adapter) UpdatePolicy

func (a *Adapter) UpdatePolicy(sec string, ptype string, oldRule, newPolicy []string) error

UpdatePolicy replaces an old policy rule with a new one.

type ArangoTransactionContext

type ArangoTransactionContext struct {
	// contains filtered or unexported fields
}

ArangoTransactionContext wraps an ArangoDB transaction for Casbin.

func (*ArangoTransactionContext) Commit

func (atx *ArangoTransactionContext) Commit() error

Commit commits the database transaction.

func (*ArangoTransactionContext) GetAdapter

func (atx *ArangoTransactionContext) GetAdapter() persist.Adapter

GetAdapter returns an adapter that uses this transaction. Any policies you add/remove through it will be part of the transaction.

func (*ArangoTransactionContext) Rollback

func (atx *ArangoTransactionContext) Rollback() error

Rollback rolls back the database transaction.

type BatchFilter

type BatchFilter struct {
	// contains filtered or unexported fields
}

BatchFilter wraps multiple filters for batch operations.

type CasbinRule

type CasbinRule struct {
	Key   string `json:"_key,omitempty"` // ArangoDB document key
	Ptype string `json:"ptype"`          // Policy type (p, g, p2, g2, etc.)
	V0    string `json:"v0"`
	V1    string `json:"v1"`
	V2    string `json:"v2"`
	V3    string `json:"v3"`
	V4    string `json:"v4"`
	V5    string `json:"v5"`
}

CasbinRule represents a single policy rule in ArangoDB. Casbin supports up to 6 values per rule, so we've got V0 through V5.

type Config

type Config struct {
	Endpoints      []string    // ArangoDB endpoints (e.g., ["http://localhost:8529"])
	Username       string      // Database username
	Password       string      // Database password
	DatabaseName   string      // Name of the database to use
	CollectionName string      // Name of the collection for Casbin rules
	TLSEnabled     bool        // Whether to use TLS
	CACertPath     string      // Path to CA certificate file (for TLS)
	TLSConfig      *tls.Config // Custom TLS configuration (optional)
}

Config holds the configuration for connecting to ArangoDB.

func NewConfig

func NewConfig(opts ...Option) *Config

NewConfig creates a default configuration.

type Filter

type Filter struct {
	Ptype []string
	V0    []string
	V1    []string
	V2    []string
	V3    []string
	V4    []string
	V5    []string
}

Filter lets you query policies based on specific field values. Each field is a slice so you can match against multiple values.

type Option

type Option func(*Config)

Option is a functional option for configuring the adapter.

func WithAuthentication

func WithAuthentication(username, password string) Option

WithAuthentication sets the username and password.

func WithCollection

func WithCollection(name string) Option

WithCollection sets the collection name for Casbin rules.

func WithDatabase

func WithDatabase(name string) Option

WithDatabase sets the database name.

func WithEndpoints

func WithEndpoints(endpoints ...string) Option

WithEndpoints sets the ArangoDB endpoints.

func WithTLS

func WithTLS(caCertPath string) Option

WithTLS enables TLS and optionally sets a CA certificate path.

func WithTLSConfig

func WithTLSConfig(tlsConfig *tls.Config) Option

WithTLSConfig sets a custom TLS configuration.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL