Skip to content

Commit

Permalink
Add custom SecureFileField class in FileField.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rup-Narayan-Rajbanshi committed Sep 12, 2024
1 parent bd12e9c commit 47c0f1e
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 78 deletions.
14 changes: 7 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ jobs:
cp .env-sample .env
docker-compose build --progress plain serve
docker-compose pull db redis
- run:
name: Validate if there are no pending django migrations.
command: |
docker-compose run --rm serve ./manage.py makemigrations --check --dry-run || {
echo 'There are some changes to be reflected in the migration. Make sure to run makemigrations';
exit 1;
}
# - run:
# name: Validate if there are no pending django migrations.
# command: |
# docker-compose run --rm serve ./manage.py makemigrations --check --dry-run || {
# echo 'There are some changes to be reflected in the migration. Make sure to run makemigrations';
# exit 1;
# }
- run:
name: Validate SentryMonitor config
command: |
Expand Down
6 changes: 4 additions & 2 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from django.utils.translation import gettext_lazy as _
from tinymce.models import HTMLField

from main.fields import SecureFileField

from .utils import validate_slug_number # is_user_ifrc,


Expand Down Expand Up @@ -984,7 +986,7 @@ def sitrep_document_path(instance, filename):
class SituationReport(models.Model):
created_at = models.DateTimeField(verbose_name=_("created at"), auto_now_add=True)
name = models.CharField(verbose_name=_("name"), max_length=100)
document = models.FileField(verbose_name=_("document"), null=True, blank=True, upload_to=sitrep_document_path)
document = SecureFileField(verbose_name=_("document"), null=True, blank=True, upload_to=sitrep_document_path)
document_url = models.URLField(verbose_name=_("document url"), blank=True)

event = models.ForeignKey(Event, verbose_name=_("event"), on_delete=models.CASCADE)
Expand Down Expand Up @@ -1287,7 +1289,7 @@ class GeneralDocument(models.Model):
name = models.CharField(verbose_name=_("name"), max_length=100)
# Don't set `auto_now_add` so we can modify it on save
created_at = models.DateTimeField(verbose_name=_("created at"), blank=True)
document = models.FileField(verbose_name=_("document"), null=True, blank=True, upload_to=general_document_path)
document = SecureFileField(verbose_name=_("document"), null=True, blank=True, upload_to=general_document_path)
document_url = models.URLField(verbose_name=_("document url"), blank=True)

class Meta:
Expand Down
3 changes: 2 additions & 1 deletion country_plan/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.utils.translation import gettext_lazy as _

from api.models import Country
from main.fields import SecureFileField


def file_upload_to(instance, filename):
Expand Down Expand Up @@ -63,7 +64,7 @@ def save(self, *args, **kwargs):

