Commits

Sergiy Kuzmenko  committed 8992a55

added auth shortcuts

  • Participants
  • Parent commits c8b4704

Comments (0)

Files changed (11)

File docs/auth.txt

+AUTH SHORTCUTS
+--------------
+
+``xdjango.contrib.auth.shortcuts`` defines two handy function for retrieving users
+by permission. Permissions could be defined directly for the user or via groups.
+
+
+get_users_by_permission(permission_name, include_superusers=True)
+=================================================================
+
+Returns the queryset of User objects with the given permission. Permission name
+is in the form appname.permission, same format as used by
+``django.contrib.auth.decorators.permission_required``. If ``include_superusers``
+is set to False this function will only return users with explicitely set
+permissions.
+
+
+q_users_by_permission(permission_name, include_superusers=True)
+===============================================================
+
+Like get_users_by_permission but returns a Q object instead of queryset. Handy to
+use in model definitions with limit_choices_to:
+
+    class Task(models.Model):
+        name = models.CharField(max_length=128)
+        user = models.ForeignKey(
+            User,
+            limit_choices_to=q_users_by_permission("myapp.change_task")
+        )
+
+
+Troubleshooting
+================
+
+Users with overlapping permissions (e.g., the same permission is assigned via
+multiple groups) may appear multiple times in the queryset. To adress this problem remove
+duplicate permissions for the user or use the ``distinct()`` clause on the queryset:
+
+    get_users_by_permission("myapp.change_task").distinct()
+

File tests/authtests/__init__.py

Empty file added.

File tests/authtests/fixtures/test-data.json

+[
+    {
+        "model":"auth.group",
+        "pk": 1,
+        "fields": {
+            "name": "Task user group",
+            "permissions": [
+                ["change_task","authtests","task"]
+            ]
+        }
+    },
+
+    {
+        "model":"auth.user",
+        "pk": 1,
+        "fields": {
+            "username": "user_can_change_by_group",
+            "password": "pass",
+            "is_superuser": false,
+            "groups": [1],
+            "user_permissions": []
+        }
+    },
+    {
+        "model":"auth.user",
+        "pk": 2,
+        "fields": {
+            "username": "user_can_change_by_perm",
+            "password": "pass",
+            "is_superuser": false,
+            "groups": [],
+            "user_permissions": [["change_task","authtests","task"]]
+        }
+    },
+    {
+        "model":"auth.user",
+        "pk": 3,
+        "fields": {
+            "username": "superuser",
+            "password": "pass",
+            "is_superuser": true,
+            "groups": [],
+            "user_permissions": []
+        }
+    },
+    {
+        "model":"auth.user",
+        "pk": 4,
+        "fields": {
+            "username": "user_cannot_change",
+            "password": "pass",
+            "is_superuser": false,
+            "groups": [],
+            "user_permissions": []
+        }
+    }
+    
+]

File tests/authtests/forms.py

+from django.forms.models import ModelForm
+from models import Task
+
+class TaskForm(ModelForm):
+    class Meta:
+        model = Task

File tests/authtests/models.py

+from django.contrib.auth.models import User
+from django.db import models
+
+from xdjango.contrib.auth.shortcuts import q_users_by_permission
+
+class Task(models.Model):
+    name = models.CharField(max_length=128)
+    user = models.ForeignKey(User, limit_choices_to=q_users_by_permission("authtests.change_task"))

File tests/authtests/tests.py

+from django.test import TestCase
+from django.contrib.auth.models import User
+from xdjango.contrib.auth.shortcuts import get_users_by_permission
+from forms import TaskForm
+
+
+class UsersByPermissionTest(TestCase):
+    fixtures = ("test-data",)
+    
+    def test_users_by_permission(self):
+        """ Test that we get correct users by permission. """
+        
+        users = get_users_by_permission("authtests.change_task")
+        usernames = set([user.username for user in users])
+        
+        self.assertEqual(len(users), 3)
+        self.assertIn("user_can_change_by_group", usernames)
+        self.assertIn("user_can_change_by_perm", usernames)
+        self.assertIn("superuser", usernames)
+    
+    
+    def test_users_by_permission_excluding_superusers(self):
+        """ Test that we get correct users by explicitely given permissions. """
+        
+        users = get_users_by_permission("authtests.change_task", False)
+        usernames = set([user.username for user in users])
+        
+        self.assertEqual(len(users), 2)
+        self.assertIn("user_can_change_by_group", usernames)
+        self.assertIn("user_can_change_by_perm", usernames)
+        self.assertNotIn("superuser", usernames)
+
+
+class UsersByPermissionFormTest(TestCase):
+    fixtures = ("test-data",)
+    
+    def test_users_by_permission(self):
+        """ Test that we get correct users in the form. """
+        
+        form = TaskForm()
+        html_form = form.as_table()
+        
+        self.assertIn("user_can_change_by_group", html_form)
+        self.assertIn("user_can_change_by_perm", html_form)
+        self.assertIn("superuser", html_form)
+        
+        # add another user
+        
+        User.objects.create(username="spiderman", password="pass", is_superuser=True)
+        
+        form = TaskForm()
+        html_form = form.as_table()
+        self.assertIn("spiderman", html_form)
+        
+
+
+
+
+

File tests/runtests.py

File contents unchanged.

File tests/settings_sqlite.py

 SECRET_KEY = '~~~~~ secret key for testing ~~~~~'
 
 INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
     'modeltests',
+    'authtests',
 )
 

File xdjango/contrib/__init__.py

Empty file added.

File xdjango/contrib/auth/__init__.py

Empty file added.

File xdjango/contrib/auth/shortcuts.py

+from django.contrib.auth.models import User
+from django.db.models import Q
+
+def q_users_by_permission(permission_name, include_superusers=True):
+    """ Returns the Q object suitable for querying users by permission. If include_superusers
+    is true (default) all superusers will be also included. Otherwise
+    only users with explicitely set permissions will be included. """
+    
+    (appname, codename) = permission_name.split(".")
+    
+    query = \
+        Q(user_permissions__codename=codename, user_permissions__content_type__app_label=appname) | \
+        Q(groups__permissions__codename=codename, groups__permissions__content_type__app_label=appname)    
+    
+    if include_superusers:
+        query |= Q(is_superuser=True)
+    
+    return query
+
+
+def get_users_by_permission(permission_name, include_superusers=True):
+    """ Returns the queryset of User objects with the given permission. Permission name
+    is in the form appname.permission similar to the format
+    required by django.contrib.auth.decorators.permission_required
+    """
+    return User.objects.filter( q_users_by_permission(permission_name, include_superusers) )