Codebase list django-guardian / 0033bf7
New upstream version 1.4.8 Brian May 6 years ago
90 changed file(s) with 1894 addition(s) and 785 deletion(s). Raw diff Collapse all Expand all
1919 example_project/conf/*.py
2020 .idea/
2121 .eggs/
22 .cache/
2223
2324 # WebDAV remote filesystem
2425 .DAV
0 [settings]
1 line_length=120
2 force_single_line=False
3 force_alphabetical_sort=True
00 language: python
11 sudo: false
2
3 cache: pip
4
25 python:
36 - 2.7
4 - 3.3
57 - 3.4
68 - 3.5
9 - 3.6
710
811 env:
9 - DJANGO_VERSION=1.7.10 DATABASE_URL=postgres://postgres@/django_guardian
10 - DJANGO_VERSION=1.8.7 DATABASE_URL=postgres://postgres@/django_guardian
11 - DJANGO_VERSION=1.9.1 DATABASE_URL=postgres://postgres@/django_guardian
12 - DJANGO_VERSION=1.8 DATABASE_URL=postgres://postgres@/django_guardian
13 - DJANGO_VERSION=1.10 DATABASE_URL=postgres://postgres@/django_guardian
14 - DJANGO_VERSION=1.11 DATABASE_URL=postgres://postgres@/django_guardian
1215 - DJANGO_VERSION=master DATABASE_URL=postgres://postgres@/django_guardian
1316
14 - DJANGO_VERSION=1.7.10 DATABASE_URL=mysql://root:@localhost/django_guardian
15 - DJANGO_VERSION=1.8.7 DATABASE_URL=mysql://root:@localhost/django_guardian
16 - DJANGO_VERSION=1.9.1 DATABASE_URL=mysql://root:@localhost/django_guardian
17 - DJANGO_VERSION=1.8 DATABASE_URL=mysql://root:@localhost/django_guardian
18 - DJANGO_VERSION=1.10 DATABASE_URL=mysql://root:@localhost/django_guardian
19 - DJANGO_VERSION=1.11 DATABASE_URL=mysql://root:@localhost/django_guardian
1720 - DJANGO_VERSION=master DATABASE_URL=mysql://root:@localhost/django_guardian
1821
19 - DJANGO_VERSION=1.7.10 DATABASE_URL=sqlite://
20 - DJANGO_VERSION=1.8.7 DATABASE_URL=sqlite://
21 - DJANGO_VERSION=1.9.1 DATABASE_URL=sqlite://
22 - DJANGO_VERSION=1.8 DATABASE_URL=sqlite://
23 - DJANGO_VERSION=1.10 DATABASE_URL=sqlite://
24 - DJANGO_VERSION=1.11 DATABASE_URL=sqlite://
2225 - DJANGO_VERSION=master DATABASE_URL=sqlite://
26
27 before_install:
28 - pip install -q -U pytest
2329
2430 install:
2531 - travis_retry pip install -q mock==1.0.1 pytest pytest-django pytest-cov django-environ setuptools_scm
2632 # Install django master or version
2733 - bash -c "if [[ "$DJANGO_VERSION" == 'master' ]]; then pip install 'https://github.com/django/django/archive/master.tar.gz'; else pip install Django==$DJANGO_VERSION; fi; "
2834 # Install database drivers
29 - bash -c "if [[ $DATABASE_URL = postgres* ]]; then pip install psycopg2==2.6.1; fi; "
30 - bash -c "if [[ $DATABASE_URL = mysql* ]]; then pip install mysqlclient==1.3.7; fi; "
35 - bash -c "if [[ $DATABASE_URL = postgres* ]]; then pip install psycopg2==2.7.1; fi; "
36 - bash -c "if [[ $DATABASE_URL = mysql* ]]; then pip install mysqlclient==1.3.10; fi; "
3137
3238 script:
3339 - python ./setup.py --version
3440 - py.test --cov=guardian
41 - bash -c "if [[ $DJANGO_VERSION = 1.10 || $DJANGO_VERSION = 1.11 || $DJANGO_VERSION = "master" ]]; then pip install .; cd example_project; python manage.py test; fi; "
3542
3643 notifications:
3744 irc: "irc.freenode.net#django-guardian"
3946
4047 matrix:
4148 fast_finish: true
42 exclude:
43 # Drop python 3.3 and django 1.9
44 - python: 3.3
45 env: DJANGO_VERSION=1.9.1 DATABASE_URL=postgres://postgres@/django_guardian
46 - python: 3.3
47 env: DJANGO_VERSION=1.9.1 DATABASE_URL=mysql://root:@localhost/django_guardian
48 - python: 3.3
49 env: DJANGO_VERSION=1.9.1 DATABASE_URL=sqlite://
50 # Drop python 3.3 and django master
51 - python: 3.3
52 env: DJANGO_VERSION=master DATABASE_URL=postgres://postgres@/django_guardian
53 - python: 3.3
54 env: DJANGO_VERSION=master DATABASE_URL=mysql://root:@localhost/django_guardian
55 - python: 3.3
56 env: DJANGO_VERSION=master DATABASE_URL=sqlite://
57 # Drop python 3.5 and django 1.7
58 - python: 3.5
59 env: DJANGO_VERSION=1.7.10 DATABASE_URL=postgres://postgres@/django_guardian
60 - python: 3.5
61 env: DJANGO_VERSION=1.7.10 DATABASE_URL=mysql://root:@localhost/django_guardian
62 - python: 3.5
63 env: DJANGO_VERSION=1.7.10 DATABASE_URL=sqlite://
64 # Drop python 3.3 with postgres due lack of driver
65 - python: 3.3
66 env: DJANGO_VERSION=1.7.10 DATABASE_URL=postgres://postgres@/django_guardian
67 - python: 3.3
68 env: DJANGO_VERSION=1.8.7 DATABASE_URL=postgres://postgres@/django_guardian
69 - python: 3.3
70 env: DJANGO_VERSION=1.9.1 DATABASE_URL=postgres://postgres@/django_guardian
7149 allow_failures:
7250 - env: DJANGO_VERSION=master DATABASE_URL=postgres://postgres@/django_guardian
7351 - env: DJANGO_VERSION=master DATABASE_URL=mysql://root:@localhost/django_guardian
5454 - Bertrand Svetchine <bertrand.svetchine@gmail.com>
5555 - Frank Wickström <frank@bambuser.com>
5656 - George Karakostas <gckarakostas@gmail.com>
57 - Adam Dobrawy <guardian@jawnosc.tk>
0 Release 1.4.8 (Apr 04, 2017)
1 ============================
2
3 * Improved performance of `clean_orphan_obj_perms` management command
4 * Use bumpversion for versioning.
5 * Enable Python 3.6 testing
6 * Python 2.7, 3.4, 3.5, 3.6 are only supported Python versions
7 * Django 1.8, 1.10, and 1.11 are only supported Django versions
8 * Added explicity on_delete to all ForeignKeys
9
10 Release 1.4.6 (Sep 09, 2016)
11 ============================
12
13 * Improved performance of get_objects_for_user
14 * Added test-covered and documented guardian.mixins.PermissionListMixin
15 * Allow content type retrieval to be overridden fg. for django-polymorphic support
16 * Added support CreateView-like (no object) view in PermissionRequiredMixin
17 * Added django 1.10 to TravisCI and tox
18 * Run tests for example_project in TravisCI
19 * Require django 1.9+ for example_project (django-guardian core support django 1.7+)
20 * Fix django versions compatibility in example_project
21 * Drop django in install_requires of setuptools
22
23 Release 1.4.5 (Aug 09, 2016)
24 ============================
25
26 * Fix caching issue with prefetch_perms.
27 * Convert readthedocs link for their .org -> .io migration for hosted projects
28 * Added example CRUD CBV project
29 * Added TEMPLATES in example_project settings
30 * Added Queryset support to assign_perm
31 * Added QuerySet support to remove_perm
32 * Updated assign_perm and remove_perm docstrings
33 * Moved queryset support in assign_perms to its own function
34 * Moved queryset support in remove_perms to its own function
35 * Consolidated {User,Group}ObjectPermissionManager, move logic of bulk_*_perm
36 to managers
37 * `assign_perm` and `remove_perm` shortcuts accept `Permission`
38 instance as `perm` and `QuerySet` as `obj` too.
39 * Consolidated bulk_assign_perm to assign_perm and bulk_remove_perm to remove_perm
40 * Upgraded Grappelli templates breadcrumbs block to new Django 1.9 and
41 Grappelli 2.8 standards, including proper URLs and support for
42 preserved_filters. Removed the duplicated field.errors in the field.html
43 template file.
44 * Made UserManage/GroupManage forms overridable
45 * Fixed GuardedModelAdminMixin views render for Django 1.10
46
47
048 Release 1.4.4 (Apr 04, 2016)
149 ============================
250
0 Copyright (c) 2010-2014 Lukasz Balcerzak <lukaszbalcerzak@gmail.com>
0 Copyright (c) 2010-2016 Lukasz Balcerzak <lukaszbalcerzak@gmail.com>
11 All rights reserved.
22
33 Redistribution and use in source and binary forms, with or without
22 include README.rst
33 include MANIFEST.in
44 include *.py
5 include *.ini
56 include run_test_and_report.sh
67 include *.txt
78 include AUTHORS
8 include tox.ini
99 recursive-include guardian *.py
1010 recursive-include guardian/locale *.po *.mo
11 recursive-include guardian/fixtures *.json
12 recursive-include guardian/templates *.html
13 recursive-include guardian/tests/templates *.html
1411 recursive-include docs *.bat
15 recursive-include docs *.conf
1612 recursive-include docs *.css
1713 recursive-include docs *.eot
1814 recursive-include docs *.html
1915 recursive-include docs *.js
2016 recursive-include docs *.py
21 recursive-include docs *.rb
2217 recursive-include docs *.rst
23 recursive-include docs *.sass
2418 recursive-include docs *.sh
2519 recursive-include docs *.svg
2620 recursive-include docs *.ttf
3125 recursive-include example_project *.js
3226 recursive-include example_project *.png
3327 recursive-include example_project *.txt
28 recursive-include example_project *.py
3429 recursive-exclude docs/build *
3530 recursive-include guardian *.html
3631 recursive-include guardian *.svg
32 recursive-include benchmarks *.py
00 Metadata-Version: 1.1
11 Name: django-guardian
2 Version: 1.4.4
2 Version: 1.4.8
33 Summary: Implementation of per object permissions for Django.
44 Home-page: http://github.com/django-guardian/django-guardian
55 Author: Lukasz Balcerzak
77 License: BSD
88 Download-URL: https://github.com/django-guardian/django-guardian/tags
99 Description: ===============
10 django-guardian
11 ===============
12
13 .. image:: https://travis-ci.org/django-guardian/django-guardian.svg?branch=devel
14 :target: https://travis-ci.org/django-guardian/django-guardian
15
16 ``django-guardian`` is an implementation of per object permissions [1]_ on top
17 of Django's authorization backend
18
19 Documentation
20 -------------
21
22 Online documentation is available at https://django-guardian.readthedocs.io/.
23
24 Requirements
25 ------------
26
27 * Python 2.7 or 3.4+
28 * A supported version of Django (currently 1.8+)
29
30 Travis CI tests on Django version 1.8, 1.10, and 1.11.
31
32 Installation
33 ------------
34
35 To install ``django-guardian`` simply run::
36
37 pip install django-guardian
38
39 Configuration
40 -------------
41
42 We need to hook ``django-guardian`` into our project.
43
44 1. Put ``guardian`` into your ``INSTALLED_APPS`` at settings module:
45
46 .. code:: python
47
48 INSTALLED_APPS = (
49 ...
50 'guardian',
51 )
52
53 2. Add extra authorization backend to your ``settings.py``:
54
55 .. code:: python
56
57 AUTHENTICATION_BACKENDS = (
58 'django.contrib.auth.backends.ModelBackend', # default
59 'guardian.backends.ObjectPermissionBackend',
60 )
61
62 3. Create ``guardian`` database tables by running::
63
64 python manage.py migrate
65
66 Usage
67 -----
68
69 After installation and project hooks we can finally use object permissions
70 with Django_.
71
72 Lets start really quickly:
73
74 .. code:: python
75
76 >>> from django.contrib.auth.models import User, Group
77 >>> jack = User.objects.create_user('jack', 'jack@example.com', 'topsecretagentjack')
78 >>> admins = Group.objects.create(name='admins')
79 >>> jack.has_perm('change_group', admins)
80 False
81 >>> from guardian.models import UserObjectPermission
82 >>> UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins)
83 <UserObjectPermission: admins | jack | change_group>
84 >>> jack.has_perm('change_group', admins)
85 True
86
87 Of course our agent jack here would not be able to *change_group* globally:
88
89 .. code:: python
90
91 >>> jack.has_perm('change_group')
92 False
93
94 Admin integration
95 -----------------
96
97 Replace ``admin.ModelAdmin`` with ``GuardedModelAdmin`` for those models
98 which should have object permissions support within admin panel.
99
100 For example:
101
102 .. code:: python
103
104 from django.contrib import admin
105 from myapp.models import Author
106 from guardian.admin import GuardedModelAdmin
107
108 # Old way:
109 #class AuthorAdmin(admin.ModelAdmin):
110 # pass
111
112 # With object permissions support
113 class AuthorAdmin(GuardedModelAdmin):
114 pass
115
116 admin.site.register(Author, AuthorAdmin)
117
118
119 .. [1] Great paper about this feature is available at `djangoadvent articles <https://github.com/djangoadvent/djangoadvent-articles/blob/master/1.2/06_object-permissions.rst>`_.
120
121 .. _Django: http://www.djangoproject.com/
122
123
10124 Platform: UNKNOWN
11125 Classifier: Development Status :: 5 - Production/Stable
12126 Classifier: Environment :: Web Environment
17131 Classifier: Programming Language :: Python
18132 Classifier: Topic :: Security
19133 Classifier: Programming Language :: Python :: 2.7
20 Classifier: Programming Language :: Python :: 3.3
21134 Classifier: Programming Language :: Python :: 3.4
22135 Classifier: Programming Language :: Python :: 3.5
136 Classifier: Programming Language :: Python :: 3.6
44 .. image:: https://travis-ci.org/django-guardian/django-guardian.svg?branch=devel
55 :target: https://travis-ci.org/django-guardian/django-guardian
66
7 ``django-guardian`` is implementation of per object permissions [1]_ as
8 authorization backend which is supported since Django_ 1.5. It won't
9 work with older Django_ releases.
7 ``django-guardian`` is an implementation of per object permissions [1]_ on top
8 of Django's authorization backend
109
1110 Documentation
1211 -------------
1312
14 Online documentation is available at http://django-guardian.rtfd.org/.
13 Online documentation is available at https://django-guardian.readthedocs.io/.
14
15 Requirements
16 ------------
17
18 * Python 2.7 or 3.4+
19 * A supported version of Django (currently 1.8+)
20
21 Travis CI tests on Django version 1.8, 1.10, and 1.11.
1522
1623 Installation
1724 ------------
2532
2633 We need to hook ``django-guardian`` into our project.
2734
28 1. Put ``guardian`` into your ``INSTALLED_APPS`` at settings module::
35 1. Put ``guardian`` into your ``INSTALLED_APPS`` at settings module:
2936
30 INSTALLED_APPS = (
31 ...
32 'guardian',
33 )
37 .. code:: python
38
39 INSTALLED_APPS = (
40 ...
41 'guardian',
42 )
3443
35 2. Add extra authorization backend to your `settings.py`::
44 2. Add extra authorization backend to your ``settings.py``:
3645
37 AUTHENTICATION_BACKENDS = (
38 'django.contrib.auth.backends.ModelBackend', # default
39 'guardian.backends.ObjectPermissionBackend',
40 )
46 .. code:: python
4147
42 4. Create ``guardian`` database tables by running::
48 AUTHENTICATION_BACKENDS = (
49 'django.contrib.auth.backends.ModelBackend', # default
50 'guardian.backends.ObjectPermissionBackend',
51 )
52
53 3. Create ``guardian`` database tables by running::
4354
4455 python manage.py migrate
4556
4960 After installation and project hooks we can finally use object permissions
5061 with Django_.
5162
52 Lets start really quickly::
63 Lets start really quickly:
5364
54 >>> from django.contrib.auth.models import User, Group
55 >>> jack = User.objects.create_user('jack', 'jack@example.com', 'topsecretagentjack')
56 >>> admins = Group.objects.create(name='admins')
57 >>> jack.has_perm('change_group', admins)
58 False
59 >>> from guardian.models import UserObjectPermission
60 >>> UserObjectPermission.objects.assign_perm('change_group', user=jack, obj=admins)
61 <UserObjectPermission: admins | jack | change_group>
62 >>> jack.has_perm('change_group', admins)
63 True
65 .. code:: python
6466
65 Of course our agent jack here would not be able to *change_group* globally::
67 >>> from django.contrib.auth.models import User, Group
68 >>> jack = User.objects.create_user('jack', 'jack@example.com', 'topsecretagentjack')
69 >>> admins = Group.objects.create(name='admins')
70 >>> jack.has_perm('change_group', admins)
71 False
72 >>> from guardian.models import UserObjectPermission
73 >>> UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins)
74 <UserObjectPermission: admins | jack | change_group>
75 >>> jack.has_perm('change_group', admins)
76 True
77
78 Of course our agent jack here would not be able to *change_group* globally:
79
80 .. code:: python
6681
6782 >>> jack.has_perm('change_group')
6883 False
7388 Replace ``admin.ModelAdmin`` with ``GuardedModelAdmin`` for those models
7489 which should have object permissions support within admin panel.
7590
76 For example::
91 For example:
92
93 .. code:: python
7794
7895 from django.contrib import admin
7996 from myapp.models import Author
77
88
99 class DirectUser(UserObjectPermissionBase):
10 content_object = models.ForeignKey('TestDirectModel')
10 content_object = models.ForeignKey('TestDirectModel', on_delete=models.CASCADE)
1111
1212
1313 class DirectGroup(GroupObjectPermissionBase):
14 content_object = models.ForeignKey('TestDirectModel')
14 content_object = models.ForeignKey('TestDirectModel', on_delete=models.CASCADE)
1515
1616
1717 class TestDirectModel(models.Model):
00 Metadata-Version: 1.1
11 Name: django-guardian
2 Version: 1.4.4
2 Version: 1.4.8
33 Summary: Implementation of per object permissions for Django.
44 Home-page: http://github.com/django-guardian/django-guardian
55 Author: Lukasz Balcerzak
77 License: BSD
88 Download-URL: https://github.com/django-guardian/django-guardian/tags
99 Description: ===============
10 django-guardian
11 ===============
12
13 .. image:: https://travis-ci.org/django-guardian/django-guardian.svg?branch=devel
14 :target: https://travis-ci.org/django-guardian/django-guardian
15
16 ``django-guardian`` is an implementation of per object permissions [1]_ on top
17 of Django's authorization backend
18
19 Documentation
20 -------------
21
22 Online documentation is available at https://django-guardian.readthedocs.io/.
23
24 Requirements
25 ------------
26
27 * Python 2.7 or 3.4+
28 * A supported version of Django (currently 1.8+)
29
30 Travis CI tests on Django version 1.8, 1.10, and 1.11.
31
32 Installation
33 ------------
34
35 To install ``django-guardian`` simply run::
36
37 pip install django-guardian
38
39 Configuration
40 -------------
41
42 We need to hook ``django-guardian`` into our project.
43
44 1. Put ``guardian`` into your ``INSTALLED_APPS`` at settings module:
45
46 .. code:: python
47
48 INSTALLED_APPS = (
49 ...
50 'guardian',
51 )
52
53 2. Add extra authorization backend to your ``settings.py``:
54
55 .. code:: python
56
57 AUTHENTICATION_BACKENDS = (
58 'django.contrib.auth.backends.ModelBackend', # default
59 'guardian.backends.ObjectPermissionBackend',
60 )
61
62 3. Create ``guardian`` database tables by running::
63
64 python manage.py migrate
65
66 Usage
67 -----
68
69 After installation and project hooks we can finally use object permissions
70 with Django_.
71
72 Lets start really quickly:
73
74 .. code:: python
75
76 >>> from django.contrib.auth.models import User, Group
77 >>> jack = User.objects.create_user('jack', 'jack@example.com', 'topsecretagentjack')
78 >>> admins = Group.objects.create(name='admins')
79 >>> jack.has_perm('change_group', admins)
80 False
81 >>> from guardian.models import UserObjectPermission
82 >>> UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins)
83 <UserObjectPermission: admins | jack | change_group>
84 >>> jack.has_perm('change_group', admins)
85 True
86
87 Of course our agent jack here would not be able to *change_group* globally:
88
89 .. code:: python
90
91 >>> jack.has_perm('change_group')
92 False
93
94 Admin integration
95 -----------------
96
97 Replace ``admin.ModelAdmin`` with ``GuardedModelAdmin`` for those models
98 which should have object permissions support within admin panel.
99
100 For example:
101
102 .. code:: python
103
104 from django.contrib import admin
105 from myapp.models import Author
106 from guardian.admin import GuardedModelAdmin
107
108 # Old way:
109 #class AuthorAdmin(admin.ModelAdmin):
110 # pass
111
112 # With object permissions support
113 class AuthorAdmin(GuardedModelAdmin):
114 pass
115
116 admin.site.register(Author, AuthorAdmin)
117
118
119 .. [1] Great paper about this feature is available at `djangoadvent articles <https://github.com/djangoadvent/djangoadvent-articles/blob/master/1.2/06_object-permissions.rst>`_.
120
121 .. _Django: http://www.djangoproject.com/
122
123
10124 Platform: UNKNOWN
11125 Classifier: Development Status :: 5 - Production/Stable
12126 Classifier: Environment :: Web Environment
17131 Classifier: Programming Language :: Python
18132 Classifier: Topic :: Security
19133 Classifier: Programming Language :: Python :: 2.7
20 Classifier: Programming Language :: Python :: 3.3
21134 Classifier: Programming Language :: Python :: 3.4
22135 Classifier: Programming Language :: Python :: 3.5
136 Classifier: Programming Language :: Python :: 3.6
00 .gitignore
1 .isort.cfg
12 .travis.yml
23 AUTHORS
34 CHANGES
6162 docs/userguide/performance.rst
6263 docs/userguide/remove.rst
6364 example_project/__init__.py
64 example_project/context_processors.py
6565 example_project/manage.py
6666 example_project/requirements.txt
6767 example_project/settings.py
6868 example_project/urls.py
69 example_project/articles/__init__.py
70 example_project/articles/admin.py
71 example_project/articles/apps.py
72 example_project/articles/models.py
73 example_project/articles/tests.py
74 example_project/articles/urls.py
75 example_project/articles/views.py
76 example_project/articles/migrations/0001_initial.py
77 example_project/articles/migrations/0002_auto_20160622_1050.py
78 example_project/articles/migrations/__init__.py
79 example_project/articles/templates/articles/article_confirm_delete.html
80 example_project/articles/templates/articles/article_detail.html
81 example_project/articles/templates/articles/article_form.html
82 example_project/articles/templates/articles/article_list.html
6983 example_project/core/__init__.py
7084 example_project/core/admin.py
85 example_project/core/context_processors.py
7186 example_project/core/models.py
7287 example_project/core/migrations/0001_initial.py
7388 example_project/core/migrations/__init__.py
96111 guardian/checks.py
97112 guardian/compat.py
98113 guardian/core.py
114 guardian/ctypes.py
99115 guardian/decorators.py
100116 guardian/exceptions.py
101117 guardian/forms.py
104120 guardian/models.py
105121 guardian/shortcuts.py
106122 guardian/utils.py
107 guardian/version.py
108123 guardian/conf/__init__.py
109124 guardian/conf/settings.py
110125 guardian/locale/es/LC_MESSAGES/django.mo
142157 guardian/testapp/testsettings.py
143158 guardian/testapp/migrations/0001_initial.py
144159 guardian/testapp/migrations/0002_logentrywithgroup.py
145 guardian/testapp/migrations/0003_auto_20141124_0729.py
146 guardian/testapp/migrations/0004_auto_20151112_2209.py
147 guardian/testapp/migrations/0005_auto_20151217_2344.py
148 guardian/testapp/migrations/0006_auto_20160221_1054.py
149 guardian/testapp/migrations/0007_auto_20160309_0245.py
150160 guardian/testapp/migrations/__init__.py
151161 guardian/testapp/tests/__init__.py
152162 guardian/testapp/tests/conf.py
170180 guardian/testapp/tests/templates/404.html
171181 guardian/testapp/tests/templates/500.html
172182 guardian/testapp/tests/templates/blank.html
173 guardian/testapp/tests/templates/dummy403.html⏎
183 guardian/testapp/tests/templates/dummy403.html
184 guardian/testapp/tests/templates/dummy404.html
185 guardian/testapp/tests/templates/list.html⏎
2525 .. autoclass:: guardian.mixins.PermissionRequiredMixin
2626 :members:
2727
28 PermissionListMixin
29 -----------------------
30
31 .. autoclass:: guardian.mixins.PermissionListMixin
32 :members:
5454
5555 # General information about the project.
5656 project = u'django-guardian'
57 copyright = u'2010-2013, Lukasz Balcerzak'
57 copyright = u'Lukasz Balcerzak'
5858
5959 # The version info for the project you're documenting, acts as replacement for
6060 # |version| and |release|, also used in various other places throughout the
6161 # built documents.
6262 #
6363 # The short X.Y version.
64 from setuptools_scm import get_version
65 version = get_version(root="..")
64 version = guardian.__version__
6665 # The full version, including alpha/beta/rc tags.
6766 release = version
6867
138137 # Add any paths that contain custom static files (such as style sheets) here,
139138 # relative to this directory. They are copied after the builtin static files,
140139 # so a file named "default.css" will overwrite the builtin "default.css".
141 html_static_path = ['_static']
140 # html_static_path = ['_static']
142141
143142 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
144143 # using the given strftime format.
134134
135135
136136 .. seealso:: :ref:`custom-user-model-anonymous`
137
138 GUARDIAN_GET_CONTENT_TYPE
139 -------------------------
140
141 .. versionadded:: 1.5
142
143 Guardian allows applications to supply a custom function to retrieve the
144 content type from objects and models. This is useful when a class or class
145 hierarchy uses the ``ContentType`` framework in an non-standard way. Most
146 applications will not have to change this setting.
147
148 As an example, when using ``django-polymorphic`` it's useful to use a
149 permission on the base model which applies to all child models. In this case,
150 the custom function would return the ``ContentType`` of the base class for
151 polymorphic models and the regular model ``ContentType`` for non-polymorphic
152 classes.
153
154 Defaults to ``"guardian.ctypes.get_default_content_type"``.⏎
2828 Alternate projects
2929 ------------------
3030
31 Django_ 1.2 still has *only* foundation for object permissions [1]_ and
31 Django_ still has *only* foundation for object permissions [1]_ and
3232 ``django-guardian`` make use of new facilities and it is based on them. There
33 are some other pluggable applications which does *NOT* require latest 1.2
33 are some other pluggable applications which does *NOT* require 1.2+
3434 version of Django_. For instance, there are great `django-authority`_ or
3535 `django-permissions`_ available out there.
3636
3737 def __unicode__(self):
3838 return self.title
3939
40 @models.permalink
4140 def get_absolute_url(self):
4241 return {'post_slug': self.slug}
4342
1515 class Task(models.Model):
1616 summary = models.CharField(max_length=32)
1717 content = models.TextField()
18 reported_by = models.ForeignKey(User)
18 reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
1919 created_at = models.DateTimeField(auto_now_add=True)
2020
2121 ... and we want to be able to set custom permission *view_task*. We let Django
2727 class Task(models.Model):
2828 summary = models.CharField(max_length=32)
2929 content = models.TextField()
30 reported_by = models.ForeignKey(User)
30 reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
3131 created_at = models.DateTimeField(auto_now_add=True)
3232
3333 class Meta:
127127 >>> userA.has_perm('change_company', companyB)
128128 False
129129 >>> userB = User.objects.create(username="User B")
130 >>> userB.groups.add(companyUserGroupB)
130131 >>> userB.has_perm('change_company', companyA)
131132 False
132 >>> userA.has_perm('change_company', companyB)
133 >>> userB.has_perm('change_company', companyB)
133134 True
134135
135136 Assigning Permissions inside Signals
1515 of the same app where the custom user model lives.
1616
1717 To fix this, it is recommended to add the setting ``GUARDIAN_MONKEY_PATCH = False``
18 in your settings.py and add the ``GuardianUserMixin`` to your custom user model.
18 in your settings.py and subclass ``guardian.mixins.GuardianUserMixin`` in your custom user model.
1919
2020 .. important::
2121 ``django-guardian`` relies **heavily** on the ``auth.User`` model.
8787 name = models.CharField(max_length=128, unique=True)
8888
8989 class ProjectUserObjectPermission(UserObjectPermissionBase):
90 content_object = models.ForeignKey(Project)
90 content_object = models.ForeignKey(Project, on_delete=models.CASCADE)
9191
9292 class ProjectGroupObjectPermission(GroupObjectPermissionBase):
93 content_object = models.ForeignKey(Project)
93 content_object = models.ForeignKey(Project, on_delete=models.CASCADE)
9494
9595
9696 .. important::
112112 .. _performance-prefetch:
113113
114114 Prefetching permissions
115 -------------------
115 -----------------------
116116
117117 .. versionadded:: 1.4.3
118118
0 from django.contrib import admin
1 from .models import Article
2
3
4 class ArticleAdmin(admin.ModelAdmin):
5 list_display = ('title', 'slug', 'created_at')
6 list_filter = ('created_at',)
7 search_fields = ('title',)
8
9 admin.site.register(Article, ArticleAdmin)
0 from __future__ import unicode_literals
1
2 from django.apps import AppConfig
3
4
5 class ArticlesConfig(AppConfig):
6 name = 'articles'
0 # -*- coding: utf-8 -*-
1 # Generated by Django 1.9.4 on 2016-06-22 05:23
2 from __future__ import unicode_literals
3
4 from django.conf import settings
5 from django.db import migrations, models
6 import django.db.models.deletion
7
8
9 class Migration(migrations.Migration):
10
11 initial = True
12
13 dependencies = [
14 ('auth', '0001_initial'),
15 migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 ]
17
18 operations = [
19 migrations.CreateModel(
20 name='Article',
21 fields=[
22 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 ('title', models.CharField(max_length=64, verbose_name='title')),
24 ('slug', models.SlugField(max_length=64)),
25 ('content', models.TextField(verbose_name='content')),
26 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
27 ],
28 options={
29 'get_latest_by': 'created_at',
30 'permissions': (('view_articlew', 'Can view article'),),
31 },
32 ),
33 migrations.CreateModel(
34 name='ArticleGroupObjectPermission',
35 fields=[
36 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
37 ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')),
38 ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
39 ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')),
40 ],
41 options={
42 'abstract': False,
43 },
44 ),
45 migrations.CreateModel(
46 name='ArticleUserObjectPermission',
47 fields=[
48 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
49 ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')),
50 ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')),
51 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
52 ],
53 options={
54 'abstract': False,
55 },
56 ),
57 migrations.AlterUniqueTogether(
58 name='articleuserobjectpermission',
59 unique_together=set([('user', 'permission', 'content_object')]),
60 ),
61 migrations.AlterUniqueTogether(
62 name='articlegroupobjectpermission',
63 unique_together=set([('group', 'permission', 'content_object')]),
64 ),
65 ]
0 # -*- coding: utf-8 -*-
1 # Generated by Django 1.9.4 on 2016-06-22 10:50
2 from __future__ import unicode_literals
3
4 from django.db import migrations
5
6
7 class Migration(migrations.Migration):
8
9 dependencies = [
10 ('articles', '0001_initial'),
11 ]
12
13 operations = [
14 migrations.AlterModelOptions(
15 name='article',
16 options={'get_latest_by': 'created_at', 'permissions': (('view_article', 'Can view article'),)},
17 ),
18 ]
0 from __future__ import unicode_literals
1
2 from django.db import models
3 from guardian.compat import reverse
4
5
6 from guardian.models import GroupObjectPermissionBase, UserObjectPermissionBase
7
8
9 class Article(models.Model):
10 title = models.CharField('title', max_length=64)
11 slug = models.SlugField(max_length=64)
12 content = models.TextField('content')
13 created_at = models.DateTimeField(auto_now_add=True, db_index=True)
14
15 class Meta:
16 permissions = (
17 ('view_article', 'Can view article'),
18 )
19 get_latest_by = 'created_at'
20
21 def __unicode__(self):
22 return self.title
23
24 def get_absolute_url(self):
25 return reverse('articles:details', kwargs={'slug': self.slug})
26
27
28 class ArticleUserObjectPermission(UserObjectPermissionBase):
29 content_object = models.ForeignKey(Article, on_delete=models.CASCADE)
30
31
32 class ArticleGroupObjectPermission(GroupObjectPermissionBase):
33 content_object = models.ForeignKey(Article, on_delete=models.CASCADE)
0 {% extends "base.html" %}
1
2 {% load guardian_tags %}
3
4
5 {% block content %}
6 <p>Are you sure do you want to delete "<a href="{{object.get_absolute_url}}">{{object}}</a>"?</p>
7 <form method="POST">
8 {% csrf_token %}
9 <input type="submit" class="btn btn-primary" value="Delete">
10 </form>
11 {% endblock %}
12
0 {% extends "base.html" %}
1
2 {% load guardian_tags %}
3
4
5 {% block content %}
6 {% get_obj_perms request.user for object as 'object_perms' %}
7 <div class="row">
8 <div class="span3">
9 <a href="{% url "articles:list" %}">Back to article list</a>
10 </div>
11
12
13 <div class="span9">
14 {% if "change_article" in object_perms %}
15 <a href="{% url 'articles:update' slug=object.slug%}" class="btn btn-primary">Update</a>
16 {% endif %}
17 {% if "delete_article" in object_perms %}
18 <a href="{% url 'articles:delete' slug=object.slug%}" class="btn btn-primary">Delete</a>
19 {% endif %}
20 <h1 name="{{ object.id }}">{{ object.title }}</h1>
21 <h5>at {{ object.created_at }}</h5>
22 <hr/>
23 {{ object.content|safe }}
24 </div>
25 </div>
26 {% endblock %}
27
0 {% extends "base.html" %}
1
2 {% load guardian_tags %}
3
4 {% block content %}
5 <div class="row">
6 <div class="span12">
7 <h1>{% if object %}
8 <a href="{{ object.get_absolute_url }}" name="{{ object.id }}">Back to "{{ object.title }}"</a>
9 {% else %}
10 <a href="{% url "articles:list" %}">Back to article list</a>
11 {% endif %}</h1>
12 <form method="post">{% csrf_token %} {{ form.as_p }}
13 <input type="submit" value="Save" class="btn btn-primary" />
14 </form>
15 </div>
16 </div>
17 {% endblock %}
0 {% extends "base.html" %}
1
2 {% block content %}
3 <div class="row">
4
5 <div class="span12">
6 <div class="hero-unit">
7 <h1>Articles</h1>
8 <a href="{% url "articles:create" %}" class="btn btn-primary">Add article</a>
9 </div>
10 </div>
11 {% for object in object_list %}
12 <div class="span3">
13 </div>
14 <div class="span9">
15 <a href="{{object.get_absolute_url}}" name="{{ post.id }}"><h1>{{ object.title }}</h1></a>
16 <h5>at {{ object.created_at }}</h5>
17 <hr/>
18 {{ object.content|safe }}
19 </div>
20 {% endfor %}
21 </div>
22
23
24 {% endblock %}
0 from django.test import TestCase
1 from django.test.client import RequestFactory
2 from guardian.compat import get_user_model
3 from guardian.shortcuts import assign_perm
4 from articles.models import Article
5 from articles.views import (ArticleCreateView, ArticleDeleteView, ArticleDetailView,
6 ArticleListView, ArticleUpdateView)
7
8
9 class ViewTestCase(TestCase):
10 def setUp(self):
11 self.article = Article.objects.create(title='foo-title',
12 slug='foo-slug',
13 content='bar-content')
14 self.factory = RequestFactory()
15 self.user = get_user_model().objects.create_user(
16 'joe', 'joe@doe.com', 'doe')
17 self.client.login(username='joe', password='doe')
18
19 def test_list_permitted(self):
20 request = self.factory.get('/')
21 request.user = self.user
22 assign_perm('articles.view_article', self.user, self.article)
23 assign_perm('articles.delete_article', self.user, self.article)
24 view = ArticleListView.as_view()
25 response = view(request)
26 response.render()
27 self.assertContains(response, 'foo-title')
28
29 def test_list_denied(self):
30 request = self.factory.get('/')
31 request.user = self.user
32 view = ArticleListView.as_view()
33 response = view(request)
34 response.render()
35 self.assertNotContains(response, 'foo-title')
36
37 def test_create_permitted(self):
38 request = self.factory.get('/~create')
39 request.user = self.user
40 assign_perm('articles.add_article', self.user)
41 view = ArticleCreateView.as_view()
42 response = view(request)
43 self.assertEqual(response.status_code, 200)
44
45 def test_create_denied(self):
46 request = self.factory.get('/~create')
47 request.user = self.user
48 view = ArticleCreateView.as_view()
49 response = view(request)
50 self.assertEqual(response.status_code, 302)
51
52 def test_detail_permitted(self):
53 request = self.factory.get('/foo/')
54 request.user = self.user
55 assign_perm('articles.view_article', self.user, self.article)
56 view = ArticleDetailView.as_view()
57 response = view(request, slug='foo-slug')
58 self.assertEqual(response.status_code, 200)
59
60 def test_detail_denied(self):
61 request = self.factory.get('/foo/')
62 request.user = self.user
63 view = ArticleDetailView.as_view()
64 response = view(request, slug='foo-slug')
65 self.assertEqual(response.status_code, 302)
66
67 def test_update_permitted(self):
68 request = self.factory.get('/')
69 request.user = self.user
70 assign_perm('articles.view_article', self.user, self.article)
71 assign_perm('articles.change_article', self.user, self.article)
72 view = ArticleUpdateView.as_view()
73 response = view(request, slug='foo-slug')
74 self.assertEqual(response.status_code, 200)
75
76 def test_update_denied(self):
77 request = self.factory.get('/')
78 request.user = self.user
79 view = ArticleUpdateView.as_view()
80 response = view(request, slug='foo-slug')
81 self.assertEqual(response.status_code, 302)
82
83 def test_delete_permitted(self):
84 request = self.factory.get('/foo-slug/~delete')
85 request.user = self.user
86 assign_perm('articles.view_article', self.user, self.article)
87 assign_perm('articles.delete_article', self.user, self.article)
88 view = ArticleDeleteView.as_view()
89 response = view(request, slug='foo-slug')
90 self.assertEqual(response.status_code, 200)
91
92 def test_delete_denied(self):
93 request = self.factory.get('/foo/~delete')
94 request.user = self.user
95 view = ArticleDeleteView.as_view()
96 response = view(request, slug='foo-slug')
97 self.assertEqual(response.status_code, 302)
0 # -*- coding: utf-8 -*-
1 from __future__ import unicode_literals
2
3 from django.conf.urls import url
4 from . import views
5
6 urlpatterns = [
7 url(r'^$', views.ArticleListView.as_view(),
8 name="list"),
9 url(r'^~create$', views.ArticleCreateView.as_view(),
10 name="create"),
11 url(r'^(?P<slug>[\w-]+)$', views.ArticleDetailView.as_view(),
12 name="details"),
13 url(r'^(?P<slug>[\w-]+)/~update$', views.ArticleUpdateView.as_view(),
14 name="update"),
15 url(r'^(?P<slug>[\w-]+)/~delete$', views.ArticleDeleteView.as_view(),
16 name="delete"),
17 ]
0 from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
1 UpdateView)
2 from guardian.compat import reverse_lazy
3 from guardian.mixins import PermissionRequiredMixin, PermissionListMixin
4 from guardian.shortcuts import assign_perm
5 from articles.models import Article
6
7
8 class ArticleListView(PermissionListMixin, ListView):
9 model = Article
10 permission_required = ['view_article', ]
11
12
13 class ArticleDetailView(PermissionRequiredMixin, DetailView):
14 model = Article
15 permission_required = ['view_article']
16
17
18 class ArticleCreateView(PermissionRequiredMixin, CreateView):
19 model = Article
20 permission_object = None
21 permission_required = ['articles.add_article']
22 fields = ['title', 'slug', 'content']
23
24 def form_valid(self, *args, **kwargs):
25 resp = super(ArticleCreateView, self).form_valid(*args, **kwargs)
26 assign_perm('view_article', self.request.user, self.object)
27 assign_perm('change_article', self.request.user, self.object)
28 assign_perm('delete_article', self.request.user, self.object)
29 return resp
30
31
32 class ArticleUpdateView(PermissionRequiredMixin, UpdateView):
33 model = Article
34 permission_required = ['view_article', 'change_article']
35 fields = ['title', 'slug', 'content']
36
37
38 class ArticleDeleteView(PermissionRequiredMixin, DeleteView):
39 model = Article
40 success_url = reverse_lazy('articles:list')
41 permission_required = ['view_article', 'delete_article']
+0
-5
example_project/context_processors.py less more
0 import guardian
1
2
3 def version(request):
4 return {'version': guardian.get_version()}
0 import guardian
1
2
3 def version(request):
4 return {'version': guardian.get_version()}
00 # -*- coding: utf-8 -*-
11 from __future__ import unicode_literals
2
2 import django
33 from django.db import migrations, models
44 import django.utils.timezone
55 import django.core.validators
66 import django.contrib.auth.models
77
8 if django.VERSION >= (1, 8):
9 django_version_depend = {'managers': [
10 ('objects', django.contrib.auth.models.UserManager()),
11 ]}
12 else:
13 django_version_depend = {}
14
815
916 class Migration(migrations.Migration):
1017
1118 dependencies = [
12 ('auth', '0006_require_contenttypes_0002'),
19 ('auth', '0001_initial'),
1320 ]
1421
1522 operations = [
4956 'verbose_name': 'user',
5057 'verbose_name_plural': 'users',
5158 },
52 managers=[
53 ('objects', django.contrib.auth.models.UserManager()),
54 ],
59 **django_version_depend
5560 ),
5661 ]
44
55 if django.VERSION < (1, 5):
66 sys.stderr.write("ERROR: guardian's example project must be run with "
7 "Django 1.5 or later!\n")
7 "Django 1.8 or later!\n")
88 sys.exit(1)
99
1010
2626 'django.contrib.messages',
2727 'guardian',
2828 'posts',
29 'articles',
2930 'core',
3031 'django.contrib.staticfiles',
3132 )
5960 ROOT_URLCONF = 'urls'
6061
6162 TEMPLATE_CONTEXT_PROCESSORS = (
62 'context_processors.version',
63 'core.context_processors.version',
6364 "django.contrib.auth.context_processors.auth",
6465 "django.template.context_processors.debug",
6566 "django.template.context_processors.i18n",
6667 "django.template.context_processors.media",
6768 "django.template.context_processors.static",
69 "django.template.context_processors.request",
6870 "django.template.context_processors.tz",
6971 "django.contrib.messages.context_processors.messages"
7072 )
99101 )
100102
101103 AUTH_USER_MODEL = 'core.CustomUser'
104
105
106 TEMPLATES = [
107 {
108 'BACKEND': 'django.template.backends.django.DjangoTemplates',
109 'DIRS': TEMPLATE_DIRS,
110 'OPTIONS': {
111 'debug': TEMPLATE_DEBUG,
112 'loaders': TEMPLATE_LOADERS,
113 'context_processors': TEMPLATE_CONTEXT_PROCESSORS,
114 },
115 },
116 ]
2424 <div class="nav-collapse collapse">
2525 <ul class="nav">
2626 <li class="active"><a href="{% url "posts_post_list" %}">Posts</a></li>
27 <li class="active"><a href="{% url "articles:list" %}">Articles</a></li>
2728 <li><a href="https://github.com/django-guardian/django-guardian" target="_repo">Repository</a></li>
2829 <li><a href="https://github.com/django-guardian/django-guardian/issues" target="_issues">Issue tracker</a></li>
29 <li><a href="https://django-guardian.readthedocs.org/en/latest/" target="_docs">Documentation</a></li>
30 <li><a href="https://django-guardian.readthedocs.io/en/latest/" target="_docs">Documentation</a></li>
3031 </ul>
3132 <p class="navbar-text pull-right">
3233 {% if user.is_authenticated %}
11 from django.conf import settings
22 from django.contrib import admin
33 from django.contrib.auth.views import logout
4 from django.conf.urls import url
54
65 __all__ = ['handler404', 'handler500']
76
1110 urlpatterns = [
1211 url(r'^admin/', include(admin.site.urls)),
1312 url(r'^logout/$', logout, {'next_page': '/'}, name='logout'),
13 url(r'^article/', include('articles.urls', namespace='articles')),
1414 url(r'^', include('posts.urls')),
1515 ]
1616
33 from __future__ import unicode_literals
44 from . import checks
55
6 default_app_config = 'guardian.apps.GuardianConfig'
67
7 try:
8 from .version import version as __version__
9 __version__split__ = __version__.split(".")
10 VERSION = tuple([int(each) for each in __version__split__[:3]] + __version__split__[3:])
8 # PEP 396: The __version__ attribute's value SHOULD be a string.
9 __version__ = '1.4.8'
1110
12 def get_version():
13 """
14 Returns shorter version (digit parts only) as string.
15 """
16 return '.'.join((str(each) for each in VERSION[:3]))
17 except ImportError:
18 pass
11 # Compatibility to eg. django-rest-framework
12 VERSION = tuple(int(x) for x in __version__.split('.')[:3])
1913
2014
21 default_app_config = 'guardian.apps.GuardianConfig'
15 def get_version():
16 return __version__
2217
2318
2419 def monkey_patch_user():
00 from __future__ import unicode_literals
1
2 import django
31 from django import forms
42 from django.conf import settings
5 from guardian.compat import url
6 from django.contrib import admin
7 from django.contrib import messages
3 from django.contrib import admin, messages
84 from django.contrib.admin.widgets import FilteredSelectMultiple
9 from django.core.urlresolvers import reverse
10 from django.shortcuts import render_to_response, get_object_or_404, redirect
5 from django.shortcuts import get_object_or_404, redirect, render_to_response, render
116 from django.template import RequestContext
12 from django.utils.translation import ugettext, ugettext_lazy as _
13
14 from guardian.compat import OrderedDict, get_user_model, get_model_name
15 from guardian.forms import UserObjectPermissionsForm
16 from guardian.forms import GroupObjectPermissionsForm
17 from guardian.shortcuts import get_user_perms
18 from guardian.shortcuts import get_group_perms
19 from guardian.shortcuts import get_users_with_perms
20 from guardian.shortcuts import get_groups_with_perms
21 from guardian.shortcuts import get_perms_for_model
7 from django.utils.translation import ugettext_lazy as _
8 from django.utils.translation import ugettext
9 from guardian.compat import get_model_name, get_user_model, OrderedDict, url, reverse
10 from guardian.forms import GroupObjectPermissionsForm, UserObjectPermissionsForm
2211 from guardian.models import Group
12 from guardian.shortcuts import (get_group_perms, get_groups_with_perms, get_perms_for_model, get_user_perms,
13 get_users_with_perms)
14
15 import django
2316
2417
2518 class AdminUserObjectPermissionsForm(UserObjectPermissionsForm):
177170 )
178171
179172 if request.method == 'POST' and 'submit_manage_user' in request.POST:
180 user_form = UserManage(request.POST)
181 group_form = GroupManage()
173 user_form = self.get_obj_perms_user_select_form(request)(request.POST)
174 group_form = self.get_obj_perms_group_select_form(request)(request.POST)
182175 info = (
183176 self.admin_site.name,
184177 self.model._meta.app_label,
192185 )
193186 return redirect(url)
194187 elif request.method == 'POST' and 'submit_manage_group' in request.POST:
195 user_form = UserManage()
196 group_form = GroupManage(request.POST)
188 user_form = self.get_obj_perms_user_select_form(request)(request.POST)
189 group_form = self.get_obj_perms_group_select_form(request)(request.POST)
197190 info = (
198191 self.admin_site.name,
199192 self.model._meta.app_label,
207200 )
208201 return redirect(url)
209202 else:
210 user_form = UserManage()
211 group_form = GroupManage()
203 user_form = self.get_obj_perms_user_select_form(request)()
204 group_form = self.get_obj_perms_group_select_form(request)()
212205
213206 context = self.get_obj_perms_base_context(request, obj)
214207 context['users_perms'] = users_perms
219212 # https://github.com/django/django/commit/cf1f36bb6eb34fafe6c224003ad585a647f6117b
220213 request.current_app = self.admin_site.name
221214
215 if django.VERSION >= (1, 10):
216 return render(request, self.get_obj_perms_manage_template(), context)
217
222218 return render_to_response(self.get_obj_perms_manage_template(), context, RequestContext(request))
223219
224220 def get_obj_perms_manage_template(self):
245241
246242 user = get_object_or_404(get_user_model(), pk=user_id)
247243 obj = get_object_or_404(self.get_queryset(request), pk=object_pk)
248 form_class = self.get_obj_perms_manage_user_form()
244 form_class = self.get_obj_perms_manage_user_form(request)
249245 form = form_class(user, obj, request.POST or None)
250246
251247 if request.method == 'POST' and form.is_valid():
270266
271267 request.current_app = self.admin_site.name
272268
269 if django.VERSION >= (1, 10):
270 return render(request, self.get_obj_perms_manage_user_template(), context)
271
273272 return render_to_response(self.get_obj_perms_manage_user_template(), context, RequestContext(request))
274273
275274 def get_obj_perms_manage_user_template(self):
286285 return 'admin/guardian/contrib/grappelli/obj_perms_manage_user.html'
287286 return self.obj_perms_manage_user_template
288287
289 def get_obj_perms_manage_user_form(self):
288 def get_obj_perms_user_select_form(self, request):
289 """
290 Returns form class for selecting a user for permissions management. By
291 default :form:`UserManage` is returned.
292 """
293 return UserManage
294
295 def get_obj_perms_group_select_form(self, request):
296 """
297 Returns form class for selecting a group for permissions management. By
298 default :form:`GroupManage` is returned.
299 """
300 return GroupManage
301
302
303 def get_obj_perms_manage_user_form(self, request):
290304 """
291305 Returns form class for user object permissions management. By default
292306 :form:`AdminUserObjectPermissionsForm` is returned.
303317
304318 group = get_object_or_404(Group, id=group_id)
305319 obj = get_object_or_404(self.get_queryset(request), pk=object_pk)
306 form_class = self.get_obj_perms_manage_group_form()
320 form_class = self.get_obj_perms_manage_group_form(request)
307321 form = form_class(group, obj, request.POST or None)
308322
309323 if request.method == 'POST' and form.is_valid():
328342
329343 request.current_app = self.admin_site.name
330344
345 if django.VERSION >= (1, 10):
346 return render(request, self.get_obj_perms_manage_group_template(), context)
347
331348 return render_to_response(self.get_obj_perms_manage_group_template(), context, RequestContext(request))
332349
333350 def get_obj_perms_manage_group_template(self):
344361 return 'admin/guardian/contrib/grappelli/obj_perms_manage_group.html'
345362 return self.obj_perms_manage_group_template
346363
347 def get_obj_perms_manage_group_form(self):
364 def get_obj_perms_manage_group_form(self, request):
348365 """
349366 Returns form class for group object permissions management. By default
350367 :form:`AdminGroupObjectPermissionsForm` is returned.
0
0 from . import monkey_patch_user
11 from django.apps import AppConfig
2 from . import monkey_patch_user
32 from guardian.conf import settings
43
54
00 from __future__ import unicode_literals
1
21 from django.db import models
3
4 from guardian.compat import get_user_model
2 from guardian.compat import get_user_model, is_authenticated
53 from guardian.conf import settings
4 from guardian.core import ObjectPermissionChecker
5 from guardian.ctypes import get_content_type
66 from guardian.exceptions import WrongAppError
7 from guardian.core import ObjectPermissionChecker
87
98
109 def check_object_support(obj):
2726 """
2827 # This is how we support anonymous users - simply try to retrieve User
2928 # instance and perform checks for that predefined user
30 if not user_obj.is_authenticated():
29 if not is_authenticated(user_obj):
3130 # If anonymous user permission is disabled then they are always
3231 # unauthorized
3332 if settings.ANONYMOUS_USER_NAME is None:
8483 if '.' in perm:
8584 app_label, perm = perm.split('.')
8685 if app_label != obj._meta.app_label:
87 raise WrongAppError("Passed perm has app label of '%s' and "
88 "given obj has '%s'" % (app_label, obj._meta.app_label))
86 # Check the content_type app_label when permission
87 # and obj app labels don't match.
88 ctype = get_content_type(obj)
89 if app_label != ctype.app_label:
90 raise WrongAppError("Passed perm has app label of '%s' while "
91 "given obj has app label '%s' and given obj"
92 "content_type has app label '%s'" %
93 (app_label, obj._meta.app_label, ctype.app_label))
8994
9095 check = ObjectPermissionChecker(user_obj)
9196 return check.has_perm(perm, obj)
0
0 from django.conf import settings
11 from django.core.checks import register, Tags, Warning
2 from django.conf import settings
32
43
54 # noinspection PyUnusedLocal
00 from __future__ import unicode_literals
1 # OrderedDict only available in Python 2.7.
2 # This will always be the case in Django 1.7 and above, as these versions
3 # no longer support Python 2.6.
4
5 from collections import OrderedDict
6 from django.conf import settings
7 from django.conf.urls import handler404, handler500, include, url
8 from django.contrib.auth.models import AnonymousUser, Group, Permission
9 from importlib import import_module
110
211 import django
3 from django.conf import settings
4 from django.contrib.auth.models import Group
5 from django.contrib.auth.models import Permission
6 from django.contrib.auth.models import AnonymousUser
712 import six
813 import sys
9 from importlib import import_module
10
11 from django.conf.urls import url, include, handler404, handler500
1214
1315 __all__ = [
1416 'User',
100102 basestring = unicode = str = str
101103
102104
103 # OrderedDict only available in Python 2.7.
104 # This will always be the case in Django 1.7 and above, as these versions
105 # no longer support Python 2.6.
106 from collections import OrderedDict
107
108
109105 # Django 1.7 compatibility
110106 # create_permission API changed: skip the create_models (second
111107 # positional argument) if we have django 1.7+ and 2+ positional
139135 if hasattr(settings, 'TEMPLATE_DEBUG'):
140136 return settings.TEMPLATE_DEBUG
141137 return settings.TEMPLATES[0]['OPTIONS'].get('DEBUG', False)
138
139
140 # Django 1.9 compatibility
141 def remote_field(field):
142 """
143 https://docs.djangoproject.com/en/1.9/releases/1.9/#field-rel-changes
144 """
145 if django.VERSION < (1, 9):
146 return field.rel
147 return field.remote_field
148
149
150 def remote_model(field):
151 if django.VERSION < (1, 9):
152 return remote_field(field).to
153 return remote_field(field).model
154
155
156 # Django 1.10 compatibility
157 def is_authenticated(user):
158 if django.VERSION < (1, 10):
159 return user.is_authenticated()
160 return user.is_authenticated
161
162
163 def is_anonymous(user):
164 if django.VERSION < (1, 10):
165 return user.is_anonymous()
166 return user.is_anonymous
167
168 try:
169 from django.urls import reverse, reverse_lazy
170 except ImportError:
171 from django.core.urlresolvers import reverse, reverse_lazy
1414 RENDER_403 = getattr(settings, 'GUARDIAN_RENDER_403', False)
1515 TEMPLATE_403 = getattr(settings, 'GUARDIAN_TEMPLATE_403', '403.html')
1616 RAISE_403 = getattr(settings, 'GUARDIAN_RAISE_403', False)
17 RENDER_404 = getattr(settings, 'GUARDIAN_RENDER_404', False)
18 TEMPLATE_404 = getattr(settings, 'GUARDIAN_TEMPLATE_404', '404.html')
19 RAISE_404 = getattr(settings, 'GUARDIAN_RAISE_404', False)
1720 GET_INIT_ANONYMOUS_USER = getattr(settings, 'GUARDIAN_GET_INIT_ANONYMOUS_USER',
1821 'guardian.management.get_init_anonymous_user')
1922
2023 MONKEY_PATCH = getattr(settings, 'GUARDIAN_MONKEY_PATCH', True)
24
25 GET_CONTENT_TYPE = getattr(settings, 'GUARDIAN_GET_CONTENT_TYPE', 'guardian.ctypes.get_default_content_type')
2126
2227
2328 def check_configuration():
00 from __future__ import unicode_literals
1
2 from itertools import chain
3
41 from django.contrib.auth.models import Permission
5 from django.contrib.contenttypes.models import ContentType
62 from django.db.models.query import QuerySet
73 from django.utils.encoding import force_text
8
9 from guardian.utils import get_identity
10 from guardian.utils import get_user_obj_perms_model
11 from guardian.utils import get_group_obj_perms_model
124 from guardian.compat import get_user_model
5 from guardian.ctypes import get_content_type
6 from guardian.utils import get_group_obj_perms_model, get_identity, get_user_obj_perms_model
7 from itertools import chain
138
149
1510 def _get_pks_model_and_ctype(objects):
2116 if isinstance(objects, QuerySet):
2217 model = objects.model
2318 pks = [force_text(pk) for pk in objects.values_list('pk', flat=True)]
24 ctype = ContentType.objects.get_for_model(model)
19 ctype = get_content_type(model)
2520 else:
2621 pks = []
2722 for idx, obj in enumerate(objects):
2823 if not idx:
2924 model = type(obj)
30 ctype = ContentType.objects.get_for_model(model)
25 ctype = get_content_type(model)
3126 pks.append(force_text(obj.pk))
3227
3328 return pks, model, ctype
7166 :param obj: Django model instance for which permission should be checked
7267
7368 """
74 perm = perm.split('.')[-1]
7569 if self.user and not self.user.is_active:
7670 return False
7771 elif self.user and self.user.is_superuser:
7872 return True
73 perm = perm.split('.')[-1]
7974 return perm in self.get_perms(obj)
8075
8176 def get_group_filters(self, obj):
8277 User = get_user_model()
83 ctype = ContentType.objects.get_for_model(obj)
78 ctype = get_content_type(obj)
8479
8580 group_model = get_group_obj_perms_model(obj)
8681 group_rel_name = group_model.permission.field.related_query_name()
10398 return group_filters
10499
105100 def get_user_filters(self, obj):
106 ctype = ContentType.objects.get_for_model(obj)
101 ctype = get_content_type(obj)
107102 model = get_user_obj_perms_model(obj)
108103 related_name = model.permission.field.related_query_name()
109104
119114 return user_filters
120115
121116 def get_user_perms(self, obj):
122 ctype = ContentType.objects.get_for_model(obj)
117 ctype = get_content_type(obj)
123118
124119 perms_qs = Permission.objects.filter(content_type=ctype)
125120 user_filters = self.get_user_filters(obj)
129124 return user_perms
130125
131126 def get_group_perms(self, obj):
132 ctype = ContentType.objects.get_for_model(obj)
127 ctype = get_content_type(obj)
133128
134129 perms_qs = Permission.objects.filter(content_type=ctype)
135130 group_filters = self.get_group_filters(obj)
147142 """
148143 if self.user and not self.user.is_active:
149144 return []
150 ctype = ContentType.objects.get_for_model(obj)
145 ctype = get_content_type(obj)
151146 key = self.get_local_cache_key(obj)
152147 if key not in self._obj_perms_cache:
153148 if self.user and self.user.is_superuser:
173168 """
174169 Returns cache key for ``_obj_perms_cache`` dict.
175170 """
176 ctype = ContentType.objects.get_for_model(obj)
171 ctype = get_content_type(obj)
177172 return (ctype.id, force_text(obj.pk))
178173
179174 def prefetch_perms(self, objects):
203198
204199 group_model = get_group_obj_perms_model(model)
205200
206 group_filters = {}
207201 if self.user:
208202 fieldname = 'group__%s' % (
209203 User.groups.field.related_query_name(),
210204 )
211 group_filters.update({fieldname: self.user})
205 group_filters = {fieldname: self.user}
212206 else:
213207 group_filters = {'group': self.group}
214208
248242 *(group_model.objects.filter(**group_filters).select_related('permission'),)
249243 )
250244
245 # initialize entry in '_obj_perms_cache' for all prefetched objects
246 for obj in objects:
247 key = self.get_local_cache_key(obj)
248 if key not in self._obj_perms_cache:
249 self._obj_perms_cache[key] = []
250
251251 for perm in perms:
252252 if type(perm).objects.is_generic():
253253 key = (ctype.id, perm.object_pk)
254254 else:
255255 key = (ctype.id, force_text(perm.content_object_id))
256256
257 if key in self._obj_perms_cache:
258 self._obj_perms_cache[key].append(perm.permission.codename)
259 else:
260 self._obj_perms_cache[key] = [perm.permission.codename]
257 self._obj_perms_cache[key].append(perm.permission.codename)
261258
262259 return True
0 from __future__ import unicode_literals
1
2 from django.contrib.contenttypes.models import ContentType
3
4 from guardian.conf import settings as guardian_settings
5 from guardian.compat import import_string
6
7
8 def get_content_type(obj):
9 get_content_type_function = import_string(
10 guardian_settings.GET_CONTENT_TYPE)
11 return get_content_type_function(obj)
12
13
14 def get_default_content_type(obj):
15 return ContentType.objects.get_for_model(obj)
00 from __future__ import unicode_literals
1
1 from django.apps import apps
22 from django.conf import settings
33 from django.contrib.auth import REDIRECT_FIELD_NAME
4 from django.utils.functional import wraps
54 from django.db.models import Model
6 from django.apps import apps
75 from django.db.models.base import ModelBase
86 from django.db.models.query import QuerySet
97 from django.shortcuts import get_object_or_404
8 from django.utils.functional import wraps
109 from guardian.compat import basestring
1110 from guardian.exceptions import GuardianError
12 from guardian.utils import get_403_or_None
11 from guardian.utils import get_40x_or_None
1312
1413
1514 def permission_required(perm, lookup_variables=None, **kwargs):
3231 login page, response with status code 403 is returned (
3332 ``django.http.HttpResponseForbidden`` instance or rendered template -
3433 see :setting:`GUARDIAN_RENDER_403`). Defaults to ``False``.
34 :param return_404: if set to ``True`` then instead of redirecting to the
35 login page, response with status code 404 is returned (
36 ``django.http.HttpResponseNotFound`` instance or rendered template -
37 see :setting:`GUARDIAN_RENDER_404`). Defaults to ``False``.
3538 :param accept_global_perms: if set to ``True``, then *object level
3639 permission* would be required **only if user does NOT have global
3740 permission** for target *model*. If turned on, makes this decorator
7376 redirect_field_name = kwargs.pop(
7477 'redirect_field_name', REDIRECT_FIELD_NAME)
7578 return_403 = kwargs.pop('return_403', False)
79 return_404 = kwargs.pop('return_404', False)
7680 accept_global_perms = kwargs.pop('accept_global_perms', False)
7781
7882 # Check if perm is given as string in order not to decorate
113117 lookup_dict[lookup] = kwargs[view_arg]
114118 obj = get_object_or_404(model, **lookup_dict)
115119
116 response = get_403_or_None(request, perms=[perm], obj=obj,
120 response = get_40x_or_None(request, perms=[perm], obj=obj,
117121 login_url=login_url, redirect_field_name=redirect_field_name,
118 return_403=return_403, accept_global_perms=accept_global_perms)
122 return_403=return_403, return_404=return_404, accept_global_perms=accept_global_perms)
119123 if response:
120124 return response
121125 return view_func(request, *args, **kwargs)
136140 """
137141 kwargs['return_403'] = True
138142 return permission_required(perm, *args, **kwargs)
143
144
145 def permission_required_or_404(perm, *args, **kwargs):
146 """
147 Simple wrapper for permission_required decorator.
148
149 Standard Django's permission_required decorator redirects user to login page
150 in case permission check failed. This decorator may be used to return
151 HttpResponseNotFound (status 404) instead of redirection.
152
153 The only difference between ``permission_required`` decorator is that this
154 one always set ``return_404`` parameter to ``True``.
155 """
156 kwargs['return_404'] = True
157 return permission_required(perm, *args, **kwargs)
00 from __future__ import unicode_literals
1
21 from django import forms
32 from django.utils.translation import ugettext as _
4
5 from guardian.shortcuts import assign_perm
6 from guardian.shortcuts import remove_perm
7 from guardian.shortcuts import get_user_perms
8 from guardian.shortcuts import get_group_perms
9 from guardian.shortcuts import get_perms_for_model
3 from guardian.shortcuts import assign_perm, get_group_perms, get_perms_for_model, get_user_perms, remove_perm
104
115
126 class BaseObjectPermissionsForm(forms.Form):
00 from __future__ import unicode_literals
1
21 from django.db import models
3 from django.contrib.contenttypes.models import ContentType
4
2 from django.db.models import Q
3 from guardian.core import ObjectPermissionChecker
4 from guardian.ctypes import get_content_type
55 from guardian.exceptions import ObjectNotPersisted
66 from guardian.models import Permission
7
78 import warnings
8
9 # TODO: consolidate UserObjectPermissionManager and
10 # GroupObjectPermissionManager
119
1210
1311 class BaseObjectPermissionManager(models.Manager):
12
13 @property
14 def user_or_group_field(self):
15 try:
16 self.model._meta.get_field('user')
17 return 'user'
18 except models.fields.FieldDoesNotExist:
19 return 'group'
1420
1521 def is_generic(self):
1622 try:
1925 except models.fields.FieldDoesNotExist:
2026 return False
2127
22
23 class UserObjectPermissionManager(BaseObjectPermissionManager):
24
25 def assign_perm(self, perm, user, obj):
28 def assign_perm(self, perm, user_or_group, obj):
2629 """
2730 Assigns permission with given ``perm`` for an instance ``obj`` and
2831 ``user``.
3033 if getattr(obj, 'pk', None) is None:
3134 raise ObjectNotPersisted("Object %s needs to be persisted first"
3235 % obj)
33 ctype = ContentType.objects.get_for_model(obj)
34 permission = Permission.objects.get(content_type=ctype, codename=perm)
36 ctype = get_content_type(obj)
37 if not isinstance(perm, Permission):
38 permission = Permission.objects.get(content_type=ctype, codename=perm)
39 else:
40 permission = perm
3541
36 kwargs = {'permission': permission, 'user': user}
42 kwargs = {'permission': permission, self.user_or_group_field: user_or_group}
3743 if self.is_generic():
3844 kwargs['content_type'] = ctype
3945 kwargs['object_pk'] = obj.pk
4046 else:
4147 kwargs['content_object'] = obj
42 obj_perm, created = self.get_or_create(**kwargs)
48 obj_perm, _ = self.get_or_create(**kwargs)
4349 return obj_perm
4450
45 def assign(self, perm, user, obj):
51 def bulk_assign_perm(self, perm, user_or_group, queryset):
52 """
53 Bulk assigns permissions with given ``perm`` for an objects in ``queryset`` and
54 ``user_or_group``.
55 """
56
57 ctype = get_content_type(queryset.model)
58 if not isinstance(perm, Permission):
59 permission = Permission.objects.get(content_type=ctype, codename=perm)
60 else:
61 permission = perm
62
63 checker = ObjectPermissionChecker(user_or_group)
64 checker.prefetch_perms(queryset)
65
66 assigned_perms = []
67 for instance in queryset:
68 if not checker.has_perm(permission.codename, instance):
69 kwargs = {'permission': permission, self.user_or_group_field: user_or_group}
70 if self.is_generic():
71 kwargs['content_type'] = ctype
72 kwargs['object_pk'] = instance.pk
73 else:
74 kwargs['content_object'] = instance
75 assigned_perms.append(self.model(**kwargs))
76 self.model.objects.bulk_create(assigned_perms)
77
78 return assigned_perms
79
80 def assign(self, perm, user_or_group, obj):
4681 """ Depreciated function name left in for compatibility"""
4782 warnings.warn("UserObjectPermissionManager method 'assign' is being renamed to 'assign_perm'. Update your code accordingly as old name will be depreciated in 2.0 version.", DeprecationWarning)
48 return self.assign_perm(perm, user, obj)
83 return self.assign_perm(perm, user_or_group, obj)
4984
50 def remove_perm(self, perm, user, obj):
85 def remove_perm(self, perm, user_or_group, obj):
5186 """
52 Removes permission ``perm`` for an instance ``obj`` and given ``user``.
87 Removes permission ``perm`` for an instance ``obj`` and given ``user_or_group``.
5388
5489 Please note that we do NOT fetch object permission from database - we
5590 use ``Queryset.delete`` method for removing it. Main implication of this
5893 if getattr(obj, 'pk', None) is None:
5994 raise ObjectNotPersisted("Object %s needs to be persisted first"
6095 % obj)
61 filters = {
62 'permission__codename': perm,
63 'permission__content_type': ContentType.objects.get_for_model(obj),
64 'user': user,
65 }
96
97 filters = Q(**{self.user_or_group_field: user_or_group})
98
99 if isinstance(perm, Permission):
100 filters &= Q(permission=perm)
101 else:
102 filters &= Q(permission__codename=perm,
103 permission__content_type=get_content_type(obj))
104
66105 if self.is_generic():
67 filters['object_pk'] = obj.pk
106 filters &= Q(object_pk=obj.pk)
68107 else:
69 filters['content_object__pk'] = obj.pk
70 self.filter(**filters).delete()
108 filters &= Q(content_object__pk=obj.pk)
109 return self.filter(filters).delete()
110
111 def bulk_remove_perm(self, perm, user_or_group, queryset):
112 """
113 Removes permission ``perm`` for a ``queryset`` and given ``user_or_group``.
114
115 Please note that we do NOT fetch object permission from database - we
116 use ``Queryset.delete`` method for removing it. Main implication of this
117 is that ``post_delete`` signals would NOT be fired.
118 """
119 filters = Q(**{self.user_or_group_field: user_or_group})
120
121 if isinstance(perm, Permission):
122 filters &= Q(permission=perm)
123 else:
124 ctype = get_content_type(queryset.model)
125 filters &= Q(permission__codename=perm,
126 permission__content_type=ctype)
127
128 if self.is_generic():
129 filters &= Q(object_pk__in = [str(pk) for pk in queryset.values_list('pk', flat=True)])
130 else:
131 filters &= Q(content_object__in=queryset)
132
133 return self.filter(filters).delete()
134
135
136 class UserObjectPermissionManager(BaseObjectPermissionManager):
137 pass
71138
72139
73140 class GroupObjectPermissionManager(BaseObjectPermissionManager):
74
75 def assign_perm(self, perm, group, obj):
76 """
77 Assigns permission with given ``perm`` for an instance ``obj`` and
78 ``group``.
79 """
80 if getattr(obj, 'pk', None) is None:
81 raise ObjectNotPersisted("Object %s needs to be persisted first"
82 % obj)
83 ctype = ContentType.objects.get_for_model(obj)
84 permission = Permission.objects.get(content_type=ctype, codename=perm)
85
86 kwargs = {'permission': permission, 'group': group}
87 if self.is_generic():
88 kwargs['content_type'] = ctype
89 kwargs['object_pk'] = obj.pk
90 else:
91 kwargs['content_object'] = obj
92 obj_perm, created = self.get_or_create(**kwargs)
93 return obj_perm
94
95 def assign(self, perm, user, obj):
96 """ Depreciated function name left in for compatibility"""
97 warnings.warn("UserObjectPermissionManager method 'assign' is being renamed to 'assign_perm'. Update your code accordingly as old name will be depreciated in 2.0 version.", DeprecationWarning)
98 return self.assign_perm(perm, user, obj)
99
100 def remove_perm(self, perm, group, obj):
101 """
102 Removes permission ``perm`` for an instance ``obj`` and given ``group``.
103 """
104 if getattr(obj, 'pk', None) is None:
105 raise ObjectNotPersisted("Object %s needs to be persisted first"
106 % obj)
107 filters = {
108 'permission__codename': perm,
109 'permission__content_type': ContentType.objects.get_for_model(obj),
110 'group': group,
111 }
112 if self.is_generic():
113 filters['object_pk'] = obj.pk
114 else:
115 filters['content_object__pk'] = obj.pk
116
117 self.filter(**filters).delete()
141 pass
2020 serialize=False, auto_created=True, verbose_name='ID')),
2121 ('object_pk', models.CharField(
2222 max_length=255, verbose_name='object ID')),
23 ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
24 ('group', models.ForeignKey(to='auth.Group')),
25 ('permission', models.ForeignKey(to='auth.Permission')),
23 ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)),
24 ('group', models.ForeignKey(to='auth.Group', on_delete=models.CASCADE)),
25 ('permission', models.ForeignKey(to='auth.Permission', on_delete=models.CASCADE)),
2626 ],
2727 options={
2828 },
3535 serialize=False, auto_created=True, verbose_name='ID')),
3636 ('object_pk', models.CharField(
3737 max_length=255, verbose_name='object ID')),
38 ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
39 ('permission', models.ForeignKey(to='auth.Permission')),
40 ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
38 ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)),
39 ('permission', models.ForeignKey(to='auth.Permission', on_delete=models.CASCADE)),
40 ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
4141 ],
4242 options={
4343 },
00 from __future__ import unicode_literals
1
21 from collections import Iterable
32 from django.conf import settings
4 from django.contrib.auth.decorators import REDIRECT_FIELD_NAME
5 from django.contrib.auth.decorators import login_required
6 from django.core.exceptions import ImproperlyConfigured
7 from django.core.exceptions import PermissionDenied
3 from django.contrib.auth.decorators import login_required, REDIRECT_FIELD_NAME
4 from django.core.exceptions import ImproperlyConfigured, PermissionDenied
85 from guardian.compat import basestring
96 from guardian.models import UserObjectPermission
10 from guardian.utils import get_403_or_None
11 from guardian.utils import get_anonymous_user
7 from guardian.utils import get_40x_or_None, get_anonymous_user
8 from guardian.shortcuts import get_objects_for_user
129
1310
1411 class LoginRequiredMixin(object):
105102 *Default*: ``False``. Returns 403 error page instead of redirecting
106103 user.
107104
105 ``PermissionRequiredMixin.return_404``
106
107 *Default*: ``False``. Returns 404 error page instead of redirecting
108 user.
109
108110 ``PermissionRequiredMixin.raise_exception``
109111
110112 *Default*: ``False``
119121 proceed to check object level permissions.
120122
121123 ``PermissionRequiredMixin.permission_object``
122 *Default*: ``None``, object against which test the permission; if None fallback
124 *Default*: ``(not set)``, object against which test the permission; if not set fallback
123125 to ``self.get_permission_object()`` which return ``self.get_object()``
124126 or ``self.object`` by default.
125127
129131 permission_required = None
130132 redirect_field_name = REDIRECT_FIELD_NAME
131133 return_403 = False
134 return_404 = False
132135 raise_exception = False
133136 accept_global_perms = False
134 permission_object = None
135137
136138 def get_required_permissions(self, request=None):
137139 """
153155 return perms
154156
155157 def get_permission_object(self):
156 if self.permission_object:
158 if hasattr(self, 'permission_object'):
157159 return self.permission_object
158 return (hasattr(self, 'get_object') and self.get_object()
159 or getattr(self, 'object', None))
160 return (hasattr(self, 'get_object') and self.get_object() or
161 getattr(self, 'object', None))
160162
161163 def check_permissions(self, request):
162164 """
167169 """
168170 obj = self.get_permission_object()
169171
170 forbidden = get_403_or_None(request,
172 forbidden = get_40x_or_None(request,
171173 perms=self.get_required_permissions(
172174 request),
173175 obj=obj,
174176 login_url=self.login_url,
175177 redirect_field_name=self.redirect_field_name,
176178 return_403=self.return_403,
179 return_404=self.return_404,
177180 accept_global_perms=self.accept_global_perms
178181 )
179182 if forbidden:
215218
216219 def del_obj_perm(self, perm, obj):
217220 return UserObjectPermission.objects.remove_perm(perm, self, obj)
221
222
223 class PermissionListMixin(object):
224 """
225 A view mixin that filter object in queryset for the current logged by required permission.
226
227 **Example Usage**::
228
229 class SecureView(PermissionListMixin, ListView):
230 ...
231 permission_required = 'articles.view_article'
232 ...
233
234 or::
235
236 class SecureView(PermissionListMixin, ListView):
237 ...
238 permission_required = 'auth.change_user'
239 get_objects_for_user_extra_kwargs = {'use_groups': False}
240 ...
241
242 **Class Settings**
243
244 ``PermissionListMixin.permission_required``
245
246 *Default*: ``None``, must be set to either a string or list of strings
247 in format: *<app_label>.<permission_codename>*.
248
249 ``PermissionListMixin.get_objects_for_user_extra_kwargs``
250
251 *Default*: ``{}``, A extra params to pass for ```guardian.shorcuts.get_objects_for_user```
252
253 """
254 permission_required = None
255 get_objects_for_user_extra_kwargs = {}
256
257 def get_required_permissions(self, request=None):
258 """
259 Returns list of permissions in format *<app_label>.<codename>* that
260 should be checked against *request.user* and *object*. By default, it
261 returns list from ``permission_required`` attribute.
262
263 :param request: Original request.
264 """
265 if isinstance(self.permission_required, basestring):
266 perms = [self.permission_required]
267 elif isinstance(self.permission_required, Iterable):
268 perms = [p for p in self.permission_required]
269 else:
270 raise ImproperlyConfigured("'PermissionRequiredMixin' requires "
271 "'permission_required' attribute to be set to "
272 "'<app_label>.<permission codename>' but is set to '%s' instead"
273 % self.permission_required)
274 return perms
275
276 def get_get_objects_for_user_kwargs(self, queryset):
277 """
278 Returns dict of kwargs that should be pass to ```get_objects_for_user```.
279
280 :param request: Queryset to filter
281 """
282 return dict(user=self.request.user,
283 perms=self.get_required_permissions(self.request),
284 klass=queryset,
285 **self.get_objects_for_user_extra_kwargs)
286
287 def get_queryset(self, *args, **kwargs):
288 qs = super(PermissionListMixin, self).get_queryset(*args, **kwargs)
289 return get_objects_for_user(**self.get_get_objects_for_user_kwargs(qs))
00 from __future__ import unicode_literals
1
1 from django.contrib.auth.models import Group, Permission
2 from django.contrib.contenttypes.models import ContentType
3 from django.core.exceptions import ValidationError
24 from django.db import models
3 from django.core.exceptions import ValidationError
4 from django.contrib.auth.models import Group
5 from django.contrib.auth.models import Permission
6 from django.contrib.contenttypes.models import ContentType
5 from django.utils.translation import ugettext_lazy as _
6 from guardian.compat import unicode, user_model_label
7 from guardian.ctypes import get_content_type
8 from guardian.managers import GroupObjectPermissionManager, UserObjectPermissionManager
79
810 try:
911 from django.contrib.contenttypes.fields import GenericForeignKey
1012 except ImportError:
1113 from django.contrib.contenttypes.generic import GenericForeignKey
12
13 from django.utils.translation import ugettext_lazy as _
14
15 from guardian.compat import user_model_label
16 from guardian.compat import unicode
17 from guardian.managers import GroupObjectPermissionManager
18 from guardian.managers import UserObjectPermissionManager
1914
2015
2116 class BaseObjectPermission(models.Model):
2318 Abstract ObjectPermission class. Actual class should additionally define
2419 a ``content_object`` field and either ``user`` or ``group`` field.
2520 """
26 permission = models.ForeignKey(Permission)
21 permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
2722
2823 class Meta:
2924 abstract = True
3530 unicode(self.permission.codename))
3631
3732 def save(self, *args, **kwargs):
38 content_type = ContentType.objects.get_for_model(self.content_object)
33 content_type = get_content_type(self.content_object)
3934 if content_type != self.permission.content_type:
4035 raise ValidationError("Cannot persist permission not designed for "
4136 "this class (permission's type is %r and object's type is %r)"
4439
4540
4641 class BaseGenericObjectPermission(models.Model):
47 content_type = models.ForeignKey(ContentType)
42 content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
4843 object_pk = models.CharField(_('object ID'), max_length=255)
4944 content_object = GenericForeignKey(fk_field='object_pk')
5045
5651 """
5752 **Manager**: :manager:`UserObjectPermissionManager`
5853 """
59 user = models.ForeignKey(user_model_label)
54 user = models.ForeignKey(user_model_label, on_delete=models.CASCADE)
6055
6156 objects = UserObjectPermissionManager()
6257
7570 """
7671 **Manager**: :manager:`GroupObjectPermissionManager`
7772 """
78 group = models.ForeignKey(Group)
73 group = models.ForeignKey(Group, on_delete=models.CASCADE)
7974
8075 objects = GroupObjectPermissionManager()
8176
22 """
33 from __future__ import unicode_literals
44
5 from django.contrib.auth.models import Group
6 from django.contrib.auth.models import Permission
5 import warnings
6 from collections import defaultdict
7 from itertools import groupby
8
9 from django.apps import apps
10 from django.contrib.auth.models import Group, Permission
711 from django.contrib.contenttypes.models import ContentType
8 from django.db.models import Count, Q
9 from django.apps import apps
12 from django.db.models import Count, Q, QuerySet
1013 from django.shortcuts import _get_queryset
11 from itertools import groupby
12
13 from guardian.compat import basestring
14 from guardian.compat import get_user_model
14
15 from guardian.compat import basestring, get_user_model, is_anonymous
1516 from guardian.core import ObjectPermissionChecker
16 from guardian.exceptions import MixedContentTypeError
17 from guardian.exceptions import WrongAppError
18 from guardian.utils import get_anonymous_user
19 from guardian.utils import get_group_obj_perms_model
20 from guardian.utils import get_identity
21 from guardian.utils import get_user_obj_perms_model
22 import warnings
17 from guardian.ctypes import get_content_type
18 from guardian.exceptions import MixedContentTypeError, WrongAppError
19 from guardian.models import GroupObjectPermission
20 from guardian.utils import get_anonymous_user, get_group_obj_perms_model, get_identity, get_user_obj_perms_model
2321
2422
2523 def assign_perm(perm, user_or_group, obj=None):
2725 Assigns permission to user/group and object pair.
2826
2927 :param perm: proper permission for given ``obj``, as string (in format:
30 ``app_label.codename`` or ``codename``). If ``obj`` is not given, must
31 be in format ``app_label.codename``.
28 ``app_label.codename`` or ``codename``) or ``Permission`` instance.
29 If ``obj`` is not given, must be in format ``app_label.codename`` or
30 ``Permission`` instance.
3231
3332 :param user_or_group: instance of ``User``, ``AnonymousUser`` or ``Group``;
3433 passing any other object would raise
3534 ``guardian.exceptions.NotUserNorGroup`` exception
3635
37 :param obj: persisted Django's ``Model`` instance or ``None`` if assigning
38 global permission. Default is ``None``.
36 :param obj: persisted Django's ``Model`` instance or QuerySet of Django
37 ``Model`` instances or ``None`` if assigning global permission.
38 Default is ``None``.
3939
4040 We can assign permission for ``Model`` instance for specific user:
4141
7272 user, group = get_identity(user_or_group)
7373 # If obj is None we try to operate on global permissions
7474 if obj is None:
75 try:
76 app_label, codename = perm.split('.', 1)
77 except ValueError:
78 raise ValueError("For global permissions, first argument must be in"
79 " format: 'app_label.codename' (is %r)" % perm)
80 perm = Permission.objects.get(content_type__app_label=app_label,
81 codename=codename)
75 if not isinstance(perm, Permission):
76 try:
77 app_label, codename = perm.split('.', 1)
78 except ValueError:
79 raise ValueError("For global permissions, first argument must be in"
80 " format: 'app_label.codename' (is %r)" % perm)
81 perm = Permission.objects.get(content_type__app_label=app_label,
82 codename=codename)
83
8284 if user:
8385 user.user_permissions.add(perm)
8486 return perm
8587 if group:
8688 group.permissions.add(perm)
8789 return perm
88 perm = perm.split('.')[-1]
90
91 if not isinstance(perm, Permission):
92 perm = perm.split('.')[-1]
93
94 if isinstance(obj, QuerySet):
95 if user:
96 model = get_user_obj_perms_model(obj.model)
97 return model.objects.bulk_assign_perm(perm, user, obj)
98 if group:
99 model = get_group_obj_perms_model(obj.model)
100 return model.objects.bulk_assign_perm(perm, group, obj)
101
89102 if user:
90103 model = get_user_obj_perms_model(obj)
91104 return model.objects.assign_perm(perm, user, obj)
105
92106 if group:
93107 model = get_group_obj_perms_model(obj)
94108 return model.objects.assign_perm(perm, group, obj)
96110
97111 def assign(perm, user_or_group, obj=None):
98112 """ Depreciated function name left in for compatibility"""
99 warnings.warn("Shortcut function 'assign' is being renamed to 'assign_perm'. Update your code accordingly as old name will be depreciated in 2.0 version.", DeprecationWarning)
113 warnings.warn(
114 "Shortcut function 'assign' is being renamed to 'assign_perm'. Update your code accordingly as old name will be depreciated in 2.0 version.",
115 DeprecationWarning)
100116 return assign_perm(perm, user_or_group, obj)
101117
102118
112128 passing any other object would raise
113129 ``guardian.exceptions.NotUserNorGroup`` exception
114130
115 :param obj: persisted Django's ``Model`` instance or ``None`` if assigning
116 global permission. Default is ``None``.
131 :param obj: persisted Django's ``Model`` instance or QuerySet of Django
132 ``Model`` instances or ``None`` if assigning global permission.
133 Default is ``None``.
117134
118135 """
119136 user, group = get_identity(user_or_group)
131148 elif group:
132149 group.permissions.remove(perm)
133150 return
134 perm = perm.split('.')[-1]
151
152 if not isinstance(perm, Permission):
153 perm = perm.split('.')[-1]
154
155 if isinstance(obj, QuerySet):
156 if user:
157 model = get_user_obj_perms_model(obj.model)
158 return model.objects.bulk_remove_perm(perm, user, obj)
159 if group:
160 model = get_group_obj_perms_model(obj.model)
161 return model.objects.bulk_remove_perm(perm, group, obj)
162
135163 if user:
136164 model = get_user_obj_perms_model(obj)
137 model.objects.remove_perm(perm, user, obj)
165 return model.objects.remove_perm(perm, user, obj)
166
138167 if group:
139168 model = get_group_obj_perms_model(obj)
140 model.objects.remove_perm(perm, group, obj)
169 return model.objects.remove_perm(perm, group, obj)
141170
142171
143172 def get_perms(user_or_group, obj):
177206 model = apps.get_model(app_label, model_name)
178207 else:
179208 model = cls
180 ctype = ContentType.objects.get_for_model(model)
209 ctype = get_content_type(model)
181210 return Permission.objects.filter(content_type=ctype)
182211
183212
217246 {<User: joe>: [u'change_flatpage']}
218247
219248 """
220 ctype = ContentType.objects.get_for_model(obj)
249 ctype = get_content_type(obj)
221250 if not attach_perms:
222251 # It's much easier without attached perms so we do it first if that is
223252 # the case
289318 {<Group: admins>: [u'change_flatpage']}
290319
291320 """
292 ctype = ContentType.objects.get_for_model(obj)
321 ctype = get_content_type(obj)
322 group_model = get_group_obj_perms_model(obj)
323
293324 if not attach_perms:
294 # It's much easier without attached perms so we do it first if that is
295 # the case
296 group_model = get_group_obj_perms_model(obj)
325 # It's much easier without attached perms so we do it first if that is the case
297326 group_rel_name = group_model.group.field.related_query_name()
298327 if group_model.objects.is_generic():
299328 group_filters = {
302331 }
303332 else:
304333 group_filters = {'%s__content_object' % group_rel_name: obj}
305 groups = Group.objects.filter(**group_filters).distinct()
306 return groups
334 return Group.objects.filter(**group_filters).distinct()
307335 else:
308 # TODO: Do not hit db for each group!
309 groups = {}
310 for group in get_groups_with_perms(obj):
311 if group not in groups:
312 groups[group] = sorted(get_group_perms(group, obj))
313 return groups
336 group_perms_mapping = defaultdict(list)
337 groups_with_perms = get_groups_with_perms(obj)
338 qs = group_model.objects.filter(group__in=groups_with_perms).prefetch_related('group', 'permission')
339 if group_model is GroupObjectPermission:
340 qs = qs.filter(object_pk=obj.pk)
341 else:
342 qs = qs.filter(content_object_id=obj.pk)
343
344 for group_perm in qs:
345 group_perms_mapping[group_perm.group].append(group_perm.permission.codename)
346 return dict(group_perms_mapping)
314347
315348
316349 def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=False,
318351 """
319352 Returns queryset of objects for which a given ``user`` has *all*
320353 permissions present at ``perms``.
321
322 If ``perms`` is an empty list, then it returns objects for which
323 a given ``user`` has *any* object permission.
324354
325355 :param user: ``User`` or ``AnonymousUser`` instance for which objects would
326356 be returned.
447477 # Compute queryset and ctype if still missing
448478 if ctype is None and klass is not None:
449479 queryset = _get_queryset(klass)
450 ctype = ContentType.objects.get_for_model(queryset.model)
480 ctype = get_content_type(queryset.model)
451481 elif ctype is not None and klass is None:
452482 queryset = _get_queryset(ctype.model_class())
453483 elif klass is None:
469499 # Check if the user is anonymous. The
470500 # django.contrib.auth.models.AnonymousUser object doesn't work for queries
471501 # and it's nice to be able to pass in request.user blindly.
472 if user.is_anonymous():
502 if is_anonymous(user):
473503 user = get_anonymous_user()
474504
475505 global_perms = set()
529559 group_fields = generic_fields
530560 else:
531561 group_fields = direct_fields
532 if not any_perm and len(codenames) and not has_global_perms:
562 if not any_perm and len(codenames) > 1 and not has_global_perms:
533563 user_obj_perms = user_obj_perms_queryset.values_list(*user_fields)
534564 groups_obj_perms = groups_obj_perms_queryset.values_list(*group_fields)
535565 data = list(user_obj_perms) + list(groups_obj_perms)
552582
553583 values = user_obj_perms_queryset.values_list(user_fields[0], flat=True)
554584 if user_model.objects.is_generic():
555 values = list(values)
585 values = set(values)
556586 q = Q(pk__in=values)
557587 if use_groups:
558588 values = groups_obj_perms_queryset.values_list(group_fields[0], flat=True)
559589 if group_model.objects.is_generic():
560 values = list(values)
590 values = set(values)
561591 q |= Q(pk__in=values)
562592
563593 return queryset.filter(q)
650680 # Compute queryset and ctype if still missing
651681 if ctype is None and klass is not None:
652682 queryset = _get_queryset(klass)
653 ctype = ContentType.objects.get_for_model(queryset.model)
683 ctype = get_content_type(queryset.model)
654684 elif ctype is not None and klass is None:
655685 queryset = _get_queryset(ctype.model_class())
656686 elif klass is None:
1010 {% endif %}
1111 {% if field.help_text %}<p class="grp-help">{{ field.help_text|safe }}</p>{% endif %}
1212 {{ field.errors }}
13 {% if field.errors %}
14 <ul class="errorlist">
15 {% for error in field.errors %}
16 <li>{{ error }}</li>
17 {% endfor %}
18 </ul>
19 {% endif %}
2013 </div>
2114 </div>
2215 </div>⏎
11 {% load i18n %}
22 {% load guardian_tags %}
33 {% load admin_static %}
4 {% load admin_urls %}
45
5 {% block breadcrumbs %}{% if not is_popup %}
6 {% block breadcrumbs %}
67 <ul>
7 <li><a href="../../../../">{% trans "Home" %}</a></li>
8 <li><a href="../../../">{% trans app_label|capfirst|escape %}</a></li>
9 <li>{% if has_change_permission %}<a href="../../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}</li>
10 <li>{% if has_change_permission %}<a href="../">{{ original|truncatewords:"18" }}</a>{% else %}{{ original|truncatewords:"18" }}{% endif %}</li>
11 <li>{% trans "Object permissions" %}</li>
8 <li><a href="{% url 'admin:index' %}">{% trans 'Home' %}</a></li>
9 <li><a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a></li>
10 {% if has_change_permission %}
11 <li>
12 {% url opts|admin_urlname:'changelist' as changelist_url %}
13 <a href="{% add_preserved_filters changelist_url %}">{{ opts.verbose_name_plural|capfirst }}</a>
14 </li>
15 <li>
16 {% url opts|admin_urlname:'change' object.pk|admin_urlquote as object_url %}
17 <a href="{% add_preserved_filters object_url %}">{{ object|truncatewords:"18" }}</a>
18 </li>
19 {% else %}
20 <li>{{ opts.verbose_name_plural|capfirst }}</li>
21 <li>{{ original|truncatewords:"18" }}</li>
22 {% endif %}
23 <li>{% trans 'Object permissions' %}</li>
1224 </ul>
13 {% endif %}{% endblock %}
25 {% endblock %}
1426
1527 {% block content %}
1628 <form action="." method="post">
135147 </fieldset>
136148 </div>
137149 </form>
138 {% endblock %}
139
140
150 {% endblock %}⏎
00 {% extends "admin/change_form.html" %}
11 {% load i18n %}
2 {% load admin_urls %}
23
34 {% block extrahead %}{{ block.super }}
45 {{ form.media }}
67
78 {% block breadcrumbs %}{% if not is_popup %}
89 <ul>
9 <li><a href="../../../../../../">{% trans "Home" %}</a></li>
10 <li><a href="../../../../../">{% trans app_label|capfirst|escape %}</a></li>
11 <li>{% if has_change_permission %}<a href="../../../../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}</li>
12 <li>{% if has_change_permission %}<a href="../../../">{{ original|truncatewords:"18" }}</a>{% else %}{{ original|truncatewords:"18" }}{% endif %}</li>
13 <li>{% if has_change_permission %}<a href="../../">{% trans "Object permissions" %}</a>{% else %}{% trans "Object permissions" %}{% endif %}</li>
10 <li><a href="{% url 'admin:index' %}">{% trans 'Home' %}</a></li>
11 <li><a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a></li>
12 {% if has_change_permission %}
13 <li>
14 {% url opts|admin_urlname:'changelist' as changelist_url %}
15 <a href="{% add_preserved_filters changelist_url %}">{{ opts.verbose_name_plural|capfirst }}</a>
16 </li>
17 <li>
18 {% url opts|admin_urlname:'change' object.pk|admin_urlquote as object_url %}
19 <a href="{% add_preserved_filters object_url %}">{{ object|truncatewords:"18" }}</a>
20 </li>
21 <li>
22 {% url opts|admin_urlname:'permissions' object.pk|admin_urlquote as permissions_url %}
23 <a href="{% add_preserved_filters permissions_url %}">{% trans "Object permissions" %}</a>
24 </li>
25 {% else %}
26 <li>{{ opts.verbose_name_plural|capfirst }}</li>
27 <li>{{ original|truncatewords:"18" }}</li>
28 <li>{% trans "Object permissions" %}</li>
29 {% endif %}
1430 <li>{% trans "Manage group" %}: {{ group_obj|truncatewords:"18" }}</li>
1531 </ul>
1632 {% endif %}{% endblock %}
17
1833
1934 {% block content %}
2035 <form action="." method="post">
4863 </fieldset>
4964 </div>
5065 </form>
51 {% endblock %}
66 {% endblock %}⏎
00 {% extends "admin/change_form.html" %}
11 {% load i18n %}
2 {% load admin_urls %}
23
34 {% block extrahead %}{{ block.super }}
45 {{ form.media }}
67
78 {% block breadcrumbs %}{% if not is_popup %}
89 <ul>
9 <li><a href="../../../../../../">{% trans "Home" %}</a></li>
10 <li><a href="../../../../../">{% trans app_label|capfirst|escape %}</a></li>
11 <li>{% if has_change_permission %}<a href="../../../../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}</li>
12 <li>{% if has_change_permission %}<a href="../../../">{{ original|truncatewords:"18" }}</a>{% else %}{{ original|truncatewords:"18" }}{% endif %}</li>
13 <li>{% if has_change_permission %}<a href="../../">{% trans "Object permissions" %}</a>{% else %}{% trans "Object permissions" %}{% endif %}</li>
10 <li><a href="{% url 'admin:index' %}">{% trans 'Home' %}</a></li>
11 <li><a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a></li>
12 {% if has_change_permission %}
13 <li>
14 {% url opts|admin_urlname:'changelist' as changelist_url %}
15 <a href="{% add_preserved_filters changelist_url %}">{{ opts.verbose_name_plural|capfirst }}</a>
16 </li>
17 <li>
18 {% url opts|admin_urlname:'change' object.pk|admin_urlquote as object_url %}
19 <a href="{% add_preserved_filters object_url %}">{{ object|truncatewords:"18" }}</a>
20 </li>
21 <li>
22 {% url opts|admin_urlname:'permissions' object.pk|admin_urlquote as permissions_url %}
23 <a href="{% add_preserved_filters permissions_url %}">{% trans "Object permissions" %}</a>
24 </li>
25 {% else %}
26 <li>{{ opts.verbose_name_plural|capfirst }}</li>
27 <li>{{ original|truncatewords:"18" }}</li>
28 <li>{% trans "Object permissions" %}</li>
29 {% endif %}
1430 <li>{% trans "Manage user" %}: {{ user_obj|truncatewords:"18" }}</li>
1531 </ul>
1632 {% endif %}{% endblock %}
17
1833
1934 {% block content %}
2035 <form action="." method="post">
4863 </fieldset>
4964 </div>
5065 </form>
51 {% endblock %}
66 {% endblock %}⏎
55
66 """
77 from __future__ import unicode_literals
8
89 from django import template
9 from django.contrib.auth.models import Group, AnonymousUser
10 from django.contrib.auth.models import AnonymousUser, Group
1011
1112 from guardian.compat import get_user_model
13 from guardian.core import ObjectPermissionChecker
1214 from guardian.exceptions import NotUserNorGroup
13 from guardian.core import ObjectPermissionChecker
1415
1516 register = template.Library()
1617
0 # -*- coding: utf-8 -*-
1 # Generated by Django 1.9.9 on 2016-09-18 17:52
02 from __future__ import unicode_literals
13
24 import datetime
3
45 from django.conf import settings
6 import django.contrib.auth.models
57 import django.core.validators
6 from django.db import models, migrations
8 from django.db import migrations, models
9 import django.db.models.deletion
710 import django.utils.timezone
811 import guardian.mixins
912
1013
1114 class Migration(migrations.Migration):
1215
16 initial = True
17
1318 dependencies = [
1419 ('auth', '0001_initial'),
15 migrations.swappable_dependency(settings.AUTH_USER_MODEL),
1620 ]
1721
1822 operations = [
2024 name='CustomUser',
2125 fields=[
2226 ('password', models.CharField(max_length=128, verbose_name='password')),
23 ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login', null=True)),
27 ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
2428 ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
25 ('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.@-]+$', 'Enter a valid username.', 'invalid')])),
26 ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
27 ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
28 ('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)),
29 ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username')),
30 ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
31 ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')),
32 ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
2933 ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
3034 ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
3135 ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
32 ('custom_id', models.AutoField(serialize=False, primary_key=True)),
33 ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.', verbose_name='groups')),
34 ('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')),
36 ('custom_id', models.AutoField(primary_key=True, serialize=False)),
37 ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
38 ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
3539 ],
3640 options={
3741 'abstract': False,
3943 'verbose_name_plural': 'users',
4044 },
4145 bases=(models.Model, guardian.mixins.GuardianUserMixin),
46 managers=[
47 ('objects', django.contrib.auth.models.UserManager()),
48 ],
49 ),
50 migrations.CreateModel(
51 name='CustomUsernameUser',
52 fields=[
53 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
54 ('password', models.CharField(max_length=128, verbose_name='password')),
55 ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
56 ('email', models.EmailField(max_length=100, unique=True)),
57 ],
58 options={
59 'abstract': False,
60 },
61 bases=(models.Model, guardian.mixins.GuardianUserMixin),
4262 ),
4363 migrations.CreateModel(
4464 name='Mixed',
4565 fields=[
46 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
47 ('name', models.CharField(unique=True, max_length=128)),
66 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
67 ('name', models.CharField(max_length=128, unique=True)),
4868 ],
49 options={
50 },
51 bases=(models.Model,),
5269 ),
5370 migrations.CreateModel(
5471 name='MixedGroupObjectPermission',
5572 fields=[
56 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
57 ('content_object', models.ForeignKey(to='testapp.Mixed')),
58 ('group', models.ForeignKey(to='auth.Group')),
59 ('permission', models.ForeignKey(to='auth.Permission')),
73 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
74 ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.Mixed')),
75 ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
76 ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')),
6077 ],
6178 options={
6279 'abstract': False,
6380 },
64 bases=(models.Model,),
81 ),
82 migrations.CreateModel(
83 name='NonIntPKModel',
84 fields=[
85 ('char_pk', models.CharField(max_length=128, primary_key=True, serialize=False)),
86 ],
87 ),
88 migrations.CreateModel(
89 name='Post',
90 fields=[
91 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
92 ('title', models.CharField(max_length=64, verbose_name='title')),
93 ],
6594 ),
6695 migrations.CreateModel(
6796 name='Project',
6897 fields=[
69 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
70 ('name', models.CharField(unique=True, max_length=128)),
98 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
99 ('name', models.CharField(max_length=128, unique=True)),
71100 ('created_at', models.DateTimeField(default=datetime.datetime.now)),
72101 ],
73102 options={
74103 'get_latest_by': 'created_at',
75104 },
76 bases=(models.Model,),
77105 ),
78106 migrations.CreateModel(
79107 name='ProjectGroupObjectPermission',
80108 fields=[
81 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
82 ('content_object', models.ForeignKey(to='testapp.Project')),
83 ('group', models.ForeignKey(to='auth.Group')),
84 ('permission', models.ForeignKey(to='auth.Permission')),
109 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
110 ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.Project')),
111 ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
112 ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')),
85113 ],
86114 options={
87115 'abstract': False,
88116 },
89 bases=(models.Model,),
90117 ),
91118 migrations.CreateModel(
92119 name='ProjectUserObjectPermission',
93120 fields=[
94 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
95 ('content_object', models.ForeignKey(to='testapp.Project')),
96 ('permission', models.ForeignKey(to='auth.Permission')),
97 ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
121 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
122 ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.Project')),
123 ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')),
124 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
98125 ],
99126 options={
100127 'abstract': False,
101128 },
102 bases=(models.Model,),
129 ),
130 migrations.CreateModel(
131 name='ReverseMixed',
132 fields=[
133 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
134 ('name', models.CharField(max_length=128, unique=True)),
135 ],
136 ),
137 migrations.CreateModel(
138 name='ReverseMixedUserObjectPermission',
139 fields=[
140 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
141 ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.ReverseMixed')),
142 ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')),
143 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
144 ],
145 options={
146 'abstract': False,
147 },
148 ),
149 migrations.AlterUniqueTogether(
150 name='reversemixeduserobjectpermission',
151 unique_together=set([('user', 'permission', 'content_object')]),
103152 ),
104153 migrations.AlterUniqueTogether(
105154 name='projectuserobjectpermission',
113162 name='mixedgroupobjectpermission',
114163 unique_together=set([('group', 'permission', 'content_object')]),
115164 ),
116 ]⏎
165 ]
0 # -*- coding: utf-8 -*-
1 # Generated by Django 1.9.9 on 2016-09-18 17:52
02 from __future__ import unicode_literals
13
2 from django.db import models, migrations
4 from django.db import migrations, models
5 import django.db.models.deletion
36
47
58 class Migration(migrations.Migration):
1417 migrations.CreateModel(
1518 name='LogEntryWithGroup',
1619 fields=[
17 ('logentry_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='admin.LogEntry')),
18 ('group', models.ForeignKey(blank=True, to='auth.Group', null=True)),
20 ('logentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='admin.LogEntry')),
21 ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
1922 ],
20 options={
21 },
2223 bases=('admin.logentry',),
2324 ),
24 ]⏎
25 ]
+0
-30
guardian/testapp/migrations/0003_auto_20141124_0729.py less more
0 # -*- coding: utf-8 -*-
1 from __future__ import unicode_literals
2
3 from django.db import models, migrations
4 import django.core.validators
5
6
7 class Migration(migrations.Migration):
8
9 dependencies = [
10 ('testapp', '0002_logentrywithgroup'),
11 ]
12
13 operations = [
14 migrations.CreateModel(
15 name='NonIntPKModel',
16 fields=[
17 ('char_pk', models.CharField(max_length=128, serialize=False, primary_key=True)),
18 ],
19 options={
20 },
21 bases=(models.Model,),
22 ),
23 migrations.AlterField(
24 model_name='customuser',
25 name='username',
26 field=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.@+-]+$', 'Enter a valid username.', 'invalid')]),
27 preserve_default=True,
28 ),
29 ]
+0
-36
guardian/testapp/migrations/0004_auto_20151112_2209.py less more
0 # -*- coding: utf-8 -*-
1 from __future__ import unicode_literals
2
3 from django.db import migrations, models
4 import django.core.validators
5 import django.contrib.auth.models
6
7
8 class Migration(migrations.Migration):
9
10 dependencies = [
11 ('testapp', '0003_auto_20141124_0729'),
12 ]
13
14 operations = [
15 migrations.AlterField(
16 model_name='customuser',
17 name='email',
18 field=models.EmailField(max_length=254, verbose_name='email address', blank=True),
19 ),
20 migrations.AlterField(
21 model_name='customuser',
22 name='groups',
23 field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'),
24 ),
25 migrations.AlterField(
26 model_name='customuser',
27 name='last_login',
28 field=models.DateTimeField(null=True, verbose_name='last login', blank=True),
29 ),
30 migrations.AlterField(
31 model_name='customuser',
32 name='username',
33 field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username'),
34 ),
35 ]
+0
-22
guardian/testapp/migrations/0005_auto_20151217_2344.py less more
0 # -*- coding: utf-8 -*-
1 # Generated by Django 1.9 on 2015-12-17 23:44
2 from __future__ import unicode_literals
3
4 from django.db import migrations, models
5
6
7 class Migration(migrations.Migration):
8
9 dependencies = [
10 ('testapp', '0004_auto_20151112_2209'),
11 ]
12
13 operations = [
14 migrations.CreateModel(
15 name='Post',
16 fields=[
17 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 ('title', models.CharField(max_length=64, verbose_name='title')),
19 ],
20 ),
21 ]
+0
-30
guardian/testapp/migrations/0006_auto_20160221_1054.py less more
0 # -*- coding: utf-8 -*-
1 from __future__ import unicode_literals
2
3 from django.db import models, migrations
4 import django.utils.timezone
5 import guardian.mixins
6 import django.core.validators
7
8
9 class Migration(migrations.Migration):
10
11 dependencies = [
12 ('testapp', '0005_auto_20151217_2344'),
13 ]
14
15 operations = [
16 migrations.CreateModel(
17 name='CustomUsernameUser',
18 fields=[
19 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
20 ('password', models.CharField(max_length=128, verbose_name='password')),
21 ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')),
22 ('email', models.EmailField(unique=True, max_length=100)),
23 ],
24 options={
25 'abstract': False,
26 },
27 bases=(models.Model, guardian.mixins.GuardianUserMixin),
28 )
29 ]
+0
-46
guardian/testapp/migrations/0007_auto_20160309_0245.py less more
0 # -*- coding: utf-8 -*-
1 # Generated by Django 1.9.1 on 2016-03-09 02:45
2 from __future__ import unicode_literals
3
4 from django.conf import settings
5 import django.contrib.auth.models
6 import django.core.validators
7 from django.db import migrations, models
8 import django.db.models.deletion
9
10
11 class Migration(migrations.Migration):
12
13 dependencies = [
14 ('testapp', '0006_auto_20160221_1054'),
15 ]
16
17 operations = [
18 migrations.CreateModel(
19 name='ReverseMixed',
20 fields=[
21 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 ('name', models.CharField(max_length=128, unique=True)),
23 ],
24 ),
25 migrations.CreateModel(
26 name='ReverseMixedUserObjectPermission',
27 fields=[
28 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
29 ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.ReverseMixed')),
30 ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')),
31 ],
32 options={
33 'abstract': False,
34 },
35 ),
36 migrations.AddField(
37 model_name='reversemixeduserobjectpermission',
38 name='user',
39 field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
40 ),
41 migrations.AlterUniqueTogether(
42 name='reversemixeduserobjectpermission',
43 unique_together=set([('user', 'permission', 'content_object')]),
44 ),
45 ]
33 from django.db import models
44 from django.contrib.admin.models import LogEntry
55 from django.contrib.auth.models import AbstractUser, AbstractBaseUser
6 from django.utils.encoding import python_2_unicode_compatible
67
78 from guardian.mixins import GuardianUserMixin
89 from guardian.models import UserObjectPermissionBase
910 from guardian.models import GroupObjectPermissionBase
1011
1112
13 @python_2_unicode_compatible
1214 class Post(models.Model):
1315 title = models.CharField('title', max_length=64)
16
17 def __str__(self):
18 return self.title
1419
1520
1621 class DynamicAccessor(object):
2328
2429
2530 class ProjectUserObjectPermission(UserObjectPermissionBase):
26 content_object = models.ForeignKey('Project')
31 content_object = models.ForeignKey('Project', on_delete=models.CASCADE)
2732
2833
2934 class ProjectGroupObjectPermission(GroupObjectPermissionBase):
30 content_object = models.ForeignKey('Project')
35 content_object = models.ForeignKey('Project', on_delete=models.CASCADE)
3136
3237
3338 class Project(models.Model):
3742 class Meta:
3843 get_latest_by = 'created_at'
3944
40 def __unicode__(self):
45 def __str__(self):
4146 return self.name
4247
4348
4550
4651
4752 class MixedGroupObjectPermission(GroupObjectPermissionBase):
48 content_object = models.ForeignKey('Mixed')
53 content_object = models.ForeignKey('Mixed', on_delete=models.CASCADE)
4954
5055
56 @python_2_unicode_compatible
5157 class Mixed(models.Model):
5258 """
5359 Model for tests obj perms checks with generic user object permissions model
5561 """
5662 name = models.CharField(max_length=128, unique=True)
5763
58 def __unicode__(self):
64 def __str__(self):
5965 return self.name
6066
6167
6268 class ReverseMixedUserObjectPermission(UserObjectPermissionBase):
63 content_object = models.ForeignKey('ReverseMixed')
69 content_object = models.ForeignKey('ReverseMixed', on_delete=models.CASCADE)
6470
6571
72 @python_2_unicode_compatible
6673 class ReverseMixed(models.Model):
6774 """
6875 Model for tests obj perms checks with generic group object permissions model
7077 """
7178 name = models.CharField(max_length=128, unique=True)
7279
73 def __unicode__(self):
80 def __str__(self):
7481 return self.name
7582
7683
7784 class LogEntryWithGroup(LogEntry):
78 group = models.ForeignKey('auth.Group', null=True, blank=True)
85 group = models.ForeignKey('auth.Group', null=True, blank=True, on_delete=models.CASCADE)
86
87 objects = models.Manager()
7988
8089
8190 class NonIntPKModel(models.Model):
0 <ul>
1 {% for object in object_list %}
2 <li>{{object}}</li>
3 {% endfor %}
4 </ul>
44 from django.conf import settings
55 from django.contrib import admin
66 from django.contrib.contenttypes.models import ContentType
7 from django.core.urlresolvers import reverse
87 from django.http import HttpRequest
98 from django.test import TestCase
109 from django.test.client import Client
1110
1211 from guardian.admin import GuardedModelAdmin
1312 from guardian.compat import get_user_model, get_model_name
13 from guardian.compat import reverse
1414 from guardian.compat import str
1515 from guardian.shortcuts import get_perms
1616 from guardian.shortcuts import get_perms_for_model
319319 def test_obj_perms_manage_user_form_attr(self):
320320 attrs = {'obj_perms_manage_user_form': forms.Form}
321321 gma = self._get_gma(attrs=attrs)
322 self.assertTrue(gma.get_obj_perms_manage_user_form(), forms.Form)
322 self.assertTrue(issubclass(gma.get_obj_perms_manage_user_form(None), forms.Form))
323
324 def test_obj_perms_user_select_form_attr(self):
325 attrs = {'obj_perms_user_select_form': forms.Form}
326 gma = self._get_gma(attrs=attrs)
327 self.assertTrue(issubclass(gma.get_obj_perms_user_select_form(None), forms.Form))
323328
324329 def test_obj_perms_manage_group_template_attr(self):
325330 attrs = {'obj_perms_manage_group_template': 'foobar.html'}
330335 def test_obj_perms_manage_group_form_attr(self):
331336 attrs = {'obj_perms_manage_group_form': forms.Form}
332337 gma = self._get_gma(attrs=attrs)
333 self.assertTrue(gma.get_obj_perms_manage_group_form(), forms.Form)
338 self.assertTrue(issubclass(gma.get_obj_perms_manage_group_form(None), forms.Form))
339
340 def test_obj_perms_group_select_form_attr(self):
341 attrs = {'obj_perms_group_select_form': forms.Form}
342 gma = self._get_gma(attrs=attrs)
343 self.assertTrue(issubclass(gma.get_obj_perms_group_select_form(None), forms.Form))
334344
335345 def test_user_can_acces_owned_objects_only(self):
336346 attrs = {
22 from django.test import TestCase
33 import mock
44 from guardian.conf import settings as guardian_settings
5 from guardian.ctypes import get_content_type
56
67
78 class TestConfiguration(TestCase):
1213 with mock.patch('guardian.conf.settings.RAISE_403', True):
1314 self.assertRaises(ImproperlyConfigured,
1415 guardian_settings.check_configuration)
16
17 def test_get_content_type(self):
18 with mock.patch('guardian.conf.settings.GET_CONTENT_TYPE', 'guardian.testapp.tests.test_conf.get_test_content_type'):
19 self.assertEqual(get_content_type(None), 'x')
20
21
22 def get_test_content_type(obj):
23 """ Used in TestConfiguration.test_get_content_type()."""
24 return 'x'
4141 self.user.groups.add(self.group)
4242 self.ctype = ContentType.objects.create(
4343 model='bar', app_label='fake-for-guardian-tests')
44 self.ctype_qset = ContentType.objects.filter(model='bar',
45 app_label='fake-for-guardian-tests')
4446 self.anonymous_user = User.objects.get(
4547 username=guardian_settings.ANONYMOUS_USER_NAME)
48
49 def get_permission(self, codename, app_label=None):
50 qs = Permission.objects
51 if app_label:
52 qs = qs.filter(content_type__app_label=app_label)
53 return Permission.objects.get(codename=codename)
4654
4755
4856 class ObjectPermissionCheckerTest(ObjectPermissionTestCase):
189197 from django.db import connection
190198
191199 ContentType.objects.clear_cache()
192 new_group = Group.objects.create(name='new-group')
200 group1 = Group.objects.create(name='group1')
201 group2 = Group.objects.create(name='group2')
193202 user = User.objects.create(username='active_user', is_active=True)
194203 assign_perm("change_group", user, self.group)
195 assign_perm("change_group", user, new_group)
204 assign_perm("change_group", user, group1)
196205 checker = ObjectPermissionChecker(user)
197206
198207 # Prefetch permissions
199 self.assertTrue(checker.prefetch_perms([self.group, new_group]))
200 query_count = len(connection.queries)
201
202 # Checking cache is filled
203 self.assertEqual(len(checker._obj_perms_cache), 2)
208 prefetched_objects = [self.group, group1, group2]
209 self.assertTrue(checker.prefetch_perms(prefetched_objects))
210 query_count = len(connection.queries)
211
212 # Checking cache is filled
213 self.assertEqual(
214 len(checker._obj_perms_cache),
215 len(prefetched_objects)
216 )
204217
205218 # Checking shouldn't spawn any queries
206219 checker.has_perm("change_group", self.group)
212225 self.assertEqual(len(connection.queries), query_count)
213226
214227 # Checking for same model but other instance shouldn't spawn any queries
215 checker.has_perm("change_group", new_group)
216 self.assertEqual(len(connection.queries), query_count)
217
228 checker.has_perm("change_group", group1)
229 self.assertEqual(len(connection.queries), query_count)
230
231 # Checking for same model but other instance shouldn't spawn any queries
232 # Even though User doesn't have perms on Group2, we still should
233 # not hit DB
234 self.assertFalse(checker.has_perm("change_group", group2))
235 self.assertEqual(len(connection.queries), query_count)
218236 finally:
219237 settings.DEBUG = False
220238
224242 from django.db import connection
225243
226244 ContentType.objects.clear_cache()
227 new_group = Group.objects.create(name='new-group')
245 group1 = Group.objects.create(name='group1')
228246 user = User.objects.create(username='active_superuser',
229247 is_superuser=True, is_active=True)
230248 assign_perm("change_group", user, self.group)
231249 checker = ObjectPermissionChecker(user)
232250
233251 # Prefetch permissions
234 self.assertTrue(checker.prefetch_perms([self.group, new_group]))
235 query_count = len(connection.queries)
236
237 # Checking cache is filled
238 self.assertEqual(len(checker._obj_perms_cache), 2)
252 prefetched_objects = [self.group, group1]
253 self.assertTrue(checker.prefetch_perms(prefetched_objects))
254 query_count = len(connection.queries)
255
256 # Checking cache is filled
257 self.assertEqual(
258 len(checker._obj_perms_cache),
259 len(prefetched_objects)
260 )
239261
240262 # Checking shouldn't spawn any queries
241263 checker.has_perm("change_group", self.group)
247269 self.assertEqual(len(connection.queries), query_count)
248270
249271 # Checking for same model but other instance shouldn't spawn any queries
250 checker.has_perm("change_group", new_group)
251 self.assertEqual(len(connection.queries), query_count)
252
272 checker.has_perm("change_group", group1)
273 self.assertEqual(len(connection.queries), query_count)
253274 finally:
254275 settings.DEBUG = False
255276
259280 from django.db import connection
260281
261282 ContentType.objects.clear_cache()
262 new_group = Group.objects.create(name='new-group')
263 assign_perm("change_group", new_group, self.group)
264 assign_perm("change_group", new_group, new_group)
265 checker = ObjectPermissionChecker(new_group)
266
267 # Prefetch permissions
268 self.assertTrue(checker.prefetch_perms([self.group, new_group]))
269 query_count = len(connection.queries)
270
271 # Checking cache is filled
272 self.assertEqual(len(checker._obj_perms_cache), 2)
283 group1 = Group.objects.create(name='group1')
284 group2 = Group.objects.create(name='group2')
285 assign_perm("change_group", group1, self.group)
286 assign_perm("change_group", group1, group1)
287 checker = ObjectPermissionChecker(group1)
288
289 # Prefetch permissions
290 prefetched_objects = [self.group, group1, group2]
291 self.assertTrue(checker.prefetch_perms(prefetched_objects))
292
293 query_count = len(connection.queries)
294
295 # Checking cache is filled
296 self.assertEqual(
297 len(checker._obj_perms_cache),
298 len(prefetched_objects)
299 )
273300
274301 # Checking shouldn't spawn any queries
275302 checker.has_perm("change_group", self.group)
281308 self.assertEqual(len(connection.queries), query_count)
282309
283310 # Checking for same model but other instance shouldn't spawn any queries
284 checker.has_perm("change_group", new_group)
285 self.assertEqual(len(connection.queries), query_count)
286
311 checker.has_perm("change_group", group1)
312 self.assertEqual(len(connection.queries), query_count)
313
314 # Checking for same model but other instance shouldn't spawn any queries
315 # Even though User doesn't have perms on Group2, we still should
316 # not hit DB
317 self.assertFalse(checker.has_perm("change_group", group2))
318 self.assertEqual(len(connection.queries), query_count)
287319 finally:
288320 settings.DEBUG = False
289321
296328 user = User.objects.create(username='active_user', is_active=True)
297329 projects = \
298330 [Project.objects.create(name='Project%s' % i)
299 for i in range(2)]
300 for project in projects:
301 assign_perm("change_project", user, project)
331 for i in range(3)]
332 assign_perm("change_project", user, projects[0])
333 assign_perm("change_project", user, projects[1])
334
302335 checker = ObjectPermissionChecker(user)
303336
304337 # Prefetch permissions
306339 query_count = len(connection.queries)
307340
308341 # Checking cache is filled
309 self.assertEqual(len(checker._obj_perms_cache), 2)
342 self.assertEqual(len(checker._obj_perms_cache), len(projects))
310343
311344 # Checking shouldn't spawn any queries
312345 checker.has_perm("change_project", projects[0])
322355 checker.has_perm("change_project", projects[1])
323356 self.assertEqual(len(connection.queries), query_count)
324357
358 # Checking for same model but other instance shouldn't spawn any queries
359 # Even though User doesn't have perms on projects[2], we still
360 # should not hit DB
361 self.assertFalse(checker.has_perm("change_project", projects[2]))
362 self.assertEqual(len(connection.queries), query_count)
325363 finally:
326364 settings.DEBUG = False
327365
336374 projects = \
337375 [Project.objects.create(name='Project%s' % i)
338376 for i in range(2)]
339 for project in projects:
340 assign_perm("change_project", user, project)
377 assign_perm("change_project", user, projects[0])
378
341379 checker = ObjectPermissionChecker(user)
342380
343381 # Prefetch permissions
345383 query_count = len(connection.queries)
346384
347385 # Checking cache is filled
348 self.assertEqual(len(checker._obj_perms_cache), 2)
386 self.assertEqual(len(checker._obj_perms_cache), len(projects))
349387
350388 # Checking shouldn't spawn any queries
351389 checker.has_perm("change_project", projects[0])
360398 # queries
361399 checker.has_perm("change_project", projects[1])
362400 self.assertEqual(len(connection.queries), query_count)
363
364401 finally:
365402 settings.DEBUG = False
366403
370407 from django.db import connection
371408
372409 ContentType.objects.clear_cache()
373 new_group = Group.objects.create(name='new-group')
410 group = Group.objects.create(name='new-group')
374411 projects = \
375412 [Project.objects.create(name='Project%s' % i)
376 for i in range(2)]
377 for project in projects:
378 assign_perm("change_project", new_group, project)
379 checker = ObjectPermissionChecker(new_group)
413 for i in range(3)]
414 assign_perm("change_project", group, projects[0])
415 assign_perm("change_project", group, projects[1])
416
417 checker = ObjectPermissionChecker(group)
380418
381419 # Prefetch permissions
382420 self.assertTrue(checker.prefetch_perms(projects))
383421 query_count = len(connection.queries)
384422
385423 # Checking cache is filled
386 self.assertEqual(len(checker._obj_perms_cache), 2)
424 self.assertEqual(len(checker._obj_perms_cache), len(projects))
387425
388426 # Checking shouldn't spawn any queries
389427 checker.has_perm("change_project", projects[0])
399437 checker.has_perm("change_project", projects[1])
400438 self.assertEqual(len(connection.queries), query_count)
401439
402 finally:
403 settings.DEBUG = False
440 # Checking for same model but other instance shouldn't spawn any queries
441 # Even though User doesn't have perms on projects[2], we still
442 # should not hit DB
443 self.assertFalse(checker.has_perm("change_project", projects[2]))
444 self.assertEqual(len(connection.queries), query_count)
445 finally:
446 settings.DEBUG = False
77
88 class CustomPKModelTest(TestCase):
99 """
10 Tests agains custom model with primary key other than *standard*
10 Tests against custom model with primary key other than *standard*
1111 ``id`` integer field.
1212 """
1313
00 from __future__ import unicode_literals
11 from django.conf import settings, global_settings
22 from django.contrib.auth.models import Group, AnonymousUser
3 from django.core.exceptions import PermissionDenied
3 from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
44 from django.db.models.base import ModelBase
55 from django.http import HttpRequest
66 from django.http import HttpResponse
77 from django.http import HttpResponseForbidden
8 from django.http import HttpResponseNotFound
89 from django.http import HttpResponseRedirect
910 from django.shortcuts import get_object_or_404
1011 from django.template import TemplateDoesNotExist
1415 from guardian.compat import get_user_model_path
1516 from guardian.compat import get_user_permission_full_codename
1617 import mock
17 from guardian.decorators import permission_required, permission_required_or_403
18 from guardian.decorators import permission_required, permission_required_or_403, permission_required_or_404
1819 from guardian.exceptions import GuardianError
1920 from guardian.exceptions import WrongAppError
2021 from guardian.shortcuts import assign_perm
6970 self.assertEqual(response.content, b'')
7071 self.assertTrue(isinstance(response, HttpResponseForbidden))
7172
73 def test_RENDER_404_is_false(self):
74 request = self._get_request(self.anon)
75
76 @permission_required_or_404('not_installed_app.change_user')
77 def dummy_view(request):
78 return HttpResponse('dummy_view')
79
80 with mock.patch('guardian.conf.settings.RENDER_404', False):
81 response = dummy_view(request)
82 self.assertEqual(response.content, b'')
83 self.assertTrue(isinstance(response, HttpResponseNotFound))
84
7285 @mock.patch('guardian.conf.settings.RENDER_403', True)
7386 def test_TEMPLATE_403_setting(self):
7487 request = self._get_request(self.anon)
8194 response = dummy_view(request)
8295 self.assertEqual(response.content, b'foobar403\n')
8396
97 @mock.patch('guardian.conf.settings.RENDER_404', True)
98 def test_TEMPLATE_404_setting(self):
99 request = self._get_request(self.anon)
100
101 @permission_required_or_404('not_installed_app.change_user')
102 def dummy_view(request):
103 return HttpResponse('dummy_view')
104
105 with mock.patch('guardian.conf.settings.TEMPLATE_404', 'dummy404.html'):
106 response = dummy_view(request)
107 self.assertEqual(response.content, b'foobar404\n')
108
84109 @mock.patch('guardian.conf.settings.RENDER_403', True)
85110 def test_403_response_raises_error(self):
86111 request = self._get_request(self.anon)
92117 '_non-exisitng-403.html'):
93118 self.assertRaises(TemplateDoesNotExist, dummy_view, request)
94119
120 @mock.patch('guardian.conf.settings.RENDER_404', True)
121 def test_404_response_raises_error(self):
122 request = self._get_request(self.anon)
123
124 @permission_required_or_404('not_installed_app.change_user')
125 def dummy_view(request):
126 return HttpResponse('dummy_view')
127 with mock.patch('guardian.conf.settings.TEMPLATE_404',
128 '_non-exisitng-404.html'):
129 self.assertRaises(TemplateDoesNotExist, dummy_view, request)
130
95131 @mock.patch('guardian.conf.settings.RENDER_403', False)
96132 @mock.patch('guardian.conf.settings.RAISE_403', True)
97133 def test_RAISE_403_setting_is_true(self):
102138 return HttpResponse('dummy_view')
103139
104140 self.assertRaises(PermissionDenied, dummy_view, request)
141
142 @mock.patch('guardian.conf.settings.RENDER_404', False)
143 @mock.patch('guardian.conf.settings.RAISE_404', True)
144 def test_RAISE_404_setting_is_true(self):
145 request = self._get_request(self.anon)
146
147 @permission_required_or_404('not_installed_app.change_user')
148 def dummy_view(request):
149 return HttpResponse('dummy_view')
150
151 self.assertRaises(ObjectDoesNotExist, dummy_view, request)
105152
106153 def test_anonymous_user_wrong_app(self):
107154
00 from __future__ import unicode_literals
1 from guardian.testapp.models import Mixed, ReverseMixed
2 from guardian.testapp.models import Project
3 from guardian.testapp.models import ProjectGroupObjectPermission
4 from guardian.testapp.models import ProjectUserObjectPermission
1
52 from django.contrib.auth.models import Group, Permission
63 from django.test import TestCase
4
75 from guardian.compat import get_user_model
86 from guardian.shortcuts import assign_perm
97 from guardian.shortcuts import get_groups_with_perms
119 from guardian.shortcuts import get_objects_for_user
1210 from guardian.shortcuts import get_users_with_perms
1311 from guardian.shortcuts import remove_perm
12 from guardian.testapp.models import Mixed, ReverseMixed
13 from guardian.testapp.models import Project
14 from guardian.testapp.models import ProjectGroupObjectPermission
15 from guardian.testapp.models import ProjectUserObjectPermission
1416 from guardian.testapp.tests.conf import skipUnlessTestApp
15
1617
1718 User = get_user_model()
1819
1920
2021 @skipUnlessTestApp
2122 class TestDirectUserPermissions(TestCase):
22
2323 def setUp(self):
2424 self.joe = User.objects.create_user('joe', 'joe@example.com', 'foobar')
2525 self.project = Project.objects.create(name='Foobar')
7272 assign_perm('change_project', jane, self.project)
7373 self.assertEqual(get_users_with_perms(self.project, attach_perms=True),
7474 {
75 self.joe: ['add_project', 'change_project'],
76 jane: ['change_project'],
77 })
75 self.joe: ['add_project', 'change_project'],
76 jane: ['change_project'],
77 })
7878
7979 def test_get_users_with_perms_plus_groups(self):
8080 User.objects.create_user('john', 'john@foobar.com', 'john')
8686 assign_perm('change_project', jane, self.project)
8787 self.assertEqual(get_users_with_perms(self.project, attach_perms=True),
8888 {
89 self.joe: ['add_project', 'change_project'],
90 jane: ['change_project'],
91 })
89 self.joe: ['add_project', 'change_project'],
90 jane: ['change_project'],
91 })
9292
9393 def test_get_objects_for_user(self):
9494 foo = Project.objects.create(name='foo')
120120
121121 @skipUnlessTestApp
122122 class TestDirectGroupPermissions(TestCase):
123
124123 def setUp(self):
125124 self.joe = User.objects.create_user('joe', 'joe@example.com', 'foobar')
126125 self.group = Group.objects.create(name='admins')
175174 assign_perm('change_project', devs, self.project)
176175 self.assertEqual(get_groups_with_perms(self.project, attach_perms=True),
177176 {
178 self.group: ['add_project', 'change_project'],
179 devs: ['change_project'],
180 })
177 self.group: ['add_project', 'change_project'],
178 devs: ['change_project'],
179 })
180
181 def test_get_groups_with_perms_doesnt_spawn_extra_queries_for_more_groups_with_perms(self):
182 Group.objects.create(name='managers')
183 devs = Group.objects.create(name='devs')
184 devs1 = Group.objects.create(name='devs1')
185 devs2 = Group.objects.create(name='devs2')
186 devs3 = Group.objects.create(name='devs3')
187 devs4 = Group.objects.create(name='devs4')
188 devs5 = Group.objects.create(name='devs5')
189 assign_perm('add_project', self.group, self.project)
190 assign_perm('change_project', self.group, self.project)
191 for group in [devs, devs1, devs2, devs3, devs4, devs5]:
192 assign_perm('add_project', group, self.project)
193 assign_perm('change_project', group, self.project)
194
195 with self.assertNumQueries(3):
196 result = get_groups_with_perms(self.project, attach_perms=True)
197
198 self.assertEqual(result,
199 {
200 self.group: ['add_project', 'change_project'],
201 devs: ['add_project', 'change_project'],
202 devs1: ['add_project', 'change_project'],
203 devs2: ['add_project', 'change_project'],
204 devs3: ['add_project', 'change_project'],
205 devs4: ['add_project', 'change_project'],
206 devs5: ['add_project', 'change_project'],
207 })
181208
182209 def test_get_objects_for_group(self):
183210 foo = Project.objects.create(name='foo')
193220
194221 @skipUnlessTestApp
195222 class TestMixedDirectAndGenericObjectPermission(TestCase):
196
197223 def setUp(self):
198224 self.joe = User.objects.create_user('joe', 'joe@example.com', 'foobar')
199225 self.group = Group.objects.create(name='admins')
211237 assign_perm('change_mixed', jane, self.mixed)
212238 self.assertEqual(get_users_with_perms(self.mixed, attach_perms=True),
213239 {
214 self.joe: ['add_mixed', 'change_mixed'],
215 jane: ['change_mixed'],
216 })
240 self.joe: ['add_mixed', 'change_mixed'],
241 jane: ['change_mixed'],
242 })
217243 result = get_objects_for_user(self.joe, 'testapp.add_mixed')
218244 self.assertEqual(sorted(p.pk for p in result),
219245 sorted([self.mixed.pk]))
228254 assign_perm('change_reversemixed', jane, self.reverse_mixed)
229255 self.assertEqual(get_users_with_perms(self.reverse_mixed, attach_perms=True),
230256 {
231 self.joe: ['add_reversemixed', 'change_reversemixed'],
232 jane: ['change_reversemixed'],
233 })
257 self.joe: ['add_reversemixed', 'change_reversemixed'],
258 jane: ['change_reversemixed'],
259 })
234260 result = get_objects_for_user(self.joe, 'testapp.add_reversemixed')
235261 self.assertEqual(sorted(p.pk for p in result),
236262 sorted([self.reverse_mixed.pk]))
00 from __future__ import unicode_literals
11 from django.test import TestCase
22 import mock
3 import warnings
34 from guardian.managers import UserObjectPermissionManager
45 from guardian.managers import GroupObjectPermissionManager
56
910 def test_user_manager_assign(self):
1011 manager = UserObjectPermissionManager()
1112 manager.assign_perm = mock.Mock()
12 manager.assign('perm', 'user', 'object')
13
14 with warnings.catch_warnings(record=True) as w:
15 warnings.simplefilter("always")
16
17 manager.assign('perm', 'user', 'object')
18
1319 manager.assign_perm.assert_called_once_with('perm', 'user', 'object')
20
21 self.assertTrue(issubclass(w[0].category, DeprecationWarning))
22 self.assertIn("UserObjectPermissionManager method 'assign' is being renamed to 'assign_perm'.", str(w[0].message))
1423
1524 def test_group_manager_assign(self):
1625 manager = GroupObjectPermissionManager()
1726 manager.assign_perm = mock.Mock()
18 manager.assign('perm', 'group', 'object')
27
28 with warnings.catch_warnings(record=True) as w:
29 warnings.simplefilter("always")
30
31 manager.assign('perm', 'group', 'object')
32
1933 manager.assign_perm.assert_called_once_with('perm', 'group', 'object')
34
35 self.assertTrue(issubclass(w[0].category, DeprecationWarning))
36 self.assertIn("UserObjectPermissionManager method 'assign' is being renamed to 'assign_perm'.", str(w[0].message))
66 from django.test import TestCase
77 from django.test.client import RequestFactory
88 from django.views.generic import View
9
9 from django.views.generic import ListView
10
11 from guardian.shortcuts import assign_perm
1012 from guardian.compat import get_user_model
1113 import mock
1214 from guardian.mixins import LoginRequiredMixin
1315 from guardian.mixins import PermissionRequiredMixin
16 from guardian.mixins import PermissionListMixin
1417
1518 from ..models import Post
1619
3437 permission_required = 'testapp.change_post'
3538
3639
40 class GlobalNoObjectView(PermissionRequiredMixin, RemoveDatabaseView):
41 permission_required = 'testapp.add_post'
42 accept_global_perms = True
43
44
45 class PostPermissionListView(PermissionListMixin, ListView):
46 model = Post
47 permission_required = 'testapp.change_post'
48 template_name = 'list.html'
49
50
3751 class TestViewMixins(TestCase):
3852
3953 def setUp(self):
40 self.post = Post.objects.create(title='foo')
54 self.post = Post.objects.create(title='foo-post-title')
4155 self.factory = RequestFactory()
4256 self.user = get_user_model().objects.create_user(
4357 'joe', 'joe@doe.com', 'doe')
110124 response = view(request)
111125 self.assertEqual(response.status_code, 302)
112126
127 def test_permission_required_global_no_object(self):
128 """
129 This test would fail if permission is checked on a view's
130 object when it not set and **no** global permission
131 """
132
133 request = self.factory.get('/')
134 request.user = self.user
135 view = GlobalNoObjectView.as_view()
136 response = view(request)
137 self.assertEqual(response.status_code, 302)
138
139 def test_permission_granted_global_no_object(self):
140 """
141 This test would fail if permission is checked on a view's
142 object when it not set and **has** global permission
143 """
144
145 request = self.factory.get('/')
146 request.user = self.user
147 assign_perm('testapp.add_post', request.user)
148 view = GlobalNoObjectView.as_view()
149 with self.assertRaises(DatabaseRemovedError):
150 view(request)
151
113152 def test_permission_required_as_list(self):
114153 """
115154 This test would fail if permission is checked **after** view is
159198 response = view(request)
160199 self.assertEqual(response.status_code, 200)
161200 self.assertEqual(response.content, b'secret-view')
201
202 def test_list_permission(self):
203 request = self.factory.get('/some-secret-list/')
204 request.user = AnonymousUser()
205
206 view = PostPermissionListView.as_view()
207
208 response = view(request)
209 self.assertNotContains(response, b'foo-post-title')
210
211 request.user = self.user
212 request.user.add_obj_perm('change_post', self.post)
213
214 response = view(request)
215 self.assertContains(response, b'foo-post-title')
6868 user_or_group=self.user)
6969
7070 def test_user_assign_perm(self):
71 assign_perm("change_contenttype", self.user, self.ctype)
71 assign_perm("add_contenttype", self.user, self.ctype)
7272 assign_perm("change_contenttype", self.group, self.ctype)
73 assign_perm(self.get_permission("delete_contenttype"), self.user, self.ctype)
74 self.assertTrue(self.user.has_perm("add_contenttype", self.ctype))
7375 self.assertTrue(self.user.has_perm("change_contenttype", self.ctype))
76 self.assertTrue(self.user.has_perm("delete_contenttype", self.ctype))
7477
7578 def test_group_assign_perm(self):
79 assign_perm("add_contenttype", self.group, self.ctype)
7680 assign_perm("change_contenttype", self.group, self.ctype)
77 assign_perm("delete_contenttype", self.group, self.ctype)
81 assign_perm(self.get_permission("delete_contenttype"), self.group, self.ctype)
7882
7983 check = ObjectPermissionChecker(self.group)
84 self.assertTrue(check.has_perm("add_contenttype", self.ctype))
8085 self.assertTrue(check.has_perm("change_contenttype", self.ctype))
8186 self.assertTrue(check.has_perm("delete_contenttype", self.ctype))
87
88 def test_user_assign_perm_queryset(self):
89 assign_perm("add_contenttype", self.user, self.ctype_qset)
90 assign_perm("change_contenttype", self.group, self.ctype_qset)
91 assign_perm(self.get_permission("delete_contenttype"), self.user, self.ctype_qset)
92 for obj in self.ctype_qset:
93 self.assertTrue(self.user.has_perm("add_contenttype", obj))
94 self.assertTrue(self.user.has_perm("change_contenttype", obj))
95 self.assertTrue(self.user.has_perm("delete_contenttype", obj))
96
97 def test_group_assign_perm_queryset(self):
98 assign_perm("add_contenttype", self.group, self.ctype_qset)
99 assign_perm("change_contenttype", self.group, self.ctype_qset)
100 assign_perm(self.get_permission("delete_contenttype"), self.group, self.ctype_qset)
101
102 check = ObjectPermissionChecker(self.group)
103 for obj in self.ctype_qset:
104 self.assertTrue(check.has_perm("add_contenttype", obj))
105 self.assertTrue(check.has_perm("change_contenttype", obj))
106 self.assertTrue(check.has_perm("delete_contenttype", obj))
82107
83108 def test_user_assign_perm_global(self):
84109 perm = assign_perm("contenttypes.change_contenttype", self.user)
128153
129154 check = ObjectPermissionChecker(self.group)
130155 self.assertFalse(check.has_perm("change_contenttype", self.ctype))
156
157 def test_user_remove_perm_queryset(self):
158 assign_perm("change_contenttype", self.user, self.ctype_qset)
159 remove_perm("change_contenttype", self.user, self.ctype_qset)
160 for obj in self.ctype_qset:
161 self.assertFalse(self.user.has_perm("change_contenttype", obj))
162
163 def test_user_remove_perm_empty_queryset(self):
164 assign_perm("change_contenttype", self.user, self.ctype_qset)
165 remove_perm("change_contenttype", self.user, self.ctype_qset.none())
166
167 self.assertEquals(list(self.ctype_qset.none()), [])
168 for obj in self.ctype_qset:
169 self.assertTrue(self.user.has_perm("change_contenttype", obj))
170
171 def test_group_remove_perm_queryset(self):
172 assign_perm("change_contenttype", self.group, self.ctype_qset)
173 remove_perm("change_contenttype", self.group, self.ctype_qset)
174
175 check = ObjectPermissionChecker(self.group)
176 for obj in self.ctype_qset:
177 self.assertFalse(check.has_perm("change_contenttype", obj))
131178
132179 def test_user_remove_perm_global(self):
133180 # assign perm first
1212 permission_required = 'testapp.change_project'
1313
1414 urlpatterns = [
15 url(r'^admin/', include(admin.site.urls)),
15 url(r'^admin/', admin.site.urls),
1616 url(r'^accounts/login/', login, {'template_name': 'blank.html'}),
1717 url(r'^permission_required/', TestClassRedirectView.as_view()),
1818 ]
11 import random
22 import string
33 import environ
4 import django
45
56 env = environ.Env()
67
2728 'guardian.backends.ObjectPermissionBackend',
2829 )
2930
30 # this fixes warnings in django 1.7
31 MIDDLEWARE_CLASSES = (
31 # this fixes warnings in django 1.10
32 MIDDLEWARE = (
3233 'django.middleware.common.CommonMiddleware',
3334 'django.contrib.sessions.middleware.SessionMiddleware',
3435 'django.contrib.auth.middleware.AuthenticationMiddleware',
3536 'django.contrib.messages.middleware.MessageMiddleware',
3637 )
3738
39 if django.VERSION < (1, 10):
40 MIDDLEWARE_CLASSES = MIDDLEWARE
41
3842 TEST_RUNNER = 'django.test.runner.DiscoverRunner'
3943
4044 ROOT_URLCONF = 'guardian.testapp.tests.urls'
4145 SITE_ID = 1
42
43 TEMPLATE_DIRS = (
44 os.path.join(os.path.dirname(__file__), 'tests', 'templates'),
45 )
4646
4747 SECRET_KEY = ''.join([random.choice(string.ascii_letters) for x in range(40)])
4848
5454 TEMPLATES = [
5555 {
5656 'BACKEND': 'django.template.backends.django.DjangoTemplates',
57 'DIRS': TEMPLATE_DIRS,
57 'DIRS': (
58 os.path.join(os.path.dirname(__file__), 'tests', 'templates'),
59 ),
5860 'APP_DIRS': True,
5961 'OPTIONS': {
6062 'context_processors': [
6971 },
7072 },
7173 ]
74
75 if django.VERSION < (1, 8):
76 TEMPLATE_DIRS = TEMPLATES[0]['DIRS']
55 they actual input parameters/output type may change in future releases.
66 """
77 from __future__ import unicode_literals
8 import os
9 import logging
10 from itertools import chain
11 import django
128 from django.conf import settings
139 from django.contrib.auth import REDIRECT_FIELD_NAME
1410 from django.contrib.auth.models import AnonymousUser, Group
15 from django.contrib.contenttypes.models import ContentType
16 from django.core.exceptions import PermissionDenied
11 from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
1712 from django.db.models import Model
18 from django.http import HttpResponseForbidden
13 from django.http import HttpResponseForbidden, HttpResponseNotFound
1914 from django.shortcuts import render_to_response
20 from django.template import RequestContext, TemplateDoesNotExist
15 from django.template import RequestContext
16 from guardian.compat import get_user_model, remote_model
17 from guardian.conf import settings as guardian_settings
18 from guardian.ctypes import get_content_type
19 from guardian.exceptions import NotUserNorGroup
20 from itertools import chain
2121
22 from guardian.compat import get_user_model
23 from guardian.conf import settings as guardian_settings
24 from guardian.exceptions import NotUserNorGroup
25
26 from django.contrib.auth.views import redirect_to_login
22 import django
23 import logging
24 import os
2725
2826 logger = logging.getLogger(__name__)
2927 abspath = lambda *p: os.path.abspath(os.path.join(*p))
8280 "(got %s)" % identity)
8381
8482
85 def get_403_or_None(request, perms, obj=None, login_url=None,
86 redirect_field_name=None, return_403=False, accept_global_perms=False):
83 def get_40x_or_None(request, perms, obj=None, login_url=None,
84 redirect_field_name=None, return_403=False,
85 return_404=False, accept_global_perms=False):
8786 login_url = login_url or settings.LOGIN_URL
8887 redirect_field_name = redirect_field_name or REDIRECT_FIELD_NAME
8988
110109 elif guardian_settings.RAISE_403:
111110 raise PermissionDenied
112111 return HttpResponseForbidden()
112 if return_404:
113 if guardian_settings.RENDER_404:
114 response = render_to_response(
115 guardian_settings.TEMPLATE_404, {},
116 RequestContext(request))
117 response.status_code = 404
118 return response
119 elif guardian_settings.RAISE_404:
120 raise ObjectDoesNotExist
121 return HttpResponseNotFound()
113122 else:
123 from django.contrib.auth.views import redirect_to_login
114124 return redirect_to_login(request.get_full_path(),
115125 login_url,
116126 redirect_field_name)
128138
129139 deleted = 0
130140 # TODO: optimise
131 for perm in chain(UserObjectPermission.objects.all(),
132 GroupObjectPermission.objects.all()):
141 for perm in chain(UserObjectPermission.objects.all().iterator(),
142 GroupObjectPermission.objects.all().iterator()):
133143 if perm.content_object is None:
134144 logger.debug("Removing %s (pk=%d)" % (perm, perm.pk))
135145 perm.delete()
145155 def get_obj_perms_model(obj, base_cls, generic_cls):
146156 if isinstance(obj, Model):
147157 obj = obj.__class__
148 ctype = ContentType.objects.get_for_model(obj)
158 ctype = get_content_type(obj)
149159
150160 if django.VERSION >= (1, 8):
151161 fields = (f for f in obj._meta.get_fields()
165175 # make sure that content_object's content_type is same as
166176 # the one of given obj
167177 fk = model._meta.get_field('content_object')
168 if ctype == ContentType.objects.get_for_model(fk.rel.to):
178 if ctype == get_content_type(remote_model(fk)):
169179 return model
170180 return generic_cls
171181
+0
-4
guardian/version.py less more
0 # coding: utf-8
1 # file generated by setuptools_scm
2 # don't change, don't track in version control
3 version = '1.4.4'
0 Django==1.9.1
0 Django>=1.10.6
11 six
22 django-environ
3 setuptools_scm
3 bumpversion
0 [bumpversion]
1 current_version = 1.4.8
2
03 [build_sphinx]
14 source-dir = docs/
25 build-dir = docs/build
69 upload-dir = docs/build/html
710
811 [bdist_rpm]
9 requires = Django >= 1.2
12 requires = Django >= 1.8
1013
1114 [wheel]
1215 universal = 1
1417 [aliases]
1518 test = pytest
1619
20 [bumpversion:file:setup.py]
21 search = version = '{current_version}'
22 replace = version = '{new_version}'
23
24 [bumpversion:file:guardian/__init__.py]
25 search = __version__ = '{current_version}'
26 replace = __version__ = '{new_version}'
27
1728 [egg_info]
1829 tag_build =
1930 tag_date = 0
00 import os
1 import sys
21 from setuptools import setup
32 from extras import RunFlakesCommand
43
54
6 def version_scheme(version):
7 from setuptools_scm.version import guess_next_dev_version
8 version = guess_next_dev_version(version)
9 return version.lstrip("v")
5 version = '1.4.8'
106
117 readme_file = os.path.join(os.path.dirname(__file__), 'README.rst')
128 with open(readme_file, 'r') as f:
13 long_description = f.readline().strip()
9 long_description = f.read()
1410
1511 setup(
1612 name='django-guardian',
17 use_scm_version={
18 'write_to': "guardian/version.py",
19 'version_scheme': version_scheme,
20 },
21 setup_requires=['setuptools_scm', 'pytest-runner'],
13 version=version,
14 setup_requires=['pytest-runner', ],
2215 url='http://github.com/django-guardian/django-guardian',
2316 author='Lukasz Balcerzak',
2417 author_email='lukaszbalcerzak@gmail.com',
3528 include_package_data=True,
3629 license='BSD',
3730 install_requires=[
38 'Django >= 1.7',
3931 'six',
4032 ],
4133 tests_require=['mock', 'django-environ', 'pytest', 'pytest-django'],
4840 'Programming Language :: Python',
4941 'Topic :: Security',
5042 'Programming Language :: Python :: 2.7',
51 'Programming Language :: Python :: 3.3',
5243 'Programming Language :: Python :: 3.4',
5344 'Programming Language :: Python :: 3.5',
45 'Programming Language :: Python :: 3.6',
5446 ],
5547 test_suite='tests.main',
5648 cmdclass={'flakes': RunFlakesCommand},
00 [tox]
11 downloadcache = {toxworkdir}/cache/
22 envlist =
3 py27-django17,
4 py27-django18,
5 py27-django19,
6 py33-django17,
7 py33-django18,
8 py34-django17,
9 py34-django18,
10 py35-django18,
11 py34-django19,
12 py35-django19,
3 {core,docs}-py27-django18,
4 {core}-py34-django18,
5 {core,docs}-py35-django18,
6 {core,example,docs}-py27-django110,
7 {core,example,docs}-py27-django111,
8 {core}-py34-django110,
9 {core}-py34-django111,
10 {core,example,docs}-py35-django110
11 {core,example,docs}-py35-django111
12 {core,example,docs}-py36-django110
13 {core,example,docs}-py36-django111
1314
1415 [testenv]
1516 passenv = DATABASE_URL
1617 basepython =
17 py26: python2.6
1818 py27: python2.7
19 py33: python3.3
2019 py34: python3.4
2120 py35: python3.5
21 py36: python3.6
22 changedir =
23 example: example_project
24 docs: docs
2225 commands =
23 # python setup.py flakes
24 py.test --cov=guardian
25 sphinx-build -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
26 core: py.test --cov=guardian
27 docs: sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
28 example: python manage.py test
2629 deps =
27 sphinx
28 mock>=0.7.2
29 setuptools>=17.1
30 setuptools_scm
31 sphinx_rtd_theme
32 pyflakes
3330 django-environ
34 pytest
35 pytest-django
36 pytest-cov
37 django17: django==1.7.10
38 django18: django==1.8.7
39 django19: django==1.9.1
31 core: mock>=0.7.2
32 core: setuptools>=17.1
33 core: pyflakes
34 core: pytest
35 core: pytest-django
36 core: pytest-cov
37 example: .
38 docs: sphinx
39 docs: sphinx_rtd_theme
40 docs: setuptools_scm
41 django18: django==1.8.17
42 django110: django>=1.10<1.11
43 django111: django>=1.11<1.12