Metadata-Version: 2.4
Name: roleflow
Version: 0.1.5
Summary: A lightweight, hassle-free and production-ready RBAC (Role-Based Access Control) library.
Project-URL: Homepage, https://github.com/sougata010/roboflow
Author-email: sougata <sougatachongder8@gmail.com>
License: MIT
Requires-Python: >=3.8
Requires-Dist: pydantic>=2.0.0
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.100.0; extra == 'fastapi'
Provides-Extra: test
Requires-Dist: fastapi; extra == 'test'
Requires-Dist: httpx; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Requires-Dist: pytest-asyncio; extra == 'test'
Description-Content-Type: text/markdown

# RoleFlow

A lightweight, robust, and production-ready Role-Based Access Control (RBAC) package for Python. Designed to be simple, fast, and framework-agnostic, while featuring seamless integration out-of-the-box for FastAPI. 

RoleFlow provides enterprise-grade authorization features like **Role Inheritance**, **Multi-Role Checks**, and **Dynamic Permissions** while maintaining a hassle-free developer experience.

## Features
- **Generic RBAC Engine**: Easily verify permissions using wildcards (`*`, `table.*`) or exact matches.
- **Role Inheritance (Hierarchical RBAC)**: Roles can inherit permissions from multiple parent roles.
- **Multi-Role Checking**: Easily verify if a user with multiple roles has access.
- **Dynamic & Bulk Operations**: Grant or revoke permissions on the fly, with method chaining support.
- **Configuration Loading**: Load and export your entire RBAC state from dictionaries (JSON/YAML).
- **FastAPI Integration**: Native `RBACGuard` dependency injection for secure and hassle-free route protection.

## Installation

```bash
pip install roleflow
```

To install with FastAPI dependencies:   
```bash
pip install roleflow[fastapi]
```

## Quick Start

### 1. Define your Roles
```python
from roleflow import Role, RBACEngine

# Define roles. Note how ADMIN inherits from both USER and EDITOR
roles = [
    Role(id=1, name="ROLE_USER", permissions=["profile.read"]),
    Role(id=2, name="ROLE_EDITOR", permissions=["article.write"], parents=["ROLE_USER"]),
    Role(id=3, name="ROLE_ADMIN", permissions=["*"], parents=["ROLE_EDITOR"])
]

engine = RBACEngine(roles=roles)
```

### 2. Check Permissions (Inheritance & Wildcards)
```python
# Exact match
engine.is_granted("ROLE_USER", "profile.read") # Returns True

# Inherited from ROLE_USER
engine.is_granted("ROLE_EDITOR", "profile.read") # Returns True

# Root Wildcard (*)
engine.is_granted("ROLE_ADMIN", "anything.you.want") # Returns True

# Access Denied
engine.is_granted("ROLE_USER", "article.write") # Returns False
```

### 3. Check Access for a User with Multiple Roles
In the real world, users often have multiple roles. Use `check_access` to verify if ANY of the user's roles grant the required permission:
```python
user_roles = ["ROLE_USER", "ROLE_MODERATOR"]

if engine.check_access(user_roles, "article.delete"):
    print("User has access!")
```

### 4. Dynamic Permissions & Bulk Operations (Method Chaining)
RoleFlow supports method chaining, allowing you to manipulate permissions efficiently on the fly:
```python
(
    engine.add_role(Role(id=4, name="ROLE_STUDENT"))
          .grant_permission("ROLE_STUDENT", "exam.take")
          .grant_permissions("ROLE_STUDENT", ["course.read", "assignment.submit"])
)

# Bulk Checks
engine.has_all_permissions("ROLE_STUDENT", ["exam.take", "course.read"]) # Returns True
engine.has_any_permission("ROLE_STUDENT", ["exam.take", "course.delete"]) # Returns True

# Revoking
engine.revoke_permission("ROLE_STUDENT", "exam.take")
```

### 5. Load and Export Configuration
You can load roles directly from a configuration dictionary, making it easy to store your RBAC structure in JSON or YAML. You can also export the current state.
```python
config_data = {
    "roles": [
        {"id": 1, "name": "ROLE_MANAGER", "permissions": ["users.*"], "parents": []}
    ]
}

engine = RBACEngine().load_from_config(config_data)

# Export back to dictionary
current_state = engine.export_config()
```

### 6. Dynamic Database Loading
You don't have to provide all roles upfront. Hook into your Database ORM by passing a `role_loader` callback function.
```python
def db_role_loader(role_name: str) -> Role:
    # Query your database (e.g., SQLAlchemy) and return a Role model
    pass

engine = RBACEngine(role_loader=db_role_loader)

# Engine calls db_role_loader("ROLE_ADMIN") and caches it automatically
engine.is_granted("ROLE_ADMIN", "table1.read")
```

### 7. FastAPI Integration
```python
from fastapi import FastAPI, Depends
from roleflow.fastapi import RBACGuard

app = FastAPI()

def get_current_user_roles() -> list[str]:
    return ["ROLE_USER", "ROLE_STUDENT"] # Return list of roles from JWT/Session

guard = RBACGuard(engine=engine, role_provider=get_current_user_roles)

@app.get("/courses", dependencies=[Depends(guard("course.read"))])
def list_courses():
    return {"message": "You can read courses!"}
```
