Skip to content

Commit

Permalink
Merge pull request #201 from linea-it/192-remove-another-users-product
Browse files Browse the repository at this point in the history
  • Loading branch information
jandsonrj authored Oct 6, 2023
2 parents 990080a + 4b49abb commit 80fa8d3
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 7 deletions.
5 changes: 5 additions & 0 deletions backend/core/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ def delete(self, *args, **kwargs):
# raise OSError("Failed to remove directory: [ %s ] %s" % (product_path, e))

super().delete(*args, **kwargs)

def can_delete(self, user) -> bool:
if self.user.id == user.id or user.profile.is_admin():
return True
return False
8 changes: 7 additions & 1 deletion backend/core/serializers/product.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from core.models import Product, ProductType, Release
from pkg_resources import require
from rest_framework import serializers
from core.models import Release, ProductType, Product


class ProductSerializer(serializers.ModelSerializer):
Expand All @@ -20,6 +20,8 @@ class ProductSerializer(serializers.ModelSerializer):

is_owner = serializers.SerializerMethodField()

can_delete = serializers.SerializerMethodField()

class Meta:
model = Product
read_only_fields = ("internal_name", "is_owner")
Expand All @@ -43,3 +45,7 @@ def get_is_owner(self, obj):
return True
else:
return False

def get_can_delete(self, obj):
current_user = self.context["request"].user
return obj.can_delete(current_user)
22 changes: 22 additions & 0 deletions backend/core/test/test_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def test_product_serialized_format(self):
"product_type_name": self.product_type.display_name,
"uploaded_by": self.user.username,
"is_owner": True,
"can_delete": True,
"internal_name": self.product.internal_name,
"display_name": self.product.display_name,
"official_product": self.product.official_product,
Expand Down Expand Up @@ -336,6 +337,27 @@ def test_product_object_delete(self):

self.assertEqual(204, response.status_code)

def test_product_object_delete_by_admin(self):
"""Tests if the product admin can remove it"""
view = ProductViewSet.as_view({"delete": "destroy"})

# Cria um usuario que faz parte do grupo admin
adm_group = Group.objects.create(name="Admin")
user = User.objects.create_user("john2", "[email protected]", "you_know_nothing")
user.groups.add(adm_group)
token = Token.objects.create(user=user)
# Cria uma requisicao utilizando Factory
# para que o metodo destroy da view tenha acesso ao request.user
factory = APIRequestFactory()
request = factory.delete(self.url, format="json")
force_authenticate(request, user=user, token=user.auth_token)
request.user = user

raw_response = view(request, pk=self.product.pk)
response = raw_response.render()

self.assertEqual(204, response.status_code)

def test_access_another_user_product(self):
"""Verifica se é possivel um usuario ler produtos de outro usuario.
Valida se a flag is_owner retorna False
Expand Down
6 changes: 4 additions & 2 deletions backend/core/views/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,11 @@ def zip_product(self, internal_name, path, tmpdir):
return zip_path

def destroy(self, request, pk=None, *args, **kwargs):
# TODO: Duvida, Admin pode remover produto que não seja dele?
"""Produto só pode ser excluido pelo DONO ou se o usuario tiver profile de admin.
"""
# Regra do admin atualizada na issue: #192 - https://github.com/linea-it/pzserver_app/issues/192
instance = self.get_object()
if self.request.user.id == instance.user.pk:
if instance.can_delete(self.request.user):
return super(ProductViewSet, self).destroy(request, pk, *args, **kwargs)
else:
raise exceptions.PermissionDenied()
25 changes: 21 additions & 4 deletions frontend/components/ProductGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import DeleteIcon from '@mui/icons-material/Delete'
import DownloadIcon from '@mui/icons-material/Download'
import ShareIcon from '@mui/icons-material/Share'
import Alert from '@mui/material/Alert'
import Tooltip from '@mui/material/Tooltip'
import Link from '@mui/material/Link'
import Snackbar from '@mui/material/Snackbar'
import { DataGrid, GridActionsCellItem } from '@mui/x-data-grid'
Expand Down Expand Up @@ -158,15 +159,30 @@ export default function ProductGrid(props) {
width: 120,
sortable: false,
renderCell: params => (
<GridActionsCellItem
icon={<DeleteIcon />}
onClick={() => handleDelete(params.row)}
/>
<Tooltip
title={
!params.row.can_delete
? 'You cannot delete this data product because it belongs to another user.'
: 'Delete this data product.'
}
>
<div>
<GridActionsCellItem
icon={<DeleteIcon />}
onClick={() => handleDelete(params.row)}
disabled={!params.row.can_delete}
/>
</div>
</Tooltip>
)
}
]
}, [getProductUrl, router])

function handleError(errorMessage) {
console.error(errorMessage)
}

return (
<React.Fragment>
<DataGrid
Expand Down Expand Up @@ -198,6 +214,7 @@ export default function ProductGrid(props) {
onClose={() => setDelRecordId(null)}
recordId={delRecordId}
onRemoveSuccess={loadProducts}
onError={handleError}
/>
)}

Expand Down

0 comments on commit 80fa8d3

Please sign in to comment.