Skip to content
This repository has been archived by the owner on Aug 10, 2023. It is now read-only.

Commit

Permalink
Make allowed file endings configurable
Browse files Browse the repository at this point in the history
Fixes: #205
  • Loading branch information
PhilippMatthes committed Mar 21, 2019
1 parent f49afc2 commit 13e6a89
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 7 deletions.
4 changes: 2 additions & 2 deletions inloop/accounts/constance.py
Expand Up @@ -20,8 +20,8 @@
))

fieldsets = {
"Signup form settings": ("SIGNUP_ALLOWED", "EMAIL_PATTERN",
"EMAIL_HELP_TEXT", "EMAIL_ERROR_MESSAGE")
"Signup form settings": ["SIGNUP_ALLOWED", "EMAIL_PATTERN",
"EMAIL_HELP_TEXT", "EMAIL_ERROR_MESSAGE"]
}

fields = {
Expand Down
2 changes: 1 addition & 1 deletion inloop/gitload/constance.py
Expand Up @@ -5,5 +5,5 @@
config["GITLOAD_BRANCH"] = ("master", "Git branch to be used for the checkout.")

fieldsets = {
"Gitload settings": ("GITLOAD_URL", "GITLOAD_BRANCH")
"Gitload settings": ["GITLOAD_URL", "GITLOAD_BRANCH"]
}
4 changes: 4 additions & 0 deletions inloop/settings.py
Expand Up @@ -18,6 +18,7 @@

from inloop.accounts import constance as accounts_constance
from inloop.gitload import constance as gitload_constance
from inloop.solutions import constance as solutions_constance

if sys.getfilesystemencoding() != "utf-8":
raise ImproperlyConfigured("LANG must be a utf-8 locale")
Expand Down Expand Up @@ -207,5 +208,8 @@
CONSTANCE_CONFIG.update(gitload_constance.config)
CONSTANCE_CONFIG_FIELDSETS.update(gitload_constance.fieldsets)

CONSTANCE_CONFIG.update(solutions_constance.config)
CONSTANCE_CONFIG_FIELDSETS.update(solutions_constance.fieldsets)

JPLAG_JAR_PATH = str(BASE_DIR / "lib" / "jplag-2.11.9-SNAPSHOT.jar")
JPLAG_SIMILARITY = 90
13 changes: 13 additions & 0 deletions inloop/solutions/constance.py
@@ -0,0 +1,13 @@
from collections import OrderedDict

from django.utils.safestring import mark_safe

config = OrderedDict()
config["ALLOWED_FILENAME_EXTENSIONS"] = (".java", mark_safe(
"Filename extensions to be accepted in solution upload forms "
"such as <code>.java, .c, .cpp, .h, .hpp</code>. "
"Separate multiple filename extensions by commas."
"Surrounding whitespace will be stripped."
))

fieldsets = {"General settings": ["ALLOWED_FILENAME_EXTENSIONS"]}
30 changes: 30 additions & 0 deletions inloop/solutions/validators.py
@@ -0,0 +1,30 @@
from django.core.exceptions import ValidationError

from constance import config


def _get_allowed_filename_extensions():
"""
Return all allowed filename extensions.
"""
allowed_filename_extensions = config.ALLOWED_FILENAME_EXTENSIONS
if not allowed_filename_extensions:
return None
return [e.strip().lower() for e in allowed_filename_extensions.split(",")]


def validate_filenames(upload):
"""
Verify that all uploaded files comply to the given naming constraints.
"""
allowed_filename_extensions = _get_allowed_filename_extensions()
if not allowed_filename_extensions:
raise ValidationError("Currently no filename extensions are accepted.")
if not upload:
raise ValidationError("No files were supplied.")
for file_name, _ in upload.items():
if not file_name.lower().endswith(tuple(allowed_filename_extensions)):
raise ValidationError(
"One or more files contain disallowed filename extensions. "
"(Allowed: {})".format(", ".join(allowed_filename_extensions))
)
14 changes: 10 additions & 4 deletions inloop/solutions/views.py
Expand Up @@ -2,6 +2,7 @@

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import transaction
from django.http import Http404, JsonResponse
Expand All @@ -14,6 +15,7 @@
xml_strings_from_testoutput)
from inloop.solutions.prettyprint.junit import checkeroutput_filter, xml_to_dict
from inloop.solutions.signals import solution_submitted
from inloop.solutions.validators import validate_filenames
from inloop.tasks.models import Task


Expand Down Expand Up @@ -52,8 +54,10 @@ def post(self, request, slug):
messages.error(request, "You haven't uploaded any files.")
return failure_response

if not all([file_name.endswith(".java") for file_name, _ in uploads.items()]):
messages.error(request, "You have uploaded invalid files (allowed: *.java).")
try:
validate_filenames(uploads)
except ValidationError as e:
messages.error(request, e.message)
return failure_response

files = [SimpleUploadedFile(f, c.encode()) for f, c in uploads.items()]
Expand Down Expand Up @@ -149,8 +153,10 @@ def post(self, request, slug):
messages.error(request, "You haven't uploaded any files.")
return redirect_to_upload

if not all([f.name.endswith(".java") for f in uploads]):
messages.error(request, "You have uploaded invalid files (allowed: *.java).")
try:
validate_filenames(uploads)
except ValidationError as e:
messages.error(request, e.message)
return redirect_to_upload

with transaction.atomic():
Expand Down
50 changes: 50 additions & 0 deletions tests/solutions/test_validators.py
@@ -0,0 +1,50 @@
from django.core.exceptions import ValidationError
from django.test import TestCase

from constance.test import override_config

from inloop.solutions.validators import _get_allowed_filename_extensions, validate_filenames


@override_config(ALLOWED_FILENAME_EXTENSIONS=".java , .b ,.cpp, \n.h,\r.py")
class FileNameExtensionValidationTest(TestCase):
def test_get_allowed_filename_extensions(self):
filename_extensions = _get_allowed_filename_extensions()
self.assertEqual([".java", ".b", ".cpp", ".h", ".py"], filename_extensions)

def test_valid_uploads(self):
uploads = {
"HelloWorld.java": "public class HelloWorld {//...}",
"HelloWorld.b": "+[-[<<[+[--->]-[<<<]]]>>>-]>-.---.>..>.<<<<-.<+.>>>>>.>.<<.<-.",
"HelloWorld.cpp": "int main() {//...}",
"HelloWorld.h": "",
"HelloWorld.py": "print(\"Hello World\")"
}
try:
validate_filenames(uploads)
except ValidationError as e:
self.fail("Filename validation should succeed on the "
"given files. ({})".format(e.message))

def test_invalid_uploads(self):
uploads = {
"HelloWorld.kt": "class Test {}",
}
self.assertRaises(ValidationError, validate_filenames, uploads)

def test_no_uploads(self):
uploads = {}
self.assertRaises(ValidationError, validate_filenames, uploads)


@override_config(ALLOWED_FILENAME_EXTENSIONS="")
class EmptyAllowedFileNameExtensionsValidationTest(TestCase):
def test_get_allowed_filename_extensions(self):
filename_extensions = _get_allowed_filename_extensions()
self.assertFalse(filename_extensions)

def test_uploads(self):
uploads = {
"HelloWorld.java": "public class HelloWorld {//...}",
}
self.assertRaises(ValidationError, validate_filenames, uploads)

0 comments on commit 13e6a89

Please sign in to comment.