Skip to content

Commit

Permalink
docs: update readme
Browse files Browse the repository at this point in the history
Co-authored-by: Ghaith Kdimati <[email protected]>
  • Loading branch information
2 people authored and baraka95 committed Sep 27, 2024
1 parent cbf3326 commit e8240b6
Showing 1 changed file with 41 additions and 14 deletions.
55 changes: 41 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ a package to provide row level security seamlessly to you `fastapi` app by exten
Not done yet

#### Source Code

After cloning the repo

install dependencies using poetry
Expand Down Expand Up @@ -53,6 +54,7 @@ class Item(Base):
Permissive(
condition_args={
"comparator_name": "user_id",
"comparator_source": ComparatorSource.bearerTokenPayload,
"operation": Operation.equality,
"type": ExpressionTypes.integer,
"column_name": "owner_id",
Expand All @@ -64,21 +66,24 @@ class Item(Base):
```

```python
# main.py

from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select

from rls.database import get_async_session

from rls.database import get_session
from test.models import Item

from .engines import async_engine as db_engine


app = FastAPI()

Session = Depends(get_session(db_engine))


@app.get("/users/items")
async def get_users(db: AsyncSession = Depends(get_async_session)):
async def get_users(db: AsyncSession = Session):
stmt = select(Item)
result = await db.execute(stmt)
items = result.scalars().all()
Expand All @@ -88,40 +93,60 @@ async def get_users(db: AsyncSession = Depends(get_async_session)):
```

#### Run Tests

No e2e tests done yet

## API

These functions provide a structured way to handle database sessions and enforce row-level security (RLS) in your application. If you're working with SQLAlchemy models and need a secure and efficient way to manage database access, here's how you can use them:

### `register_rls(Base: Type[DeclarativeMeta])`

This function is used to automatically apply Row-Level Security (RLS) policies to your SQLAlchemy models. From the user's perspective:

- **When to Use**: Call this function when setting up your SQLAlchemy models (typically during application initialization).
- **Purpose**: It ensures that RLS policies are applied after creating tables in the database. This helps enforce fine-grained access control, allowing you to control which rows of data users are allowed to access based on the defined policies.
- **How it Works**: It listens for table creation events and applies security policies automatically, so you don't have to manually enforce them for each table.

### `get_sync_session(request: Request)`
### `get_session(db_engine: Engine)`

This function provides a synchronous session for interacting with the database. You can use it to execute SQL statements or queries in your API routes or service functions:
- **When to Use**: Use this function when working with synchronous code, such as in FastAPI routes that do not use `async` functionality.
- **How it Works**: It creates a session, applies any necessary set statements (e.g., for tenant-specific data filtering), and provides it for use in the current request context. Once the work is done, it automatically handles cleanup (via Python's context management).
This function returns the appropriate session factory based on the type of database engine passed to it. It supports both synchronous and asynchronous engines.

### `get_async_session(request: Request)`

This function is similar to `get_sync_session`, but for asynchronous code:
- **When to Use**: Use this function when working in an asynchronous context (e.g., with `async def` routes in FastAPI).
- **How it Works**: It provides an asynchronous session that allows you to interact with the database without blocking the event loop. Like the sync version, it applies RLS policies or other session-level statements and automatically handles the session's lifecycle.
**Parameters**:
- db_engine:
- The database engine (either synchronous Engine or asynchronous AsyncEngine).

**Behavior**:
- Engine Binding:
- It binds the engine to manage database connections.
- Session Factory:
- Returns get_async_session if the engine is asynchronous.
- Returns get_sync_session if the engine is synchronous.
- Error Handling: Raises a ValueError if an invalid engine type is provided.

## Examples

using the models.py and main.py found in source code installation

you can send a request with curl for example :

```bash
curl -H "user_id: 2" localhost:8000/users/items
curl -H
"Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwibmFtZSI6Ik9tYXIgR2hhaXRoIiwiaWF0IjoxNTE2MjM5MDIyfQ.sk8xw3duwaNnLM7BwrqXmI_k2Kov3hkXLs7Mb9S6M38 "
localhost:8000/users/items
```
Note: this token payload is :
```json
{
"sub": "1",
"name": "Omar Ghaith",
"role": "admin",
"iat": 1516239022
}
```

it will return only the items owned by owner whose id is 2 as specified in the policy

```json
[
{
Expand All @@ -138,7 +163,9 @@ it will return only the items owned by owner whose id is 2 as specified in the p
}
]
```

noting that the items table contains

```json
[
{
Expand Down

0 comments on commit e8240b6

Please sign in to comment.