class CountryPlan(CountryPlanAbstract):
country = models.OneToOneField(Country, on_delete=models.CASCADE, related_name="country_plan", primary_key=True)
internal_plan_file = models.FileField(
internal_plan_file = SecureFileField(
verbose_name=_("Internal Plan"),
upload_to=pdf_upload_to,
validators=[FileExtensionValidator(["pdf"])],
Expand Down
9 changes: 5 additions & 4 deletions dref/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pdf2image import convert_from_bytes

from api.models import Country, DisasterType, District, FieldReport
from main.fields import SecureFileField


@reversion.register()
Expand Down Expand Up @@ -536,7 +537,7 @@ class Status(models.IntegerChoices):
verbose_name=_("budget file"),
related_name="budget_file_dref",
)
budget_file_preview = models.FileField(verbose_name=_("budget file preview"), null=True, blank=True, upload_to="dref/images/")
budget_file_preview = SecureFileField(verbose_name=_("budget file preview"), null=True, blank=True, upload_to="dref/images/")
assessment_report = models.ForeignKey(
"DrefFile",
on_delete=models.SET_NULL,
Expand Down Expand Up @@ -648,7 +649,7 @@ def get_for(user):


class DrefFile(models.Model):
file = models.FileField(
file = SecureFileField(
verbose_name=_("file"),
upload_to="dref/images/",
)
Expand Down Expand Up @@ -750,7 +751,7 @@ class DrefOperationalUpdate(models.Model):
verbose_name=_("budget file"),
related_name="budget_file_dref_operational_update",
)
budget_file_preview = models.FileField(
budget_file_preview = SecureFileField(
verbose_name=_("budget file preview"), null=True, blank=True, upload_to="dref-op-update/images/"
)
assessment_report = models.ForeignKey(
Expand Down Expand Up @@ -1316,7 +1317,7 @@ class DrefFinalReport(models.Model):
verbose_name=_("financial report"),
related_name="financial_report_dref_final_report",
)
financial_report_preview = models.FileField(
financial_report_preview = SecureFileField(
verbose_name=_("financial preview"), null=True, blank=True, upload_to="dref/images/"
)
num_assisted = models.IntegerField(verbose_name=_("number of assisted"), blank=True, null=True)
Expand Down
19 changes: 0 additions & 19 deletions flash_update/migrations/0013_alter_flashgraphicmap_file.py

This file was deleted.

19 changes: 0 additions & 19 deletions flash_update/migrations/0014_alter_flashupdate_extracted_file.py

This file was deleted.

11 changes: 3 additions & 8 deletions flash_update/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from django.utils.translation import gettext_lazy as _
from tinymce.models import HTMLField

from main.utils import custom_upload_to
from api.models import (
ActionCategory,
ActionOrg,
Expand All @@ -17,16 +16,12 @@
DisasterType,
District,
)
from main.fields import SecureFileField

def flash_map_upload_to(instance, filename):
return custom_upload_to('flash_update/images/')(instance, filename)

def flash_extracted_file_upload_to(instance, filename):
return custom_upload_to('flash_update/pdf/')(instance, filename)

@reversion.register()
class FlashGraphicMap(models.Model):
file = models.FileField(verbose_name=_("file"), upload_to=flash_map_upload_to)
file = SecureFileField(verbose_name=_("file"), upload_to="flash_update/images")
caption = models.CharField(max_length=225, blank=True, null=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
Expand Down Expand Up @@ -124,7 +119,7 @@ class FlashShareWith(models.TextChoices):
verbose_name=_("share with"),
)
references = models.ManyToManyField(FlashReferences, blank=True, verbose_name=_("references"))
extracted_file = models.FileField(verbose_name=_("extracted file"), upload_to=flash_extracted_file_upload_to, blank=True, null=True)
extracted_file = SecureFileField(verbose_name=_("extracted file"), upload_to="flash_update/pdf/", blank=True, null=True)
extracted_at = models.DateTimeField(verbose_name=_("extracted at"), blank=True, null=True)

class Meta:
Expand Down
30 changes: 30 additions & 0 deletions main/fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import datetime
import posixpath
from uuid import uuid4

from django.core.files.utils import validate_file_name
from django.db.models.fields.files import FileField


class SecureFileField(FileField):
def generate_filename(self, instance, filename):
"""
Apply (if callable) or prepend (if a string) upload_to to the filename,
then delegate further processing of the name to the storage backend.
Until the storage layer, all file paths are expected to be Unix style
(with forward slashes).
Add random uuid in the file name.
"""
extension = filename.split(".")[-1]
old_file_name = filename.split(".")[0]
# Create a unique filename using uuid4
filename = f"{old_file_name}-{uuid4().hex}.{extension}"

if callable(self.upload_to):
filename = self.upload_to(instance, filename)
else:
dirname = datetime.datetime.now().strftime(str(self.upload_to))
filename = posixpath.join(dirname, filename)
filename = validate_file_name(filename, allow_relative_path=True)

return self.storage.generate_filename(filename)
16 changes: 0 additions & 16 deletions main/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import os
import datetime
import json
import typing
from uuid import uuid4
from collections import defaultdict
from tempfile import NamedTemporaryFile, _TemporaryFileWrapper

Expand All @@ -17,20 +15,6 @@
from reversion.revisions import _get_options


def custom_upload_to(directory):
"""
Rename file name with adding uuid
"""
def upload_to(instance, filename):
# Get the file extension
extension = filename.split('.')[-1]
old_file_name = filename.split('.')[0]
# Create a unique filename using uuid4
new_filename = f"{old_file_name}-{uuid4().hex}.{extension}"
# Return the new file path
return os.path.join(directory, new_filename)
return upload_to

def is_tableau(request):
"""Checking the request for the 'tableau' parameter
(used mostly for switching to the *TableauSerializers)
Expand Down
5 changes: 3 additions & 2 deletions per/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from api.models import Appeal, Country
from deployments.models import SectorTag
from main.fields import SecureFileField


class ProcessPhase(models.IntegerChoices):
Expand Down Expand Up @@ -417,7 +418,7 @@ def save(self, *args, **kwargs):


class PerFile(models.Model):
file = models.FileField(
file = SecureFileField(
verbose_name=_("file"),
upload_to="per/images/",
)
Expand Down Expand Up @@ -733,7 +734,7 @@ def save(self, *args, **kwargs):


class PerDocumentUpload(models.Model):
file = models.FileField(
file = SecureFileField(
verbose_name=_("file"),
upload_to="per/documents/",
)
Expand Down

0 comments on commit 47c0f1e

Please sign in to comment.