Skip to content

Commit

Permalink
[QOLDEV-935] clean up obsolete Pylons intercepts
Browse files Browse the repository at this point in the history
  • Loading branch information
ThrawnCA committed Aug 30, 2024
1 parent 3862d53 commit 28b194b
Showing 1 changed file with 4 additions and 151 deletions.
155 changes: 4 additions & 151 deletions ckanext/qgov/common/intercepts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,18 @@
""" Monkey-patch CKAN core functions with our own implementations.
"""

import json
from logging import getLogger
import re
import six

import requests

from ckan.lib.navl.dictization_functions import Missing
from ckan.lib.navl.validators import ignore_missing, not_empty
from ckan.lib.redis import connect_to_redis
import ckan.logic
import ckan.logic.action.update
import ckan.logic.schema as schemas
from ckan.logic import validators
from ckan.plugins import toolkit
from ckan.plugins.toolkit import _, abort, c, g, h, get_validator, \
chained_action, redirect_to, request
from ckan.plugins.toolkit import _, get_or_bust, get_validator, \
chained_action

from . import helpers
from .authenticator import unlock_account, LOGIN_THROTTLE_EXPIRY
from .urlm import get_purl_response
from .authenticator import unlock_account
from .user_creation import helpers as user_creation_helpers

LOG = getLogger(__name__)
Expand All @@ -33,8 +24,6 @@
DEFAULT_UPDATE_USER_SCHEMA = schemas.default_update_user_schema()
RESOURCE_SCHEMA = schemas.default_resource_schema()

EMAIL_REGEX = re.compile(r"[^@]+@[^@]+\.[^@]+")


def configure(config):
global password_min_length
Expand Down Expand Up @@ -63,40 +52,6 @@ def set_intercepts():
schemas.default_resource_schema = default_resource_schema


def set_pylons_intercepts():
from ckan.controllers.user import UserController
from ckan.controllers.package import PackageController
try:
from ckan.controllers.storage import StorageController
storage_enabled = True
except ImportError:
storage_enabled = False
from ckan.lib import base
from ckan.controllers import group, package, user

global LOGGED_IN, PACKAGE_EDIT, RESOURCE_EDIT, RESOURCE_DOWNLOAD, STORAGE_DOWNLOAD, ABORT
LOGGED_IN = UserController.logged_in
PACKAGE_EDIT = PackageController._save_edit
RESOURCE_EDIT = PackageController.resource_edit
RESOURCE_DOWNLOAD = PackageController.resource_download
ABORT = base.abort

UserController.logged_in = logged_in
PackageController._save_edit = save_edit
PackageController.resource_edit = validate_resource_edit

if storage_enabled:
STORAGE_DOWNLOAD = StorageController.file
StorageController.file = storage_download_with_headers
PackageController.resource_download = resource_download_with_headers

# Monkey-patch ourselves into the 404 handler
base.abort = abort_with_purl
group.abort = abort_with_purl
package.abort = abort_with_purl
user.abort = abort_with_purl


def user_password_validator(key, data, errors, context):
""" Strengthen the built-in password validation to require more length and complexity.
"""
Expand Down Expand Up @@ -210,108 +165,6 @@ def user_update(original_action, context, data_dict):
'''
return_value = original_action(context, data_dict)
if u'reset_key' in data_dict:
account_id = ckan.logic.get_or_bust(data_dict, 'id')
account_id = get_or_bust(data_dict, 'id')
unlock_account(account_id)
return return_value


def logged_in(self):
""" Provide a custom error code when login fails due to account lockout.
"""
if not c.user:
# a number of failed login attempts greater than 10 indicates
# that the locked user is associated with the current request
redis_conn = connect_to_redis()

for key in redis_conn.keys('{}.ckanext.qgov.login_attempts.*'.format(g.site_id)):
login_attempts = redis_conn.get(key)
if login_attempts > 10:
redis_conn.set(key, 10, ex=LOGIN_THROTTLE_EXPIRY)
return self.login('account-locked')
return LOGGED_IN(self)


def save_edit(self, name_or_id, context, package_type=None):
'''
Intercept save_edit
Replace author, maintainer, maintainer_email
'''
# Harvest package types do not have 'author_email' in their schema.
if package_type == 'harvest':
return PACKAGE_EDIT(self, name_or_id, context, package_type)

try:
author_email = request.POST.getone('author_email')
except Exception:
return abort(400, _('No author email or multiple author emails provided'))
if not EMAIL_REGEX.match(author_email):
return abort(400, _('Invalid email.'))

if 'author' in request.POST:
request.POST.__delitem__('author')
if 'maintainer' in request.POST:
request.POST.__delitem__('maintainer')
if 'maintainer_email' in request.POST:
request.POST.__delitem__('maintainer_email')

request.POST.add('author', author_email)
request.POST.add('maintainer', author_email)
request.POST.add('maintainer_email', author_email)

return PACKAGE_EDIT(self, name_or_id, context, package_type=None)


def validate_resource_edit(self, id, resource_id,
data=None, errors=None, error_summary=None):
'''
Intercept save_edit
Replace author, maintainer, maintainer_email
'''
if 'validation_schema' in request.POST and 'format' in request.POST:
resource_format = request.POST.getone('format')
validation_schema = request.POST.getone('validation_schema')
if resource_format == 'CSV' and validation_schema and validation_schema != '':
schema_url = helpers.generate_download_url(id, validation_schema)
data_url = helpers.generate_download_url(id, resource_id)
validation_url = "http://goodtables.okfnlabs.org/api/run?format=csv&schema={0}&data={1}&row_limit=100000&report_limit=1000&report_type=grouped".format(schema_url, data_url)
req = requests.get(validation_url, verify=False)
if req.status_code == requests.codes.ok:
response_text = json.loads(req.text)
if response_text['success']:
h.flash_success("CSV was validated successfully against the selected schema")
else:
h.flash_error("CSV was NOT validated against the selected schema")

return RESOURCE_EDIT(self, id, resource_id, data, errors, error_summary)


def _set_download_headers(response):
response.headers['Content-Disposition'] = 'attachment'
response.headers['X-Content-Type-Options'] = 'nosniff'


def storage_download_with_headers(self, label):
""" Add security headers to protect against download-based exploits.
"""
file_download = STORAGE_DOWNLOAD(self, label)
_set_download_headers(toolkit.response)
return file_download


def resource_download_with_headers(self, id, resource_id, filename=None):
""" Add security headers to protect against download-based exploits.
"""
file_download = RESOURCE_DOWNLOAD(self, id, resource_id, filename)
_set_download_headers(toolkit.response)
return file_download


def abort_with_purl(status_code=None, detail='', headers=None, comment=None):
""" Consult PURL about a 404, redirecting if it reports a new URL.
"""
if status_code == 404:
redirect_url = get_purl_response(request.url)
if redirect_url:
return redirect_to(redirect_url, 301)

return ABORT(status_code, detail, headers, comment)

0 comments on commit 28b194b

Please sign in to comment.