diff --git a/service_catalog/models/instance.py b/service_catalog/models/instance.py index 0b9068e07..5378dff20 100644 --- a/service_catalog/models/instance.py +++ b/service_catalog/models/instance.py @@ -12,6 +12,7 @@ from Squest.utils.ansible_when import AnsibleWhen from Squest.utils.squest_model import SquestModel from profiles.models.scope import Scope +from service_catalog.models import RequestState, OperationType from service_catalog.models.hooks import HookManager from service_catalog.models.instance_state import InstanceState from service_catalog.models.services import Service @@ -210,6 +211,10 @@ def on_change(cls, sender, instance, *args, **kwargs): HookManager.trigger_hook(sender=sender, instance=instance, name="on_change_instance", source=previous.state, target=instance.state, *args, **kwargs) + @property + def has_pending_delete_request(self): + return self.request_set.filter(state__in=[RequestState.SUBMITTED, RequestState.ACCEPTED], + operation__type=OperationType.DELETE).exists() pre_save.connect(Instance.on_change, sender=Instance) diff --git a/service_catalog/views/instance.py b/service_catalog/views/instance.py index 519503932..51fbefc5d 100644 --- a/service_catalog/views/instance.py +++ b/service_catalog/views/instance.py @@ -1,3 +1,5 @@ +import logging + from django.contrib import messages from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied @@ -6,9 +8,9 @@ from django.urls import reverse from django.utils.safestring import mark_safe from django_fsm import can_proceed -from Squest.utils.squest_table import SquestRequestConfig from jinja2 import UndefinedError, TemplateError +from Squest.utils.squest_table import SquestRequestConfig from Squest.utils.squest_views import SquestListView, SquestDetailView, SquestUpdateView, SquestDeleteView, \ SquestPermissionDenied from service_catalog.filters.instance_filter import InstanceFilter, InstanceArchivedFilter @@ -25,8 +27,6 @@ from service_catalog.tables.request_tables import RequestTable from service_catalog.tables.support_tables import SupportTable -import logging - logger = logging.getLogger(__name__) @@ -172,6 +172,9 @@ def instance_request_new_operation(request, instance_id, operation_id): raise PermissionDenied("Operation service and instance service doesn't match") if operation.type not in [OperationType.UPDATE, OperationType.DELETE]: raise PermissionDenied("Operation type UPDATE and DELETE only") + # do not allow to ask for a deletion if delete request already there + if instance.has_pending_delete_request: + raise PermissionDenied("A deletion request has already been submitted for this instance") parameters = { 'operation': operation, diff --git a/templates/403.html b/templates/403.html index 3d45926c5..dcb9d80f3 100644 --- a/templates/403.html +++ b/templates/403.html @@ -9,12 +9,14 @@

403

Access denied

-

- The page you are trying to access has restricted access. -
- {{ exception }} +

+ {% if exception %} + {{ exception }} + {% else %} + The page you are trying to access has restricted access. + {% endif %} +

-
diff --git a/templates/service_catalog/custom_columns/operation_request.html b/templates/service_catalog/custom_columns/operation_request.html index 4b89dca1f..3ac57acb1 100644 --- a/templates/service_catalog/custom_columns/operation_request.html +++ b/templates/service_catalog/custom_columns/operation_request.html @@ -1,4 +1,6 @@ - - - +
+ + + +
diff --git a/tests/test_service_catalog/test_views/test_customer/test_instance/test_instance_request_operation.py b/tests/test_service_catalog/test_views/test_customer/test_instance/test_instance_request_operation.py index 675ea5193..e5d6ead88 100644 --- a/tests/test_service_catalog/test_views/test_customer/test_instance/test_instance_request_operation.py +++ b/tests/test_service_catalog/test_views/test_customer/test_instance/test_instance_request_operation.py @@ -67,6 +67,19 @@ def test_customer_cannot_request_admin_operation(self): response = self.client.post(url, data=data) self.assertEqual(403, response.status_code) + def test_cannot_request_operation_when_deletion_already_asked(self): + self.test_request.operation = self.delete_operation_test + self.test_request.save() + args = { + 'instance_id': self.test_instance.id, + 'operation_id': self.delete_operation_test.id + } + data = {'text_variable': 'my_var'} + url = reverse('service_catalog:instance_request_new_operation', kwargs=args) + self.client.force_login(user=self.standard_user) + response = self.client.post(url, data=data) + self.assertEqual(403, response.status_code) + def test_cannot_request_non_available_instance(self): for state in [InstanceState.PENDING, InstanceState.PROVISIONING, InstanceState.DELETING, InstanceState.DELETED]: self.test_instance.state = state