Change modal form permissions to use new "role" strategy

This commit is contained in:
Oliver Walters 2020-10-06 11:42:16 +11:00
parent c740cce5e4
commit 11d31960c7
2 changed files with 89 additions and 104 deletions

View File

@ -108,31 +108,66 @@ class TreeSerializer(views.APIView):
return JsonResponse(response, safe=False)
class AjaxMixin(PermissionRequiredMixin):
class InvenTreeRoleMixin(PermissionRequiredMixin):
"""
Permission class based on user roles, not user 'permissions'.
To specify which role is required for the mixin,
set the class attribute 'role_required' to something like the following:
role_required = 'part.add'
role_required = [
'part.change',
'build.add',
]
"""
# By default, no roles are required
# Roles must be specified
role_required = None
def has_permission(self):
"""
Determine if the current user
"""
roles_required = []
if type(self.role_required) is str:
roles_required.append(self.role_required)
elif type(self.role_required) in [list, tuple]:
roles_required = self.role_required
user = self.request.user
# Superuser can have any permissions they desire
if user.is_superuser:
return True
for required in roles_required:
(role, permission) = required.split('.')
# Return False if the user does not have *any* of the required roles
if not check_user_role(user, role, permission):
return False
# We did not fail any required checks
return True
class AjaxMixin(InvenTreeRoleMixin):
""" AjaxMixin provides basic functionality for rendering a Django form to JSON.
Handles jsonResponse rendering, and adds extra data for the modal forms to process
on the client side.
Any view which inherits the AjaxMixin will need
correct permissions set using the 'permission_required' attribute
correct permissions set using the 'role_required' attribute
"""
# By default, allow *any* permissions
permission_required = '*'
def has_permission(self):
"""
Override the default behaviour of has_permission from PermissionRequiredMixin.
Basically, if permission_required attribute = '*',
no permissions are actually required!
"""
if self.permission_required == '*':
return True
else:
return super().has_permission()
# By default, allow *any* role
role_required = None
# By default, point to the modal_form template
# (this can be overridden by a child class)
@ -683,53 +718,3 @@ class DatabaseStatsView(AjaxView):
"""
return ctx
class InvenTreeRoleMixin(PermissionRequiredMixin):
"""
Permission class based on user roles, not user 'permissions'.
To specify which role is required for the mixin,
set the class attribute 'role_required' to something like the following:
role_required = 'part.add'
role_required = [
'part.change',
'build.add',
]
"""
# By default, no roles are required
# Roles must be specified
role_required = None
def has_permission(self):
"""
Determine if the current user
"""
roles_required = []
if type(self.role_required) is str:
roles_required.append(self.role_required)
elif type(self.role_required) in [list, tuple]:
roles_required = self.role_required
user = self.request.user
# Superuser can have any permissions they desire
if user.is_superuser:
return True
print(type(self), "Required roles:", roles_required)
for required in roles_required:
(role, permission) = required.split('.')
# Return False if the user does not have *any* of the required roles
if not check_user_role(user, role, permission):
return False
# We did not fail any required checks
return True

View File

@ -80,7 +80,7 @@ class PartAttachmentCreate(AjaxCreateView):
ajax_form_title = _("Add part attachment")
ajax_template_name = "modal_form.html"
permission_required = 'part.add_partattachment'
role_required = 'part.add'
def post_save(self):
""" Record the user that uploaded the attachment """
@ -130,7 +130,7 @@ class PartAttachmentEdit(AjaxUpdateView):
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit attachment')
permission_required = 'part.change_partattachment'
role_required = 'part.change'
def get_data(self):
return {
@ -153,7 +153,7 @@ class PartAttachmentDelete(AjaxDeleteView):
ajax_template_name = "attachment_delete.html"
context_object_name = "attachment"
permission_required = 'part.delete_partattachment'
role_required = 'part.delete'
def get_data(self):
return {
@ -168,7 +168,7 @@ class PartTestTemplateCreate(AjaxCreateView):
form_class = part_forms.EditPartTestTemplateForm
ajax_form_title = _("Create Test Template")
permission_required = 'part.add_parttesttemplate'
role_required = 'part.add'
def get_initial(self):
@ -197,7 +197,7 @@ class PartTestTemplateEdit(AjaxUpdateView):
form_class = part_forms.EditPartTestTemplateForm
ajax_form_title = _("Edit Test Template")
permission_required = 'part.change_parttesttemplate'
role_required = 'part.change'
def get_form(self):
@ -213,7 +213,7 @@ class PartTestTemplateDelete(AjaxDeleteView):
model = PartTestTemplate
ajax_form_title = _("Delete Test Template")
permission_required = 'part.delete_parttesttemplate'
role_required = 'part.delete'
class PartSetCategory(AjaxUpdateView):
@ -223,7 +223,7 @@ class PartSetCategory(AjaxUpdateView):
ajax_form_title = _('Set Part Category')
form_class = part_forms.SetPartCategoryForm
permission_required = 'part.change_part'
role_required = 'part.change'
category = None
parts = []
@ -308,7 +308,7 @@ class MakePartVariant(AjaxCreateView):
ajax_form_title = _('Create Variant')
ajax_template_name = 'part/variant_part.html'
permission_required = 'part.add_part'
role_required = 'part.add'
def get_part_template(self):
return get_object_or_404(Part, id=self.kwargs['pk'])
@ -388,7 +388,7 @@ class PartDuplicate(AjaxCreateView):
ajax_form_title = _("Duplicate Part")
ajax_template_name = "part/copy_part.html"
permission_required = 'part.add_part'
role_required = 'part.add'
def get_data(self):
return {
@ -513,7 +513,7 @@ class PartCreate(AjaxCreateView):
ajax_form_title = _('Create new part')
ajax_template_name = 'part/create_part.html'
permission_required = 'part.add_part'
role_required = 'part.add'
def get_data(self):
return {
@ -637,7 +637,7 @@ class PartNotes(UpdateView):
template_name = 'part/notes.html'
model = Part
permission_required = 'part.change_part'
role_required = 'part.change'
fields = ['notes']
@ -734,7 +734,7 @@ class PartQRCode(QRCodeView):
ajax_form_title = _("Part QR Code")
permission_required = 'part.view_part'
role_required = 'part.view'
def get_qr_data(self):
""" Generate QR code data for the Part """
@ -755,7 +755,7 @@ class PartImageUpload(AjaxUpdateView):
form_class = part_forms.PartImageForm
permission_required = 'part.change_part'
role_required = 'part.change'
def get_data(self):
return {
@ -770,7 +770,7 @@ class PartImageSelect(AjaxUpdateView):
ajax_template_name = 'part/select_image.html'
ajax_form_title = _('Select Part Image')
permission_required = 'part.change_part'
role_required = 'part.change'
fields = [
'image',
@ -813,7 +813,7 @@ class PartEdit(AjaxUpdateView):
ajax_form_title = _('Edit Part Properties')
context_object_name = 'part'
permission_required = 'part.change_part'
role_required = 'part.change'
def get_form(self):
""" Create form for Part editing.
@ -839,7 +839,7 @@ class BomValidate(AjaxUpdateView):
context_object_name = 'part'
form_class = part_forms.BomValidateForm
permission_required = ('part.change_part')
role_required = 'part.change'
def get_context(self):
return {
@ -1507,7 +1507,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
class PartExport(AjaxView):
""" Export a CSV file containing information on multiple parts """
permission_required = 'part.view_part'
role_required = 'part.view'
def get_parts(self, request):
""" Extract part list from the POST parameters.
@ -1586,7 +1586,7 @@ class BomDownload(AjaxView):
- File format should be passed as a query param e.g. ?format=csv
"""
permission_required = ('part.view_part', 'part.view_bomitem')
role_required = 'part.view'
model = Part
@ -1641,7 +1641,7 @@ class BomExport(AjaxView):
form_class = part_forms.BomExportForm
ajax_form_title = _("Export Bill of Materials")
permission_required = ('part.view_part', 'part.view_bomitem')
role_required = 'part.view'
def get(self, request, *args, **kwargs):
return self.renderJsonResponse(request, self.form_class())
@ -1692,7 +1692,7 @@ class PartDelete(AjaxDeleteView):
ajax_form_title = _('Confirm Part Deletion')
context_object_name = 'part'
permission_required = 'part.delete_part'
role_required = 'part.delete'
success_url = '/part/'
@ -1710,7 +1710,7 @@ class PartPricing(AjaxView):
ajax_form_title = _("Part Pricing")
form_class = part_forms.PartPriceForm
permission_required = ('company.view_supplierpricebreak', 'part.view_part')
role_required = ['sales_order.view', 'part.view']
def get_part(self):
try:
@ -1829,7 +1829,7 @@ class PartPricing(AjaxView):
class PartParameterTemplateCreate(AjaxCreateView):
""" View for creating a new PartParameterTemplate """
permission_required = 'part.add_partparametertemplate'
role_required = 'part.add'
model = PartParameterTemplate
form_class = part_forms.EditPartParameterTemplateForm
@ -1839,7 +1839,7 @@ class PartParameterTemplateCreate(AjaxCreateView):
class PartParameterTemplateEdit(AjaxUpdateView):
""" View for editing a PartParameterTemplate """
permission_required = 'part.change_partparametertemplate'
role_required = 'part.change'
model = PartParameterTemplate
form_class = part_forms.EditPartParameterTemplateForm
@ -1849,7 +1849,7 @@ class PartParameterTemplateEdit(AjaxUpdateView):
class PartParameterTemplateDelete(AjaxDeleteView):
""" View for deleting an existing PartParameterTemplate """
permission_required = 'part.delete_partparametertemplate'
role_required = 'part.delete'
model = PartParameterTemplate
ajax_form_title = _("Delete Part Parameter Template")
@ -1858,7 +1858,7 @@ class PartParameterTemplateDelete(AjaxDeleteView):
class PartParameterCreate(AjaxCreateView):
""" View for creating a new PartParameter """
permission_required = 'part.add_partparameter'
role_required = 'part.add'
model = PartParameter
form_class = part_forms.EditPartParameterForm
@ -1910,7 +1910,7 @@ class PartParameterCreate(AjaxCreateView):
class PartParameterEdit(AjaxUpdateView):
""" View for editing a PartParameter """
permission_required = 'part.change_partparameter'
role_required = 'part.change'
model = PartParameter
form_class = part_forms.EditPartParameterForm
@ -1926,7 +1926,7 @@ class PartParameterEdit(AjaxUpdateView):
class PartParameterDelete(AjaxDeleteView):
""" View for deleting a PartParameter """
permission_required = 'part.delete_partparameter'
role_required = 'part.delete'
model = PartParameter
ajax_template_name = 'part/param_delete.html'
@ -1991,7 +1991,7 @@ class CategoryEdit(AjaxUpdateView):
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Part Category')
permission_required = 'part.change_partcategory'
role_required = 'part.change'
def get_context_data(self, **kwargs):
context = super(CategoryEdit, self).get_context_data(**kwargs).copy()
@ -2030,7 +2030,7 @@ class CategoryDelete(AjaxDeleteView):
context_object_name = 'category'
success_url = '/part/'
permission_required = 'part.delete_partcategory'
role_required = 'part.delete'
def get_data(self):
return {
@ -2046,7 +2046,7 @@ class CategoryCreate(AjaxCreateView):
ajax_template_name = 'modal_form.html'
form_class = part_forms.EditCategoryForm
permission_required = 'part.add_partcategory'
role_required = 'part.add'
def get_context_data(self, **kwargs):
""" Add extra context data to template.
@ -2099,7 +2099,7 @@ class BomItemCreate(AjaxCreateView):
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Create BOM item')
permission_required = 'part.add_bomitem'
role_required = 'part.add'
def get_form(self):
""" Override get_form() method to reduce Part selection options.
@ -2167,7 +2167,7 @@ class BomItemEdit(AjaxUpdateView):
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit BOM item')
permission_required = 'part.change_bomitem'
role_required = 'part.change'
def get_form(self):
""" Override get_form() method to filter part selection options
@ -2217,7 +2217,7 @@ class BomItemDelete(AjaxDeleteView):
context_object_name = 'item'
ajax_form_title = _('Confim BOM item deletion')
permission_required = 'part.delete_bomitem'
role_required = 'part.delete'
class PartSalePriceBreakCreate(AjaxCreateView):
@ -2227,7 +2227,7 @@ class PartSalePriceBreakCreate(AjaxCreateView):
form_class = part_forms.EditPartSalePriceBreakForm
ajax_form_title = _('Add Price Break')
permission_required = 'part.add_partsellpricebreak'
role_required = 'part.add'
def get_data(self):
return {
@ -2278,7 +2278,7 @@ class PartSalePriceBreakEdit(AjaxUpdateView):
form_class = part_forms.EditPartSalePriceBreakForm
ajax_form_title = _('Edit Price Break')
permission_required = 'part.change_partsellpricebreak'
role_required = 'part.change'
def get_form(self):
@ -2295,4 +2295,4 @@ class PartSalePriceBreakDelete(AjaxDeleteView):
ajax_form_title = _("Delete Price Break")
ajax_template_name = "modal_delete_form.html"
permission_required = 'part.delete_partsalepricebreak'
role_required = 'part.delete'