Skip to content

Commit

Permalink
Merge pull request #212 from rackerlabs/issue-204-validate-scan-statu…
Browse files Browse the repository at this point in the history
…s-changes

Validate scan status changes
  • Loading branch information
derpadoo authored Jun 10, 2020
2 parents 2c0ca44 + e349dc3 commit ad542bd
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 10 deletions.
4 changes: 2 additions & 2 deletions agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import time
import urllib.request

__version__ = "1.0"
__version__ = "1.01"

# Disable SSL/TLS verification.
ssl._create_default_https_context = ssl._create_unverified_context
Expand Down Expand Up @@ -503,7 +503,7 @@ def go(self):
if scan_jobs:
for scan_job in scan_jobs:

ROOT_LOGGER.info(f"scan_job: {scan_job}")
ROOT_LOGGER.info(f"scan_job: {json.dumps(scan_job, indent=4)}")

# Create new dictionary that will contain scan_job and config_data information.
scan_job_dict = {
Expand Down
2 changes: 1 addition & 1 deletion master/django_scantron/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.32"
__version__ = "1.33"
38 changes: 32 additions & 6 deletions master/django_scantron/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# Third party Python libraries.
from django.conf import settings
from django.http import Http404
from django.http import Http404, JsonResponse
import redis
from rest_framework import mixins, viewsets
from rest_framework.permissions import IsAdminUser, IsAuthenticated
Expand Down Expand Up @@ -108,22 +108,48 @@ class ScheduledScanViewSet(ListRetrieveUpdateViewSet, DefaultsMixin):

def partial_update(self, request, pk=None, **kwargs):

# Any updates to this dictionary should also be updated in master/django_scantron/models.py
scan_status_allowed_state_update_dict = {
"pending": ["started", "error"],
"started": ["pause", "cancel", "completed", "error"],
"pause": ["paused", "error"],
"paused": ["pending", "cancel", "error"],
"cancel": ["cancelled", "error"],
"cancelled": ["error"],
"completed": ["error"],
"error": ["pending"],
}

try:

# Extract the json payload.
body = self.request.data
new_scan_status = body["scan_status"]

if body["scan_status"] in ["started", "pause", "paused", "cancel", "cancelled", "completed", "error"]:
if new_scan_status in ["started", "pause", "paused", "cancel", "cancelled", "completed", "error"]:

# Filter only the applicable ScheduledScans for the agent. Prevents an agent modifying another agent's
# ScheduledScan information.
scheduled_scan_dict = (
ScheduledScan.objects.filter(scan_agent=request.user).filter(pk=pk).values()[0]
) # noqa
scheduled_scan_dict = ScheduledScan.objects.filter(scan_agent=request.user).filter(pk=pk).values()[0]

current_scan_status = scheduled_scan_dict["scan_status"]

# Based off the current scan status, ensure the updated scan status is valid.
if new_scan_status not in scan_status_allowed_state_update_dict[current_scan_status]:

# Convert list to a string.
valid_scan_states = ", ".join(scan_status_allowed_state_update_dict[current_scan_status])

response_dict = {
"detail": f"Invalid scan status change requested. Scan status state '{current_scan_status}' "
f"can only transition to: {valid_scan_states}"
}

return JsonResponse(response_dict)

# Update the scheduled_scan_dict with the most recent scan_status state from the PUT request. When
# originally querying above, the old state is passed to utility.py unless it is updated.
scheduled_scan_dict["scan_status"] = body["scan_status"]
scheduled_scan_dict["scan_status"] = new_scan_status

# Create a redis connection object.
redis_conn = redis.Redis(host="127.0.0.1", port=6379, db=0)
Expand Down
34 changes: 34 additions & 0 deletions master/django_scantron/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,40 @@ class ScheduledScan(models.Model):
verbose_name="Scan binary process ID.",
)

def clean(self):
"""Based off the current scan status, ensure the updated scan status is valid."""

# Any updates to this dictionary should also be updated in master/django_scantron/api/views.py
scan_status_allowed_state_update_dict = {
"pending": ["started", "error"],
"started": ["pause", "cancel", "completed", "error"],
"pause": ["paused", "error"],
"paused": ["pending", "cancel", "error"],
"cancel": ["cancelled", "error"],
"cancelled": ["error"],
"completed": ["error"],
"error": ["pending"],
}

scheduled_scan_dict = ScheduledScan.objects.get(pk=self.pk)
current_scan_status = scheduled_scan_dict.scan_status

if self.scan_status not in scan_status_allowed_state_update_dict[current_scan_status]:
# Convert list to a string.
valid_scan_states = ", ".join(scan_status_allowed_state_update_dict[current_scan_status])

raise ValidationError(
f"Invalid scan status change requested. Scan status state '{current_scan_status}' can only transition "
f"to: {valid_scan_states}"
)

# If a scan is paused and needs to be cancelled, don't set the state to "cancel", because the agent will try and
# cancel a running process that doesn't exist and error out. Just bypass the "cancel" state and set it to
# "cancelled". This logic is not needed on the client / API side in the ScheduledScanViewSet class in
# master/django_scantron/api/views.py.
if current_scan_status == "paused" and self.scan_status == "cancel":
self.scan_status = "cancelled"

def __str__(self):
return str(self.id)

Expand Down
2 changes: 1 addition & 1 deletion master/requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
argon2-cffi==19.2.0

# Django
Django==2.2.10
Django==2.2.13

# Forms
django-crispy-forms==1.8.1
Expand Down

0 comments on commit ad542bd

Please sign in to comment.