Skip to content

Commit

Permalink
Merge branch 'develop' into uat
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkCalvert committed Aug 16, 2023
2 parents 76fe9d7 + 8c58cc4 commit 19e5a38
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 84 deletions.
106 changes: 23 additions & 83 deletions ckanext/qdes/access/middleware.py
Original file line number Diff line number Diff line change
@@ -1,89 +1,29 @@
import logging
import ckan.lib.api_token as api_token
import ckan.model as model
import ckan.plugins.toolkit as toolkit
import ckan.views as views
import six

from ckan.common import json
import ckan.authz as authz
import ckan.views.api as api

log = logging.getLogger(__name__)


class QdesAuthMiddleware(object):
def __init__(self, app, app_conf):
self.app = app

def __call__(self, environ, start_response):
# if logged in via browser cookies all pages accessible
if 'repoze.who.identity' not in environ:
# If api request check for api key
if environ['PATH_INFO'].startswith('/api/action/') \
or environ['PATH_INFO'].startswith('/api/util/') \
or environ['PATH_INFO'].startswith('/api/i18n/'):
# All API requests require authenticated api keys
if not self._get_user_for_apikey(environ):
log.debug(f"Unauthorized api accessed: {environ['PATH_INFO']}")
return_dict = {}
return_dict[u'error'] = {u'__type': u'Authorization Error',
u'message': toolkit._(u'Access denied')}
return_dict[u'success'] = False
response_msg = json.dumps(return_dict, for_json=True).encode('utf8')
status = '403 Unauthorized'
headers = [('Content-type', 'application/json;charset=utf-8')]
start_response(status, headers)
# Return now as we want to end the request
return [response_msg]
else:
# The only pages unauthorized users have access to are below
# service login, saml2login, acs (SAML Assertion Consumer Service), user unauthorised and logout pages
# But still allow unauthorized access to assets
unauthorized_pages_allowed = toolkit.aslist(toolkit.config.get('ckanext.qdes_access.unauthorized_pages_allowed', ''))
if environ['PATH_INFO'] not in unauthorized_pages_allowed \
and not environ['PATH_INFO'].startswith('/base') \
and not environ['PATH_INFO'].startswith('/webassets') \
and not environ['PATH_INFO'].startswith('/images') \
and not environ['PATH_INFO'].startswith('/css') \
and not environ['PATH_INFO'].startswith('/js') \
and not environ['PATH_INFO'].startswith('/_debug') \
and not environ['PATH_INFO'].startswith('/uploads'):

log.debug(f"Unauthorized page accessed: {environ['PATH_INFO']}")
# Status code needs to be 3xx (redirection) for Location header to be used
status = "302 Unauthorized"
location = toolkit.config.get('ckanext.qdes_access.unauthorized_redirect_location', '/user/saml2login')
headers = [('Location', location),
('Content-Length', '0')]
log.debug(f"Redirecting to: {location}")
start_response(status, headers)
# Return now as we want to end the request
return []

return self.app(environ, start_response)

def _get_user_for_apikey(self, environ):
# Based off method _get_user_for_apikey from https://github.com/ckan/ckan/blob/afe077e78e929a38112980d24df7c5d057b363ec/ckan/views/__init__.py
# Updated to remove request and use paramater environ
apikey_header_name = toolkit.config.get(views.APIKEY_HEADER_NAME_KEY,
views.APIKEY_HEADER_NAME_DEFAULT)
apikey = environ.get(apikey_header_name, u'')
if not apikey:
# For misunderstanding old documentation (now fixed).
apikey = environ.get(u'HTTP_AUTHORIZATION', u'')
if not apikey:
apikey = environ.get(u'Authorization', u'')
# Forget HTTP Auth credentials (they have spaces).
if u' ' in apikey:
apikey = u''

if not apikey:
return None

apikey = six.ensure_text(apikey, errors=u"ignore")
query = model.Session.query(model.User)
user = query.filter_by(apikey=apikey).first()

if not user:
user = api_token.get_user_from_token(apikey)

return user
def qdes_access_before_request():
if toolkit.request.endpoint not in toolkit.aslist(toolkit.config.get('ckanext.qdes_access.allowed_cached_endpoints')):
log.debug(f'Setting __no_cache__ for endpoint {toolkit.request.endpoint}')
toolkit.request.environ['__no_cache__'] = True
else:
log.debug(f'Allowed cached endpoint {toolkit.request.endpoint}')

if not authz.auth_is_loggedin_user() and toolkit.request.endpoint not in toolkit.aslist(toolkit.config.get('ckanext.qdes_access.unauthenticated_allowed_endpoints')):
if toolkit.request.endpoint.startswith('api'):
log.warning(f'Unauthenticated access to {toolkit.request.endpoint}. Returning 403 Authorization Error.')
return_dict = {}
return_dict['error'] = {'__type': 'Authorization Error',
'message': toolkit._('Access denied')}
return_dict['success'] = False
toolkit.request.environ['__no_cache__'] = True
return api._finish(403, return_dict, content_type='json')
else:
unauthenticated_redirect_endpoint = toolkit.config.get('ckanext.qdes_access.unauthenticated_redirect_endpoint')
log.warning(f'Unauthenticated access to {toolkit.request.endpoint}. Redirecting to {unauthenticated_redirect_endpoint} page.')
toolkit.request.environ['__no_cache__'] = True
return toolkit.redirect_to(unauthenticated_redirect_endpoint)
3 changes: 2 additions & 1 deletion ckanext/qdes/access/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ def get_auth_functions(self):

# IMiddleware
def make_middleware(self, app, config):
return middleware.QdesAuthMiddleware(app, config)
app.before_request(middleware.qdes_access_before_request)
return app

# IConfigurer
def update_config(self, config_):
Expand Down

0 comments on commit 19e5a38

Please sign in to comment.