Skip to content

Commit

Permalink
Add DDM views tests
Browse files Browse the repository at this point in the history
  • Loading branch information
np5 committed Jan 23, 2025
1 parent 36fd55b commit 689f244
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 11 deletions.
193 changes: 193 additions & 0 deletions tests/mdm/test_management_data_asset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
from functools import reduce
from io import BytesIO
import operator
from django.contrib.auth.models import Group, Permission
from django.db.models import Q
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils.crypto import get_random_string
from accounts.models import User
from zentral.contrib.mdm.models import Artifact, Channel, DataAsset, Platform
from .utils import build_plistfile, build_zipfile


@override_settings(
STATICFILES_STORAGE='django.contrib.staticfiles.storage.StaticFilesStorage',
STORAGES={"default": {"BACKEND": "django.core.files.storage.InMemoryStorage"}}
)
class MDMDataAssetManagementViewsTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user("godzilla", "godzilla@zentral.io", get_random_string(12))
cls.group = Group.objects.create(name=get_random_string(12))
cls.user.groups.set([cls.group])

# utiliy methods

def _login_redirect(self, url, data=None):
if data:
func = self.client.post
else:
func = self.client.get
response = func(url, data=data)
self.assertRedirects(response, "{u}?next={n}".format(u=reverse("login"), n=url))

def _login(self, *permissions):
if permissions:
permission_filter = reduce(operator.or_, (
Q(content_type__app_label=app_label, codename=codename)
for app_label, codename in (
permission.split(".")
for permission in permissions
)
))
self.group.permissions.set(list(Permission.objects.filter(permission_filter)))
else:
self.group.permissions.clear()
self.client.force_login(self.user)

# upload data asset GET

def test_upload_data_asset_get_redirect(self):
self._login_redirect(reverse("mdm:upload_data_asset"))

def test_upload_data_asset_get_permission_denied(self):
self._login()
response = self.client.get(reverse("mdm:upload_data_asset"))
self.assertEqual(response.status_code, 403)

def test_upload_data_asset_get(self):
self._login("mdm.add_artifact")
response = self.client.get(reverse("mdm:upload_data_asset"))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/dataasset_form.html")

# upload data_asset POST

def test_upload_data_asset_post_redirect(self):
zipfile = build_zipfile()
self._login_redirect(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.ZIP),
"file": zipfile,
"name": get_random_string(12),
"platforms": [str(Platform.MACOS)]})

def test_upload_data_asset_post_permission_denied(self):
zipfile = build_zipfile()
self._login()
response = self.client.post(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.ZIP),
"file": zipfile,
"name": get_random_string(12),
"platforms": [str(Platform.MACOS)]})
self.assertEqual(response.status_code, 403)

# PLIST

def test_upload_data_asset_post_invalid_plist_ext(self):
notaplist = BytesIO(b"-")
notaplist.name = "test.yolo"
self._login("mdm.add_artifact", "mdm.view_artifact")
response = self.client.post(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.PLIST),
"file": notaplist,
"name": get_random_string(12),
"platforms": [str(Platform.MACOS)]},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/dataasset_form.html")
self.assertFormError(response.context["form"], "file", "File name must have a .plist extension")

def test_upload_data_asset_post_invalid_plist(self):
notaplist = BytesIO(b"-")
notaplist.name = "test.plist"
self._login("mdm.add_artifact", "mdm.view_artifact")
response = self.client.post(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.PLIST),
"file": notaplist,
"name": get_random_string(12),
"platforms": [str(Platform.MACOS)]},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/dataasset_form.html")
self.assertFormError(response.context["form"], "file", "Invalid PLIST file")

def test_upload_data_asset_post_plist(self):
plistfile = build_plistfile()
name = get_random_string(12)
self._login("mdm.add_artifact", "mdm.view_artifact")
response = self.client.post(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.PLIST),
"file": plistfile,
"name": name,
"platforms": [str(Platform.MACOS), str(Platform.IOS)]},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/artifact_detail.html")
artifact = response.context["object"]
self.assertEqual(artifact.type, Artifact.Type.DATA_ASSET)
self.assertEqual(artifact.channel, Channel.DEVICE)
self.assertEqual(set(artifact.platforms), {Platform.IOS, Platform.MACOS})
self.assertEqual(artifact.name, name)
self.assertEqual(artifact.artifactversion_set.count(), 1)
artifact_version = artifact.artifactversion_set.first()
self.assertEqual(artifact_version.version, 1)
data_asset = artifact_version.data_asset
self.assertEqual(data_asset.type, DataAsset.Type.PLIST)
self.assertEqual(data_asset.filename, plistfile.name)
self.assertEqual(data_asset.get_content_type(), "text/xml")

# ZIP

