[Python-modules-commits] [django-guardian] 08/13: Import django-guardian_1.3.orig.tar.gz
Brian May
bam at moszumanska.debian.org
Tue Nov 3 10:02:00 UTC 2015
This is an automated email from the git hooks/post-receive script.
bam pushed a commit to branch master
in repository django-guardian.
commit 94753ae8e998c63b43db42d8921dd6c8804c59b3
Author: Brian May <brian at linuxpenguins.xyz>
Date: Tue Nov 3 19:50:17 2015 +1100
Import django-guardian_1.3.orig.tar.gz
---
.travis.yml | 27 ++--
AUTHORS | 4 +
CHANGES | 11 +-
benchmarks/run_benchmarks.py | 14 +-
docs/userguide/assign.rst | 5 +-
example_project/settings.py | 6 +-
guardian/__init__.py | 2 +-
guardian/admin.py | 16 +-
guardian/backends.py | 70 ++++++---
guardian/compat.py | 18 ++-
guardian/management/__init__.py | 5 +-
guardian/migrations/0001_initial.py | 163 +++++++--------------
guardian/models.py | 7 +-
guardian/shortcuts.py | 78 +++++++++-
.../0001_initial.py | 0
...tpermission_object_pk__add_field_userobjectp.py | 0
.../0003_update_objectpermission_object_pk.py | 0
...tpermission_object_id__del_unique_groupobjec.py | 0
...tpermission_object_pk__chg_field_userobjectp.py | 0
guardian/south_migrations/__init__.py | 0
guardian/templatetags/guardian_tags.py | 10 +-
guardian/testapp/migrations/0001_initial.py | 2 +-
guardian/testapp/tests/admin_test.py | 4 +-
guardian/testapp/tests/direct_rel_test.py | 16 ++
guardian/testapp/tests/orphans_test.py | 5 +-
guardian/testapp/tests/shortcuts_test.py | 157 +++++++++++++++++++-
guardian/testsettings.py | 5 +-
tests.py | 9 +-
tox.ini | 18 +++
29 files changed, 471 insertions(+), 181 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index e74e6b4..b7b81be 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,23 @@
language: python
+sudo: false
python:
- 2.6
- 2.7
- 3.3
+ - 3.4
env:
- DJANGO_VERSION=1.2.7
- DJANGO_VERSION=1.3.7
- - DJANGO_VERSION=1.4.16
- - DJANGO_VERSION=1.5.11
- - DJANGO_VERSION=1.6.8
+ - DJANGO_VERSION=1.4.19
+ - DJANGO_VERSION=1.5.12
+ - DJANGO_VERSION=1.6.10
+ - DJANGO_VERSION=1.7.4
+ - DJANGO_VERSION=1.8
install:
- - pip install -q mock==0.8 Django==$DJANGO_VERSION coverage coveralls
- - pip install . --use-mirrors
+ - travis_retry pip install -q mock==1.0.1 Django==$DJANGO_VERSION coverage coveralls
+ - travis_retry pip install --use-mirrors
script:
- coverage run --source=guardian setup.py test
@@ -33,9 +37,12 @@ matrix:
- python: 3.3
env: DJANGO_VERSION=1.3.7
- python: 3.3
- env: DJANGO_VERSION=1.4.16
- include:
- - python: 3.3
- env: DJANGO_VERSION=1.7.1
+ env: DJANGO_VERSION=1.4.19
+ - python: 3.4
+ env: DJANGO_VERSION=1.2.7
+ - python: 3.4
+ env: DJANGO_VERSION=1.3.7
- python: 3.4
- env: DJANGO_VERSION=1.7.1
+ env: DJANGO_VERSION=1.4.19
+ - python: 2.6
+ - env: DJANGO_VERSION=1.7.4
diff --git a/AUTHORS b/AUTHORS
index 57da6ac..f8c96cc 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -50,3 +50,7 @@ Authors ordered by first contribution
- Morgan Aubert <morgan.aubert at zoho.com>
- Brian May <brian at microcomaustralia.com.au>
- Troy Grosfield <troy.grosfield at gmail.com>
+- Michael Drescher <kaesemeister at gmail.com>
+- Verena Jaspersen <verena.jaspersen at gmail.com>
+- Bertrand Svetchine <bertrand.svetchine at gmail.com>
+- Frank Wickström <frank at bambuser.com>
diff --git a/CHANGES b/CHANGES
index f99e6db..2a6bfb2 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,11 +1,18 @@
-Release DEV
-===========
+Release 1.3 (Jun 3, 2015)
+=========================
+
+* Official Django 1.8 support (thanks to multiple contributors)
+
+
+Release 1.2.5 (Dec 28, 2014)
+============================
* Official Django 1.7 support (thanks Troy Grosfield and Brian May)
* Allow to override ``PermissionRequiredMixin.get_permission_object``, part
of ``PermissionRequiredMixin.check_permissions`` method, responsible for
retrieving single object (Thanks zauddelig)
* French translations (Thanks Morgan Aubert)
+* Added support for ``User.get_all_permissions`` (thanks Michael Drescher)
Release 1.2.4 (Jul 14, 2014)
============================
diff --git a/benchmarks/run_benchmarks.py b/benchmarks/run_benchmarks.py
index 7dc984d..cb67228 100644
--- a/benchmarks/run_benchmarks.py
+++ b/benchmarks/run_benchmarks.py
@@ -34,6 +34,7 @@ settings.INSTALLED_APPS = (
)
from utils import show_settings
+import django
from django.contrib.auth.models import User, Group
from django.utils.termcolors import colorize
from benchmarks.models import TestModel
@@ -47,6 +48,17 @@ def random_string(length=25, chars=string.ascii_letters+string.digits):
return ''.join(random.choice(chars) for i in range(length))
+def get_model_name(model):
+ """
+ Returns the name of the model
+ """
+ # model._meta.module_name is deprecated in django version 1.7 and removed in django version 1.8.
+ # It is replaced by model._meta.model_name
+ if django.VERSION < (1, 7):
+ return model._meta.module_name
+ return model._meta.model_name
+
+
class Call(object):
def __init__(self, args, kwargs, start=None, finish=None):
self.args = args
@@ -94,7 +106,7 @@ class Benchmark(object):
self.objects_with_perms_count = objects_with_perms_count
self.Model = model
- self.perm = 'auth.change_%s' % model._meta.module_name
+ self.perm = 'auth.change_%s' % get_model_name(model)
def info(self, msg):
print(colorize(msg + '\n', fg='green'))
diff --git a/docs/userguide/assign.rst b/docs/userguide/assign.rst
index b76b3e9..551b7d6 100644
--- a/docs/userguide/assign.rst
+++ b/docs/userguide/assign.rst
@@ -36,8 +36,9 @@ model could look like:
('view_task', 'View task'),
)
-After we call ``syncdb`` management command our *view_task* permission would be
-added to default set of permissions.
+After we call ``syncdb`` (with a ``--all`` switch if you are using south)
+management command our *view_task* permission would be added to default set of
+permissions.
.. note::
By default, Django adds 3 permissions for each registered model:
diff --git a/example_project/settings.py b/example_project/settings.py
index 7f0eb88..02291df 100644
--- a/example_project/settings.py
+++ b/example_project/settings.py
@@ -33,7 +33,6 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.messages',
- 'django.contrib.staticfiles',
'guardian',
'guardian.testapp',
'posts',
@@ -104,7 +103,10 @@ USE_L10N = True
LOGIN_REDIRECT_URL = '/'
-TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
+if django.VERSION < (1, 8):
+ TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
+else:
+ TEST_RUNNER = 'django.test.runner.DiscoverRunner'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
diff --git a/guardian/__init__.py b/guardian/__init__.py
index 51a1c76..0d8d71e 100644
--- a/guardian/__init__.py
+++ b/guardian/__init__.py
@@ -3,7 +3,7 @@ Implementation of per object permissions for Django 1.2 or later.
"""
from __future__ import unicode_literals
-VERSION = (1, 2, 5, 'dev')
+VERSION = (1, 3)
__version__ = '.'.join((str(each) for each in VERSION[:4]))
diff --git a/guardian/admin.py b/guardian/admin.py
index e2a54f2..5c83454 100644
--- a/guardian/admin.py
+++ b/guardian/admin.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+import django
from django import forms
from django.conf import settings
from guardian.compat import url, patterns
@@ -12,7 +13,7 @@ from django.template import RequestContext
from django.utils.datastructures import SortedDict
from django.utils.translation import ugettext, ugettext_lazy as _
-from guardian.compat import get_user_model
+from guardian.compat import get_user_model, get_model_name
from guardian.forms import UserObjectPermissionsForm
from guardian.forms import GroupObjectPermissionsForm
from guardian.shortcuts import get_perms
@@ -65,7 +66,7 @@ class GuardedModelAdminMixin(object):
# backward compatibility
method = getattr(
super(GuardedModelAdminMixin, self), 'get_queryset',
- super(GuardedModelAdminMixin, self).queryset)
+ getattr(super(GuardedModelAdminMixin, self), 'queryset', None))
qs = method(request)
if request.user.is_superuser:
@@ -85,7 +86,6 @@ class GuardedModelAdminMixin(object):
# Allow queryset method as fallback for Django versions < 1.6
# for versions >= 1.6 this is taken care of by Django itself
# and triggers a warning message automatically.
- import django
if django.VERSION < (1, 6):
queryset = get_queryset
@@ -104,7 +104,7 @@ class GuardedModelAdminMixin(object):
"""
urls = super(GuardedModelAdminMixin, self).get_urls()
if self.include_object_permissions_urls:
- info = self.model._meta.app_label, self.model._meta.module_name
+ info = self.model._meta.app_label, get_model_name(self.model)
myurls = patterns('',
url(r'^(?P<object_pk>.+)/permissions/$',
view=self.admin_site.admin_view(self.obj_perms_manage_view),
@@ -165,7 +165,7 @@ class GuardedModelAdminMixin(object):
info = (
self.admin_site.name,
self.model._meta.app_label,
- self.model._meta.module_name
+ get_model_name(self.model)
)
if user_form.is_valid():
user_id = user_form.cleaned_data['user'].pk
@@ -180,7 +180,7 @@ class GuardedModelAdminMixin(object):
info = (
self.admin_site.name,
self.model._meta.app_label,
- self.model._meta.module_name
+ get_model_name(self.model)
)
if group_form.is_valid():
group_id = group_form.cleaned_data['group'].id
@@ -232,7 +232,7 @@ class GuardedModelAdminMixin(object):
info = (
self.admin_site.name,
self.model._meta.app_label,
- self.model._meta.module_name
+ get_model_name(self.model)
)
url = reverse(
'%s:%s_%s_permissions_manage_user' % info,
@@ -285,7 +285,7 @@ class GuardedModelAdminMixin(object):
info = (
self.admin_site.name,
self.model._meta.app_label,
- self.model._meta.module_name
+ get_model_name(self.model)
)
url = reverse(
'%s:%s_%s_permissions_manage_group' % info,
diff --git a/guardian/backends.py b/guardian/backends.py
index 2839f05..66f41a8 100644
--- a/guardian/backends.py
+++ b/guardian/backends.py
@@ -7,6 +7,45 @@ from guardian.conf import settings
from guardian.exceptions import WrongAppError
from guardian.core import ObjectPermissionChecker
+
+def check_object_support(obj):
+ """
+ Returns ``True`` if given ``obj`` is supported
+ """
+ # Backend checks only object permissions (isinstance implies that obj
+ # is not None)
+ # Backend checks only permissions for Django models
+ return isinstance(obj, models.Model)
+
+
+def check_user_support(user_obj):
+ """
+ Returns a tuple of checkresult and ``user_obj`` which should be used for
+ permission checks
+
+ Checks if the given user is supported. Anonymous users need explicit
+ activation via ANONYMOUS_USER_ID
+ """
+ # This is how we support anonymous users - simply try to retrieve User
+ # instance and perform checks for that predefined user
+ if not user_obj.is_authenticated():
+ # If anonymous user permission is disabled then they are always unauthorized
+ if settings.ANONYMOUS_USER_ID is None:
+ return False, user_obj
+ user_obj = get_user_model().objects.get(pk=settings.ANONYMOUS_USER_ID)
+
+ return True, user_obj
+
+
+def check_support(user_obj, obj):
+ """
+ Combination of ``check_object_support`` and ``check_user_support``
+ """
+ obj_support = check_object_support(obj)
+ user_support, user_obj = check_user_support(user_obj)
+ return obj_support and user_support, user_obj
+
+
class ObjectPermissionBackend(object):
supports_object_permissions = True
supports_anonymous_user = True
@@ -34,24 +73,10 @@ class ObjectPermissionBackend(object):
If user is authenticated but inactive at the same time, all checks
always returns ``False``.
"""
- # Backend checks only object permissions
- if obj is None:
- return False
-
- # Backend checks only permissions for Django models
- if not isinstance(obj, models.Model):
- return False
-
- # This is how we support anonymous users - simply try to retrieve User
- # instance and perform checks for that predefined user
- if not user_obj.is_authenticated():
- # If anonymous user permission is disabled then they are always unauthorized
- if settings.ANONYMOUS_USER_ID is None:
- return False
- user_obj = get_user_model().objects.get(pk=settings.ANONYMOUS_USER_ID)
- # Do not check any further if user is not active
- if not user_obj.is_active:
+ # check if user_obj and object are supported
+ support, user_obj = check_support(user_obj, obj)
+ if not support:
return False
if '.' in perm:
@@ -63,3 +88,14 @@ class ObjectPermissionBackend(object):
check = ObjectPermissionChecker(user_obj)
return check.has_perm(perm, obj)
+ def get_all_permissions(self, user_obj, obj=None):
+ """
+ Returns a set of permission strings that the given ``user_obj`` has for ``obj``
+ """
+ # check if user_obj and object are supported
+ support, user_obj = check_support(user_obj, obj)
+ if not support:
+ return set()
+
+ check = ObjectPermissionChecker(user_obj)
+ return check.get_perms(obj)
diff --git a/guardian/compat.py b/guardian/compat.py
index 160125c..387903f 100644
--- a/guardian/compat.py
+++ b/guardian/compat.py
@@ -1,14 +1,19 @@
from __future__ import unicode_literals
+import django
from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
from django.contrib.auth.models import AnonymousUser
-from django.utils.importlib import import_module
import six
import sys
try:
+ from importlib import import_module
+except ImportError:
+ from django.utils.importlib import import_module
+
+try:
from django.conf.urls import url, patterns, include, handler404, handler500
except ImportError:
from django.conf.urls.defaults import url, patterns, include, handler404, handler500 # pyflakes:ignore
@@ -129,3 +134,14 @@ def create_permissions(*args, **kwargs):
return original_create_permissions(*args, **kwargs)
__all__ = ['User', 'Group', 'Permission', 'AnonymousUser']
+
+
+def get_model_name(model):
+ """
+ Returns the name of the model
+ """
+ # model._meta.module_name is deprecated in django version 1.7 and removed in django version 1.8.
+ # It is replaced by model._meta.model_name
+ if django.VERSION < (1, 7):
+ return model._meta.module_name
+ return model._meta.model_name
diff --git a/guardian/management/__init__.py b/guardian/management/__init__.py
index f66c9fc..5f38daf 100644
--- a/guardian/management/__init__.py
+++ b/guardian/management/__init__.py
@@ -31,9 +31,9 @@ def create_anonymous_user(sender, **kwargs):
User.objects.get(pk=guardian_settings.ANONYMOUS_USER_ID)
except User.DoesNotExist:
if django.VERSION >= (1, 5):
- retrieve_anonymous_functon = import_string(
+ retrieve_anonymous_function = import_string(
guardian_settings.GET_INIT_ANONYMOUS_USER)
- user = retrieve_anonymous_functon(User)
+ user = retrieve_anonymous_function(User)
# Always set pk to the one pointed at settings
user.pk = guardian_settings.ANONYMOUS_USER_ID
user.save()
@@ -45,4 +45,3 @@ def create_anonymous_user(sender, **kwargs):
if guardian_settings.ANONYMOUS_USER_ID is not None:
signals.post_syncdb.connect(create_anonymous_user, sender=guardian_app,
dispatch_uid="guardian.management.create_anonymous_user")
-
diff --git a/guardian/migrations/0001_initial.py b/guardian/migrations/0001_initial.py
index bc491b7..94a5da7 100644
--- a/guardian/migrations/0001_initial.py
+++ b/guardian/migrations/0001_initial.py
@@ -1,112 +1,51 @@
-# encoding: utf-8
-from south.db import db
-from south.v2 import SchemaMigration
-
-from guardian.compat import user_model_label
-from guardian.compat import get_user_model
-
-User = get_user_model()
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
-
- # Adding model 'UserObjectPermission'
- db.create_table('guardian_userobjectpermission', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('permission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.Permission'])),
- ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
- ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
- ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm[user_model_label])),
- ))
- db.send_create_signal('guardian', ['UserObjectPermission'])
-
- # Adding unique constraint on 'UserObjectPermission', fields ['user', 'permission', 'content_type', 'object_id']
- db.create_unique('guardian_userobjectpermission', ['user_id', 'permission_id', 'content_type_id', 'object_id'])
-
- # Adding model 'GroupObjectPermission'
- db.create_table('guardian_groupobjectpermission', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('permission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.Permission'])),
- ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
- ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
- ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.Group'])),
- ))
- db.send_create_signal('guardian', ['GroupObjectPermission'])
-
- # Adding unique constraint on 'GroupObjectPermission', fields ['group', 'permission', 'content_type', 'object_id']
- db.create_unique('guardian_groupobjectpermission', ['group_id', 'permission_id', 'content_type_id', 'object_id'])
-
-
- def backwards(self, orm):
-
- # Removing unique constraint on 'GroupObjectPermission', fields ['group', 'permission', 'content_type', 'object_id']
- db.delete_unique('guardian_groupobjectpermission', ['group_id', 'permission_id', 'content_type_id', 'object_id'])
-
- # Removing unique constraint on 'UserObjectPermission', fields ['user', 'permission', 'content_type', 'object_id']
- db.delete_unique('guardian_userobjectpermission', ['user_id', 'permission_id', 'content_type_id', 'object_id'])
-
- # Deleting model 'UserObjectPermission'
- db.delete_table('guardian_userobjectpermission')
-
- # Deleting model 'GroupObjectPermission'
- db.delete_table('guardian_groupobjectpermission')
-
-
- models = {
- 'auth.group': {
- 'Meta': {'object_name': 'Group'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
- 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
- },
- 'auth.permission': {
- 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
- 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
- },
- user_model_label: {
- 'Meta': {'object_name': user_model_label.split('.')[-1], 'db_table': "'%s'" % User._meta.db_table},
- 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
- 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
- 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
- },
- 'contenttypes.contenttype': {
- 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
- 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
- },
- 'guardian.groupobjectpermission': {
- 'Meta': {'unique_together': "(['group', 'permission', 'content_type', 'object_id'],)", 'object_name': 'GroupObjectPermission'},
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Permission']"})
- },
- 'guardian.userobjectpermission': {
- 'Meta': {'unique_together': "(['user', 'permission', 'content_type', 'object_id'],)", 'object_name': 'UserObjectPermission'},
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Permission']"}),
- 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['%s']" % user_model_label})
- }
- }
-
- complete_apps = ['guardian']
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+from django.conf import settings
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('contenttypes', '0001_initial'),
+ ('auth', '0001_initial'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='GroupObjectPermission',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
+ ('object_pk', models.CharField(max_length=255, verbose_name='object ID')),
+ ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
+ ('group', models.ForeignKey(to='auth.Group')),
+ ('permission', models.ForeignKey(to='auth.Permission')),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='UserObjectPermission',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
+ ('object_pk', models.CharField(max_length=255, verbose_name='object ID')),
+ ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
+ ('permission', models.ForeignKey(to='auth.Permission')),
+ ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.AlterUniqueTogether(
+ name='userobjectpermission',
+ unique_together=set([('user', 'permission', 'object_pk')]),
+ ),
+ migrations.AlterUniqueTogether(
+ name='groupobjectpermission',
+ unique_together=set([('group', 'permission', 'object_pk')]),
+ ),
+ ]
diff --git a/guardian/models.py b/guardian/models.py
index bbff1db..c9ab496 100644
--- a/guardian/models.py
+++ b/guardian/models.py
@@ -5,7 +5,12 @@ from django.core.exceptions import ValidationError
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes.generic import GenericForeignKey
+
+try:
+ from django.contrib.contenttypes.fields import GenericForeignKey
+except ImportError:
+ from django.contrib.contenttypes.generic import GenericForeignKey
+
from django.utils.translation import ugettext_lazy as _
from guardian.compat import user_model_label
diff --git a/guardian/shortcuts.py b/guardian/shortcuts.py
index 3334640..5ef280c 100644
--- a/guardian/shortcuts.py
+++ b/guardian/shortcuts.py
@@ -285,7 +285,7 @@ def get_groups_with_perms(obj, attach_perms=False):
def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=False,
- with_superuser=True):
+ with_superuser=True, accept_global_perms=True):
"""
Returns queryset of objects for which a given ``user`` has *all*
permissions present at ``perms``.
@@ -302,9 +302,14 @@ def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=Fals
this parameter would be computed based on given ``params``.
:param use_groups: if ``False``, wouldn't check user's groups object
permissions. Default is ``True``.
- :param any_perm: if True, any of permission in sequence is accepted
+ :param any_perm: if True, any of permission in sequence is accepted. Default is ``False``.
:param with_superuser: if ``True`` returns the entire queryset if not it will
- only return objects the user has explicit permissions.
+ only return objects the user has explicit permissions. Default is ``True``.
+ :param accept_global_perms: if ``True`` takes global permissions into account.
+ Object based permissions are taken into account if more than one permission is handed in in perms and at least
+ one of these perms is not globally set. If any_perm is set to false then the intersection of matching object
+ is returned. Note, that if with_superuser is False, accept_global_perms will be ignored, which means that only
+ object permissions will be checked! Default is ``True``.
:raises MixedContentTypeError: when computed content type for ``perms``
and/or ``klass`` clashes.
@@ -324,6 +329,7 @@ def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=Fals
>>> get_objects_for_user(joe, 'auth.change_group')
[<Group some group>]
+
The permission string can also be an iterable. Continuing with the previous example:
>>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group'])
@@ -334,6 +340,18 @@ def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=Fals
>>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group'])
[<Group some group>]
+ Take global permissions into account:
+ >>> jack = User.objects.get(username='jack')
+ >>> assign_perm('auth.change_group', jack) # this will set a global permission
+ >>> get_objects_for_user(jack, 'auth.change_group')
+ [<Group some group>]
+ >>> group2 = Group.objects.create('other group')
+ >>> assign_perm('auth.delete_group', jack, group2)
+ >>> get_objects_for_user(jack, ['auth.change_group', 'auth.delete_group']) # this retrieves intersection
+ [<Group other group>]
+ >>> get_objects_for_user(jack, ['auth.change_group', 'auth.delete_group'], any_perm) # this retrieves union
+ [<Group some group>, <Group other group>]
+
"""
if isinstance(perms, basestring):
perms = [perms]
@@ -390,6 +408,33 @@ def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=Fals
if user.is_anonymous():
user = get_anonymous_user()
+ global_perms = set()
+ has_global_perms = False
+ # a superuser has by default assigned global perms for any
+ if accept_global_perms and with_superuser:
+ for code in codenames:
+ if user.has_perm(app_label + '.' + code):
+ global_perms.add(code)
+ for code in global_perms:
+ codenames.remove(code)
+ ## prerequisite: there must be elements in global_perms otherwise just follow the procedure for
+ # object based permissions only AND
+ # 1. codenames is empty, which means that permissions are ONLY set globally, therefore return the full queryset.
+ # OR
+ # 2. any_perm is True, then the global permission beats the object based permission anyway,
+ # therefore return full queryset
+ if len(global_perms) > 0 and (len(codenames) == 0 or any_perm):
+ return queryset
+ # if we have global perms and still some object based perms differing from global perms and any_perm is set
+ # to false, then we have to flag that global perms exist in order to merge object based permissions by user
+ # and by group correctly. Scenario: global perm change_xx and object based perm delete_xx on object A for user,
+ # and object based permission delete_xx on object B for group, to which user is assigned.
+ # get_objects_for_user(user, [change_xx, delete_xx], use_groups=True, any_perm=False, accept_global_perms=True)
+ # must retrieve object A and B.
+ elif len(global_perms) > 0 and (len(codenames) > 0):
+ has_global_perms = True
+
+
# Now we should extract list of pk values for which we would filter queryset
user_model = get_user_obj_perms_model(queryset.model)
user_obj_perms_queryset = (user_model.objects
@@ -413,7 +458,7 @@ def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=Fals
fields = ['object_pk', 'permission__codename']
else:
fields = ['content_object__pk', 'permission__codename']
- if not any_perm:
+ if not any_perm and not has_global_perms:
user_obj_perms = user_obj_perms_queryset.values_list(*fields)
groups_obj_perms = groups_obj_perms_queryset.values_list(*fields)
data = list(user_obj_perms) + list(groups_obj_perms)
@@ -444,7 +489,7 @@ def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=Fals
return objects
-def get_objects_for_group(group, perms, klass=None, any_perm=False):
+def get_objects_for_group(group, perms, klass=None, any_perm=False, accept_global_perms=True):
"""
Returns queryset of objects for which a given ``group`` has *all*
permissions present at ``perms``.
@@ -459,6 +504,9 @@ def get_objects_for_group(group, perms, klass=None, any_perm=False):
:param klass: may be a Model, Manager or QuerySet object. If not given
this parameter would be computed based on given ``params``.
:param any_perm: if True, any of permission in sequence is accepted
+ :param accept_global_perms: if ``True`` takes global permissions into account.
+ If any_perm is set to false then the intersection of matching objects based on global and object based permissions
+ is returned. Default is ``True``.
:raises MixedContentTypeError: when computed content type for ``perms``
and/or ``klass`` clashes.
@@ -489,6 +537,14 @@ def get_objects_for_group(group, perms, klass=None, any_perm=False):
>>> get_objects_for_group(group, ['tasker.add_task', 'tasker.delete_task'])
[<Task some task>]
+ Global permissions assigned to the group are also taken into account. Continuing with previous example:
+ >>> task_other = Task.objects.create('other task')
+ >>> assign_perm('tasker.change_task', group)
+ >>> get_objects_for_group(group, ['tasker.change_task'])
+ [<Task some task>, <Task other task>]
+ >>> get_objects_for_group(group, ['tasker.change_task'], accept_global_perms=False)
+ [<Task some task>]
+
"""
if isinstance(perms, basestring):
perms = [perms]
@@ -535,6 +591,17 @@ def get_objects_for_group(group, perms, klass=None, any_perm=False):
# match which means: ctype.model_class() == queryset.model
# we should also have ``codenames`` list
+ global_perms = set()
+ if accept_global_perms:
+ global_perm_set = group.permissions.values_list('codename', flat=True)
+ for code in codenames:
+ if code in global_perm_set:
+ global_perms.add(code)
+ for code in global_perms:
+ codenames.remove(code)
+ if len(global_perms) > 0 and (len(codenames) == 0 or any_perm):
+ return queryset
+
# Now we should extract list of pk values for which we would filter queryset
group_model = get_group_obj_perms_model(queryset.model)
groups_obj_perms_queryset = (group_model.objects
@@ -555,6 +622,5 @@ def get_objects_for_group(group, perms, klass=None, any_perm=False):
obj_codenames = set((e[1] for e in group))
if any_perm or codenames.issubset(obj_codenames):
pk_list.append(pk)
-
objects = queryset.filter(pk__in=pk_list)
return objects
diff --git a/guardian/migrations/0001_initial.py b/guardian/south_migrations/0001_initial.py
similarity index 100%
copy from guardian/migrations/0001_initial.py
copy to guardian/south_migrations/0001_initial.py
diff --git a/guardian/migrations/0002_auto__add_field_groupobjectpermission_object_pk__add_field_userobjectp.py b/guardian/south_migrations/0002_auto__add_field_groupobjectpermission_object_pk__add_field_userobjectp.py
similarity index 100%
rename from guardian/migrations/0002_auto__add_field_groupobjectpermission_object_pk__add_field_userobjectp.py
rename to guardian/south_migrations/0002_auto__add_field_groupobjectpermission_object_pk__add_field_userobjectp.py
diff --git a/guardian/migrations/0003_update_objectpermission_object_pk.py b/guardian/south_migrations/0003_update_objectpermission_object_pk.py
similarity index 100%
rename from guardian/migrations/0003_update_objectpermission_object_pk.py
rename to guardian/south_migrations/0003_update_objectpermission_object_pk.py
diff --git a/guardian/migrations/0004_auto__del_field_groupobjectpermission_object_id__del_unique_groupobjec.py b/guardian/south_migrations/0004_auto__del_field_groupobjectpermission_object_id__del_unique_groupobjec.py
similarity index 100%
rename from guardian/migrations/0004_auto__del_field_groupobjectpermission_object_id__del_unique_groupobjec.py
rename to guardian/south_migrations/0004_auto__del_field_groupobjectpermission_object_id__del_unique_groupobjec.py
diff --git a/guardian/migrations/0005_auto__chg_field_groupobjectpermission_object_pk__chg_field_userobjectp.py b/guardian/south_migrations/0005_auto__chg_field_groupobjectpermission_object_pk__chg_field_userobjectp.py
similarity index 100%
rename from guardian/migrations/0005_auto__chg_field_groupobjectpermission_object_pk__chg_field_userobjectp.py
rename to guardian/south_migrations/0005_auto__chg_field_groupobjectpermission_object_pk__chg_field_userobjectp.py
diff --git a/guardian/south_migrations/__init__.py b/guardian/south_migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/guardian/templatetags/guardian_tags.py b/guardian/templatetags/guardian_tags.py
index 3590bbb..04c3595 100644
--- a/guardian/templatetags/guardian_tags.py
+++ b/guardian/templatetags/guardian_tags.py
@@ -8,8 +8,14 @@
from __future__ import unicode_literals
from django import template
from django.contrib.auth.models import Group, AnonymousUser
-from django.template import get_library
-from django.template import InvalidTemplateLibrary
+try:
+ # Django < 1.8
+ from django.template import get_library
+ from django.template import InvalidTemplateLibrary
+except ImportError:
+ # Django >= 1.8
+ from django.template.base import get_library
+ from django.template.base import InvalidTemplateLibrary
from django.template.defaulttags import LoadNode
from guardian.compat import get_user_model
diff --git a/guardian/testapp/migrations/0001_initial.py b/guardian/testapp/migrations/0001_initial.py
index f329972..5f92d18 100644
--- a/guardian/testapp/migrations/0001_initial.py
+++ b/guardian/testapp/migrations/0001_initial.py
@@ -21,7 +21,7 @@ class Migration(migrations.Migration):
name='CustomUser',
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
- ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')),
+ ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login', null=True)),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, max_length=30, verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w. at -]+$', 'Enter a valid username.', 'invalid')])),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
diff --git a/guardian/testapp/tests/admin_test.py b/guardian/testapp/tests/admin_test.py
index 775421b..33625cf 100644
--- a/guardian/testapp/tests/admin_test.py
+++ b/guardian/testapp/tests/admin_test.py
@@ -11,7 +11,7 @@ from django.test import TestCase
from django.test.client import Client
from guardian.admin import GuardedModelAdmin
-from guardian.compat import get_user_model
+from guardian.compat import get_user_model, get_model_name
from guardian.compat import str
from guardian.shortcuts import get_perms
from guardian.shortcuts import get_perms_for_model
@@ -43,7 +43,7 @@ class AdminTests(TestCase):
self.client = Client()
self.obj = ContentType.objects.create(name='foo', model='bar',
app_label='fake-for-guardian-tests')
- self.obj_info = self.obj._meta.app_label, self.obj._meta.module_name
+ self.obj_info = self.obj._meta.app_label, get_model_name(self.obj)
def tearDown(self):
self.client.logout()
diff --git a/guardian/testapp/tests/direct_rel_test.py b/guardian/testapp/tests/direct_rel_test.py
index f839ec6..393c48b 100644
--- a/guardian/testapp/tests/direct_rel_test.py
+++ b/guardian/testapp/tests/direct_rel_test.py
@@ -101,6 +101,22 @@ class TestDirectUserPermissions(TestCase):
result = get_objects_for_user(self.joe, 'testapp.add_project')
self.assertEqual(sorted(p.pk for p in result), sorted([foo.pk, bar.pk]))
+ def test_get_all_permissions(self):
+ foo = Project.objects.create(name='foo')
+ assign_perm('add_project', self.joe, foo)
+ assign_perm('change_project', self.joe, foo)
+
+ result = self.joe.get_all_permissions(foo)
+ self.assertEqual(result, set(('add_project', 'change_project')))
+
+ def test_get_all_permissions_no_object(self):
+ foo = Project.objects.create(name='foo')
+ assign_perm('add_project', self.joe, foo)
+ assign_perm('change_project', self.joe, foo)
+
+ result = self.joe.get_all_permissions()
+ self.assertEqual(result, set())
+
@skipUnlessTestApp
class TestDirectGroupPermissions(TestCase):
diff --git a/guardian/testapp/tests/orphans_test.py b/guardian/testapp/tests/orphans_test.py
index 6cab41f..a49a922 100644
--- a/guardian/testapp/tests/orphans_test.py
+++ b/guardian/testapp/tests/orphans_test.py
@@ -10,7 +10,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.test import TestCase
-from guardian.compat import get_user_model, create_permissions
+from guardian.compat import get_user_model, create_permissions, get_model_name
from guardian.utils import clean_orphan_obj_perms
from guardian.shortcuts import assign_perm
from guardian.models import Group
@@ -18,7 +18,7 @@ from guardian.testapp.tests.conf import skipUnlessTestApp
User = get_user_model()
-user_module_name = User._meta.module_name
+user_module_name = get_model_name(User)
@skipUnlessTestApp
class OrphanedObjectPermissionsTest(TestCase):
@@ -99,4 +99,3 @@ class OrphanedObjectPermissionsTest(TestCase):
target.save()
for perm in perms:
self.assertFalse(self.user.has_perm(perm, target))
-
diff --git a/guardian/testapp/tests/shortcuts_test.py b/guardian/testapp/tests/shortcuts_test.py
index 56512d0..8f5d650 100644
--- a/guardian/testapp/tests/shortcuts_test.py
+++ b/guardian/testapp/tests/shortcuts_test.py
@@ -8,7 +8,7 @@ from django.test import TestCase
from guardian.shortcuts import get_perms_for_model
from guardian.core import ObjectPermissionChecker
from guardian.compat import get_user_model
-from guardian.compat import get_user_permission_full_codename
+from guardian.compat import get_user_permission_full_codename, get_model_name
from guardian.shortcuts import assign
from guardian.shortcuts import assign_perm
from guardian.shortcuts import remove_perm
@@ -29,7 +29,7 @@ import warnings
User = get_user_model()
user_app_label = User._meta.app_label
-user_module_name = User._meta.module_name
+user_module_name = get_model_name(User)
class ShortcutsTests(ObjectPermissionTestCase):
@@ -476,11 +476,13 @@ class GetObjectsForUser(TestCase):
['change_group'])
def test_empty_perms_sequence(self):
+ objects = get_objects_for_user(self.user, [], Group.objects.all())
self.assertEqual(
- set(get_objects_for_user(self.user, [], Group.objects.all())),
+ set(objects),
set()
)
+
def test_perms_single(self):
perm = 'auth.change_group'
assign_perm(perm, self.user, self.group)
@@ -578,7 +580,7 @@ class GetObjectsForUser(TestCase):
# Objects to operate on
ctypes = list(ContentType.objects.all().order_by('id'))
-
+ assign_perm('auth.change_group', self.user)
assign_perm('change_contenttype', self.user, ctypes[0])
assign_perm('change_contenttype', self.user, ctypes[1])
assign_perm('delete_contenttype', self.user, ctypes[1])
@@ -608,6 +610,113 @@ class GetObjectsForUser(TestCase):
set(objects.values_list('id', flat=True)),
set(ctypes[i].id for i in [0, 1, 3, 4]))
+ def test_has_global_permission_only(self):
+ group_names = ['group1', 'group2', 'group3']
+ groups = [Group.objects.create(name=name) for name in group_names]
+ #global permission to change any group
+ perm = 'auth.change_group'
+
+ assign_perm(perm, self.user)
+ objects = get_objects_for_user(self.user, perm)
+ remove_perm(perm, self.user)
+ self.assertEqual(set(objects),
+ set(Group.objects.all()))
+
+ def test_has_global_permission_and_object_based_permission(self):
+ group_names = ['group1', 'group2', 'group3']
+ groups = [Group.objects.create(name=name) for name in group_names]
+ #global permission to change any group
+ perm_global = 'auth.change_group'
+ perm_obj = 'delete_group'
+ assign_perm(perm_global, self.user)
... 245 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-guardian.git
More information about the Python-modules-commits
mailing list