From a7a73a89efe51dc74769caeecdaf68294fb7614a Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Thu, 11 Mar 2021 23:16:56 +1300 Subject: [PATCH 1/2] Block non-image uploads - Added `IAuthFunctions` to `plugin.py` - Added chained auth functions for `user_update`, `organization_update` and `group_update` to check `request.files` mimetype --- ckanext/fortify/logic/__init__.py | 0 ckanext/fortify/logic/auth/__init__.py | 0 ckanext/fortify/logic/auth/update.py | 38 ++++++++++++++++++++++++++ ckanext/fortify/plugin.py | 14 ++++++++++ 4 files changed, 52 insertions(+) create mode 100644 ckanext/fortify/logic/__init__.py create mode 100644 ckanext/fortify/logic/auth/__init__.py create mode 100644 ckanext/fortify/logic/auth/update.py diff --git a/ckanext/fortify/logic/__init__.py b/ckanext/fortify/logic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ckanext/fortify/logic/auth/__init__.py b/ckanext/fortify/logic/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ckanext/fortify/logic/auth/update.py b/ckanext/fortify/logic/auth/update.py new file mode 100644 index 0000000..1471c49 --- /dev/null +++ b/ckanext/fortify/logic/auth/update.py @@ -0,0 +1,38 @@ +import ckan.plugins.toolkit as toolkit +import logging + +from ckan.common import _, request + +log = logging.getLogger(__name__) + + +def disallow_non_image_uploads(next_auth, context, data_dict): + try: + if request.files: + files_dict = dict(request.files) + image_upload = files_dict.get('image_upload') + if image_upload and image_upload.mimetype and 'image' not in image_upload.mimetype: + log.error('User {0} upload attempt blocked - file: {1}'.format( + context['user'], + image_upload + )) + return {'success': False, 'msg': _('Invalid filetype')} + except Exception as e: + log.error(str(e)) + + return next_auth(context, data_dict) + + +@toolkit.chained_auth_function +def user_update(next_auth, context, data_dict): + return disallow_non_image_uploads(next_auth, context, data_dict) + + +@toolkit.chained_auth_function +def organization_update(next_auth, context, data_dict): + return disallow_non_image_uploads(next_auth, context, data_dict) + + +@toolkit.chained_auth_function +def group_update(next_auth, context, data_dict): + return disallow_non_image_uploads(next_auth, context, data_dict) diff --git a/ckanext/fortify/plugin.py b/ckanext/fortify/plugin.py index f4e6471..fdaaee5 100644 --- a/ckanext/fortify/plugin.py +++ b/ckanext/fortify/plugin.py @@ -4,6 +4,7 @@ import logging from ckanext.fortify import helpers, validators, blueprint +from ckanext.fortify.logic.auth import update as auth_update from ckan.lib.uploader import ALLOWED_UPLOAD_TYPES config = toolkit.config @@ -37,6 +38,8 @@ def create(self, entity): if asbool(config.get('ckan.fortify.block_html_resource_uploads', False)): plugins.implements(plugins.IUploader, inherit=True) + plugins.implements(plugins.IAuthFunctions) + # IUploader def get_resource_uploader(self, data_dict): @@ -47,6 +50,17 @@ def get_resource_uploader(self, data_dict): # Returning None will make sure it uses the CKAN default uploader ResourceUpload return None + # IAuthFunctions + + def get_auth_functions(self): + return { + 'user_update': auth_update.user_update, + 'organization_update': auth_update.organization_update, + 'group_update': auth_update.group_update, + } + + + if asbool(config.get('ckan.fortify.enable_anti_csrf_tokens', False)) \ or asbool(config.get('ckan.fortify.enable_password_policy', False)) \ or asbool(config.get('ckan.fortify.force_html_resource_downloads', False)): From d841c54b08dd91f1f7c4a5c2364177a10f03a4af Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Fri, 12 Mar 2021 11:07:32 +1300 Subject: [PATCH 2/2] Updates: Block non-image uploads - Added chained auth functions for `group_create`, `organization_create` and `user_create` - Moved `disallow_non_image_uploads` into `logic/auth/helpers.py` --- ckanext/fortify/logic/auth/create.py | 18 ++++++++++++++ ckanext/fortify/logic/auth/helpers.py | 23 ++++++++++++++++++ ckanext/fortify/logic/auth/update.py | 34 ++++++--------------------- ckanext/fortify/plugin.py | 18 +++++++++----- 4 files changed, 60 insertions(+), 33 deletions(-) create mode 100644 ckanext/fortify/logic/auth/create.py create mode 100644 ckanext/fortify/logic/auth/helpers.py diff --git a/ckanext/fortify/logic/auth/create.py b/ckanext/fortify/logic/auth/create.py new file mode 100644 index 0000000..ea1e875 --- /dev/null +++ b/ckanext/fortify/logic/auth/create.py @@ -0,0 +1,18 @@ +import ckan.plugins.toolkit as toolkit + +from ckanext.fortify.logic.auth import helpers + + +@toolkit.chained_auth_function +def fortify_group_create(next_auth, context, data_dict): + return helpers.disallow_non_image_uploads(next_auth, context, data_dict) + + +@toolkit.chained_auth_function +def fortify_organization_create(next_auth, context, data_dict): + return helpers.disallow_non_image_uploads(next_auth, context, data_dict) + + +@toolkit.chained_auth_function +def fortify_user_create(next_auth, context, data_dict): + return helpers.disallow_non_image_uploads(next_auth, context, data_dict) diff --git a/ckanext/fortify/logic/auth/helpers.py b/ckanext/fortify/logic/auth/helpers.py new file mode 100644 index 0000000..98313d9 --- /dev/null +++ b/ckanext/fortify/logic/auth/helpers.py @@ -0,0 +1,23 @@ +import logging + +from ckan.common import _, request + +log = logging.getLogger(__name__) +invalid_filetype_response = {'success': False, 'msg': _('Invalid filetype')} + + +def disallow_non_image_uploads(next_auth, context, data_dict): + try: + if request.files: + files_dict = dict(request.files) + image_upload = files_dict.get('image_upload') + if image_upload and image_upload.mimetype and 'image' not in image_upload.mimetype: + log.error('User {0} upload attempt blocked - file: {1}'.format( + context['user'], + image_upload + )) + return invalid_filetype_response + except Exception as e: + log.error(str(e)) + + return next_auth(context, data_dict) diff --git a/ckanext/fortify/logic/auth/update.py b/ckanext/fortify/logic/auth/update.py index 1471c49..900cab8 100644 --- a/ckanext/fortify/logic/auth/update.py +++ b/ckanext/fortify/logic/auth/update.py @@ -1,38 +1,18 @@ import ckan.plugins.toolkit as toolkit -import logging -from ckan.common import _, request - -log = logging.getLogger(__name__) - - -def disallow_non_image_uploads(next_auth, context, data_dict): - try: - if request.files: - files_dict = dict(request.files) - image_upload = files_dict.get('image_upload') - if image_upload and image_upload.mimetype and 'image' not in image_upload.mimetype: - log.error('User {0} upload attempt blocked - file: {1}'.format( - context['user'], - image_upload - )) - return {'success': False, 'msg': _('Invalid filetype')} - except Exception as e: - log.error(str(e)) - - return next_auth(context, data_dict) +from ckanext.fortify.logic.auth import helpers @toolkit.chained_auth_function -def user_update(next_auth, context, data_dict): - return disallow_non_image_uploads(next_auth, context, data_dict) +def fortify_group_update(next_auth, context, data_dict): + return helpers.disallow_non_image_uploads(next_auth, context, data_dict) @toolkit.chained_auth_function -def organization_update(next_auth, context, data_dict): - return disallow_non_image_uploads(next_auth, context, data_dict) +def fortify_organization_update(next_auth, context, data_dict): + return helpers.disallow_non_image_uploads(next_auth, context, data_dict) @toolkit.chained_auth_function -def group_update(next_auth, context, data_dict): - return disallow_non_image_uploads(next_auth, context, data_dict) +def fortify_user_update(next_auth, context, data_dict): + return helpers.disallow_non_image_uploads(next_auth, context, data_dict) diff --git a/ckanext/fortify/plugin.py b/ckanext/fortify/plugin.py index fdaaee5..62bd1b3 100644 --- a/ckanext/fortify/plugin.py +++ b/ckanext/fortify/plugin.py @@ -4,7 +4,10 @@ import logging from ckanext.fortify import helpers, validators, blueprint -from ckanext.fortify.logic.auth import update as auth_update +from ckanext.fortify.logic.auth import ( + create as auth_create, + update as auth_update +) from ckan.lib.uploader import ALLOWED_UPLOAD_TYPES config = toolkit.config @@ -54,13 +57,16 @@ def get_resource_uploader(self, data_dict): def get_auth_functions(self): return { - 'user_update': auth_update.user_update, - 'organization_update': auth_update.organization_update, - 'group_update': auth_update.group_update, + # Create auth function overrides + 'group_create': auth_create.fortify_group_create, + 'organization_create': auth_create.fortify_organization_create, + 'user_create': auth_create.fortify_user_create, + # Update auth function overrides + 'group_update': auth_update.fortify_group_update, + 'organization_update': auth_update.fortify_organization_update, + 'user_update': auth_update.fortify_user_update, } - - if asbool(config.get('ckan.fortify.enable_anti_csrf_tokens', False)) \ or asbool(config.get('ckan.fortify.enable_password_policy', False)) \ or asbool(config.get('ckan.fortify.force_html_resource_downloads', False)):