Repository: ckan/ckan

Issue:
'_Globals' has no attribute 'user' : exception when using an IAuthenticator on CKAN 2.8.0
I'm putting together a new deployment based on the new CKAN v2.8.0 release. I'm using ckanext-ldap as an authenticator, though it looks like this bug would apply to any authenticator plugin.
This exact setup worked fine on CKAN v2.7.3.

### CKAN Version if known (or site URL)
CKAN v 2.8.0
ckanext-ldap @ `ckan-upgrade-2.8.0a`

### Please describe the expected behaviour
If the IAuthenticator plugin cannot authenticate the user, it does not set `g.user`, and CKAN should run the default authenticator.

### Please describe the actual behaviour
If the IAuthenticator plugin cannot authenticate the user, it does not set `g.user`, and CKAN tries to lookup `g.user` and crashes with traceback:
```
Traceback (most recent call last):
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1610, in full_dispatch_request
    rv = self.preprocess_request()
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1831, in preprocess_request
    rv = func()
  File "/usr/lib/ckan/venv/src/ckan/ckan/config/middleware/flask_app.py", line 281, in ckan_before_request
    identify_user()
  File "/usr/lib/ckan/venv/src/ckan/ckan/views/__init__.py", line 101, in identify_user
    if g.user:
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/usr/lib/ckan/venv/src/ckan/ckan/config/middleware/flask_app.py", line 334, in __getattr__
    return getattr(app_globals.app_globals, name)
AttributeError: '_Globals' object has no attribute 'user'
```
### What steps can be taken to reproduce the issue? 
* Install CKAN v2.8.0 as per documented instructions
* Install a plugin that implements IAuthenticator (In this case I am using the ckanext-ldap plugin in the 2.8.0 branch), that may not be able to authenticate the user, so may not set `g.user`.
* Run CKAN normally
* Attempt to load any page. 

What is odd is that this section of code at `identify_user` in `ckan/views/__init__.py` has not changed between v2.7.3 and v2.8.0. And the way the authenticator plugin handles/sets `g.user` has not changed either. I'm guessing this is caused by a change in the way the _Globals object behaves when it cannot find an attribute.


Discussion:
For the record, issue also occurs with _ckanext-saml2_ extension.
I propose to reopen this bug. I still have the very same error, but with a bit different Traceback.
The page loads correctly 4 times out of 5, if I am logged or not.
It does not happen if I run CKAN locally. I am using the very same container image on another server, where I do have this error. I checked multiple times, and the container has the patch that has been merged.
I use ckan-ldap, and the version I use has a patch too for this issue.