def test_upload_data_asset_post_invalid_zip_ext(self):
notazip = BytesIO(b"-")
notazip.name = "test.yolo"
self._login("mdm.add_artifact", "mdm.view_artifact")
response = self.client.post(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.ZIP),
"file": notazip,
"name": get_random_string(12),
"platforms": [str(Platform.MACOS)]},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/dataasset_form.html")
self.assertFormError(response.context["form"], "file", "File name must have a .zip extension")

def test_upload_data_asset_post_invalid_zip(self):
notazip = BytesIO(b"-")
notazip.name = "test.zip"
self._login("mdm.add_artifact", "mdm.view_artifact")
response = self.client.post(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.ZIP),
"file": notazip,
"name": get_random_string(12),
"platforms": [str(Platform.MACOS)]},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/dataasset_form.html")
self.assertFormError(response.context["form"], "file", "Invalid ZIP file")

def test_upload_data_asset_post_zip(self):
zipfile = build_zipfile()
name = get_random_string(12)
self._login("mdm.add_artifact", "mdm.view_artifact")
response = self.client.post(reverse("mdm:upload_data_asset"),
{"type": str(DataAsset.Type.ZIP),
"file": zipfile,
"name": name,
"platforms": [str(Platform.MACOS)]},
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "mdm/artifact_detail.html")
artifact = response.context["object"]
self.assertEqual(artifact.type, Artifact.Type.DATA_ASSET)
self.assertEqual(artifact.channel, Channel.DEVICE)
self.assertEqual(set(artifact.platforms), {Platform.MACOS})
self.assertEqual(artifact.name, name)
self.assertEqual(artifact.artifactversion_set.count(), 1)
artifact_version = artifact.artifactversion_set.first()
self.assertEqual(artifact_version.version, 1)
data_asset = artifact_version.data_asset
self.assertEqual(data_asset.type, DataAsset.Type.ZIP)
self.assertEqual(data_asset.filename, zipfile.name)
self.assertEqual(data_asset.get_content_type(), "application/zip")
30 changes: 24 additions & 6 deletions tests/mdm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,27 @@ def force_location_asset(asset=None, location=None):
)


def build_plistfile(filename=None):
if filename is None:
filename = "{}.plist".format(get_random_string(17))
plist_buffer = io.BytesIO()
plistlib.dump({"un": 2}, plist_buffer)
plist_buffer.name = filename
plist_buffer.seek(0)
return plist_buffer


def build_zipfile(filename=None):
if filename is None:
filename = "{}.zip".format(get_random_string(17))
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
zip_file.writestr("etc/sudoers", "Defaults log_allowed\nDefaults timestamp_timeout=0")
zip_buffer.name = filename
zip_buffer.seek(0)
return zip_buffer


def force_artifact(
version_count=1,
artifact_type=Artifact.Type.PROFILE,
Expand Down Expand Up @@ -690,15 +711,12 @@ def force_artifact(
location_asset=force_location_asset(),
)
elif artifact_type == Artifact.Type.DATA_ASSET:
filename = "{}.zip".format(get_random_string(17))
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
zip_file.writestr("etc/sudoers", "Defaults log_allowed\nDefaults timestamp_timeout= 0")
content = zip_buffer.getvalue()
zipfile = build_zipfile()
content = zipfile.getvalue()
DataAsset.objects.create(
artifact_version=artifact_version,
type=DataAsset.Type.ZIP,
file=SimpleUploadedFile(name=filename, content=content),
file=SimpleUploadedFile(name=zipfile.name, content=content),
filename=filename,
file_size=len(content),
file_sha256=hashlib.sha256(content).hexdigest(),
Expand Down
14 changes: 9 additions & 5 deletions zentral/contrib/mdm/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,18 +845,22 @@ def clean(self):
data_asset_type = DataAsset.Type(data_asset_type)
if data_asset_type == DataAsset.Type.PLIST:
if not ext == ".plist":
raise forms.ValidationError("File name must have a .plist extension")
self.add_error("file", "File name must have a .plist extension")
return
try:
plistlib.load(file, fmt=plistlib.FMT_XML) # Only XML files because of the mimetype
except plistlib.InvalidFileException:
raise forms.ValidationError("Invalid PLIST file")
except Exception:
self.add_error("file", "Invalid PLIST file")
return
else:
file.seek(0)
elif data_asset_type == DataAsset.Type.ZIP:
if not ext == ".zip":
raise forms.ValidationError("File name must have a .zip extension")
self.add_error("file", "File name must have a .zip extension")
return
if not zipfile.is_zipfile(file):
raise forms.ValidationError("Not a ZIP file")
self.add_error("file", "Invalid ZIP file")
return
# update instance
self.instance.type = data_asset_type
self.instance.filename = file.name
Expand Down

0 comments on commit 689f244

Please sign in to comment.