From d5a4f209c3889a76a23a19f3212ab2d7b5c62e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Sun, 30 Sep 2012 18:13:23 +0300 Subject: [PATCH] Fixed #18991 -- Allowed permission lookup by "if in" When looking permissions from PermWrapper it is now possible to use {% if "someapp.someperm" in perms %} instead of {% if perms.someapp.someperm %}. --- django/contrib/auth/context_processors.py | 11 +++++ .../contrib/auth/tests/context_processors.py | 41 +++++++++++++++---- .../auth_attrs_perm_in_perms.html | 4 ++ .../context_processors/auth_attrs_perms.html | 3 ++ django/contrib/auth/tests/urls.py | 5 +++ docs/releases/1.5.txt | 4 ++ docs/topics/auth.txt | 14 +++++++ 7 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 django/contrib/auth/tests/templates/context_processors/auth_attrs_perm_in_perms.html diff --git a/django/contrib/auth/context_processors.py b/django/contrib/auth/context_processors.py index 77face01a7..5929505359 100644 --- a/django/contrib/auth/context_processors.py +++ b/django/contrib/auth/context_processors.py @@ -32,6 +32,17 @@ class PermWrapper(object): # 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 diff --git a/django/contrib/auth/tests/context_processors.py b/django/contrib/auth/tests/context_processors.py index 8d87e0ae15..32fea8ac80 100644 --- a/django/contrib/auth/tests/context_processors.py +++ b/django/contrib/auth/tests/context_processors.py @@ -3,6 +3,8 @@ import os 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 @@ -10,13 +12,13 @@ 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 @@ -40,13 +42,19 @@ class PermWrapperTests(TestCase): 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 @@ -92,9 +100,28 @@ class AuthContextProcessorTests(TestCase): 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') diff --git a/django/contrib/auth/tests/templates/context_processors/auth_attrs_perm_in_perms.html b/django/contrib/auth/tests/templates/context_processors/auth_attrs_perm_in_perms.html new file mode 100644 index 0000000000..3a18cd7405 --- /dev/null +++ b/django/contrib/auth/tests/templates/context_processors/auth_attrs_perm_in_perms.html @@ -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 %} diff --git a/django/contrib/auth/tests/templates/context_processors/auth_attrs_perms.html b/django/contrib/auth/tests/templates/context_processors/auth_attrs_perms.html index a5db868e9e..6f441afc10 100644 --- a/django/contrib/auth/tests/templates/context_processors/auth_attrs_perms.html +++ b/django/contrib/auth/tests/templates/context_processors/auth_attrs_perms.html @@ -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 %} diff --git a/django/contrib/auth/tests/urls.py b/django/contrib/auth/tests/urls.py index dbbd35ee88..8f9e848aa9 100644 --- a/django/contrib/auth/tests/urls.py +++ b/django/contrib/auth/tests/urls.py @@ -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', @@ -58,6 +62,7 @@ urlpatterns = urlpatterns + patterns('', (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"), ) diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index b371214994..c39592122b 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -180,6 +180,10 @@ Django 1.5 also includes several smaller improvements worth noting: and inversion, expanding the types of expressions that can be passed to the database. +* When 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 ===================================== diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 1d320df9c1..0a19f5ed5a 100644 --- a/docs/topics/auth.txt +++ b/docs/topics/auth.txt @@ -1710,6 +1710,20 @@ Thus, you can check permissions in template ``{% if %}`` statements:

You don't have permission to do anything in the foo app.

{% 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 %} +

In lookup works, too.

+ {% endif %} + {% endif %} + Groups ======