Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion docs/security/authorization.md
Original file line number Diff line number Diff line change
@@ -1 +1,42 @@
# Coming Soon
# Authorization

Authorization is a crucial security feature in Ellar that determines what resources authenticated users can access. Ellar provides a flexible and powerful authorization system through policies, roles, and claims.

## Table of Contents

1. [Basic Authorization](#basic-authorization)
2. [Policies](./authorization/policies.md)
3. [Role-Based Authorization](./authorization/role-based.md)
4. [Claims-Based Authorization](./authorization/claims-based.md)
5. [Custom Policies with Requirements](./authorization/custom-policies.md)
6. [Combining Policies](./authorization/combining-policies.md)

## Basic Authorization

To use authorization in your Ellar application, you need to:

1. Decorate your controllers or routes with `@Authorize()`
2. Apply specific policies using `@CheckPolicies()`
3. Ensure users are authenticated using `@AuthenticationRequired()`

Here's a basic example:

```python
from ellar.auth import AuthenticationRequired, Authorize, CheckPolicies
from ellar.common import Controller, get

@Controller("/articles")
@Authorize() # Enable authorization for all routes
@AuthenticationRequired() # Require authentication
class ArticleController:
@get("/admin")
@CheckPolicies(RolePolicy("admin")) # Only allow admins
async def admin_dashboard(self):
return "Admin Dashboard"

@get("/public")
async def public_articles(self): # Accessible to any authenticated user
return "Public Articles"
```

For detailed information about specific authorization features, please refer to the respective sections in the documentation.
141 changes: 141 additions & 0 deletions docs/security/authorization/claims-based.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Claims-Based Authorization

Claims-based authorization provides a more flexible and granular approach to authorization compared to role-based authorization. Claims are key-value pairs that represent attributes of the user and their access rights.

## Using ClaimsPolicy

Ellar provides the `ClaimsPolicy` class for implementing claims-based authorization:

```python
from ellar.auth import AuthenticationRequired, Authorize, CheckPolicies
from ellar.auth.policy import ClaimsPolicy
from ellar.common import Controller, get

@Controller("/articles")
@Authorize()
class ArticleController:
@get("/create")
@CheckPolicies(ClaimsPolicy("article", "create"))
async def create_article(self):
return "Create Article"

@get("/publish")
@CheckPolicies(ClaimsPolicy("article", "create", "publish"))
async def publish_article(self):
return "Publish Article"
```

## How ClaimsPolicy Works

The `ClaimsPolicy` checks if the user has specific claim values for a given claim type. Claims are typically stored in the user's identity:

```python
# Example user data structure with claims
user_data = {
"id": "123",
"username": "john_doe",
"article": ["create", "read", "publish"], # Claim type: "article" with multiple values
"subscription": "premium" # Claim type: "subscription" with single value
}
```

## Single vs Multiple Claim Values

Claims can have single or multiple values:

```python
@Controller("/content")
@Authorize()
class ContentController:
@get("/premium")
@CheckPolicies(ClaimsPolicy("subscription", "premium")) # Single claim value
async def premium_content(self):
return "Premium Content"

@get("/manage")
@CheckPolicies(ClaimsPolicy("permissions", "create", "edit", "delete")) # Multiple claim values
async def manage_content(self):
return "Content Management"
```

## Combining Claims Policies

You can combine multiple claims policies using logical operators:

```python
@Controller("/advanced")
@Authorize()
class AdvancedController:
@get("/editor")
@CheckPolicies(
ClaimsPolicy("article", "edit") &
ClaimsPolicy("status", "active")
)
async def editor_dashboard(self):
return "Editor Dashboard"

@get("/moderator")
@CheckPolicies(
ClaimsPolicy("content", "moderate") |
ClaimsPolicy("role", "admin")
)
async def moderator_dashboard(self):
return "Moderator Dashboard"
```

## Best Practices

1. Use descriptive claim types and values
2. Keep claim values simple and atomic
3. Use claims for fine-grained permissions
4. Consider using claims instead of roles for more flexible authorization
5. Document your claim types and their possible values

## Example: E-commerce Authorization

Here's a comprehensive example showing claims-based authorization in an e-commerce application:

```python
@Controller("/store")
@Authorize()
class StoreController:
@get("/products")
@CheckPolicies(ClaimsPolicy("store", "view_products"))
async def view_products(self):
return "Product List"

@get("/products/manage")
@CheckPolicies(
ClaimsPolicy("store", "manage_products") &
ClaimsPolicy("account_status", "verified")
)
async def manage_products(self):
return "Product Management"

@get("/orders")
@CheckPolicies(
ClaimsPolicy("store", "view_orders") |
ClaimsPolicy("role", "customer_service")
)
async def view_orders(self):
return "Order List"

@get("/reports")
@CheckPolicies(
ClaimsPolicy("store", "view_reports") &
(ClaimsPolicy("role", "manager") | ClaimsPolicy("permissions", "analytics"))
)
async def view_reports(self):
return "Store Reports"
```

## Claims vs Roles

While roles are a form of claims, dedicated claims offer several advantages:

1. **Granularity**: Claims can represent specific permissions rather than broad role categories
2. **Flexibility**: Claims can be easily added or modified without changing role structures
3. **Clarity**: Claims directly express what a user can do rather than implying it through roles
4. **Scalability**: Claims can grow with your application's needs without role explosion

For complex authorization scenarios, consider combining claims with roles and custom policies. See [Combining Policies](./combining-policies.md) for more information.
204 changes: 204 additions & 0 deletions docs/security/authorization/combining-policies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Combining Policies

Ellar provides powerful operators to combine different types of policies for complex authorization scenarios. This guide shows you how to use these combinations effectively.

## Basic Policy Operators

Ellar supports three logical operators for combining policies:

- `&` (AND): Both policies must return `True`
- `|` (OR): At least one policy must return `True`
- `~` (NOT): Inverts the policy result

## Simple Combinations

Here are basic examples of combining policies:

```python
from ellar.auth import AuthenticationRequired, Authorize, CheckPolicies
from ellar.auth.policy import RolePolicy, ClaimsPolicy
from ellar.common import Controller, get

@Controller("/examples")
@Authorize()
@AuthenticationRequired()
class ExampleController:
@get("/and-example")
@CheckPolicies(RolePolicy("admin") & RolePolicy("editor"))
async def requires_both_roles(self):
return "Must be both admin and editor"

@get("/or-example")
@CheckPolicies(RolePolicy("admin") | RolePolicy("moderator"))
async def requires_either_role(self):
return "Must be either admin or moderator"

@get("/not-example")
@CheckPolicies(~RolePolicy("banned"))
async def not_banned(self):
return "Access allowed if not banned"
```

## Complex Combinations

You can create more complex authorization rules by combining multiple policies:

```python
@Controller("/advanced")
@Authorize()
@AuthenticationRequired()
class AdvancedController:
@get("/complex")
@CheckPolicies(
(RolePolicy("editor") & ClaimsPolicy("department", "content")) |
RolePolicy("admin")
)
async def complex_access(self):
return "Complex access rules"

@get("/nested")
@CheckPolicies(
RolePolicy("user") &
(ClaimsPolicy("subscription", "premium") | RolePolicy("staff")) &
~RolePolicy("restricted")
)
async def nested_rules(self):
return "Nested policy rules"
```

## Combining Different Policy Types

You can mix and match different types of policies:

```python
from ellar.auth import RolePolicy, ClaimsPolicy
from ellar.common import Controller, get
from .custom_policies import AgeRequirementPolicy, TeamMemberPolicy

@Controller("/mixed")
@Authorize()
class MixedPolicyController:
@get("/content")
@CheckPolicies(
(AgeRequirementPolicy[18] & ClaimsPolicy("region", "US", "CA")) |
RolePolicy("global_admin")
)
async def age_and_region(self):
return "Age and region restricted content"

@get("/team-access")
@CheckPolicies(
TeamMemberPolicy["engineering"] &
(RolePolicy("developer") | RolePolicy("team_lead")) &
ClaimsPolicy("security_clearance", "level2")
)
async def team_access(self):
return "Team-specific access"
```

## Real-World Examples

Here are some practical examples of policy combinations:

### Content Management System

```python
@Controller("/cms")
@Authorize()
class CMSController:
@get("/articles/{id}/edit")
@CheckPolicies(
(RolePolicy("editor") & ClaimsPolicy("article", "edit")) |
RolePolicy("admin") |
(TeamMemberPolicy["content"] & ClaimsPolicy("article", "edit"))
)
async def edit_article(self):
return "Edit Article"

@get("/articles/{id}/publish")
@CheckPolicies(
(RolePolicy("editor") & ClaimsPolicy("article", "publish") & ~RolePolicy("junior")) |
RolePolicy("senior_editor") |
RolePolicy("admin")
)
async def publish_article(self):
return "Publish Article"
```

### E-commerce Platform

```python
@Controller("/store")
@Authorize()
class StoreController:
@get("/products/{id}/manage")
@CheckPolicies(
(RolePolicy("vendor") & ClaimsPolicy("store", "manage_products")) |
RolePolicy("store_admin")
)
async def manage_product(self):
return "Manage Product"

@get("/orders/{id}/refund")
@CheckPolicies(
(RolePolicy("support") & ClaimsPolicy("order", "refund") & AgeRequirementPolicy[21]) |
RolePolicy("finance_admin")
)
async def process_refund(self):
return "Process Refund"
```

## Best Practices

1. **Readability**
- Use parentheses to make complex combinations clear
- Break long policy combinations into multiple lines
- Consider creating custom policies for very complex rules

2. **Performance**
- Order OR conditions with the most likely to succeed first
- Order AND conditions with the least expensive to evaluate first
- Consider caching policy results for expensive evaluations

3. **Maintenance**
- Document complex policy combinations
- Create reusable policy combinations for common patterns
- Keep policy logic modular and testable

4. **Security**
- Always start with the principle of least privilege
- Use OR combinations carefully as they broaden access
- Regularly audit policy combinations for security implications

## Common Patterns

Here are some common patterns for combining policies:

```python
# Role hierarchy
base_access = RolePolicy("user")
elevated_access = base_access & RolePolicy("premium")
admin_access = elevated_access & RolePolicy("admin")

# Feature access with fallback
feature_access = (
ClaimsPolicy("feature", "beta") & RolePolicy("beta_tester")
) | RolePolicy("admin")

# Geographic restrictions with age verification
regional_access = (
AgeRequirementPolicy[21] &
ClaimsPolicy("region", "US", "CA")
) | RolePolicy("global_access")

# Team-based access with role requirements
team_access = (
TeamMemberPolicy["project-x"] &
(RolePolicy("developer") | RolePolicy("designer"))
) & ~RolePolicy("restricted")
```

For more specific examples of each policy type, refer to:
- [Role-Based Authorization](./role-based.md)
- [Claims-Based Authorization](./claims-based.md)
- [Custom Policies with Requirements](./custom-policies.md)
Loading
Loading