pax_global_header00006660000000000000000000000064134776735520014535gustar00rootroot0000000000000052 comment=534fee0a3c9689873b2a99b4bbb70731ad74fba1 django-guardian-2.0.0/000077500000000000000000000000001347767355200145665ustar00rootroot00000000000000django-guardian-2.0.0/.gitignore000066400000000000000000000004461347767355200165620ustar00rootroot00000000000000guardian/version.py *.pyc *.log *.egg *.egg-info *.swp *.bak *.db *.orig build _build dist .DS_Store .coverage .hgignore .tox .ropeproject example_project/media example_project/conf/*.py .idea/ .eggs/ .cache/ .mypy_cache/ .pytest_cache/ # WebDAV remote filesystem .DAV .project .pydevproject django-guardian-2.0.0/.isort.cfg000066400000000000000000000001201347767355200164560ustar00rootroot00000000000000[settings] line_length=120 force_single_line=False force_alphabetical_sort=True django-guardian-2.0.0/.travis.yml000066400000000000000000000041641347767355200167040ustar00rootroot00000000000000language: python dist: xenial cache: pip install: - ./contrib/travis/install.sh script: - ./contrib/travis/test.sh notifications: irc: "irc.freenode.net#django-guardian" templates: django21: &django21 DJANGO_VERSION=2.1.* django22: &django22 DJANGO_VERSION=2.2.* djangomaster: &djangomaster DJANGO_VERSION=master postgres: &postgres DATABASE_URL=postgres://postgres@/django_guardian mysql: &mysql DATABASE_URL=mysql://root:@localhost/django_guardian sqlite: &sqlite DATABASE_URL=sqlite:// pgdb: &pgdb addons: {postgresql: "10"} mariadb: &mariadb addons: {mariadb: "10.3"} matrix: fast_finish: true include: - { python: 3.5, env: [*django21, *postgres], <<: *pgdb} - { python: 3.5, env: [*django21, *mysql], <<: *mariadb} - { python: 3.5, env: [*django21, *sqlite]} - { python: 3.5, env: [*django22, *postgres], <<: *pgdb} - { python: 3.5, env: [*django22, *mysql], <<: *mariadb} - { python: 3.5, env: [*django22, *sqlite]} - { python: 3.6, env: [*django21, *postgres], <<: *pgdb} - { python: 3.6, env: [*django21, *mysql], <<: *mariadb} - { python: 3.6, env: [*django21, *sqlite]} - { python: 3.6, env: [*django22, *postgres], <<: *pgdb} - { python: 3.6, env: [*django22, *mysql], <<: *mariadb} - { python: 3.6, env: [*django22, *sqlite]} - { python: 3.6, env: [*djangomaster, *postgres], <<: *pgdb} - { python: 3.6, env: [*djangomaster, *mysql], <<: *mariadb} - { python: 3.6, env: [*djangomaster, *sqlite]} - { python: 3.7, env: [*django21, *postgres], <<: *pgdb} - { python: 3.7, env: [*django21, *mysql], <<: *mariadb} - { python: 3.7, env: [*django21, *sqlite]} - { python: 3.7, env: [*django22, *postgres], <<: *pgdb} - { python: 3.7, env: [*django22, *mysql], <<: *mariadb} - { python: 3.7, env: [*django22, *sqlite]} - { python: 3.7, env: [*djangomaster, *postgres], <<: *pgdb} - { python: 3.7, env: [*djangomaster, *mysql], <<: *mariadb} - { python: 3.7, env: [*djangomaster, *sqlite]} allow_failures: - env: [*djangomaster, *postgres] - env: [*djangomaster, *mysql] - env: [*djangomaster, *sqlite] django-guardian-2.0.0/AUTHORS000066400000000000000000000044101347767355200156350ustar00rootroot00000000000000Authors ordered by first contribution - Lukasz Balcerzak - Cesar Canassa - Vincent Driessen - John Hensley - Ramanan Sivaranjan - Woosuk Suh - Bojan Mihelac - Rafael Ponieman - Daniel Sokolowski - Ali Lozano - BJ Dierkes - Rach Belaid - Michael Crosby - Greg Hinch - Aram Dulyan - Florian Hahn - Piotr Kilczuk - Reavis Sutphin-Gray - Adrián López - Jameel Al-Aziz - John P. Neumann - Andreas Madsack - Ivan Kharlamov - Miguel de Val-Borro - Jan Nakladal - Yonel Ceruto - Luke Faraone - John Wegis - Florentin Sardan - Geoff Greer - Hans Larsen - Fabio C. Barrionuevo da Luz @luzfcb - Tomasz Wsuł <2nickers@gmail.com> - Xavier Ordoquy - Joshua Bonnett - Jernej Kos - Bruno Ribeiro da Silva - Cezar Jenkins - Warren Volz - Omer Katz - Vishal Lal - Steven DeMartini - zauddelig - Remco Wendt - Kevin London - Kouhei Maeda - Samuel Sutch - Morgan Aubert - Brian May - Troy Grosfield - Michael Drescher - Verena Jaspersen - Bertrand Svetchine - Frank Wickström - George Karakostas - Adam Dobrawy - Jeff Hackshaw django-guardian-2.0.0/CHANGES000066400000000000000000000305701347767355200155660ustar00rootroot00000000000000Release 2.0.0 (June 11, 2019) ========================== * Drop support for Python 2.7 & 3.4 and Django 1.11 & 2.0 Release 1.5.1 (May 2, 2019) ========================== * Restore compatibility with Python 2.7 .. important:: The 1.5.x release line will be the last one that has support for Python 2.7. Release 1.5.0 (Jan 31, 2019) ========================== * Updates for compatibility with latest Python and Django versions. Release 1.4.9 (Jul 01, 2017) ============================ * Drop ``django_guardian.compat.OrderedDict`` as a consequence of drop Python 1.6 earlier. * Fix django admin 1.11 not showing selected permissions * Add a optional checker variable to get_obj_perms * Add missing classifiers in setup.py Release 1.4.8 (Apr 04, 2017) ============================ * Improved performance of `clean_orphan_obj_perms` management command * Use bumpversion for versioning. * Enable Python 3.6 testing * Python 2.7, 3.4, 3.5, 3.6 are only supported Python versions * Django 1.8, 1.10, and 1.11 are only supported Django versions * Added explicity on_delete to all ForeignKeys Release 1.4.6 (Sep 09, 2016) ============================ * Improved performance of get_objects_for_user * Added test-covered and documented guardian.mixins.PermissionListMixin * Allow content type retrieval to be overridden fg. for django-polymorphic support * Added support CreateView-like (no object) view in PermissionRequiredMixin * Added django 1.10 to TravisCI and tox * Run tests for example_project in TravisCI * Require django 1.9+ for example_project (django-guardian core support django 1.7+) * Fix django versions compatibility in example_project * Drop django in install_requires of setuptools Release 1.4.5 (Aug 09, 2016) ============================ * Fix caching issue with prefetch_perms. * Convert readthedocs link for their .org -> .io migration for hosted projects * Added example CRUD CBV project * Added TEMPLATES in example_project settings * Added Queryset support to assign_perm * Added QuerySet support to remove_perm * Updated assign_perm and remove_perm docstrings * Moved queryset support in assign_perms to its own function * Moved queryset support in remove_perms to its own function * Consolidated {User,Group}ObjectPermissionManager, move logic of bulk_*_perm to managers * `assign_perm` and `remove_perm` shortcuts accept `Permission` instance as `perm` and `QuerySet` as `obj` too. * Consolidated bulk_assign_perm to assign_perm and bulk_remove_perm to remove_perm * Upgraded Grappelli templates breadcrumbs block to new Django 1.9 and Grappelli 2.8 standards, including proper URLs and support for preserved_filters. Removed the duplicated field.errors in the field.html template file. * Made UserManage/GroupManage forms overridable * Fixed GuardedModelAdminMixin views render for Django 1.10 Release 1.4.4 (Apr 04, 2016) ============================ * Don't install support example_project. * Direct ForeignKey perms in prefetch_perms. Release 1.4.3 (Apr 03, 2016) ============================ * guardian.VERSION should be a tuple, not a list. Fixes #411. * Support for prefetching permissions. * Fixed union between queries. * Allow specifying an empty list of permissions for get_objects_for_group. * Mixed group and user direction relations broken. Fixes #271. * Lookup anonymous user using custom username field. * Fix up processing of ANONYMOUS_USER_NAME where set to None. Fixes #409. * Require TEMPLATE_403 to exist if RENDER_403 set. Release 1.4.2 (Mar 09, 2016) ============================ * Test against django-master (Django 1.10 - not released). * Django 1.10 fixes. * Fixes for documentation. * PEP8 fixes. * Fix distributed files in MANIFEST.in * Use pytest for tests. * Add dependancy on django-environ. * Don't use ANONYMOUS_USER_ID. Uses ANONYMOUS_DEFAULT_USERNAME and USERNAME_FIELD instead. * Use setuptools_scm for versioning. * Initialise admin context using each_context for Django >= 1.8. * Add missing with_superusers parameter to get_users_with_perms(). * Use setuptools scm for versioning. * Fixes for example_project. * Only display permissions if permission actually assigned. * When using `attach_perms` with `get_users_with_perms`, and `with_group_users` and `with_superusers` set to `False`, only directly assigned permissions are now returned, and not effective (infered) permissions. Release 1.4.1 (Jan 10, 2016) ============================ * Fix broken documentation. * Fix setup.py errors (#387). * Fix tox tests. * Fix travis tests. Release 1.4.0 (Jan 8, 2016) =========================== * Drop support for Django < 1.7 * Drop support for django south migrations. * Remove depreciated code. * Fix many Django depreciated warnings. * Fix tests and example_project. * Work around for postgresql specific Django bug (#366). This is a regression that was introduced in version 1.3.2. * Updates to documentation. * Require can_change permission to change object perms in admin. * Fixes broke admin URLS (#376 and #381). * Tests now work with Mysql and Postgresql as well as sqlite. * Uses django-environ for tests. Release 1.3.2 (Nov 14, 2015) ============================ * Fixes tests for all versions of Django. * Tests pass for Django 1.9b1. * Drops support for Django < 1.5 * Add Russian translation. * Various bug fixes. * Ensure password for anonymous user is set to unusable, not None. Release 1.3.1 (Oct 20, 2015) ============================ * Fixes for 1.8 compat Release 1.3 (Jun 3, 2015) ========================= * Official Django 1.8 support (thanks to multiple contributors) Release 1.2.5 (Dec 28, 2014) ============================ * Official Django 1.7 support (thanks Troy Grosfield and Brian May) * Allow to override ``PermissionRequiredMixin.get_permission_object``, part of ``PermissionRequiredMixin.check_permissions`` method, responsible for retrieving single object (Thanks zauddelig) * French translations (Thanks Morgan Aubert) * Added support for ``User.get_all_permissions`` (thanks Michael Drescher) Release 1.2.4 (Jul 14, 2014) ============================ * Fixed another issue with custom primary keys at admin extensions (Thanks Omer Katz) Release 1.2.3 (Jul 14, 2014) ============================ Unfortunately this was broken release not including any important changes. Release 1.2.2 (Jul 2, 2014) =========================== * Fixed issue with custom primary keys at admin extensions (Thanks Omer Katz) * ``get_403_or_None`` now accepts Python path to the view function, for example ``'django.contrib.auth.views.login'`` (Thanks Warren Volz) * Added ``with_superuser`` flag to ``guardian.shortcuts.get_objects_for_user`` (Thanks Bruno Ribeiro da Silva) * Added possibility to disable monkey patching of the ``User`` model. (Thanks Cezar Jenkins) Release 1.2 (Mar 7, 2014) ========================= * Removed ``get_for_object`` methods from managers (#188) * Extended documentation * GuardedModelAdmin has been splitted into mixins * Faster queries in get_objects_for_user when use_groups=False or any_perm=True (#148) * Improved speed of get_objects_for_user shortcut * Support for custom User model with not default username field * Added GUARDIAN_GET_INIT_ANONYMOUS_USER setting (#179) * Added ``accept_global_perms`` to ``PermissionRequiredMixin`` * Added brazilian portuguese translations * Added polish translations * Added ``wheel`` support * Fixed wrong anonymous user checks * Support for Django 1.6 * Support for Django 1.7 alpha .. important:: In this release we have removed undocumented ``get_for_object`` method from both ``UserObjectPermissionManager`` and ``GroupObjectPermissionManager``. Not deprecated, removed. Those methods were not used within ``django-guardian`` and their odd names could lead to issues if user would believe they would return object level permissions associated with user/group and object passed as the input. If you depend on those methods, you'd need to stick with version 1.1 and make sure you do not misuse them. Release 1.1 (May 26, 2013) ========================== * Support for Django 1.5 (including Python 3 combination) * Support for custom user models (introduced by Django 1.5) * Ability to create permissions using Foreign Keys * Added ``user_can_access_owned_by_group_objects_only`` option to ``GuardedModelAdmin.`` * Minor documentation fixups * Spanish translations * Better support for grappelli_ * Updated examples project * Speed up ``get_perms`` shortcut function Release 1.0.4 (Jul 15, 2012) ============================ * Added ``GUARDIAN_RENDER_403`` and ``GUARDIAN_RAISE_403`` settings (#40) * Updated docstring for ``get_obj_perms`` (#43) * Updated codes to run with newest django-grappelli (#51) * Fixed problem with building a RPM package (#50) * Updated caveats docs related with oprhaned object permissions (#47) * Updated ``permission_required`` docstring (#49) * Added ``accept_global_perms`` for decorators (#49) * Fixed problem with MySQL and booleans (#56) * Added flag to check for *any* permission in ``get_objects_for_user`` and ``get_objects_for_group`` (#65) * Added missing *tag closing* at template (#63) * Added view mixins related with authorization and authentication (#73) * Added tox_ support * Added Travis_ support Release 1.0.3 (Jul 25, 2011) ============================ * Added ``get_objects_for_group`` shortcut (thanks to Rafael Ponieman) * Added ``user_can_access_owned_objects_only`` flag to ``GuardedModelAdmin`` * Updated and fixed issues with example app (thanks to Bojan Mihelac) * Minor typo fixed at documentation * Included ADC theme for documentation Release 1.0.2 (Apr 12, 2011) ============================ * ``get_users_with_perms`` now accepts ``with_group_users`` flag * Fixed ``group_id`` issue at admin templates * Small fix for documentation building process * It's 2011 (updated dates within this file) Release 1.0.1 (Mar 25, 2011) ============================ * ``get_users_with_perms`` now accepts ``with_superusers`` flag * Small fix for documentation building process Release 1.0.0 (Jan 27, 2011) ============================ * A final v1.0 release! Release 1.0.0.beta2 (Jan 14, 2011) ================================== * Added ``get_objects_for_user`` shortcut function * Added few tests * Fixed issues related with ``django.contrib.auth`` tests * Removed example project from source distribution Release 1.0.0.beta1 (Jan 11, 2011) ================================== * Simplified example project * Fixed issues related with test suite * Added ability to clear orphaned object permissions * Added ``clean_orphan_obj_perms`` management command * Documentation cleanup * Added grappelli_ admin templates Release 1.0.0.alpha2 (Dec 2, 2010) ================================== * Added possibility to operate with global permissions for assign and ``remove_perm`` shortcut functions * Added possibility to generate PDF documentation * Fixed some tests Release 1.0.0.alpha1 (Nov 23, 2010) =================================== * Fixed admin templates not included in ``MANIFEST.in`` * Fixed admin integration codes Release 1.0.0.pre (Nov 23, 2010) ================================ * Added admin integration * Added reusable forms for object permissions management Release 0.2.3 (Nov 17, 2010) ============================ * Added ``guardian.shortcuts.get_users_with_perms`` function * Added ``AUTHORS`` file Release 0.2.2 (Oct 19, 2010) ============================ * Fixed migrations order (thanks to Daniel Rech) Release 0.2.1 (Oct 3, 2010) =========================== * Fixed migration (it wasn't actually updating object_pk field) Release 0.2.0 (Oct 3, 2010) =========================== Fixes ~~~~~ * #4: guardian now supports models with not-integer primary keys and they don't need to be called "id". .. important:: For 0.1.X users: it is required to *migrate* guardian in your projects. Add ``south`` to ``INSTALLED_APPS`` and run:: python manage.py syncdb python manage.py migrate guardian 0001 --fake python manage.py migrate guardian Improvements ~~~~~~~~~~~~ * Added South_ migrations support Release 0.1.1 (Sep 27, 2010) ============================ Improvements ~~~~~~~~~~~~ * Added view decorators: ``permission_required`` and ``permission_required_403`` Release 0.1.0 (Jun 6, 2010) =========================== * Initial public release .. _south: http://south.aeracode.org/ .. _grappelli: https://github.com/sehmaschine/django-grappelli .. _tox: http://tox.testrun.org/ .. _travis: http://travis-ci.org/ .. vim: ft=rst django-guardian-2.0.0/LICENSE000066400000000000000000000050601347767355200155740ustar00rootroot00000000000000Copyright (c) 2010-2016 Lukasz Balcerzak All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The SVG icons in guardian/static/guardian/img are copied from Django. SVG icons source: https://github.com/encharm/Font-Awesome-SVG-PNG Font-Awesome-SVG-PNG is licensed under the MIT license: The MIT License (MIT) Copyright (c) 2014 Code Charm Ltd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. django-guardian-2.0.0/MANIFEST.in000066400000000000000000000016741347767355200163340ustar00rootroot00000000000000include CHANGES include LICENSE include README.rst include MANIFEST.in include *.py include *.ini include run_test_and_report.sh include *.txt include AUTHORS recursive-include guardian *.py recursive-include guardian/locale *.po *.mo recursive-include docs *.bat recursive-include docs *.css recursive-include docs *.eot recursive-include docs *.html recursive-include docs *.js recursive-include docs *.py recursive-include docs *.rst recursive-include docs *.sh recursive-include docs *.svg recursive-include docs *.ttf recursive-include docs *.woff recursive-include docs Makefile recursive-include example_project *.css recursive-include example_project *.html recursive-include example_project *.js recursive-include example_project *.png recursive-include example_project *.txt recursive-include example_project *.py recursive-exclude docs/build * recursive-include guardian *.html recursive-include guardian *.svg recursive-include benchmarks *.py django-guardian-2.0.0/README.rst000066400000000000000000000053671347767355200162700ustar00rootroot00000000000000=============== django-guardian =============== .. image:: https://travis-ci.org/django-guardian/django-guardian.svg?branch=devel :target: https://travis-ci.org/django-guardian/django-guardian ``django-guardian`` is an implementation of per object permissions [1]_ on top of Django's authorization backend Documentation ------------- Online documentation is available at https://django-guardian.readthedocs.io/. Requirements ------------ * Python 3.5+ * A supported version of Django (currently 2.1+) Travis CI tests on Django version 2.1, 2.2, and master. Installation ------------ To install ``django-guardian`` simply run:: pip install django-guardian Configuration ------------- We need to hook ``django-guardian`` into our project. 1. Put ``guardian`` into your ``INSTALLED_APPS`` at settings module: .. code:: python INSTALLED_APPS = ( ... 'guardian', ) 2. Add extra authorization backend to your ``settings.py``: .. code:: python AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', # default 'guardian.backends.ObjectPermissionBackend', ) 3. Create ``guardian`` database tables by running:: python manage.py migrate Usage ----- After installation and project hooks we can finally use object permissions with Django_. Lets start really quickly: .. code:: python >>> from django.contrib.auth.models import User, Group >>> jack = User.objects.create_user('jack', 'jack@example.com', 'topsecretagentjack') >>> admins = Group.objects.create(name='admins') >>> jack.has_perm('change_group', admins) False >>> from guardian.models import UserObjectPermission >>> UserObjectPermission.objects.assign_perm('change_group', jack, obj=admins) >>> jack.has_perm('change_group', admins) True Of course our agent jack here would not be able to *change_group* globally: .. code:: python >>> jack.has_perm('change_group') False Admin integration ----------------- Replace ``admin.ModelAdmin`` with ``GuardedModelAdmin`` for those models which should have object permissions support within admin panel. For example: .. code:: python from django.contrib import admin from myapp.models import Author from guardian.admin import GuardedModelAdmin # Old way: #class AuthorAdmin(admin.ModelAdmin): # pass # With object permissions support class AuthorAdmin(GuardedModelAdmin): pass admin.site.register(Author, AuthorAdmin) .. [1] Great paper about this feature is available at `djangoadvent articles `_. .. _Django: http://www.djangoproject.com/ django-guardian-2.0.0/benchmarks/000077500000000000000000000000001347767355200167035ustar00rootroot00000000000000django-guardian-2.0.0/benchmarks/__init__.py000066400000000000000000000000001347767355200210020ustar00rootroot00000000000000django-guardian-2.0.0/benchmarks/models.py000066400000000000000000000010561347767355200205420ustar00rootroot00000000000000from django.db import models from guardian.models import UserObjectPermissionBase from guardian.models import GroupObjectPermissionBase class TestModel(models.Model): name = models.CharField(max_length=128) class DirectUser(UserObjectPermissionBase): content_object = models.ForeignKey('TestDirectModel', on_delete=models.CASCADE) class DirectGroup(GroupObjectPermissionBase): content_object = models.ForeignKey('TestDirectModel', on_delete=models.CASCADE) class TestDirectModel(models.Model): name = models.CharField(max_length=128) django-guardian-2.0.0/benchmarks/run_benchmarks.py000066400000000000000000000133721347767355200222640ustar00rootroot00000000000000#!/usr/bin/env python """ This benchmark package should be treated as work-in-progress, not a production ready benchmarking solution for django-guardian. """ import datetime import os import random import string import sys abspath = lambda *p: os.path.abspath(os.path.join(*p)) THIS_DIR = abspath(os.path.dirname(__file__)) ROOT_DIR = abspath(THIS_DIR, '..') # so the preferred guardian module is one within this repo and # not system-wide sys.path.insert(0, ROOT_DIR) os.environ["DJANGO_SETTINGS_MODULE"] = 'benchmarks.settings' import django django.setup() from benchmarks import settings from guardian.shortcuts import assign_perm from django.core.exceptions import ImproperlyConfigured from utils import show_settings from django.contrib.auth.models import User, Group from django.utils.termcolors import colorize from benchmarks.models import TestModel from benchmarks.models import TestDirectModel from guardian.models import UserObjectPermission from django.contrib.contenttypes.models import ContentType USERS_COUNT = 50 OBJECTS_COUNT = 100 OBJECTS_WIHT_PERMS_COUNT = 100 def random_string(length=25, chars=string.ascii_letters + string.digits): return ''.join(random.choice(chars) for i in range(length)) class Call: def __init__(self, args, kwargs, start=None, finish=None): self.args = args self.kwargs = kwargs self.start = start self.finish = finish def delta(self): return self.finish - self.start class Timed: def __init__(self, action=None): self.action = action def __call__(self, func): if not hasattr(func, 'calls'): func.calls = [] def wrapper(*args, **kwargs): if self.action: print(" -> [%s]" % self.action) start = datetime.datetime.now() call = Call(list(args), dict(kwargs), start) try: return func(*args, **kwargs) finally: call.finish = datetime.datetime.now() func.calls.append(call) if self.action: print(" -> [%s] Done (Total time: %s)" % (self.action, call.delta())) return wrapper class Benchmark: def __init__(self, name, users_count, objects_count, objects_with_perms_count, model, subquery): self.name = name self.users_count = users_count self.objects_count = objects_count self.objects_with_perms_count = objects_with_perms_count self.subquery = subquery self.Model = model self.perm = 'add_%s' % model._meta.model_name def info(self, msg): print(colorize(msg + '\n', fg='green')) def prepare_db(self): from django.core.management import call_command call_command('makemigrations', interactive=False) call_command('migrate', interactive=False) for model in [User, Group, self.Model]: model.objects.all().delete() @Timed("Creating users") def create_users(self): User.objects.bulk_create(User(id=x, username=random_string().capitalize()) for x in range(self.users_count)) @Timed("Creating objects") def create_objects(self): Model = self.Model Model.objects.bulk_create(Model(id=x, name=random_string(20)) for x in range(self.objects_count)) @Timed("Grant permissions") def grant_perms(self): ids = range(1, self.objects_count) for user in User.objects.iterator(): for x in xrange(self.objects_with_perms_count): obj = self.Model.objects.get(id=random.choice(ids)) self.grant_perm(user, obj, self.perm) def grant_perm(self, user, obj, perm): assign_perm(perm, user, obj) @Timed("Check permissions") def check_perms(self): ids = range(1, self.objects_count) for user in User.objects.iterator(): for x in xrange(self.objects_with_perms_count): obj = self.Model.objects.get(id=random.choice(ids)) self.check_perm(user, obj, self.perm) @Timed("Get objects") def get_objects(self): ctype = ContentType.objects.get_for_model(self.Model) ids = range(1, self.users_count) for user in User.objects.iterator(): for x in xrange(self.objects_with_perms_count): filters = {'user': random.choice(ids), 'permission__codename__in': [self.perm], 'content_type': ctype } qs = UserObjectPermission.objects.filter(**filters).all() if not self.subquery: qs = [v.object_pk for v in qs] list(self.Model.objects.filter(id__in=qs)) def check_perm(self, user, obj, perm): user.has_perm(perm, obj) @Timed("Benchmark") def main(self): self.info('=' * 80) self.info(self.name.center(80)) self.info('=' * 80) self.prepare_db() self.create_users() self.create_objects() self.grant_perms() self.check_perms() if not isinstance(self.Model, TestModel): self.get_objects() def main(): show_settings(settings, 'benchmarks') glob = [USERS_COUNT, OBJECTS_COUNT, OBJECTS_WIHT_PERMS_COUNT] Benchmark('Direct relations benchmark with subqueries', *glob, model=TestDirectModel, subquery=True).main() Benchmark('Direct relations benchmark without subqueries', *glob, model=TestDirectModel, subquery=False).main() Benchmark('Generic relations benchmark without subqueries', *glob, model=TestModel, subquery=False).main() if __name__ == '__main__': main() django-guardian-2.0.0/benchmarks/settings.py000066400000000000000000000011501347767355200211120ustar00rootroot00000000000000import os import sys import environ env = environ.Env() abspath = lambda *p: os.path.abspath(os.path.join(*p)) THIS_DIR = abspath(os.path.dirname(__file__)) ROOT_DIR = abspath(THIS_DIR, '..') # so the preferred guardian module is one within this repo and # not system-wide sys.path.insert(0, ROOT_DIR) SECRET_KEY = 'NO_NEED_SECRET' INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.contenttypes', 'django.contrib.admin', 'django.contrib.sites', 'guardian', 'benchmarks', ) DJALOG_LEVEL = 40 DATABASES = {'default': env.db(default="sqlite:///")} django-guardian-2.0.0/contrib/000077500000000000000000000000001347767355200162265ustar00rootroot00000000000000django-guardian-2.0.0/contrib/travis/000077500000000000000000000000001347767355200175365ustar00rootroot00000000000000django-guardian-2.0.0/contrib/travis/install.sh000077500000000000000000000012351347767355200215440ustar00rootroot00000000000000#!/bin/bash set -ev pip install -U pip # Array of packages PACKAGES=('mock==1.0.1' 'pytest' 'pytest-django' 'pytest-cov' 'django-environ' 'setuptools_scm') # Install django master or version if [[ "$DJANGO_VERSION" == 'master' ]]; then PACKAGES+=('https://github.com/django/django/archive/master.tar.gz'); else PACKAGES+=("Django==$DJANGO_VERSION"); fi; # Install database drivers if [[ $DATABASE_URL = postgres* ]]; then PACKAGES+=('psycopg2==2.7.5'); fi; if [[ $DATABASE_URL = mysql* ]]; then PACKAGES+=('mysqlclient==1.3.13'); fi; echo "Install " ${PACKAGES[*]}; pip install --upgrade --upgrade-strategy=only-if-needed ${PACKAGES[*]}; pip check django-guardian-2.0.0/contrib/travis/test.sh000077500000000000000000000005051347767355200210540ustar00rootroot00000000000000#!/bin/bash python ./setup.py --version py.test --cov=guardian # Test example_project on supported django versions if [ "${DJANGO_VERSION:0:3}" = "2.1" ] || \ [ "${DJANGO_VERSION:0:3}" = "2.2" ] || \ [ "$DJANGO_VERSION" = "master" ]; then pip install .; cd example_project; python -Wa manage.py test; fi; django-guardian-2.0.0/docs/000077500000000000000000000000001347767355200155165ustar00rootroot00000000000000django-guardian-2.0.0/docs/Makefile000066400000000000000000000063661347767355200171710ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pdf to make PDF document" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-guardian.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-guardian.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." pdf: $(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) $(BUILDDIR)/pdf @echo @echo "Build finished. The PDF files are in $(BUILDDIR)/pdf." django-guardian-2.0.0/docs/api/000077500000000000000000000000001347767355200162675ustar00rootroot00000000000000django-guardian-2.0.0/docs/api/guardian.admin.rst000066400000000000000000000004611347767355200217030ustar00rootroot00000000000000.. _api-admin: Admin ===== .. automodule:: guardian.admin .. admin:: GuardedModelAdmin GuardedModelAdmin ----------------- .. autoclass:: guardian.admin.GuardedModelAdmin :members: GuardedModelAdminMixin ---------------------- .. autoclass:: guardian.admin.GuardedModelAdminMixin :members: django-guardian-2.0.0/docs/api/guardian.backends.rst000066400000000000000000000003021347767355200223570ustar00rootroot00000000000000.. _api-backends: Backends ======== .. automodule:: guardian.backends ObjectPermissionBackend ----------------------- .. autoclass:: guardian.backends.ObjectPermissionBackend :members: django-guardian-2.0.0/docs/api/guardian.core.rst000066400000000000000000000002561347767355200215450ustar00rootroot00000000000000.. _api-core: Core ==== .. automodule:: guardian.core ObjectPermissionChecker ----------------------- .. autoclass:: guardian.core.ObjectPermissionChecker :members: django-guardian-2.0.0/docs/api/guardian.decorators.rst000066400000000000000000000006111347767355200227550ustar00rootroot00000000000000.. _api-decorators: Decorators ========== .. automodule:: guardian.decorators .. _api-decorators-permission_required: permission_required ------------------- .. autofunction:: guardian.decorators.permission_required .. _api-decorators-permission_required_or_403: permission_required_or_403 -------------------------- .. autofunction:: guardian.decorators.permission_required_or_403 django-guardian-2.0.0/docs/api/guardian.forms.rst000066400000000000000000000011201347767355200217320ustar00rootroot00000000000000.. _api-forms: Forms ===== .. automodule:: guardian.forms .. form:: UserObjectPermissionsForm UserObjectPermissionsForm ------------------------- .. autoclass:: guardian.forms.UserObjectPermissionsForm :members: :show-inheritance: .. form:: GroupObjectPermissionsForm GroupObjectPermissionsForm -------------------------- .. autoclass:: guardian.forms.GroupObjectPermissionsForm :members: :show-inheritance: .. form:: BaseObjectPermissionsForm BaseObjectPermissionsForm ------------------------- .. autoclass:: guardian.forms.BaseObjectPermissionsForm :members: django-guardian-2.0.0/docs/api/guardian.management.commands.rst000066400000000000000000000002701347767355200245250ustar00rootroot00000000000000.. _api-management-commands: Management commands =================== .. command:: clean_orphan_obj_perms .. autoclass:: guardian.management.commands.clean_orphan_obj_perms.Command django-guardian-2.0.0/docs/api/guardian.managers.rst000066400000000000000000000006521347767355200224120ustar00rootroot00000000000000.. _api-managers: Managers ======== .. automodule:: guardian.managers .. manager:: UserObjectPermissionManager UserObjectPermissionManager --------------------------- .. autoclass:: guardian.managers.UserObjectPermissionManager :members: .. manager:: GroupObjectPermissionManager GroupObjectPermissionManager ---------------------------- .. autoclass:: guardian.managers.GroupObjectPermissionManager :members: django-guardian-2.0.0/docs/api/guardian.mixins.rst000066400000000000000000000007521347767355200221250ustar00rootroot00000000000000.. _api-mixins: Mixins ====== .. versionadded:: 1.0.4 .. automodule:: guardian.mixins .. mixin:: LoginRequiredMixin LoginRequiredMixin ------------------ .. autoclass:: guardian.mixins.LoginRequiredMixin :members: .. mixin:: PermissionRequiredMixin PermissionRequiredMixin ----------------------- .. autoclass:: guardian.mixins.PermissionRequiredMixin :members: PermissionListMixin ----------------------- .. autoclass:: guardian.mixins.PermissionListMixin :members: django-guardian-2.0.0/docs/api/guardian.models.rst000066400000000000000000000007621347767355200221020ustar00rootroot00000000000000.. _api-models: Models ====== .. automodule:: guardian.models .. model:: BaseObjectPermission BaseObjectPermission -------------------- .. autoclass:: guardian.models.BaseObjectPermission :members: .. model:: UserObjectPermission UserObjectPermission -------------------- .. autoclass:: guardian.models.UserObjectPermission :members: .. model:: GroupObjectPermission GroupObjectPermission --------------------- .. autoclass:: guardian.models.GroupObjectPermission :members: django-guardian-2.0.0/docs/api/guardian.shortcuts.rst000066400000000000000000000025021347767355200226470ustar00rootroot00000000000000.. _api-shortcuts: Shortcuts ========= .. automodule:: guardian.shortcuts .. _api-shortcuts-assign: assign_perm ----------- .. autofunction:: guardian.shortcuts.assign_perm .. _api-shortcuts-remove_perm: remove_perm ----------- .. autofunction:: guardian.shortcuts.remove_perm .. _api-shortcuts-get_perms: get_perms --------- .. autofunction:: guardian.shortcuts.get_perms .. _api-shortcuts-get_user_perms: get_user_perms -------------- .. autofunction:: guardian.shortcuts.get_user_perms .. _api-shortcuts-get_group_perms: get_group_perms --------------- .. autofunction:: guardian.shortcuts.get_group_perms .. _api-shortcuts-get_perms_for_model: get_perms_for_model ------------------- .. autofunction:: guardian.shortcuts.get_perms_for_model .. _api-shortcuts-get_users_with_perms: get_users_with_perms -------------------- .. autofunction:: guardian.shortcuts.get_users_with_perms .. _api-shortcuts-get_groups_with_perms: get_groups_with_perms --------------------- .. autofunction:: guardian.shortcuts.get_groups_with_perms .. _api-shortcuts-get_objects_for_user: .. shortcut:: get_objects_for_user get_objects_for_user -------------------- .. autofunction:: guardian.shortcuts.get_objects_for_user get_objects_for_group --------------------- .. autofunction:: guardian.shortcuts.get_objects_for_group django-guardian-2.0.0/docs/api/guardian.templatetags.guardian_tags.rst000066400000000000000000000003151347767355200261120ustar00rootroot00000000000000.. _api-template-tags: Template tags ============= .. automodule:: guardian.templatetags.guardian_tags get_obj_perms ------------- .. autofunction:: guardian.templatetags.guardian_tags.get_obj_perms django-guardian-2.0.0/docs/api/guardian.utils.rst000066400000000000000000000005121347767355200217500ustar00rootroot00000000000000.. _api-utils: .. currentmodule:: guardian.utils Utilities ========= .. automodule:: guardian.utils get_anonymous_user ------------------ .. autofunction:: get_anonymous_user get_identity ------------ .. autofunction:: get_identity clean_orphan_obj_perms ---------------------- .. autofunction:: clean_orphan_obj_perms django-guardian-2.0.0/docs/api/index.rst000066400000000000000000000005251347767355200201320ustar00rootroot00000000000000.. _api: API Reference ============= .. toctree:: :maxdepth: 2 guardian.admin guardian.backends guardian.core guardian.decorators guardian.forms guardian.management.commands guardian.managers guardian.mixins guardian.models guardian.shortcuts guardian.utils guardian.templatetags.guardian_tags django-guardian-2.0.0/docs/conf.py000066400000000000000000000160321347767355200170170ustar00rootroot00000000000000# django-guardian documentation build configuration file, created by # sphinx-quickstart on Thu Feb 18 23:18:28 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os on_rtd = os.environ.get('READTHEDOCS', None) == 'True' # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__)))) os.environ['DJANGO_SETTINGS_MODULE'] = 'guardian.testapp.testsettings' guardian = __import__('guardian') import django django.setup() # -- General configuration ----------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'exts'] try: import rst2pdf if rst2pdf.version >= '0.16': extensions.append('rst2pdf.pdfbuilder') except ImportError: print("[NOTE] In order to build PDF you need rst2pdf with version >=0.16") autoclass_content = "both" # Add any paths that contain templates here, relative to this directory. templates_path = ['templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'django-guardian' copyright = u'Lukasz Balcerzak' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = guardian.__version__ # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. #html_theme = 'default' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'guardiandoc' # -- Options for LaTeX output -------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'guardian.tex', u'guardian Documentation', u'Lukasz Balcerzak', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True pdf_documents = [ ('index', u'django-guardian', u'Documentation for django-guardian', u'Lukasz Balcerzak'), ] pdf_stylesheets = ['sphinx', 'kerning', 'a4'] pdf_break_level = 2 pdf_inline_footnotes = True #pdf_extensions = ['vectorpdf', 'dotted_toc'] django-guardian-2.0.0/docs/configuration.rst000066400000000000000000000110171347767355200211170ustar00rootroot00000000000000.. _configuration: Configuration ============= After :ref:`installation ` we can prepare our project for object permissions handling. In a settings module we need to add guardian to ``INSTALLED_APPS``:: INSTALLED_APPS = ( # ... 'guardian', ) and hook guardian's authentication backend:: AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', # this is default 'guardian.backends.ObjectPermissionBackend', ) .. note:: Once project is configured to work with ``django-guardian``, calling ``migrate`` management command would create ``User`` instance for anonymous user support (with name of ``AnonymousUser``). .. note:: The Guardian anonymous user is different to the Django Anonymous user. The Django Anonymous user does not have an entry in the database, however the Guardian anonymous user does. This means that the following code will return an unexpected result: .. code-block:: python from django.contrib.auth import get_user_model User = get_user_model() anon = User.get_anonymous() anon.is_anonymous # returns False We can change id to whatever we like. Project should be now ready to use object permissions. Optional settings ================= Guardian has following, optional configuration variables: .. setting:: GUARDIAN_RAISE_403 GUARDIAN_RAISE_403 ------------------ .. versionadded:: 1.0.4 If set to ``True``, guardian would raise ``django.core.exceptions.PermissionDenied`` error instead of returning empty ``django.http.HttpResponseForbidden``. .. warning:: Remember that you cannot use both :setting:`GUARDIAN_RENDER_403` **AND** :setting:`GUARDIAN_RAISE_403` - if both are set to ``True``, ``django.core.exceptions.ImproperlyConfigured`` would be raised. .. setting:: GUARDIAN_RENDER_403 GUARDIAN_RENDER_403 ------------------- .. versionadded:: 1.0.4 If set to ``True``, guardian would try to render 403 response rather than return contentless ``django.http.HttpResponseForbidden``. Would use template pointed by :setting:`GUARDIAN_TEMPLATE_403` to do that. Default is ``False``. .. warning:: Remember that you cannot use both :setting:`GUARDIAN_RENDER_403` **AND** :setting:`GUARDIAN_RAISE_403` - if both are set to ``True``, ``django.core.exceptions.ImproperlyConfigured`` would be raised. .. setting:: GUARDIAN_TEMPLATE_403 GUARDIAN_TEMPLATE_403 --------------------- .. versionadded:: 1.0.4 Tells parts of guardian what template to use for responses with status code ``403`` (i.e. :ref:`api-decorators-permission_required`). Defaults to ``403.html``. .. setting:: ANONYMOUS_USER_NAME ANONYMOUS_USER_NAME ------------------- .. versionadded:: 1.4.2 This is the username of the anonymous user. Used to create the anonymous user and subsequently fetch the anonymous user as required. If ``ANONYMOUS_USER_NAME`` is set to ``None``, anonymous user object permissions-are disabled. You may need to choose this option if creating an ``User`` object-to represent anonymous users would be problematic in your environment. Defaults to ``"AnonymousUser"``. .. seealso:: https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#substituting-a-custom-user-model .. setting:: GUARDIAN_GET_INIT_ANONYMOUS_USER GUARDIAN_GET_INIT_ANONYMOUS_USER -------------------------------- .. versionadded:: 1.2 Guardian supports object level permissions for anonymous users, however when in our project we use custom User model, default function might fail. This can lead to issues as ``guardian`` tries to create anonymous user after each ``migrate`` call. Object that is going to be created is retrieved using function pointed by this setting. Once retrieved, ``save`` method would be called on that instance. Defaults to ``"guardian.management.get_init_anonymous_user"``. .. seealso:: :ref:`custom-user-model-anonymous` GUARDIAN_GET_CONTENT_TYPE ------------------------- .. versionadded:: 1.5 Guardian allows applications to supply a custom function to retrieve the content type from objects and models. This is useful when a class or class hierarchy uses the ``ContentType`` framework in an non-standard way. Most applications will not have to change this setting. As an example, when using ``django-polymorphic`` it's useful to use a permission on the base model which applies to all child models. In this case, the custom function would return the ``ContentType`` of the base class for polymorphic models and the regular model ``ContentType`` for non-polymorphic classes. Defaults to ``"guardian.ctypes.get_default_content_type"``. django-guardian-2.0.0/docs/develop/000077500000000000000000000000001347767355200171545ustar00rootroot00000000000000django-guardian-2.0.0/docs/develop/changes.rst000066400000000000000000000000771347767355200213220ustar00rootroot00000000000000.. _changes: Changelog --------- .. include:: ../../CHANGES django-guardian-2.0.0/docs/develop/index.rst000066400000000000000000000001621347767355200210140ustar00rootroot00000000000000.. _develop: Development =========== .. toctree:: overview testing supported-versions changes django-guardian-2.0.0/docs/develop/overview.rst000066400000000000000000000065661347767355200215710ustar00rootroot00000000000000.. _dev_overview: Overview ======== Here we describe the development process overview. It's in F.A.Q. format to make it simple. Why devel is default branch? ---------------------------- Since version 1.2 we try to make ``master`` in a production-ready state. It does NOT mean it is production ready, but it SHOULD be. In example, tests at ``master`` should always pass. Actually, whole tox suite should pass. And it's test coverage should be at 100% level. ``devel`` branch, on the other hand, can break. It shouldn't but it is acceptable. As a user, you should NEVER use non-master branches at production. All the changes are pushed from ``devel`` to ``master`` before next release. It might happen more frequently. How to file a ticket? --------------------- Just go to https://github.com/django-guardian/django-guardian/issues and create new one. How do I get involved? ---------------------- It's simple! If you want to fix a bug, extend documentation or whatever you think is appropriate for the project and involves changes, just fork the project at github (https://github.com/django-guardian/django-guardian), create a separate branch, hack on it, publish changes at your fork and create a pull request. Here is a quick how to: 1. Fork a project: https://github.com/django-guardian/django-guardian/fork 2. Checkout project to your local machine:: $ git clone git@github.com:YOUR_NAME/django-guardian.git 3. Create a new branch with name describing change you are going to work on:: $ git checkout -b bugfix/support-for-custom-model 4. Perform changes at newly created branch. Remember to include tests (if this is code related change) and run test suite. See :ref:`running tests documentation `. Also, remember to add yourself to the ``AUTHORS`` file. 5. (Optional) Squash commits. If you have multiple commits and it doesn't make much sense to have them separated (and it usually makes little sense), please consider merging all changes into single commit. Please see https://help.github.com/articles/interactive-rebase if you need help with that. 6. Publish changes:: $ git push origin YOUR_BRANCH_NAME 6. Create a Pull Request (https://help.github.com/articles/creating-a-pull-request). Usually it's as simple as opening up https://github.com/YOUR_NAME/django-guardian and clicking on review button for newly created branch. There you can make final review of your changes and if everything seems fine, create a Pull Request. Why my issue/pull request was closed? ------------------------------------- We usually put an explonation while we close issue or PR. It might be for various reasons, i.e. there were no reply for over a month after our last comment, there were no tests for the changes etc. How to do a new release? ---------------------------- To enroll a new relase you should perform the following task: * Ensure file ``CHANGES`` reflects all important changes. * Ensure file ``CHANGES`` includes a new version identifier and current release date. * Execute ``bumpversion patch`` (or accordinly - see `Semantic Versioning 2.0 `_ ) to reflects changes in codebase. * Commit changes of codebase, e.g. ``git commit -m "Release 1.4.8" -a``. * Tag a new release, e.g. ``git tag "v1.4.8"``. * Push new tag to repo - ``git push origin --tags``. * Push a new release to PyPI - ``python3 setup.py sdist bdist_wheel upload``. django-guardian-2.0.0/docs/develop/supported-versions.rst000066400000000000000000000003671347767355200236070ustar00rootroot00000000000000.. _supported-versions: Supported versions ================== ``django-guardian`` supports Python 3.5+ and Django 2.1+. Rules ----- * We support Python 3.5+. * We support Django 2.1+. This is due to many simplifications in code we could do. django-guardian-2.0.0/docs/develop/testing.rst000066400000000000000000000102401347767355200213600ustar00rootroot00000000000000.. _testing: Testing ======= Introduction ------------ ``django-guardian`` is extending capabilities of Django's authorization facilities and as so, it changes it's security somehow. It is extremaly important to provide as simplest :ref:`api` as possible. According to OWASP_, `broken authentication `_ is one of most commonly security issue exposed in web applications. Having this on mind we tried to build small set of necessary functions and created a lot of testing scenarios. Neverteless, if anyone would found a bug in this application, please take a minute and file it at `issue-tracker`_. Moreover, if someone would spot a *security hole* (a bug that might affect security of systems that use ``django-guardian`` as permission management library), please **DO NOT** create a public issue but contact me directly (lukaszbalcerzak@gmail.com). Running tests ------------- Tests are run by Django's buildin test runner. To call it simply run:: $ python setup.py test or inside a project with ``guardian`` set at ``INSTALLED_APPS``:: $ python manage.py test guardian or using the bundled ``testapp`` project:: $ python manage.py test Coverage support ---------------- Coverage_ is a tool for measuring code coverage of Python programs. It is great for tests and we use it as a backup - we try to cover 100% of the code used by ``django-guardian``. This of course does *NOT* mean that if all of the codebase is covered by tests we can be sure there is no bug (as specification of almost all applications requries some unique scenarios to be tested). On the other hand it definitely helps to track missing parts. To run tests with coverage_ support and show the report after we have provided simple bash script which can by called by running:: $ ./run_test_and_report.sh Result should be somehow similar to following:: (...) ................................................ ---------------------------------------------------------------------- Ran 48 tests in 2.516s OK Destroying test database 'default'... Name Stmts Exec Cover Missing ------------------------------------------------------------------- guardian/__init__ 4 4 100% guardian/backends 20 20 100% guardian/conf/__init__ 1 1 100% guardian/core 29 29 100% guardian/exceptions 8 8 100% guardian/management/__init__ 10 10 100% guardian/managers 40 40 100% guardian/models 36 36 100% guardian/shortcuts 30 30 100% guardian/templatetags/__init__ 1 1 100% guardian/templatetags/guardian_tags 39 39 100% guardian/utils 13 13 100% ------------------------------------------------------------------- TOTAL 231 231 100% Tox --- .. versionadded:: 1.0.4 We also started using tox_ to ensure ``django-guardian``'s tests would pass on all supported Python and Django versions (see :ref:`supported-versions`). To use it, simply install ``tox``:: pip install tox and run it within ``django-guardian`` checkout directory:: tox First time should take some time (it needs to create separate virtual environments and pull dependencies) but would ensure everything is fine. Travis CI --------- .. versionadded:: 1.0.4 .. image:: https://secure.travis-ci.org/django-guardian/django-guardian.png?branch=master :target: http://travis-ci.org/django-guardian/django-guardian Recently we have added support for Travis_, continuous integration server so it is even more easy to follow if test fails with new commits: http://travis-ci.org/#!/lukaszb/django-guardian. .. _owasp: http://www.owasp.org/ .. _issue-tracker: http://github.com/lukaszb/django-guardian .. _coverage: http://nedbatchelder.com/code/coverage/ .. _tox: http://pypi.python.org/pypi/tox .. _travis: http://travis-ci.org/ django-guardian-2.0.0/docs/exts.py000066400000000000000000000021011347767355200170450ustar00rootroot00000000000000 def setup(app): app.add_crossref_type( directivename="admin", rolename="admin", indextemplate="pair: %s; admin", ) app.add_crossref_type( directivename="command", rolename="command", indextemplate="pair: %s; command", ) app.add_crossref_type( directivename="form", rolename="form", indextemplate="pair: %s; form", ) app.add_crossref_type( directivename="manager", rolename="manager", indextemplate="pair: %s; manager", ) app.add_crossref_type( directivename="mixin", rolename="mixin", indextemplate="pair: %s; mixin", ) app.add_crossref_type( directivename="model", rolename="model", indextemplate="pair: %s; model", ) app.add_crossref_type( directivename="setting", rolename="setting", indextemplate="pair: %s; setting", ) app.add_crossref_type( directivename="shortcut", rolename="shortcut", indextemplate="pair: %s; shortcut", ) django-guardian-2.0.0/docs/index.rst000066400000000000000000000011561347767355200173620ustar00rootroot00000000000000.. _index: ===================================================== django-guardian - per object permissions for Django ===================================================== :Date: |today| :Version: |version| **Documentation**: .. image:: https://secure.travis-ci.org/django-guardian/django-guardian.png?branch=master :target: http://travis-ci.org/django-guardian/django-guardian .. toctree:: :maxdepth: 2 overview installation configuration userguide/index api/index develop/index license Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` django-guardian-2.0.0/docs/installation.rst000066400000000000000000000011511347767355200207470ustar00rootroot00000000000000.. _installation: Installation ============ This application requires Django_ 1.11 or higher and it is the only prerequisite before ``django-guardian`` may be used. In order to install ``django-guardian`` simply use ``pip``:: pip install django-guardian or ``easy_install``:: easy_install django-guardian This would be enough to run ``django-guardian``. However, in order to run tests or boundled example application, there are some other requirements. See more details about the topics: - :ref:`Testing ` - :ref:`Example project ` .. _django: http://www.djangoproject.com/ django-guardian-2.0.0/docs/license.rst000066400000000000000000000000771347767355200176760ustar00rootroot00000000000000.. _license: License ======= .. literalinclude:: ../LICENSE django-guardian-2.0.0/docs/make.bat000066400000000000000000000063241347767355200171300ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation set SPHINXBUILD=sphinx-build set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. pdf to make PDF document echo. dirhtml to make HTML files named index.html in directories echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-guardian.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-guardian.ghc goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "pdf" ( %SPHINXBUILD% -b pdf %ALLSPHINXOPTS% %BUILDDIR%/pdf echo. echo.Build finished. The PDF files are in %BUILDDIR%/pdf. goto end ) :end django-guardian-2.0.0/docs/overview.rst000066400000000000000000000023641347767355200201230ustar00rootroot00000000000000.. _overview: Overview ======== ``django-guardian`` is an implementation of object permissions for Django_ providing an extra *authentication backend*. Features -------- - Object permissions for Django_ - AnonymousUser support - High level API - Heavily tested - Django's admin integration - Decorators Incoming -------- - Admin templates for grappelli_ Source and issue tracker ------------------------ Sources are available at `issue-tracker`_. You may also file a bug there. Alternate projects ------------------ Django_ still *only* has the foundation for object permissions [1]_ and ``django-guardian`` makes use of new facilities and it is based on them. There are some other pluggable applications which do *NOT* require Django_ version 1.2+. For instance, `django-authority`_ or `django-permissions`_ are great options available. .. _django: http://www.djangoproject.com/ .. _django-authority: https://github.com/jazzband/django-authority .. _django-permissions: https://github.com/lambdalisue/django-permission .. _issue-tracker: http://github.com/lukaszb/django-guardian .. _grappelli: https://github.com/sehmaschine/django-grappelli .. [1] See http://docs.djangoproject.com/en/1.2/topics/auth/#handling-object-permissions for more detail. django-guardian-2.0.0/docs/userguide/000077500000000000000000000000001347767355200175125ustar00rootroot00000000000000django-guardian-2.0.0/docs/userguide/admin-integration.rst000066400000000000000000000052301347767355200236550ustar00rootroot00000000000000.. _admin-integration: Admin integration ================= Django comes with excellent and widely used *Admin* application. Basically, it provides content management for Django applications. User with access to admin panel can manage users, groups, permissions and other data provided by system. ``django-guardian`` comes with simple object permissions management integration for Django's admin application. Usage ----- It is very easy to use admin integration. Simply use :admin:`GuardedModelAdmin` instead of standard ``django.contrib.admin.ModelAdmin`` class for registering models within the admin. In example, look at following model: .. code-block:: python from django.db import models class Post(models.Model): title = models.CharField('title', max_length=64) slug = models.SlugField(max_length=64) content = models.TextField('content') created_at = models.DateTimeField(auto_now_add=True, db_index=True) class Meta: permissions = ( ('hide_post', 'Can hide post'), ) get_latest_by = 'created_at' def __str__(self): return self.title def get_absolute_url(self): return {'post_slug': self.slug} We want to register ``Post`` model within admin application. Normally, we would do this as follows within ``admin.py`` file of our application: .. code-block:: python from django.contrib import admin from posts.models import Post class PostAdmin(admin.ModelAdmin): prepopulated_fields = {"slug": ("title",)} list_display = ('title', 'slug', 'created_at') search_fields = ('title', 'content') ordering = ('-created_at',) date_hierarchy = 'created_at' admin.site.register(Post, PostAdmin) If we would like to add object permissions management for ``Post`` model we would need to change ``PostAdmin`` base class into ``GuardedModelAdmin``. Our code could look as follows: .. code-block:: python from django.contrib import admin from posts.models import Post from guardian.admin import GuardedModelAdmin class PostAdmin(GuardedModelAdmin): prepopulated_fields = {"slug": ("title",)} list_display = ('title', 'slug', 'created_at') search_fields = ('title', 'content') ordering = ('-created_at',) date_hierarchy = 'created_at' admin.site.register(Post, PostAdmin) And thats it. We can now navigate to **change** post page and just next to the *history* link we can click *Object permissions* button to manage row level permissions. .. note:: Example above is shipped with ``django-guardian`` package with the example project. django-guardian-2.0.0/docs/userguide/assign.rst000066400000000000000000000132721347767355200215350ustar00rootroot00000000000000.. _assign: Assign object permissions ========================= Assigning object permissions should be very simple once permissions are created for models. Prepare permissions ------------------- Let's assume we have following model: .. code-block:: python class Task(models.Model): summary = models.CharField(max_length=32) content = models.TextField() reported_by = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) ... and we want to be able to set custom permission *assign_task*. We let Django know to do so by adding ``permissions`` tuple to ``Meta`` class and our final model could look like: .. code-block:: python class Task(models.Model): summary = models.CharField(max_length=32) content = models.TextField() reported_by = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) class Meta: permissions = ( ('assign_task', 'Assign task'), ) After we call management commands ``makemigrations`` and ``migrate`` our *assign_task* permission would be added to default set of permissions. .. note:: By default, Django adds 3 permissions for each registered model: - *add_modelname* - *change_modelname* - *delete_modelname* - *view_modelname* (since Django 2.1) (where *modelname* is a simplified name of our model's class). See https://docs.djangoproject.com/en/dev/topics/auth/default/#default-permissions for more detail. .. note:: When upgrading to Django 2.1 custom created *view_modelname* permissions clash with the newly built-in ones. The simplest way to fix this is to add ``default_permissions = ('add', 'change', 'delete')`` to ``Meta``. There is nothing new here since creation of permissions is `handled by django `_. Now we can move to :ref:`assigning object permissions `. .. _assign-obj-perms: Assign object permissions ------------------------- We can assign permissions for any user/group and object pairs using same, convenient function: :func:`guardian.shortcuts.assign_perm`. For user ~~~~~~~~ Continuing our example we now can allow Joe user to assign some task: .. code-block:: python >>> from django.contrib.auth.models import User >>> boss = User.objects.create(username='Big Boss') >>> joe = User.objects.create(username='joe') >>> task = Task.objects.create(summary='Some job', content='', reported_by=boss) >>> joe.has_perm('assign_task', task) False Well, not so fast Joe, let us create an object permission finally: .. code-block:: python >>> from guardian.shortcuts import assign_perm >>> assign_perm('assign_task', joe, task) >>> joe.has_perm('assign_task', task) True For group ~~~~~~~~~ This case doesn't really differ from user permissions assignment. The only difference is we have to pass ``Group`` instance rather than ``User``. .. code-block:: python >>> from django.contrib.auth.models import Group >>> group = Group.objects.create(name='employees') >>> assign_perm('change_task', group, task) >>> joe.has_perm('change_task', task) False >>> # Well, joe is not yet within an *employees* group >>> joe.groups.add(group) >>> joe.has_perm('change_task', task) True Another example: .. code-block:: python >>> from django.contrib.auth.models import User, Group >>> from guardian.shortcuts import assign_perm # fictional companies >>> companyA = Company.objects.create(name="Company A") >>> companyB = Company.objects.create(name="Company B") # create groups >>> companyUserGroupA = Group.objects.create(name="Company User Group A") >>> companyUserGroupB = Group.objects.create(name="Company User Group B") # assign object specific permissions to groups >>> assign_perm('change_company', companyUserGroupA, companyA) >>> assign_perm('change_company', companyUserGroupB, companyB) # create user and add it to one group for testing >>> userA = User.objects.create(username="User A") >>> userA.groups.add(companyUserGroupA) >>> userA.has_perm('change_company', companyA) True >>> userA.has_perm('change_company', companyB) False >>> userB = User.objects.create(username="User B") >>> userB.groups.add(companyUserGroupB) >>> userB.has_perm('change_company', companyA) False >>> userB.has_perm('change_company', companyB) True Assigning Permissions inside Signals ------------------------------------ Note that the Anonymous User is created before the Permissions are created. This may result in Django signals, e.g. ``post_save`` being sent before the Permissions are created. You will need to take this into an account when processing the signal. .. code-block:: python @receiver(post_save, sender=User) def user_post_save(sender, **kwargs): """ Create a Profile instance for all newly created User instances. We only run on user creation to avoid having to check for existence on each call to User.save. """ user, created = kwargs["instance"], kwargs["created"] if created and user.username != settings.ANONYMOUS_USER_NAME: from profiles.models import Profile profile = Profile.objects.create(pk=user.pk, user=user, creator=user) assign_perm("change_user", user, user) assign_perm("change_profile", user, profile) The check for ``user.username != settings.ANONYMOUS_USER_NAME`` is required otherwise the ``assign_perm`` calls will occur when the Anonymous User is created, however before there are any permissions available. django-guardian-2.0.0/docs/userguide/caveats.rst000066400000000000000000000073641347767355200217040ustar00rootroot00000000000000.. _caveats: Caveats ======= Orphaned object permissions --------------------------- Note the following does not apply if using direct foreign keys, as documented in :ref:`performance-direct-fk`. Permissions, including so called *per object permissions*, are sometimes tricky to manage. One case is how we can manage permissions that are no longer used. Normally, there should be no problems, however with some particular setup it is possible to reuse primary keys of database models which were used in the past once. We will not answer how bad such situation can be - instead we will try to cover how we can deal with this. Let's imagine our table has primary key to the filesystem path. We have a record with pk equal to ``/home/www/joe.config``. User *jane* has read access to joe's configuration and we store that information in database by creating guardian's object permissions. Now, *joe* user removes account from our site and another user creates account with *joe* as username. The problem is that if we haven't removed object permissions explicitly in the process of first *joe* account removal, *jane* still has read permissions for *joe's* configuration file - but this is another user. There is no easy way to deal with orphaned permissions as they are not foreign keyed with objects directly. Even if they would, there are some database engines - or *ON DELETE* rules - which restricts removal of related objects. .. important:: It is **extremely** important to remove :model:`UserObjectPermission` and :model:`GroupObjectPermission` as we delete objects for which permissions are defined. Guardian comes with utility function which tries to help to remove orphaned object permissions. Remember - those are only helpers. Applications should remove those object permissions explicitly by itself. Taking our previous example, our application should remove user object for *joe*, however, permisions for *joe* user assigned to *jane* would **NOT** be removed. In this case, it would be very easy to remove user/group object permissions if we connect proper action with proper signal. This could be achieved by following snippet:: from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.db.models.signals import pre_delete from guardian.models import User from guardian.models import UserObjectPermission from guardian.models import GroupObjectPermission def remove_obj_perms_connected_with_user(sender, instance, **kwargs): filters = Q(content_type=ContentType.objects.get_for_model(instance), object_pk=instance.pk) UserObjectPermission.objects.filter(filters).delete() GroupObjectPermission.objects.filter(filters).delete() pre_delete.connect(remove_obj_perms_connected_with_user, sender=User) This signal handler would remove all object permissions connected with user just before user is actually removed. If we forgot to add such handlers, we may still remove orphaned object permissions by using :command:`clean_orphan_obj_perms` command. If our application uses celery_, it is also very easy to remove orphaned permissions periodically with :func:`guardian.utils.clean_orphan_obj_perms` function. We would still **strongly** advise to remove orphaned object permissions explicitly (i.e. at view that confirms object removal or using signals as described above). .. seealso:: - :func:`guardian.utils.clean_orphan_obj_perms` - :command:`clean_orphan_obj_perms` .. _celery: http://www.celeryproject.org/ Using multiple databases ------------------------ This is not supported at present time due to a Django bug. See 288_ and 16281_. .. _288: https://github.com/django-guardian/django-guardian/issues/288 .. _16281: https://code.djangoproject.com/ticket/16281 django-guardian-2.0.0/docs/userguide/check.rst000066400000000000000000000152071347767355200213260ustar00rootroot00000000000000.. _check: Check object permissions ======================== Once we have :ref:`assigned some permissions `, we can get into detail about verifying permissions of a user or group. Standard way ------------ Normally to check if Joe is permitted to change ``Site`` objects we call ``has_perm`` method on an ``User`` instance:: >>> joe.has_perm('sites.change_site') False And for a specific ``Site`` instance we do the same but we pass ``site`` as additional argument:: >>> site = Site.objects.get_current() >>> joe.has_perm('sites.change_site', site) False Let's assign permission and check again:: >>> from guardian.shortcuts import assign_perm >>> assign_perm('sites.change_site', joe, site) >>> joe = User.objects.get(username='joe') >>> joe.has_perm('sites.change_site', site) True This uses the backend we have specified at settings module (see :ref:`configuration`). More on the backend can be found at :class:`Backend's API `. Inside views ------------ Aside from the standard ``has_perm`` method, ``django-guardian`` provides some useful helpers for object permission checks. get_perms ~~~~~~~~~ To check permissions we can use a quick-and-dirty shortcut:: >>> from guardian.shortcuts import get_perms >>> >>> joe = User.objects.get(username='joe') >>> site = Site.objects.get_current() >>> >>> 'change_site' in get_perms(joe, site) True It is probably better to use standard ``has_perm`` method. But for ``Group`` instances it is not as easy and ``get_perms`` could be handy here as it accepts both ``User`` and ``Group`` instances. If we need to do some more work, we can use lower level ``ObjectPermissionChecker`` class which is described in the next section. There is also ``get_user_perms`` to get permissions assigned directly to the user (and not inherited from its superuser status or group membership. Similarly, ``get_group_perms`` returns only permissions which are inferred through user's group membership. ``get_user_perms`` and ``get_group_perms`` are useful when you care what permissions user has assigned, while ``has_perm`` is useful when you care about user's effective permissions. get_objects_for_user ~~~~~~~~~~~~~~~~~~~~ Sometimes there is a need to extract list of objects based on particular user, type of the object and provided permissions. For instance, lets say there is a ``Project`` model at ``projects`` application with custom ``view_project`` permission. We want to show our users projects they can actually *view*. This could be easily achieved using :shortcut:`get_objects_for_user`: .. code-block:: python from django.shortcuts import render from django.template import RequestContext from projects.models import Project from guardian.shortcuts import get_objects_for_user def user_dashboard(request, template_name='projects/dashboard.html'): projects = get_objects_for_user(request.user, 'projects.view_project') return render(request, template_name, {'projects': projects}, RequestContext(request)) It is also possible to provide list of permissions rather than single string, own queryset (as ``klass`` argument) or control if result should be computed with (default) or without user's groups permissions. .. seealso:: Documentation for :shortcut:`get_objects_for_user` ObjectPermissionChecker ~~~~~~~~~~~~~~~~~~~~~~~ At the ``core`` module of ``django-guardian``, there is a :class:`guardian.core.ObjectPermissionChecker` which checks permission of user/group for specific object. It caches results so it may be used at part of codes where we check permissions more than once. Let's see it in action:: >>> joe = User.objects.get(username='joe') >>> site = Site.objects.get_current() >>> from guardian.core import ObjectPermissionChecker >>> checker = ObjectPermissionChecker(joe) # we can pass user or group >>> checker.has_perm('change_site', site) True >>> checker.has_perm('add_site', site) # no additional query made False >>> checker.get_perms(site) [u'change_site'] Using decorators ~~~~~~~~~~~~~~~~ Standard ``permission_required`` decorator doesn't allow to check for object permissions. ``django-guardian`` is shipped with two decorators which may be helpful for simple object permission checks but remember that those decorators hits database before decorated view is called - this means that if there is similar lookup made within a view then most probably one (or more, depending on lookups) extra database query would occur. Let's assume we pass ``'group_name'`` argument to our view function which returns form to edit the group. Moreover, we want to return 403 code if check fails. This can be simply achieved using ``permission_required_or_403`` decorator:: >>> joe = User.objects.get(username='joe') >>> foobars = Group.objects.create(name='foobars') >>> >>> from guardian.decorators import permission_required_or_403 >>> from django.http import HttpResponse >>> >>> @permission_required_or_403('auth.change_group', >>> (Group, 'name', 'group_name')) >>> def edit_group(request, group_name): >>> return HttpResponse('some form') >>> >>> from django.http import HttpRequest >>> request = HttpRequest() >>> request.user = joe >>> edit_group(request, group_name='foobars') >>> >>> joe.groups.add(foobars) >>> edit_group(request, group_name='foobars') >>> >>> from guardian.shortcuts import assign_perm >>> assign_perm('auth.change_group', joe, foobars) >>> >>> edit_group(request, group_name='foobars') >>> # Note that we now get normal HttpResponse, not forbidden More on decorators can be read at corresponding :ref:`API page `. .. note:: Overall idea of decorators' lookups was taken from `django-authority`_ and all credits go to it's creator, Jannis Leidel. Inside templates ---------------- ``django-guardian`` comes with special template tag :func:`guardian.templatetags.guardian_tags.get_obj_perms` which can store object permissions for a given user/group and instance pair. In order to use it we need to put following inside a template:: {% load guardian_tags %} get_obj_perms ~~~~~~~~~~~~~ .. autofunction:: guardian.templatetags.guardian_tags.get_obj_perms :noindex: .. _django-authority: http://bitbucket.org/jezdez/django-authority/ django-guardian-2.0.0/docs/userguide/custom-user-model.rst000066400000000000000000000052011347767355200236260ustar00rootroot00000000000000.. _custom-user-model: Custom User model ================= .. versionadded:: 1.1 Django comes with the ability to customize default ``auth.User`` model - either by subclassing ``AbstractUser`` or defining very own class. This can be very powerful, it must be done with caution, though. Basically, if we subclass ``AbstractUser`` or define many-to-many relation with ``auth.Group`` (and give reverse relate name **groups**) we should be fine. By default django-guardian monkey patches the user model to add some needed functionality. This can result in errors if guardian is imported into the models.py of the same app where the custom user model lives. To fix this, it is recommended to add the setting ``GUARDIAN_MONKEY_PATCH = False`` in your settings.py and subclass ``guardian.mixins.GuardianUserMixin`` in your custom user model. .. important:: ``django-guardian`` relies **heavily** on the ``auth.User`` model. Specifically it was build from the ground-up with relation between ``auth.User`` and ``auth.Group`` models. Retaining this relation is crucial for ``guardian`` - **without many to many User (custom or default) and auth.Group relation django-guardian will BREAK**. .. seealso:: Read more about customizing User model here: https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#substituting-a-custom-user-model. .. _custom-user-model-anonymous: Anonymous user creation ----------------------- It is also possible to override default behavior of how instance for anonymous user is created. In example, let's imagine we have our user model as follows:: from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): real_username = models.CharField(max_length=120, unique=True) birth_date = models.DateField() # field without default value USERNAME_FIELD = 'real_username' Note that there is a ``birth_date`` field defined at the model and it does not have a default value. It would fail to create anonymous user instance as default implementation cannot know anything about ``CustomUser`` model. In order to override the way anonymous instance is created we need to make :setting:`GUARDIAN_GET_INIT_ANONYMOUS_USER` pointing at our custom implementation. In example, let's define our init function:: import datetime def get_anonymous_user_instance(User): return User(real_username='Anonymous', birth_date=datetime.date(1970, 1, 1)) and put it at ``myapp/models.py``. Last step is to set proper configuration in our settings module:: GUARDIAN_GET_INIT_ANONYMOUS_USER = 'myapp.models.get_anonymous_user_instance' django-guardian-2.0.0/docs/userguide/example_project.rst000066400000000000000000000015321347767355200234260ustar00rootroot00000000000000.. _example-project: Example project =============== Example project should be bundled with archive and be available at ``example_project``. Before you can run it, some requirements have to be met. Those are easily installed using following command at example project's directory:: $ cd example_project $ pip install -r requirements.txt ``django-guardian`` from a directory above the ``example_project`` is automatically added to Python path at runtime. And last thing before we can run example project is to create sqlite database:: $ ./manage.py migrate Finally we can run dev server:: $ ./manage.py runserver You should also create a user who can login to the admin site:: $ ./manage.py createsuperuser Project is really basic and shows almost nothing but eventually it should expose some ``django-guardian`` functionality. django-guardian-2.0.0/docs/userguide/index.rst000066400000000000000000000003031347767355200213470ustar00rootroot00000000000000.. _guide: User Guide ========== .. toctree:: :maxdepth: 2 example_project assign check remove admin-integration custom-user-model performance caveats django-guardian-2.0.0/docs/userguide/performance.rst000066400000000000000000000121751347767355200225530ustar00rootroot00000000000000.. _performance: Performance tuning =================== It is important to remember that by default ``django-guardian`` uses generic foreign keys to retain relation with any Django model. For most cases, it's probably good enough, however if we have a lot of queries being spanned and our database seems to be choking it might be a good choice to use *direct* foreign keys. Let's start with quick overview of how generic solution work and then we will move on to the tuning part. Default, generic solution ------------------------- ``django-guardian`` comes with two models: :model:`UserObjectPermission` and :model:`GroupObjectPermission`. They both have same, generic way of pointing to other models: - ``content_type`` field telling what table (model class) target permission references to (``ContentType`` instance) - ``object_pk`` field storing value of target model instance primary key - ``content_object`` field being a ``GenericForeignKey``. Actually, it is not a foreign key in standard, relational database meaning - it is simply a proxy that can retrieve proper model instance being targeted by two previous fields .. seealso:: https://docs.djangoproject.com/en/1.4/ref/contrib/contenttypes/#generic-relations Let's consider following model: .. code-block:: python class Project(models.Model): name = models.CharField(max_length=128, unique=True) In order to add a *change_project* permission for *joe* user we would use :ref:`api-shortcuts-assign` shortcut: .. code-block:: python >>> from guardian.shortcuts import assign_perm >>> project = Project.objects.get(name='Foobar') >>> joe = User.objects.get(username='joe') >>> assign_perm('change_project', joe, project) What it really does is: create an instance of :model:`UserObjectPermission`. Something similar to: .. code-block:: python >>> content_type = ContentType.objects.get_for_model(Project) >>> perm = Permission.objects.get(content_type__app_label='app', ... codename='change_project') >>> UserObjectPermission.objects.create(user=joe, content_type=content_type, ... permission=perm, object_pk=project.pk) As there are no real foreign keys pointing at the target model, this solution might not be enough for all cases. For example, if we try to build an issues tracking service and we'd like to be able to support thousands of users and their project/tickets, object level permission checks can be slow with this generic solution. .. _performance-direct-fk: Direct foreign keys ------------------- .. versionadded:: 1.1 In order to make our permission checks faster we can use direct foreign key solution. It actually is very simple to setup - we need to declare two new models next to our ``Project`` model, one for ``User`` and one for ``Group`` models: .. code-block:: python from guardian.models import UserObjectPermissionBase from guardian.models import GroupObjectPermissionBase class Project(models.Model): name = models.CharField(max_length=128, unique=True) class ProjectUserObjectPermission(UserObjectPermissionBase): content_object = models.ForeignKey(Project, on_delete=models.CASCADE) class ProjectGroupObjectPermission(GroupObjectPermissionBase): content_object = models.ForeignKey(Project, on_delete=models.CASCADE) .. important:: Name of the ``ForeignKey`` field is important and it should be ``content_object`` as underlying queries depends on it. From now on, ``guardian`` will figure out that ``Project`` model has direct relation for user/group object permissions and will use those models. It is also possible to use only user or only group-based direct relation, however it is discouraged (it's not consistent and might be a quick road to hell from the maintainence point of view, especially). To temporarily disable the detection of this direct relation model, add ``enabled = False`` to the object permission model classes. This is useful to allow the ORM to create the tables for you and for you to migrate data from the generic model tables before using the direct models. .. note:: By defining direct relation models we can also tweak that object permission model, i.e. by adding some fields. .. _performance-prefetch: Prefetching permissions ----------------------- .. versionadded:: 1.4.3 Naively looping through objects and checking permissions on each one using ``has_perms`` results in a permissions lookup in the database for each object. Large numbers of objects therefore produce large numbers of database queries which can considerably slow down your app. To avoid this, create an ``ObjectPermissionChecker`` and use its ``prefetch_perms`` method before looping through the objects. This will do a single lookup for all the objects and cache the results. .. code-block:: python from guardian.core import ObjectPermissionChecker joe = User.objects.get(username='joe') projects = Project.objects.all() checker = ObjectPermissionChecker(joe) # Prefetch the permissions checker.prefetch_perms(projects) for project in projects: # No additional lookups needed to check permissions checker.has_perm('change_project', project) django-guardian-2.0.0/docs/userguide/remove.rst000066400000000000000000000012061347767355200215400ustar00rootroot00000000000000.. _remove: Remove object permissions ========================= Removing object permissions is as easy as assigning them. Just instead of :func:`guardian.shortcuts.assign` we would use :func:`guardian.shortcuts.remove_perm` function (it accepts same arguments). Example ------- Let's get back to our fellow Joe:: >>> site = Site.object.get_current() >>> joe.has_perm('change_site', site) True Now, simply remove this permission:: >>> from guardian.shortcuts import remove_perm >>> remove_perm('change_site', joe, site) >>> joe = User.objects.get(username='joe') >>> joe.has_perm('change_site', site) False django-guardian-2.0.0/docs/watch-docs.sh000077500000000000000000000005131347767355200201100ustar00rootroot00000000000000#!/bin/sh DIR="." CMD="make html" NOTIFY='growlnotify guardian Documentation -m Recreated' # Run command echo " => Documentation would be available at file://$PWD/build/html/index.html" $CMD $NOTIFY # Run command on .rst file change. watchmedo shell-command --pattern "*.rst" --recursive -w -c "clear && $CMD && $NOTIFY" $DIR django-guardian-2.0.0/example_project/000077500000000000000000000000001347767355200177475ustar00rootroot00000000000000django-guardian-2.0.0/example_project/__init__.py000066400000000000000000000000001347767355200220460ustar00rootroot00000000000000django-guardian-2.0.0/example_project/articles/000077500000000000000000000000001347767355200215555ustar00rootroot00000000000000django-guardian-2.0.0/example_project/articles/__init__.py000066400000000000000000000000001347767355200236540ustar00rootroot00000000000000django-guardian-2.0.0/example_project/articles/admin.py000066400000000000000000000004051347767355200232160ustar00rootroot00000000000000from django.contrib import admin from .models import Article class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'slug', 'created_at') list_filter = ('created_at',) search_fields = ('title',) admin.site.register(Article, ArticleAdmin) django-guardian-2.0.0/example_project/articles/apps.py000066400000000000000000000001331347767355200230670ustar00rootroot00000000000000from django.apps import AppConfig class ArticlesConfig(AppConfig): name = 'articles' django-guardian-2.0.0/example_project/articles/migrations/000077500000000000000000000000001347767355200237315ustar00rootroot00000000000000django-guardian-2.0.0/example_project/articles/migrations/0001_initial.py000066400000000000000000000053711347767355200264020ustar00rootroot00000000000000# Generated by Django 1.9.4 on 2016-06-22 05:23 from django.conf import settings from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): initial = True dependencies = [ ('auth', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Article', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=64, verbose_name='title')), ('slug', models.SlugField(max_length=64)), ('content', models.TextField(verbose_name='content')), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ], options={ 'get_latest_by': 'created_at', 'default_permissions': ('add', 'change', 'delete'), 'permissions': (('view_article', 'Can view article'),), }, ), migrations.CreateModel( name='ArticleGroupObjectPermission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')), ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')), ], options={ 'abstract': False, }, ), migrations.CreateModel( name='ArticleUserObjectPermission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')), ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'abstract': False, }, ), migrations.AlterUniqueTogether( name='articleuserobjectpermission', unique_together=set([('user', 'permission', 'content_object')]), ), migrations.AlterUniqueTogether( name='articlegroupobjectpermission', unique_together=set([('group', 'permission', 'content_object')]), ), ] django-guardian-2.0.0/example_project/articles/migrations/__init__.py000066400000000000000000000000001347767355200260300ustar00rootroot00000000000000django-guardian-2.0.0/example_project/articles/models.py000066400000000000000000000017621347767355200234200ustar00rootroot00000000000000from django.db import models from django.urls import reverse from guardian.models import GroupObjectPermissionBase, UserObjectPermissionBase class Article(models.Model): title = models.CharField('title', max_length=64) slug = models.SlugField(max_length=64) content = models.TextField('content') created_at = models.DateTimeField(auto_now_add=True, db_index=True) class Meta: default_permissions = ('add', 'change', 'delete') permissions = ( ('view_article', 'Can view article'), ) get_latest_by = 'created_at' def __str__(self): return self.title def get_absolute_url(self): return reverse('articles:details', kwargs={'slug': self.slug}) class ArticleUserObjectPermission(UserObjectPermissionBase): content_object = models.ForeignKey(Article, on_delete=models.CASCADE) class ArticleGroupObjectPermission(GroupObjectPermissionBase): content_object = models.ForeignKey(Article, on_delete=models.CASCADE) django-guardian-2.0.0/example_project/articles/templates/000077500000000000000000000000001347767355200235535ustar00rootroot00000000000000django-guardian-2.0.0/example_project/articles/templates/articles/000077500000000000000000000000001347767355200253615ustar00rootroot00000000000000django-guardian-2.0.0/example_project/articles/templates/articles/article_confirm_delete.html000066400000000000000000000004571347767355200327370ustar00rootroot00000000000000{% extends "base.html" %} {% load guardian_tags %} {% block content %}

Are you sure do you want to delete "{{object}}"?

{% csrf_token %}
{% endblock %} django-guardian-2.0.0/example_project/articles/templates/articles/article_detail.html000066400000000000000000000014131347767355200312130ustar00rootroot00000000000000{% extends "base.html" %} {% load guardian_tags %} {% block content %} {% get_obj_perms request.user for object as 'object_perms' %}
{% if "change_article" in object_perms %} Update {% endif %} {% if "delete_article" in object_perms %} Delete {% endif %}

{{ object.title }}

at {{ object.created_at }}

{{ object.content|safe }}
{% endblock %} django-guardian-2.0.0/example_project/articles/templates/articles/article_form.html000066400000000000000000000010501347767355200307110ustar00rootroot00000000000000{% extends "base.html" %} {% load guardian_tags %} {% block content %}

{% if object %} Back to "{{ object.title }}" {% else %} Back to article list {% endif %}

{% csrf_token %} {{ form.as_p }}
{% endblock %} django-guardian-2.0.0/example_project/articles/templates/articles/article_list.html000066400000000000000000000011271347767355200307260ustar00rootroot00000000000000{% extends "base.html" %} {% block content %}

Articles

Add article
{% for object in object_list %}

{{ object.title }}

at {{ object.created_at }}

{{ object.content|safe }}
{% endfor %}
{% endblock %} django-guardian-2.0.0/example_project/articles/tests.py000066400000000000000000000075231347767355200233000ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.test import TestCase from django.test.client import RequestFactory from guardian.shortcuts import assign_perm from articles.models import Article from articles.views import (ArticleCreateView, ArticleDeleteView, ArticleDetailView, ArticleListView, ArticleUpdateView) class ViewTestCase(TestCase): def setUp(self): self.article = Article.objects.create(title='foo-title', slug='foo-slug', content='bar-content') self.factory = RequestFactory() self.user = get_user_model().objects.create_user( 'joe', 'joe@doe.com', 'doe') self.client.login(username='joe', password='doe') def test_list_permitted(self): request = self.factory.get('/') request.user = self.user assign_perm('articles.view_article', self.user, self.article) assign_perm('articles.delete_article', self.user, self.article) view = ArticleListView.as_view() response = view(request) response.render() self.assertContains(response, 'foo-title') def test_list_denied(self): request = self.factory.get('/') request.user = self.user view = ArticleListView.as_view() response = view(request) response.render() self.assertNotContains(response, 'foo-title') def test_create_permitted(self): request = self.factory.get('/~create') request.user = self.user assign_perm('articles.add_article', self.user) view = ArticleCreateView.as_view() response = view(request) self.assertEqual(response.status_code, 200) def test_create_denied(self): request = self.factory.get('/~create') request.user = self.user view = ArticleCreateView.as_view() response = view(request) self.assertEqual(response.status_code, 302) def test_detail_permitted(self): request = self.factory.get('/foo/') request.user = self.user assign_perm('articles.view_article', self.user, self.article) view = ArticleDetailView.as_view() response = view(request, slug='foo-slug') self.assertEqual(response.status_code, 200) def test_detail_denied(self): request = self.factory.get('/foo/') request.user = self.user view = ArticleDetailView.as_view() response = view(request, slug='foo-slug') self.assertEqual(response.status_code, 302) def test_update_permitted(self): request = self.factory.get('/') request.user = self.user assign_perm('articles.view_article', self.user, self.article) assign_perm('articles.change_article', self.user, self.article) view = ArticleUpdateView.as_view() response = view(request, slug='foo-slug') self.assertEqual(response.status_code, 200) def test_update_denied(self): request = self.factory.get('/') request.user = self.user view = ArticleUpdateView.as_view() response = view(request, slug='foo-slug') self.assertEqual(response.status_code, 302) def test_delete_permitted(self): request = self.factory.get('/foo-slug/~delete') request.user = self.user assign_perm('articles.view_article', self.user, self.article) assign_perm('articles.delete_article', self.user, self.article) view = ArticleDeleteView.as_view() response = view(request, slug='foo-slug') self.assertEqual(response.status_code, 200) def test_delete_denied(self): request = self.factory.get('/foo/~delete') request.user = self.user view = ArticleDeleteView.as_view() response = view(request, slug='foo-slug') self.assertEqual(response.status_code, 302) django-guardian-2.0.0/example_project/articles/urls.py000066400000000000000000000010251347767355200231120ustar00rootroot00000000000000from django.conf.urls import url from . import views app_name = 'articles' urlpatterns = [ url(r'^$', views.ArticleListView.as_view(), name="list"), url(r'^~create$', views.ArticleCreateView.as_view(), name="create"), url(r'^(?P[\w-]+)$', views.ArticleDetailView.as_view(), name="details"), url(r'^(?P[\w-]+)/~update$', views.ArticleUpdateView.as_view(), name="update"), url(r'^(?P[\w-]+)/~delete$', views.ArticleDeleteView.as_view(), name="delete"), ] django-guardian-2.0.0/example_project/articles/views.py000066400000000000000000000027001347767355200232630ustar00rootroot00000000000000from django.urls import reverse_lazy from django.views.generic import (CreateView, DeleteView, DetailView, ListView, UpdateView) from guardian.mixins import PermissionRequiredMixin, PermissionListMixin from guardian.shortcuts import assign_perm from articles.models import Article class ArticleListView(PermissionListMixin, ListView): model = Article permission_required = ['view_article', ] class ArticleDetailView(PermissionRequiredMixin, DetailView): model = Article permission_required = ['view_article'] class ArticleCreateView(PermissionRequiredMixin, CreateView): model = Article permission_object = None permission_required = ['articles.add_article'] fields = ['title', 'slug', 'content'] def form_valid(self, *args, **kwargs): resp = super().form_valid(*args, **kwargs) assign_perm('view_article', self.request.user, self.object) assign_perm('change_article', self.request.user, self.object) assign_perm('delete_article', self.request.user, self.object) return resp class ArticleUpdateView(PermissionRequiredMixin, UpdateView): model = Article permission_required = ['view_article', 'change_article'] fields = ['title', 'slug', 'content'] class ArticleDeleteView(PermissionRequiredMixin, DeleteView): model = Article success_url = reverse_lazy('articles:list') permission_required = ['view_article', 'delete_article'] django-guardian-2.0.0/example_project/core/000077500000000000000000000000001347767355200206775ustar00rootroot00000000000000django-guardian-2.0.0/example_project/core/__init__.py000066400000000000000000000000001347767355200227760ustar00rootroot00000000000000django-guardian-2.0.0/example_project/core/admin.py000066400000000000000000000002341347767355200223400ustar00rootroot00000000000000from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .models import CustomUser admin.site.register(CustomUser, UserAdmin) django-guardian-2.0.0/example_project/core/context_processors.py000066400000000000000000000001271347767355200252170ustar00rootroot00000000000000import guardian def version(request): return {'version': guardian.get_version()} django-guardian-2.0.0/example_project/core/migrations/000077500000000000000000000000001347767355200230535ustar00rootroot00000000000000django-guardian-2.0.0/example_project/core/migrations/0001_initial.py000066400000000000000000000066021347767355200255220ustar00rootroot00000000000000import django from django.db import migrations, models import django.utils.timezone import django.core.validators import django.contrib.auth.models class Migration(migrations.Migration): dependencies = [ ('auth', '0001_initial'), ] operations = [ migrations.CreateModel( name='CustomUser', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('password', models.CharField( max_length=128, verbose_name='password')), ('last_login', models.DateTimeField( null=True, verbose_name='last login', blank=True)), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('username', models.CharField(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')), ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), ('email', models.EmailField(max_length=254, verbose_name='email address', blank=True)), ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), ('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')), ('date_joined', models.DateTimeField( default=django.utils.timezone.now, verbose_name='date joined')), ('birth_date', models.DateField(null=True, blank=True)), ('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 their groups.', verbose_name='groups')), ('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')), ], options={ 'abstract': False, 'verbose_name': 'user', 'verbose_name_plural': 'users', }, managers=[ ('objects', django.contrib.auth.models.UserManager()), ], ), ] django-guardian-2.0.0/example_project/core/migrations/__init__.py000066400000000000000000000000001347767355200251520ustar00rootroot00000000000000django-guardian-2.0.0/example_project/core/models.py000066400000000000000000000005071347767355200225360ustar00rootroot00000000000000import datetime from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): birth_date = models.DateField(null=True, blank=True) def get_custom_anon_user(User): return User( username='AnonymousUser', birth_date=datetime.date(1410, 7, 15), ) django-guardian-2.0.0/example_project/manage.py000077500000000000000000000003631347767355200215560ustar00rootroot00000000000000#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-guardian-2.0.0/example_project/posts/000077500000000000000000000000001347767355200211175ustar00rootroot00000000000000django-guardian-2.0.0/example_project/posts/__init__.py000066400000000000000000000000001347767355200232160ustar00rootroot00000000000000django-guardian-2.0.0/example_project/posts/admin.py000066400000000000000000000006071347767355200225640ustar00rootroot00000000000000from django.contrib import admin from posts.models import Post from guardian.admin import GuardedModelAdmin class PostAdmin(GuardedModelAdmin): prepopulated_fields = {"slug": ("title",)} list_display = ('title', 'slug', 'created_at') search_fields = ('title', 'content') ordering = ('-created_at',) date_hierarchy = 'created_at' admin.site.register(Post, PostAdmin) django-guardian-2.0.0/example_project/posts/migrations/000077500000000000000000000000001347767355200232735ustar00rootroot00000000000000django-guardian-2.0.0/example_project/posts/migrations/0001_initial.py000066400000000000000000000016501347767355200257400ustar00rootroot00000000000000from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='Post', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(max_length=64, verbose_name=b'title')), ('slug', models.SlugField(max_length=64)), ('content', models.TextField(verbose_name=b'content')), ('created_at', models.DateTimeField( auto_now_add=True, db_index=True)), ], options={ 'get_latest_by': 'created_at', 'default_permissions': ('add', 'change', 'delete'), 'permissions': (('view_post', 'Can view post'),), }, ), ] django-guardian-2.0.0/example_project/posts/migrations/__init__.py000066400000000000000000000000001347767355200253720ustar00rootroot00000000000000django-guardian-2.0.0/example_project/posts/models.py000066400000000000000000000012151347767355200227530ustar00rootroot00000000000000from django.db import models from django.urls import reverse class Post(models.Model): title = models.CharField('title', max_length=64) slug = models.SlugField(max_length=64) content = models.TextField('content') created_at = models.DateTimeField(auto_now_add=True, db_index=True) class Meta: default_permissions = ('add', 'change', 'delete') permissions = ( ('view_post', 'Can view post'), ) get_latest_by = 'created_at' def __str__(self): return self.title def get_absolute_url(self): return reverse('posts_post_detail', args=(), kwargs={'slug': self.slug}) django-guardian-2.0.0/example_project/posts/templates/000077500000000000000000000000001347767355200231155ustar00rootroot00000000000000django-guardian-2.0.0/example_project/posts/templates/posts/000077500000000000000000000000001347767355200242655ustar00rootroot00000000000000django-guardian-2.0.0/example_project/posts/templates/posts/post_detail.html000066400000000000000000000026711347767355200274700ustar00rootroot00000000000000{% extends "base.html" %} {% load guardian_tags %} {% block content %}
Back to posts list

User permissions

{{ post.title }}

at {{ post.created_at }}

{{ post.content|safe }}
{% endblock %} django-guardian-2.0.0/example_project/posts/templates/posts/post_list.html000066400000000000000000000016701347767355200271770ustar00rootroot00000000000000{% extends "base.html" %} {% block content %}

Posts

Add post
{% for post in posts %}

{{ post.title }}

at {{ post.created_at }}

{{ post.content|safe }}
{% endfor %}
{% endblock %} django-guardian-2.0.0/example_project/posts/urls.py000066400000000000000000000003231347767355200224540ustar00rootroot00000000000000from guardian.compat import url from posts import views urlpatterns = [ url(r'^$', views.post_list, name='posts_post_list'), url(r'^(?P[-\w]+)/$', views.post_detail, name='posts_post_detail'), ] django-guardian-2.0.0/example_project/posts/views.py000066400000000000000000000014601347767355200226270ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.shortcuts import render, get_object_or_404 from django.views.generic import ListView from django.template import RequestContext from guardian.decorators import permission_required_or_403 from .models import Post User = get_user_model() class PostList(ListView): model = Post context_object_name = 'posts' post_list = PostList.as_view() @permission_required_or_403('posts.view_post', (Post, 'slug', 'slug')) def post_detail(request, slug, **kwargs): data = { 'post': get_object_or_404(Post, slug=slug), 'users': User.objects.all(), 'groups': Group.objects.all(), } return render( request, 'posts/post_detail.html', data, RequestContext(request)) django-guardian-2.0.0/example_project/requirements.txt000066400000000000000000000001401347767355200232260ustar00rootroot00000000000000Django==2.1.* django-environ==0.4.5 django-rosetta==0.9.3 microsofttranslator==0.8 polib==1.1.0 django-guardian-2.0.0/example_project/settings.py000066400000000000000000000056361347767355200221730ustar00rootroot00000000000000import os import sys import environ env = environ.Env() abspath = lambda *p: os.path.abspath(os.path.join(*p)) DEBUG = True SECRET_KEY = 'CHANGE_THIS_TO_SOMETHING_UNIQUE_AND_SECURE' PROJECT_ROOT = abspath(os.path.dirname(__file__)) GUARDIAN_MODULE_PATH = abspath(PROJECT_ROOT, '..') sys.path.insert(0, GUARDIAN_MODULE_PATH) DATABASES = {'default': env.db(default="sqlite://./example.db")} INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.messages', 'guardian', 'posts', 'articles', 'core', 'django.contrib.staticfiles', ) if 'GRAPPELLI' in os.environ: try: __import__('grappelli') INSTALLED_APPS = ('grappelli',) + INSTALLED_APPS except ImportError: print("django-grappelli not installed") try: import rosetta INSTALLED_APPS += ('rosetta',) except ImportError: pass MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ) STATIC_ROOT = abspath(PROJECT_ROOT, '..', 'public', 'static') STATIC_URL = '/static/' STATICFILES_DIRS = [abspath(PROJECT_ROOT, 'static')] GUARDIAN_RAISE_403 = True ROOT_URLCONF = 'urls' SITE_ID = 1 USE_I18N = True USE_L10N = True LOGIN_REDIRECT_URL = '/' TEST_RUNNER = 'django.test.runner.DiscoverRunner' AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'guardian.backends.ObjectPermissionBackend', ) GUARDIAN_GET_INIT_ANONYMOUS_USER = 'core.models.get_custom_anon_user' PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.SHA1PasswordHasher', ) AUTH_USER_MODEL = 'core.CustomUser' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': ( os.path.join(os.path.dirname(__file__), 'templates'), ), 'OPTIONS': { 'debug': DEBUG, 'loaders': ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ), 'context_processors': ( 'core.context_processors.version', 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.request', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages' ), }, }, ] django-guardian-2.0.0/example_project/static/000077500000000000000000000000001347767355200212365ustar00rootroot00000000000000django-guardian-2.0.0/example_project/static/css/000077500000000000000000000000001347767355200220265ustar00rootroot00000000000000django-guardian-2.0.0/example_project/static/css/bootstrap.min.css000066400000000000000000003167231347767355200253530ustar00rootroot00000000000000/*! * Bootstrap v2.3.0 * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover,a:focus{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover,a.muted:focus{color:#808080}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#dff0d8}.table tbody tr.error>td{background-color:#f2dede}.table tbody tr.warning>td{background-color:#fcf8e3}.table tbody tr.info>td{background-color:#d9edf7}.table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.table-hover tbody tr.error:hover>td{background-color:#ebcccc}.table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.table-hover tbody tr.info:hover>td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} django-guardian-2.0.0/example_project/static/img/000077500000000000000000000000001347767355200220125ustar00rootroot00000000000000django-guardian-2.0.0/example_project/static/img/glyphicons-halflings-white.png000066400000000000000000000211111347767355200277560ustar00rootroot00000000000000PNG  IHDRӳ{PLTEmmmⰰᒒttt󻻻bbbeeeggg𶶶xxx󛛛Ƽ몪֢UUU鿿rOtRNS#_ /oS?C kDOS_6>4!~a @1_'onҋM3BQjp&%!l"Xqr; A[<`am}43/0IPCM!6(*gK&YQGDP,`{VP-x)h7e1]W$1bzSܕcO]U;Zi'y"؆K 64Y*.v@c.};tN%DI !ZЏ5LH26 ɯ" -bE,,)ʏ B>mn6pmRO wm@V#?'CȑZ#qb|$:)/E%nRqChn%i̓}lm ?idd",`H"r.z~(bQU&)5X#EMR<*p[[%.Ọk7lIoJF lV!̡ăuH`&,zRk$|$lXbjߪdU?Σ$HW$U'HE3*խU\}( zhVk}guRk$%|T|ck獳"D_W+.Q)@ƽHbslTDR2Xm#a 3lYzj㒚#! 4J8(cvt]aT D ΅Q?^-_^$:\V $N|=(vZ'q6Z׆B5V!y3K㱿bv4xR]al!IoP@tVyL٪mlڿIUb|[*lke'*WddDӝ}\W_WߝrN?vޫ۲X%0uoui*JVƦb%}i5IYlNE-wςf_W3mI-mQ)S kTC7m<"܌bT|'$ҘR&>O p6tSN\ׯLm\r@3uT b7t.5.q3r0=8TiJ\6uF R32^'ŪxI F8O{%8kJMSȴdBEdWCYO:/ON/I_=xFE! =i:o~ y?''[͓[͓[͓[͓[ͭ.U>$PƦc%]\c:| ,eSZ,oXrX!R@Zv 0>?* <|N60;{ad2v+D^t[q!۞V}fۨϏYeॗ)Vyl|" fUq@Ǽ4Y-Y-!6aB:o%JIUQ|UKO`=\ :0x Pau@!KPdxhw1>$j΍vZdxSUA&[URd7øzk/rU^w:I.VǮc>q.!zSr&2)Wg R -iQ 8Pa\ОU%iݡU_=p Lu(N?0?Æ:]άtB%U|NsorNf ,P !v" Y6hL_@@bscqgv4||0lϟ$S9bʱj#~?o}}7sAPm:IV=n !{{hEࢪ8suoLT$;VscqD3 ༂3.DBB4&V' T `D6Ϸqyj8V*X%@s\jrN$|=5Ά 'mUiKi%CI:ssaƅ`*`=l)>u՘MeuSI_OL_}o&jzp{lu:O)s%Q@$<]f xO%PCbhr2PKpf5Në3^o]eJiB464^tuٲU֌:G4'22YpuG'/Py4?.SBP_>I 1t3ΓBɭɭɭɭVVVVVs]!67(g y@ 4>Q VF}^Xׇڼje26 L%YGh lC})< !EEPZWZV+@†R 5{@ouɐ4&H6ey V݀VťcqZޒrJyByFzFN$Hb*+jՏqэ ګkݿUXle1d0d^-B%} {Y%r*j5Ak5u",:~ҸY~ hSA~6 fulՇf{ȵQtATHZkƭ/_Sn u']b]|m`BāJ,O$du]Zs FL:aǙT4o~by?wpj滥A(x]†f~an֧/^dڲcՇ,!1i&xi_VK@ip̓9Vi%a; L?0J*Ū5U'x^6V[^ {eU|:0=0d۫o*Jq%[YN.sQLud[29I:WnmXlڃ6!lNlVէKUjV\J%UߊBLcKfb>a=b~R]aG%[js@/9MطݘU>yɲX@} Ftg^vO\Ӹwvpz3K5i!$P>ā'VƛL2r@UMKZ6tw맟¦bm1h||]}~0MjA(JJP68C&yr׉e}j_cJ?I0k>šW |Bޝ."TEXd 8!cw*E(J)![W"j_ТeX_XB;oO0~?:PC (.[!Wq%*leY)E<^KZT60.#A\5;Rmtkd/8)5~^0 #Ckgey)ͶԺ6ĥ<(?&uAVm0^h.txR*a':,H|ō l5z;8+e#b'#|}2w(|KcJ l6 w^Տoi3H R ̔9,YgPְ:N [5SR![)]i}`mN4Хv`|;f(FltL8÷Z#AO%Y)NU5YedJE3dZذݣHT1 ;8MjnʏӤqp 1h^<<>yt{?|'j)}YUU{@V/J1F+7䀉[OWO[ yUY!?BD%DWj>-Ai6xz)U R7 d@g\so)a4zf[W+> P> |qLG8vȣlj2Zt+VA6gT *ʆUz(m)CD `He/.:zN9pgo &NC׃އ>Wհ_Hj)Xe6F7pm-`'c.AZ=^e8F;{Rtn(z!S7o Iew3]bܗ85|iϠRJkʱZRO+8U&:]ZieR(JMޗ7Z@5a^\GzsρU*rMezT^:ɬͦX=>$ bi>U&XQoybbGk8 Ҙn).Սo ^MmdZi$soo*{4eLbLٳ""mx:`:mk[geTެ)'0*TB{!I ''''[͓[͓[͓[͓[]Zj Q.e '/yvQ71(Z&X?(_Z){tڀmZWϏ)-C jqn,̋"IvUL!h꛿skAcrN佚фVE40yX~4zʸV㳰%,)fqtpu~  *^0:ܲ33JO(ZB?K^ v]unlWi0p6[착C_5X#[wX3b廫R{NKAe Se|wxso>P\儔ԕ6;nVmfI$V͓J-J%֌0UwYЎSnum藮xz˗VƫIvnW_qLZ"_Xz 8]Ap?C543zw({7e*Ȳ`۰!AQ:KUnz]1yVGaCm0PY ٚUx6TT&hV9V ӬzÑ 1[XzZ9erqJND/gX*9oN6D` {I%Mz9—TQ7f\"j_3~xB'ܷY]*KЌ%"5"qxq~ƕ=jS>jV&~]2xzF1X_yD<#NRB}K/iy !V^˿eJ}/FkA7 S+.(ecJ:zWZ몖wQ~ä́p6,e5,+,tv%O^OO}ן -O7>ekC6wa_C |9*WA)UJg8=:mjUvqysܒLglC6+[FSWg9wV31A ND<$5e(s[ ۨbaF.]KIENDB`django-guardian-2.0.0/example_project/static/img/glyphicons-halflings.png000066400000000000000000000307771347767355200266620ustar00rootroot00000000000000PNG  IHDRtEXtSoftwareAdobe ImageReadyqe<1IDATx}ml\EW^ɺD$|nw';vю8m0kQSnSV;1KGsԩ>UoTU1cƖYuּca&#C,pؚ>kں ULW -sn3Vq~NocI~L{- H8%_M£wB6EW,ĢpY2+(Y@&A/3kXhߍ-aA<>P'\J;(}#Qz:4%m?nfntK*l9J+DIYu1YZ^(]YYEf@ОlXz]Ut u &5-PW}@t|#LY=s܂,w#+R+?Ƌax X0"ea)tG*ԡwVwV^rf%xB(qּ4>WG#lWU<ЁXJVѶlR$kDVrI7:X%X1NEzw;y9z9O%~~uɗ*=Ixcy}Y(ou ±N$^j e\iX񝜬];Y-rѲ&>!zlYaVHVN԰9=]=mRMdOUC JUiT}rWW'ڹu)ʢF"YU#P׾&ܑЅROwyzm$Os? +^FTIEq%&~ >M}]ԖwA? [Nteexn(措BdMTpʥnqqS?bWXmW6x*{V_!VjΧsVL^j XkQjU6sk̩n~[qǸ-` O:G7l"ksRe2vQ=QƼJUX`gQy~ ďKȰE]#P:td\T/u;س:Jc-%'e q ?j"/yh48Zi1|JUu>_N;hxwNU JQU7\j̮bT:B?6oJ1Ί%I UY-Ii4{=rǤ7@)HKJ+f4X8Cd?'j1 N< 39EWo VTGzg# %D0#ܠ3[tiآ( U,]125|Ṋfw7w u+Š]Db]K xbW ՛7|ВX㕛{UcGXk¬|(h)IUa)lp 3luPU]D)/7~4Wt5J}V X0z VM;>Gԙ^|gF:jaZ^)74C#jwr,еSlGu;1vm><)}ZQՖ&mZ:1UMB~ a:/᜗:KWWOҠ&Y2f7cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘g*3fF5LbN2#Tf=C`!ZGUe꣇e2V<1mkS4iϗ*.{N8Xaj~ڀnAx,%fE:|YDVj ¢lg6(:k~MM5?4 ]WO>诋WZiG|QGJeK[YcյpmjE\f/ǎ8&OQ3 .3tt2'-V8pXSrY#J!Q ",ub@FK:u^iy[]<.Cw+W\)b kr-.MtڀMqʄ۰#$^X$"V`T4m~w%Pp1|+&UxY8*r8:k7QЃҀT$Ўƙ S>~Sjs:5q.w&_Z.X=:ވbw` _kd{'0:ds#qi!224nq\9-KUTsSUuVo@;Uz>^=Np>oPO @I@'Gj5o*U>^*ew>ͫʧ᫠Q5 ̈́<$#5Jٻj6e)_ d]2B:^(*:8JYS鬆Kݗ ]U4_rj{5ׇaǑ/yV?GtGb@xPU7O3|鍪 IQ5QGw *(;wf0*PUU<YƔvbt5{2!,}Ҧ:)j2OkΪ' ֊0I.q\(%ojQĖՇa<ԍexAgt'[d;׸`rcdjPFU$UeJI6T&Z}z(z vfuz {}ۿߝݞlxUZ謊.Y岟b%nw@ǩS9|źs%>_o#9\EU~/ځt(r[QZuOo;!MrU]0TcpDő?.cPuF;L_Sb}R/J_+h2$ai UǩS9>Є}76rzu~国4oĨ 1J ^̘~iC޸55G׹]gwsn zTuO=?/zƲc>Οb#7ֻcgkޛTUj*-T=]uu}>ݨNЭ [ ]:%/_ Sz]6D.mD7Uƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1c>J4hPP+A;'G_XKmL5I.},wFFum$S-E-;Õ C3I-`BRx1ғTJݕ;hΊ8 DYJo;Yš5MKɰM;%Pd9KhnD[zgVh,'C p!^M(WK2X>UQ%^p8 ˽^#Ζ؄+.@gCz%ɔ-Pr KX n>=ՔѨeSvRLz5%9UQS \WիK'hp)ô Jrh M0F (f_R5///G+x 1"eS 5 :Tf=+7Qɧ\TEs༬rYs8&k#pSՊ5MTbD܊[Ng5Q\s5PB@[8ɨV1&4Wsy[Ǿ wU2V77jމd^~YfC_h;a.&M i UWpzs`>/"'OI۲y:BzdTq£=йb:"m/-/PWDQǴ͐57m`H%AV!Hԛ׿@"Qzދ|ߒT-*OU^Ҧ6!Cwk|h&Hd5LEYy'ƣ7%*{=)Z%ٝP *G]/8Lw$?8M)\į/#7Ufd7'6\h1 vIfEIr=1w\WKVZHKgZ͡$mx % `j}TuTQJZ*H>*QxkLFTyU-)ôbiA|q`F'+ 4^Qy xH)#t^?@]^`ARSqjgB:rK۷l<2-4YKhgQLxVwP~M Φ0l 3ƅaŊITȀhwJmxIMչ|U7xˆS~2ߕ?kW1kC3];YnSґAeXYz8,'x< k7Kx]$x$vgT#w;o@ z_Vmn|HֵhZg-^TAn- )@4[*9xKƋj>!,Vt:eqn8%ohS(2\Q^aigF3vTUDVlQꅧWc%Ueq4ҝº/U $_Q!>t| ,țG<tC[xTXmf|Q%d#jUՆ|; H[bά#,Ws7NT1~m&ǻ{' \㟾 bBKJo8%!$Qj:/RX)$Sy޳ 䍧RDUg_D軦J\jN֖SU;~?Ohssdƣ}6(T <_4b5 ^N N%8QejF7toMyө`)g[/|?өJuGL坕/=CTܠhdifHcǞG4,`D՞{'xG_p/5@m +$jVH3a"*ũ,,HJҵȸT^Qyo&IÉJUVwWLeM~3tA6rwɤ6տ \0HL%LX5c@HHÃZ|NV+7WM{cig*ȸU7iÉбzd * ?gtX8̝OX:]2ɍ]p^++>AVڛE{ DB.&/56ArxY#ܕy)cKQtȪ~! ;C}ʃtf{6$NVsj wupZ)zŁ|-wg+nMVj/d+U~ͯi:_ix whqr>駃-x뼬)ݷyR=! ì:J/lIkV@n74758Z KJ(Uxz1w)^\ԣzȪ󲦨c2f؍v+6f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘2N oC\F1ִ UZJV̚\4Mgq1z{&YT ,HX~D u\g}x>+YdN̮ol ZX+F[/j+S~2/jV8Jr^ԉ]J}J*ۏ<2԰&JݣjOM@ѯ#0O[SXB^ uze\]dd./xXE f'vO_H${%;kt7ށmő|d{aފ^ǛڎE5ʋBr]W=_SAf(0 oU5q ,_\luz˪uz㻲o=Yi~| 0+=VJت /ލzM\zCL[U:|k*^8"\Wٚ\ .XTjX5 SkFu\1 q'mģ/QUؕ*AɽDNZ׮?_[# ˍ4:^j|5LG ||øBW{6[uQF.1$qF9IHg)\5>C#uXZ$#*<ߐsRv1Tj>Jm>*#( [Fhsש5*jQʼ&&&P犛L[Q1* ;X}Iΰ[Q?qQZ Hݙ֞VEsBCZ9JTK tup˷ /O,.kUdsOHMg4=-)+ؿh2Nw/r|WQn=GIU;'j,vfdzpe$V GTYsBZO1pj:r"nTUSCgr veAۘ˜FC+Ֆ#[JTe'v9-3 Dmӻuuz?0 o hxuY &_54=f07kלU0]D:jdw/+PGUVS<\2uatc^zYRąmC+7#,|:iNw*|^sm|X>Ъ^1\#͹ &%{,2U>ݎ.c05z# ogNO+Q쓭 ,˗-%K\[S_`y+b_94"U+Ύap}I[M,B.NtwHj漬E L߀ 0DX(kڵ NoU{gquz RwkէRx'uZ[3'zyyד%sƕ3jYF\s=m1&VAɼ?k\+]6yモ1gtOIW7al|1 >$]e 7؝WIe?ަL#>| ҭ] pM5MUdI61ԠeǼYGhOn3խR:^k_'Yuuq#p# J2xl>OjcY馃!ڡ+sZ/ D}2AY mpc#<'xSKx`*W[,e|6BH)㶤kjpDU(2qzx9*tqa/, Z[ 0>Ө֜xN)fă@qըFU՝w(a;ˋ>|Tc|w2eiT]*!_\WG{ ]^݅Z5t|6oYHaO@= my^akE.uz]#٥hWv(:,6A߉JFa\ wWex>vetuMYA>).,;ɦCbwjE)W Fӫ@s4e6^Q9oI}4x<.B?B߫#$Hx.x9,a!RTpgd5xBe.L7@* AsduttSVUaRU|I xG߃$T񭟬#_IFMŒ_X@foQIDII?|%$r {ENĸwޕqq?Dؽ}}o/`ӣCTi /ywO rD 9YUD] Ή@s]+'UaL} hrU'7:sU|k)H@hNq#ϵ8y˭Xű#w 1!흉R'7fuד0p!WÖW+Nmp\-ioD$g٠˅%%ÐmV]̱rw*Z}y+L Nouj}xt)lStuqxmNyKUOnDbhf}k>6ufT%{ <񐮸mjFcmUïc;w8@dGFUA& =nq5]iP}z:k⼶-ʓ Κl*'UzaxWFdZzTNRs+# wzgi:MBqtM l#^'Gߣ*^t{=rERnQ$adJl02%Tڊ^<~g?Of*U^?:N+o[PUs|QR']V-L)H K䐞 mYn\4}YVD hR;g-'3aסM Dh}1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌk*Ț4`L$b U4\dt'>HȄ|.+Y+/Gy2OCWv3v,'kia W O6߯E=Hv $LlxI躍/}^]x\3 ɮ5 QT&G9Ay^i}O[5ޱwq4,s JJI.myE^%'VB~dׯ}*j* ~uTk\fKЬ*Y]_v'I˨鑩6Xo'j&uɧngT]oڌ9\*wVHӖ| >:5EF'J ɝ`!A e~_;5ױϊ镋m_&OVi<}"靍hW9X6KPƣ G"ƭ?/O^hCHLciPj)}QQզ#tMg9 xGw~d;_J+RỲ<;e 5/Qs/5N[!a+NPb+ѺI}-t_qU=MKʞY5no*vvbʊ{]| ~ Z{-끇^FVviϵ3Ya=6ndS;-ʹ^;uꪪ^ |=_w+"i&4l#wir|W3U$"J~O@]~tRJVMHw:̦@?>O?vdrtS*$&~1>Z}^nL(]f*&*QaIꝄ|3*O?r?*4Gyz[k/tkQϖWCCKk/x5|S*`ϹγQEwy o KYqTb$-/PtsZNKQ*>ݢU@Џ"JQ;¹& Lx;+T /+O赟> (T?ķD^N*'p$IW֐W~ =J|_UTe7ְP`;CYjk=sU[mߙ-;};2|wo1p0~>0m @Jrǟcٷ4͜?q\UUIV?2L/+Шꄾ< ܇^T ?tj\JrҀB*=km X,n}aՒIadp׷ll{\6v8RꅟҲf1F|Տ;e=\D ,D:ψrxQT◎*|{nS 9~=}ӕG~%j:Dj<ឫ:jO% $T8!jvm|'OЗ¹➱z\vsIv`Ȕʨj-^$-^G Q{m`T#c֞㸝|n.ߪN$O JUVʼt,jg-mסּNV z:(Ι*|1Ux=Yk*t MNNDUhK ؞X(刄Rv!#B_cxRŹoE5Dg>?fXQQ˔|@"աMveC>mO$H#]Y I=)_`k* :a>!X!W^wҒl'<;vwgIt_?Jh`#E:fdx=6Wu<Ӌd2di˂c#h¬c4?<HFYoVpN;ݷJ\ >` (t3{>⦊;;qFx4YcS$w.da*k|Q,+xs^K߫P^nO֮L5mIwl?-.ʲJ8 F B.-:2Ȕ!/A#b_m%I($|PZ[1G{^#o>3mw?'cx[^:Wk/`'=~֥W(gQbfv7UzM3+؍K:4|GCtA+Kʨ{@Ɩ [05E|yn4MIENDB`django-guardian-2.0.0/example_project/static/js/000077500000000000000000000000001347767355200216525ustar00rootroot00000000000000django-guardian-2.0.0/example_project/static/js/bootstrap.min.js000066400000000000000000000674021347767355200250200ustar00rootroot00000000000000/*! * Bootstrap.js by @fat & @mdo * Copyright 2012 Twitter, Inc. * http://www.apache.org/licenses/LICENSE-2.0.txt */ !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'

'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);django-guardian-2.0.0/example_project/templates/000077500000000000000000000000001347767355200217455ustar00rootroot00000000000000django-guardian-2.0.0/example_project/templates/403.html000066400000000000000000000005421347767355200231420ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}403 - Permission denied{% endblock %} {% block content %}

    403

    Permission Denied

    {% endblock %} django-guardian-2.0.0/example_project/templates/404.html000066400000000000000000000005471347767355200231500ustar00rootroot00000000000000{% extends "base.html" %} {% block title %}404 - Page does not exist{% endblock %} {% block content %}

    404

    Page does not exist

    {% endblock %} django-guardian-2.0.0/example_project/templates/500.html000066400000000000000000000006771347767355200231510ustar00rootroot00000000000000 Page unavailable

    Page unavailable

    Sorry, but the requested page is unavailable due to a server hiccup.

    Our engineers have been notified, so check back later.

    django-guardian-2.0.0/example_project/templates/base.html000066400000000000000000000052261347767355200235520ustar00rootroot00000000000000 {% block title %}Guardian example{% endblock %} {% block navbar %} {% endblock %}
    {% block content %}

    Hello, world!

    {% endblock %}
    django-guardian-2.0.0/example_project/templates/home.html000066400000000000000000000005411347767355200235630ustar00rootroot00000000000000{% extends "base_1col.html" %} {% block col-single-title %}Home{% endblock %} {% block col-single-content %}

    Pages

    {% endblock %} django-guardian-2.0.0/example_project/urls.py000066400000000000000000000013001347767355200213000ustar00rootroot00000000000000from guardian.compat import include, url, handler404, handler500 from django.conf import settings from django.contrib import admin from django.contrib.auth.views import LogoutView __all__ = ['handler404', 'handler500'] admin.autodiscover() urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^logout/$', LogoutView.as_view(next_page='/'), name='logout'), url(r'^article/', include('articles.urls', namespace='articles')), url(r'^', include('posts.urls')), ] if 'grappelli' in settings.INSTALLED_APPS: urlpatterns += [url(r'^grappelli/', include('grappelli.urls')), ] if 'rosetta' in settings.INSTALLED_APPS: urlpatterns += [url(r'^rosetta/', include('rosetta.urls')), ] django-guardian-2.0.0/extras.py000066400000000000000000000056621347767355200164570ustar00rootroot00000000000000import _ast import os import sys from setuptools import Command #from pyflakes.scripts import pyflakes as flakes def check(filename): from pyflakes import reporter as mod_reporter from pyflakes.checker import Checker codeString = open(filename).read() reporter = mod_reporter._makeDefaultReporter() # First, compile into an AST and handle syntax errors. try: tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST) except SyntaxError: value = sys.exc_info()[1] msg = value.args[0] (lineno, offset, text) = value.lineno, value.offset, value.text # If there's an encoding problem with the file, the text is None. if text is None: # Avoid using msg, since for the only known case, it contains a # bogus message that claims the encoding the file declared was # unknown. reporter.unexpectedError(filename, 'problem decoding source') else: reporter.syntaxError(filename, msg, lineno, offset, text) return 1 except Exception: reporter.unexpectedError(filename, 'problem decoding source') return 1 else: # Okay, it's syntactically valid. Now check it. lines = codeString.splitlines() warnings = Checker(tree, filename) warnings.messages.sort(key=lambda m: m.lineno) real_messages = [] for m in warnings.messages: line = lines[m.lineno - 1] if 'pyflakes:ignore' in line.rsplit('#', 1)[-1]: # ignore lines with pyflakes:ignore pass else: real_messages.append(m) reporter.flake(m) return len(real_messages) class RunFlakesCommand(Command): """ Runs pyflakes against guardian codebase. """ description = "Check sources with pyflakes" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): try: import pyflakes # pyflakes:ignore except ImportError: sys.stderr.write("No pyflakes installed!\n") sys.exit(-1) thisdir = os.path.dirname(__file__) guardiandir = os.path.join(thisdir, 'guardian') warns = 0 # Define top-level directories for topdir, dirnames, filenames in os.walk(guardiandir): paths = (os.path.join(topdir, f) for f in filenames if f .endswith('.py')) for path in paths: if path.endswith('tests/__init__.py'): # ignore that module (it should only gather test cases with # *) continue warns += check(path) if warns > 0: sys.stderr.write( "ERROR: Finished with total %d warnings.\n" % warns) sys.exit(1) else: print("No problems found in source codes.") django-guardian-2.0.0/guardian/000077500000000000000000000000001347767355200163605ustar00rootroot00000000000000django-guardian-2.0.0/guardian/__init__.py000066400000000000000000000016561347767355200205010ustar00rootroot00000000000000""" Implementation of per object permissions for Django. """ from . import checks default_app_config = 'guardian.apps.GuardianConfig' # PEP 396: The __version__ attribute's value SHOULD be a string. __version__ = '2.0.0' # Compatibility to eg. django-rest-framework VERSION = tuple(int(x) for x in __version__.split('.')[:3]) def get_version(): return __version__ def monkey_patch_user(): from django.contrib.auth import get_user_model from .utils import get_anonymous_user from .models import UserObjectPermission User = get_user_model() # Prototype User and Group methods setattr(User, 'get_anonymous', staticmethod(lambda: get_anonymous_user())) setattr(User, 'add_obj_perm', lambda self, perm, obj: UserObjectPermission.objects.assign_perm(perm, self, obj)) setattr(User, 'del_obj_perm', lambda self, perm, obj: UserObjectPermission.objects.remove_perm(perm, self, obj)) django-guardian-2.0.0/guardian/admin.py000066400000000000000000000435321347767355200200310ustar00rootroot00000000000000from collections import OrderedDict from django import forms from django.conf import settings from django.contrib import admin, messages from django.contrib.admin.widgets import FilteredSelectMultiple from django.contrib.auth import get_user_model from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext from guardian.compat import url from guardian.forms import GroupObjectPermissionsForm, UserObjectPermissionsForm from guardian.models import Group from guardian.shortcuts import (get_group_perms, get_groups_with_perms, get_perms_for_model, get_user_perms, get_users_with_perms) class AdminUserObjectPermissionsForm(UserObjectPermissionsForm): """ Extends :form:`UserObjectPermissionsForm`. It only overrides ``get_obj_perms_field_widget`` method so it return ``django.contrib.admin.widgets.FilteredSelectMultiple`` widget. """ def get_obj_perms_field_widget(self): return FilteredSelectMultiple(_("Permissions"), False) class AdminGroupObjectPermissionsForm(GroupObjectPermissionsForm): """ Extends :form:`GroupObjectPermissionsForm`. It only overrides ``get_obj_perms_field_widget`` method so it return ``django.contrib.admin.widgets.FilteredSelectMultiple`` widget. """ def get_obj_perms_field_widget(self): return FilteredSelectMultiple(_("Permissions"), False) class GuardedModelAdminMixin: """ Serves as a helper for custom subclassing ``admin.ModelAdmin``. """ change_form_template = \ 'admin/guardian/model/change_form.html' obj_perms_manage_template = \ 'admin/guardian/model/obj_perms_manage.html' obj_perms_manage_user_template = \ 'admin/guardian/model/obj_perms_manage_user.html' obj_perms_manage_group_template = \ 'admin/guardian/model/obj_perms_manage_group.html' user_can_access_owned_objects_only = False user_owned_objects_field = 'user' user_can_access_owned_by_group_objects_only = False group_owned_objects_field = 'group' include_object_permissions_urls = True def get_queryset(self, request): qs = super().get_queryset(request) if request.user.is_superuser: return qs if self.user_can_access_owned_objects_only: filters = {self.user_owned_objects_field: request.user} qs = qs.filter(**filters) if self.user_can_access_owned_by_group_objects_only: User = get_user_model() user_rel_name = User.groups.field.related_query_name() qs_key = '%s__%s' % (self.group_owned_objects_field, user_rel_name) filters = {qs_key: request.user} qs = qs.filter(**filters) return qs def get_urls(self): """ Extends standard admin model urls with the following: - ``.../permissions/`` under ``app_mdodel_permissions`` url name (params: object_pk) - ``.../permissions/user-manage//`` under ``app_model_permissions_manage_user`` url name (params: object_pk, user_pk) - ``.../permissions/group-manage//`` under ``app_model_permissions_manage_group`` url name (params: object_pk, group_pk) .. note:: ``...`` above are standard, instance detail url (i.e. ``/admin/flatpages/1/``) """ urls = super().get_urls() if self.include_object_permissions_urls: info = self.model._meta.app_label, self.model._meta.model_name myurls = [ url(r'^(?P.+)/permissions/$', view=self.admin_site.admin_view( self.obj_perms_manage_view), name='%s_%s_permissions' % info), url(r'^(?P.+)/permissions/user-manage/(?P\-?\d+)/$', view=self.admin_site.admin_view( self.obj_perms_manage_user_view), name='%s_%s_permissions_manage_user' % info), url(r'^(?P.+)/permissions/group-manage/(?P\-?\d+)/$', view=self.admin_site.admin_view( self.obj_perms_manage_group_view), name='%s_%s_permissions_manage_group' % info), ] urls = myurls + urls return urls def get_obj_perms_base_context(self, request, obj): """ Returns context dictionary with common admin and object permissions related content. It uses AdminSite.each_context, making sure all required template vars are in the context. """ context = self.admin_site.each_context(request) context.update({ 'adminform': {'model_admin': self}, 'media': self.media, 'object': obj, 'app_label': self.model._meta.app_label, 'opts': self.model._meta, 'original': str(obj), 'has_change_permission': self.has_change_permission(request, obj), 'model_perms': get_perms_for_model(obj), 'title': _("Object permissions"), }) return context def obj_perms_manage_view(self, request, object_pk): """ Main object permissions view. Presents all users and groups with any object permissions for the current model *instance*. Users or groups without object permissions for related *instance* would **not** be shown. In order to add or manage user or group one should use links or forms presented within the page. """ if not self.has_change_permission(request, None): post_url = reverse('admin:index', current_app=self.admin_site.name) return redirect(post_url) from django.contrib.admin.utils import unquote obj = get_object_or_404(self.get_queryset( request), pk=unquote(object_pk)) users_perms = OrderedDict( sorted( get_users_with_perms(obj, attach_perms=True, with_group_users=False).items(), key=lambda user: getattr( user[0], get_user_model().USERNAME_FIELD) ) ) groups_perms = OrderedDict( sorted( get_groups_with_perms(obj, attach_perms=True).items(), key=lambda group: group[0].name ) ) if request.method == 'POST' and 'submit_manage_user' in request.POST: user_form = self.get_obj_perms_user_select_form(request)(request.POST) group_form = self.get_obj_perms_group_select_form(request)(request.POST) info = ( self.admin_site.name, self.model._meta.app_label, self.model._meta.model_name, ) if user_form.is_valid(): user_id = user_form.cleaned_data['user'].pk url = reverse( '%s:%s_%s_permissions_manage_user' % info, args=[obj.pk, user_id] ) return redirect(url) elif request.method == 'POST' and 'submit_manage_group' in request.POST: user_form = self.get_obj_perms_user_select_form(request)(request.POST) group_form = self.get_obj_perms_group_select_form(request)(request.POST) info = ( self.admin_site.name, self.model._meta.app_label, self.model._meta.model_name, ) if group_form.is_valid(): group_id = group_form.cleaned_data['group'].id url = reverse( '%s:%s_%s_permissions_manage_group' % info, args=[obj.pk, group_id] ) return redirect(url) else: user_form = self.get_obj_perms_user_select_form(request)() group_form = self.get_obj_perms_group_select_form(request)() context = self.get_obj_perms_base_context(request, obj) context['users_perms'] = users_perms context['groups_perms'] = groups_perms context['user_form'] = user_form context['group_form'] = group_form # https://github.com/django/django/commit/cf1f36bb6eb34fafe6c224003ad585a647f6117b request.current_app = self.admin_site.name return render(request, self.get_obj_perms_manage_template(), context) def get_obj_perms_manage_template(self): """ Returns main object permissions admin template. May be overridden if need to change it dynamically. .. note:: If ``INSTALLED_APPS`` contains ``grappelli`` this function would return ``"admin/guardian/grappelli/obj_perms_manage.html"``. """ if 'grappelli' in settings.INSTALLED_APPS: return 'admin/guardian/contrib/grappelli/obj_perms_manage.html' return self.obj_perms_manage_template def obj_perms_manage_user_view(self, request, object_pk, user_id): """ Manages selected users' permissions for current object. """ if not self.has_change_permission(request, None): post_url = reverse('admin:index', current_app=self.admin_site.name) return redirect(post_url) user = get_object_or_404(get_user_model(), pk=user_id) obj = get_object_or_404(self.get_queryset(request), pk=object_pk) form_class = self.get_obj_perms_manage_user_form(request) form = form_class(user, obj, request.POST or None) if request.method == 'POST' and form.is_valid(): form.save_obj_perms() msg = ugettext("Permissions saved.") messages.success(request, msg) info = ( self.admin_site.name, self.model._meta.app_label, self.model._meta.model_name, ) url = reverse( '%s:%s_%s_permissions_manage_user' % info, args=[obj.pk, user.pk] ) return redirect(url) context = self.get_obj_perms_base_context(request, obj) context['user_obj'] = user context['user_perms'] = get_user_perms(user, obj) context['form'] = form request.current_app = self.admin_site.name return render(request, self.get_obj_perms_manage_user_template(), context) def get_obj_perms_manage_user_template(self): """ Returns object permissions for user admin template. May be overridden if need to change it dynamically. .. note:: If ``INSTALLED_APPS`` contains ``grappelli`` this function would return ``"admin/guardian/grappelli/obj_perms_manage_user.html"``. """ if 'grappelli' in settings.INSTALLED_APPS: return 'admin/guardian/contrib/grappelli/obj_perms_manage_user.html' return self.obj_perms_manage_user_template def get_obj_perms_user_select_form(self, request): """ Returns form class for selecting a user for permissions management. By default :form:`UserManage` is returned. """ return UserManage def get_obj_perms_group_select_form(self, request): """ Returns form class for selecting a group for permissions management. By default :form:`GroupManage` is returned. """ return GroupManage def get_obj_perms_manage_user_form(self, request): """ Returns form class for user object permissions management. By default :form:`AdminUserObjectPermissionsForm` is returned. """ return AdminUserObjectPermissionsForm def obj_perms_manage_group_view(self, request, object_pk, group_id): """ Manages selected groups' permissions for current object. """ if not self.has_change_permission(request, None): post_url = reverse('admin:index', current_app=self.admin_site.name) return redirect(post_url) group = get_object_or_404(Group, id=group_id) obj = get_object_or_404(self.get_queryset(request), pk=object_pk) form_class = self.get_obj_perms_manage_group_form(request) form = form_class(group, obj, request.POST or None) if request.method == 'POST' and form.is_valid(): form.save_obj_perms() msg = ugettext("Permissions saved.") messages.success(request, msg) info = ( self.admin_site.name, self.model._meta.app_label, self.model._meta.model_name, ) url = reverse( '%s:%s_%s_permissions_manage_group' % info, args=[obj.pk, group.id] ) return redirect(url) context = self.get_obj_perms_base_context(request, obj) context['group_obj'] = group context['group_perms'] = get_group_perms(group, obj) context['form'] = form request.current_app = self.admin_site.name return render(request, self.get_obj_perms_manage_group_template(), context) def get_obj_perms_manage_group_template(self): """ Returns object permissions for group admin template. May be overridden if need to change it dynamically. .. note:: If ``INSTALLED_APPS`` contains ``grappelli`` this function would return ``"admin/guardian/grappelli/obj_perms_manage_group.html"``. """ if 'grappelli' in settings.INSTALLED_APPS: return 'admin/guardian/contrib/grappelli/obj_perms_manage_group.html' return self.obj_perms_manage_group_template def get_obj_perms_manage_group_form(self, request): """ Returns form class for group object permissions management. By default :form:`AdminGroupObjectPermissionsForm` is returned. """ return AdminGroupObjectPermissionsForm class GuardedModelAdmin(GuardedModelAdminMixin, admin.ModelAdmin): """ Extends ``django.contrib.admin.ModelAdmin`` class. Provides some extra views for object permissions management at admin panel. It also changes default ``change_form_template`` option to ``'admin/guardian/model/change_form.html'`` which is required for proper url (object permissions related) being shown at the model pages. **Extra options** ``GuardedModelAdmin.obj_perms_manage_template`` *Default*: ``admin/guardian/model/obj_perms_manage.html`` ``GuardedModelAdmin.obj_perms_manage_user_template`` *Default*: ``admin/guardian/model/obj_perms_manage_user.html`` ``GuardedModelAdmin.obj_perms_manage_group_template`` *Default*: ``admin/guardian/model/obj_perms_manage_group.html`` ``GuardedModelAdmin.user_can_access_owned_objects_only`` *Default*: ``False`` If this would be set to ``True``, ``request.user`` would be used to filter out objects he or she doesn't own (checking ``user`` field of used model - field name may be overridden by ``user_owned_objects_field`` option). .. note:: Please remember that this will **NOT** affect superusers! Admins would still see all items. ``GuardedModelAdmin.user_can_access_owned_by_group_objects_only`` *Default*: ``False`` If this would be set to ``True``, ``request.user`` would be used to filter out objects her or his group doesn't own (checking if any group user belongs to is set as ``group`` field of the object; name of the field can be changed by overriding ``group_owned_objects_field``). .. note:: Please remember that this will **NOT** affect superusers! Admins would still see all items. ``GuardedModelAdmin.group_owned_objects_field`` *Default*: ``group`` ``GuardedModelAdmin.include_object_permissions_urls`` *Default*: ``True`` .. versionadded:: 1.2 Might be set to ``False`` in order **NOT** to include guardian-specific urls. **Usage example** Just use :admin:`GuardedModelAdmin` instead of ``django.contrib.admin.ModelAdmin``. .. code-block:: python from django.contrib import admin from guardian.admin import GuardedModelAdmin from myapp.models import Author class AuthorAdmin(GuardedModelAdmin): pass admin.site.register(Author, AuthorAdmin) """ class UserManage(forms.Form): user = forms.CharField(label=_("User identification"), max_length=200, error_messages={'does_not_exist': _( "This user does not exist")}, help_text=_( 'Enter a value compatible with User.USERNAME_FIELD') ) def clean_user(self): """ Returns ``User`` instance based on the given identification. """ identification = self.cleaned_data['user'] user_model = get_user_model() try: username_field = user_model.USERNAME_FIELD except AttributeError: username_field = 'username' try: user = user_model.objects.get(**{username_field: identification}) return user except user_model.DoesNotExist: raise forms.ValidationError( self.fields['user'].error_messages['does_not_exist']) class GroupManage(forms.Form): group = forms.CharField(max_length=80, error_messages={'does_not_exist': _("This group does not exist")}) def clean_group(self): """ Returns ``Group`` instance based on the given group name. """ name = self.cleaned_data['group'] try: group = Group.objects.get(name=name) return group except Group.DoesNotExist: raise forms.ValidationError( self.fields['group'].error_messages['does_not_exist']) django-guardian-2.0.0/guardian/apps.py000066400000000000000000000003661347767355200177020ustar00rootroot00000000000000from . import monkey_patch_user from django.apps import AppConfig from guardian.conf import settings class GuardianConfig(AppConfig): name = 'guardian' def ready(self): if settings.MONKEY_PATCH: monkey_patch_user() django-guardian-2.0.0/guardian/backends.py000066400000000000000000000073721347767355200205150ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.db import models from guardian.conf import settings from guardian.core import ObjectPermissionChecker from guardian.ctypes import get_content_type from guardian.exceptions import WrongAppError def check_object_support(obj): """ Returns ``True`` if given ``obj`` is supported """ # Backend checks only object permissions (isinstance implies that obj # is not None) # Backend checks only permissions for Django models return isinstance(obj, models.Model) def check_user_support(user_obj): """ Returns a tuple of checkresult and ``user_obj`` which should be used for permission checks Checks if the given user is supported. Anonymous users need explicit activation via ANONYMOUS_USER_NAME """ # This is how we support anonymous users - simply try to retrieve User # instance and perform checks for that predefined user if not user_obj.is_authenticated: # If anonymous user permission is disabled then they are always # unauthorized if settings.ANONYMOUS_USER_NAME is None: return False, user_obj User = get_user_model() lookup = {User.USERNAME_FIELD: settings.ANONYMOUS_USER_NAME} user_obj = User.objects.get(**lookup) return True, user_obj def check_support(user_obj, obj): """ Combination of ``check_object_support`` and ``check_user_support`` """ obj_support = check_object_support(obj) user_support, user_obj = check_user_support(user_obj) return obj_support and user_support, user_obj class ObjectPermissionBackend: supports_object_permissions = True supports_anonymous_user = True supports_inactive_user = True def authenticate(self, request, username, password): return None def has_perm(self, user_obj, perm, obj=None): """ Returns ``True`` if given ``user_obj`` has ``perm`` for ``obj``. If no ``obj`` is given, ``False`` is returned. .. note:: Remember, that if user is not *active*, all checks would return ``False``. Main difference between Django's ``ModelBackend`` is that we can pass ``obj`` instance here and ``perm`` doesn't have to contain ``app_label`` as it can be retrieved from given ``obj``. **Inactive user support** If user is authenticated but inactive at the same time, all checks always returns ``False``. """ # check if user_obj and object are supported support, user_obj = check_support(user_obj, obj) if not support: return False if '.' in perm: app_label, _ = perm.split('.', 1) if app_label != obj._meta.app_label: # Check the content_type app_label when permission # and obj app labels don't match. ctype = get_content_type(obj) if app_label != ctype.app_label: raise WrongAppError("Passed perm has app label of '%s' while " "given obj has app label '%s' and given obj" "content_type has app label '%s'" % (app_label, obj._meta.app_label, ctype.app_label)) check = ObjectPermissionChecker(user_obj) return check.has_perm(perm, obj) def get_all_permissions(self, user_obj, obj=None): """ Returns a set of permission strings that the given ``user_obj`` has for ``obj`` """ # check if user_obj and object are supported support, user_obj = check_support(user_obj, obj) if not support: return set() check = ObjectPermissionChecker(user_obj) return check.get_perms(obj) django-guardian-2.0.0/guardian/checks.py000066400000000000000000000014731347767355200201770ustar00rootroot00000000000000from django.conf import settings from django.core.checks import register, Tags, Warning # noinspection PyUnusedLocal @register(Tags.compatibility) def check_settings(app_configs, **kwargs): """ Check that settings are implemented properly :param app_configs: a list of apps to be checks or None for all :param kwargs: keyword arguments :return: a list of errors """ checks = [] if 'guardian.backends.ObjectPermissionBackend' not in settings.AUTHENTICATION_BACKENDS: msg = ("Guardian authentication backend is not hooked. You can add this in settings as eg: " "`AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend', " "'guardian.backends.ObjectPermissionBackend')`.") checks.append(Warning(msg, id='guardian.W001')) return checks django-guardian-2.0.0/guardian/compat.py000066400000000000000000000032451347767355200202210ustar00rootroot00000000000000from django.conf import settings from django.conf.urls import handler404, handler500, include, url from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser, Group, Permission __all__ = [ 'Group', 'Permission', 'AnonymousUser', 'get_user_model', 'user_model_label', 'url', 'include', 'handler404', 'handler500', ] # Since get_user_model() causes a circular import if called when app models are # being loaded, the user_model_label should be used when possible, with calls # to get_user_model deferred to execution time user_model_label = getattr(settings, 'AUTH_USER_MODEL', 'auth.User') def get_user_model_path(): """ Returns 'app_label.ModelName' for User model. Basically if ``AUTH_USER_MODEL`` is set at settings it would be returned, otherwise ``auth.User`` is returned. """ return getattr(settings, 'AUTH_USER_MODEL', 'auth.User') def get_user_permission_full_codename(perm): """ Returns 'app_label._'. If standard ``auth.User`` is used, for 'change' perm this would return ``auth.change_user`` and if ``myapp.CustomUser`` is used it would return ``myapp.change_customuser``. """ User = get_user_model() model_name = User._meta.model_name return '%s.%s_%s' % (User._meta.app_label, perm, model_name) def get_user_permission_codename(perm): """ Returns '_'. If standard ``auth.User`` is used, for 'change' perm this would return ``change_user`` and if ``myapp.CustomUser`` is used it would return ``change_customuser``. """ return get_user_permission_full_codename(perm).split('.')[1] django-guardian-2.0.0/guardian/conf/000077500000000000000000000000001347767355200173055ustar00rootroot00000000000000django-guardian-2.0.0/guardian/conf/__init__.py000066400000000000000000000000001347767355200214040ustar00rootroot00000000000000django-guardian-2.0.0/guardian/conf/settings.py000066400000000000000000000026521347767355200215240ustar00rootroot00000000000000import warnings from django.conf import settings from django.core.exceptions import ImproperlyConfigured try: ANONYMOUS_USER_NAME = settings.ANONYMOUS_USER_NAME except AttributeError: try: ANONYMOUS_USER_NAME = settings.ANONYMOUS_DEFAULT_USERNAME_VALUE warnings.warn("The ANONYMOUS_DEFAULT_USERNAME_VALUE setting has been renamed to ANONYMOUS_USER_NAME.", DeprecationWarning) except AttributeError: ANONYMOUS_USER_NAME = "AnonymousUser" RENDER_403 = getattr(settings, 'GUARDIAN_RENDER_403', False) TEMPLATE_403 = getattr(settings, 'GUARDIAN_TEMPLATE_403', '403.html') RAISE_403 = getattr(settings, 'GUARDIAN_RAISE_403', False) RENDER_404 = getattr(settings, 'GUARDIAN_RENDER_404', False) TEMPLATE_404 = getattr(settings, 'GUARDIAN_TEMPLATE_404', '404.html') RAISE_404 = getattr(settings, 'GUARDIAN_RAISE_404', False) GET_INIT_ANONYMOUS_USER = getattr(settings, 'GUARDIAN_GET_INIT_ANONYMOUS_USER', 'guardian.management.get_init_anonymous_user') MONKEY_PATCH = getattr(settings, 'GUARDIAN_MONKEY_PATCH', True) GET_CONTENT_TYPE = getattr(settings, 'GUARDIAN_GET_CONTENT_TYPE', 'guardian.ctypes.get_default_content_type') def check_configuration(): if RENDER_403 and RAISE_403: raise ImproperlyConfigured("Cannot use both GUARDIAN_RENDER_403 AND " "GUARDIAN_RAISE_403 - only one of this config may be True") check_configuration() django-guardian-2.0.0/guardian/core.py000066400000000000000000000216101347767355200176620ustar00rootroot00000000000000from itertools import chain from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission from django.db.models.query import QuerySet from django.utils.encoding import force_text from guardian.ctypes import get_content_type from guardian.utils import get_group_obj_perms_model, get_identity, get_user_obj_perms_model def _get_pks_model_and_ctype(objects): """ Returns the primary keys, model and content type of an iterable of Django model objects. Assumes that all objects are of the same content type. """ if isinstance(objects, QuerySet): model = objects.model pks = [force_text(pk) for pk in objects.values_list('pk', flat=True)] ctype = get_content_type(model) else: pks = [] for idx, obj in enumerate(objects): if not idx: model = type(obj) ctype = get_content_type(model) pks.append(force_text(obj.pk)) return pks, model, ctype class ObjectPermissionChecker: """ Generic object permissions checker class being the heart of ``django-guardian``. .. note:: Once checked for single object, permissions are stored and we don't hit database again if another check is called for this object. This is great for templates, views or other request based checks (assuming we don't have hundreds of permissions on a single object as we fetch all permissions for checked object). On the other hand, if we call ``has_perm`` for perm1/object1, then we change permission state and call ``has_perm`` again for same perm1/object1 on same instance of ObjectPermissionChecker we won't see a difference as permissions are already fetched and stored within cache dictionary. """ def __init__(self, user_or_group=None): """ Constructor for ObjectPermissionChecker. :param user_or_group: should be an ``User``, ``AnonymousUser`` or ``Group`` instance """ self.user, self.group = get_identity(user_or_group) self._obj_perms_cache = {} def has_perm(self, perm, obj): """ Checks if user/group has given permission for object. :param perm: permission as string, may or may not contain app_label prefix (if not prefixed, we grab app_label from ``obj``) :param obj: Django model instance for which permission should be checked """ if self.user and not self.user.is_active: return False elif self.user and self.user.is_superuser: return True if '.' in perm: _, perm = perm.split('.', 1) return perm in self.get_perms(obj) def get_group_filters(self, obj): User = get_user_model() ctype = get_content_type(obj) group_model = get_group_obj_perms_model(obj) group_rel_name = group_model.permission.field.related_query_name() if self.user: fieldname = '%s__group__%s' % ( group_rel_name, User.groups.field.related_query_name(), ) group_filters = {fieldname: self.user} else: group_filters = {'%s__group' % group_rel_name: self.group} if group_model.objects.is_generic(): group_filters.update({ '%s__content_type' % group_rel_name: ctype, '%s__object_pk' % group_rel_name: obj.pk, }) else: group_filters['%s__content_object' % group_rel_name] = obj return group_filters def get_user_filters(self, obj): ctype = get_content_type(obj) model = get_user_obj_perms_model(obj) related_name = model.permission.field.related_query_name() user_filters = {'%s__user' % related_name: self.user} if model.objects.is_generic(): user_filters.update({ '%s__content_type' % related_name: ctype, '%s__object_pk' % related_name: obj.pk, }) else: user_filters['%s__content_object' % related_name] = obj return user_filters def get_user_perms(self, obj): ctype = get_content_type(obj) perms_qs = Permission.objects.filter(content_type=ctype) user_filters = self.get_user_filters(obj) user_perms_qs = perms_qs.filter(**user_filters) user_perms = user_perms_qs.values_list("codename", flat=True) return user_perms def get_group_perms(self, obj): ctype = get_content_type(obj) perms_qs = Permission.objects.filter(content_type=ctype) group_filters = self.get_group_filters(obj) group_perms_qs = perms_qs.filter(**group_filters) group_perms = group_perms_qs.values_list("codename", flat=True) return group_perms def get_perms(self, obj): """ Returns list of ``codename``'s of all permissions for given ``obj``. :param obj: Django model instance for which permission should be checked """ if self.user and not self.user.is_active: return [] ctype = get_content_type(obj) key = self.get_local_cache_key(obj) if key not in self._obj_perms_cache: if self.user and self.user.is_superuser: perms = list(chain(*Permission.objects .filter(content_type=ctype) .values_list("codename"))) elif self.user: # Query user and group permissions separately and then combine # the results to avoid a slow query user_perms = self.get_user_perms(obj) group_perms = self.get_group_perms(obj) perms = list(set(chain(user_perms, group_perms))) else: perms = list(set(self.get_group_perms(obj))) self._obj_perms_cache[key] = perms return self._obj_perms_cache[key] def get_local_cache_key(self, obj): """ Returns cache key for ``_obj_perms_cache`` dict. """ ctype = get_content_type(obj) return (ctype.id, force_text(obj.pk)) def prefetch_perms(self, objects): """ Prefetches the permissions for objects in ``objects`` and puts them in the cache. :param objects: Iterable of Django model objects """ if self.user and not self.user.is_active: return [] User = get_user_model() pks, model, ctype = _get_pks_model_and_ctype(objects) if self.user and self.user.is_superuser: perms = list(chain( *Permission.objects .filter(content_type=ctype) .values_list("codename"))) for pk in pks: key = (ctype.id, force_text(pk)) self._obj_perms_cache[key] = perms return True group_model = get_group_obj_perms_model(model) if self.user: fieldname = 'group__%s' % ( User.groups.field.related_query_name(), ) group_filters = {fieldname: self.user} else: group_filters = {'group': self.group} if group_model.objects.is_generic(): group_filters.update({ 'content_type': ctype, 'object_pk__in': pks, }) else: group_filters.update({ 'content_object_id__in': pks }) if self.user: model = get_user_obj_perms_model(model) user_filters = { 'user': self.user, } if model.objects.is_generic(): user_filters.update({ 'content_type': ctype, 'object_pk__in': pks }) else: user_filters.update({ 'content_object_id__in': pks }) # Query user and group permissions separately and then combine # the results to avoid a slow query user_perms_qs = model.objects.filter(**user_filters).select_related('permission') group_perms_qs = group_model.objects.filter(**group_filters).select_related('permission') perms = chain(user_perms_qs, group_perms_qs) else: perms = chain( *(group_model.objects.filter(**group_filters).select_related('permission'),) ) # initialize entry in '_obj_perms_cache' for all prefetched objects for obj in objects: key = self.get_local_cache_key(obj) if key not in self._obj_perms_cache: self._obj_perms_cache[key] = [] for perm in perms: if type(perm).objects.is_generic(): key = (ctype.id, perm.object_pk) else: key = (ctype.id, force_text(perm.content_object_id)) self._obj_perms_cache[key].append(perm.permission.codename) return True django-guardian-2.0.0/guardian/ctypes.py000066400000000000000000000006431347767355200202440ustar00rootroot00000000000000from django.contrib.contenttypes.models import ContentType from django.utils.module_loading import import_string from guardian.conf import settings as guardian_settings def get_content_type(obj): get_content_type_function = import_string( guardian_settings.GET_CONTENT_TYPE) return get_content_type_function(obj) def get_default_content_type(obj): return ContentType.objects.get_for_model(obj) django-guardian-2.0.0/guardian/decorators.py000066400000000000000000000165311347767355200211050ustar00rootroot00000000000000from django.apps import apps from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME from django.db.models import Model from django.db.models.base import ModelBase from django.db.models.query import QuerySet from django.shortcuts import get_object_or_404 from django.utils.functional import wraps from guardian.exceptions import GuardianError from guardian.utils import get_40x_or_None def permission_required(perm, lookup_variables=None, **kwargs): """ Decorator for views that checks whether a user has a particular permission enabled. Optionally, instances for which check should be made may be passed as an second argument or as a tuple parameters same as those passed to ``get_object_or_404`` but must be provided as pairs of strings. This way decorator can fetch i.e. ``User`` instance based on performed request and check permissions on it (without this, one would need to fetch user instance at view's logic and check permission inside a view). :param login_url: if denied, user would be redirected to location set by this parameter. Defaults to ``django.conf.settings.LOGIN_URL``. :param redirect_field_name: name of the parameter passed if redirected. Defaults to ``django.contrib.auth.REDIRECT_FIELD_NAME``. :param return_403: if set to ``True`` then instead of redirecting to the login page, response with status code 403 is returned ( ``django.http.HttpResponseForbidden`` instance or rendered template - see :setting:`GUARDIAN_RENDER_403`). Defaults to ``False``. :param return_404: if set to ``True`` then instead of redirecting to the login page, response with status code 404 is returned ( ``django.http.HttpResponseNotFound`` instance or rendered template - see :setting:`GUARDIAN_RENDER_404`). Defaults to ``False``. :param accept_global_perms: if set to ``True``, then *object level permission* would be required **only if user does NOT have global permission** for target *model*. If turned on, makes this decorator like an extension over standard ``django.contrib.admin.decorators.permission_required`` as it would check for global permissions first. Defaults to ``False``. Examples:: @permission_required('auth.change_user', return_403=True) def my_view(request): return HttpResponse('Hello') @permission_required('auth.change_user', (User, 'username', 'username')) def my_view(request, username): ''' auth.change_user permission would be checked based on given 'username'. If view's parameter would be named ``name``, we would rather use following decorator:: @permission_required('auth.change_user', (User, 'username', 'name')) ''' user = get_object_or_404(User, username=username) return user.get_absolute_url() @permission_required('auth.change_user', (User, 'username', 'username', 'groups__name', 'group_name')) def my_view(request, username, group_name): ''' Similar to the above example, here however we also make sure that one of user's group is named same as request's ``group_name`` param. ''' user = get_object_or_404(User, username=username, group__name=group_name) return user.get_absolute_url() """ login_url = kwargs.pop('login_url', settings.LOGIN_URL) redirect_field_name = kwargs.pop( 'redirect_field_name', REDIRECT_FIELD_NAME) return_403 = kwargs.pop('return_403', False) return_404 = kwargs.pop('return_404', False) accept_global_perms = kwargs.pop('accept_global_perms', False) # Check if perm is given as string in order not to decorate # view function itself which makes debugging harder if not isinstance(perm, str): raise GuardianError("First argument must be in format: " "'app_label.codename or a callable which return similar string'") def decorator(view_func): def _wrapped_view(request, *args, **kwargs): # if more than one parameter is passed to the decorator we try to # fetch object for which check would be made obj = None if lookup_variables: model, lookups = lookup_variables[0], lookup_variables[1:] # Parse model if isinstance(model, str): splitted = model.split('.') if len(splitted) != 2: raise GuardianError("If model should be looked up from " "string it needs format: 'app_label.ModelClass'") model = apps.get_model(*splitted) elif issubclass(model.__class__, (Model, ModelBase, QuerySet)): pass else: raise GuardianError("First lookup argument must always be " "a model, string pointing at app/model or queryset. " "Given: %s (type: %s)" % (model, type(model))) # Parse lookups if len(lookups) % 2 != 0: raise GuardianError("Lookup variables must be provided " "as pairs of lookup_string and view_arg") lookup_dict = {} for lookup, view_arg in zip(lookups[::2], lookups[1::2]): if view_arg not in kwargs: raise GuardianError("Argument %s was not passed " "into view function" % view_arg) lookup_dict[lookup] = kwargs[view_arg] obj = get_object_or_404(model, **lookup_dict) response = get_40x_or_None(request, perms=[perm], obj=obj, login_url=login_url, redirect_field_name=redirect_field_name, return_403=return_403, return_404=return_404, accept_global_perms=accept_global_perms) if response: return response return view_func(request, *args, **kwargs) return wraps(view_func)(_wrapped_view) return decorator def permission_required_or_403(perm, *args, **kwargs): """ Simple wrapper for permission_required decorator. Standard Django's permission_required decorator redirects user to login page in case permission check failed. This decorator may be used to return HttpResponseForbidden (status 403) instead of redirection. The only difference between ``permission_required`` decorator is that this one always set ``return_403`` parameter to ``True``. """ kwargs['return_403'] = True return permission_required(perm, *args, **kwargs) def permission_required_or_404(perm, *args, **kwargs): """ Simple wrapper for permission_required decorator. Standard Django's permission_required decorator redirects user to login page in case permission check failed. This decorator may be used to return HttpResponseNotFound (status 404) instead of redirection. The only difference between ``permission_required`` decorator is that this one always set ``return_404`` parameter to ``True``. """ kwargs['return_404'] = True return permission_required(perm, *args, **kwargs) django-guardian-2.0.0/guardian/exceptions.py000066400000000000000000000006571347767355200211230ustar00rootroot00000000000000""" Exceptions used by django-guardian. All internal and guardian-specific errors should extend GuardianError class. """ class GuardianError(Exception): pass class NotUserNorGroup(GuardianError): pass class ObjectNotPersisted(GuardianError): pass class WrongAppError(GuardianError): pass class MixedContentTypeError(GuardianError): pass class MultipleIdentityAndObjectError(GuardianError): pass django-guardian-2.0.0/guardian/forms.py000066400000000000000000000144531347767355200200670ustar00rootroot00000000000000from django import forms from django.utils.translation import ugettext as _ from guardian.shortcuts import assign_perm, get_group_perms, get_perms_for_model, get_user_perms, remove_perm class BaseObjectPermissionsForm(forms.Form): """ Base form for object permissions management. Needs to be extended for usage with users and/or groups. """ def __init__(self, obj, *args, **kwargs): """ Constructor for BaseObjectPermissionsForm. :param obj: Any instance which form would use to manage object permissions" """ self.obj = obj super().__init__(*args, **kwargs) field_name = self.get_obj_perms_field_name() self.fields[field_name] = self.get_obj_perms_field() def get_obj_perms_field(self): """ Returns field instance for object permissions management. May be replaced entirely. """ field_class = self.get_obj_perms_field_class() field = field_class( label=self.get_obj_perms_field_label(), choices=self.get_obj_perms_field_choices(), initial=list(self.get_obj_perms_field_initial()), widget=self.get_obj_perms_field_widget(), required=self.are_obj_perms_required(), ) return field def get_obj_perms_field_name(self): """ Returns name of the object permissions management field. Default: ``permission``. """ return 'permissions' def get_obj_perms_field_label(self): """ Returns label of the object permissions management field. Defualt: ``_("Permissions")`` (marked to be translated). """ return _("Permissions") def get_obj_perms_field_choices(self): """ Returns choices for object permissions management field. Default: list of tuples ``(codename, name)`` for each ``Permission`` instance for the managed object. """ choices = [(p.codename, p.name) for p in get_perms_for_model(self.obj)] return choices def get_obj_perms_field_initial(self): """ Returns initial object permissions management field choices. Default: ``[]`` (empty list). """ return [] def get_obj_perms_field_class(self): """ Returns object permissions management field's base class. Default: ``django.forms.MultipleChoiceField``. """ return forms.MultipleChoiceField def get_obj_perms_field_widget(self): """ Returns object permissions management field's widget base class. Default: ``django.forms.SelectMultiple``. """ return forms.SelectMultiple def are_obj_perms_required(self): """ Indicates if at least one object permission should be required. Default: ``False``. """ return False def save_obj_perms(self): """ Must be implemented in concrete form class. This method should store selected object permissions. """ raise NotImplementedError class UserObjectPermissionsForm(BaseObjectPermissionsForm): """ Object level permissions management form for usage with ``User`` instances. Example usage:: from django.shortcuts import get_object_or_404 from myapp.models import Post from guardian.forms import UserObjectPermissionsForm from django.contrib.auth.models import User def my_view(request, post_slug, user_id): user = get_object_or_404(User, id=user_id) post = get_object_or_404(Post, slug=post_slug) form = UserObjectPermissionsForm(user, post, request.POST or None) if request.method == 'POST' and form.is_valid(): form.save_obj_perms() ... """ def __init__(self, user, *args, **kwargs): self.user = user super().__init__(*args, **kwargs) def get_obj_perms_field_initial(self): perms = get_user_perms(self.user, self.obj) return perms def save_obj_perms(self): """ Saves selected object permissions by creating new ones and removing those which were not selected but already exists. Should be called *after* form is validated. """ perms = set(self.cleaned_data[self.get_obj_perms_field_name()]) model_perms = set([c[0] for c in self.get_obj_perms_field_choices()]) init_perms = set(self.get_obj_perms_field_initial()) to_remove = (model_perms - perms) & init_perms for perm in to_remove: remove_perm(perm, self.user, self.obj) for perm in perms - init_perms: assign_perm(perm, self.user, self.obj) class GroupObjectPermissionsForm(BaseObjectPermissionsForm): """ Object level permissions management form for usage with ``Group`` instances. Example usage:: from django.shortcuts import get_object_or_404 from myapp.models import Post from guardian.forms import GroupObjectPermissionsForm from guardian.models import Group def my_view(request, post_slug, group_id): group = get_object_or_404(Group, id=group_id) post = get_object_or_404(Post, slug=post_slug) form = GroupObjectPermissionsForm(group, post, request.POST or None) if request.method == 'POST' and form.is_valid(): form.save_obj_perms() ... """ def __init__(self, group, *args, **kwargs): self.group = group super().__init__(*args, **kwargs) def get_obj_perms_field_initial(self): perms = get_group_perms(self.group, self.obj) return perms def save_obj_perms(self): """ Saves selected object permissions by creating new ones and removing those which were not selected but already exists. Should be called *after* form is validated. """ perms = set(self.cleaned_data[self.get_obj_perms_field_name()]) model_perms = set([c[0] for c in self.get_obj_perms_field_choices()]) init_perms = set(self.get_obj_perms_field_initial()) to_remove = (model_perms - perms) & init_perms for perm in to_remove: remove_perm(perm, self.group, self.obj) for perm in perms - init_perms: assign_perm(perm, self.group, self.obj) django-guardian-2.0.0/guardian/locale/000077500000000000000000000000001347767355200176175ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/es/000077500000000000000000000000001347767355200202265ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/es/LC_MESSAGES/000077500000000000000000000000001347767355200220135ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/es/LC_MESSAGES/django.mo000066400000000000000000000032301347767355200236100ustar00rootroot00000000000000%@AHMSelt y    F$kp  g$6 =GN^pw)=CK_qz      ActionEditGroupGroup permissionsGroupsHistoryHomeManage groupManage userObjectObject permissionsPermissionsPermissions saved.Please correct the errors below.SaveThis group does not existThis user does not existThis value may contain only letters, numbers and @/./+/-/_ characters.UserUser permissionsUsernameUsersView on siteobject IDProject-Id-Version: django-guardian 1.0.4 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-05-10 18:01+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Adrián López Calvo Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); AcciónEditarGrupoPermisos de grupoGruposHistorialInicioGestionar grupoGestionar usuarioObjetoPermisos de objetoPermisosPermisos guardados.Por favor, corrija los siguientes erroresGuardarEste grupo no existeEste usuario no existeEste valor puede contener sólo letras, números y @/./+/-/_.UsuarioPermisos de usuarioNombre de usuarioUsuariosVer en el sitioID del objetodjango-guardian-2.0.0/guardian/locale/es/LC_MESSAGES/django.po000066400000000000000000000140441347767355200236200ustar00rootroot00000000000000# Spanish translation of django-guardian. # This file is distributed under the same license as django-guardian's package. # Translator: Adrián López Calvo , 2013. # msgid "" msgstr "" "Project-Id-Version: django-guardian 1.0.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-05-10 18:01+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Adrián López Calvo \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:32 admin.py:42 forms.py:55 msgid "Permissions" msgstr "Permisos" #: admin.py:189 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:22 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:25 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:25 #: templates/admin/guardian/model/change_form.html:7 #: templates/admin/guardian/model/obj_perms_manage.html:17 #: templates/admin/guardian/model/obj_perms_manage_group.html:14 #: templates/admin/guardian/model/obj_perms_manage_group.html:25 #: templates/admin/guardian/model/obj_perms_manage_user.html:14 #: templates/admin/guardian/model/obj_perms_manage_user.html:25 msgid "Object permissions" msgstr "Permisos de objeto" #: admin.py:278 admin.py:331 msgid "Permissions saved." msgstr "Permisos guardados." #: admin.py:375 msgid "Username" msgstr "Nombre de usuario" #: admin.py:378 msgid "This value may contain only letters, numbers and @/./+/-/_ characters." msgstr "Este valor puede contener sólo letras, números y @/./+/-/_." #: admin.py:380 msgid "This user does not exist" msgstr "Este usuario no existe" #: admin.py:397 msgid "This group does not exist" msgstr "Este grupo no existe" #: models.py:46 msgid "object ID" msgstr "ID del objeto" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:18 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:10 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:10 #: templates/admin/guardian/model/obj_perms_manage.html:13 #: templates/admin/guardian/model/obj_perms_manage_group.html:10 #: templates/admin/guardian/model/obj_perms_manage_user.html:10 msgid "Home" msgstr "Inicio" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:33 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:93 #: templates/admin/guardian/model/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:82 msgid "Please correct the errors below." msgstr "Por favor, corrija los siguientes errores" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:37 #: templates/admin/guardian/model/obj_perms_manage.html:32 msgid "Users" msgstr "Usuarios" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:43 #: templates/admin/guardian/model/obj_perms_manage.html:38 msgid "User permissions" msgstr "Permisos de usuario" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:46 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:36 #: templates/admin/guardian/model/obj_perms_manage.html:41 #: templates/admin/guardian/model/obj_perms_manage_user.html:30 msgid "User" msgstr "Usuario" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:50 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:110 #: templates/admin/guardian/model/obj_perms_manage.html:45 #: templates/admin/guardian/model/obj_perms_manage.html:99 msgid "Action" msgstr "Acción" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:67 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:127 #: templates/admin/guardian/model/obj_perms_manage.html:62 #: templates/admin/guardian/model/obj_perms_manage.html:116 msgid "Edit" msgstr "Editar" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:80 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:73 #: templates/admin/guardian/model/obj_perms_manage_user.html:15 msgid "Manage user" msgstr "Gestionar usuario" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:97 #: templates/admin/guardian/model/obj_perms_manage.html:86 msgid "Groups" msgstr "Grupos" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:103 #: templates/admin/guardian/model/obj_perms_manage.html:92 msgid "Group permissions" msgstr "Permisos de grupo" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:106 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:36 #: templates/admin/guardian/model/obj_perms_manage.html:95 #: templates/admin/guardian/model/obj_perms_manage_group.html:30 msgid "Group" msgstr "Grupo" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:140 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:127 #: templates/admin/guardian/model/obj_perms_manage_group.html:15 msgid "Manage group" msgstr "Gestionar grupo" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:28 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:28 #: templates/admin/guardian/model/obj_perms_manage_group.html:27 #: templates/admin/guardian/model/obj_perms_manage_user.html:27 msgid "Object" msgstr "Objeto" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:48 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:48 #: templates/admin/guardian/model/obj_perms_manage_group.html:37 #: templates/admin/guardian/model/obj_perms_manage_user.html:37 msgid "Save" msgstr "Guardar" #: templates/admin/guardian/model/change_form.html:8 msgid "History" msgstr "Historial" #: templates/admin/guardian/model/change_form.html:9 msgid "View on site" msgstr "Ver en el sitio" django-guardian-2.0.0/guardian/locale/fr/000077500000000000000000000000001347767355200202265ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/fr/LC_MESSAGES/000077500000000000000000000000001347767355200220135ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/fr/LC_MESSAGES/django.mo000066400000000000000000000034231347767355200236140ustar00rootroot00000000000000%0181=ou   @ &GLf [5%[bz P)E o{        ActionEditEnter a value compatible with User.USERNAME_FIELDGroupGroup permissionsGroupsHomeManage groupManage userObjectObject permissionsPermissionsPermissions saved.Please correct the error below.Please correct the errors below.Please correct the errors below.SaveThis group does not existThis user does not existUserUser identificationUser permissionsUsersobject IDProject-Id-Version: django-guardian 1.1.1 Report-Msgid-Bugs-To: POT-Creation-Date: 2014-01-12 19:50+0100 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Morgan Aubert Language: fr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); ActionModifierEntrez une valeur compatible avec User.USERNAME_FIELDGroupePermissions des groupesGroupesAccueilGérer le groupeGérer l'utilisateurObjetPermissions de l'objetPermissionsPermissions sauvegardées.Veuillez corriger l'erreur ci-dessous.Veuillez corriger les erreurs ci-dessous.Veuillez corriger les erreurs ci-dessous.SauvegarderCe groupe n'existe pasCet utilisateur n'existe pasUtilisateurIdentification de l'utilisateurPermissions des utilisateursUtilisateursID de l'objetdjango-guardian-2.0.0/guardian/locale/fr/LC_MESSAGES/django.po000066400000000000000000000140731347767355200236220ustar00rootroot00000000000000# French translation of django-guardian. # This file is distributed under the same license as the PACKAGE package. # Translator: Morgan Aubert , 2014. # msgid "" msgstr "" "Project-Id-Version: django-guardian 1.2.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-01-12 19:50+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Morgan Aubert \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: admin.py:32 admin.py:42 forms.py:55 msgid "Permissions" msgstr "Permissions" #: admin.py:127 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:12 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:25 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:25 #: templates/admin/guardian/model/change_form.html:5 #: templates/admin/guardian/model/obj_perms_manage.html:17 #: templates/admin/guardian/model/obj_perms_manage_group.html:14 #: templates/admin/guardian/model/obj_perms_manage_group.html:25 #: templates/admin/guardian/model/obj_perms_manage_user.html:14 #: templates/admin/guardian/model/obj_perms_manage_user.html:25 msgid "Object permissions" msgstr "Permissions de l'objet" #: admin.py:218 admin.py:271 msgid "Permissions saved." msgstr "Permissions sauvegardées." #: admin.py:395 msgid "User identification" msgstr "Identification de l'utilisateur" #: admin.py:397 msgid "This user does not exist" msgstr "Cet utilisateur n'existe pas" #: admin.py:398 msgid "Enter a value compatible with User.USERNAME_FIELD" msgstr "Entrez une valeur compatible avec User.USERNAME_FIELD" #: admin.py:421 msgid "This group does not exist" msgstr "Ce groupe n'existe pas" #: models.py:46 msgid "object ID" msgstr "ID de l'objet" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:8 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:10 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:10 #: templates/admin/guardian/model/obj_perms_manage.html:13 #: templates/admin/guardian/model/obj_perms_manage_group.html:10 #: templates/admin/guardian/model/obj_perms_manage_user.html:10 msgid "Home" msgstr "Accueil" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:21 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:82 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "Veuillez corriger l'erreur ci-dessous." msgstr[1] "Veuillez corriger les erreurs ci-dessous." #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:25 #: templates/admin/guardian/model/obj_perms_manage.html:32 msgid "Users" msgstr "Utilisateurs" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:38 msgid "User permissions" msgstr "Permissions des utilisateurs" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:33 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:41 #: templates/admin/guardian/model/obj_perms_manage_user.html:30 msgid "User" msgstr "Utilisateur" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:37 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:98 #: templates/admin/guardian/model/obj_perms_manage.html:45 #: templates/admin/guardian/model/obj_perms_manage.html:99 msgid "Action" msgstr "Action" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:54 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:115 #: templates/admin/guardian/model/obj_perms_manage.html:62 #: templates/admin/guardian/model/obj_perms_manage.html:116 msgid "Edit" msgstr "Modifier" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:70 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:73 #: templates/admin/guardian/model/obj_perms_manage_user.html:15 msgid "Manage user" msgstr "Gérer l'utilisateur" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:86 #: templates/admin/guardian/model/obj_perms_manage.html:86 msgid "Groups" msgstr "Groupes" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:89 #: templates/admin/guardian/model/obj_perms_manage.html:92 msgid "Group permissions" msgstr "Permissions des groupes" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:94 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:95 #: templates/admin/guardian/model/obj_perms_manage_group.html:30 msgid "Group" msgstr "Groupe" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:132 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:127 #: templates/admin/guardian/model/obj_perms_manage_group.html:15 msgid "Manage group" msgstr "Gérer le groupe" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:28 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:28 #: templates/admin/guardian/model/obj_perms_manage_group.html:27 #: templates/admin/guardian/model/obj_perms_manage_user.html:27 msgid "Object" msgstr "Objet" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:45 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:45 #: templates/admin/guardian/model/obj_perms_manage_group.html:37 #: templates/admin/guardian/model/obj_perms_manage_user.html:37 msgid "Save" msgstr "Sauvegarder" #: templates/admin/guardian/model/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:82 msgid "Please correct the errors below." msgstr "Veuillez corriger les erreurs ci-dessous." django-guardian-2.0.0/guardian/locale/nl/000077500000000000000000000000001347767355200202305ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/nl/LC_MESSAGES/000077500000000000000000000000001347767355200220155ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/nl/LC_MESSAGES/django.mo000066400000000000000000000032761347767355200236240ustar00rootroot00000000000000%0181=ou   @ &GLf J <RXiqv ;3;S oy       ActionEditEnter a value compatible with User.USERNAME_FIELDGroupGroup permissionsGroupsHomeManage groupManage userObjectObject permissionsPermissionsPermissions saved.Please correct the error below.Please correct the errors below.Please correct the errors below.SaveThis group does not existThis user does not existUserUser identificationUser permissionsUsersobject IDProject-Id-Version: 1.4.9 Report-Msgid-Bugs-To: POT-Creation-Date: 2017-10-16 07:58+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Melvyn Sopacua Language: nl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); ActieVeranderenVoer een waarde in die compatible is met User.USERNAME_FIELDGroepGroep permissiesGroepenHomeBeheer de groepBeheer de gebruikerObjectObject permissiesPermissiesPermissies zijn opgeslagen.Corrigeer de fout hieronder.Corrigeer de fouten hieronder.Corrigeer de fout hieronder.OpslaanDeze groep bestaat nietDeze gebruiker bestaat nietGebruikerGebruikersindentificatieGebruiker's permissiesGebruikersobject IDdjango-guardian-2.0.0/guardian/locale/nl/LC_MESSAGES/django.po000066400000000000000000000141761347767355200236300ustar00rootroot00000000000000# Dutch translation of django-guardian # This file is distributed under the same license as the PACKAGE package. # Translator: Melvyn Sopacua , 2017. # msgid "" msgstr "" "Project-Id-Version: 1.4.9\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-10-16 07:58+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Melvyn Sopacua \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:30 admin.py:41 forms.py:52 msgid "Permissions" msgstr "Permissies" #: admin.py:136 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:24 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:24 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:29 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:40 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:24 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:29 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:40 #: templates/admin/guardian/model/change_form.html:6 #: templates/admin/guardian/model/obj_perms_manage.html:16 #: templates/admin/guardian/model/obj_perms_manage_group.html:14 #: templates/admin/guardian/model/obj_perms_manage_group.html:25 #: templates/admin/guardian/model/obj_perms_manage_user.html:14 #: templates/admin/guardian/model/obj_perms_manage_user.html:25 msgid "Object permissions" msgstr "Object permissies" #: admin.py:253 admin.py:329 msgid "Permissions saved." msgstr "Permissies zijn opgeslagen." #: admin.py:457 msgid "User identification" msgstr "Gebruikersindentificatie" #: admin.py:460 msgid "This user does not exist" msgstr "Deze gebruiker bestaat niet" #: admin.py:462 msgid "Enter a value compatible with User.USERNAME_FIELD" msgstr "Voer een waarde in die compatible is met User.USERNAME_FIELD" #: admin.py:485 msgid "This group does not exist" msgstr "Deze groep bestaat niet" #: models.py:46 msgid "object ID" msgstr "object ID" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:9 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:11 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:11 #: templates/admin/guardian/model/obj_perms_manage.html:12 #: templates/admin/guardian/model/obj_perms_manage_group.html:10 #: templates/admin/guardian/model/obj_perms_manage_user.html:10 msgid "Home" msgstr "Home" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:33 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:94 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "Corrigeer de fout hieronder." msgstr[1] "Corrigeer de fouten hieronder." #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:37 #: templates/admin/guardian/model/obj_perms_manage.html:31 msgid "Users" msgstr "Gebruikers" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:40 #: templates/admin/guardian/model/obj_perms_manage.html:37 msgid "User permissions" msgstr "Gebruiker's permissies" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:45 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:49 #: templates/admin/guardian/model/obj_perms_manage.html:40 #: templates/admin/guardian/model/obj_perms_manage_user.html:30 msgid "User" msgstr "Gebruiker" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:49 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:110 #: templates/admin/guardian/model/obj_perms_manage.html:44 #: templates/admin/guardian/model/obj_perms_manage.html:98 msgid "Action" msgstr "Actie" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:66 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:127 #: templates/admin/guardian/model/obj_perms_manage.html:61 #: templates/admin/guardian/model/obj_perms_manage.html:115 msgid "Edit" msgstr "Veranderen" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:82 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:31 #: templates/admin/guardian/model/obj_perms_manage.html:72 #: templates/admin/guardian/model/obj_perms_manage_user.html:15 msgid "Manage user" msgstr "Beheer de gebruiker" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:98 #: templates/admin/guardian/model/obj_perms_manage.html:85 msgid "Groups" msgstr "Groepen" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:101 #: templates/admin/guardian/model/obj_perms_manage.html:91 msgid "Group permissions" msgstr "Groep permissies" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:106 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:49 #: templates/admin/guardian/model/obj_perms_manage.html:94 #: templates/admin/guardian/model/obj_perms_manage_group.html:30 msgid "Group" msgstr "Groep" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:144 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:31 #: templates/admin/guardian/model/obj_perms_manage.html:126 #: templates/admin/guardian/model/obj_perms_manage_group.html:15 msgid "Manage group" msgstr "Beheer de groep" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:43 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:43 #: templates/admin/guardian/model/obj_perms_manage_group.html:27 #: templates/admin/guardian/model/obj_perms_manage_user.html:27 msgid "Object" msgstr "Object" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:60 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:60 #: templates/admin/guardian/model/obj_perms_manage_group.html:37 #: templates/admin/guardian/model/obj_perms_manage_user.html:37 msgid "Save" msgstr "Opslaan" #: templates/admin/guardian/model/obj_perms_manage.html:27 #: templates/admin/guardian/model/obj_perms_manage.html:81 msgid "Please correct the errors below." msgstr "Corrigeer de fout hieronder." django-guardian-2.0.0/guardian/locale/pl/000077500000000000000000000000001347767355200202325ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/pl/LC_MESSAGES/000077500000000000000000000000001347767355200220175ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/pl/LC_MESSAGES/django.mo000066400000000000000000000032471347767355200236240ustar00rootroot00000000000000    @ `F %/.5 LXHn$G ^j   ActionEditGroupGroup permissionsGroupsHomeManage groupManage userObjectObject permissionsPermissionsPermissions saved.Please correct the error below.Please correct the errors below.Please correct the errors below.SaveThis group does not existThis user does not existThis value may contain only letters, numbers and @/./+/-/_ characters.UserUser permissionsUsernameUsersobject IDProject-Id-Version: django-guardian 1.2 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-12-29 00:14+0100 PO-Revision-Date: 2013-12-29 17:29-0600 Last-Translator: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: pl Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); X-Translated-Using: django-rosetta 0.7.3 AkcjaEdytujGrupaUprawnienia grupyGrupyStartZarządzaj grupąZarządzaj użytkownikiemObiektUprawnienia do obiektuUprawnieniaZapisano uprawnienia.Proszę poprawić poniższe błędy.Proszę poprawić poniższy błąd.Proszę poprawić poniższe błędy.ZapiszTa grupa nie istniejeTen użytkownik nie istniejeTa wartość może zawierać tylko litery, cyfry oraz symbole @/./+/-/_UżytkownikUprawnienia użytkownikaUżytkownikUżytkownicyID obiektudjango-guardian-2.0.0/guardian/locale/pl/LC_MESSAGES/django.po000066400000000000000000000141651347767355200236300ustar00rootroot00000000000000# Polish translation of django-guardian. # This file is distributed under the same license as django-guardian's package. # Translator: Tomasz Wsuł <2nickers@gmail.com>, 2013. # msgid "" msgstr "" "Project-Id-Version: django-guardian 1.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-12-29 00:14+0100\n" "PO-Revision-Date: 2013-12-29 17:29-0600\n" "Last-Translator: <2nickers@gmail.com>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pl\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Translated-Using: django-rosetta 0.7.3\n" #: admin.py:32 admin.py:42 forms.py:55 msgid "Permissions" msgstr "Uprawnienia" #: admin.py:189 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:12 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:25 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:25 #: templates/admin/guardian/model/change_form.html:5 #: templates/admin/guardian/model/obj_perms_manage.html:17 #: templates/admin/guardian/model/obj_perms_manage_group.html:14 #: templates/admin/guardian/model/obj_perms_manage_group.html:25 #: templates/admin/guardian/model/obj_perms_manage_user.html:14 #: templates/admin/guardian/model/obj_perms_manage_user.html:25 msgid "Object permissions" msgstr "Uprawnienia do obiektu" #: admin.py:278 admin.py:331 msgid "Permissions saved." msgstr "Zapisano uprawnienia." #: admin.py:375 msgid "Username" msgstr "Użytkownik" #: admin.py:378 msgid "This value may contain only letters, numbers and @/./+/-/_ characters." msgstr "Ta wartość może zawierać tylko litery, cyfry oraz symbole @/./+/-/_" #: admin.py:380 msgid "This user does not exist" msgstr "Ten użytkownik nie istnieje" #: admin.py:397 msgid "This group does not exist" msgstr "Ta grupa nie istnieje" #: models.py:46 msgid "object ID" msgstr "ID obiektu" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:8 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:10 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:10 #: templates/admin/guardian/model/obj_perms_manage.html:13 #: templates/admin/guardian/model/obj_perms_manage_group.html:10 #: templates/admin/guardian/model/obj_perms_manage_user.html:10 msgid "Home" msgstr "Start" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:21 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:82 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "Proszę poprawić poniższe błędy." msgstr[1] "Proszę poprawić poniższy błąd." #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:25 #: templates/admin/guardian/model/obj_perms_manage.html:32 msgid "Users" msgstr "Użytkownicy" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:38 msgid "User permissions" msgstr "Uprawnienia użytkownika" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:33 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:41 #: templates/admin/guardian/model/obj_perms_manage_user.html:30 msgid "User" msgstr "Użytkownik" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:37 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:98 #: templates/admin/guardian/model/obj_perms_manage.html:45 #: templates/admin/guardian/model/obj_perms_manage.html:99 msgid "Action" msgstr "Akcja" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:54 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:115 #: templates/admin/guardian/model/obj_perms_manage.html:62 #: templates/admin/guardian/model/obj_perms_manage.html:116 msgid "Edit" msgstr "Edytuj" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:70 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:73 #: templates/admin/guardian/model/obj_perms_manage_user.html:15 msgid "Manage user" msgstr "Zarządzaj użytkownikiem" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:86 #: templates/admin/guardian/model/obj_perms_manage.html:86 msgid "Groups" msgstr "Grupy" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:89 #: templates/admin/guardian/model/obj_perms_manage.html:92 msgid "Group permissions" msgstr "Uprawnienia grupy" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:94 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:95 #: templates/admin/guardian/model/obj_perms_manage_group.html:30 msgid "Group" msgstr "Grupa" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:132 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:127 #: templates/admin/guardian/model/obj_perms_manage_group.html:15 msgid "Manage group" msgstr "Zarządzaj grupą" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:28 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:28 #: templates/admin/guardian/model/obj_perms_manage_group.html:27 #: templates/admin/guardian/model/obj_perms_manage_user.html:27 msgid "Object" msgstr "Obiekt" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:45 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:45 #: templates/admin/guardian/model/obj_perms_manage_group.html:37 #: templates/admin/guardian/model/obj_perms_manage_user.html:37 msgid "Save" msgstr "Zapisz" #: templates/admin/guardian/model/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:82 msgid "Please correct the errors below." msgstr "Proszę poprawić poniższe błędy." django-guardian-2.0.0/guardian/locale/pt_BR/000077500000000000000000000000001347767355200206255ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/pt_BR/LC_MESSAGES/000077500000000000000000000000001347767355200224125ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/pt_BR/LC_MESSAGES/django.mo000066400000000000000000000034001347767355200242060ustar00rootroot00000000000000%0181=ou   @ &GLf <C3J~ E #Osz       ActionEditEnter a value compatible with User.USERNAME_FIELDGroupGroup permissionsGroupsHomeManage groupManage userObjectObject permissionsPermissionsPermissions saved.Please correct the error below.Please correct the errors below.Please correct the errors below.SaveThis group does not existThis user does not existUserUser identificationUser permissionsUsersobject IDProject-Id-Version: 1.2.0 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-12-14 02:39-0300 PO-Revision-Date: 2013-12-14 02:39-0300 Last-Translator: Fabio C. Barrionuevo da Luz Language-Team: pt_BR Language: Brazilian Portuguese MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); AçãoEditarInsira um valor compatível com User.USERNAME_FIELDGrupoGrupos de permissõesGruposHomeGerenciar grupoGerênciar usuárioObjetoPermissões para ObjetosPermissõesPermissões salvasPor favor, corrija o erro abaixo.Por favor, corrija os erros abaixo.Por favor, corrija os erros abaixo.SalvarEste grupo não existeEste usuário não existeUsuárioIdentificação de UsuárioPermissões de usuáriosUsuáriosid do objetodjango-guardian-2.0.0/guardian/locale/pt_BR/LC_MESSAGES/django.po000066400000000000000000000141501347767355200242150ustar00rootroot00000000000000# django-guardian # Copyright (c) 2010-2013 Lukasz Balcerzak # This file is distributed under the same license as the PACKAGE package. # Fabio C. Barrionuevo da Luz , 2013. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 1.2.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-12-14 02:39-0300\n" "PO-Revision-Date: 2013-12-14 02:39-0300\n" "Last-Translator: Fabio C. Barrionuevo da Luz \n" "Language-Team: pt_BR \n" "Language: Brazilian Portuguese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: admin.py:32 admin.py:42 forms.py:55 msgid "Permissions" msgstr "Permissões" #: admin.py:189 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:12 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:25 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:25 #: templates/admin/guardian/model/change_form.html:5 #: templates/admin/guardian/model/obj_perms_manage.html:17 #: templates/admin/guardian/model/obj_perms_manage_group.html:14 #: templates/admin/guardian/model/obj_perms_manage_group.html:25 #: templates/admin/guardian/model/obj_perms_manage_user.html:14 #: templates/admin/guardian/model/obj_perms_manage_user.html:25 msgid "Object permissions" msgstr "Permissões para Objetos" #: admin.py:280 admin.py:333 msgid "Permissions saved." msgstr "Permissões salvas" #: admin.py:377 msgid "User identification" msgstr "Identificação de Usuário" #: admin.py:379 msgid "This user does not exist" msgstr "Este usuário não existe" #: admin.py:380 msgid "Enter a value compatible with User.USERNAME_FIELD" msgstr "Insira um valor compatível com User.USERNAME_FIELD" #: admin.py:403 msgid "This group does not exist" msgstr "Este grupo não existe" #: models.py:46 msgid "object ID" msgstr "id do objeto" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:8 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:10 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:10 #: templates/admin/guardian/model/obj_perms_manage.html:13 #: templates/admin/guardian/model/obj_perms_manage_group.html:10 #: templates/admin/guardian/model/obj_perms_manage_user.html:10 msgid "Home" msgstr "Home" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:21 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:82 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "Por favor, corrija o erro abaixo." msgstr[1] "Por favor, corrija os erros abaixo." #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:25 #: templates/admin/guardian/model/obj_perms_manage.html:32 msgid "Users" msgstr "Usuários" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:38 msgid "User permissions" msgstr "Permissões de usuários" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:33 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:41 #: templates/admin/guardian/model/obj_perms_manage_user.html:30 msgid "User" msgstr "Usuário" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:37 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:98 #: templates/admin/guardian/model/obj_perms_manage.html:45 #: templates/admin/guardian/model/obj_perms_manage.html:99 msgid "Action" msgstr "Ação" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:54 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:115 #: templates/admin/guardian/model/obj_perms_manage.html:62 #: templates/admin/guardian/model/obj_perms_manage.html:116 msgid "Edit" msgstr "Editar" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:70 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:73 #: templates/admin/guardian/model/obj_perms_manage_user.html:15 msgid "Manage user" msgstr "Gerênciar usuário" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:86 #: templates/admin/guardian/model/obj_perms_manage.html:86 msgid "Groups" msgstr "Grupos" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:89 #: templates/admin/guardian/model/obj_perms_manage.html:92 msgid "Group permissions" msgstr "Grupos de permissões" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:94 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:95 #: templates/admin/guardian/model/obj_perms_manage_group.html:30 msgid "Group" msgstr "Grupo" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:132 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:127 #: templates/admin/guardian/model/obj_perms_manage_group.html:15 msgid "Manage group" msgstr "Gerenciar grupo" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:28 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:28 #: templates/admin/guardian/model/obj_perms_manage_group.html:27 #: templates/admin/guardian/model/obj_perms_manage_user.html:27 msgid "Object" msgstr "Objeto" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:45 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:45 #: templates/admin/guardian/model/obj_perms_manage_group.html:37 #: templates/admin/guardian/model/obj_perms_manage_user.html:37 msgid "Save" msgstr "Salvar" #: templates/admin/guardian/model/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:82 msgid "Please correct the errors below." msgstr "Por favor, corrija os erros abaixo." django-guardian-2.0.0/guardian/locale/ru/000077500000000000000000000000001347767355200202455ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/ru/LC_MESSAGES/000077500000000000000000000000001347767355200220325ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/ru/LC_MESSAGES/django.mo000066400000000000000000000042351347767355200236350ustar00rootroot00000000000000%018=CU\ a nz @ 4FM    #/* Zg T)--@;nj#.Rr     ActionEditGroupGroup permissionsGroupsHomeManage groupManage userObjectObject permissionsPermissionsPermissions saved.Please correct the error below.Please correct the errors below.Please correct the errors below.SaveThis group does not existThis user does not existThis value may contain only letters, numbers and @/./+/-/_ characters.UserUser permissionsUsernameUsersobject IDProject-Id-Version: django-guardian 1.2 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-12-29 00:14+0100 PO-Revision-Date: 2015-09-25 10:30+0300 Last-Translator: 2nickers@gmail.com MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: pl Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); X-Translated-Using: django-rosetta 0.7.3 Language-Team: X-Generator: Poedit 1.8.5 ДействиеРедактироватьГруппаГрупповые праваГруппыДомойУправление группойУправление пользователемОбъектПрава на объектПраваПрава сохранены.Исправьте ошибку ниже.Исправьте ошибки ниже.Исправьте ошибки ниже.СохранитьЭта группа не существуетЭтот пользователь не существуетЭто значение должно содержать буквы, цифры и символы @/./+/-/_ПользовательПрава пользователяИмя пользователяПользователиID объектаdjango-guardian-2.0.0/guardian/locale/ru/LC_MESSAGES/django.po000066400000000000000000000146441347767355200236450ustar00rootroot00000000000000# Polish translation of django-guardian. # This file is distributed under the same license as django-guardian's package. # Translator: Tomasz Wsuł <2nickers@gmail.com>, 2013. # msgid "" msgstr "" "Project-Id-Version: django-guardian 1.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-12-29 00:14+0100\n" "PO-Revision-Date: 2013-12-29 17:29-0600\n" "Last-Translator: <2nickers@gmail.com>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pl\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Translated-Using: django-rosetta 0.7.3\n" #: admin.py:32 admin.py:42 forms.py:55 msgid "Permissions" msgstr "Права" #: admin.py:189 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:12 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:25 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:14 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:25 #: templates/admin/guardian/model/change_form.html:5 #: templates/admin/guardian/model/obj_perms_manage.html:17 #: templates/admin/guardian/model/obj_perms_manage_group.html:14 #: templates/admin/guardian/model/obj_perms_manage_group.html:25 #: templates/admin/guardian/model/obj_perms_manage_user.html:14 #: templates/admin/guardian/model/obj_perms_manage_user.html:25 msgid "Object permissions" msgstr "Права на объект" #: admin.py:278 admin.py:331 msgid "Permissions saved." msgstr "Права сохранены." #: admin.py:375 msgid "Username" msgstr "Имя пользователя" #: admin.py:378 msgid "This value may contain only letters, numbers and @/./+/-/_ characters." msgstr "Это значение должно содержать буквы, цифры и символы @/./+/-/_" #: admin.py:380 msgid "This user does not exist" msgstr "Этот пользователь не существует" #: admin.py:397 msgid "This group does not exist" msgstr "Эта группа не существует" #: models.py:46 msgid "object ID" msgstr "ID объекта" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:8 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:10 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:10 #: templates/admin/guardian/model/obj_perms_manage.html:13 #: templates/admin/guardian/model/obj_perms_manage_group.html:10 #: templates/admin/guardian/model/obj_perms_manage_user.html:10 msgid "Home" msgstr "Домой" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:21 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:82 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "Исправьте ошибку ниже." msgstr[1] "Исправьте ошибки ниже." #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:25 #: templates/admin/guardian/model/obj_perms_manage.html:32 msgid "Users" msgstr "Пользователи" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:38 msgid "User permissions" msgstr "Права пользователя" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:33 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:41 #: templates/admin/guardian/model/obj_perms_manage_user.html:30 msgid "User" msgstr "Пользователь" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:37 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:98 #: templates/admin/guardian/model/obj_perms_manage.html:45 #: templates/admin/guardian/model/obj_perms_manage.html:99 msgid "Action" msgstr "Действие" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:54 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:115 #: templates/admin/guardian/model/obj_perms_manage.html:62 #: templates/admin/guardian/model/obj_perms_manage.html:116 msgid "Edit" msgstr "Редактировать" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:70 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:73 #: templates/admin/guardian/model/obj_perms_manage_user.html:15 msgid "Manage user" msgstr "Управление пользователем" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:86 #: templates/admin/guardian/model/obj_perms_manage.html:86 msgid "Groups" msgstr "Группы" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:89 #: templates/admin/guardian/model/obj_perms_manage.html:92 msgid "Group permissions" msgstr "Групповые права" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:94 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:34 #: templates/admin/guardian/model/obj_perms_manage.html:95 #: templates/admin/guardian/model/obj_perms_manage_group.html:30 msgid "Group" msgstr "Группа" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:132 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:15 #: templates/admin/guardian/model/obj_perms_manage.html:127 #: templates/admin/guardian/model/obj_perms_manage_group.html:15 msgid "Manage group" msgstr "Управление группой" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:28 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:28 #: templates/admin/guardian/model/obj_perms_manage_group.html:27 #: templates/admin/guardian/model/obj_perms_manage_user.html:27 msgid "Object" msgstr "Объект" #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:45 #: templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:45 #: templates/admin/guardian/model/obj_perms_manage_group.html:37 #: templates/admin/guardian/model/obj_perms_manage_user.html:37 msgid "Save" msgstr "Сохранить" #: templates/admin/guardian/model/obj_perms_manage.html:28 #: templates/admin/guardian/model/obj_perms_manage.html:82 msgid "Please correct the errors below." msgstr "Исправьте ошибки ниже." django-guardian-2.0.0/guardian/locale/zh_Hans/000077500000000000000000000000001347767355200212115ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/zh_Hans/LC_MESSAGES/000077500000000000000000000000001347767355200227765ustar00rootroot00000000000000django-guardian-2.0.0/guardian/locale/zh_Hans/LC_MESSAGES/django.mo000066400000000000000000000031071347767355200245760ustar00rootroot00000000000000%0181=ou   @ &GLf d%',T Xbf m w +   )6 =     ActionEditEnter a value compatible with User.USERNAME_FIELDGroupGroup permissionsGroupsHomeManage groupManage userObjectObject permissionsPermissionsPermissions saved.Please correct the error below.Please correct the errors below.Please correct the errors below.SaveThis group does not existThis user does not existUserUser identificationUser permissionsUsersobject IDProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2018-07-27 10:10+0800 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; 行为编辑输入对应 User.USERNAME_FIELD 的值组组权限组首页管理组管理用户对象对象权限权限权限已保存请修复下列错误请修复下列错误请修复下列错误保存组不存在用户不存在用户用户身份用户权限用户对象 IDdjango-guardian-2.0.0/guardian/locale/zh_Hans/LC_MESSAGES/django.po000066400000000000000000000152761347767355200246130ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-07-27 10:10+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: guardian/admin.py:30 guardian/admin.py:41 guardian/forms.py:52 msgid "Permissions" msgstr "权限" #: guardian/admin.py:136 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:24 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:24 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:29 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:40 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:24 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:29 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:40 #: guardian/templates/admin/guardian/model/change_form.html:6 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:16 #: guardian/templates/admin/guardian/model/obj_perms_manage_group.html:14 #: guardian/templates/admin/guardian/model/obj_perms_manage_group.html:25 #: guardian/templates/admin/guardian/model/obj_perms_manage_user.html:14 #: guardian/templates/admin/guardian/model/obj_perms_manage_user.html:25 msgid "Object permissions" msgstr "对象权限" #: guardian/admin.py:253 guardian/admin.py:329 msgid "Permissions saved." msgstr "权限已保存" #: guardian/admin.py:457 msgid "User identification" msgstr "用户 ID" #: guardian/admin.py:460 msgid "This user does not exist" msgstr "用户不存在" #: guardian/admin.py:462 msgid "Enter a value compatible with User.USERNAME_FIELD" msgstr "输入对应 User.USERNAME_FIELD 的值" #: guardian/admin.py:485 msgid "This group does not exist" msgstr "组不存在" #: guardian/models.py:46 msgid "object ID" msgstr "对象 ID" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:9 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:11 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:11 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:12 #: guardian/templates/admin/guardian/model/obj_perms_manage_group.html:10 #: guardian/templates/admin/guardian/model/obj_perms_manage_user.html:10 msgid "Home" msgstr "首页" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:33 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:94 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "请修复下列错误" msgstr[1] "请修复下列错误" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:37 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:31 msgid "Users" msgstr "用户" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:40 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:37 msgid "User permissions" msgstr "用户权限" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:45 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:49 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:40 #: guardian/templates/admin/guardian/model/obj_perms_manage_user.html:30 msgid "User" msgstr "用户" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:49 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:110 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:44 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:98 msgid "Action" msgstr "行为" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:66 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:127 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:61 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:115 msgid "Edit" msgstr "编辑" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:82 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:31 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:72 #: guardian/templates/admin/guardian/model/obj_perms_manage_user.html:15 msgid "Manage user" msgstr "管理用户" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:98 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:85 msgid "Groups" msgstr "组" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:101 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:91 msgid "Group permissions" msgstr "组权限" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:106 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:49 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:94 #: guardian/templates/admin/guardian/model/obj_perms_manage_group.html:30 msgid "Group" msgstr "组" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html:144 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:31 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:126 #: guardian/templates/admin/guardian/model/obj_perms_manage_group.html:15 msgid "Manage group" msgstr "管理组" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:43 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:43 #: guardian/templates/admin/guardian/model/obj_perms_manage_group.html:27 #: guardian/templates/admin/guardian/model/obj_perms_manage_user.html:27 msgid "Object" msgstr "对象" #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html:60 #: guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html:60 #: guardian/templates/admin/guardian/model/obj_perms_manage_group.html:37 #: guardian/templates/admin/guardian/model/obj_perms_manage_user.html:37 msgid "Save" msgstr "保存" #: guardian/templates/admin/guardian/model/obj_perms_manage.html:27 #: guardian/templates/admin/guardian/model/obj_perms_manage.html:81 msgid "Please correct the errors below." msgstr "请修复下列错误" django-guardian-2.0.0/guardian/management/000077500000000000000000000000001347767355200204745ustar00rootroot00000000000000django-guardian-2.0.0/guardian/management/__init__.py000066400000000000000000000027511347767355200226120ustar00rootroot00000000000000import django from django.contrib.auth import get_user_model from django.db.models import signals from django.utils.module_loading import import_string from guardian.conf import settings as guardian_settings def get_init_anonymous_user(User): """ Returns User model instance that would be referenced by guardian when permissions are checked against users that haven't signed into the system. :param User: User model - result of ``django.contrib.auth.get_user_model``. """ kwargs = { User.USERNAME_FIELD: guardian_settings.ANONYMOUS_USER_NAME } user = User(**kwargs) user.set_unusable_password() return user def create_anonymous_user(sender, **kwargs): """ Creates anonymous User instance with id and username from settings. """ User = get_user_model() try: lookup = {User.USERNAME_FIELD: guardian_settings.ANONYMOUS_USER_NAME} User.objects.get(**lookup) except User.DoesNotExist: retrieve_anonymous_function = import_string( guardian_settings.GET_INIT_ANONYMOUS_USER) user = retrieve_anonymous_function(User) user.save() # Only create an anonymous user if support is enabled. if guardian_settings.ANONYMOUS_USER_NAME is not None: from django.apps import apps guardian_app = apps.get_app_config('guardian') signals.post_migrate.connect(create_anonymous_user, sender=guardian_app, dispatch_uid="guardian.management.create_anonymous_user") django-guardian-2.0.0/guardian/management/commands/000077500000000000000000000000001347767355200222755ustar00rootroot00000000000000django-guardian-2.0.0/guardian/management/commands/__init__.py000066400000000000000000000000001347767355200243740ustar00rootroot00000000000000django-guardian-2.0.0/guardian/management/commands/clean_orphan_obj_perms.py000066400000000000000000000012341347767355200273400ustar00rootroot00000000000000from django.core.management.base import BaseCommand from guardian.utils import clean_orphan_obj_perms class Command(BaseCommand): """ clean_orphan_obj_perms command is a tiny wrapper around :func:`guardian.utils.clean_orphan_obj_perms`. Usage:: $ python manage.py clean_orphan_obj_perms Removed 11 object permission entries with no targets """ help = "Removes object permissions with not existing targets" def handle(self, **options): removed = clean_orphan_obj_perms() if options['verbosity'] > 0: print("Removed %d object permission entries with no targets" % removed) django-guardian-2.0.0/guardian/managers.py000066400000000000000000000140021347767355200205240ustar00rootroot00000000000000from django.db import models from django.db.models import Q from guardian.core import ObjectPermissionChecker from guardian.ctypes import get_content_type from guardian.exceptions import ObjectNotPersisted from guardian.models import Permission import warnings class BaseObjectPermissionManager(models.Manager): @property def user_or_group_field(self): try: self.model._meta.get_field('user') return 'user' except models.fields.FieldDoesNotExist: return 'group' def is_generic(self): try: self.model._meta.get_field('object_pk') return True except models.fields.FieldDoesNotExist: return False def assign_perm(self, perm, user_or_group, obj): """ Assigns permission with given ``perm`` for an instance ``obj`` and ``user``. """ if getattr(obj, 'pk', None) is None: raise ObjectNotPersisted("Object %s needs to be persisted first" % obj) ctype = get_content_type(obj) if not isinstance(perm, Permission): permission = Permission.objects.get(content_type=ctype, codename=perm) else: permission = perm kwargs = {'permission': permission, self.user_or_group_field: user_or_group} if self.is_generic(): kwargs['content_type'] = ctype kwargs['object_pk'] = obj.pk else: kwargs['content_object'] = obj obj_perm, _ = self.get_or_create(**kwargs) return obj_perm def bulk_assign_perm(self, perm, user_or_group, queryset): """ Bulk assigns permissions with given ``perm`` for an objects in ``queryset`` and ``user_or_group``. """ ctype = get_content_type(queryset.model) if not isinstance(perm, Permission): permission = Permission.objects.get(content_type=ctype, codename=perm) else: permission = perm checker = ObjectPermissionChecker(user_or_group) checker.prefetch_perms(queryset) assigned_perms = [] for instance in queryset: if not checker.has_perm(permission.codename, instance): kwargs = {'permission': permission, self.user_or_group_field: user_or_group} if self.is_generic(): kwargs['content_type'] = ctype kwargs['object_pk'] = instance.pk else: kwargs['content_object'] = instance assigned_perms.append(self.model(**kwargs)) self.model.objects.bulk_create(assigned_perms) return assigned_perms def assign_perm_to_many(self, perm, users_or_groups, obj): """ Bulk assigns given ``perm`` for the object ``obj`` to a set of users or a set of groups. """ ctype = get_content_type(obj) if not isinstance(perm, Permission): permission = Permission.objects.get(content_type=ctype, codename=perm) else: permission = perm kwargs = {'permission': permission} if self.is_generic(): kwargs['content_type'] = ctype kwargs['object_pk'] = obj.pk else: kwargs['content_object'] = obj to_add = [] field = self.user_or_group_field for user in users_or_groups: kwargs[field] = user to_add.append( self.model(**kwargs) ) return self.model.objects.bulk_create(to_add) def assign(self, perm, user_or_group, obj): """ Depreciated function name left in for compatibility""" 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) return self.assign_perm(perm, user_or_group, obj) def remove_perm(self, perm, user_or_group, obj): """ Removes permission ``perm`` for an instance ``obj`` and given ``user_or_group``. Please note that we do NOT fetch object permission from database - we use ``Queryset.delete`` method for removing it. Main implication of this is that ``post_delete`` signals would NOT be fired. """ if getattr(obj, 'pk', None) is None: raise ObjectNotPersisted("Object %s needs to be persisted first" % obj) filters = Q(**{self.user_or_group_field: user_or_group}) if isinstance(perm, Permission): filters &= Q(permission=perm) else: filters &= Q(permission__codename=perm, permission__content_type=get_content_type(obj)) if self.is_generic(): filters &= Q(object_pk=obj.pk) else: filters &= Q(content_object__pk=obj.pk) return self.filter(filters).delete() def bulk_remove_perm(self, perm, user_or_group, queryset): """ Removes permission ``perm`` for a ``queryset`` and given ``user_or_group``. Please note that we do NOT fetch object permission from database - we use ``Queryset.delete`` method for removing it. Main implication of this is that ``post_delete`` signals would NOT be fired. """ filters = Q(**{self.user_or_group_field: user_or_group}) if isinstance(perm, Permission): filters &= Q(permission=perm) else: ctype = get_content_type(queryset.model) filters &= Q(permission__codename=perm, permission__content_type=ctype) if self.is_generic(): filters &= Q(object_pk__in=[str(pk) for pk in queryset.values_list('pk', flat=True)]) else: filters &= Q(content_object__in=queryset) return self.filter(filters).delete() class UserObjectPermissionManager(BaseObjectPermissionManager): pass class GroupObjectPermissionManager(BaseObjectPermissionManager): pass django-guardian-2.0.0/guardian/migrations/000077500000000000000000000000001347767355200205345ustar00rootroot00000000000000django-guardian-2.0.0/guardian/migrations/0001_initial.py000066400000000000000000000041701347767355200232010ustar00rootroot00000000000000from django.db import models, migrations from django.conf import settings class Migration(migrations.Migration): dependencies = [ ('contenttypes', '0001_initial'), ('auth', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='GroupObjectPermission', fields=[ ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), ('object_pk', models.CharField( max_length=255, verbose_name='object ID')), ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), ('group', models.ForeignKey(to='auth.Group', on_delete=models.CASCADE)), ('permission', models.ForeignKey(to='auth.Permission', on_delete=models.CASCADE)), ], options={ }, bases=(models.Model,), ), migrations.CreateModel( name='UserObjectPermission', fields=[ ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')), ('object_pk', models.CharField( max_length=255, verbose_name='object ID')), ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), ('permission', models.ForeignKey(to='auth.Permission', on_delete=models.CASCADE)), ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ }, bases=(models.Model,), ), migrations.AlterUniqueTogether( name='userobjectpermission', unique_together=set([('user', 'permission', 'object_pk')]), ), migrations.AlterUniqueTogether( name='groupobjectpermission', unique_together=set([('group', 'permission', 'object_pk')]), ), ] django-guardian-2.0.0/guardian/migrations/__init__.py000066400000000000000000000000001347767355200226330ustar00rootroot00000000000000django-guardian-2.0.0/guardian/mixins.py000066400000000000000000000246651347767355200202560ustar00rootroot00000000000000from collections.abc import Iterable from django.conf import settings from django.contrib.auth.decorators import login_required, REDIRECT_FIELD_NAME from django.core.exceptions import ImproperlyConfigured, PermissionDenied from guardian.models import UserObjectPermission from guardian.utils import get_40x_or_None, get_anonymous_user from guardian.shortcuts import get_objects_for_user class LoginRequiredMixin: """ A login required mixin for use with class based views. This Class is a light wrapper around the `login_required` decorator and hence function parameters are just attributes defined on the class. Due to parent class order traversal this mixin must be added as the left most mixin of a view. The mixin has exaclty the same flow as `login_required` decorator: If the user isn't logged in, redirect to ``settings.LOGIN_URL``, passing the current absolute path in the query string. Example: ``/accounts/login/?next=/polls/3/``. If the user is logged in, execute the view normally. The view code is free to assume the user is logged in. **Class Settings** ``LoginRequiredMixin.redirect_field_name`` *Default*: ``'next'`` ``LoginRequiredMixin.login_url`` *Default*: ``settings.LOGIN_URL`` """ redirect_field_name = REDIRECT_FIELD_NAME login_url = settings.LOGIN_URL def dispatch(self, request, *args, **kwargs): return login_required(redirect_field_name=self.redirect_field_name, login_url=self.login_url)( super().dispatch )(request, *args, **kwargs) class PermissionRequiredMixin: """ A view mixin that verifies if the current logged in user has the specified permission by wrapping the ``request.user.has_perm(..)`` method. If a `get_object()` method is defined either manually or by including another mixin (for example ``SingleObjectMixin``) or ``self.object`` is defined then the permission will be tested against that specific instance, alternatively you can specify `get_permission_object()` method if ``self.object`` or `get_object()` does not return the object against you want to test permission .. note: Testing of a permission against a specific object instance requires an authentication backend that supports. Please see ``django-guardian`` to add object level permissions to your project. The mixin does the following: If the user isn't logged in, redirect to settings.LOGIN_URL, passing the current absolute path in the query string. Example: /accounts/login/?next=/polls/3/. If the `raise_exception` is set to True than rather than redirect to login page a `PermissionDenied` (403) is raised. If the user is logged in, and passes the permission check than the view is executed normally. **Example Usage**:: class SecureView(PermissionRequiredMixin, View): ... permission_required = 'auth.change_user' ... **Class Settings** ``PermissionRequiredMixin.permission_required`` *Default*: ``None``, must be set to either a string or list of strings in format: *.*. ``PermissionRequiredMixin.login_url`` *Default*: ``settings.LOGIN_URL`` ``PermissionRequiredMixin.redirect_field_name`` *Default*: ``'next'`` ``PermissionRequiredMixin.return_403`` *Default*: ``False``. Returns 403 error page instead of redirecting user. ``PermissionRequiredMixin.return_404`` *Default*: ``False``. Returns 404 error page instead of redirecting user. ``PermissionRequiredMixin.raise_exception`` *Default*: ``False`` `permission_required` - the permission to check of form "." i.e. 'polls.can_vote' for a permission on a model in the polls application. ``PermissionRequiredMixin.accept_global_perms`` *Default*: ``False``, If accept_global_perms would be set to True, then mixing would first check for global perms, if none found, then it will proceed to check object level permissions. ``PermissionRequiredMixin.permission_object`` *Default*: ``(not set)``, object against which test the permission; if not set fallback to ``self.get_permission_object()`` which return ``self.get_object()`` or ``self.object`` by default. """ # default class view settings login_url = settings.LOGIN_URL permission_required = None redirect_field_name = REDIRECT_FIELD_NAME return_403 = False return_404 = False raise_exception = False accept_global_perms = False def get_required_permissions(self, request=None): """ Returns list of permissions in format *.* that should be checked against *request.user* and *object*. By default, it returns list from ``permission_required`` attribute. :param request: Original request. """ if isinstance(self.permission_required, str): perms = [self.permission_required] elif isinstance(self.permission_required, Iterable): perms = [p for p in self.permission_required] else: raise ImproperlyConfigured("'PermissionRequiredMixin' requires " "'permission_required' attribute to be set to " "'.' but is set to '%s' instead" % self.permission_required) return perms def get_permission_object(self): if hasattr(self, 'permission_object'): return self.permission_object return (hasattr(self, 'get_object') and self.get_object() or getattr(self, 'object', None)) def check_permissions(self, request): """ Checks if *request.user* has all permissions returned by *get_required_permissions* method. :param request: Original request. """ obj = self.get_permission_object() forbidden = get_40x_or_None(request, perms=self.get_required_permissions( request), obj=obj, login_url=self.login_url, redirect_field_name=self.redirect_field_name, return_403=self.return_403, return_404=self.return_404, accept_global_perms=self.accept_global_perms ) if forbidden: self.on_permission_check_fail(request, forbidden, obj=obj) if forbidden and self.raise_exception: raise PermissionDenied() return forbidden def on_permission_check_fail(self, request, response, obj=None): """ Method called upon permission check fail. By default it does nothing and should be overridden, if needed. :param request: Original request :param response: 403 response returned by *check_permissions* method. :param obj: Object that was fetched from the view (using ``get_object`` method or ``object`` attribute, in that order). """ def dispatch(self, request, *args, **kwargs): self.request = request self.args = args self.kwargs = kwargs response = self.check_permissions(request) if response: return response return super().dispatch(request, *args, **kwargs) class GuardianUserMixin: @staticmethod def get_anonymous(): return get_anonymous_user() def add_obj_perm(self, perm, obj): return UserObjectPermission.objects.assign_perm(perm, self, obj) def del_obj_perm(self, perm, obj): return UserObjectPermission.objects.remove_perm(perm, self, obj) class PermissionListMixin: """ A view mixin that filter object in queryset for the current logged by required permission. **Example Usage**:: class SecureView(PermissionListMixin, ListView): ... permission_required = 'articles.view_article' ... or:: class SecureView(PermissionListMixin, ListView): ... permission_required = 'auth.change_user' get_objects_for_user_extra_kwargs = {'use_groups': False} ... **Class Settings** ``PermissionListMixin.permission_required`` *Default*: ``None``, must be set to either a string or list of strings in format: *.*. ``PermissionListMixin.get_objects_for_user_extra_kwargs`` *Default*: ``{}``, A extra params to pass for ```guardian.shorcuts.get_objects_for_user``` """ permission_required = None get_objects_for_user_extra_kwargs = {} def get_required_permissions(self, request=None): """ Returns list of permissions in format *.* that should be checked against *request.user* and *object*. By default, it returns list from ``permission_required`` attribute. :param request: Original request. """ if isinstance(self.permission_required, str): perms = [self.permission_required] elif isinstance(self.permission_required, Iterable): perms = [p for p in self.permission_required] else: raise ImproperlyConfigured("'PermissionRequiredMixin' requires " "'permission_required' attribute to be set to " "'.' but is set to '%s' instead" % self.permission_required) return perms def get_get_objects_for_user_kwargs(self, queryset): """ Returns dict of kwargs that should be pass to ```get_objects_for_user```. :param request: Queryset to filter """ return dict(user=self.request.user, perms=self.get_required_permissions(self.request), klass=queryset, **self.get_objects_for_user_extra_kwargs) def get_queryset(self, *args, **kwargs): qs = super().get_queryset(*args, **kwargs) return get_objects_for_user(**self.get_get_objects_for_user_kwargs(qs)) django-guardian-2.0.0/guardian/models.py000066400000000000000000000057541347767355200202300ustar00rootroot00000000000000from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import ugettext_lazy as _ from guardian.compat import user_model_label from guardian.ctypes import get_content_type from guardian.managers import GroupObjectPermissionManager, UserObjectPermissionManager class BaseObjectPermission(models.Model): """ Abstract ObjectPermission class. Actual class should additionally define a ``content_object`` field and either ``user`` or ``group`` field. """ permission = models.ForeignKey(Permission, on_delete=models.CASCADE) class Meta: abstract = True def __str__(self): return '%s | %s | %s' % ( str(self.content_object), str(getattr(self, 'user', False) or self.group), str(self.permission.codename)) def save(self, *args, **kwargs): content_type = get_content_type(self.content_object) if content_type != self.permission.content_type: raise ValidationError("Cannot persist permission not designed for " "this class (permission's type is %r and object's type is %r)" % (self.permission.content_type, content_type)) return super().save(*args, **kwargs) class BaseGenericObjectPermission(models.Model): content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_pk = models.CharField(_('object ID'), max_length=255) content_object = GenericForeignKey(fk_field='object_pk') class Meta: abstract = True class UserObjectPermissionBase(BaseObjectPermission): """ **Manager**: :manager:`UserObjectPermissionManager` """ user = models.ForeignKey(user_model_label, on_delete=models.CASCADE) objects = UserObjectPermissionManager() class Meta: abstract = True unique_together = ['user', 'permission', 'content_object'] class UserObjectPermission(UserObjectPermissionBase, BaseGenericObjectPermission): class Meta: unique_together = ['user', 'permission', 'object_pk'] class GroupObjectPermissionBase(BaseObjectPermission): """ **Manager**: :manager:`GroupObjectPermissionManager` """ group = models.ForeignKey(Group, on_delete=models.CASCADE) objects = GroupObjectPermissionManager() class Meta: abstract = True unique_together = ['group', 'permission', 'content_object'] class GroupObjectPermission(GroupObjectPermissionBase, BaseGenericObjectPermission): class Meta: unique_together = ['group', 'permission', 'object_pk'] setattr(Group, 'add_obj_perm', lambda self, perm, obj: GroupObjectPermission.objects.assign_perm(perm, self, obj)) setattr(Group, 'del_obj_perm', lambda self, perm, obj: GroupObjectPermission.objects.remove_perm(perm, self, obj)) django-guardian-2.0.0/guardian/shortcuts.py000066400000000000000000001000771347767355200207750ustar00rootroot00000000000000""" Convenient shortcuts to manage or check object permissions. """ import warnings from collections import defaultdict from itertools import groupby from django.apps import apps from django.contrib.auth import get_user_model from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType from django.db.models import Count, Q, QuerySet from django.shortcuts import _get_queryset from guardian.core import ObjectPermissionChecker from guardian.ctypes import get_content_type from guardian.exceptions import MixedContentTypeError, WrongAppError, MultipleIdentityAndObjectError from guardian.models import GroupObjectPermission from guardian.utils import get_anonymous_user, get_group_obj_perms_model, get_identity, get_user_obj_perms_model def assign_perm(perm, user_or_group, obj=None): """ Assigns permission to user/group and object pair. :param perm: proper permission for given ``obj``, as string (in format: ``app_label.codename`` or ``codename``) or ``Permission`` instance. If ``obj`` is not given, must be in format ``app_label.codename`` or ``Permission`` instance. :param user_or_group: instance of ``User``, ``AnonymousUser``, ``Group``, list of ``User`` or ``Group``, or queryset of ``User`` or ``Group``; passing any other object would raise ``guardian.exceptions.NotUserNorGroup`` exception :param obj: persisted Django's ``Model`` instance or QuerySet of Django ``Model`` instances or ``None`` if assigning global permission. Default is ``None``. We can assign permission for ``Model`` instance for specific user: >>> from django.contrib.sites.models import Site >>> from guardian.models import User >>> from guardian.shortcuts import assign_perm >>> site = Site.objects.get_current() >>> user = User.objects.create(username='joe') >>> assign_perm("change_site", user, site) >>> user.has_perm("change_site", site) True ... or we can assign permission for group: >>> group = Group.objects.create(name='joe-group') >>> user.groups.add(group) >>> assign_perm("delete_site", group, site) >>> user.has_perm("delete_site", site) True **Global permissions** This function may also be used to assign standard, *global* permissions if ``obj`` parameter is omitted. Added Permission would be returned in that case: >>> assign_perm("sites.change_site", user) """ user, group = get_identity(user_or_group) # If obj is None we try to operate on global permissions if obj is None: if not isinstance(perm, Permission): try: app_label, codename = perm.split('.', 1) except ValueError: raise ValueError("For global permissions, first argument must be in" " format: 'app_label.codename' (is %r)" % perm) perm = Permission.objects.get(content_type__app_label=app_label, codename=codename) if user: user.user_permissions.add(perm) return perm if group: group.permissions.add(perm) return perm if not isinstance(perm, Permission): perm = perm.split('.')[-1] if isinstance(obj, QuerySet): if isinstance(user_or_group, (QuerySet, list)): raise MultipleIdentityAndObjectError("Only bulk operations on either users/groups OR objects supported") if user: model = get_user_obj_perms_model(obj.model) return model.objects.bulk_assign_perm(perm, user, obj) if group: model = get_group_obj_perms_model(obj.model) return model.objects.bulk_assign_perm(perm, group, obj) if isinstance(user_or_group, (QuerySet, list)): if user: model = get_user_obj_perms_model(obj) return model.objects.assign_perm_to_many(perm, user, obj) if group: model = get_group_obj_perms_model(obj) return model.objects.assign_perm_to_many(perm, group, obj) if user: model = get_user_obj_perms_model(obj) return model.objects.assign_perm(perm, user, obj) if group: model = get_group_obj_perms_model(obj) return model.objects.assign_perm(perm, group, obj) def assign(perm, user_or_group, obj=None): """ Depreciated function name left in for compatibility""" 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) return assign_perm(perm, user_or_group, obj) def remove_perm(perm, user_or_group=None, obj=None): """ Removes permission from user/group and object pair. :param perm: proper permission for given ``obj``, as string (in format: ``app_label.codename`` or ``codename``). If ``obj`` is not given, must be in format ``app_label.codename``. :param user_or_group: instance of ``User``, ``AnonymousUser`` or ``Group``; passing any other object would raise ``guardian.exceptions.NotUserNorGroup`` exception :param obj: persisted Django's ``Model`` instance or QuerySet of Django ``Model`` instances or ``None`` if assigning global permission. Default is ``None``. """ user, group = get_identity(user_or_group) if obj is None: try: app_label, codename = perm.split('.', 1) except ValueError: raise ValueError("For global permissions, first argument must be in" " format: 'app_label.codename' (is %r)" % perm) perm = Permission.objects.get(content_type__app_label=app_label, codename=codename) if user: user.user_permissions.remove(perm) return elif group: group.permissions.remove(perm) return if not isinstance(perm, Permission): perm = perm.split('.')[-1] if isinstance(obj, QuerySet): if user: model = get_user_obj_perms_model(obj.model) return model.objects.bulk_remove_perm(perm, user, obj) if group: model = get_group_obj_perms_model(obj.model) return model.objects.bulk_remove_perm(perm, group, obj) if user: model = get_user_obj_perms_model(obj) return model.objects.remove_perm(perm, user, obj) if group: model = get_group_obj_perms_model(obj) return model.objects.remove_perm(perm, group, obj) def get_perms(user_or_group, obj): """ Returns permissions for given user/group and object pair, as list of strings. """ check = ObjectPermissionChecker(user_or_group) return check.get_perms(obj) def get_user_perms(user, obj): """ Returns permissions for given user and object pair, as list of strings, only those assigned directly for the user. """ check = ObjectPermissionChecker(user) return check.get_user_perms(obj) def get_group_perms(user_or_group, obj): """ Returns permissions for given user/group and object pair, as list of strings. It returns only those which are inferred through groups. """ check = ObjectPermissionChecker(user_or_group) return check.get_group_perms(obj) def get_perms_for_model(cls): """ Returns queryset of all Permission objects for the given class. It is possible to pass Model as class or instance. """ if isinstance(cls, str): app_label, model_name = cls.split('.') model = apps.get_model(app_label, model_name) else: model = cls ctype = get_content_type(model) return Permission.objects.filter(content_type=ctype) def get_users_with_perms(obj, attach_perms=False, with_superusers=False, with_group_users=True, only_with_perms_in=None): """ Returns queryset of all ``User`` objects with *any* object permissions for the given ``obj``. :param obj: persisted Django's ``Model`` instance :param attach_perms: Default: ``False``. If set to ``True`` result would be dictionary of ``User`` instances with permissions' codenames list as values. This would fetch users eagerly! :param with_superusers: Default: ``False``. If set to ``True`` result would contain all superusers. :param with_group_users: Default: ``True``. If set to ``False`` result would **not** contain those users who have only group permissions for given ``obj``. :param only_with_perms_in: Default: ``None``. If set to an iterable of permission strings then only users with those permissions would be returned. Example:: >>> from django.contrib.flatpages.models import FlatPage >>> from django.contrib.auth.models import User >>> from guardian.shortcuts import assign_perm, get_users_with_perms >>> >>> page = FlatPage.objects.create(title='Some page', path='/some/page/') >>> joe = User.objects.create_user('joe', 'joe@example.com', 'joesecret') >>> dan = User.objects.create_user('dan', 'dan@example.com', 'dansecret') >>> assign_perm('change_flatpage', joe, page) >>> assign_perm('delete_flatpage', dan, page) >>> >>> get_users_with_perms(page) [, ] >>> >>> get_users_with_perms(page, attach_perms=True) {: [u'change_flatpage'], : [u'delete_flatpage']} >>> get_users_with_perms(page, only_with_perms_in=['change_flatpage']) [] """ ctype = get_content_type(obj) if not attach_perms: # It's much easier without attached perms so we do it first if that is # the case user_model = get_user_obj_perms_model(obj) related_name = user_model.user.field.related_query_name() if user_model.objects.is_generic(): user_filters = { '%s__content_type' % related_name: ctype, '%s__object_pk' % related_name: obj.pk, } else: user_filters = {'%s__content_object' % related_name: obj} qset = Q(**user_filters) if only_with_perms_in is not None: permission_ids = Permission.objects.filter(content_type=ctype, codename__in=only_with_perms_in).values_list('id', flat=True) qset &= Q(**{ '%s__permission_id__in' % related_name: permission_ids, }) if with_group_users: group_model = get_group_obj_perms_model(obj) group_rel_name = group_model.group.field.related_query_name() if group_model.objects.is_generic(): group_filters = { 'groups__%s__content_type' % group_rel_name: ctype, 'groups__%s__object_pk' % group_rel_name: obj.pk, } else: group_filters = { 'groups__%s__content_object' % group_rel_name: obj, } if only_with_perms_in is not None: group_filters.update({ 'groups__%s__permission_id__in' % group_rel_name: permission_ids, }) qset = qset | Q(**group_filters) if with_superusers: qset = qset | Q(is_superuser=True) return get_user_model().objects.filter(qset).distinct() else: # TODO: Do not hit db for each user! users = {} for user in get_users_with_perms(obj, with_group_users=with_group_users, only_with_perms_in=only_with_perms_in, with_superusers=with_superusers): # TODO: Support the case of set with_group_users but not with_superusers. if with_group_users or with_superusers: users[user] = sorted(get_perms(user, obj)) else: users[user] = sorted(get_user_perms(user, obj)) return users def get_groups_with_perms(obj, attach_perms=False): """ Returns queryset of all ``Group`` objects with *any* object permissions for the given ``obj``. :param obj: persisted Django's ``Model`` instance :param attach_perms: Default: ``False``. If set to ``True`` result would be dictionary of ``Group`` instances with permissions' codenames list as values. This would fetch groups eagerly! Example:: >>> from django.contrib.flatpages.models import FlatPage >>> from guardian.shortcuts import assign_perm, get_groups_with_perms >>> from guardian.models import Group >>> >>> page = FlatPage.objects.create(title='Some page', path='/some/page/') >>> admins = Group.objects.create(name='Admins') >>> assign_perm('change_flatpage', admins, page) >>> >>> get_groups_with_perms(page) [] >>> >>> get_groups_with_perms(page, attach_perms=True) {: [u'change_flatpage']} """ ctype = get_content_type(obj) group_model = get_group_obj_perms_model(obj) if not attach_perms: # It's much easier without attached perms so we do it first if that is the case group_rel_name = group_model.group.field.related_query_name() if group_model.objects.is_generic(): group_filters = { '%s__content_type' % group_rel_name: ctype, '%s__object_pk' % group_rel_name: obj.pk, } else: group_filters = {'%s__content_object' % group_rel_name: obj} return Group.objects.filter(**group_filters).distinct() else: group_perms_mapping = defaultdict(list) groups_with_perms = get_groups_with_perms(obj) qs = group_model.objects.filter(group__in=groups_with_perms).prefetch_related('group', 'permission') if group_model is GroupObjectPermission: qs = qs.filter(object_pk=obj.pk, content_type=ctype) else: qs = qs.filter(content_object_id=obj.pk) for group_perm in qs: group_perms_mapping[group_perm.group].append(group_perm.permission.codename) return dict(group_perms_mapping) def get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=False, with_superuser=True, accept_global_perms=True): """ Returns queryset of objects for which a given ``user`` has *all* permissions present at ``perms``. :param user: ``User`` or ``AnonymousUser`` instance for which objects would be returned. :param perms: single permission string, or sequence of permission strings which should be checked. If ``klass`` parameter is not given, those should be full permission names rather than only codenames (i.e. ``auth.change_user``). If more than one permission is present within sequence, their content type **must** be the same or ``MixedContentTypeError`` exception would be raised. :param klass: may be a Model, Manager or QuerySet object. If not given this parameter would be computed based on given ``params``. :param use_groups: if ``False``, wouldn't check user's groups object permissions. Default is ``True``. :param any_perm: if True, any of permission in sequence is accepted. Default is ``False``. :param with_superuser: if ``True`` and if ``user.is_superuser`` is set, returns the entire queryset. Otherwise will only return objects the user has explicit permissions. This must be ``True`` for the accept_global_perms parameter to have any affect. Default is ``True``. :param accept_global_perms: if ``True`` takes global permissions into account. Object based permissions are taken into account if more than one permission is handed in in perms and at least one of these perms is not globally set. If any_perm is set to false then the intersection of matching object is returned. Note, that if with_superuser is False, accept_global_perms will be ignored, which means that only object permissions will be checked! Default is ``True``. :raises MixedContentTypeError: when computed content type for ``perms`` and/or ``klass`` clashes. :raises WrongAppError: if cannot compute app label for given ``perms``/ ``klass``. Example:: >>> from django.contrib.auth.models import User >>> from guardian.shortcuts import get_objects_for_user >>> joe = User.objects.get(username='joe') >>> get_objects_for_user(joe, 'auth.change_group') [] >>> from guardian.shortcuts import assign_perm >>> group = Group.objects.create('some group') >>> assign_perm('auth.change_group', joe, group) >>> get_objects_for_user(joe, 'auth.change_group') [] The permission string can also be an iterable. Continuing with the previous example: >>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group']) [] >>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group'], any_perm=True) [] >>> assign_perm('auth.delete_group', joe, group) >>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group']) [] Take global permissions into account: >>> jack = User.objects.get(username='jack') >>> assign_perm('auth.change_group', jack) # this will set a global permission >>> get_objects_for_user(jack, 'auth.change_group') [] >>> group2 = Group.objects.create('other group') >>> assign_perm('auth.delete_group', jack, group2) >>> get_objects_for_user(jack, ['auth.change_group', 'auth.delete_group']) # this retrieves intersection [] >>> get_objects_for_user(jack, ['auth.change_group', 'auth.delete_group'], any_perm) # this retrieves union [, ] If accept_global_perms is set to ``True``, then all assigned global permissions will also be taken into account. - Scenario 1: a user has view permissions generally defined on the model 'books' but no object based permission on a single book instance: - If accept_global_perms is ``True``: List of all books will be returned. - If accept_global_perms is ``False``: list will be empty. - Scenario 2: a user has view permissions generally defined on the model 'books' and also has an object based permission to view book 'Whatever': - If accept_global_perms is ``True``: List of all books will be returned. - If accept_global_perms is ``False``: list will only contain book 'Whatever'. - Scenario 3: a user only has object based permission on book 'Whatever': - If accept_global_perms is ``True``: List will only contain book 'Whatever'. - If accept_global_perms is ``False``: List will only contain book 'Whatever'. - Scenario 4: a user does not have any permission: - If accept_global_perms is ``True``: Empty list. - If accept_global_perms is ``False``: Empty list. """ if isinstance(perms, str): perms = [perms] ctype = None app_label = None codenames = set() # Compute codenames set and ctype if possible for perm in perms: if '.' in perm: new_app_label, codename = perm.split('.', 1) if app_label is not None and app_label != new_app_label: raise MixedContentTypeError("Given perms must have same app " "label (%s != %s)" % (app_label, new_app_label)) else: app_label = new_app_label else: codename = perm codenames.add(codename) if app_label is not None: new_ctype = ContentType.objects.get(app_label=app_label, permission__codename=codename) if ctype is not None and ctype != new_ctype: raise MixedContentTypeError("ContentType was once computed " "to be %s and another one %s" % (ctype, new_ctype)) else: ctype = new_ctype # Compute queryset and ctype if still missing if ctype is None and klass is not None: queryset = _get_queryset(klass) ctype = get_content_type(queryset.model) elif ctype is not None and klass is None: queryset = _get_queryset(ctype.model_class()) elif klass is None: raise WrongAppError("Cannot determine content type") else: queryset = _get_queryset(klass) if ctype.model_class() != queryset.model: raise MixedContentTypeError("Content type for given perms and " "klass differs") # At this point, we should have both ctype and queryset and they should # match which means: ctype.model_class() == queryset.model # we should also have ``codenames`` list # First check if user is superuser and if so, return queryset immediately if with_superuser and user.is_superuser: return queryset # Check if the user is anonymous. The # django.contrib.auth.models.AnonymousUser object doesn't work for queries # and it's nice to be able to pass in request.user blindly. if user.is_anonymous: user = get_anonymous_user() global_perms = set() has_global_perms = False # a superuser has by default assigned global perms for any if accept_global_perms and with_superuser: for code in codenames: if user.has_perm(ctype.app_label + '.' + code): global_perms.add(code) for code in global_perms: codenames.remove(code) # prerequisite: there must be elements in global_perms otherwise just follow the procedure for # object based permissions only AND # 1. codenames is empty, which means that permissions are ONLY set globally, therefore return the full queryset. # OR # 2. any_perm is True, then the global permission beats the object based permission anyway, # therefore return full queryset if len(global_perms) > 0 and (len(codenames) == 0 or any_perm): return queryset # if we have global perms and still some object based perms differing from global perms and any_perm is set # to false, then we have to flag that global perms exist in order to merge object based permissions by user # and by group correctly. Scenario: global perm change_xx and object based perm delete_xx on object A for user, # and object based permission delete_xx on object B for group, to which user is assigned. # get_objects_for_user(user, [change_xx, delete_xx], use_groups=True, any_perm=False, accept_global_perms=True) # must retrieve object A and B. elif len(global_perms) > 0 and (len(codenames) > 0): has_global_perms = True # Now we should extract list of pk values for which we would filter # queryset user_model = get_user_obj_perms_model(queryset.model) user_obj_perms_queryset = (user_model.objects .filter(user=user) .filter(permission__content_type=ctype)) if len(codenames): user_obj_perms_queryset = user_obj_perms_queryset.filter( permission__codename__in=codenames) direct_fields = ['content_object__pk', 'permission__codename'] generic_fields = ['object_pk', 'permission__codename'] if user_model.objects.is_generic(): user_fields = generic_fields else: user_fields = direct_fields if use_groups: group_model = get_group_obj_perms_model(queryset.model) group_filters = { 'permission__content_type': ctype, 'group__%s' % get_user_model().groups.field.related_query_name(): user, } if len(codenames): group_filters.update({ 'permission__codename__in': codenames, }) groups_obj_perms_queryset = group_model.objects.filter(**group_filters) if group_model.objects.is_generic(): group_fields = generic_fields else: group_fields = direct_fields if not any_perm and len(codenames) > 1 and not has_global_perms: user_obj_perms = user_obj_perms_queryset.values_list(*user_fields) groups_obj_perms = groups_obj_perms_queryset.values_list(*group_fields) data = list(user_obj_perms) + list(groups_obj_perms) # sorting/grouping by pk (first in result tuple) keyfunc = lambda t: t[0] data = sorted(data, key=keyfunc) pk_list = [] for pk, group in groupby(data, keyfunc): obj_codenames = set((e[1] for e in group)) if codenames.issubset(obj_codenames): pk_list.append(pk) objects = queryset.filter(pk__in=pk_list) return objects if not any_perm and len(codenames) > 1: counts = user_obj_perms_queryset.values( user_fields[0]).annotate(object_pk_count=Count(user_fields[0])) user_obj_perms_queryset = counts.filter( object_pk_count__gte=len(codenames)) values = user_obj_perms_queryset.values_list(user_fields[0], flat=True) if user_model.objects.is_generic(): values = set(values) q = Q(pk__in=values) if use_groups: values = groups_obj_perms_queryset.values_list(group_fields[0], flat=True) if group_model.objects.is_generic(): values = set(values) q |= Q(pk__in=values) return queryset.filter(q) def get_objects_for_group(group, perms, klass=None, any_perm=False, accept_global_perms=True): """ Returns queryset of objects for which a given ``group`` has *all* permissions present at ``perms``. :param group: ``Group`` instance for which objects would be returned. :param perms: single permission string, or sequence of permission strings which should be checked. If ``klass`` parameter is not given, those should be full permission names rather than only codenames (i.e. ``auth.change_user``). If more than one permission is present within sequence, their content type **must** be the same or ``MixedContentTypeError`` exception would be raised. :param klass: may be a Model, Manager or QuerySet object. If not given this parameter would be computed based on given ``params``. :param any_perm: if True, any of permission in sequence is accepted :param accept_global_perms: if ``True`` takes global permissions into account. If any_perm is set to false then the intersection of matching objects based on global and object based permissions is returned. Default is ``True``. :raises MixedContentTypeError: when computed content type for ``perms`` and/or ``klass`` clashes. :raises WrongAppError: if cannot compute app label for given ``perms``/ ``klass``. Example: Let's assume we have a ``Task`` model belonging to the ``tasker`` app with the default add_task, change_task and delete_task permissions provided by Django:: >>> from guardian.shortcuts import get_objects_for_group >>> from tasker import Task >>> group = Group.objects.create('some group') >>> task = Task.objects.create('some task') >>> get_objects_for_group(group, 'tasker.add_task') [] >>> from guardian.shortcuts import assign_perm >>> assign_perm('tasker.add_task', group, task) >>> get_objects_for_group(group, 'tasker.add_task') [] The permission string can also be an iterable. Continuing with the previous example: >>> get_objects_for_group(group, ['tasker.add_task', 'tasker.delete_task']) [] >>> assign_perm('tasker.delete_task', group, task) >>> get_objects_for_group(group, ['tasker.add_task', 'tasker.delete_task']) [] Global permissions assigned to the group are also taken into account. Continuing with previous example: >>> task_other = Task.objects.create('other task') >>> assign_perm('tasker.change_task', group) >>> get_objects_for_group(group, ['tasker.change_task']) [, ] >>> get_objects_for_group(group, ['tasker.change_task'], accept_global_perms=False) [] """ if isinstance(perms, str): perms = [perms] ctype = None app_label = None codenames = set() # Compute codenames set and ctype if possible for perm in perms: if '.' in perm: new_app_label, codename = perm.split('.', 1) if app_label is not None and app_label != new_app_label: raise MixedContentTypeError("Given perms must have same app " "label (%s != %s)" % (app_label, new_app_label)) else: app_label = new_app_label else: codename = perm codenames.add(codename) if app_label is not None: new_ctype = ContentType.objects.get(app_label=app_label, permission__codename=codename) if ctype is not None and ctype != new_ctype: raise MixedContentTypeError("ContentType was once computed " "to be %s and another one %s" % (ctype, new_ctype)) else: ctype = new_ctype # Compute queryset and ctype if still missing if ctype is None and klass is not None: queryset = _get_queryset(klass) ctype = get_content_type(queryset.model) elif ctype is not None and klass is None: queryset = _get_queryset(ctype.model_class()) elif klass is None: raise WrongAppError("Cannot determine content type") else: queryset = _get_queryset(klass) if ctype.model_class() != queryset.model: raise MixedContentTypeError("Content type for given perms and " "klass differs") # At this point, we should have both ctype and queryset and they should # match which means: ctype.model_class() == queryset.model # we should also have ``codenames`` list global_perms = set() if accept_global_perms: global_perm_set = group.permissions.values_list('codename', flat=True) for code in codenames: if code in global_perm_set: global_perms.add(code) for code in global_perms: codenames.remove(code) if len(global_perms) > 0 and (len(codenames) == 0 or any_perm): return queryset # Now we should extract list of pk values for which we would filter # queryset group_model = get_group_obj_perms_model(queryset.model) groups_obj_perms_queryset = (group_model.objects .filter(group=group) .filter(permission__content_type=ctype)) if len(codenames): groups_obj_perms_queryset = groups_obj_perms_queryset.filter( permission__codename__in=codenames) if group_model.objects.is_generic(): fields = ['object_pk', 'permission__codename'] else: fields = ['content_object__pk', 'permission__codename'] if not any_perm and len(codenames): groups_obj_perms = groups_obj_perms_queryset.values_list(*fields) data = list(groups_obj_perms) keyfunc = lambda t: t[0] # sorting/grouping by pk (first in result tuple) data = sorted(data, key=keyfunc) pk_list = [] for pk, group in groupby(data, keyfunc): obj_codenames = set((e[1] for e in group)) if any_perm or codenames.issubset(obj_codenames): pk_list.append(pk) objects = queryset.filter(pk__in=pk_list) return objects values = groups_obj_perms_queryset.values_list(fields[0], flat=True) if group_model.objects.is_generic(): values = list(values) return queryset.filter(pk__in=values) django-guardian-2.0.0/guardian/static/000077500000000000000000000000001347767355200176475ustar00rootroot00000000000000django-guardian-2.0.0/guardian/static/guardian/000077500000000000000000000000001347767355200214415ustar00rootroot00000000000000django-guardian-2.0.0/guardian/static/guardian/img/000077500000000000000000000000001347767355200222155ustar00rootroot00000000000000django-guardian-2.0.0/guardian/static/guardian/img/icon-no.svg000066400000000000000000000010601347767355200242750ustar00rootroot00000000000000 django-guardian-2.0.0/guardian/static/guardian/img/icon-yes.svg000066400000000000000000000006641347767355200244720ustar00rootroot00000000000000 django-guardian-2.0.0/guardian/templates/000077500000000000000000000000001347767355200203565ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templates/admin/000077500000000000000000000000001347767355200214465ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templates/admin/guardian/000077500000000000000000000000001347767355200232405ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templates/admin/guardian/contrib/000077500000000000000000000000001347767355200247005ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templates/admin/guardian/contrib/grappelli/000077500000000000000000000000001347767355200266575ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templates/admin/guardian/contrib/grappelli/field.html000066400000000000000000000012371347767355200306330ustar00rootroot00000000000000
    {% if field.is_checkbox %}
     
    {{ field }} {% else %}
    {{ field }} {% endif %} {% if field.help_text %}

    {{ field.help_text|safe }}

    {% endif %} {{ field.errors }}
    django-guardian-2.0.0/guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html000066400000000000000000000153031347767355200330370ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n %} {% load guardian_tags %} {% load admin_static %} {% load admin_urls %} {% block breadcrumbs %} {% endblock %} {% block content %}
    {% csrf_token %}
    {% if user_form.errors %}

    {% blocktrans count user_form.errors|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}

      {% for error in user_form.errors %}
    • {{ error }}
    • {% endfor %}
    {% endif %}

    {% trans "Users" %}

    {% for perm in model_perms %} {% endfor %} {% for user, user_perms in users_perms.items %} {% for perm in model_perms %} {% endfor %} {% endfor %}
    {% trans "User" %}{{ perm.name }}{% trans "Action" %}
    {{ user }} {% if perm.codename in user_perms %} {% else %} {% endif %} {% trans "Edit" %}
    {% for field in user_form %} {% include "admin/guardian/contrib/grappelli/field.html" %} {% endfor %}
     
    {% csrf_token %}
    {% if group_form.errors %}

    {% blocktrans count group_form.errors|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}

      {% for error in group_form.errors %}
    • {{ error }}
    • {% endfor %}
    {% endif %}

    {% trans "Groups" %}

    {% for perm in model_perms %} {% endfor %} {% for group, group_perms in groups_perms.items %} {% for perm in model_perms %} {% endfor %} {% endfor %}
    {% trans "Group" %}{{ perm.name }}{% trans "Action" %}
    {{ group }} {% if perm.codename in group_perms %} {% else %} {% endif %} {% trans "Edit" %}
    {% for field in group_form %} {% include "admin/guardian/contrib/grappelli/field.html" %} {% endfor %}
     
    {% endblock %} obj_perms_manage_group.html000066400000000000000000000052051347767355200341740ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templates/admin/guardian/contrib/grappelli{% extends "admin/change_form.html" %} {% load i18n %} {% load admin_urls %} {% block extrahead %}{{ block.super }} {{ form.media }} {% endblock %} {% block breadcrumbs %}{% if not is_popup %} {% endif %}{% endblock %} {% block content %}
    {% csrf_token %}

    {% trans "Object permissions" %}

    {{ object }}
    {{ group_obj }}
    {% for field in form %} {% include "admin/guardian/contrib/grappelli/field.html" %} {% endfor %}
     
    {% endblock %}django-guardian-2.0.0/guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html000066400000000000000000000051771347767355200341050ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n %} {% load admin_urls %} {% block extrahead %}{{ block.super }} {{ form.media }} {% endblock %} {% block breadcrumbs %}{% if not is_popup %} {% endif %}{% endblock %} {% block content %}
    {% csrf_token %}

    {% trans "Object permissions" %}

    {{ object }}
    {{ user_obj }}
    {% for field in form %} {% include "admin/guardian/contrib/grappelli/field.html" %} {% endfor %}
     
    {% endblock %}django-guardian-2.0.0/guardian/templates/admin/guardian/model/000077500000000000000000000000001347767355200243405ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templates/admin/guardian/model/change_form.html000066400000000000000000000005201347767355200274730ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n admin_urls %} {% block object-tools-items %} {% url opts|admin_urlname:'permissions' original.pk|admin_urlquote as history_url %}
  • {% trans "Object permissions" %}
  • {{ block.super }} {% endblock %} django-guardian-2.0.0/guardian/templates/admin/guardian/model/field.html000066400000000000000000000006121347767355200263100ustar00rootroot00000000000000
    {% if field.errors %}
      {% for error in field.errors %}
    • {{ error }}
    • {% endfor %}
    {% endif %}
    {{ field.label_tag }} {{ field }} {% if field.help_text %}

    {{ field.help_text }}

    {% endif %}
    django-guardian-2.0.0/guardian/templates/admin/guardian/model/obj_perms_manage.html000066400000000000000000000116061347767355200305220ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n %} {% load guardian_tags %} {% block extrahead %}{{ block.super }} {% endblock %} {% block breadcrumbs %}{% if not is_popup %} {% endif %}{% endblock %} {% block content %}
    {% csrf_token %} {% if user_form.errors %}

    {% trans "Please correct the errors below." %}

    {% endif %}

    {% trans "Users" %}

    {% for error in user_form.errors %}

    {{ error }}

    {% endfor %}
    {% for perm in model_perms %} {% endfor %} {% for user, user_perms in users_perms.items %} {% for perm in model_perms %} {% endfor %} {% endfor %}
    {% trans "User permissions" %}
    {% trans "User" %}{{ perm.name }}{% trans "Action" %}
    {{ user }} {% if perm.codename in user_perms %} {% include "admin/guardian/model/obj_perms_yes.html" %} {% else %} {% include "admin/guardian/model/obj_perms_no.html" %} {% endif %} {% trans "Edit" %}
    {% for field in user_form %} {% include "admin/guardian/model/field.html" %} {% endfor %}
    {% csrf_token %} {% if group_form.errors %}

    {% trans "Please correct the errors below." %}

    {% endif %}

    {% trans "Groups" %}

    {% for error in group_form.errors %}

    {{ error }}

    {% endfor %}
    {% for perm in model_perms %} {% endfor %} {% for group, group_perms in groups_perms.items %} {% for perm in model_perms %} {% endfor %} {% endfor %}
    {% trans "Group permissions" %}
    {% trans "Group" %}{{ perm.name }}{% trans "Action" %}
    {{ group }} {% if perm.codename in group_perms %} {% include "admin/guardian/model/obj_perms_yes.html" %} {% else %} {% include "admin/guardian/model/obj_perms_no.html" %} {% endif %} {% trans "Edit" %}
    {% for field in group_form %} {% include "admin/guardian/model/field.html" %} {% endfor %}
    {% endblock %} django-guardian-2.0.0/guardian/templates/admin/guardian/model/obj_perms_manage_group.html000066400000000000000000000031111347767355200317260ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n %} {% block extrahead %}{{ block.super }} {{ form.media }} {% endblock %} {% block breadcrumbs %}{% if not is_popup %} {% endif %}{% endblock %} {% block content %}
    {% csrf_token %}

    {% trans "Object permissions" %}

    {{ object }}
    {{ group_obj }}
    {% for field in form %} {% include "admin/guardian/model/field.html" %} {% endfor %}
    {% endblock %} django-guardian-2.0.0/guardian/templates/admin/guardian/model/obj_perms_manage_user.html000066400000000000000000000030531347767355200315550ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n %} {% block extrahead %}{{ block.super }} {{ form.media }} {% endblock %} {% block breadcrumbs %}{% if not is_popup %} {% endif %}{% endblock %} {% block content %}
    {% csrf_token %}

    {% trans "Object permissions" %}

    {{ object }}
    {{ user_obj }}
    {{ form }}
    {% endblock %} django-guardian-2.0.0/guardian/templates/admin/guardian/model/obj_perms_no.html000066400000000000000000000001451347767355200277020ustar00rootroot00000000000000{% load guardian_tags %} {% load staticfiles %} django-guardian-2.0.0/guardian/templates/admin/guardian/model/obj_perms_yes.html000066400000000000000000000001461347767355200300670ustar00rootroot00000000000000{% load guardian_tags %} {% load staticfiles %} django-guardian-2.0.0/guardian/templatetags/000077500000000000000000000000001347767355200210525ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templatetags/__init__.py000066400000000000000000000000001347767355200231510ustar00rootroot00000000000000django-guardian-2.0.0/guardian/templatetags/guardian_tags.py000066400000000000000000000066001347767355200242360ustar00rootroot00000000000000""" ``django-guardian`` template tags. To use in a template just put the following *load* tag inside a template:: {% load guardian_tags %} """ from django import template from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser, Group from guardian.core import ObjectPermissionChecker from guardian.exceptions import NotUserNorGroup register = template.Library() class ObjectPermissionsNode(template.Node): def __init__(self, for_whom, obj, context_var, checker=None): self.for_whom = template.Variable(for_whom) self.obj = template.Variable(obj) self.context_var = context_var self.checker = template.Variable(checker) if checker else None def render(self, context): for_whom = self.for_whom.resolve(context) if isinstance(for_whom, get_user_model()): self.user = for_whom self.group = None elif isinstance(for_whom, AnonymousUser): self.user = get_user_model().get_anonymous() self.group = None elif isinstance(for_whom, Group): self.user = None self.group = for_whom else: raise NotUserNorGroup("User or Group instance required (got %s)" % for_whom.__class__) obj = self.obj.resolve(context) if not obj: return '' check = self.checker.resolve(context) if self.checker else ObjectPermissionChecker(for_whom) perms = check.get_perms(obj) context[self.context_var] = perms return '' @register.tag def get_obj_perms(parser, token): """ Returns a list of permissions (as ``codename`` strings) for a given ``user``/``group`` and ``obj`` (Model instance). Parses ``get_obj_perms`` tag which should be in format:: {% get_obj_perms user/group for obj as "context_var" %} .. note:: Make sure that you set and use those permissions in same template block (``{% block %}``). Example of usage (assuming ``flatpage`` and ``perm`` objects are available from *context*):: {% get_obj_perms request.user for flatpage as "flatpage_perms" %} {% if "delete_flatpage" in flatpage_perms %} Remove page {% endif %} .. note:: Please remember that superusers would always get full list of permissions for a given object. .. versionadded:: 1.2 As of v1.2, passing ``None`` as ``obj`` for this template tag won't rise obfuscated exception and would return empty permissions set instead. """ bits = token.split_contents() format = '{% get_obj_perms user/group for obj as "context_var" perm_checker %}' if not (6 <= len(bits) <= 7) or bits[2] != 'for' or bits[4] != 'as': raise template.TemplateSyntaxError("get_obj_perms tag should be in " "format: %s" % format) for_whom = bits[1] obj = bits[3] context_var = bits[5] if context_var[0] != context_var[-1] or context_var[0] not in ('"', "'"): raise template.TemplateSyntaxError("get_obj_perms tag's context_var " "argument should be in quotes") context_var = context_var[1:-1] checker = bits[6] if len(bits) == 7 else None return ObjectPermissionsNode(for_whom, obj, context_var, checker) django-guardian-2.0.0/guardian/testapp/000077500000000000000000000000001347767355200200405ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/__init__.py000066400000000000000000000000001347767355200221370ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/migrations/000077500000000000000000000000001347767355200222145ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/migrations/0001_initial.py000066400000000000000000000207431347767355200246650ustar00rootroot00000000000000# Generated by Django 1.9.9 on 2016-09-18 17:52 import datetime from django.conf import settings import django.contrib.auth.models import django.core.validators from django.db import migrations, models import django.db.models.deletion import django.utils.timezone import guardian.mixins class Migration(migrations.Migration): initial = True dependencies = [ ('auth', '0001_initial'), ] operations = [ migrations.CreateModel( name='CustomUser', fields=[ ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('username', models.CharField(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')), ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')), ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), ('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')), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), ('custom_id', models.AutoField(primary_key=True, serialize=False)), ('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')), ('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')), ], options={ 'abstract': False, 'verbose_name': 'user', 'verbose_name_plural': 'users', }, bases=(models.Model, guardian.mixins.GuardianUserMixin), managers=[ ('objects', django.contrib.auth.models.UserManager()), ], ), migrations.CreateModel( name='CustomUsernameUser', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('email', models.EmailField(max_length=100, unique=True)), ], options={ 'abstract': False, }, bases=(models.Model, guardian.mixins.GuardianUserMixin), ), migrations.CreateModel( name='Mixed', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=128, unique=True)), ], ), migrations.CreateModel( name='MixedGroupObjectPermission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.Mixed')), ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')), ], options={ 'abstract': False, }, ), migrations.CreateModel( name='NonIntPKModel', fields=[ ('char_pk', models.CharField(max_length=128, primary_key=True, serialize=False)), ], ), migrations.CreateModel( name='Post', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=64, verbose_name='title')), ], ), migrations.CreateModel( name='Project', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=128, unique=True)), ('created_at', models.DateTimeField(default=datetime.datetime.now)), ], options={ 'get_latest_by': 'created_at', }, ), migrations.CreateModel( name='ProjectGroupObjectPermission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.Project')), ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')), ], options={ 'abstract': False, }, ), migrations.CreateModel( name='ProjectUserObjectPermission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.Project')), ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'abstract': False, }, ), migrations.CreateModel( name='ReverseMixed', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=128, unique=True)), ], ), migrations.CreateModel( name='ReverseMixedUserObjectPermission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testapp.ReverseMixed')), ('permission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Permission')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'abstract': False, }, ), migrations.AlterUniqueTogether( name='reversemixeduserobjectpermission', unique_together=set([('user', 'permission', 'content_object')]), ), migrations.AlterUniqueTogether( name='projectuserobjectpermission', unique_together=set([('user', 'permission', 'content_object')]), ), migrations.AlterUniqueTogether( name='projectgroupobjectpermission', unique_together=set([('group', 'permission', 'content_object')]), ), migrations.AlterUniqueTogether( name='mixedgroupobjectpermission', unique_together=set([('group', 'permission', 'content_object')]), ), ] django-guardian-2.0.0/guardian/testapp/migrations/0002_logentrywithgroup.py000066400000000000000000000014441347767355200270460ustar00rootroot00000000000000# Generated by Django 1.9.9 on 2016-09-18 17:52 from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ ('auth', '0001_initial'), ('admin', '0001_initial'), ('testapp', '0001_initial'), ] operations = [ migrations.CreateModel( name='LogEntryWithGroup', fields=[ ('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')), ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), ], bases=('admin.logentry',), ), ] django-guardian-2.0.0/guardian/testapp/migrations/0003_auto_20190611_0440.py000066400000000000000000000015771347767355200256440ustar00rootroot00000000000000# Generated by Django 2.2.2 on 2019-06-11 04:40 import django.contrib.auth.validators from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('testapp', '0002_logentrywithgroup'), ] operations = [ migrations.AlterField( model_name='customuser', name='last_name', field=models.CharField(blank=True, max_length=150, verbose_name='last name'), ), migrations.AlterField( model_name='customuser', name='username', field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'), ), ] django-guardian-2.0.0/guardian/testapp/migrations/__init__.py000066400000000000000000000000001347767355200243130ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/models.py000066400000000000000000000053711347767355200217030ustar00rootroot00000000000000from datetime import datetime from django.db import models from django.contrib.admin.models import LogEntry from django.contrib.auth.models import AbstractUser, AbstractBaseUser from guardian.mixins import GuardianUserMixin from guardian.models import UserObjectPermissionBase from guardian.models import GroupObjectPermissionBase class Post(models.Model): title = models.CharField('title', max_length=64) def __str__(self): return self.title class DynamicAccessor: def __init__(self): pass def __getattr__(self, key): return DynamicAccessor() class ProjectUserObjectPermission(UserObjectPermissionBase): content_object = models.ForeignKey('Project', on_delete=models.CASCADE) class ProjectGroupObjectPermission(GroupObjectPermissionBase): content_object = models.ForeignKey('Project', on_delete=models.CASCADE) class Project(models.Model): name = models.CharField(max_length=128, unique=True) created_at = models.DateTimeField(default=datetime.now) class Meta: get_latest_by = 'created_at' def __str__(self): return self.name Project.not_a_relation_descriptor = DynamicAccessor() class MixedGroupObjectPermission(GroupObjectPermissionBase): content_object = models.ForeignKey('Mixed', on_delete=models.CASCADE) class Mixed(models.Model): """ Model for tests obj perms checks with generic user object permissions model and direct group object permissions model. """ name = models.CharField(max_length=128, unique=True) def __str__(self): return self.name class ReverseMixedUserObjectPermission(UserObjectPermissionBase): content_object = models.ForeignKey('ReverseMixed', on_delete=models.CASCADE) class ReverseMixed(models.Model): """ Model for tests obj perms checks with generic group object permissions model and generic group object permissions model. """ name = models.CharField(max_length=128, unique=True) def __str__(self): return self.name class LogEntryWithGroup(LogEntry): group = models.ForeignKey('auth.Group', null=True, blank=True, on_delete=models.CASCADE) objects = models.Manager() class NonIntPKModel(models.Model): """ Model for testing whether get_objects_for_user will work when the objects to be returned have non-integer primary keys. """ char_pk = models.CharField(primary_key=True, max_length=128) class CustomUser(AbstractUser, GuardianUserMixin): custom_id = models.AutoField(primary_key=True) class CustomUsernameUser(AbstractBaseUser, GuardianUserMixin): email = models.EmailField(max_length=100, unique=True) USERNAME_FIELD = 'email' def get_full_name(self): return self.email def get_short_name(self): return self.email django-guardian-2.0.0/guardian/testapp/tests/000077500000000000000000000000001347767355200212025ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/tests/__init__.py000066400000000000000000000000001347767355200233010ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/tests/conf.py000066400000000000000000000052101347767355200224770ustar00rootroot00000000000000import unittest from guardian.conf import settings as guardian_settings from django.conf import settings from django.conf import UserSettingsHolder from django.utils.functional import wraps def skipUnlessTestApp(obj): app = 'guardian.testapp' return unittest.skipUnless(app in settings.INSTALLED_APPS, 'app %r must be installed to run this test' % app)(obj) class TestDataMixin: def setUp(self): super().setUp() from django.contrib.auth.models import Group from django.contrib.auth import get_user_model User = get_user_model() Group.objects.create(pk=1, name='admins') jack_group = Group.objects.create(pk=2, name='jackGroup') User.objects.get_or_create(username=guardian_settings.ANONYMOUS_USER_NAME) jack = User.objects.create(username='jack', is_active=True, is_superuser=False, is_staff=False) jack.groups.add(jack_group) class override_settings: """ Acts as either a decorator, or a context manager. If it's a decorator it takes a function and returns a wrapped function. If it's a contextmanager it's used with the ``with`` statement. In either event entering/exiting are called before and after, respectively, the function/block is executed. """ def __init__(self, **kwargs): self.options = kwargs self.wrapped = settings._wrapped def __enter__(self): self.enable() def __exit__(self, exc_type, exc_value, traceback): self.disable() def __call__(self, test_func): from django.test import TransactionTestCase if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase): original_pre_setup = test_func._pre_setup original_post_teardown = test_func._post_teardown def _pre_setup(innerself): self.enable() original_pre_setup(innerself) def _post_teardown(innerself): original_post_teardown(innerself) self.disable() test_func._pre_setup = _pre_setup test_func._post_teardown = _post_teardown return test_func else: @wraps(test_func) def inner(*args, **kwargs): with self: return test_func(*args, **kwargs) return inner def enable(self): override = UserSettingsHolder(settings._wrapped) for key, new_value in self.options.items(): setattr(override, key, new_value) settings._wrapped = override def disable(self): settings._wrapped = self.wrapped django-guardian-2.0.0/guardian/testapp/tests/templates/000077500000000000000000000000001347767355200232005ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/tests/templates/404.html000066400000000000000000000000001347767355200243630ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/tests/templates/500.html000066400000000000000000000000001347767355200243600ustar00rootroot00000000000000django-guardian-2.0.0/guardian/testapp/tests/templates/blank.html000066400000000000000000000000011347767355200251440ustar00rootroot00000000000000 django-guardian-2.0.0/guardian/testapp/tests/templates/dummy403.html000066400000000000000000000000121347767355200254410ustar00rootroot00000000000000foobar403 django-guardian-2.0.0/guardian/testapp/tests/templates/dummy404.html000066400000000000000000000000121347767355200254420ustar00rootroot00000000000000foobar404 django-guardian-2.0.0/guardian/testapp/tests/templates/list.html000066400000000000000000000001201347767355200250320ustar00rootroot00000000000000
      {% for object in object_list %}
    • {{object}}
    • {% endfor %}
    django-guardian-2.0.0/guardian/testapp/tests/test_admin.py000066400000000000000000000513751347767355200237160ustar00rootroot00000000000000import copy from django import forms from django.conf import settings from django.contrib import admin from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.http import HttpRequest from django.test import TestCase from django.test.client import Client from django.urls import reverse from guardian.admin import GuardedModelAdmin from guardian.shortcuts import get_perms from guardian.shortcuts import get_perms_for_model from guardian.models import Group from guardian.testapp.tests.conf import skipUnlessTestApp from guardian.testapp.models import LogEntryWithGroup as LogEntry User = get_user_model() class ContentTypeGuardedAdmin(GuardedModelAdmin): pass try: admin.site.unregister(ContentType) except admin.sites.NotRegistered: pass admin.site.register(ContentType, ContentTypeGuardedAdmin) class AdminTests(TestCase): def setUp(self): self.admin = User.objects.create_superuser('admin', 'admin@example.com', 'admin') self.user = User.objects.create_user('joe', 'joe@example.com', 'joe') self.group = Group.objects.create(name='group') self.client = Client() self.obj = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') self.obj_info = self.obj._meta.app_label, self.obj._meta.model_name def tearDown(self): self.client.logout() def _login_superuser(self): self.client.login(username='admin', password='admin') def test_view_manage_wrong_obj(self): self._login_superuser() url = reverse('admin:%s_%s_permissions_manage_user' % self.obj_info, kwargs={'object_pk': -10, 'user_id': self.user.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 404) def test_view(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.context['object'], self.obj) def test_view_manage_wrong_user(self): self._login_superuser() url = reverse('admin:%s_%s_permissions_manage_user' % self.obj_info, kwargs={'object_pk': self.obj.pk, 'user_id': -10}) response = self.client.get(url) self.assertEqual(response.status_code, 404) def test_view_manage_user_form(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'user': self.user.username, 'submit_manage_user': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) redirect_url = reverse('admin:%s_%s_permissions_manage_user' % self.obj_info, kwargs={'object_pk': self.obj.pk, 'user_id': self.user.pk}) self.assertEqual(response.request['PATH_INFO'], redirect_url) def test_view_manage_negative_user_form(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) self.user = User.objects.create(username='negative_id_user', pk=-2010) data = {'user': self.user.username, 'submit_manage_user': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) redirect_url = reverse('admin:%s_%s_permissions_manage_user' % self.obj_info, args=[self.obj.pk, self.user.pk]) self.assertEqual(response.request['PATH_INFO'], redirect_url) def test_view_manage_user_form_wrong_user(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'user': 'wrong-user', 'submit_manage_user': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 0) self.assertEqual(response.status_code, 200) self.assertTrue('user' in response.context['user_form'].errors) def test_view_manage_user_form_wrong_field(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'user': '', 'submit_manage_user': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 0) self.assertEqual(response.status_code, 200) self.assertTrue('user' in response.context['user_form'].errors) def test_view_manage_user_form_empty_user(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'user': '', 'submit_manage_user': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 0) self.assertEqual(response.status_code, 200) self.assertTrue('user' in response.context['user_form'].errors) def test_view_manage_user_wrong_perms(self): self._login_superuser() url = reverse('admin:%s_%s_permissions_manage_user' % self.obj_info, args=[self.obj.pk, self.user.pk]) perms = ['change_user'] # This is not self.obj related permission data = {'permissions': perms} response = self.client.post(url, data, follow=True) self.assertEqual(response.status_code, 200) self.assertTrue('permissions' in response.context['form'].errors) def test_view_manage_user(self): self._login_superuser() url = reverse('admin:%s_%s_permissions_manage_user' % self.obj_info, args=[self.obj.pk, self.user.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) choices = set([c[0] for c in response.context['form'].fields['permissions'].choices]) self.assertEqual( set([p.codename for p in get_perms_for_model(self.obj)]), choices, ) # Add some perms and check if changes were persisted perms = ['change_%s' % self.obj_info[ 1], 'delete_%s' % self.obj_info[1]] data = {'permissions': perms} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) self.assertIn('selected', str(response.context['form'])) self.assertEqual( set(get_perms(self.user, self.obj)), set(perms), ) # Remove perm and check if change was persisted perms = ['change_%s' % self.obj_info[1]] data = {'permissions': perms} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) self.assertEqual( set(get_perms(self.user, self.obj)), set(perms), ) def test_view_manage_group_form(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'group': self.group.name, 'submit_manage_group': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) redirect_url = reverse('admin:%s_%s_permissions_manage_group' % self.obj_info, args=[self.obj.pk, self.group.id]) self.assertEqual(response.request['PATH_INFO'], redirect_url) def test_view_manage_negative_group_form(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) self.group = Group.objects.create(name='neagive_id_group', id=-2010) data = {'group': self.group.name, 'submit_manage_group': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) redirect_url = reverse('admin:%s_%s_permissions_manage_group' % self.obj_info, args=[self.obj.pk, self.group.id]) self.assertEqual(response.request['PATH_INFO'], redirect_url) def test_view_manage_group_form_wrong_group(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'group': 'wrong-group', 'submit_manage_group': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 0) self.assertEqual(response.status_code, 200) self.assertTrue('group' in response.context['group_form'].errors) def test_view_manage_group_form_wrong_field(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'group': '', 'submit_manage_group': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 0) self.assertEqual(response.status_code, 200) self.assertTrue('group' in response.context['group_form'].errors) def test_view_manage_group_form_empty_group(self): self._login_superuser() url = reverse('admin:%s_%s_permissions' % self.obj_info, args=[self.obj.pk]) data = {'group': '', 'submit_manage_group': 'submit'} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 0) self.assertEqual(response.status_code, 200) self.assertTrue('group' in response.context['group_form'].errors) def test_view_manage_group_wrong_perms(self): self._login_superuser() url = reverse('admin:%s_%s_permissions_manage_group' % self.obj_info, args=[self.obj.pk, self.group.id]) perms = ['change_user'] # This is not self.obj related permission data = {'permissions': perms} response = self.client.post(url, data, follow=True) self.assertEqual(response.status_code, 200) self.assertTrue('permissions' in response.context['form'].errors) def test_view_manage_group(self): self._login_superuser() url = reverse('admin:%s_%s_permissions_manage_group' % self.obj_info, args=[self.obj.pk, self.group.id]) response = self.client.get(url) self.assertEqual(response.status_code, 200) choices = set([c[0] for c in response.context['form'].fields['permissions'].choices]) self.assertEqual( set([p.codename for p in get_perms_for_model(self.obj)]), choices, ) # Add some perms and check if changes were persisted perms = ['change_%s' % self.obj_info[ 1], 'delete_%s' % self.obj_info[1]] data = {'permissions': perms} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) self.assertEqual( set(get_perms(self.group, self.obj)), set(perms), ) # Remove perm and check if change was persisted perms = ['delete_%s' % self.obj_info[1]] data = {'permissions': perms} response = self.client.post(url, data, follow=True) self.assertEqual(len(response.redirect_chain), 1) self.assertEqual(response.redirect_chain[0][1], 302) self.assertEqual( set(get_perms(self.group, self.obj)), set(perms), ) if 'django.contrib.admin' not in settings.INSTALLED_APPS: # Skip admin tests if admin app is not registered # we simpy clean up AdminTests class ... # TODO: use @unittest.skipUnless('django.contrib.admin' in settings.INSTALLED_APPS) # if possible (requires Python 2.7, though) AdminTests = type('AdminTests', (TestCase,), {}) # pyflakes:ignore @skipUnlessTestApp class GuardedModelAdminTests(TestCase): def _get_gma(self, attrs=None, name=None, model=None): """ Returns ``GuardedModelAdmin`` instance. """ attrs = attrs or {} name = str(name or 'GMA') model = model or User GMA = type(name, (GuardedModelAdmin,), attrs) gma = GMA(model, admin.site) return gma def test_obj_perms_manage_template_attr(self): attrs = {'obj_perms_manage_template': 'foobar.html'} gma = self._get_gma(attrs=attrs) self.assertTrue(gma.get_obj_perms_manage_template(), 'foobar.html') def test_obj_perms_manage_user_template_attr(self): attrs = {'obj_perms_manage_user_template': 'foobar.html'} gma = self._get_gma(attrs=attrs) self.assertTrue( gma.get_obj_perms_manage_user_template(), 'foobar.html') def test_obj_perms_manage_user_form_attr(self): attrs = {'obj_perms_manage_user_form': forms.Form} gma = self._get_gma(attrs=attrs) self.assertTrue(issubclass(gma.get_obj_perms_manage_user_form(None), forms.Form)) def test_obj_perms_user_select_form_attr(self): attrs = {'obj_perms_user_select_form': forms.Form} gma = self._get_gma(attrs=attrs) self.assertTrue(issubclass(gma.get_obj_perms_user_select_form(None), forms.Form)) def test_obj_perms_manage_group_template_attr(self): attrs = {'obj_perms_manage_group_template': 'foobar.html'} gma = self._get_gma(attrs=attrs) self.assertTrue(gma.get_obj_perms_manage_group_template(), 'foobar.html') def test_obj_perms_manage_group_form_attr(self): attrs = {'obj_perms_manage_group_form': forms.Form} gma = self._get_gma(attrs=attrs) self.assertTrue(issubclass(gma.get_obj_perms_manage_group_form(None), forms.Form)) def test_obj_perms_group_select_form_attr(self): attrs = {'obj_perms_group_select_form': forms.Form} gma = self._get_gma(attrs=attrs) self.assertTrue(issubclass(gma.get_obj_perms_group_select_form(None), forms.Form)) def test_user_can_acces_owned_objects_only(self): attrs = { 'user_can_access_owned_objects_only': True, 'user_owned_objects_field': 'user', } gma = self._get_gma(attrs=attrs, model=LogEntry) joe = User.objects.create_user('joe', 'joe@example.com', 'joe') jane = User.objects.create_user('jane', 'jane@example.com', 'jane') ctype = ContentType.objects.get_for_model(User) joe_entry = LogEntry.objects.create(user=joe, content_type=ctype, object_id=joe.pk, action_flag=1, change_message='foo') LogEntry.objects.create(user=jane, content_type=ctype, object_id=jane.pk, action_flag=1, change_message='bar') request = HttpRequest() request.user = joe qs = gma.get_queryset(request) self.assertEqual([e.pk for e in qs], [joe_entry.pk]) def test_user_can_acces_owned_objects_only_unless_superuser(self): attrs = { 'user_can_access_owned_objects_only': True, 'user_owned_objects_field': 'user', } gma = self._get_gma(attrs=attrs, model=LogEntry) joe = User.objects.create_superuser('joe', 'joe@example.com', 'joe') jane = User.objects.create_user('jane', 'jane@example.com', 'jane') ctype = ContentType.objects.get_for_model(User) joe_entry = LogEntry.objects.create(user=joe, content_type=ctype, object_id=joe.pk, action_flag=1, change_message='foo') jane_entry = LogEntry.objects.create(user=jane, content_type=ctype, object_id=jane.pk, action_flag=1, change_message='bar') request = HttpRequest() request.user = joe qs = gma.get_queryset(request) self.assertEqual(sorted([e.pk for e in qs]), sorted([joe_entry.pk, jane_entry.pk])) def test_user_can_access_owned_by_group_objects_only(self): attrs = { 'user_can_access_owned_by_group_objects_only': True, 'group_owned_objects_field': 'group', } gma = self._get_gma(attrs=attrs, model=LogEntry) joe = User.objects.create_user('joe', 'joe@example.com', 'joe') joe_group = Group.objects.create(name='joe-group') joe.groups.add(joe_group) jane = User.objects.create_user('jane', 'jane@example.com', 'jane') jane_group = Group.objects.create(name='jane-group') jane.groups.add(jane_group) ctype = ContentType.objects.get_for_model(User) LogEntry.objects.create(user=joe, content_type=ctype, object_id=joe.pk, action_flag=1, change_message='foo') LogEntry.objects.create(user=jane, content_type=ctype, object_id=jane.pk, action_flag=1, change_message='bar') joe_entry_group = LogEntry.objects.create(user=jane, content_type=ctype, object_id=joe.pk, action_flag=1, change_message='foo', group=joe_group) request = HttpRequest() request.user = joe qs = gma.get_queryset(request) self.assertEqual([e.pk for e in qs], [joe_entry_group.pk]) def test_user_can_access_owned_by_group_objects_only_unless_superuser(self): attrs = { 'user_can_access_owned_by_group_objects_only': True, 'group_owned_objects_field': 'group', } gma = self._get_gma(attrs=attrs, model=LogEntry) joe = User.objects.create_superuser('joe', 'joe@example.com', 'joe') joe_group = Group.objects.create(name='joe-group') joe.groups.add(joe_group) jane = User.objects.create_user('jane', 'jane@example.com', 'jane') jane_group = Group.objects.create(name='jane-group') jane.groups.add(jane_group) ctype = ContentType.objects.get_for_model(User) LogEntry.objects.create(user=joe, content_type=ctype, object_id=joe.pk, action_flag=1, change_message='foo') LogEntry.objects.create(user=jane, content_type=ctype, object_id=jane.pk, action_flag=1, change_message='bar') LogEntry.objects.create(user=jane, content_type=ctype, object_id=joe.pk, action_flag=1, change_message='foo', group=joe_group) LogEntry.objects.create(user=joe, content_type=ctype, object_id=joe.pk, action_flag=1, change_message='foo', group=jane_group) request = HttpRequest() request.user = joe qs = gma.get_queryset(request) self.assertEqual(sorted(e.pk for e in qs), sorted(LogEntry.objects.values_list('pk', flat=True))) class GrappelliGuardedModelAdminTests(TestCase): org_installed_apps = copy.copy(settings.INSTALLED_APPS) def _get_gma(self, attrs=None, name=None, model=None): """ Returns ``GuardedModelAdmin`` instance. """ attrs = attrs or {} name = str(name or 'GMA') model = model or User GMA = type(name, (GuardedModelAdmin,), attrs) gma = GMA(model, admin.site) return gma def setUp(self): settings.INSTALLED_APPS = ['grappelli'] + list(settings.INSTALLED_APPS) def tearDown(self): settings.INSTALLED_APPS = self.org_installed_apps def test_get_obj_perms_manage_template(self): gma = self._get_gma() self.assertEqual(gma.get_obj_perms_manage_template(), 'admin/guardian/contrib/grappelli/obj_perms_manage.html') def test_get_obj_perms_manage_user_template(self): gma = self._get_gma() self.assertEqual(gma.get_obj_perms_manage_user_template(), 'admin/guardian/contrib/grappelli/obj_perms_manage_user.html') def test_get_obj_perms_manage_group_template(self): gma = self._get_gma() self.assertEqual(gma.get_obj_perms_manage_group_template(), 'admin/guardian/contrib/grappelli/obj_perms_manage_group.html') django-guardian-2.0.0/guardian/testapp/tests/test_checks.py000066400000000000000000000007271347767355200240610ustar00rootroot00000000000000 from django.test import TestCase from guardian.checks import check_settings class SystemCheckTestCase(TestCase): def test_checks(self): """ Test custom system checks :return: None """ self.assertFalse(check_settings(None)) with self.settings( AUTHENTICATION_BACKENDS=( 'django.contrib.auth.backends.ModelBackend',), ): self.assertEqual(len(check_settings(None)), 1) django-guardian-2.0.0/guardian/testapp/tests/test_conf.py000066400000000000000000000015621347767355200235440ustar00rootroot00000000000000from django.core.exceptions import ImproperlyConfigured from django.test import TestCase import mock from guardian.conf import settings as guardian_settings from guardian.ctypes import get_content_type class TestConfiguration(TestCase): def test_check_configuration(self): with mock.patch('guardian.conf.settings.RENDER_403', True): with mock.patch('guardian.conf.settings.RAISE_403', True): self.assertRaises(ImproperlyConfigured, guardian_settings.check_configuration) def test_get_content_type(self): with mock.patch('guardian.conf.settings.GET_CONTENT_TYPE', 'guardian.testapp.tests.test_conf.get_test_content_type'): self.assertEqual(get_content_type(None), 'x') def get_test_content_type(obj): """ Used in TestConfiguration.test_get_content_type().""" return 'x' django-guardian-2.0.0/guardian/testapp/tests/test_core.py000066400000000000000000000443171347767355200235540ustar00rootroot00000000000000from itertools import chain from django.conf import settings from guardian.conf import settings as guardian_settings from django.apps import apps as django_apps from django.contrib.auth import get_user_model from django.contrib.auth.management import create_permissions from django.contrib.auth.models import Group, Permission, AnonymousUser from django.contrib.contenttypes.models import ContentType from django.test import TestCase from guardian.core import ObjectPermissionChecker from guardian.exceptions import NotUserNorGroup from guardian.models import UserObjectPermission, GroupObjectPermission from guardian.shortcuts import assign_perm from guardian.management import create_anonymous_user from guardian.testapp.models import Project auth_app = django_apps.get_app_config('auth') User = get_user_model() class CustomUserTests(TestCase): def test_create_anonymous_user(self): create_anonymous_user(object()) self.assertEqual(1, User.objects.all().count()) anonymous = User.objects.all()[0] self.assertEqual(anonymous.username, guardian_settings.ANONYMOUS_USER_NAME) class ObjectPermissionTestCase(TestCase): def setUp(self): self.group, created = Group.objects.get_or_create(name='jackGroup') self.user, created = User.objects.get_or_create(username='jack') self.user.groups.add(self.group) self.ctype = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') self.ctype_qset = ContentType.objects.filter(model='bar', app_label='fake-for-guardian-tests') self.anonymous_user = User.objects.get( username=guardian_settings.ANONYMOUS_USER_NAME) def get_permission(self, codename, app_label=None): qs = Permission.objects if app_label: qs = qs.filter(content_type__app_label=app_label) return Permission.objects.get(codename=codename) class ObjectPermissionCheckerTest(ObjectPermissionTestCase): def setUp(self): super().setUp() # Required if MySQL backend is used :/ create_permissions(auth_app, 1) def test_cache_for_queries_count(self): settings.DEBUG = True try: from django.db import connection ContentType.objects.clear_cache() checker = ObjectPermissionChecker(self.user) # has_perm on Checker should spawn only two queries plus one extra # for fetching the content type first time we check for specific # model and two more content types as there are additional checks # at get_user_obj_perms_model and get_group_obj_perms_model query_count = len(connection.queries) res = checker.has_perm("change_group", self.group) if 'guardian.testapp' in settings.INSTALLED_APPS: expected = 5 else: # TODO: This is strange, need to investigate; totally not sure # why there are more queries if testapp is not included expected = 11 self.assertEqual(len(connection.queries), query_count + expected) # Checking again shouldn't spawn any queries query_count = len(connection.queries) res_new = checker.has_perm("change_group", self.group) self.assertEqual(res, res_new) self.assertEqual(len(connection.queries), query_count) # Checking for other permission but for Group object again # shouldn't spawn any query too query_count = len(connection.queries) checker.has_perm("delete_group", self.group) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance should spawn 2 queries new_group = Group.objects.create(name='new-group') query_count = len(connection.queries) checker.has_perm("change_group", new_group) self.assertEqual(len(connection.queries), query_count + 2) # Checking for permission for other model should spawn 4 queries # every added direct relation adds one more query.. # (again: content type and actual permissions for the object... query_count = len(connection.queries) checker.has_perm("change_user", self.user) self.assertEqual(len(connection.queries), query_count + 4) finally: settings.DEBUG = False def test_init(self): self.assertRaises(NotUserNorGroup, ObjectPermissionChecker, user_or_group=ContentType()) self.assertRaises(NotUserNorGroup, ObjectPermissionChecker) def test_anonymous_user(self): user = AnonymousUser() check = ObjectPermissionChecker(user) # assert anonymous user has no object permissions at all for obj self.assertTrue([] == list(check.get_perms(self.ctype))) def test_superuser(self): user = User.objects.create(username='superuser', is_superuser=True) check = ObjectPermissionChecker(user) ctype = ContentType.objects.get_for_model(self.ctype) perms = sorted(chain(*Permission.objects .filter(content_type=ctype) .values_list('codename'))) self.assertEqual(perms, check.get_perms(self.ctype)) for perm in perms: self.assertTrue(check.has_perm(perm, self.ctype)) def test_not_active_superuser(self): user = User.objects.create(username='not_active_superuser', is_superuser=True, is_active=False) check = ObjectPermissionChecker(user) ctype = ContentType.objects.get_for_model(self.ctype) perms = sorted(chain(*Permission.objects .filter(content_type=ctype) .values_list('codename'))) self.assertEqual(check.get_perms(self.ctype), []) for perm in perms: self.assertFalse(check.has_perm(perm, self.ctype)) def test_not_active_user(self): user = User.objects.create(username='notactive') assign_perm("change_contenttype", user, self.ctype) # new ObjectPermissionChecker is created for each User.has_perm call self.assertTrue(user.has_perm("change_contenttype", self.ctype)) user.is_active = False self.assertFalse(user.has_perm("change_contenttype", self.ctype)) # use on one checker only (as user's is_active attr should be checked # before try to use cache user = User.objects.create(username='notactive-cache') assign_perm("change_contenttype", user, self.ctype) check = ObjectPermissionChecker(user) self.assertTrue(check.has_perm("change_contenttype", self.ctype)) user.is_active = False self.assertFalse(check.has_perm("change_contenttype", self.ctype)) def test_get_perms(self): group = Group.objects.create(name='group') obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') obj2 = ContentType.objects.create( model='bar', app_label='guardian-tests') assign_perms = { group: ('change_group', 'delete_group'), obj1: ('change_contenttype', 'delete_contenttype'), obj2: ('delete_contenttype',), } check = ObjectPermissionChecker(self.user) for obj, perms in assign_perms.items(): for perm in perms: UserObjectPermission.objects.assign_perm(perm, self.user, obj) self.assertEqual(sorted(perms), sorted(check.get_perms(obj))) check = ObjectPermissionChecker(self.group) for obj, perms in assign_perms.items(): for perm in perms: GroupObjectPermission.objects.assign_perm( perm, self.group, obj) self.assertEqual(sorted(perms), sorted(check.get_perms(obj))) def test_prefetch_user_perms(self): settings.DEBUG = True try: from django.db import connection ContentType.objects.clear_cache() group1 = Group.objects.create(name='group1') group2 = Group.objects.create(name='group2') user = User.objects.create(username='active_user', is_active=True) assign_perm("change_group", user, self.group) assign_perm("change_group", user, group1) checker = ObjectPermissionChecker(user) # Prefetch permissions prefetched_objects = [self.group, group1, group2] self.assertTrue(checker.prefetch_perms(prefetched_objects)) query_count = len(connection.queries) # Checking cache is filled self.assertEqual( len(checker._obj_perms_cache), len(prefetched_objects) ) # Checking shouldn't spawn any queries checker.has_perm("change_group", self.group) self.assertEqual(len(connection.queries), query_count) # Checking for other permission but for Group object again # shouldn't spawn any query too checker.has_perm("delete_group", self.group) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any queries checker.has_perm("change_group", group1) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any queries # Even though User doesn't have perms on Group2, we still should # not hit DB self.assertFalse(checker.has_perm("change_group", group2)) self.assertEqual(len(connection.queries), query_count) finally: settings.DEBUG = False def test_prefetch_superuser_perms(self): settings.DEBUG = True try: from django.db import connection ContentType.objects.clear_cache() group1 = Group.objects.create(name='group1') user = User.objects.create(username='active_superuser', is_superuser=True, is_active=True) assign_perm("change_group", user, self.group) checker = ObjectPermissionChecker(user) # Prefetch permissions prefetched_objects = [self.group, group1] self.assertTrue(checker.prefetch_perms(prefetched_objects)) query_count = len(connection.queries) # Checking cache is filled self.assertEqual( len(checker._obj_perms_cache), len(prefetched_objects) ) # Checking shouldn't spawn any queries checker.has_perm("change_group", self.group) self.assertEqual(len(connection.queries), query_count) # Checking for other permission but for Group object again # shouldn't spawn any query too checker.has_perm("delete_group", self.group) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any queries checker.has_perm("change_group", group1) self.assertEqual(len(connection.queries), query_count) finally: settings.DEBUG = False def test_prefetch_group_perms(self): settings.DEBUG = True try: from django.db import connection ContentType.objects.clear_cache() group1 = Group.objects.create(name='group1') group2 = Group.objects.create(name='group2') assign_perm("change_group", group1, self.group) assign_perm("change_group", group1, group1) checker = ObjectPermissionChecker(group1) # Prefetch permissions prefetched_objects = [self.group, group1, group2] self.assertTrue(checker.prefetch_perms(prefetched_objects)) query_count = len(connection.queries) # Checking cache is filled self.assertEqual( len(checker._obj_perms_cache), len(prefetched_objects) ) # Checking shouldn't spawn any queries checker.has_perm("change_group", self.group) self.assertEqual(len(connection.queries), query_count) # Checking for other permission but for Group object again # shouldn't spawn any query too checker.has_perm("delete_group", self.group) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any queries checker.has_perm("change_group", group1) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any queries # Even though User doesn't have perms on Group2, we still should # not hit DB self.assertFalse(checker.has_perm("change_group", group2)) self.assertEqual(len(connection.queries), query_count) finally: settings.DEBUG = False def test_prefetch_user_perms_direct_rel(self): settings.DEBUG = True try: from django.db import connection ContentType.objects.clear_cache() user = User.objects.create(username='active_user', is_active=True) projects = \ [Project.objects.create(name='Project%s' % i) for i in range(3)] assign_perm("change_project", user, projects[0]) assign_perm("change_project", user, projects[1]) checker = ObjectPermissionChecker(user) # Prefetch permissions self.assertTrue(checker.prefetch_perms(projects)) query_count = len(connection.queries) # Checking cache is filled self.assertEqual(len(checker._obj_perms_cache), len(projects)) # Checking shouldn't spawn any queries checker.has_perm("change_project", projects[0]) self.assertEqual(len(connection.queries), query_count) # Checking for other permission but for Group object again # shouldn't spawn any query too checker.has_perm("delete_project", projects[0]) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any # queries checker.has_perm("change_project", projects[1]) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any queries # Even though User doesn't have perms on projects[2], we still # should not hit DB self.assertFalse(checker.has_perm("change_project", projects[2])) self.assertEqual(len(connection.queries), query_count) finally: settings.DEBUG = False def test_prefetch_superuser_perms_direct_rel(self): settings.DEBUG = True try: from django.db import connection ContentType.objects.clear_cache() user = User.objects.create( username='active_user', is_active=True, is_superuser=True) projects = \ [Project.objects.create(name='Project%s' % i) for i in range(2)] assign_perm("change_project", user, projects[0]) checker = ObjectPermissionChecker(user) # Prefetch permissions self.assertTrue(checker.prefetch_perms(projects)) query_count = len(connection.queries) # Checking cache is filled self.assertEqual(len(checker._obj_perms_cache), len(projects)) # Checking shouldn't spawn any queries checker.has_perm("change_project", projects[0]) self.assertEqual(len(connection.queries), query_count) # Checking for other permission but for Group object again # shouldn't spawn any query too checker.has_perm("delete_project", projects[0]) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any # queries checker.has_perm("change_project", projects[1]) self.assertEqual(len(connection.queries), query_count) finally: settings.DEBUG = False def test_prefetch_group_perms_direct_rel(self): settings.DEBUG = True try: from django.db import connection ContentType.objects.clear_cache() group = Group.objects.create(name='new-group') projects = \ [Project.objects.create(name='Project%s' % i) for i in range(3)] assign_perm("change_project", group, projects[0]) assign_perm("change_project", group, projects[1]) checker = ObjectPermissionChecker(group) # Prefetch permissions self.assertTrue(checker.prefetch_perms(projects)) query_count = len(connection.queries) # Checking cache is filled self.assertEqual(len(checker._obj_perms_cache), len(projects)) # Checking shouldn't spawn any queries checker.has_perm("change_project", projects[0]) self.assertEqual(len(connection.queries), query_count) # Checking for other permission but for Group object again # shouldn't spawn any query too checker.has_perm("delete_project", projects[0]) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any # queries checker.has_perm("change_project", projects[1]) self.assertEqual(len(connection.queries), query_count) # Checking for same model but other instance shouldn't spawn any queries # Even though User doesn't have perms on projects[2], we still # should not hit DB self.assertFalse(checker.has_perm("change_project", projects[2])) self.assertEqual(len(connection.queries), query_count) finally: settings.DEBUG = False django-guardian-2.0.0/guardian/testapp/tests/test_custompkmodel.py000066400000000000000000000023421347767355200255020ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.test import TestCase from guardian.shortcuts import assign_perm, remove_perm class CustomPKModelTest(TestCase): """ Tests against custom model with primary key other than *standard* ``id`` integer field. """ def setUp(self): self.user = get_user_model().objects.create(username='joe') self.ctype = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') def test_assign_perm(self): assign_perm('contenttypes.change_contenttype', self.user, self.ctype) self.assertTrue(self.user.has_perm('contenttypes.change_contenttype', self.ctype)) def test_remove_perm(self): assign_perm('contenttypes.change_contenttype', self.user, self.ctype) self.assertTrue(self.user.has_perm('contenttypes.change_contenttype', self.ctype)) remove_perm('contenttypes.change_contenttype', self.user, self.ctype) self.assertFalse(self.user.has_perm('contenttypes.change_contenttype', self.ctype)) django-guardian-2.0.0/guardian/testapp/tests/test_decorators.py000066400000000000000000000351201347767355200247610ustar00rootroot00000000000000from django.conf import global_settings from django.contrib.auth import get_user_model from django.contrib.auth.models import Group, AnonymousUser from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.db.models.base import ModelBase from django.http import HttpRequest from django.http import HttpResponse from django.http import HttpResponseForbidden from django.http import HttpResponseNotFound from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.template import TemplateDoesNotExist from django.test import TestCase from guardian.compat import get_user_model_path from guardian.compat import get_user_permission_full_codename import mock from guardian.decorators import permission_required, permission_required_or_403, permission_required_or_404 from guardian.exceptions import GuardianError from guardian.exceptions import WrongAppError from guardian.shortcuts import assign_perm from guardian.testapp.tests.conf import TestDataMixin from guardian.testapp.tests.conf import override_settings from guardian.testapp.tests.conf import skipUnlessTestApp User = get_user_model() user_model_path = get_user_model_path() @skipUnlessTestApp class PermissionRequiredTest(TestDataMixin, TestCase): def setUp(self): super().setUp() self.anon = AnonymousUser() self.user = User.objects.get_or_create(username='jack')[0] self.group = Group.objects.get_or_create(name='jackGroup')[0] def _get_request(self, user=None): if user is None: user = AnonymousUser() request = HttpRequest() request.user = user return request def test_no_args(self): try: @permission_required def dummy_view(request): return HttpResponse('dummy_view') except GuardianError: pass else: self.fail("Trying to decorate using permission_required without " "permission as first argument should raise exception") def test_RENDER_403_is_false(self): request = self._get_request(self.anon) @permission_required_or_403('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') with mock.patch('guardian.conf.settings.RENDER_403', False): response = dummy_view(request) self.assertEqual(response.content, b'') self.assertTrue(isinstance(response, HttpResponseForbidden)) def test_RENDER_404_is_false(self): request = self._get_request(self.anon) @permission_required_or_404('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') with mock.patch('guardian.conf.settings.RENDER_404', False): response = dummy_view(request) self.assertEqual(response.content, b'') self.assertTrue(isinstance(response, HttpResponseNotFound)) @mock.patch('guardian.conf.settings.RENDER_403', True) def test_TEMPLATE_403_setting(self): request = self._get_request(self.anon) @permission_required_or_403('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') with mock.patch('guardian.conf.settings.TEMPLATE_403', 'dummy403.html'): response = dummy_view(request) self.assertEqual(response.content, b'foobar403\n') @mock.patch('guardian.conf.settings.RENDER_404', True) def test_TEMPLATE_404_setting(self): request = self._get_request(self.anon) @permission_required_or_404('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') with mock.patch('guardian.conf.settings.TEMPLATE_404', 'dummy404.html'): response = dummy_view(request) self.assertEqual(response.content, b'foobar404\n') @mock.patch('guardian.conf.settings.RENDER_403', True) def test_403_response_raises_error(self): request = self._get_request(self.anon) @permission_required_or_403('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') with mock.patch('guardian.conf.settings.TEMPLATE_403', '_non-exisitng-403.html'): self.assertRaises(TemplateDoesNotExist, dummy_view, request) @mock.patch('guardian.conf.settings.RENDER_404', True) def test_404_response_raises_error(self): request = self._get_request(self.anon) @permission_required_or_404('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') with mock.patch('guardian.conf.settings.TEMPLATE_404', '_non-exisitng-404.html'): self.assertRaises(TemplateDoesNotExist, dummy_view, request) @mock.patch('guardian.conf.settings.RENDER_403', False) @mock.patch('guardian.conf.settings.RAISE_403', True) def test_RAISE_403_setting_is_true(self): request = self._get_request(self.anon) @permission_required_or_403('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') self.assertRaises(PermissionDenied, dummy_view, request) @mock.patch('guardian.conf.settings.RENDER_404', False) @mock.patch('guardian.conf.settings.RAISE_404', True) def test_RAISE_404_setting_is_true(self): request = self._get_request(self.anon) @permission_required_or_404('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') self.assertRaises(ObjectDoesNotExist, dummy_view, request) def test_anonymous_user_wrong_app(self): request = self._get_request(self.anon) @permission_required_or_403('not_installed_app.change_user') def dummy_view(request): return HttpResponse('dummy_view') self.assertEqual(dummy_view(request).status_code, 403) def test_anonymous_user_wrong_codename(self): request = self._get_request() @permission_required_or_403('auth.wrong_codename') def dummy_view(request): return HttpResponse('dummy_view') self.assertEqual(dummy_view(request).status_code, 403) def test_anonymous_user(self): request = self._get_request() @permission_required_or_403('auth.change_user') def dummy_view(request): return HttpResponse('dummy_view') self.assertEqual(dummy_view(request).status_code, 403) def test_wrong_lookup_variables_number(self): request = self._get_request() try: @permission_required_or_403('auth.change_user', (User, 'username')) def dummy_view(request, username): pass dummy_view(request, username='jack') except GuardianError: pass else: self.fail("If lookup variables are passed they must be tuple of: " "(ModelClass/app_label.ModelClass/queryset, " ")\n" "Otherwise GuardianError should be raised") def test_wrong_lookup_variables(self): request = self._get_request() args = ( (2010, 'username', 'username'), ('User', 'username', 'username'), (User, 'username', 'no_arg'), ) for tup in args: try: @permission_required_or_403('auth.change_user', tup) def show_user(request, username): user = get_object_or_404(User, username=username) return HttpResponse("It's %s here!" % user.username) show_user(request, 'jack') except GuardianError: pass else: self.fail("Wrong arguments given but GuardianError not raised") def test_user_has_no_access(self): request = self._get_request() @permission_required_or_403('auth.change_user') def dummy_view(request): return HttpResponse('dummy_view') self.assertEqual(dummy_view(request).status_code, 403) def test_user_has_access(self): perm = get_user_permission_full_codename('change') joe, created = User.objects.get_or_create(username='joe') assign_perm(perm, self.user, obj=joe) request = self._get_request(self.user) @permission_required_or_403(perm, ( user_model_path, 'username', 'username')) def dummy_view(request, username): return HttpResponse('dummy_view') response = dummy_view(request, username='joe') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'dummy_view') def test_user_has_access_on_model_with_metaclass(self): """ Test to the fix issues of comparaison made via type() in the decorator. In the case of a `Model` implementing a custom metaclass, the decorator fail because type doesn't return `ModelBase` """ perm = get_user_permission_full_codename('change') class TestMeta(ModelBase): pass class ProxyUser(User): class Meta: proxy = True app_label = User._meta.app_label __metaclass__ = TestMeta joe, created = ProxyUser.objects.get_or_create(username='joe') assign_perm(perm, self.user, obj=joe) request = self._get_request(self.user) @permission_required_or_403(perm, ( ProxyUser, 'username', 'username')) def dummy_view(request, username): return HttpResponse('dummy_view') response = dummy_view(request, username='joe') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'dummy_view') def test_user_has_obj_access_even_if_we_also_check_for_global(self): perm = get_user_permission_full_codename('change') joe, created = User.objects.get_or_create(username='joe') assign_perm(perm, self.user, obj=joe) request = self._get_request(self.user) @permission_required_or_403(perm, ( user_model_path, 'username', 'username'), accept_global_perms=True) def dummy_view(request, username): return HttpResponse('dummy_view') response = dummy_view(request, username='joe') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'dummy_view') def test_user_has_no_obj_perm_access(self): perm = get_user_permission_full_codename('change') joe, created = User.objects.get_or_create(username='joe') request = self._get_request(self.user) @permission_required_or_403(perm, ( user_model_path, 'username', 'username')) def dummy_view(request, username): return HttpResponse('dummy_view') response = dummy_view(request, username='joe') self.assertEqual(response.status_code, 403) def test_user_has_global_perm_access_but_flag_not_set(self): perm = get_user_permission_full_codename('change') joe, created = User.objects.get_or_create(username='joe') assign_perm(perm, self.user) request = self._get_request(self.user) @permission_required_or_403(perm, ( user_model_path, 'username', 'username')) def dummy_view(request, username): return HttpResponse('dummy_view') response = dummy_view(request, username='joe') self.assertEqual(response.status_code, 403) def test_user_has_global_perm_access(self): perm = get_user_permission_full_codename('change') joe, created = User.objects.get_or_create(username='joe') assign_perm(perm, self.user) request = self._get_request(self.user) @permission_required_or_403(perm, ( user_model_path, 'username', 'username'), accept_global_perms=True) def dummy_view(request, username): return HttpResponse('dummy_view') response = dummy_view(request, username='joe') self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'dummy_view') def test_model_lookup(self): request = self._get_request(self.user) perm = get_user_permission_full_codename('change') joe, created = User.objects.get_or_create(username='joe') assign_perm(perm, self.user, obj=joe) models = ( user_model_path, User, User.objects.filter(is_active=True), ) for model in models: @permission_required_or_403(perm, (model, 'username', 'username')) def dummy_view(request, username): get_object_or_404(User, username=username) return HttpResponse('hello') response = dummy_view(request, username=joe.username) self.assertEqual(response.content, b'hello') def test_redirection_raises_wrong_app_error(self): from guardian.testapp.models import Project request = self._get_request(self.user) User.objects.create(username='foo') Project.objects.create(name='foobar') @permission_required('auth.change_group', (Project, 'name', 'group_name'), login_url='/foobar/') def dummy_view(request, project_name): pass # 'auth.change_group' is wrong permission codename (should be one # related with User self.assertRaises(WrongAppError, dummy_view, request, group_name='foobar') def test_redirection(self): from guardian.testapp.models import Project request = self._get_request(self.user) User.objects.create(username='foo') Project.objects.create(name='foobar') @permission_required('testapp.change_project', (Project, 'name', 'project_name'), login_url='/foobar/') def dummy_view(request, project_name): pass response = dummy_view(request, project_name='foobar') self.assertTrue(isinstance(response, HttpResponseRedirect)) self.assertTrue(response._headers['location'][1].startswith( '/foobar/')) @override_settings(LOGIN_URL='django.contrib.auth.views.login') def test_redirection_class(self): view_url = '/permission_required/' response = self.client.get(view_url) # this should be '/account/login' self.assertRedirects( response, global_settings.LOGIN_URL + "?next=" + view_url) django-guardian-2.0.0/guardian/testapp/tests/test_direct_rel.py000066400000000000000000000267321347767355200247410ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.contrib.auth.models import Group, Permission from django.test import TestCase from guardian.shortcuts import assign_perm from guardian.shortcuts import get_groups_with_perms from guardian.shortcuts import get_objects_for_group from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_users_with_perms from guardian.shortcuts import remove_perm from guardian.testapp.models import Mixed, ReverseMixed from guardian.testapp.models import Project from guardian.testapp.models import ProjectGroupObjectPermission from guardian.testapp.models import ProjectUserObjectPermission from guardian.testapp.tests.conf import skipUnlessTestApp User = get_user_model() @skipUnlessTestApp class TestDirectUserPermissions(TestCase): def setUp(self): self.joe = User.objects.create_user('joe', 'joe@example.com', 'foobar') self.project = Project.objects.create(name='Foobar') def get_perm(self, codename): filters = {'content_type__app_label': 'testapp', 'codename': codename} return Permission.objects.get(**filters) def test_after_perm_is_created_without_shortcut(self): perm = self.get_perm('add_project') # we should not use assign here - if generic user obj perms model is # used then everything could go fine if using assign shortcut and we # would not be able to see any problem ProjectUserObjectPermission.objects.create( user=self.joe, permission=perm, content_object=self.project, ) self.assertTrue(self.joe.has_perm('add_project', self.project)) def test_assign_perm(self): assign_perm('add_project', self.joe, self.project) filters = { 'content_object': self.project, 'permission__codename': 'add_project', 'user': self.joe, } result = ProjectUserObjectPermission.objects.filter(**filters).count() self.assertEqual(result, 1) def test_remove_perm(self): assign_perm('add_project', self.joe, self.project) filters = { 'content_object': self.project, 'permission__codename': 'add_project', 'user': self.joe, } result = ProjectUserObjectPermission.objects.filter(**filters).count() self.assertEqual(result, 1) remove_perm('add_project', self.joe, self.project) result = ProjectUserObjectPermission.objects.filter(**filters).count() self.assertEqual(result, 0) def test_get_users_with_perms(self): User.objects.create_user('john', 'john@foobar.com', 'john') jane = User.objects.create_user('jane', 'jane@foobar.com', 'jane') assign_perm('add_project', self.joe, self.project) assign_perm('change_project', self.joe, self.project) assign_perm('change_project', jane, self.project) self.assertEqual(get_users_with_perms(self.project, attach_perms=True), { self.joe: ['add_project', 'change_project'], jane: ['change_project'], }) def test_get_users_with_perms_plus_groups(self): User.objects.create_user('john', 'john@foobar.com', 'john') jane = User.objects.create_user('jane', 'jane@foobar.com', 'jane') group = Group.objects.create(name='devs') self.joe.groups.add(group) assign_perm('add_project', self.joe, self.project) assign_perm('change_project', group, self.project) assign_perm('change_project', jane, self.project) self.assertEqual(get_users_with_perms(self.project, attach_perms=True), { self.joe: ['add_project', 'change_project'], jane: ['change_project'], }) def test_get_objects_for_user(self): foo = Project.objects.create(name='foo') bar = Project.objects.create(name='bar') assign_perm('add_project', self.joe, foo) assign_perm('add_project', self.joe, bar) assign_perm('change_project', self.joe, bar) result = get_objects_for_user(self.joe, 'testapp.add_project') self.assertEqual(sorted(p.pk for p in result), sorted([foo.pk, bar.pk])) def test_get_all_permissions(self): foo = Project.objects.create(name='foo') assign_perm('add_project', self.joe, foo) assign_perm('change_project', self.joe, foo) result = self.joe.get_all_permissions(foo) self.assertEqual(result, set(('add_project', 'change_project'))) def test_get_all_permissions_no_object(self): foo = Project.objects.create(name='foo') assign_perm('add_project', self.joe, foo) assign_perm('change_project', self.joe, foo) result = self.joe.get_all_permissions() self.assertEqual(result, set()) @skipUnlessTestApp class TestDirectGroupPermissions(TestCase): def setUp(self): self.joe = User.objects.create_user('joe', 'joe@example.com', 'foobar') self.group = Group.objects.create(name='admins') self.joe.groups.add(self.group) self.project = Project.objects.create(name='Foobar') def get_perm(self, codename): filters = {'content_type__app_label': 'testapp', 'codename': codename} return Permission.objects.get(**filters) def test_after_perm_is_created_without_shortcut(self): perm = self.get_perm('add_project') # we should not use assign here - if generic user obj perms model is # used then everything could go fine if using assign shortcut and we # would not be able to see any problem ProjectGroupObjectPermission.objects.create( group=self.group, permission=perm, content_object=self.project, ) self.assertTrue(self.joe.has_perm('add_project', self.project)) def test_assign_perm(self): assign_perm('add_project', self.group, self.project) filters = { 'content_object': self.project, 'permission__codename': 'add_project', 'group': self.group, } result = ProjectGroupObjectPermission.objects.filter(**filters).count() self.assertEqual(result, 1) def test_remove_perm(self): assign_perm('add_project', self.group, self.project) filters = { 'content_object': self.project, 'permission__codename': 'add_project', 'group': self.group, } result = ProjectGroupObjectPermission.objects.filter(**filters).count() self.assertEqual(result, 1) remove_perm('add_project', self.group, self.project) result = ProjectGroupObjectPermission.objects.filter(**filters).count() self.assertEqual(result, 0) def test_get_groups_with_perms(self): Group.objects.create(name='managers') devs = Group.objects.create(name='devs') assign_perm('add_project', self.group, self.project) assign_perm('change_project', self.group, self.project) assign_perm('change_project', devs, self.project) self.assertEqual(get_groups_with_perms(self.project, attach_perms=True), { self.group: ['add_project', 'change_project'], devs: ['change_project'], }) def test_get_groups_with_perms_doesnt_spawn_extra_queries_for_more_groups_with_perms(self): Group.objects.create(name='managers') devs = Group.objects.create(name='devs') devs1 = Group.objects.create(name='devs1') devs2 = Group.objects.create(name='devs2') devs3 = Group.objects.create(name='devs3') devs4 = Group.objects.create(name='devs4') devs5 = Group.objects.create(name='devs5') assign_perm('add_project', self.group, self.project) assign_perm('change_project', self.group, self.project) for group in [devs, devs1, devs2, devs3, devs4, devs5]: assign_perm('add_project', group, self.project) assign_perm('change_project', group, self.project) with self.assertNumQueries(3): result = get_groups_with_perms(self.project, attach_perms=True) self.assertEqual(result, { self.group: ['add_project', 'change_project'], devs: ['add_project', 'change_project'], devs1: ['add_project', 'change_project'], devs2: ['add_project', 'change_project'], devs3: ['add_project', 'change_project'], devs4: ['add_project', 'change_project'], devs5: ['add_project', 'change_project'], }) def test_get_objects_for_group(self): foo = Project.objects.create(name='foo') bar = Project.objects.create(name='bar') assign_perm('add_project', self.group, foo) assign_perm('add_project', self.group, bar) assign_perm('change_project', self.group, bar) result = get_objects_for_group(self.group, 'testapp.add_project') self.assertEqual(sorted(p.pk for p in result), sorted([foo.pk, bar.pk])) @skipUnlessTestApp class TestMixedDirectAndGenericObjectPermission(TestCase): def setUp(self): self.joe = User.objects.create_user('joe', 'joe@example.com', 'foobar') self.group = Group.objects.create(name='admins') self.joe.groups.add(self.group) self.mixed = Mixed.objects.create(name='Foobar') self.reverse_mixed = ReverseMixed.objects.create(name='Foobar') def test_get_users_with_perms_plus_groups(self): User.objects.create_user('john', 'john@foobar.com', 'john') jane = User.objects.create_user('jane', 'jane@foobar.com', 'jane') group = Group.objects.create(name='devs') self.joe.groups.add(group) assign_perm('add_mixed', self.joe, self.mixed) assign_perm('change_mixed', group, self.mixed) assign_perm('change_mixed', jane, self.mixed) self.assertEqual(get_users_with_perms(self.mixed, attach_perms=True), { self.joe: ['add_mixed', 'change_mixed'], jane: ['change_mixed'], }) result = get_objects_for_user(self.joe, 'testapp.add_mixed') self.assertEqual(sorted(p.pk for p in result), sorted([self.mixed.pk])) def test_get_users_with_perms_plus_groups_reverse_mixed(self): User.objects.create_user('john', 'john@foobar.com', 'john') jane = User.objects.create_user('jane', 'jane@foobar.com', 'jane') group = Group.objects.create(name='devs') self.joe.groups.add(group) assign_perm('add_reversemixed', self.joe, self.reverse_mixed) assign_perm('change_reversemixed', group, self.reverse_mixed) assign_perm('change_reversemixed', jane, self.reverse_mixed) self.assertEqual(get_users_with_perms(self.reverse_mixed, attach_perms=True), { self.joe: ['add_reversemixed', 'change_reversemixed'], jane: ['change_reversemixed'], }) result = get_objects_for_user(self.joe, 'testapp.add_reversemixed') self.assertEqual(sorted(p.pk for p in result), sorted([self.reverse_mixed.pk])) django-guardian-2.0.0/guardian/testapp/tests/test_forms.py000066400000000000000000000017771347767355200237550ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.test import TestCase from guardian.forms import BaseObjectPermissionsForm class BaseObjectPermissionsFormTests(TestCase): def setUp(self): self.user = get_user_model().objects.create_user( 'joe', 'joe@example.com', 'joe') self.obj = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') def test_not_implemented(self): class MyUserObjectPermissionsForm(BaseObjectPermissionsForm): def __init__(formself, user, *args, **kwargs): self.user = user super().__init__(*args, **kwargs) form = MyUserObjectPermissionsForm(self.user, self.obj, {}) self.assertRaises(NotImplementedError, form.save_obj_perms) field_name = form.get_obj_perms_field_name() self.assertTrue(form.is_valid()) self.assertEqual(len(form.cleaned_data[field_name]), 0) django-guardian-2.0.0/guardian/testapp/tests/test_management.py000066400000000000000000000033171347767355200247330ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.test import TestCase, override_settings import mock from guardian.management import create_anonymous_user from guardian.utils import get_anonymous_user mocked_get_init_anon = mock.Mock() class TestGetAnonymousUser(TestCase): @mock.patch('guardian.management.guardian_settings') def test_uses_custom_function(self, guardian_settings): mocked_get_init_anon.reset_mock() path = 'guardian.testapp.tests.test_management.mocked_get_init_anon' guardian_settings.GET_INIT_ANONYMOUS_USER = path guardian_settings.ANONYMOUS_USER_NAME = "anonymous" User = get_user_model() anon = mocked_get_init_anon.return_value = mock.Mock() create_anonymous_user('sender') mocked_get_init_anon.assert_called_once_with(User) anon.save.assert_called_once_with() @mock.patch('guardian.management.guardian_settings') @override_settings(AUTH_USER_MODEL='testapp.CustomUsernameUser') def test_uses_custom_username_field_model(self, guardian_settings): mocked_get_init_anon.reset_mock() guardian_settings.GET_INIT_ANONYMOUS_USER = 'guardian.testapp.tests.test_management.mocked_get_init_anon' guardian_settings.ANONYMOUS_USER_NAME = 'testuser@example.com' User = get_user_model() anon = mocked_get_init_anon.return_value = mock.Mock() create_anonymous_user('sender') mocked_get_init_anon.assert_called_once_with(User) anon.save.assert_called_once_with() def test_get_anonymous_user(self): anon = get_anonymous_user() self.assertFalse(anon.has_usable_password()) self.assertEqual(anon.get_username(), "AnonymousUser") django-guardian-2.0.0/guardian/testapp/tests/test_managers.py000066400000000000000000000024671347767355200244210ustar00rootroot00000000000000import warnings import mock from django.test import TestCase from guardian.managers import UserObjectPermissionManager from guardian.managers import GroupObjectPermissionManager class TestManagers(TestCase): def test_user_manager_assign(self): manager = UserObjectPermissionManager() manager.assign_perm = mock.Mock() with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") manager.assign('perm', 'user', 'object') manager.assign_perm.assert_called_once_with('perm', 'user', 'object') self.assertTrue(issubclass(w[0].category, DeprecationWarning)) self.assertIn("UserObjectPermissionManager method 'assign' is being renamed to 'assign_perm'.", str(w[0].message)) def test_group_manager_assign(self): manager = GroupObjectPermissionManager() manager.assign_perm = mock.Mock() with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") manager.assign('perm', 'group', 'object') manager.assign_perm.assert_called_once_with('perm', 'group', 'object') self.assertTrue(issubclass(w[0].category, DeprecationWarning)) self.assertIn("UserObjectPermissionManager method 'assign' is being renamed to 'assign_perm'.", str(w[0].message)) django-guardian-2.0.0/guardian/testapp/tests/test_mixins.py000066400000000000000000000161731347767355200241320ustar00rootroot00000000000000from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import PermissionDenied from django.http import HttpResponse from django.test import TestCase from django.test.client import RequestFactory from django.views.generic import View from django.views.generic import ListView from guardian.shortcuts import assign_perm import mock from guardian.mixins import LoginRequiredMixin from guardian.mixins import PermissionRequiredMixin from guardian.mixins import PermissionListMixin from ..models import Post class DatabaseRemovedError(Exception): pass class RemoveDatabaseView(View): def get(self, request, *args, **kwargs): raise DatabaseRemovedError("You've just allowed db to be removed!") class TestView(PermissionRequiredMixin, RemoveDatabaseView): permission_required = 'testapp.change_post' object = None # should be set at each tests explicitly class NoObjectView(PermissionRequiredMixin, RemoveDatabaseView): permission_required = 'testapp.change_post' class GlobalNoObjectView(PermissionRequiredMixin, RemoveDatabaseView): permission_required = 'testapp.add_post' accept_global_perms = True class PostPermissionListView(PermissionListMixin, ListView): model = Post permission_required = 'testapp.change_post' template_name = 'list.html' class TestViewMixins(TestCase): def setUp(self): self.post = Post.objects.create(title='foo-post-title') self.factory = RequestFactory() self.user = get_user_model().objects.create_user( 'joe', 'joe@doe.com', 'doe') self.client.login(username='joe', password='doe') def test_permission_is_checked_before_view_is_computed(self): """ This test would fail if permission is checked **after** view is actually resolved. """ request = self.factory.get('/') request.user = self.user # View.object is set view = TestView.as_view(object=self.post) response = view(request) self.assertEqual(response.status_code, 302) # View.get_object returns object TestView.get_object = lambda instance: self.post view = TestView.as_view() response = view(request) self.assertEqual(response.status_code, 302) del TestView.get_object def test_permission_is_checked_before_view_is_computed_perm_denied_raised(self): """ This test would fail if permission is checked **after** view is actually resolved. """ request = self.factory.get('/') request.user = self.user view = TestView.as_view(raise_exception=True, object=self.post) with self.assertRaises(PermissionDenied): view(request) def test_permission_required_view_configured_wrongly(self): """ This test would fail if permission is checked **after** view is actually resolved. """ request = self.factory.get('/') request.user = self.user request.user.add_obj_perm('change_post', self.post) view = TestView.as_view(permission_required=None, object=self.post) with self.assertRaises(ImproperlyConfigured): view(request) def test_permission_required(self): """ This test would fail if permission is checked **after** view is actually resolved. """ request = self.factory.get('/') request.user = self.user request.user.add_obj_perm('change_post', self.post) view = TestView.as_view(object=self.post) with self.assertRaises(DatabaseRemovedError): view(request) def test_permission_required_no_object(self): """ This test would fail if permission is checked on a view's object when it has none """ request = self.factory.get('/') request.user = self.user request.user.add_obj_perm('change_post', self.post) view = NoObjectView.as_view() response = view(request) self.assertEqual(response.status_code, 302) def test_permission_required_global_no_object(self): """ This test would fail if permission is checked on a view's object when it not set and **no** global permission """ request = self.factory.get('/') request.user = self.user view = GlobalNoObjectView.as_view() response = view(request) self.assertEqual(response.status_code, 302) def test_permission_granted_global_no_object(self): """ This test would fail if permission is checked on a view's object when it not set and **has** global permission """ request = self.factory.get('/') request.user = self.user assign_perm('testapp.add_post', request.user) view = GlobalNoObjectView.as_view() with self.assertRaises(DatabaseRemovedError): view(request) def test_permission_required_as_list(self): """ This test would fail if permission is checked **after** view is actually resolved. """ global TestView class SecretView(TestView): on_permission_check_fail = mock.Mock() request = self.factory.get('/') request.user = self.user request.user.add_obj_perm('change_post', self.post) SecretView.permission_required = ['testapp.change_post', 'testapp.add_post'] view = SecretView.as_view(object=self.post) response = view(request) self.assertEqual(response.status_code, 302) SecretView.on_permission_check_fail.assert_called_once_with(request, response, obj=self.post) request.user.add_obj_perm('add_post', self.post) with self.assertRaises(DatabaseRemovedError): view(request) def test_login_required_mixin(self): class SecretView(LoginRequiredMixin, View): redirect_field_name = 'foobar' login_url = '/let-me-in/' def get(self, request): return HttpResponse('secret-view') request = self.factory.get('/some-secret-page/') request.user = AnonymousUser() view = SecretView.as_view() response = view(request) self.assertEqual(response.status_code, 302) self.assertEqual(response['Location'], '/let-me-in/?foobar=/some-secret-page/') request.user = self.user response = view(request) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'secret-view') def test_list_permission(self): request = self.factory.get('/some-secret-list/') request.user = AnonymousUser() view = PostPermissionListView.as_view() response = view(request) self.assertNotContains(response, b'foo-post-title') request.user = self.user request.user.add_obj_perm('change_post', self.post) response = view(request) self.assertContains(response, b'foo-post-title') django-guardian-2.0.0/guardian/testapp/tests/test_orphans.py000066400000000000000000000067561347767355200243030ustar00rootroot00000000000000from django.apps import apps as django_apps auth_app = django_apps.get_app_config("auth") from django.contrib.auth import get_user_model from django.contrib.auth.management import create_permissions from django.contrib.contenttypes.models import ContentType from django.core.management import call_command from django.test import TestCase from guardian.utils import clean_orphan_obj_perms from guardian.shortcuts import assign_perm from guardian.models import Group from guardian.testapp.tests.conf import skipUnlessTestApp User = get_user_model() user_module_name = User._meta.model_name @skipUnlessTestApp class OrphanedObjectPermissionsTest(TestCase): def setUp(self): # Create objects for which we would assing obj perms self.target_user1 = User.objects.create(username='user1') self.target_group1 = Group.objects.create(name='group1') self.target_obj1 = ContentType.objects.create( model='foo', app_label='fake-for-guardian-tests') self.target_obj2 = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') # Required if MySQL backend is used :/ create_permissions(auth_app, 1) self.user = User.objects.create(username='user') self.group = Group.objects.create(name='group') def test_clean_perms(self): # assign obj perms target_perms = { self.target_user1: ["change_%s" % user_module_name], self.target_group1: ["delete_group"], self.target_obj1: ["change_contenttype", "delete_contenttype"], self.target_obj2: ["change_contenttype"], } obj_perms_count = sum([len(val) for key, val in target_perms.items()]) for target, perms in target_perms.items(): target.__old_pk = target.pk # Store pkeys for perm in perms: assign_perm(perm, self.user, target) # Remove targets for target, perms in target_perms.items(): target.delete() # Clean orphans removed = clean_orphan_obj_perms() self.assertEqual(removed, obj_perms_count) # Recreate targets and check if user has no permissions for target, perms in target_perms.items(): target.pk = target.__old_pk target.save() for perm in perms: self.assertFalse(self.user.has_perm(perm, target)) def test_clean_perms_command(self): """ Same test as the one above but rather function directly, we call management command instead. """ # assign obj perms target_perms = { self.target_user1: ["change_%s" % user_module_name], self.target_group1: ["delete_group"], self.target_obj1: ["change_contenttype", "delete_contenttype"], self.target_obj2: ["change_contenttype"], } for target, perms in target_perms.items(): target.__old_pk = target.pk # Store pkeys for perm in perms: assign_perm(perm, self.user, target) # Remove targets for target, perms in target_perms.items(): target.delete() # Clean orphans call_command("clean_orphan_obj_perms", verbosity=0) # Recreate targets and check if user has no permissions for target, perms in target_perms.items(): target.pk = target.__old_pk target.save() for perm in perms: self.assertFalse(self.user.has_perm(perm, target)) django-guardian-2.0.0/guardian/testapp/tests/test_other.py000066400000000000000000000312131347767355200237340ustar00rootroot00000000000000import mock import unittest from django.contrib.auth import get_user_model from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.test import TestCase import guardian from guardian.backends import ObjectPermissionBackend from guardian.compat import get_user_model_path from guardian.compat import get_user_permission_codename from guardian.exceptions import GuardianError from guardian.exceptions import NotUserNorGroup from guardian.exceptions import ObjectNotPersisted from guardian.exceptions import WrongAppError from guardian.models import GroupObjectPermission from guardian.models import UserObjectPermission from guardian.testapp.tests.conf import TestDataMixin User = get_user_model() user_model_path = get_user_model_path() class UserPermissionTests(TestDataMixin, TestCase): def setUp(self): super().setUp() self.user = User.objects.get(username='jack') self.ctype = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') self.obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') self.obj2 = ContentType.objects.create( model='bar', app_label='guardian-tests') def test_assignement(self): self.assertFalse(self.user.has_perm('change_contenttype', self.ctype)) UserObjectPermission.objects.assign_perm('change_contenttype', self.user, self.ctype) self.assertTrue(self.user.has_perm('change_contenttype', self.ctype)) self.assertTrue(self.user.has_perm('contenttypes.change_contenttype', self.ctype)) def test_assignement_and_remove(self): UserObjectPermission.objects.assign_perm('change_contenttype', self.user, self.ctype) self.assertTrue(self.user.has_perm('change_contenttype', self.ctype)) UserObjectPermission.objects.remove_perm('change_contenttype', self.user, self.ctype) self.assertFalse(self.user.has_perm('change_contenttype', self.ctype)) def test_ctypes(self): UserObjectPermission.objects.assign_perm( 'change_contenttype', self.user, self.obj1) self.assertTrue(self.user.has_perm('change_contenttype', self.obj1)) self.assertFalse(self.user.has_perm('change_contenttype', self.obj2)) UserObjectPermission.objects.remove_perm( 'change_contenttype', self.user, self.obj1) UserObjectPermission.objects.assign_perm( 'change_contenttype', self.user, self.obj2) self.assertTrue(self.user.has_perm('change_contenttype', self.obj2)) self.assertFalse(self.user.has_perm('change_contenttype', self.obj1)) UserObjectPermission.objects.assign_perm( 'change_contenttype', self.user, self.obj1) UserObjectPermission.objects.assign_perm( 'change_contenttype', self.user, self.obj2) self.assertTrue(self.user.has_perm('change_contenttype', self.obj2)) self.assertTrue(self.user.has_perm('change_contenttype', self.obj1)) UserObjectPermission.objects.remove_perm( 'change_contenttype', self.user, self.obj1) UserObjectPermission.objects.remove_perm( 'change_contenttype', self.user, self.obj2) self.assertFalse(self.user.has_perm('change_contenttype', self.obj2)) self.assertFalse(self.user.has_perm('change_contenttype', self.obj1)) def test_assign_perm_validation(self): self.assertRaises(Permission.DoesNotExist, UserObjectPermission.objects.assign_perm, 'change_group', self.user, self.user) group = Group.objects.create(name='test_group_assign_perm_validation') ctype = ContentType.objects.get_for_model(group) user_ctype = ContentType.objects.get_for_model(self.user) codename = get_user_permission_codename('change') perm = Permission.objects.get( codename=codename, content_type=user_ctype) create_info = dict( permission=perm, user=self.user, content_type=ctype, object_pk=group.pk ) self.assertRaises(ValidationError, UserObjectPermission.objects.create, **create_info) def test_errors(self): not_saved_user = User(username='not_saved_user') codename = get_user_permission_codename('change') self.assertRaises(ObjectNotPersisted, UserObjectPermission.objects.assign_perm, codename, self.user, not_saved_user) self.assertRaises(ObjectNotPersisted, UserObjectPermission.objects.remove_perm, codename, self.user, not_saved_user) class GroupPermissionTests(TestDataMixin, TestCase): def setUp(self): super().setUp() self.user = User.objects.get(username='jack') self.group, created = Group.objects.get_or_create(name='jackGroup') self.user.groups.add(self.group) self.ctype = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') self.obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') self.obj2 = ContentType.objects.create( model='bar', app_label='guardian-tests') def test_assignement(self): self.assertFalse(self.user.has_perm('change_contenttype', self.ctype)) self.assertFalse(self.user.has_perm('contenttypes.change_contenttype', self.ctype)) GroupObjectPermission.objects.assign_perm('change_contenttype', self.group, self.ctype) self.assertTrue(self.user.has_perm('change_contenttype', self.ctype)) self.assertTrue(self.user.has_perm('contenttypes.change_contenttype', self.ctype)) def test_assignement_and_remove(self): GroupObjectPermission.objects.assign_perm('change_contenttype', self.group, self.ctype) self.assertTrue(self.user.has_perm('change_contenttype', self.ctype)) GroupObjectPermission.objects.remove_perm('change_contenttype', self.group, self.ctype) self.assertFalse(self.user.has_perm('change_contenttype', self.ctype)) def test_ctypes(self): GroupObjectPermission.objects.assign_perm('change_contenttype', self.group, self.obj1) self.assertTrue(self.user.has_perm('change_contenttype', self.obj1)) self.assertFalse(self.user.has_perm('change_contenttype', self.obj2)) GroupObjectPermission.objects.remove_perm('change_contenttype', self.group, self.obj1) GroupObjectPermission.objects.assign_perm('change_contenttype', self.group, self.obj2) self.assertTrue(self.user.has_perm('change_contenttype', self.obj2)) self.assertFalse(self.user.has_perm('change_contenttype', self.obj1)) GroupObjectPermission.objects.assign_perm('change_contenttype', self.group, self.obj1) GroupObjectPermission.objects.assign_perm('change_contenttype', self.group, self.obj2) self.assertTrue(self.user.has_perm('change_contenttype', self.obj2)) self.assertTrue(self.user.has_perm('change_contenttype', self.obj1)) GroupObjectPermission.objects.remove_perm('change_contenttype', self.group, self.obj1) GroupObjectPermission.objects.remove_perm('change_contenttype', self.group, self.obj2) self.assertFalse(self.user.has_perm('change_contenttype', self.obj2)) self.assertFalse(self.user.has_perm('change_contenttype', self.obj1)) def test_assign_perm_validation(self): self.assertRaises(Permission.DoesNotExist, GroupObjectPermission.objects.assign_perm, 'change_user', self.group, self.group) user = User.objects.create(username='testuser') ctype = ContentType.objects.get_for_model(user) perm = Permission.objects.get(codename='change_group') create_info = dict( permission=perm, group=self.group, content_type=ctype, object_pk=user.pk ) self.assertRaises(ValidationError, GroupObjectPermission.objects.create, **create_info) def test_errors(self): not_saved_group = Group(name='not_saved_group') self.assertRaises(ObjectNotPersisted, GroupObjectPermission.objects.assign_perm, "change_group", self.group, not_saved_group) self.assertRaises(ObjectNotPersisted, GroupObjectPermission.objects.remove_perm, "change_group", self.group, not_saved_group) class ObjectPermissionBackendTests(TestCase): def setUp(self): self.user = User.objects.create(username='jack') self.backend = ObjectPermissionBackend() def test_attrs(self): self.assertTrue(self.backend.supports_anonymous_user) self.assertTrue(self.backend.supports_object_permissions) self.assertTrue(self.backend.supports_inactive_user) def test_authenticate(self): self.assertEqual(self.backend.authenticate( self.user.username, self.user.password), None) def test_has_perm_noobj(self): result = self.backend.has_perm(self.user, "change_contenttype") self.assertFalse(result) def test_has_perm_notauthed(self): user = AnonymousUser() self.assertFalse(self.backend.has_perm(user, "change_user", self.user)) def test_has_perm_wrong_app(self): self.assertRaises(WrongAppError, self.backend.has_perm, self.user, "no_app.change_user", self.user) def test_obj_is_not_model(self): for obj in (Group, 666, "String", [2, 1, 5, 7], {}): self.assertFalse(self.backend.has_perm(self.user, "any perm", obj)) def test_not_active_user(self): user = User.objects.create(username='non active user') ctype = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') perm = 'change_contenttype' UserObjectPermission.objects.assign_perm(perm, user, ctype) self.assertTrue(self.backend.has_perm(user, perm, ctype)) user.is_active = False user.save() self.assertFalse(self.backend.has_perm(user, perm, ctype)) class GuardianBaseTests(TestCase): def has_attrs(self): self.assertTrue(hasattr(guardian, '__version__')) def test_version(self): for x in guardian.VERSION: self.assertTrue(isinstance(x, (int, str))) def test_get_version(self): self.assertTrue(isinstance(guardian.get_version(), str)) class TestExceptions(TestCase): def _test_error_class(self, exc_cls): self.assertTrue(isinstance(exc_cls, GuardianError)) def test_error_classes(self): self.assertTrue(isinstance(GuardianError(), Exception)) guardian_errors = [NotUserNorGroup] for err in guardian_errors: self._test_error_class(err()) @unittest.skip("test is broken") class TestMonkeyPatch(TestCase): @mock.patch('django.contrib.auth.get_user_model') def test_monkey_patch(self, mocked_get_user_model): class CustomUserTestClass(AbstractUser): pass mocked_get_user_model.return_value = CustomUserTestClass self.assertFalse(getattr(CustomUserTestClass, 'get_anonymous', False)) self.assertFalse(getattr(CustomUserTestClass, 'add_obj_perm', False)) self.assertFalse(getattr(CustomUserTestClass, 'del_obj_perm', False)) # Monkey Patch guardian.monkey_patch_user() self.assertTrue(getattr(CustomUserTestClass, 'get_anonymous', False)) self.assertTrue(getattr(CustomUserTestClass, 'add_obj_perm', False)) self.assertTrue(getattr(CustomUserTestClass, 'del_obj_perm', False)) django-guardian-2.0.0/guardian/testapp/tests/test_shortcuts.py000066400000000000000000001543101347767355200246550ustar00rootroot00000000000000import warnings import django from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes.models import ContentType from django.db.models.query import QuerySet from django.test import TestCase from guardian.shortcuts import get_perms_for_model from guardian.core import ObjectPermissionChecker from guardian.compat import get_user_permission_full_codename from guardian.shortcuts import assign from guardian.shortcuts import assign_perm from guardian.shortcuts import remove_perm from guardian.shortcuts import get_perms from guardian.shortcuts import get_user_perms from guardian.shortcuts import get_group_perms from guardian.shortcuts import get_users_with_perms from guardian.shortcuts import get_groups_with_perms from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_objects_for_group from guardian.exceptions import MixedContentTypeError from guardian.exceptions import NotUserNorGroup from guardian.exceptions import WrongAppError from guardian.exceptions import MultipleIdentityAndObjectError from guardian.testapp.models import NonIntPKModel from guardian.testapp.tests.test_core import ObjectPermissionTestCase from guardian.models import Group, Permission User = get_user_model() user_app_label = User._meta.app_label user_module_name = User._meta.model_name class ShortcutsTests(ObjectPermissionTestCase): def test_get_perms_for_model(self): expected_perms_amount = 3 if django.VERSION < (2, 1) else 4 self.assertEqual(get_perms_for_model(self.user).count(), expected_perms_amount) self.assertTrue(list(get_perms_for_model(self.user)) == list(get_perms_for_model(User))) self.assertEqual(get_perms_for_model(Permission).count(), expected_perms_amount) model_str = 'contenttypes.ContentType' self.assertEqual( sorted(get_perms_for_model(model_str).values_list()), sorted(get_perms_for_model(ContentType).values_list())) obj = ContentType() self.assertEqual( sorted(get_perms_for_model(model_str).values_list()), sorted(get_perms_for_model(obj).values_list())) class AssignPermTest(ObjectPermissionTestCase): """ Tests permission assigning for user/group and object. """ def test_not_model(self): self.assertRaises(NotUserNorGroup, assign_perm, perm="change_object", user_or_group="Not a Model", obj=self.ctype) def test_global_wrong_perm(self): self.assertRaises(ValueError, assign_perm, perm="change_site", # for global permissions must provide app_label user_or_group=self.user) def test_user_assign_perm(self): assign_perm("add_contenttype", self.user, self.ctype) assign_perm("change_contenttype", self.group, self.ctype) assign_perm(self.get_permission("delete_contenttype"), self.user, self.ctype) self.assertTrue(self.user.has_perm("add_contenttype", self.ctype)) self.assertTrue(self.user.has_perm("change_contenttype", self.ctype)) self.assertTrue(self.user.has_perm("delete_contenttype", self.ctype)) def test_group_assign_perm(self): assign_perm("add_contenttype", self.group, self.ctype) assign_perm("change_contenttype", self.group, self.ctype) assign_perm(self.get_permission("delete_contenttype"), self.group, self.ctype) check = ObjectPermissionChecker(self.group) self.assertTrue(check.has_perm("add_contenttype", self.ctype)) self.assertTrue(check.has_perm("change_contenttype", self.ctype)) self.assertTrue(check.has_perm("delete_contenttype", self.ctype)) def test_user_assign_perm_queryset(self): assign_perm("add_contenttype", self.user, self.ctype_qset) assign_perm("change_contenttype", self.group, self.ctype_qset) assign_perm(self.get_permission("delete_contenttype"), self.user, self.ctype_qset) for obj in self.ctype_qset: self.assertTrue(self.user.has_perm("add_contenttype", obj)) self.assertTrue(self.user.has_perm("change_contenttype", obj)) self.assertTrue(self.user.has_perm("delete_contenttype", obj)) def test_group_assign_perm_queryset(self): assign_perm("add_contenttype", self.group, self.ctype_qset) assign_perm("change_contenttype", self.group, self.ctype_qset) assign_perm(self.get_permission("delete_contenttype"), self.group, self.ctype_qset) check = ObjectPermissionChecker(self.group) for obj in self.ctype_qset: self.assertTrue(check.has_perm("add_contenttype", obj)) self.assertTrue(check.has_perm("change_contenttype", obj)) self.assertTrue(check.has_perm("delete_contenttype", obj)) def test_user_assign_perm_global(self): perm = assign_perm("contenttypes.change_contenttype", self.user) self.assertTrue(self.user.has_perm("contenttypes.change_contenttype")) self.assertTrue(isinstance(perm, Permission)) def test_group_assign_perm_global(self): perm = assign_perm("contenttypes.change_contenttype", self.group) self.assertTrue(self.user.has_perm("contenttypes.change_contenttype")) self.assertTrue(isinstance(perm, Permission)) def test_deprecation_warning(self): with warnings.catch_warnings(record=True) as warns: warnings.simplefilter('always') assign("contenttypes.change_contenttype", self.group) self.assertEqual(len(warns), 1) self.assertTrue(isinstance(warns[0].message, DeprecationWarning)) class MultipleIdentitiesOperationsTest(ObjectPermissionTestCase): """ Tests assignment of permission to multiple users or groups """ def setUp(self): super().setUp() self.users_list = jim, bob = [ User.objects.create_user(username='jim'), User.objects.create_user(username='bob') ] self.groups_list = jim_group, bob_group = [ Group.objects.create(name='jimgroup'), Group.objects.create(name='bobgroup') ] jim_group.user_set.add(jim) bob_group.user_set.add(bob) self.users_qs = User.objects.exclude(username='AnonymousUser') self.groups_qs = Group.objects.all() def test_assign_to_many_users_queryset(self): assign_perm("add_contenttype", self.users_qs, self.ctype) assign_perm(self.get_permission("delete_contenttype"), self.users_qs, self.ctype) for user in self.users_list: self.assertTrue(user.has_perm("add_contenttype", self.ctype)) self.assertTrue(user.has_perm("delete_contenttype", self.ctype)) def test_assign_to_many_users_list(self): assign_perm("add_contenttype", self.users_list, self.ctype) assign_perm(self.get_permission("delete_contenttype"), self.users_list, self.ctype) for user in self.users_list: self.assertTrue(user.has_perm("add_contenttype", self.ctype)) self.assertTrue(user.has_perm("delete_contenttype", self.ctype)) def test_assign_to_many_groups_queryset(self): assign_perm("add_contenttype", self.groups_qs, self.ctype) assign_perm(self.get_permission("delete_contenttype"), self.groups_qs, self.ctype) for user in self.users_list: self.assertTrue(user.has_perm("add_contenttype", self.ctype)) self.assertTrue(user.has_perm("delete_contenttype", self.ctype)) def test_assign_to_many_groups_list(self): assign_perm("add_contenttype", self.groups_list, self.ctype) assign_perm(self.get_permission("delete_contenttype"), self.groups_list, self.ctype) for user in self.users_list: self.assertTrue(user.has_perm("add_contenttype", self.ctype)) self.assertTrue(user.has_perm("delete_contenttype", self.ctype)) def test_assign_to_multiple_identity_and_obj(self): with self.assertRaises(MultipleIdentityAndObjectError): assign_perm("add_contenttype", self.users_list, self.ctype_qset) with self.assertRaises(MultipleIdentityAndObjectError): assign_perm("add_contenttype", self.users_qs, self.ctype_qset) class RemovePermTest(ObjectPermissionTestCase): """ Tests object permissions removal. """ def test_not_model(self): self.assertRaises(NotUserNorGroup, remove_perm, perm="change_object", user_or_group="Not a Model", obj=self.ctype) def test_global_wrong_perm(self): self.assertRaises(ValueError, remove_perm, perm="change_site", # for global permissions must provide app_label user_or_group=self.user) def test_user_remove_perm(self): # assign perm first assign_perm("change_contenttype", self.user, self.ctype) remove_perm("change_contenttype", self.user, self.ctype) self.assertFalse(self.user.has_perm("change_contenttype", self.ctype)) def test_group_remove_perm(self): # assign perm first assign_perm("change_contenttype", self.group, self.ctype) remove_perm("change_contenttype", self.group, self.ctype) check = ObjectPermissionChecker(self.group) self.assertFalse(check.has_perm("change_contenttype", self.ctype)) def test_user_remove_perm_queryset(self): assign_perm("change_contenttype", self.user, self.ctype_qset) remove_perm("change_contenttype", self.user, self.ctype_qset) for obj in self.ctype_qset: self.assertFalse(self.user.has_perm("change_contenttype", obj)) def test_user_remove_perm_empty_queryset(self): assign_perm("change_contenttype", self.user, self.ctype_qset) remove_perm("change_contenttype", self.user, self.ctype_qset.none()) self.assertEqual(list(self.ctype_qset.none()), []) for obj in self.ctype_qset: self.assertTrue(self.user.has_perm("change_contenttype", obj)) def test_group_remove_perm_queryset(self): assign_perm("change_contenttype", self.group, self.ctype_qset) remove_perm("change_contenttype", self.group, self.ctype_qset) check = ObjectPermissionChecker(self.group) for obj in self.ctype_qset: self.assertFalse(check.has_perm("change_contenttype", obj)) def test_user_remove_perm_global(self): # assign perm first perm = "contenttypes.change_contenttype" assign_perm(perm, self.user) remove_perm(perm, self.user) self.assertFalse(self.user.has_perm(perm)) def test_group_remove_perm_global(self): # assign perm first perm = "contenttypes.change_contenttype" assign_perm(perm, self.group) remove_perm(perm, self.group) app_label, codename = perm.split('.') perm_obj = Permission.objects.get(codename=codename, content_type__app_label=app_label) self.assertFalse(perm_obj in self.group.permissions.all()) class GetPermsTest(ObjectPermissionTestCase): """ Tests get_perms function (already done at core tests but left here as a placeholder). """ def test_not_model(self): self.assertRaises(NotUserNorGroup, get_perms, user_or_group=None, obj=self.ctype) def test_user(self): perms_to_assign = ("change_contenttype",) for perm in perms_to_assign: assign_perm("change_contenttype", self.user, self.ctype) perms = get_perms(self.user, self.ctype) for perm in perms_to_assign: self.assertTrue(perm in perms) class GetUsersWithPermsTest(TestCase): """ Tests get_users_with_perms function. """ def setUp(self): self.obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') self.obj2 = ContentType.objects.create( model='bar', app_label='guardian-tests') self.user1 = User.objects.create(username='user1') self.user2 = User.objects.create(username='user2') self.user3 = User.objects.create(username='user3') self.group1 = Group.objects.create(name='group1') self.group2 = Group.objects.create(name='group2') self.group3 = Group.objects.create(name='group3') def test_empty(self): result = get_users_with_perms(self.obj1) self.assertTrue(isinstance(result, QuerySet)) self.assertEqual(list(result), []) result = get_users_with_perms(self.obj1, attach_perms=True) self.assertTrue(isinstance(result, dict)) self.assertFalse(bool(result)) def test_simple(self): assign_perm("change_contenttype", self.user1, self.obj1) assign_perm("delete_contenttype", self.user2, self.obj1) assign_perm("delete_contenttype", self.user3, self.obj2) result = get_users_with_perms(self.obj1) result_vals = result.values_list('username', flat=True) self.assertEqual( set(result_vals), set([user.username for user in (self.user1, self.user2)]), ) def test_only_with_perms_in(self): assign_perm("change_contenttype", self.user1, self.obj1) assign_perm("delete_contenttype", self.user2, self.obj1) assign_perm("delete_contenttype", self.user3, self.obj2) result = get_users_with_perms(self.obj1, only_with_perms_in=('change_contenttype',)) result_vals = result.values_list('username', flat=True) self.assertEqual( set(result_vals), set((self.user1.username,)), ) def test_only_with_perms_in_and_with_group_users(self): self.user1.groups.add(self.group1) self.user2.groups.add(self.group2) self.user3.groups.add(self.group3) # assign perms to groups assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("delete_contenttype", self.group2, self.obj1) assign_perm("add_contenttype", self.group3, self.obj2) result = get_users_with_perms(self.obj1, only_with_perms_in=('change_contenttype', 'delete_contenttype'), with_group_users=True) result_vals = result.values_list('username', flat=True) self.assertEqual( set(result_vals), set((self.user1.username, self.user2.username)), ) def test_only_with_perms_in_and_not_with_group_users(self): self.user1.groups.add(self.group1) self.user2.groups.add(self.group2) self.user3.groups.add(self.group3) # assign perms to groups assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("delete_contenttype", self.group2, self.obj1) assign_perm("delete_contenttype", self.group3, self.obj2) # assign perms to user assign_perm("change_contenttype", self.user2, self.obj1) result = get_users_with_perms(self.obj1, only_with_perms_in=('change_contenttype', 'delete_contenttype'), with_group_users=False) result_vals = result.values_list('username', flat=True) self.assertEqual( set(result_vals), set((self.user2.username,)), ) def test_only_with_perms_in_attached(self): assign_perm("change_contenttype", self.user1, self.obj1) assign_perm("change_contenttype", self.user2, self.obj1) assign_perm("delete_contenttype", self.user2, self.obj1) assign_perm("delete_contenttype", self.user3, self.obj2) result = get_users_with_perms(self.obj1, only_with_perms_in=('delete_contenttype',), attach_perms=True) expected = {self.user2: ('change_contenttype', 'delete_contenttype')} self.assertEqual(result.keys(), expected.keys()) for key, perms in result.items(): self.assertEqual(set(perms), set(expected[key])) def test_users_groups_perms(self): self.user1.groups.add(self.group1) self.user2.groups.add(self.group2) self.user3.groups.add(self.group3) assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("change_contenttype", self.group2, self.obj1) assign_perm("delete_contenttype", self.group3, self.obj2) result = get_users_with_perms(self.obj1).values_list('pk', flat=True) self.assertEqual( set(result), set([u.pk for u in (self.user1, self.user2)]) ) def test_users_groups_after_removal(self): self.test_users_groups_perms() remove_perm("change_contenttype", self.group1, self.obj1) result = get_users_with_perms(self.obj1).values_list('pk', flat=True) self.assertEqual( set(result), set([self.user2.pk]), ) def test_attach_perms(self): self.user1.groups.add(self.group1) self.user2.groups.add(self.group2) self.user3.groups.add(self.group3) assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("change_contenttype", self.group2, self.obj1) assign_perm("delete_contenttype", self.group3, self.obj2) assign_perm("delete_contenttype", self.user2, self.obj1) assign_perm("change_contenttype", self.user3, self.obj2) # Check contenttype1 result = get_users_with_perms(self.obj1, attach_perms=True) expected = { self.user1: ["change_contenttype"], self.user2: ["change_contenttype", "delete_contenttype"], } self.assertEqual(result.keys(), expected.keys()) for key, perms in result.items(): self.assertEqual(set(perms), set(expected[key])) # Check contenttype2 result = get_users_with_perms(self.obj2, attach_perms=True) expected = { self.user3: ["change_contenttype", "delete_contenttype"], } self.assertEqual(result.keys(), expected.keys()) for key, perms in result.items(): self.assertEqual(set(perms), set(expected[key])) def test_attach_groups_only_has_perms(self): self.user1.groups.add(self.group1) assign_perm("change_contenttype", self.group1, self.obj1) result = get_users_with_perms(self.obj1, attach_perms=True) expected = {self.user1: ["change_contenttype"]} self.assertEqual(result, expected) def test_mixed(self): self.user1.groups.add(self.group1) assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("change_contenttype", self.user2, self.obj1) assign_perm("delete_contenttype", self.user2, self.obj1) assign_perm("delete_contenttype", self.user2, self.obj2) assign_perm("change_contenttype", self.user3, self.obj2) assign_perm("change_%s" % user_module_name, self.user3, self.user1) result = get_users_with_perms(self.obj1) self.assertEqual( set(result), set([self.user1, self.user2]), ) def test_with_superusers(self): admin = User.objects.create(username='admin', is_superuser=True) assign_perm("change_contenttype", self.user1, self.obj1) result = get_users_with_perms(self.obj1, with_superusers=True) self.assertEqual( set(result), set([self.user1, admin]), ) def test_without_group_users(self): self.user1.groups.add(self.group1) self.user2.groups.add(self.group2) assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("change_contenttype", self.user2, self.obj1) assign_perm("change_contenttype", self.group2, self.obj1) result = get_users_with_perms(self.obj1, with_group_users=False) expected = set([self.user2]) self.assertEqual(set(result), expected) def test_without_group_users_but_perms_attached(self): self.user1.groups.add(self.group1) self.user2.groups.add(self.group2) assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("change_contenttype", self.user2, self.obj1) assign_perm("change_contenttype", self.group2, self.obj1) result = get_users_with_perms(self.obj1, with_group_users=False, attach_perms=True) expected = {self.user2: ["change_contenttype"]} self.assertEqual(result, expected) def test_direct_perms_only(self): admin = User.objects.create(username='admin', is_superuser=True) self.user1.groups.add(self.group1) self.user2.groups.add(self.group1) assign_perm("change_contenttype", self.user1, self.obj1) assign_perm("delete_contenttype", admin, self.obj1) assign_perm("delete_contenttype", self.group1, self.obj1) expected = set([self.user1, self.user2, admin]) result = get_users_with_perms(self.obj1, with_superusers=False, with_group_users=True) self.assertEqual(set(result), expected) self.assertEqual(set(get_user_perms(self.user1, self.obj1)), set(['change_contenttype'])) self.assertEqual(set(get_user_perms(self.user2, self.obj1)), set([])) self.assertEqual(set(get_user_perms(admin, self.obj1)), set(['delete_contenttype'])) result = get_users_with_perms(self.obj1, with_superusers=False, with_group_users=False) expected = set([self.user1, admin]) self.assertEqual(set(result), expected) self.assertEqual(set(get_group_perms(self.user1, self.obj1)), set(['delete_contenttype'])) self.assertEqual(set(get_group_perms(self.user2, self.obj1)), set(['delete_contenttype'])) self.assertEqual(set(get_group_perms(self.group1, self.obj1)), set(['delete_contenttype'])) self.assertEqual(set(get_group_perms(self.group2, self.obj1)), set([])) self.assertEqual(set(get_group_perms(admin, self.obj1)), set([])) expected_permissions = ['add_contenttype', 'change_contenttype', 'delete_contenttype'] if django.VERSION >= (2, 1): expected_permissions.append('view_contenttype') self.assertEqual(set(get_perms(admin, self.obj1)), set(expected_permissions)) self.assertEqual(set(get_perms(self.user1, self.obj1)), set(['change_contenttype', 'delete_contenttype'])) self.assertEqual(set(get_perms(self.user2, self.obj1)), set(['delete_contenttype'])) self.assertEqual(set(get_perms(self.group1, self.obj1)), set(['delete_contenttype'])) self.assertEqual(set(get_perms(self.group2, self.obj1)), set([])) def test_direct_perms_only_perms_attached(self): admin = User.objects.create(username='admin', is_superuser=True) self.user1.groups.add(self.group1) self.user2.groups.add(self.group1) assign_perm("change_contenttype", self.user1, self.obj1) assign_perm("delete_contenttype", admin, self.obj1) assign_perm("delete_contenttype", self.group1, self.obj1) expected = { self.user1: ["change_contenttype", "delete_contenttype"], admin: ["add_contenttype", "change_contenttype", "delete_contenttype"], self.user2: ["delete_contenttype"] } if django.VERSION >= (2, 1): expected[admin].append("view_contenttype") result = get_users_with_perms(self.obj1, attach_perms=True, with_superusers=False, with_group_users=True) self.assertEqual(result.keys(), expected.keys()) for key, perms in result.items(): self.assertEqual(set(perms), set(expected[key])) result = get_users_with_perms(self.obj1, attach_perms=True, with_superusers=False, with_group_users=False) expected = {self.user1: ["change_contenttype"], admin: ["delete_contenttype"]} self.assertEqual(result, expected) def test_without_group_users_no_result(self): self.user1.groups.add(self.group1) assign_perm("change_contenttype", self.group1, self.obj1) result = get_users_with_perms(self.obj1, attach_perms=True, with_group_users=False) expected = {} self.assertEqual(result, expected) def test_without_group_users_no_result_but_with_superusers(self): admin = User.objects.create(username='admin', is_superuser=True) self.user1.groups.add(self.group1) assign_perm("change_contenttype", self.group1, self.obj1) result = get_users_with_perms(self.obj1, with_group_users=False, with_superusers=True) expected = [admin] self.assertEqual(set(result), set(expected)) class GetGroupsWithPerms(TestCase): """ Tests get_groups_with_perms function. """ def setUp(self): self.obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') self.obj2 = ContentType.objects.create( model='bar', app_label='guardian-tests') self.user1 = User.objects.create(username='user1') self.user2 = User.objects.create(username='user2') self.user3 = User.objects.create(username='user3') self.group1 = Group.objects.create(name='group1') self.group2 = Group.objects.create(name='group2') self.group3 = Group.objects.create(name='group3') def test_empty(self): result = get_groups_with_perms(self.obj1) self.assertTrue(isinstance(result, QuerySet)) self.assertFalse(bool(result)) result = get_groups_with_perms(self.obj1, attach_perms=True) self.assertTrue(isinstance(result, dict)) self.assertFalse(bool(result)) def test_simple(self): assign_perm("change_contenttype", self.group1, self.obj1) result = get_groups_with_perms(self.obj1) self.assertEqual(len(result), 1) self.assertEqual(result[0], self.group1) def test_simple_after_removal(self): self.test_simple() remove_perm("change_contenttype", self.group1, self.obj1) result = get_groups_with_perms(self.obj1) self.assertEqual(len(result), 0) def test_simple_attach_perms(self): assign_perm("change_contenttype", self.group1, self.obj1) result = get_groups_with_perms(self.obj1, attach_perms=True) expected = {self.group1: ["change_contenttype"]} self.assertEqual(result, expected) def test_simple_attach_perms_after_removal(self): self.test_simple_attach_perms() remove_perm("change_contenttype", self.group1, self.obj1) result = get_groups_with_perms(self.obj1, attach_perms=True) self.assertEqual(len(result), 0) def test_filter_by_contenttype(self): # Make sure that both objects have same pk. obj = ContentType.objects.create(pk=1042, model='baz', app_label='guardian-tests') group = Group.objects.create(pk=1042, name='group') assign_perm("change_group", self.group1, group) assign_perm("change_contenttype", self.group1, obj) result = get_groups_with_perms(obj, attach_perms=True) # No group permissions should be included, even though objects have same pk. self.assertEqual(result[self.group1], ["change_contenttype"]) def test_mixed(self): assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("change_contenttype", self.group1, self.obj2) assign_perm("change_%s" % user_module_name, self.group1, self.user3) assign_perm("change_contenttype", self.group2, self.obj2) assign_perm("change_contenttype", self.group2, self.obj1) assign_perm("delete_contenttype", self.group2, self.obj1) assign_perm("change_%s" % user_module_name, self.group3, self.user1) result = get_groups_with_perms(self.obj1) self.assertEqual(set(result), set([self.group1, self.group2])) def test_mixed_attach_perms(self): assign_perm("change_contenttype", self.group1, self.obj1) assign_perm("change_contenttype", self.group1, self.obj2) assign_perm("change_group", self.group1, self.group3) assign_perm("change_contenttype", self.group2, self.obj2) assign_perm("change_contenttype", self.group2, self.obj1) assign_perm("delete_contenttype", self.group2, self.obj1) assign_perm("change_group", self.group3, self.group1) result = get_groups_with_perms(self.obj1, attach_perms=True) expected = { self.group1: ["change_contenttype"], self.group2: ["change_contenttype", "delete_contenttype"], } self.assertEqual(result.keys(), expected.keys()) for key, perms in result.items(): self.assertEqual(set(perms), set(expected[key])) class GetObjectsForUser(TestCase): def setUp(self): self.user = User.objects.create(username='joe') self.group = Group.objects.create(name='group') self.ctype = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') def test_superuser(self): self.user.is_superuser = True ctypes = ContentType.objects.all() objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype'], ctypes) self.assertEqual(set(ctypes), set(objects)) def test_with_superuser_true(self): self.user.is_superuser = True ctypes = ContentType.objects.all() objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype'], ctypes, with_superuser=True) self.assertEqual(set(ctypes), set(objects)) def test_with_superuser_false(self): self.user.is_superuser = True ctypes = ContentType.objects.all() obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') assign_perm('change_contenttype', self.user, obj1) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype'], ctypes, with_superuser=False) self.assertEqual(set([obj1]), set(objects)) def test_anonymous(self): self.user = AnonymousUser() ctypes = ContentType.objects.all() objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype'], ctypes) obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') assign_perm('change_contenttype', self.user, obj1) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype'], ctypes) self.assertEqual(set([obj1]), set(objects)) def test_mixed_perms(self): codenames = [ get_user_permission_full_codename('change'), 'auth.change_permission', ] self.assertRaises(MixedContentTypeError, get_objects_for_user, self.user, codenames) def test_perms_with_mixed_apps(self): codenames = [ get_user_permission_full_codename('change'), 'contenttypes.change_contenttype', ] self.assertRaises(MixedContentTypeError, get_objects_for_user, self.user, codenames) def test_mixed_perms_and_klass(self): self.assertRaises(MixedContentTypeError, get_objects_for_user, self.user, ['auth.change_group'], User) def test_no_app_label_nor_klass(self): self.assertRaises(WrongAppError, get_objects_for_user, self.user, ['change_group']) def test_empty_perms_sequence(self): objects = get_objects_for_user(self.user, [], Group.objects.all()) self.assertEqual( set(objects), set() ) def test_perms_single(self): perm = 'auth.change_group' assign_perm(perm, self.user, self.group) self.assertEqual( set(get_objects_for_user(self.user, perm)), set(get_objects_for_user(self.user, [perm]))) def test_klass_as_model(self): assign_perm('contenttypes.change_contenttype', self.user, self.ctype) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype'], ContentType) self.assertEqual([obj.name for obj in objects], [self.ctype.name]) def test_klass_as_manager(self): assign_perm('auth.change_group', self.user, self.group) objects = get_objects_for_user(self.user, ['auth.change_group'], Group.objects) self.assertEqual([obj.name for obj in objects], [self.group.name]) def test_klass_as_queryset(self): assign_perm('auth.change_group', self.user, self.group) objects = get_objects_for_user(self.user, ['auth.change_group'], Group.objects.all()) self.assertEqual([obj.name for obj in objects], [self.group.name]) def test_ensure_returns_queryset(self): objects = get_objects_for_user(self.user, ['auth.change_group']) self.assertTrue(isinstance(objects, QuerySet)) def test_simple(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] for group in groups: assign_perm('change_group', self.user, group) objects = get_objects_for_user(self.user, ['auth.change_group']) self.assertEqual(len(objects), len(groups)) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects), set(groups)) def test_multiple_perms_to_check(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] for group in groups: assign_perm('auth.change_group', self.user, group) assign_perm('auth.delete_group', self.user, groups[1]) objects = get_objects_for_user(self.user, ['auth.change_group', 'auth.delete_group']) self.assertEqual(len(objects), 1) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects.values_list('name', flat=True)), set([groups[1].name])) def test_multiple_perms_to_check_no_groups(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] for group in groups: assign_perm('auth.change_group', self.user, group) assign_perm('auth.delete_group', self.user, groups[1]) objects = get_objects_for_user(self.user, ['auth.change_group', 'auth.delete_group'], use_groups=False) self.assertEqual(len(objects), 1) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects.values_list('name', flat=True)), set([groups[1].name])) def test_any_of_multiple_perms_to_check(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] assign_perm('auth.change_group', self.user, groups[0]) assign_perm('auth.delete_group', self.user, groups[2]) objects = get_objects_for_user(self.user, ['auth.change_group', 'auth.delete_group'], any_perm=True) self.assertEqual(len(objects), 2) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects.values_list('name', flat=True)), set([groups[0].name, groups[2].name])) def test_groups_perms(self): group1 = Group.objects.create(name='group1') group2 = Group.objects.create(name='group2') group3 = Group.objects.create(name='group3') groups = [group1, group2, group3] for group in groups: self.user.groups.add(group) # Objects to operate on ctypes = list(ContentType.objects.all().order_by('id')) assign_perm('auth.change_group', self.user) assign_perm('change_contenttype', self.user, ctypes[0]) assign_perm('change_contenttype', self.user, ctypes[1]) assign_perm('delete_contenttype', self.user, ctypes[1]) assign_perm('delete_contenttype', self.user, ctypes[2]) assign_perm('change_contenttype', groups[0], ctypes[3]) assign_perm('change_contenttype', groups[1], ctypes[3]) assign_perm('change_contenttype', groups[2], ctypes[4]) assign_perm('delete_contenttype', groups[0], ctypes[0]) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype']) self.assertEqual( set(objects.values_list('id', flat=True)), set(ctypes[i].id for i in [0, 1, 3, 4])) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype', 'contenttypes.delete_contenttype']) self.assertEqual( set(objects.values_list('id', flat=True)), set(ctypes[i].id for i in [0, 1])) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype']) self.assertEqual( set(objects.values_list('id', flat=True)), set(ctypes[i].id for i in [0, 1, 3, 4])) def test_has_global_permission_only(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] # global permission to change any group perm = 'auth.change_group' assign_perm(perm, self.user) objects = get_objects_for_user(self.user, perm) remove_perm(perm, self.user) self.assertEqual(set(objects), set(Group.objects.all())) def test_has_global_permission_and_object_based_permission(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] # global permission to change any group perm_global = 'auth.change_group' perm_obj = 'delete_group' assign_perm(perm_global, self.user) assign_perm(perm_obj, self.user, groups[0]) objects = get_objects_for_user(self.user, [perm_global, perm_obj]) remove_perm(perm_global, self.user) self.assertEqual(set(objects.values_list('name', flat=True)), set([groups[0].name])) def test_has_global_permission_and_object_based_permission_any_perm(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] # global permission to change any group perm_global = 'auth.change_group' # object based permission to change only a specific group perm_obj = 'auth.delete_group' assign_perm(perm_global, self.user) assign_perm(perm_obj, self.user, groups[0]) objects = get_objects_for_user( self.user, [perm_global, perm_obj], any_perm=True, accept_global_perms=True) remove_perm(perm_global, self.user) self.assertEqual(set(objects), set(Group.objects.all())) def test_object_based_permission_without_global_permission(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] # global permission to delete any group perm_global = 'auth.delete_group' perm_obj = 'auth.delete_group' assign_perm(perm_global, self.user) assign_perm(perm_obj, self.user, groups[0]) objects = get_objects_for_user( self.user, [perm_obj], accept_global_perms=False) remove_perm(perm_global, self.user) self.assertEqual(set(objects.values_list('name', flat=True)), set([groups[0].name])) def test_object_based_permission_with_groups_2perms(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] for group in groups: self.user.groups.add(group) # Objects to operate on ctypes = list(ContentType.objects.all().order_by('id')) assign_perm('contenttypes.change_contenttype', self.user) assign_perm('change_contenttype', self.user, ctypes[0]) assign_perm('change_contenttype', self.user, ctypes[1]) assign_perm('delete_contenttype', self.user, ctypes[1]) assign_perm('delete_contenttype', self.user, ctypes[2]) assign_perm('change_contenttype', groups[0], ctypes[3]) assign_perm('change_contenttype', groups[1], ctypes[3]) assign_perm('change_contenttype', groups[2], ctypes[4]) assign_perm('delete_contenttype', groups[0], ctypes[0]) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype', 'contenttypes.delete_contenttype'], accept_global_perms=True) self.assertEqual( set(objects.values_list('id', flat=True)), set([ctypes[0].id, ctypes[1].id, ctypes[2].id])) def test_object_based_permission_with_groups_3perms(self): group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] for group in groups: self.user.groups.add(group) # Objects to operate on ctypes = list(ContentType.objects.all().order_by('id')) assign_perm('contenttypes.change_contenttype', self.user) assign_perm('change_contenttype', self.user, ctypes[0]) assign_perm('change_contenttype', self.user, ctypes[1]) assign_perm('delete_contenttype', self.user, ctypes[1]) assign_perm('delete_contenttype', self.user, ctypes[2]) # add_contenttype does not make sense, here just for testing purposes, # to also cover one if branch in function. assign_perm('add_contenttype', self.user, ctypes[1]) assign_perm('change_contenttype', groups[0], ctypes[3]) assign_perm('change_contenttype', groups[1], ctypes[3]) assign_perm('change_contenttype', groups[2], ctypes[4]) assign_perm('delete_contenttype', groups[0], ctypes[0]) assign_perm('add_contenttype', groups[0], ctypes[0]) objects = get_objects_for_user(self.user, ['contenttypes.change_contenttype', 'contenttypes.delete_contenttype', 'contenttypes.add_contenttype'], accept_global_perms=True) self.assertEqual( set(objects.values_list('id', flat=True)), set([ctypes[0].id, ctypes[1].id])) def test_non_integer_primary_key(self): """ Verify that the function works when the objects that should be returned have non-integer primary keys. """ obj_with_char_pk = NonIntPKModel.objects.create( char_pk='testprimarykey') assign_perm('add_nonintpkmodel', self.user, obj_with_char_pk) objects = get_objects_for_user(self.user, 'testapp.add_nonintpkmodel') self.assertEqual(len(objects), 1) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects.values_list('pk', flat=True)), set([obj_with_char_pk.pk])) def test_non_integer_primary_key_with_any_perm(self): """ Verify that the function works with any_perm set to True when the objects that should be returned have non-integer primary keys. """ obj_with_char_pk = NonIntPKModel.objects.create( char_pk='testprimarykey') assign_perm('add_nonintpkmodel', self.user, obj_with_char_pk) objects = get_objects_for_user( self.user, ['testapp.add_nonintpkmodel', 'testapp.change_nonintpkmodel'], any_perm=True) self.assertEqual(len(objects), 1) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects.values_list('pk', flat=True)), set([obj_with_char_pk.pk])) def test_non_integer_primary_key_with_group_values(self): """ Verify that the function works when the objects that should be returned have non-integer primary keys, and those objects are due to the user's groups. """ obj_with_char_pk = NonIntPKModel.objects.create( char_pk='testprimarykey') assign_perm('add_nonintpkmodel', self.group, obj_with_char_pk) self.user.groups.add(self.group) objects = get_objects_for_user( self.user, ['testapp.add_nonintpkmodel', 'testapp.change_nonintpkmodel'], any_perm=True) self.assertEqual(len(objects), 1) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects.values_list('pk', flat=True)), set([obj_with_char_pk.pk])) def test_exception_different_ctypes(self): self.assertRaises(MixedContentTypeError, get_objects_for_user, self.user, ['auth.change_permission', 'auth.change_group']) def test_has_any_permissions(self): # We use groups as objects. group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] for group in groups: assign_perm('change_group', self.user, group) objects = get_objects_for_user(self.user, [], Group) self.assertEqual(len(objects), len(groups)) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects), set(groups)) def test_short_codenames_with_klass(self): assign_perm('contenttypes.change_contenttype', self.user, self.ctype) objects = get_objects_for_user(self.user, ['change_contenttype'], ContentType) self.assertEqual([obj.name for obj in objects], [self.ctype.name]) def test_has_any_group_permissions(self): # We use groups as objects. group_names = ['group1', 'group2', 'group3'] groups = [Group.objects.create(name=name) for name in group_names] for group in groups: assign_perm('change_group', self.group, group) objects = get_objects_for_group(self.group, [], Group) self.assertEqual(len(objects), len(groups)) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects), set(groups)) class GetObjectsForGroup(TestCase): """ Tests get_objects_for_group function. """ def setUp(self): self.obj1 = ContentType.objects.create( model='foo', app_label='guardian-tests') self.obj2 = ContentType.objects.create( model='bar', app_label='guardian-tests') self.obj3 = ContentType.objects.create( model='baz', app_label='guardian-tests') self.user1 = User.objects.create(username='user1') self.user2 = User.objects.create(username='user2') self.user3 = User.objects.create(username='user3') self.group1 = Group.objects.create(name='group1') self.group2 = Group.objects.create(name='group2') self.group3 = Group.objects.create(name='group3') def test_mixed_perms(self): codenames = [ get_user_permission_full_codename('change'), 'auth.change_permission', ] self.assertRaises(MixedContentTypeError, get_objects_for_group, self.group1, codenames) def test_perms_with_mixed_apps(self): codenames = [ get_user_permission_full_codename('change'), 'contenttypes.contenttypes.change_contenttype', ] self.assertRaises(MixedContentTypeError, get_objects_for_group, self.group1, codenames) def test_mixed_perms_and_klass(self): self.assertRaises(MixedContentTypeError, get_objects_for_group, self.group1, ['auth.change_group'], User) def test_no_app_label_nor_klass(self): self.assertRaises(WrongAppError, get_objects_for_group, self.group1, ['change_contenttype']) def test_empty_perms_sequence(self): self.assertEqual( set(get_objects_for_group(self.group1, [], ContentType)), set() ) def test_perms_single(self): perm = 'contenttypes.change_contenttype' assign_perm(perm, self.group1, self.obj1) self.assertEqual( set(get_objects_for_group(self.group1, perm)), set(get_objects_for_group(self.group1, [perm])) ) def test_klass_as_model(self): assign_perm('contenttypes.change_contenttype', self.group1, self.obj1) objects = get_objects_for_group(self.group1, ['contenttypes.change_contenttype'], ContentType) self.assertEqual([obj.name for obj in objects], [self.obj1.name]) def test_klass_as_manager(self): assign_perm('contenttypes.change_contenttype', self.group1, self.obj1) objects = get_objects_for_group(self.group1, ['change_contenttype'], ContentType.objects) self.assertEqual(list(objects), [self.obj1]) def test_klass_as_queryset(self): assign_perm('contenttypes.change_contenttype', self.group1, self.obj1) objects = get_objects_for_group(self.group1, ['change_contenttype'], ContentType.objects.all()) self.assertEqual(list(objects), [self.obj1]) def test_ensure_returns_queryset(self): objects = get_objects_for_group( self.group1, ['contenttypes.change_contenttype']) self.assertTrue(isinstance(objects, QuerySet)) def test_simple(self): assign_perm('change_contenttype', self.group1, self.obj1) assign_perm('change_contenttype', self.group1, self.obj2) objects = get_objects_for_group( self.group1, 'contenttypes.change_contenttype') self.assertEqual(len(objects), 2) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual( set(objects), set([self.obj1, self.obj2])) def test_simple_after_removal(self): self.test_simple() remove_perm('change_contenttype', self.group1, self.obj1) objects = get_objects_for_group( self.group1, 'contenttypes.change_contenttype') self.assertEqual(len(objects), 1) self.assertEqual(objects[0], self.obj2) def test_multiple_perms_to_check(self): assign_perm('change_contenttype', self.group1, self.obj1) assign_perm('delete_contenttype', self.group1, self.obj1) assign_perm('change_contenttype', self.group1, self.obj2) objects = get_objects_for_group(self.group1, [ 'contenttypes.change_contenttype', 'contenttypes.delete_contenttype']) self.assertEqual(len(objects), 1) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual(objects[0], self.obj1) def test_any_of_multiple_perms_to_check(self): assign_perm('change_contenttype', self.group1, self.obj1) assign_perm('delete_contenttype', self.group1, self.obj1) assign_perm('add_contenttype', self.group1, self.obj2) assign_perm('delete_contenttype', self.group1, self.obj3) objects = get_objects_for_group(self.group1, ['contenttypes.change_contenttype', 'contenttypes.delete_contenttype'], any_perm=True) self.assertTrue(isinstance(objects, QuerySet)) self.assertEqual([obj for obj in objects.order_by('app_label')], [self.obj1, self.obj3]) def test_results_for_different_groups_are_correct(self): assign_perm('change_contenttype', self.group1, self.obj1) assign_perm('delete_contenttype', self.group2, self.obj2) self.assertEqual(set(get_objects_for_group(self.group1, 'contenttypes.change_contenttype')), set([self.obj1])) self.assertEqual(set(get_objects_for_group(self.group2, 'contenttypes.change_contenttype')), set()) self.assertEqual(set(get_objects_for_group(self.group2, 'contenttypes.delete_contenttype')), set([self.obj2])) def test_has_global_permission(self): assign_perm('contenttypes.change_contenttype', self.group1) objects = get_objects_for_group( self.group1, ['contenttypes.change_contenttype']) self.assertEqual(set(objects), set(ContentType.objects.all())) def test_has_global_permission_and_object_based_permission(self): assign_perm('contenttypes.change_contenttype', self.group1) assign_perm('contenttypes.delete_contenttype', self.group1, self.obj1) objects = get_objects_for_group(self.group1, [ 'contenttypes.change_contenttype', 'contenttypes.delete_contenttype'], any_perm=False) self.assertEqual(set(objects), set([self.obj1])) def test_has_global_permission_and_object_based_permission_any_perm(self): assign_perm('contenttypes.change_contenttype', self.group1) assign_perm('contenttypes.delete_contenttype', self.group1, self.obj1) objects = get_objects_for_group(self.group1, [ 'contenttypes.change_contenttype', 'contenttypes.delete_contenttype'], any_perm=True) self.assertEqual(set(objects), set(ContentType.objects.all())) def test_has_global_permission_and_object_based_permission_3perms(self): assign_perm('contenttypes.change_contenttype', self.group1) assign_perm('contenttypes.delete_contenttype', self.group1, self.obj1) assign_perm('contenttypes.add_contenttype', self.group1, self.obj2) objects = get_objects_for_group(self.group1, [ 'contenttypes.change_contenttype', 'contenttypes.delete_contenttype', 'contenttypes.add_contenttype'], any_perm=False) self.assertEqual(set(objects), set()) def test_exception_different_ctypes(self): self.assertRaises(MixedContentTypeError, get_objects_for_group, self.group1, ['auth.change_permission', 'auth.change_group']) django-guardian-2.0.0/guardian/testapp/tests/test_tags.py000066400000000000000000000144101347767355200235510ustar00rootroot00000000000000from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import Group, AnonymousUser from django.contrib.contenttypes.models import ContentType from django.template import Template, Context, TemplateSyntaxError from django.test import TestCase from guardian.core import ObjectPermissionChecker from guardian.exceptions import NotUserNorGroup from guardian.models import UserObjectPermission, GroupObjectPermission User = get_user_model() def render(template, context): """ Returns rendered ``template`` with ``context``, which are given as string and dict respectively. """ t = Template(template) return t.render(Context(context)) class GetObjPermsTagTest(TestCase): def setUp(self): self.ctype = ContentType.objects.create( model='bar', app_label='fake-for-guardian-tests') self.group = Group.objects.create(name='jackGroup') self.user = User.objects.create(username='jack') self.user.groups.add(self.group) def test_wrong_formats(self): wrong_formats = ( '{% get_obj_perms user for contenttype as obj_perms %}', # no quotes '{% get_obj_perms user for contenttype as \'obj_perms" %}', # wrong quotes '{% get_obj_perms user for contenttype as \'obj_perms" %}', # wrong quotes '{% get_obj_perms user for contenttype as obj_perms" %}', # wrong quotes '{% get_obj_perms user for contenttype as obj_perms\' %}', # wrong quotes '{% get_obj_perms user for contenttype as %}', # no context_var '{% get_obj_perms for contenttype as "obj_perms" %}', # no user/group '{% get_obj_perms user contenttype as "obj_perms" %}', # no "for" bit '{% get_obj_perms user for contenttype "obj_perms" %}', # no "as" bit '{% get_obj_perms user for as "obj_perms" %}', # no object ) context = {'user': User.get_anonymous(), 'contenttype': self.ctype} for wrong in wrong_formats: fullwrong = '{% load guardian_tags %}' + wrong try: render(fullwrong, context) self.fail("Used wrong get_obj_perms tag format: \n\n\t%s\n\n " "but TemplateSyntaxError have not been raised" % wrong) except TemplateSyntaxError: pass def test_obj_none(self): template = ''.join(( '{% load guardian_tags %}', '{% get_obj_perms user for object as "obj_perms" %}{{ perms }}', )) context = {'user': User.get_anonymous(), 'object': None} output = render(template, context) self.assertEqual(output, '') def test_anonymous_user(self): template = ''.join(( '{% load guardian_tags %}', '{% get_obj_perms user for contenttype as "obj_perms" %}{{ perms }}', )) context = {'user': AnonymousUser(), 'contenttype': self.ctype} anon_output = render(template, context) context = {'user': User.get_anonymous(), 'contenttype': self.ctype} real_anon_user_output = render(template, context) self.assertEqual(anon_output, real_anon_user_output) def test_wrong_user_or_group(self): template = ''.join(( '{% load guardian_tags %}', '{% get_obj_perms some_obj for contenttype as "obj_perms" %}', )) context = {'some_obj': ContentType(), 'contenttype': self.ctype} # This test would raise TemplateSyntaxError instead of NotUserNorGroup # if the template option 'debug' is set to True during tests. template_options = settings.TEMPLATES[0]['OPTIONS'] tmp = template_options.get('debug', False) template_options['debug'] = False self.assertRaises(NotUserNorGroup, render, template, context) template_options['debug'] = tmp def test_superuser(self): user = User.objects.create(username='superuser', is_superuser=True) template = ''.join(( '{% load guardian_tags %}', '{% get_obj_perms user for contenttype as "obj_perms" %}', '{{ obj_perms|join:" " }}', )) context = {'user': user, 'contenttype': self.ctype} output = render(template, context) for perm in ('add_contenttype', 'change_contenttype', 'delete_contenttype'): self.assertTrue(perm in output) def test_user(self): UserObjectPermission.objects.assign_perm("change_contenttype", self.user, self.ctype) GroupObjectPermission.objects.assign_perm("delete_contenttype", self.group, self.ctype) template = ''.join(( '{% load guardian_tags %}', '{% get_obj_perms user for contenttype as "obj_perms" %}', '{{ obj_perms|join:" " }}', )) context = {'user': self.user, 'contenttype': self.ctype} output = render(template, context) self.assertEqual( set(output.split(' ')), set('change_contenttype delete_contenttype'.split(' '))) def test_group(self): GroupObjectPermission.objects.assign_perm("delete_contenttype", self.group, self.ctype) template = ''.join(( '{% load guardian_tags %}', '{% get_obj_perms group for contenttype as "obj_perms" %}', '{{ obj_perms|join:" " }}', )) context = {'group': self.group, 'contenttype': self.ctype} output = render(template, context) self.assertEqual(output, 'delete_contenttype') def test_checker(self): GroupObjectPermission.objects.assign_perm("delete_contenttype", self.group, self.ctype) checker = ObjectPermissionChecker(self.user) checker.prefetch_perms(Group.objects.all()) template = ''.join(( '{% load guardian_tags %}', '{% get_obj_perms group for contenttype as "obj_perms" checker %}', '{{ obj_perms|join:" " }}', )) context = {'group': self.group, 'contenttype': self.ctype, 'checker': checker} output = render(template, context) self.assertEqual(output, 'delete_contenttype') django-guardian-2.0.0/guardian/testapp/tests/test_utils.py000066400000000000000000000117021347767355200237540ustar00rootroot00000000000000from django.test import TestCase from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import Group, AnonymousUser from django.db import models from guardian.testapp.tests.conf import skipUnlessTestApp from guardian.testapp.tests.test_core import ObjectPermissionTestCase from guardian.testapp.models import Project from guardian.testapp.models import ProjectUserObjectPermission from guardian.testapp.models import ProjectGroupObjectPermission from guardian.models import UserObjectPermission from guardian.models import UserObjectPermissionBase from guardian.models import GroupObjectPermission from guardian.utils import get_anonymous_user from guardian.utils import get_identity from guardian.utils import get_user_obj_perms_model from guardian.utils import get_group_obj_perms_model from guardian.utils import get_obj_perms_model from guardian.exceptions import NotUserNorGroup User = get_user_model() class GetAnonymousUserTest(TestCase): def test(self): anon = get_anonymous_user() self.assertTrue(isinstance(anon, User)) class GetIdentityTest(ObjectPermissionTestCase): def test_user(self): user, group = get_identity(self.user) self.assertTrue(isinstance(user, User)) self.assertEqual(group, None) def test_anonymous_user(self): anon = AnonymousUser() user, group = get_identity(anon) self.assertTrue(isinstance(user, User)) self.assertEqual(group, None) def test_group(self): user, group = get_identity(self.group) self.assertTrue(isinstance(group, Group)) self.assertEqual(user, None) def test_not_user_nor_group(self): self.assertRaises(NotUserNorGroup, get_identity, 1) self.assertRaises(NotUserNorGroup, get_identity, "User") self.assertRaises(NotUserNorGroup, get_identity, User) def test_multiple_user_qs(self): user, group = get_identity(User.objects.all()) self.assertIsInstance(user, models.QuerySet) self.assertIsNone(group) def test_multiple_user_list(self): user, group = get_identity([self.user]) self.assertIsInstance(user, list) self.assertIsNone(group) def test_multiple_group_qs(self): user, group = get_identity(Group.objects.all()) self.assertIsInstance(group, models.QuerySet) self.assertIsNone(user) def test_multiple_group_list(self): user, group = get_identity([self.group]) self.assertIsInstance(group, list) self.assertIsNone(user) @skipUnlessTestApp class GetUserObjPermsModelTest(TestCase): def test_for_instance(self): project = Project(name='Foobar') self.assertEqual(get_user_obj_perms_model(project), ProjectUserObjectPermission) def test_for_class(self): self.assertEqual(get_user_obj_perms_model(Project), ProjectUserObjectPermission) def test_default(self): self.assertEqual(get_user_obj_perms_model(ContentType), UserObjectPermission) def test_user_model(self): # this test assumes that there were no direct obj perms model to User # model defined (i.e. while testing guardian app in some custom # project) self.assertEqual(get_user_obj_perms_model(User), UserObjectPermission) @skipUnlessTestApp class GetGroupObjPermsModelTest(TestCase): def test_for_instance(self): project = Project(name='Foobar') self.assertEqual(get_group_obj_perms_model(project), ProjectGroupObjectPermission) def test_for_class(self): self.assertEqual(get_group_obj_perms_model(Project), ProjectGroupObjectPermission) def test_default(self): self.assertEqual(get_group_obj_perms_model(ContentType), GroupObjectPermission) def test_group_model(self): # this test assumes that there were no direct obj perms model to Group # model defined (i.e. while testing guardian app in some custom # project) self.assertEqual(get_group_obj_perms_model(Group), GroupObjectPermission) class GetObjPermsModelTest(TestCase): def test_image_field(self): class SomeModel(models.Model): image = models.FileField(upload_to='images/') obj = SomeModel() perm_model = get_obj_perms_model(obj, UserObjectPermissionBase, UserObjectPermission) self.assertEqual(perm_model, UserObjectPermission) def test_file_field(self): class SomeModel2(models.Model): file = models.FileField(upload_to='images/') obj = SomeModel2() perm_model = get_obj_perms_model(obj, UserObjectPermissionBase, UserObjectPermission) self.assertEqual(perm_model, UserObjectPermission) django-guardian-2.0.0/guardian/testapp/tests/urls.py000066400000000000000000000011761347767355200225460ustar00rootroot00000000000000# handler404 and handler500 are needed for admin tests from guardian.compat import url, handler404, handler500 # pyflakes:ignore from guardian.mixins import PermissionRequiredMixin from django.contrib import admin from django.contrib.auth.views import LoginView from django.views.generic import View admin.autodiscover() class TestClassRedirectView(PermissionRequiredMixin, View): permission_required = 'testapp.change_project' urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^accounts/login/', LoginView.as_view(template_name='blank.html')), url(r'^permission_required/', TestClassRedirectView.as_view()), ] django-guardian-2.0.0/guardian/testapp/testsettings.py000066400000000000000000000034451347767355200231600ustar00rootroot00000000000000import os import random import string import environ env = environ.Env() DEBUG = False ANONYMOUS_USER_NAME = "AnonymousUser" AUTH_USER_MODEL = "testapp.CustomUser" GUARDIAN_MONKEY_PATCH = False INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.messages', 'guardian', 'guardian.testapp', ) AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'guardian.backends.ObjectPermissionBackend', ) MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ) TEST_RUNNER = 'django.test.runner.DiscoverRunner' ROOT_URLCONF = 'guardian.testapp.tests.urls' SITE_ID = 1 SECRET_KEY = ''.join([random.choice(string.ascii_letters) for x in range(40)]) # Database specific DATABASES = {'default': env.db(default="sqlite:///")} TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': ( os.path.join(os.path.dirname(__file__), 'tests', 'templates'), ), 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', ], }, }, ] django-guardian-2.0.0/guardian/utils.py000066400000000000000000000161231347767355200200750ustar00rootroot00000000000000""" django-guardian helper functions. Functions defined within this module should be considered as django-guardian's internal functionality. They are **not** guaranteed to be stable - which means they actual input parameters/output type may change in future releases. """ import logging import os from itertools import chain from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model from django.contrib.auth.models import AnonymousUser, Group from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.db.models import Model, QuerySet from django.http import HttpResponseForbidden, HttpResponseNotFound from django.shortcuts import render from guardian.conf import settings as guardian_settings from guardian.ctypes import get_content_type from guardian.exceptions import NotUserNorGroup logger = logging.getLogger(__name__) abspath = lambda *p: os.path.abspath(os.path.join(*p)) def get_anonymous_user(): """ Returns ``User`` instance (not ``AnonymousUser``) depending on ``ANONYMOUS_USER_NAME`` configuration. """ User = get_user_model() lookup = {User.USERNAME_FIELD: guardian_settings.ANONYMOUS_USER_NAME} return User.objects.get(**lookup) def get_identity(identity): """ Returns (user_obj, None) or (None, group_obj) tuple depending on what is given. Also accepts AnonymousUser instance but would return ``User`` instead - it is convenient and needed for authorization backend to support anonymous users. :param identity: either ``User`` or ``Group`` instance :raises ``NotUserNorGroup``: if cannot return proper identity instance **Examples**:: >>> from django.contrib.auth.models import User >>> user = User.objects.create(username='joe') >>> get_identity(user) (, None) >>> group = Group.objects.create(name='users') >>> get_identity(group) (None, ) >>> anon = AnonymousUser() >>> get_identity(anon) (, None) >>> get_identity("not instance") ... NotUserNorGroup: User/AnonymousUser or Group instance is required (got ) """ if isinstance(identity, AnonymousUser): identity = get_anonymous_user() # get identity from queryset model type if isinstance(identity, QuerySet): identity_model_type = identity.model if identity_model_type == get_user_model(): return identity, None elif identity_model_type == Group: return None, identity # get identity from first element in list if isinstance(identity, list) and isinstance(identity[0], get_user_model()): return identity, None if isinstance(identity, list) and isinstance(identity[0], Group): return None, identity if isinstance(identity, get_user_model()): return identity, None if isinstance(identity, Group): return None, identity raise NotUserNorGroup("User/AnonymousUser or Group instance is required " "(got %s)" % identity) def get_40x_or_None(request, perms, obj=None, login_url=None, redirect_field_name=None, return_403=False, return_404=False, accept_global_perms=False): login_url = login_url or settings.LOGIN_URL redirect_field_name = redirect_field_name or REDIRECT_FIELD_NAME # Handles both original and with object provided permission check # as ``obj`` defaults to None has_permissions = False # global perms check first (if accept_global_perms) if accept_global_perms: has_permissions = all(request.user.has_perm(perm) for perm in perms) # if still no permission granted, try obj perms if not has_permissions: has_permissions = all(request.user.has_perm(perm, obj) for perm in perms) if not has_permissions: if return_403: if guardian_settings.RENDER_403: response = render(request, guardian_settings.TEMPLATE_403) response.status_code = 403 return response elif guardian_settings.RAISE_403: raise PermissionDenied return HttpResponseForbidden() if return_404: if guardian_settings.RENDER_404: response = render(request, guardian_settings.TEMPLATE_404) response.status_code = 404 return response elif guardian_settings.RAISE_404: raise ObjectDoesNotExist return HttpResponseNotFound() else: from django.contrib.auth.views import redirect_to_login return redirect_to_login(request.get_full_path(), login_url, redirect_field_name) def clean_orphan_obj_perms(): """ Seeks and removes all object permissions entries pointing at non-existing targets. Returns number of removed objects. """ from guardian.models import UserObjectPermission from guardian.models import GroupObjectPermission deleted = 0 # TODO: optimise for perm in chain(UserObjectPermission.objects.all().iterator(), GroupObjectPermission.objects.all().iterator()): if perm.content_object is None: logger.debug("Removing %s (pk=%d)" % (perm, perm.pk)) perm.delete() deleted += 1 logger.info("Total removed orphan object permissions instances: %d" % deleted) return deleted # TODO: should raise error when multiple UserObjectPermission direct relations # are defined def get_obj_perms_model(obj, base_cls, generic_cls): if isinstance(obj, Model): obj = obj.__class__ ctype = get_content_type(obj) fields = (f for f in obj._meta.get_fields() if (f.one_to_many or f.one_to_one) and f.auto_created) for attr in fields: model = getattr(attr, 'related_model', None) if (model and issubclass(model, base_cls) and model is not generic_cls and getattr(model, 'enabled', True)): # if model is generic one it would be returned anyway if not model.objects.is_generic(): # make sure that content_object's content_type is same as # the one of given obj fk = model._meta.get_field('content_object') if ctype == get_content_type(fk.remote_field.model): return model return generic_cls def get_user_obj_perms_model(obj): """ Returns model class that connects given ``obj`` and User class. """ from guardian.models import UserObjectPermissionBase from guardian.models import UserObjectPermission return get_obj_perms_model(obj, UserObjectPermissionBase, UserObjectPermission) def get_group_obj_perms_model(obj): """ Returns model class that connects given ``obj`` and Group class. """ from guardian.models import GroupObjectPermissionBase from guardian.models import GroupObjectPermission return get_obj_perms_model(obj, GroupObjectPermissionBase, GroupObjectPermission) django-guardian-2.0.0/manage.py000077500000000000000000000004411347767355200163720ustar00rootroot00000000000000#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "guardian.testapp.testsettings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-guardian-2.0.0/pytest.ini000066400000000000000000000002211347767355200166120ustar00rootroot00000000000000[pytest] DJANGO_SETTINGS_MODULE=guardian.testapp.testsettings filterwarnings = once::DeprecationWarning once::PendingDeprecationWarning django-guardian-2.0.0/requirements.txt000066400000000000000000000000541347767355200200510ustar00rootroot00000000000000Django>=2.1 django-environ bumpversion mock django-guardian-2.0.0/run_test_and_report.sh000077500000000000000000000004151347767355200212050ustar00rootroot00000000000000#!/bin/bash echo "Running test suite with coverage report at the end" echo -e "( would require coverage python package to be installed )\n" OMIT="guardian/testsettings.py,guardian/compat.py" coverage run setup.py test coverage report --omit "$OMIT" -m guardian/*.py django-guardian-2.0.0/setup.cfg000066400000000000000000000006771347767355200164210ustar00rootroot00000000000000[bumpversion] current_version = 2.0.0 [build_sphinx] source-dir = docs/ build-dir = docs/build all_files = 1 [upload_sphinx] upload-dir = docs/build/html [bdist_rpm] requires = Django >= 2.1 [aliases] test = pytest [bumpversion:file:setup.py] search = version = '{current_version}' replace = version = '{new_version}' [bumpversion:file:guardian/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' django-guardian-2.0.0/setup.py000066400000000000000000000036331347767355200163050ustar00rootroot00000000000000import os from setuptools import setup from extras import RunFlakesCommand version = '2.0.0' readme_file = os.path.join(os.path.dirname(__file__), 'README.rst') with open(readme_file, 'r') as f: long_description = f.read() setup( name='django-guardian', version=version, python_requires='>=3.5', setup_requires=['pytest-runner', ], url='http://github.com/django-guardian/django-guardian', author='Lukasz Balcerzak', author_email='lukaszbalcerzak@gmail.com', download_url='https://github.com/django-guardian/django-guardian/tags', description="Implementation of per object permissions for Django.", long_description=long_description, zip_safe=False, packages=[ 'guardian', 'guardian.conf', 'guardian.management', 'guardian.migrations', 'guardian.templatetags', 'guardian.testapp', 'guardian.management.commands', 'guardian.testapp.migrations', 'guardian.testapp.tests' ], include_package_data=True, license='BSD', install_requires=[], tests_require=['mock', 'django-environ', 'pytest', 'pytest-django'], classifiers=['Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Framework :: Django :: 2.1', 'Framework :: Django :: 2.2', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Security', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3 :: Only', ], test_suite='tests.main', cmdclass={'flakes': RunFlakesCommand}, ) django-guardian-2.0.0/tox.ini000066400000000000000000000017111347767355200161010ustar00rootroot00000000000000[tox] downloadcache = {toxworkdir}/cache/ envlist = # sort by django version, next by python version {core,example,docs}-py{35,36,py37}-django21, {core,example,docs}-py{35,36,py37}-django22, [testenv] passenv = DATABASE_URL basepython = py35: python3.5 py36: python3.6 py37: python3.7 changedir = example: example_project docs: docs commands = django21: python {toxinidir}/manage.py makemigrations --check --dry-run django22: python {toxinidir}/manage.py makemigrations --check --dry-run core: py.test --cov=guardian docs: sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html example: python manage.py test deps = django-environ core: mock>=0.7.2 core: setuptools>=17.1 core: pyflakes core: pytest core: pytest-django core: pytest-cov example: . docs: sphinx docs: sphinx_rtd_theme docs: setuptools_scm django21: django>=2.1,<2.2 django22: django>=2.2,<2.3 django-guardian-2.0.0/utils.py000066400000000000000000000016451347767355200163060ustar00rootroot00000000000000 def show_settings(settings, action): import guardian from django.utils.termcolors import colorize guardian_path = guardian.__path__[0] msg = "django-guardian module's path: %r" % guardian_path print(colorize(msg, fg='magenta')) db_conf = settings.DATABASES['default'] output = [] msg = "Starting %s for db backend: %s" % (action, db_conf['ENGINE']) embracer = '=' * len(msg) output.append(msg) for key in sorted(db_conf.keys()): if key == 'PASSWORD': value = '****************' else: value = db_conf[key] line = ' %s: "%s"' % (key, value) output.append(line) embracer = colorize('=' * len(max(output, key=lambda s: len(s))), fg='green', opts=['bold']) output = [colorize(line, fg='blue') for line in output] output.insert(0, embracer) output.append(embracer) print('\n'.join(output))