Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #18991 -- Allow permission lookup by "if perm in perms" #404

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions django/contrib/auth/context_processors.py
Expand Up @@ -32,6 +32,17 @@ def __iter__(self):
# I am large, I contain multitudes.
raise TypeError("PermWrapper is not iterable.")

def __contains__(self, perm_name):
"""
Lookup by "someapp" or "someapp.someperm" in perms.
"""
if '.' not in perm_name:
# The name refers to module.
return bool(self[perm_name])
module_name, perm_name = perm_name.split('.', 1)
return self[module_name][perm_name]


def auth(request):
"""
Returns context variables required by apps that use Django's authentication
Expand Down
41 changes: 34 additions & 7 deletions django/contrib/auth/tests/context_processors.py
Expand Up @@ -3,20 +3,22 @@
from django.conf import global_settings
from django.contrib.auth import authenticate
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.context_processors import PermWrapper, PermLookupDict
from django.db.models import Q
from django.test import TestCase
from django.test.utils import override_settings


class MockUser(object):
def has_module_perm(self, perm):
if perm == 'mockapp.someapp':
def has_module_perms(self, perm):
if perm == 'mockapp':
return True
return False

def has_perm(self, perm):
if perm == 'someperm':
if perm == 'mockapp.someperm':
return True
return False

Expand All @@ -40,13 +42,19 @@ def __eq__(self, other):

def test_permwrapper_in(self):
"""
Test that 'something' in PermWrapper doesn't end up in endless loop.
Test that 'something' in PermWrapper works as expected.
"""
perms = PermWrapper(MockUser())
with self.assertRaises(TypeError):
self.EQLimiterObject() in perms
# Works for modules and full permissions.
self.assertTrue('mockapp' in perms)
self.assertFalse('nonexisting' in perms)
self.assertTrue('mockapp.someperm' in perms)
self.assertFalse('mockapp.nonexisting' in perms)

def test_permlookupdict_in(self):
"""
No endless loops if accessed with 'in' - refs #18979.
"""
pldict = PermLookupDict(MockUser(), 'mockapp')
with self.assertRaises(TypeError):
self.EQLimiterObject() in pldict
Expand Down Expand Up @@ -92,9 +100,28 @@ def test_session_is_accessed(self):
self.assertContains(response, "Session accessed")

def test_perms_attrs(self):
self.client.login(username='super', password='secret')
u = User.objects.create_user(username='normal', password='secret')
u.user_permissions.add(
Permission.objects.get(
content_type=ContentType.objects.get_for_model(Permission),
codename='add_permission'))
self.client.login(username='normal', password='secret')
response = self.client.get('/auth_processor_perms/')
self.assertContains(response, "Has auth permissions")
self.assertContains(response, "Has auth.add_permission permissions")
self.assertNotContains(response, "nonexisting")

def test_perm_in_perms_attrs(self):
u = User.objects.create_user(username='normal', password='secret')
u.user_permissions.add(
Permission.objects.get(
content_type=ContentType.objects.get_for_model(Permission),
codename='add_permission'))
self.client.login(username='normal', password='secret')
response = self.client.get('/auth_processor_perm_in_perms/')
self.assertContains(response, "Has auth permissions")
self.assertContains(response, "Has auth.add_permission permissions")
self.assertNotContains(response, "nonexisting")

def test_message_attrs(self):
self.client.login(username='super', password='secret')
Expand Down
@@ -0,0 +1,4 @@
{% if 'auth' in perms %}Has auth permissions{% endif %}
{% if 'auth.add_permission' in perms %}Has auth.add_permission permissions{% endif %}
{% if 'nonexisting' in perms %}nonexisting perm found{% endif %}
{% if 'auth.nonexisting' in perms %}auth.nonexisting perm found{% endif %}
@@ -1 +1,4 @@
{% if perms.auth %}Has auth permissions{% endif %}
{% if perms.auth.add_permission %}Has auth.add_permission permissions{% endif %}
{% if perms.nonexisting %}nonexisting perm found{% endif %}
{% if perms.auth.nonexisting in perms %}auth.nonexisting perm found{% endif %}
5 changes: 5 additions & 0 deletions django/contrib/auth/tests/urls.py
Expand Up @@ -37,6 +37,10 @@ def auth_processor_perms(request):
return render_to_response('context_processors/auth_attrs_perms.html',
RequestContext(request, {}, processors=[context_processors.auth]))

def auth_processor_perm_in_perms(request):
return render_to_response('context_processors/auth_attrs_perm_in_perms.html',
RequestContext(request, {}, processors=[context_processors.auth]))

def auth_processor_messages(request):
info(request, "Message 1")
return render_to_response('context_processors/auth_attrs_messages.html',
Expand All @@ -58,6 +62,7 @@ def userpage(request):
(r'^auth_processor_attr_access/$', auth_processor_attr_access),
(r'^auth_processor_user/$', auth_processor_user),
(r'^auth_processor_perms/$', auth_processor_perms),
(r'^auth_processor_perm_in_perms/$', auth_processor_perm_in_perms),
(r'^auth_processor_messages/$', auth_processor_messages),
url(r'^userpage/(.+)/$', userpage, name="userpage"),
)
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/1.5.txt
Expand Up @@ -155,6 +155,10 @@ Django 1.5 also includes several smaller improvements worth noting:
objects fetched into memory. See :meth:`QuerySet.delete()
<django.db.models.query.QuerySet.delete>` for details.

* If using :class:`~django.template.RequestContext`, it is now possible to
look up permissions by using
``{% if 'someapp.someperm' in perms %}`` in templates.

Backwards incompatible changes in 1.5
=====================================

Expand Down
14 changes: 14 additions & 0 deletions docs/topics/auth.txt
Expand Up @@ -1710,6 +1710,20 @@ Thus, you can check permissions in template ``{% if %}`` statements:
<p>You don't have permission to do anything in the foo app.</p>
{% endif %}

.. versionadded:: 1.5
Permission lookup by "if in".

It is possible to also look permissions up by ``{% if in %}`` statements.
For example:

.. code-block:: html+django

{% if 'foo' in perms %}
{% if 'foo.can_vote' in perms %}
<p>In lookup works, too.</p>
{% endif %}
{% endif %}

Groups
======

Expand Down