```
Traceback (most recent call last):
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1518, in handle_user_exception
    return handler(e)
  File "/usr/lib/ckan/venv/src/ckan/ckan/config/middleware/flask_app.py", line 453, in error_handler
    return base.render(u'error_document_template.html', extra_vars), 500
  File "/usr/lib/ckan/venv/src/ckan/ckan/lib/base.py", line 128, in render
    return flask_render_template(template_name, **extra_vars)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/templating.py", line 134, in render_template
    context, ctx.app)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/templating.py", line 116, in _render
    rv = template.render(context)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/jinja2/environment.py", line 989, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/jinja2/environment.py", line 754, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/error_document_template.html", line 1, in top-level template code
    {% extends "page.html" %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/page.html", line 1, in top-level template code
    {% extends "base.html" %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/base.html", line 101, in top-level template code
    {%- block page %}{% endblock -%}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/page.html", line 14, in block "page"
    {%- block header %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/page.html", line 15, in block "header"
    {% include "header.html" %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 1, in top-level template code
    {% block header_wrapper %} {% block header_account %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 1, in block "header_wrapper"
    {% block header_wrapper %} {% block header_account %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 4, in block "header_account"
    {% block header_account_container_content %} {% if c.userobj %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 51, in block "header_account_container_content"
    {% block header_account_notlogged %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 53, in block "header_account_notlogged"
    {% if h.check_access('user_create') %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/lib/helpers.py", line 1158, in check_access
    'user': c.user}
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/usr/lib/ckan/venv/src/ckan/ckan/config/middleware/flask_app.py", line 368, in __getattr__
    return getattr(app_globals.app_globals, name)
AttributeError: '_Globals' object has no attribute 'user'
----------------------------------------
Exception happened during processing of request from ('10.255.0.2', 51782)
Traceback (most recent call last):
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/paste/httpserver.py", line 1068, in process_request_in_thread
    self.finish_request(request, client_address)
  File "/usr/lib/python2.7/SocketServer.py", line 331, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.7/SocketServer.py", line 652, in __init__
    self.handle()
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/paste/httpserver.py", line 442, in handle
    BaseHTTPRequestHandler.handle(self)
  File "/usr/lib/python2.7/BaseHTTPServer.py", line 340, in handle
    self.handle_one_request()
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/paste/httpserver.py", line 437, in handle_one_request
    self.wsgi_execute()
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/paste/httpserver.py", line 287, in wsgi_execute
    self.wsgi_start_response)
  File "/usr/lib/ckan/venv/src/ckan/ckan/config/middleware/__init__.py", line 203, in __call__
    return self.apps[app_name](environ, start_response)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/repoze/who/middleware.py", line 86, in __call__
    app_iter = app(environ, wrapper.wrap_start_response)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/dec.py", line 147, in __call__
    resp = self.call_func(req, *args, **self.kwargs)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/dec.py", line 208, in call_func
    return self.func(req, *args, **kwargs)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/fanstatic/publisher.py", line 234, in __call__
    return request.get_response(self.app)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/request.py", line 1053, in get_response
    application, catch_exc_info=False)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/request.py", line 1022, in call_application
    app_iter = application(self.environ, start_response)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/dec.py", line 147, in __call__
    resp = self.call_func(req, *args, **self.kwargs)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/dec.py", line 208, in call_func
    return self.func(req, *args, **kwargs)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/fanstatic/injector.py", line 54, in __call__
    response = request.get_response(self.app)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/request.py", line 1053, in get_response
    application, catch_exc_info=False)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/webob/request.py", line 1022, in call_application
    app_iter = application(self.environ, start_response)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/beaker/middleware.py", line 156, in __call__
    return self.wrap_app(environ, session_start_response)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/app.py", line 1547, in handle_exception
    return self.finalize_request(handler(e), from_error_handler=True)
  File "/usr/lib/ckan/venv/src/ckan/ckan/config/middleware/flask_app.py", line 453, in error_handler
    return base.render(u'error_document_template.html', extra_vars), 500
  File "/usr/lib/ckan/venv/src/ckan/ckan/lib/base.py", line 128, in render
    return flask_render_template(template_name, **extra_vars)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/templating.py", line 134, in render_template
    context, ctx.app)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/flask/templating.py", line 116, in _render
    rv = template.render(context)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/jinja2/environment.py", line 989, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/jinja2/environment.py", line 754, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/error_document_template.html", line 1, in top-level template code
    {% extends "page.html" %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/page.html", line 1, in top-level template code
    {% extends "base.html" %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/base.html", line 101, in top-level template code
    {%- block page %}{% endblock -%}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/page.html", line 14, in block "page"
    {%- block header %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/page.html", line 15, in block "header"
    {% include "header.html" %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 1, in top-level template code
    {% block header_wrapper %} {% block header_account %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 1, in block "header_wrapper"
    {% block header_wrapper %} {% block header_account %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 4, in block "header_account"
    {% block header_account_container_content %} {% if c.userobj %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 51, in block "header_account_container_content"
    {% block header_account_notlogged %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/templates/header.html", line 53, in block "header_account_notlogged"
    {% if h.check_access('user_create') %}
  File "/usr/lib/ckan/venv/src/ckan/ckan/lib/helpers.py", line 1158, in check_access
    'user': c.user}
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/usr/lib/ckan/venv/local/lib/python2.7/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/usr/lib/ckan/venv/src/ckan/ckan/config/middleware/flask_app.py", line 368, in __getattr__
    return getattr(app_globals.app_globals, name)
```
Adding this code at the beginning of `check_access` fixes the issue for me:

```
    if not getattr(c, u'user', None):
        c.user = None
```

Resolving patch:
diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py
--- a/ckan/lib/helpers.py
+++ b/ckan/lib/helpers.py
@@ -48,7 +48,7 @@
 import ckan.plugins as p
 import ckan
 
-from ckan.common import _, ungettext, c, request, session, json
+from ckan.common import _, ungettext, c, g, request, session, json
 from markupsafe import Markup, escape
 
 
@@ -1154,8 +1154,10 @@ def sorted_extras(package_extras, auto_clean=False, subs=None, exclude=None):
 
 @core_helper
 def check_access(action, data_dict=None):
+    if not getattr(g, u'user', None):
+        g.user = ''
     context = {'model': model,
-               'user': c.user}
+               'user': g.user}
     if not data_dict:
         data_dict = {}
     try:


Test patch: