SQLAlchemy Adapter for PyCasbin
SQLAlchemy Adapter for PyCasbin enables Casbin, a powerful authorization library, to store and load policy rules from any SQLAlchemy-supported relational database. This includes popular databases like PostgreSQL, MySQL, SQLite, Oracle, Microsoft SQL Server, Firebird, and Sybase. The library is actively maintained, currently at version 1.9.0, and has a frequent release cadence, with multiple feature and bugfix releases in late 2025.
Warnings
- breaking Version 1.5.0 switched its core dependency and internal implementation from the older `casbin` library to `pycasbin`. Users migrating from `casbin-sqlalchemy-adapter` or older versions of `sqlalchemy-adapter` that implicitly relied on `casbin` must now ensure `pycasbin` is installed and update their imports to `from sqlalchemy_adapter import Adapter`.
- gotcha When initializing the `Adapter` with `create_table=False`, the library will not automatically create the `casbin_rule` table. It is then the user's responsibility to ensure the necessary policy tables exist in the database, typically via a separate database migration system like Alembic.
- gotcha Older versions of the adapter (prior to 1.9.0) might produce SQLAlchemy warnings related to duplicate `CasbinRule` class names if `db_class` was customized or specific SQLAlchemy configurations were in place. This has been addressed in v1.9.0.
- gotcha Prior to version 1.7.0, using a custom `db_class` with the adapter could inadvertently alter the global SQLAlchemy metadata state. This bug was fixed in v1.7.0.
Install
-
pip install sqlalchemy-adapter
Imports
- Adapter
from sqlalchemy_adapter import Adapter
Quickstart
import os
import casbin
import tempfile
from sqlalchemy_adapter import Adapter
# Create a dummy model.conf for the Enforcer
model_conf_content = '''
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
'''
# Write the model content to a temporary file
with tempfile.NamedTemporaryFile(mode='w+', delete=False, suffix=".conf") as model_file:
model_file.write(model_conf_content)
model_path = model_file.name
# Use an in-memory SQLite database for a simple, runnable example.
# For persistent storage, provide a file path or a full database URL
# e.g., 'sqlite:///test.db' or from an environment variable.
database_url = os.environ.get('SQLALCHEMY_DATABASE_URL', 'sqlite:///:memory:')
try:
# 1. Initialize the SQLAlchemy Adapter
# By default, create_table=True, so tables will be created if they don't exist.
adapter = Adapter(database_url)
# 2. Initialize Casbin Enforcer with the adapter
e = casbin.Enforcer(model_path, adapter)
# 3. Add policies
e.add_policy("alice", "data1", "read")
e.add_policy("bob", "data2", "write")
# 4. Save policies to the database (persists changes through the adapter)
e.save_policy()
print("Policies added and saved.")
# 5. Enforce policies
print(f"Alice can read data1: {e.enforce('alice', 'data1', 'read')}") # Expected: True
print(f"Bob can write data2: {e.enforce('bob', 'data2', 'write')}") # Expected: True
print(f"Alice can write data1: {e.enforce('alice', 'data1', 'write')}") # Expected: False
# 6. Load policies (useful if Enforcer was re-initialized or policy was changed elsewhere)
e.load_policy()
print(f"After reloading, Alice can read data1: {e.enforce('alice', 'data1', 'read')}") # Expected: True
except Exception as ex:
print(f"An error occurred: {ex}")
finally:
# Clean up the temporary model file
os.remove(model_path)