Repository: django-cms/django-cms

Issue:
Placeholder.has_foo_permission() missing anonymous user check
Regression in f6e6fd66e820f60238dff76e1d4a755401554d51. I get the following traceback when running tests. Can try to provide further steps to reproduce if needed.

```
======================================================================
ERROR: test_non_existent_category (announcements.tests.TestViews)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/shortcuts.py", line 115, in get_object_or_404
    return queryset.get(*args, **kwargs)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/db/models/query.py", line 357, in get
    self.model._meta.object_name)
announcements.models.DoesNotExist: AnnouncementCategory matching query does not exist.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/core/handlers/base.py", line 111, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/views/generic/base.py", line 69, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/views/generic/base.py", line 87, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/views/generic/list.py", line 145, in get
    self.object_list = self.get_queryset()
  File "/home/tim/code/cosnet_cms/announcements/views.py", line 30, in get_queryset
    self.category = get_object_or_404(AnnouncementCategory, slug=self.kwargs['slug'])
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/shortcuts.py", line 117, in get_object_or_404
    raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
django.http.response.Http404: No AnnouncementCategory matches the given query.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/tim/code/cosnet_cms/announcements/tests.py", line 19, in test_non_existent_category
    response = self.client.get('/foo/')
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/test/client.py", line 470, in get
    **extra)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/test/client.py", line 286, in get
    return self.generic('GET', path, secure=secure, **r)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/test/client.py", line 358, in generic
    return self.request(**r)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/test/client.py", line 440, in request
    six.reraise(*exc_info)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/utils/six.py", line 659, in reraise
    raise value
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/core/handlers/base.py", line 150, in get_response
    response = callback(request, **param_dict)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/utils/decorators.py", line 105, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/views/defaults.py", line 31, in page_not_found
    body = template.render(RequestContext(request, {'request_path': request.path}))
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/base.py", line 148, in render
    return self._render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/test/utils.py", line 88, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/debug.py", line 80, in render_node
    return node.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/loader_tags.py", line 126, in render
    return compiled_parent._render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/test/utils.py", line 88, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/debug.py", line 80, in render_node
    return node.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/loader_tags.py", line 126, in render
    return compiled_parent._render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/test/utils.py", line 88, in instrumented_test_render
    return self.nodelist.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/debug.py", line 80, in render_node
    return node.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/classytags/core.py", line 106, in render
    return self.render_tag(context, **kwargs)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/sekizai/templatetags/sekizai_tags.py", line 76, in render_tag
    rendered_contents = nodelist.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/debug.py", line 80, in render_node
    return node.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/classytags/core.py", line 106, in render
    return self.render_tag(context, **kwargs)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/sekizai/templatetags/sekizai_tags.py", line 76, in render_tag
    rendered_contents = nodelist.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/debug.py", line 80, in render_node
    return node.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/classytags/core.py", line 106, in render
    return self.render_tag(context, **kwargs)
  File "/home/tim/code/django-cms/cms/templatetags/cms_tags.py", line 679, in render_tag
    rendered_contents = nodelist.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/django/template/debug.py", line 80, in render_node
    return node.render(context)
  File "/home/tim/.virtualenvs/cosnet-cms-dev/lib/python3.4/site-packages/classytags/core.py", line 106, in render
    return self.render_tag(context, **kwargs)
  File "/home/tim/code/django-cms/cms/templatetags/cms_tags.py", line 1094, in render_tag
    content = render_placeholder(placeholder, context, name_fallback=code, default=nodelist)
  File "/home/tim/code/django-cms/cms/plugin_rendering.py", line 108, in render_placeholder
    if placeholder.has_change_permission(request):
  File "/home/tim/code/django-cms/cms/models/placeholdermodel.py", line 123, in has_change_permission
    return self._get_permission(request, 'change')
  File "/home/tim/code/django-cms/cms/models/placeholdermodel.py", line 109, in _get_permission
    if request.user.is_superuser:
AttributeError: 'WSGIRequest' object has no attribute 'user'
```



Discussion:

Resolving patch:
diff --git a/cms/models/placeholdermodel.py b/cms/models/placeholdermodel.py
--- a/cms/models/placeholdermodel.py
+++ b/cms/models/placeholdermodel.py
@@ -106,7 +106,7 @@ def _get_permission(self, request, key):
         the key can be: 'add', 'change' or 'delete'. For each attached object
         permission has to be granted either on attached model or on attached object.
         """
-        if request.user.is_superuser:
+        if getattr(request, 'user', None) and request.user.is_superuser:
             return True
         if self.page:
             return self._get_object_permission(self.page, request, key)
@@ -114,6 +114,8 @@ def _get_permission(self, request, key):
             return self._get_object_permission(obj, request, key)
 
     def _get_object_permission(self, obj, request, key):
+        if not getattr(request, 'user', None):
+            return False
         opts = obj._meta
         perm_accessor = getattr(opts, 'get_%s_permission' % key)
         perm_code = '%s.%s' % (opts.app_label, perm_accessor())


Test patch:
diff --git a/cms/tests/placeholder.py b/cms/tests/placeholder.py
--- a/cms/tests/placeholder.py
+++ b/cms/tests/placeholder.py
@@ -14,7 +14,7 @@
 from django.template import TemplateSyntaxError, Template
 from django.template.context import Context, RequestContext
 from django.template.loader import get_template
-from django.test import TestCase
+from django.test import TestCase, RequestFactory
 from django.test.utils import override_settings
 from django.utils.numberformat import format
 from djangocms_link.cms_plugins import LinkPlugin
@@ -856,6 +856,13 @@ def push(self):
 
         context_en = NoPushPopContext()
 
+        # no user: no placeholders but no error either
+        factory = RequestFactory()
+        context_en['request'] = factory.get(page_en.get_absolute_url())
+        render_placeholder(ex.placeholder, context_en)
+        self.assertEqual(len(context_en['request'].placeholders), 0)
+        self.assertNotIn(ex.placeholder, context_en['request'].placeholders)
+
         # request.placeholders is populated for superuser
         context_en['request'] = self.get_request(language="en", page=page_en)
         context_en['request'].user = self.get_superuser()