- Fix ACL test assumptions about browse() behavior * Update tests to check field-level access instead of browse().exists() * Correct test methodology for AccessError validation - Implement buddy method pattern for RPC security * Refactor all sudo()/api.model methods to use public/private pattern * Public methods perform access checks, private methods contain privileged ops * Prevents RPC privilege escalation vulnerabilities - Enhanced security architecture * Add comprehensive mail activity portal access rules * Strengthen partner access controls * Update controller method references to use secure public methods - Test suite improvements * Add extensive mail activity portal access tests * Update player removal tests to use public method interfaces * Improve test coverage for security scenarios - Security best practices enforcement * All privileged operations now encapsulated in private methods * Clear separation between public API and internal operations * Maintains functionality while securing RPC access Resolves RPC security vulnerabilities and establishes proper ACL enforcement patterns.
157 lines
6.7 KiB
Python
157 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from odoo.tests.common import TransactionCase
|
|
from odoo import fields
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
class TestMailMessageDebug(TransactionCase):
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
# Create test data similar to the failing test
|
|
self.therapist_user = self.env['res.users'].create({
|
|
'name': 'Test Therapist',
|
|
'login': 'therapist@test.com',
|
|
'email': 'therapist@test.com',
|
|
'groups_id': [(6, 0, [self.env.ref('bemade_sports_clinic.group_portal_treatment_professional').id])]
|
|
})
|
|
|
|
# Create team and add therapist as staff
|
|
self.team = self.env['sports.team'].create({
|
|
'name': 'Test Team',
|
|
'sport': 'football',
|
|
})
|
|
|
|
self.env['sports.team.staff'].create({
|
|
'team_id': self.team.id,
|
|
'partner_id': self.therapist_user.partner_id.id,
|
|
'role': 'therapist',
|
|
})
|
|
|
|
# Create authorized patient
|
|
self.authorized_patient = self.env['sports.patient'].create({
|
|
'first_name': 'John',
|
|
'last_name': 'Doe',
|
|
'team_ids': [(6, 0, [self.team.id])],
|
|
})
|
|
|
|
# Create activity type
|
|
self.patient_activity_type = self.env['mail.activity.type'].create({
|
|
'name': 'Patient Activity',
|
|
'res_model_id': self.env['ir.model']._get_id('sports.patient'),
|
|
})
|
|
|
|
def test_debug_mail_message_access(self):
|
|
"""Debug test to understand mail.message access control"""
|
|
|
|
_logger.info("=== DEBUGGING MAIL.MESSAGE ACCESS ===")
|
|
|
|
# Step 1: Create activity and complete it to generate message
|
|
_logger.info("Step 1: Creating and completing activity...")
|
|
activity = self.env['mail.activity'].create({
|
|
'activity_type_id': self.patient_activity_type.id,
|
|
'summary': 'Test activity for messages',
|
|
'user_id': self.therapist_user.id,
|
|
'date_deadline': fields.Date.today(),
|
|
'res_model_id': self.env['ir.model']._get_id('sports.patient'),
|
|
'res_id': self.authorized_patient.id,
|
|
})
|
|
|
|
# Complete activity to generate message
|
|
activity.action_feedback(feedback='Activity completed successfully')
|
|
|
|
# Step 2: Check if message was created
|
|
_logger.info("Step 2: Checking if message was created...")
|
|
all_messages = self.env['mail.message'].sudo().search([
|
|
('model', '=', 'sports.patient'),
|
|
('res_id', '=', self.authorized_patient.id)
|
|
])
|
|
_logger.info(f"Found {len(all_messages)} messages for patient {self.authorized_patient.id}")
|
|
|
|
if all_messages:
|
|
message = all_messages[0]
|
|
_logger.info(f"Message details: ID={message.id}, model={message.model}, res_id={message.res_id}, author_id={message.author_id.id}")
|
|
|
|
# Step 3: Test patient access as therapist
|
|
_logger.info("Step 3: Testing patient access as therapist...")
|
|
patient_env = self.env['sports.patient'].with_user(self.therapist_user)
|
|
accessible_patients = patient_env.search([('id', '=', self.authorized_patient.id)])
|
|
_logger.info(f"Therapist can access {len(accessible_patients)} patients (should be 1)")
|
|
|
|
if accessible_patients:
|
|
_logger.info(f"Patient accessible: {accessible_patients[0].name}")
|
|
else:
|
|
_logger.error("PROBLEM: Therapist cannot access authorized patient!")
|
|
|
|
# Step 4: Test message access as therapist using different methods
|
|
_logger.info("Step 4: Testing message access as therapist...")
|
|
message_env = self.env['mail.message'].with_user(self.therapist_user)
|
|
|
|
# Method 1: Direct search
|
|
messages_direct = message_env.search([
|
|
('model', '=', 'sports.patient'),
|
|
('res_id', '=', self.authorized_patient.id)
|
|
])
|
|
_logger.info(f"Direct search found {len(messages_direct)} messages")
|
|
|
|
# Method 2: Browse specific message ID
|
|
if all_messages:
|
|
message_browse = message_env.browse(all_messages[0].id)
|
|
_logger.info(f"Browse message exists: {message_browse.exists()}")
|
|
|
|
# Method 3: Check access manually
|
|
try:
|
|
message_browse.check_access('read')
|
|
_logger.info("Manual check_access('read') passed")
|
|
except Exception as e:
|
|
_logger.error(f"Manual check_access('read') failed: {e}")
|
|
|
|
# Step 5: Debug record rule evaluation
|
|
_logger.info("Step 5: Debugging record rule evaluation...")
|
|
|
|
# Check team staff relationships
|
|
team_staff_rels = self.therapist_user.partner_id.team_staff_rel_ids
|
|
_logger.info(f"Therapist has {len(team_staff_rels)} team staff relationships")
|
|
|
|
if team_staff_rels:
|
|
team_ids = team_staff_rels.mapped('team_id')
|
|
_logger.info(f"Therapist is staff on teams: {team_ids.mapped('name')}")
|
|
|
|
patient_ids = team_staff_rels.mapped('team_id.patient_ids.id')
|
|
_logger.info(f"Accessible patient IDs through teams: {patient_ids}")
|
|
|
|
# Check if our patient is in the list
|
|
if self.authorized_patient.id in patient_ids:
|
|
_logger.info("✓ Authorized patient is in accessible patient list")
|
|
else:
|
|
_logger.error("✗ Authorized patient is NOT in accessible patient list")
|
|
|
|
# Step 6: Test mail.message record rule domain manually
|
|
_logger.info("Step 6: Testing mail.message record rule domain manually...")
|
|
|
|
# Simulate the record rule domain
|
|
domain = [
|
|
'|',
|
|
# Messages on patients they have access to through teams
|
|
'&',
|
|
('model', '=', 'sports.patient'),
|
|
('res_id', 'in', self.therapist_user.partner_id.team_staff_rel_ids.mapped('team_id.patient_ids.id') or [0]),
|
|
'|',
|
|
# Messages on injuries they have access to through teams
|
|
'&',
|
|
('model', '=', 'sports.patient.injury'),
|
|
('res_id', 'in', self.therapist_user.partner_id.team_staff_rel_ids.mapped('team_id.patient_ids.injury_ids.id') or [0]),
|
|
# Messages authored by the user
|
|
('author_id', '=', self.therapist_user.partner_id.id)
|
|
]
|
|
|
|
manual_messages = message_env.search(domain)
|
|
_logger.info(f"Manual domain search found {len(manual_messages)} messages")
|
|
|
|
_logger.info("=== END DEBUG ===")
|
|
|
|
# Final assertion to see what happens
|
|
self.assertTrue(len(messages_direct) > 0, "Should find messages with direct search")
|