diff --git a/cms/permissions.py b/cms/permissions.py index 14eecac2..af0e8fa9 100644 --- a/cms/permissions.py +++ b/cms/permissions.py @@ -1,14 +1,22 @@ from django.conf import settings from rest_framework import permissions +from rest_framework.exceptions import PermissionDenied -from files.methods import is_mediacms_editor, is_mediacms_manager +from files.methods import ( + is_mediacms_editor, + is_mediacms_manager, + user_allowed_to_upload, +) class IsAuthorizedToAdd(permissions.BasePermission): def has_permission(self, request, view): if request.method in permissions.SAFE_METHODS: return True - return user_allowed_to_upload(request) + if not user_allowed_to_upload(request): + raise PermissionDenied("You don't have permission to upload media, or have reached max number of media uploads.") + + return True class IsAuthorizedToAddComment(permissions.BasePermission): @@ -55,26 +63,6 @@ class IsUserOrEditor(permissions.BasePermission): return obj.user == request.user -def user_allowed_to_upload(request): - """Any custom logic for whether a user is allowed - to upload content lives here - """ - if request.user.is_anonymous: - return False - if request.user.is_superuser: - return True - - if settings.CAN_ADD_MEDIA == "all": - return True - elif settings.CAN_ADD_MEDIA == "email_verified": - if request.user.email_is_verified: - return True - elif settings.CAN_ADD_MEDIA == "advancedUser": - if request.user.advancedUser: - return True - return False - - def user_allowed_to_comment(request): """Any custom logic for whether a user is allowed to comment lives here diff --git a/cms/settings.py b/cms/settings.py index c6205cd5..76f9edf8 100644 --- a/cms/settings.py +++ b/cms/settings.py @@ -226,7 +226,7 @@ POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY = "" # only in case where unlisted workflow is used and no commentary # exists -CANNOT_ADD_MEDIA_MESSAGE = "" +CANNOT_ADD_MEDIA_MESSAGE = "User cannot add media, or maximum number of media uploads has been reached." # mp4hls command, part of Bento4 MP4HLS_COMMAND = "/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bin/mp4hls" @@ -501,9 +501,14 @@ ALLOW_CUSTOM_MEDIA_URLS = False # Whether to allow anonymous users to list all users ALLOW_ANONYMOUS_USER_LISTING = True +# Maximum number of media a user can upload +NUMBER_OF_MEDIA_USER_CAN_UPLOAD = 100 + # ffmpeg options FFMPEG_DEFAULT_PRESET = "medium" # see https://trac.ffmpeg.org/wiki/Encode/H.264 +ALLOWED_MEDIA_UPLOAD_TYPES = ["video", "audio", "image", "pdf"] + try: # keep a local_settings.py file for local overrides from .local_settings import * # noqa diff --git a/docs/admins_docs.md b/docs/admins_docs.md index b9de5704..b6ba4c58 100644 --- a/docs/admins_docs.md +++ b/docs/admins_docs.md @@ -26,6 +26,8 @@ - [23. SAML setup](#23-saml-setup) - [24. Identity Providers setup](#24-identity-providers-setup) - [25. Custom urls](#25-custom-urls) +- [26. Allowed files](#26-allowed-files) +- [27. User upload limits](#27-user-upload-limits) ## 1. Welcome @@ -976,3 +978,21 @@ Visiting the admin, you will see the Identity Providers tab and you can add one. ## 25. Custom urls To enable custom urls, set `ALLOW_CUSTOM_MEDIA_URLS = True` on settings.py or local_settings.py This will enable editing the URL of the media, while editing a media. If the URL is already taken you get a message you cannot update this. + +## 26. Allowed files +MediaCMS performs identification attempts on new file uploads and only allows certain file types specified in the `ALLOWED_MEDIA_UPLOAD_TYPES` setting. By default, only ["video", "audio", "image", "pdf"] files are allowed. + +When a file is not identified as one of these allowed types, the file gets removed from the system and there's an entry indicating that this is not a supported media type. + +If you want to change the allowed file types, edit the `ALLOWED_MEDIA_UPLOAD_TYPES` list in your `settings.py` or `local_settings.py` file. + +## 27. User upload limits +MediaCMS allows you to set a maximum number of media files that each user can upload. This is controlled by the `NUMBER_OF_MEDIA_USER_CAN_UPLOAD` setting in `settings.py` or `local_settings.py`. By default, this is set to 100 media items per user. + +When a user reaches this limit, they will no longer be able to upload new media until they delete some of their existing content. This limit applies regardless of the user's role or permissions in the system. + +To change the maximum number of uploads allowed per user, modify the `NUMBER_OF_MEDIA_USER_CAN_UPLOAD` value in your settings file: + +``` +NUMBER_OF_MEDIA_USER_CAN_UPLOAD = 5 +``` \ No newline at end of file diff --git a/files/methods.py b/files/methods.py index 78502a58..fb0f89dd 100644 --- a/files/methods.py +++ b/files/methods.py @@ -401,6 +401,32 @@ def clean_comment(raw_comment): return cleaned_comment +def user_allowed_to_upload(request): + """Any custom logic for whether a user is allowed + to upload content lives here + """ + + if request.user.is_anonymous: + return False + if is_mediacms_editor(request.user): + return True + + # Check if user has reached the maximum number of uploads + if hasattr(settings, 'NUMBER_OF_MEDIA_USER_CAN_UPLOAD'): + if models.Media.objects.filter(user=request.user).count() >= settings.NUMBER_OF_MEDIA_USER_CAN_UPLOAD: + return False + + if settings.CAN_ADD_MEDIA == "all": + return True + elif settings.CAN_ADD_MEDIA == "email_verified": + if request.user.email_is_verified: + return True + elif settings.CAN_ADD_MEDIA == "advancedUser": + if request.user.advancedUser: + return True + return False + + def kill_ffmpeg_process(filepath): """Kill ffmpeg process that is processing a specific file @@ -606,3 +632,7 @@ def copy_media(media_id): None """ pass + + +def is_media_allowed_type(media): + return media.media_type in settings.ALLOWED_MEDIA_UPLOAD_TYPES diff --git a/files/models/media.py b/files/models/media.py index 03e08b7e..5a2403c6 100644 --- a/files/models/media.py +++ b/files/models/media.py @@ -336,6 +336,15 @@ class Media(models.Model): video duration, encode """ self.set_media_type() + from ..methods import is_media_allowed_type + + if not is_media_allowed_type(self): + helpers.rm_file(self.media_file.path) + if self.state == "public": + self.state = "unlisted" + self.save(update_fields=["state"]) + return False + if self.media_type == "video": self.set_thumbnail(force=True) if settings.DO_NOT_TRANSCODE_VIDEO: diff --git a/files/views/media.py b/files/views/media.py index 75b5f3ab..10add85b 100644 --- a/files/views/media.py +++ b/files/views/media.py @@ -159,6 +159,7 @@ class MediaList(APIView): ) def post(self, request, format=None): # Add new media + serializer = MediaSerializer(data=request.data, context={"request": request}) if serializer.is_valid(): media_file = request.data["media_file"] diff --git a/files/views/pages.py b/files/views/pages.py index 3c21c74a..6d61da16 100644 --- a/files/views/pages.py +++ b/files/views/pages.py @@ -8,8 +8,8 @@ from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.views.decorators.csrf import csrf_exempt -from cms.permissions import user_allowed_to_upload from cms.version import VERSION +from files.methods import user_allowed_to_upload from users.models import User from .. import helpers @@ -26,6 +26,7 @@ from ..methods import ( create_video_trim_request, get_user_or_session, handle_video_chapters, + is_media_allowed_type, is_mediacms_editor, ) from ..models import Category, Media, Playlist, Subtitle, Tag, VideoTrimRequest @@ -238,6 +239,10 @@ def edit_media(request): if not (request.user.has_contributor_access_to_media(media) or is_mediacms_editor(request.user)): return HttpResponseRedirect("/") + + if not is_media_allowed_type(media): + return HttpResponseRedirect(media.get_absolute_url()) + if request.method == "POST": form = MediaMetadataForm(request.user, request.POST, request.FILES, instance=media) if form.is_valid(): @@ -577,6 +582,7 @@ def view_media(request): if video_msg and media.user == request.user: messages.add_message(request, messages.INFO, video_msg) + context["is_media_allowed_type"] = is_media_allowed_type(media) return render(request, "cms/media.html", context) diff --git a/templates/cms/add-media.html b/templates/cms/add-media.html index fdc0c502..4a15931c 100644 --- a/templates/cms/add-media.html +++ b/templates/cms/add-media.html @@ -119,7 +119,7 @@
- Contact the admin owners for more information. + Contact portal owners for more information. {% endif %} diff --git a/templates/cms/media.html b/templates/cms/media.html index 03c25d5f..0c862e19 100644 --- a/templates/cms/media.html +++ b/templates/cms/media.html @@ -126,7 +126,19 @@ {%endblock topimports %} -{% block content %}
{% endblock content %} +{% block content %} + {% if is_media_allowed_type %} + +
+ {% else %} +
+
+ This media type is not supported. +
+
+ {% endif %} + +{% endblock content %} {% block bottomimports %} diff --git a/uploader/views.py b/uploader/views.py index ce977eab..e2f3f082 100644 --- a/uploader/views.py +++ b/uploader/views.py @@ -8,8 +8,8 @@ from django.core.files import File from django.http import JsonResponse from django.views import generic -from cms.permissions import user_allowed_to_upload from files.helpers import rm_file +from files.methods import user_allowed_to_upload from files.models import Media from .fineuploader import ChunkedFineUploader