---

````markdown
# django-org-authz

Reusable Django app for **organization-aware roles and permissions** (RBAC) with automatic multi-tenant filtering.

It provides:
- ✅ Organization, Role, Permission, and UserRole models  
- ✅ `OrgAuthzViewSet` mixin that filters data by the user’s organization(s)  
- ✅ A simple `@org_permission_required` decorator for protecting views  
- ✅ Plug-and-play integration with any Django REST API

---

## 🚀 Installation

```bash
pip install django-org-authz
````

Add to `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
    ...,
    "rest_framework",
    "org_authz",
]
```

Run migrations:

```bash
python manage.py migrate
```

---

## 🧩 Quick Start

### 1. Define your Organization (optional)

You can use the built-in `Organization` model or subclass it.

```python
# pharmacy/models.py
from org_authz.models import Organization

class Pharmacy(Organization):
    license_number = models.CharField(max_length=100, unique=True)
```

---

### 2. Create a model linked (directly or indirectly) to your organization

```python
class Invoice(models.Model):
    pharmacy = models.ForeignKey(Pharmacy, on_delete=models.CASCADE)
    total = models.DecimalField(max_digits=10, decimal_places=2)
```

---

### 3. Use the provided `OrgAuthzViewSet` mixin

It automatically filters results based on the organizations
the authenticated user is allowed to access.

```python
# pharmacy/views.py
from org_authz.views.mixins import OrgAuthzViewSet
from .models import Invoice
from .serializers import InvoiceSerializer

class InvoiceViewSet(OrgAuthzViewSet):
    queryset = Invoice.objects.all()
    serializer_class = InvoiceSerializer
```

💡 Works even for nested relationships like:

```
Invoice → Pharmacy → Organization
```

---

### 4. Register your routes

```python
# pharmacy/urls.py
from rest_framework.routers import DefaultRouter
from .views import InvoiceViewSet

router = DefaultRouter()
router.register("invoices", InvoiceViewSet)

urlpatterns = router.urls
```

---

### 5. Protect views using `@org_permission_required`

```python
from org_authz.decorators import org_permission_required

@org_permission_required("can_add_invoice")
def create_invoice(request):
    ...
```

---

## 🧠 Use Cases

| Use Case                          | Description                                                                                           |
| --------------------------------- | ----------------------------------------------------------------------------------------------------- |
| 🏥 **Pharmacies**                 | Each pharmacy has its own roles, permissions, and users. Staff can only access their pharmacy’s data. |
| 🏨 **Hotels**                     | Managers, receptionists, and cleaners have role-based access, isolated per hotel branch.              |
| 🚗 **Manufacturing (e.g. Tesla)** | Different factories or branches act as organizations, each managing its own data securely.            |
| 🏫 **Schools**                    | Teachers, admins, and students belong to their specific school organizations.                         |

---

## ⚙️ How It Works

* Users are linked to organizations via the `UserRole` model.
* Each role has assigned `Permission` objects.
* `OrgAuthzViewSet` introspects your model to find its organization relationship (direct or nested) and filters automatically.
* You can define your own `Organization` subclass per business domain.

---

## 🧱 Models Overview

| Model            | Purpose                                            |
| ---------------- | -------------------------------------------------- |
| **Organization** | Represents a company, branch, or tenant.           |
| **Role**         | Defines what a user can do within an organization. |
| **Permission**   | Represents an action (e.g. `can_add_invoice`).     |
| **UserRole**     | Connects users to roles and organizations.         |

---

## 🧪 Example Project Structure

```
myproject/
├── pharmacy/
│   ├── models.py
│   ├── views.py
│   ├── serializers.py
│   └── urls.py
├── org_authz/
│   └── (installed app)
└── settings.py
```

---

## 🪄 Example: Multi-organization setup

If your project has multiple organization types:

```python
class Hotel(Organization):
    city = models.CharField(max_length=100)

class Booking(models.Model):
    hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE)
    guest_name = models.CharField(max_length=255)
```

Then:

```python
class BookingViewSet(OrgAuthzViewSet):
    queryset = Booking.objects.all()
    serializer_class = BookingSerializer
```

`OrgAuthzViewSet` will automatically detect:

```
Booking → hotel → organization
```

and apply filtering to only return bookings for the hotels
the current user has access to.

---

## 📜 License

MIT © [Victor Mwendwa](mailto:vicmwe184@gmail.com)

---

## 🌐 Links

* **PyPI:** [django-org-authz](https://pypi.org/project/django-org-authz/)
* **GitHub:** [https://github.com/muasya-victor/django-org-authz](https://github.com/muasya-victor/django-org-authz)

