242 lines
5.5 KiB
Markdown
242 lines
5.5 KiB
Markdown
# ADR-010: Cedar Policy Engine para Authorization
|
|
|
|
**Status**: Accepted | Implemented
|
|
**Date**: 2024-11-01
|
|
**Deciders**: Security Architecture Team
|
|
**Technical Story**: Implementing declarative RBAC with audit-friendly policies
|
|
|
|
---
|
|
|
|
## Decision
|
|
|
|
Usar **Cedar policy engine** para autorización declarativa (no custom RBAC, no Casbin).
|
|
|
|
---
|
|
|
|
## Rationale
|
|
|
|
1. **Declarative Policies**: Separar políticas de autorización de lógica de código
|
|
2. **Auditable**: Políticas versionables en Git, fácil de revisar
|
|
3. **AWS Proven**: Usado internamente en AWS, production-proven
|
|
4. **Type Safe**: Schemas para resources y principals
|
|
5. **No Vendor Lock-in**: Open source, portable
|
|
|
|
---
|
|
|
|
## Alternatives Considered
|
|
|
|
### ❌ Custom RBAC Implementation
|
|
- **Pros**: Full control
|
|
- **Cons**: Mantenimiento pesada, fácil de introducir vulnerabilidades
|
|
|
|
### ❌ Casbin (Policy Engine)
|
|
- **Pros**: Flexible
|
|
- **Cons**: Menos maduro en Rust ecosystem que Cedar
|
|
|
|
### ✅ Cedar (CHOSEN)
|
|
- Declarative, auditable, production-proven, AWS-backed
|
|
|
|
---
|
|
|
|
## Trade-offs
|
|
|
|
**Pros**:
|
|
- ✅ Declarative policies separate from code
|
|
- ✅ Easy to audit and version control
|
|
- ✅ Type-safe schema validation
|
|
- ✅ AWS production-proven
|
|
- ✅ Support for complex hierarchies (teams, orgs)
|
|
|
|
**Cons**:
|
|
- ⚠️ Learning curve (new policy language)
|
|
- ⚠️ Policies must be pre-compiled for performance
|
|
- ⚠️ Smaller community than Casbin
|
|
|
|
---
|
|
|
|
## Implementation
|
|
|
|
**Policy Definition**:
|
|
```cedar
|
|
// policies/authorization.cedar
|
|
|
|
// Allow owners full access to projects
|
|
permit(
|
|
principal,
|
|
action,
|
|
resource
|
|
)
|
|
when {
|
|
principal.role == "owner"
|
|
};
|
|
|
|
// Allow members to create tasks
|
|
permit(
|
|
principal in [User],
|
|
action == Action::"create_task",
|
|
resource in [Project]
|
|
)
|
|
when {
|
|
principal.team_id == resource.team_id &&
|
|
principal.role in ["owner", "member"]
|
|
};
|
|
|
|
// Deny editing completed tasks
|
|
forbid(
|
|
principal,
|
|
action == Action::"update_task",
|
|
resource in [Task]
|
|
)
|
|
when {
|
|
resource.status == "done"
|
|
};
|
|
|
|
// Allow viewing with viewer role
|
|
permit(
|
|
principal,
|
|
action == Action::"read",
|
|
resource
|
|
)
|
|
when {
|
|
principal.role == "viewer"
|
|
};
|
|
```
|
|
|
|
**Authorization Check in Backend**:
|
|
```rust
|
|
// crates/vapora-backend/src/api/projects.rs
|
|
use cedar_policy::{Authorizer, Request, Entity, Entities};
|
|
|
|
async fn get_project(
|
|
State(app_state): State<AppState>,
|
|
Path(project_id): Path<String>,
|
|
) -> Result<Json<Project>, ApiError> {
|
|
let user = get_current_user()?;
|
|
|
|
// Create authorization request
|
|
let request = Request::new(
|
|
user.into_entity(),
|
|
action("read"),
|
|
resource("project", &project_id),
|
|
None,
|
|
)?;
|
|
|
|
// Load policies and entities
|
|
let policies = app_state.cedar_policies();
|
|
let entities = app_state.cedar_entities();
|
|
|
|
// Authorize
|
|
let authorizer = Authorizer::new();
|
|
let response = authorizer.is_authorized(&request, &policies, &entities)?;
|
|
|
|
match response.decision {
|
|
Decision::Allow => {
|
|
let project = app_state
|
|
.project_service
|
|
.get_project(&user.tenant_id, &project_id)
|
|
.await?;
|
|
Ok(Json(project))
|
|
}
|
|
Decision::Deny => Err(ApiError::Forbidden),
|
|
}
|
|
}
|
|
```
|
|
|
|
**Entity Schema**:
|
|
```rust
|
|
// crates/vapora-backend/src/auth/entities.rs
|
|
pub struct User {
|
|
pub id: String,
|
|
pub role: UserRole,
|
|
pub tenant_id: String,
|
|
}
|
|
|
|
pub struct Project {
|
|
pub id: String,
|
|
pub tenant_id: String,
|
|
pub status: ProjectStatus,
|
|
}
|
|
|
|
// Convert to Cedar entities
|
|
impl From<User> for cedar_policy::Entity {
|
|
fn from(user: User) -> Self {
|
|
// Serialized to Cedar format
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key Files**:
|
|
- `/crates/vapora-backend/src/auth/` (Cedar integration)
|
|
- `/crates/vapora-backend/src/api/` (authorization checks)
|
|
- `/policies/authorization.cedar` (policy definitions)
|
|
|
|
---
|
|
|
|
## Verification
|
|
|
|
```bash
|
|
# Validate policy syntax
|
|
cedar validate --schema schemas/schema.json --policies policies/authorization.cedar
|
|
|
|
# Test authorization decision
|
|
cedar evaluate \
|
|
--schema schemas/schema.json \
|
|
--policies policies/authorization.cedar \
|
|
--entities entities.json \
|
|
--request '{"principal": "User:alice", "action": "Action::read", "resource": "Project:123"}'
|
|
|
|
# Run authorization tests
|
|
cargo test -p vapora-backend test_cedar_authorization
|
|
|
|
# Test edge cases
|
|
cargo test -p vapora-backend test_forbidden_access
|
|
cargo test -p vapora-backend test_hierarchical_permissions
|
|
```
|
|
|
|
**Expected Output**:
|
|
- Policies validate without syntax errors
|
|
- Owners have full access
|
|
- Members can create tasks in their team
|
|
- Viewers can only read
|
|
- Completed tasks cannot be edited
|
|
- All tests pass
|
|
|
|
---
|
|
|
|
## Consequences
|
|
|
|
### Authorization Model
|
|
- Three roles: Owner, Member, Viewer
|
|
- Hierarchical teams (can nest permissions)
|
|
- Resource-scoped access (per project, per task)
|
|
- Audit trail of policy decisions
|
|
|
|
### Policy Management
|
|
- Policies versioned in Git
|
|
- Policy changes require code review
|
|
- Centralized policy repository
|
|
- No runtime policy compilation (pre-compiled)
|
|
|
|
### Performance
|
|
- Policy evaluation cached (policies don't change often)
|
|
- Entity resolution cached per request
|
|
- Negligible latency overhead (<1ms)
|
|
|
|
### Scaling
|
|
- Policies apply across all services
|
|
- Cedar policies portable to other services
|
|
- Centralized policy management
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- [Cedar Policy Language Documentation](https://docs.cedarpolicy.com/)
|
|
- [Cedar GitHub Repository](https://github.com/aws/cedar)
|
|
- `/policies/authorization.cedar` (policy definitions)
|
|
- `/crates/vapora-backend/src/auth/` (integration code)
|
|
|
|
---
|
|
|
|
**Related ADRs**: ADR-009 (Istio), ADR-025 (Multi-Tenancy)
|