attempted fix for access rights issues.
This commit is contained in:
parent
75e986a4a2
commit
9ed035a938
8 changed files with 203 additions and 61 deletions
|
|
@ -18,7 +18,7 @@
|
|||
#
|
||||
{
|
||||
"name": "Sports Clinic Management",
|
||||
"version": "16.0.1.6.0",
|
||||
"version": "16.0.1.7.0",
|
||||
"summary": "Manage the patients of a sports medicine clinic.",
|
||||
"description": """
|
||||
Adds the notion of sports teams, players (patients), coaches and treatment
|
||||
|
|
@ -38,6 +38,11 @@
|
|||
"website": "https://www.bemade.org",
|
||||
"license": "OPL-1",
|
||||
"depends": ["portal", "contacts"],
|
||||
"external_dependencies": {
|
||||
"python": [
|
||||
"openupgradelib",
|
||||
],
|
||||
},
|
||||
"data": [
|
||||
"security/sports_clinic_groups.xml",
|
||||
"security/ir.model.access.csv",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
""" Patient access revised to make the team staff relationship central to
|
||||
access. Everything is calculated from there. Inverse functions deal with
|
||||
sports.team.staff records instead of having a separate table for storing
|
||||
access rights."""
|
||||
def migrate(cr, version):
|
||||
cr.execute("DROP TABLE sports_team_res_users_rel")
|
||||
10
bemade_sports_clinic/migrations/16.0.1.7.0/pre-migrate.py
Normal file
10
bemade_sports_clinic/migrations/16.0.1.7.0/pre-migrate.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import openupgradelib.openupgrade as ou
|
||||
from odoo import SUPERUSER_ID, api
|
||||
|
||||
def migrate(cr, version):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
ou.delete_records_safely_by_xml_id(env, [
|
||||
"bemade_sports_clinic.restrict_team_access_to_allowed_internal_users",
|
||||
"bemade_sports_clinic.restrict_patient_access_to_allowed_internal_users",
|
||||
"bemade_sports_clinic.restrict_injury_access_to_allowed_internal_users",
|
||||
])
|
||||
|
|
@ -1,22 +1,48 @@
|
|||
from odoo import models, fields, api, _
|
||||
from odoo import models, fields, api, _, Command
|
||||
|
||||
|
||||
class User(models.Model):
|
||||
_inherit = 'res.users'
|
||||
_inherit = "res.users"
|
||||
|
||||
is_treatment_professional = fields.Boolean(
|
||||
compute="_compute_is_treatment_professional", store=True)
|
||||
compute="_compute_is_treatment_professional", store=True
|
||||
)
|
||||
|
||||
accessible_team_ids = fields.Many2many(
|
||||
comodel_name="sports.team",
|
||||
relation="sports_team_res_users_rel",
|
||||
column1="user_id",
|
||||
column2="team_id",
|
||||
string="Accessible Sports Teams",
|
||||
compute="_compute_accessible_team_ids",
|
||||
inverse="_inverse_accessible_team_ids",
|
||||
)
|
||||
|
||||
@api.depends('groups_id')
|
||||
@api.depends("groups_id")
|
||||
def _compute_is_treatment_professional(self):
|
||||
for rec in self:
|
||||
rec.is_treatment_professional = rec.has_group(
|
||||
'bemade_sports_clinic.group_sports_clinic_treatment_professional')
|
||||
"bemade_sports_clinic.group_sports_clinic_treatment_professional"
|
||||
)
|
||||
|
||||
def _compute_accessible_team_ids(self):
|
||||
for rec in self:
|
||||
rec.accessible_team_ids = rec.partner_id.staff_ids.mapped("team_id")
|
||||
|
||||
def _inverse_accessible_team_ids(self):
|
||||
for rec in self:
|
||||
removed_teams = (
|
||||
rec.partner_id.staff_ids.mapped("team_id") - rec.accessible_team_ids
|
||||
)
|
||||
added_teams = rec.accessible_team_ids - rec.partner_id.staff_ids.mapped(
|
||||
"team_id"
|
||||
)
|
||||
rec.partner_id.staff_ids.filtered(
|
||||
lambda team: team in removed_teams
|
||||
).unlink()
|
||||
self.env["sports.team.staff"].create(
|
||||
[
|
||||
{
|
||||
"team_id": team.id,
|
||||
"partner_id": [Command.set([rec.id])],
|
||||
"role": "other",
|
||||
}
|
||||
for team in added_teams
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -52,18 +52,14 @@ class SportsTeam(models.Model):
|
|||
website = fields.Char()
|
||||
allowed_user_ids = fields.Many2many(
|
||||
comodel_name="res.users",
|
||||
relation="sports_team_res_users_rel",
|
||||
column1="team_id",
|
||||
column2="user_id",
|
||||
string="Allowed Users",
|
||||
domain=lambda self: [["groups_id", "in", self.env.ref("base.group_user").ids]],
|
||||
compute="_compute_allowed_user_ids",
|
||||
inverse="_inverse_allowed_user_ids",
|
||||
)
|
||||
|
||||
def write(self, vals):
|
||||
previous_patient_ids = self.patient_ids
|
||||
res = super().write(vals)
|
||||
if "staff_ids" in vals or "patient_ids" in vals:
|
||||
self._allow_access_for_staff_internal_users()
|
||||
(self.patient_ids | previous_patient_ids).recompute_followers()
|
||||
return res
|
||||
|
||||
|
|
@ -101,10 +97,26 @@ class SportsTeam(models.Model):
|
|||
staff = rec.staff_ids.filtered(lambda r: r.role == "head_therapist")
|
||||
rec.head_therapist_id = staff.partner_id if staff else False
|
||||
|
||||
def _allow_access_for_staff_internal_users(self):
|
||||
def _compute_allowed_user_ids(self):
|
||||
for rec in self:
|
||||
rec.allowed_user_ids |= rec.staff_ids.user_ids.filtered(
|
||||
lambda user: user.has_group("base.group_user")
|
||||
rec.allowed_user_ids = rec.staff_ids.user_ids
|
||||
|
||||
def _inverse_allowed_user_ids(self):
|
||||
for rec in self:
|
||||
removed_staff = rec.staff_ids.filtered(
|
||||
lambda staff: staff.user_ids not in rec.allowed_user_ids
|
||||
)
|
||||
added_users = rec.allowed_user_ids - rec.staff_ids.user_ids
|
||||
removed_staff.unlink()
|
||||
self.env["sports.team.staff"].create(
|
||||
[
|
||||
{
|
||||
"team_id": rec.id,
|
||||
"partner_id": user.partner_id.id,
|
||||
"role": "other",
|
||||
}
|
||||
for user in added_users
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
<record id="restrict_staff_access_to_team_players" model="ir.rule">
|
||||
<field name="name">Restrict Team Staff Access to Their Players Only</field>
|
||||
<field name="model_id" ref="model_sports_patient"/>
|
||||
<field name="groups" eval="[(6, 0, [ref('base.group_portal')])]"/>
|
||||
<field name="groups"
|
||||
eval="[(6, 0, [ref('base.group_portal'), ref('base.group_user')])]"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
|
|
@ -17,7 +18,8 @@
|
|||
<record id="restrict_staff_access_to_teams" model="ir.rule">
|
||||
<field name="name">Restrict Team Staff Access to Their Teams Only</field>
|
||||
<field name="model_id" ref="model_sports_team"/>
|
||||
<field name="groups" eval="[(6, 0, [ref('base.group_portal')])]"/>
|
||||
<field name="groups"
|
||||
eval="[(6, 0, [ref('base.group_portal'), ref('base.group_user')])]"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
|
|
@ -29,7 +31,8 @@
|
|||
<record id="restrict_staff_access_to_team_injuries" model="ir.rule">
|
||||
<field name="name">Restrict Team Staff Access to Their Teams Only</field>
|
||||
<field name="model_id" ref="model_sports_patient_injury"/>
|
||||
<field name="groups" eval="[(6, 0, [ref('base.group_portal')])]"/>
|
||||
<field name="groups"
|
||||
eval="[(6, 0, [ref('base.group_portal'), ref('base.group_user')])]"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
|
|
@ -38,44 +41,11 @@
|
|||
[('patient_id.team_ids.staff_ids.user_ids', 'in', user.id)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="restrict_team_access_to_allowed_internal_users" model="ir.rule">
|
||||
<field name="name">Restrict Team Access to Allowed Internal Users</field>
|
||||
<field name="model_id" ref="model_sports_team"/>
|
||||
<field name="groups" eval="[(6, 0, [ref('base.group_user')])]"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_unlink" eval="True"/>
|
||||
<field name="domain_force">
|
||||
[('allowed_user_ids', 'in', user.id)]
|
||||
<record id="restrict_patient_contact_access_to_allowed_internal_users"
|
||||
model="ir.rule">
|
||||
<field name="name">Restrict Patient Contact Access to Allowed Internal
|
||||
Users
|
||||
</field>
|
||||
</record>
|
||||
<record id="restrict_patient_access_to_allowed_internal_users" model="ir.rule">
|
||||
<field name="name">Restrict Patient Access to Allowed Internal Users</field>
|
||||
<field name="model_id" ref="model_sports_patient"/>
|
||||
<field name="groups" eval="[(6, 0, [ref('base.group_user')])]"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_unlink" eval="True"/>
|
||||
<field name="domain_force">
|
||||
[('team_ids.allowed_user_ids', 'in', user.id)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="restrict_injury_access_to_allowed_internal_users" model="ir.rule">
|
||||
<field name="name">Restrict Team Access to Allowed Internal Users</field>
|
||||
<field name="model_id" ref="model_sports_patient_injury"/>
|
||||
<field name="groups" eval="[(6, 0, [ref('base.group_user')])]"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_unlink" eval="True"/>
|
||||
<field name="domain_force">
|
||||
[('patient_id.team_ids.allowed_user_ids', 'in', user.id)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="restrict_patient_contact_access_to_allowed_internal_users" model="ir.rule">
|
||||
<field name="name">Restrict Patient Contact Access to Allowed Internal Users</field>
|
||||
<field name="model_id" ref="model_sports_patient_contact"/>
|
||||
<field name="groups" eval="[(6, 0, [ref('base.group_user')])]"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
|
|
@ -83,7 +53,7 @@
|
|||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_unlink" eval="True"/>
|
||||
<field name="domain_force">
|
||||
[('patient_id.team_ids.allowed_user_ids', 'in', user.id)]
|
||||
[('patient_id.team_ids.staff_ids.user_ids', 'in', user.id)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="allow_team_access_to_admins" model="ir.rule">
|
||||
|
|
@ -126,7 +96,8 @@
|
|||
</field>
|
||||
</record>
|
||||
<record id="allow_patient_contact_access_to_admins" model="ir.rule">
|
||||
<field name="name">Allow Sports Patient Contact Access to Administrators</field>
|
||||
<field name="name">Allow Sports Patient Contact Access to Administrators
|
||||
</field>
|
||||
<field name="model_id" ref="model_sports_patient_contact"/>
|
||||
<field name="groups"
|
||||
eval="[(6, 0, [ref('base.group_system'), ref('bemade_sports_clinic.group_sports_clinic_admin')])]"/>
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
from . import test_patient
|
||||
from . import test_rights
|
||||
|
|
|
|||
111
bemade_sports_clinic/tests/test_rights.py
Normal file
111
bemade_sports_clinic/tests/test_rights.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
from odoo.tests import TransactionCase, Form
|
||||
from odoo.fields import Date
|
||||
from datetime import timedelta
|
||||
from odoo.exceptions import AccessError
|
||||
|
||||
|
||||
class TestRights(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
# Create one admin user
|
||||
cls.admin_user = cls.env["res.users"].create(
|
||||
{
|
||||
"name": "Admin User",
|
||||
"login": "admin",
|
||||
"password": "admin",
|
||||
"groups_id": [
|
||||
(
|
||||
6,
|
||||
0,
|
||||
[
|
||||
cls.env.ref(
|
||||
"bemade_sports_clinic.group_sports_clinic_admin"
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
# Create one treatment professional user
|
||||
cls.treatment_professional_user = cls.env["res.users"].create(
|
||||
{
|
||||
"name": "Treatment Professional User",
|
||||
"login": "treatment_professional",
|
||||
"password": "treatment_professional",
|
||||
"groups_id": [
|
||||
(
|
||||
6,
|
||||
0,
|
||||
[
|
||||
cls.env.ref(
|
||||
"bemade_sports_clinic.group_sports_clinic_treatment_professional"
|
||||
).id
|
||||
],
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
def test_treatment_pro_has_access_only_to_staffed_teams(self):
|
||||
"""A treatment professional should only have access to teams and,
|
||||
by extension, patients for which they are a team staff member."""
|
||||
team, patients = self._generate_team_with_patient(self.admin_user)
|
||||
with self.assertRaises(AccessError):
|
||||
Form(
|
||||
self.env["sports.team"]
|
||||
.with_user(self.treatment_professional_user)
|
||||
.browse(team.id)
|
||||
)
|
||||
with self.assertRaises(AccessError):
|
||||
Form(
|
||||
self.env["sports.patient"]
|
||||
.with_user(self.treatment_professional_user)
|
||||
.browse(patients.ids)
|
||||
)
|
||||
|
||||
def test_treatment_pro_can_remove_patient_from_team(self):
|
||||
team, patients = self._generate_team_with_patient(self.admin_user)
|
||||
self.env['sports.team.staff'].create({
|
||||
"team_id": team.id,
|
||||
"partner_id": self.treatment_professional_user.id,
|
||||
"role": "head_therapist",
|
||||
})
|
||||
# Test removing the patient since we are team staff
|
||||
# Should not throw an error...
|
||||
with Form(team.with_user(self.treatment_professional_user)) as team:
|
||||
team.patient_ids.remove(index=0)
|
||||
self.assertEqual(len(team.patient_ids), 1)
|
||||
|
||||
def _generate_team_with_patient(self, user=None):
|
||||
user = user or self.env.user
|
||||
team = (
|
||||
self.env["sports.team"]
|
||||
.with_user(self.user)
|
||||
.create(
|
||||
{
|
||||
"name": "Test Team",
|
||||
}
|
||||
)
|
||||
)
|
||||
patients = (
|
||||
self.env["sports.patient"]
|
||||
.with_user(self.user)
|
||||
.create(
|
||||
[
|
||||
{
|
||||
"first_name": "Test",
|
||||
"last_name": "Patient One",
|
||||
"date_of_birth": Date.today() - timedelta(days=-365 * 18),
|
||||
"team_ids": [6, 0, team.ids],
|
||||
},
|
||||
{
|
||||
"first_name": "Test",
|
||||
"last_name": "Patient Two",
|
||||
"date_of_birth": Date.today() - timedelta(days=-365 * 18),
|
||||
"team_ids": [6, 0, team.ids],
|
||||
},
|
||||
]
|
||||
)
|
||||
)
|
||||
return team, patients
|
||||
Loading…
Reference in a new issue