-
-
Notifications
You must be signed in to change notification settings - Fork 694
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FIX] product_cost_security: ORM-level security
Before this patch, inheriting modules had to manually add some logic related to guessing wether the user can access cost information. Also, smartypants users could still obtain the desired information through API calls. Now: - You can't read/write cost fields via API anymore. - Views automatically set those fields to readonly if the user has only read permissions (and the model inherits from the new mixin). - You don't need to enable debug mode anymore to follow configuration instructions. - Instructions improved. @moduon MT-5158
- Loading branch information
Showing
13 changed files
with
186 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
from . import product_cost_security_mixin | ||
from . import product_template | ||
from . import product_product |
87 changes: 87 additions & 0 deletions
87
product_cost_security/models/product_cost_security_mixin.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# Copyright 2024 Moduon Team S.L. | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0) | ||
from contextlib import suppress | ||
|
||
from odoo import _, api, fields, models | ||
from odoo.exceptions import AccessError | ||
|
||
|
||
class ProductCostSecurityMixin(models.AbstractModel): | ||
"""Automatic security for models related with product costs. | ||
When you inherit from this mixin, make sure to add | ||
`groups="product_cost_security.group_product_cost"` to the fields that | ||
should be protected. Odoo will take care of hiding those fields to users | ||
without that access, and this mixin will add an extra protection to prevent | ||
editing if the user is not in the | ||
`product_cost_security.group_product_edit_cost` group. | ||
""" | ||
|
||
_name = "product.cost.security.mixin" | ||
_description = "Product cost access control mixin" | ||
|
||
user_can_update_cost = fields.Boolean(compute="_compute_user_can_update_cost") | ||
|
||
@api.depends_context("uid") | ||
def _compute_user_can_update_cost(self): | ||
"""Let views know if users can edit product costs. | ||
A user could have full cost permissions but no product edition permissions. | ||
We want to prevent those from updating costs. | ||
""" | ||
self.user_can_update_cost = self._user_can_update_cost() | ||
|
||
@api.model | ||
def _user_can_update_cost(self): | ||
"""Know if current user can update product costs. | ||
Just like `self.user_can_update_cost`, but once per model. | ||
""" | ||
return self.env.user.has_group("product_cost_security.group_product_edit_cost") | ||
|
||
@api.model | ||
def _product_cost_security_fields(self): | ||
"""Fields that should be hidden if the user has no cost permissions. | ||
Returns a list of field names where the security group is applied. | ||
""" | ||
result = [] | ||
for fname, field in self._fields.items(): | ||
if ( | ||
field.groups | ||
and "product_cost_security.group_product_cost" | ||
in field.groups.split(",") | ||
): | ||
result.append(fname) | ||
return result | ||
|
||
@api.constrains(lambda self: self._product_cost_security_fields()) | ||
def _check_product_cost_security(self): | ||
"""Forbid users from updating product costs if they have no permissions. | ||
This method is called whenever a user tries to update a field that is | ||
protected by the `product_cost_security.group_product_cost` group. We | ||
apply an extra protection to prevent editing if the user is not in the | ||
`product_cost_security.group_product_edit_cost` group, because the | ||
`groups` security attribute in the fields only applies to read access. | ||
""" | ||
if not self._user_can_update_cost(): | ||
raise AccessError( | ||
_( | ||
"You don't have permission to update product costs. " | ||
"Allowed group: %s", | ||
self.env.ref( | ||
"product_cost_security.group_product_edit_cost" | ||
).display_name, | ||
) | ||
) | ||
|
||
@api.model | ||
def fields_get(self, allfields=None, attributes=None): | ||
"""Make product cost fields readonly for non-editors.""" | ||
result = super().fields_get(allfields, attributes) | ||
if not self._user_can_update_cost(): | ||
for field_name in self._product_cost_security_fields(): | ||
with suppress(KeyError): | ||
result[field_name]["readonly"] = True | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,11 @@ | ||
# Copyright 2018 Sergio Teruel - Tecnativa <sergio.teruel@tecnativa.com> | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). | ||
from odoo import api, fields, models | ||
from odoo import fields, models | ||
|
||
|
||
class ProductTemplate(models.Model): | ||
_inherit = "product.template" | ||
_name = "product.template" | ||
_inherit = ["product.template", "product.cost.security.mixin"] | ||
|
||
# Inherited fields | ||
standard_price = fields.Float(groups="product_cost_security.group_product_cost") | ||
user_can_update_cost = fields.Boolean(compute="_compute_user_can_update_cost") | ||
|
||
@api.depends_context("uid") | ||
def _compute_user_can_update_cost(self): | ||
"""A user could have full cost permissions but no product edition permissions. | ||
We want to prevent those from updating costs.""" | ||
for product in self: | ||
product.user_can_update_cost = self.env.user.has_group( | ||
"product_cost_security.group_product_edit_cost" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
To use this module you need to: | ||
|
||
#. Go to a *Setting > Users and Companies > Users*. | ||
#. Select a user and add "Access to product costs" group. | ||
#. Edit a user. | ||
#. Grant some access level using the *Product costs* dropdown. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,5 @@ | |
* `Onestein <https://www.onestein.eu>`_: | ||
|
||
* Anjeel Haria | ||
|
||
* Jairo Llopis (`Moduon <https://www.moduon.team>`__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
To use this module you need to: | ||
|
||
#. Go to product form view logged with this user and you will see the | ||
standard_price field. | ||
#. Go to product form view. | ||
#. You will not see the *Cost* field unless you follow the *Configuration* steps and get read permissions. | ||
#. You will not be able to edit it unless you are granted write permissions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters