- Added integrated mail.activity system for task management - Implemented portal access to activities for treatment professionals - Added activity creation, completion, and reassignment functionality - Enhanced player management with Canadian address validation - Improved injury tracking with parental consent and document attachments - Implemented layered security architecture (ACL + Record Rules + Controller filtering) - Added French Canadian (fr_CA) localization support - Enhanced portal UI with activity counts and navigation - Implemented RPC security protection with buddy method pattern - Added comprehensive demo data and integration features - Updated manifest to reflect all new capabilities and improvements
7.5 KiB
7.5 KiB
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
- ACL Level: Model-level CRUD permissions
- Record Rule Level: Record-level filtering based on explicit relationships
- Field Level: Field-level group access controls
- Controller Level: Additional business logic validation
Implementation Details
Access Control Lists (ACLs)
File: security/ir.model.access.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
<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
<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:
# 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
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
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
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
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)
# 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)
# 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
- Explicit Authorization Required: Never grant access based solely on project privacy settings
- Follower-Based Security: Use message_partner_ids for explicit access control
- Team-Based Authorization: Link project access to sports team relationships
- Layered Security: Multiple security layers working together
- Principle of Least Privilege: Grant minimum necessary permissions
Common Security Anti-Patterns Avoided
- ❌ Portal Visibility OR Condition:
('privacy_visibility', '=', 'portal')as standalone OR - ❌ Blanket Group Access: Adding all portal users as followers
- ❌ Overly Broad Domains: Using
[(1, '=', 1)]or similar catch-all domains - ❌ 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
- Always use explicit authorization when creating new access rules
- Test security boundaries whenever adding new portal functionality
- Document security decisions for future developers
- Regular security audits of domain logic and record rules
- Follow the established patterns for consistent security implementation