bemade-addons/bemade_sports_clinic/PROJECT_TASK_PORTAL_SECURITY.md

201 lines
7.5 KiB
Markdown
Raw Normal View History

# Project Task Portal Security Architecture
## Overview
This document outlines the comprehensive security implementation for portal treatment professionals accessing project.task objects (events) in the bemade_sports_clinic module.
## Security Principles Applied
### 1. **Explicit Authorization Only**
- **NO** blanket access based on `privacy_visibility = 'portal'`
- **YES** explicit authorization through follower relationships or team membership
- **Principle**: Users must be explicitly granted access, not implicitly through project settings
### 2. **Multi-Layer Security Architecture**
1. **ACL Level**: Model-level CRUD permissions
2. **Record Rule Level**: Record-level filtering based on explicit relationships
3. **Field Level**: Field-level group access controls
4. **Controller Level**: Additional business logic validation
## Implementation Details
### Access Control Lists (ACLs)
**File**: `security/ir.model.access.csv`
```csv
# Portal Treatment Professionals get read/write/create access to project tasks
access_project_task_portal_tp,Portal TP Access for Project Tasks,project.model_project_task,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
# Portal Treatment Professionals get read/write access to projects (for follower management)
access_project_project_portal_tp,Portal TP Access for Projects,project.model_project_project,bemade_sports_clinic.group_portal_treatment_professional,1,1,0,0
# Read-only access to supporting models
access_project_task_type_portal_tp,Portal TP Access for Task Types,project.model_project_task_type,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_project_tags_portal_tp,Portal TP Access for Project Tags,project.model_project_tags,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_project_milestone_portal_tp,Portal TP Access for Project Milestones,project.model_project_milestone,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
```
### Record Rules
**File**: `security/project_task_portal_rules.xml`
#### Project Task Access Rule
```xml
<field name="domain_force">[
'|', '|', '|',
# Tasks assigned to the user
('user_ids', 'in', [user.id]),
# Tasks where user is a follower
('message_partner_ids', 'in', [user.partner_id.id]),
# Tasks from projects where user is a follower
('project_id.message_partner_ids', 'in', [user.partner_id.id]),
# Tasks from projects where user's teams are partners
('project_id.partner_id', 'in', user.partner_id.team_staff_rel_ids.mapped('team_id.id') or [0])
]
```
#### Project Access Rule
```xml
<field name="domain_force">[
'|',
# Projects where user is explicitly a follower
('message_partner_ids', 'in', [user.partner_id.id]),
# Projects where user's teams are partners
('partner_id', 'in', user.partner_id.team_staff_rel_ids.mapped('team_id.id') or [0])
]
```
### Field-Level Security
**Files**: `models/project_task.py` and `models/project_project.py`
All critical fields are overridden with explicit group access for authorized sports clinic users only:
```python
# SECURE: Only authorized sports clinic portal users, not all portal users
_portal_groups = 'base.group_user,bemade_sports_clinic.group_portal_treatment_professional,bemade_sports_clinic.group_portal_team_coach'
# Core fields
name = fields.Char(groups=_portal_groups)
description = fields.Html(groups=_portal_groups)
user_ids = fields.Many2many(groups=_portal_groups)
project_id = fields.Many2one(groups=_portal_groups)
# ... and many more
```
### Controller Security
**File**: `controllers/events_portal.py`
#### Secure Domain Construction
```python
def _prepare_events_domain(self, view_type='all'):
# Base domain: tasks from projects where user has explicit authorization
base_domain = [
'|', '|',
# Tasks from projects where user's teams are partners
('project_id.partner_id', 'in', team_ids or [0]),
# Tasks from projects where user is a follower
('project_id.message_partner_ids', 'in', [partner.id]),
# Tasks where user is directly assigned or following
'|',
('user_ids', 'in', [user.id]),
('message_partner_ids', 'in', [partner.id])
]
```
#### Project Filtering
```python
def _get_available_projects(self):
# Only show projects where user has explicit authorization
domain = [
'|',
# Projects where user's teams are partners
('partner_id', 'in', team_ids or [0]),
# Projects where user is explicitly a follower
('message_partner_ids', 'in', [partner.id])
]
```
## Security Validation
### Model-Level Access Checking
```python
def check_portal_task_access(self):
# 1. Check user is assigned to task
# 2. Check user is follower of task
# 3. Check user is follower of project
# 4. Check user's teams are partners of project
# NO blanket portal visibility check
```
### Project Configuration
```python
def ensure_portal_access_for_treatment_professionals(self):
# Only add treatment professionals who are staff on the team
# NO blanket addition of all portal users
```
## Critical Security Fixes Applied
### ❌ **BEFORE (Vulnerable)**
```python
# SECURITY FLAW: Any portal user could access any portal-visible project
domain = [
'|', '|',
('partner_id', 'in', team_ids),
('message_partner_ids', 'in', [partner.id]),
('privacy_visibility', '=', 'portal') # ← VULNERABILITY
]
```
### ✅ **AFTER (Secure)**
```python
# SECURE: Only explicitly authorized users can access projects
domain = [
'|',
('partner_id', 'in', team_ids or [0]), # Team relationship
('message_partner_ids', 'in', [partner.id]) # Explicit follower
]
```
## Testing and Validation
### Security Test Utility
**File**: `models/project_task_security_test.py`
- Admin UI for testing field access
- Validates all security layers
- Tests unauthorized access scenarios
### Comprehensive Test Suite
**File**: `tests/test_project_task_portal_security.py`
- 10 test cases covering all security aspects
- Validates proper access isolation
- Tests record rule enforcement
## Best Practices Established
1. **Explicit Authorization Required**: Never grant access based solely on project privacy settings
2. **Follower-Based Security**: Use message_partner_ids for explicit access control
3. **Team-Based Authorization**: Link project access to sports team relationships
4. **Layered Security**: Multiple security layers working together
5. **Principle of Least Privilege**: Grant minimum necessary permissions
## Common Security Anti-Patterns Avoided
1. **❌ Portal Visibility OR Condition**: `('privacy_visibility', '=', 'portal')` as standalone OR
2. **❌ Blanket Group Access**: Adding all portal users as followers
3. **❌ Overly Broad Domains**: Using `[(1, '=', 1)]` or similar catch-all domains
4. **❌ Missing Fallbacks**: Not using `or [0]` for empty list protection
## Deployment Checklist
- [ ] Update module to install new security files
- [ ] Run security tests to validate implementation
- [ ] Verify portal users can only access authorized projects/tasks
- [ ] Test that unauthorized users are properly denied access
- [ ] Validate field-level access works correctly in portal UI
## Maintenance Guidelines
1. **Always use explicit authorization** when creating new access rules
2. **Test security boundaries** whenever adding new portal functionality
3. **Document security decisions** for future developers
4. **Regular security audits** of domain logic and record rules
5. **Follow the established patterns** for consistent security implementation