Row-Level Security (RLS) in SQLAlchemy for PostgreSQL with Row Security Policies:
- Restrict access to specific rows 🔒 minimizing unauthorized data exposure.
- Perfect for Scalability and Multi-Tenancy: keep the data playground organized 🏢, ensuring each tenant plays in their own sandbox.
Warning Understand that the database superuser bypasses all permission checks, except the right to log in. This is a dangerous privilege and should not be used in combination with RLS.
Use pip to install from PyPI:
pip install fastapi-rowsecurity
In your SQLAlchemy model, create an attribute named __rls_policies__
that is a list of Permissive
or Restrictive
policies:
from fastapi_rowsecurity import Permissive, register_rls
from fastapi_rowsecurity.principals import Authenticated, UserOwner
Base = declarative_base()
register_rls(Base) # <- create all policies
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True)
title = Column(String, index=True)
owner_id = Column(Integer, ForeignKey("users.id"))
owner = relationship("User", back_populates="items")
__rls_policies__ = [
Permissive(expr=Authenticated, cmd="SELECT"),
Permissive(expr=UserOwner, cmd=["INSERT", "UPDATE", "DELETE"]),
]
The above implies that any authenticated user can read all items; but can only insert, update or delete owned items.
expr
: any Boolean expression as a string;cmd
: any command ofALL
/SELECT
/INSERT
/UPDATE
/DELETE
.
Next, attach the current_user_id
(or other runtime parameters that you need) to the user session:
# ... def get_session() -> Session:
session.execute(text(f"SET app.current_user_id = {current_user_id}"))
Find a simple example in the .
- Change policies when model changes (prio!!)
- Documentation
then ...
- Support for Alembic
- When item is tried to delete, no error is raised?
- Python 3.11
- Coverage report
At the moment this module is work-in-progress and therefore experimental. All feedback and ideas are 100% welcome! So feel free to contribute or reach out to me!