Hello community,

here is the log from the commit of package python-django-guardian for 
openSUSE:Factory checked in at 2012-03-16 13:22:43
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-guardian (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-guardian.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-guardian", Maintainer is ""

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-django-guardian/python-django-guardian.changes
    2012-02-16 10:09:10.000000000 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-django-guardian.new/python-django-guardian.changes
       2012-03-16 13:22:45.000000000 +0100
@@ -1,0 +2,13 @@
+Mon Mar 12 14:55:35 UTC 2012 - alexan...@exatati.com.br
+
+- Update to 1.0.4:
+  * Added accept_global_perms flag for decorators
+  * Added missing *fieldset* closing tag at template
+  * Fixed misleading comments at docstrings
+  * Fixed broken grappelli tests
+  * Fixed issue disallowing creation of RPM package
+  * Started using tox for tests
+  * Expanded orphaned object permissions documentation
+  * Expanded configuration settings documentation
+
+-------------------------------------------------------------------

Old:
----
  django-guardian-1.0.3.tar.bz2

New:
----
  django-guardian-1.0.4.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-guardian.spec ++++++
--- /var/tmp/diff_new_pack.HnD8Pk/_old  2012-03-16 13:22:46.000000000 +0100
+++ /var/tmp/diff_new_pack.HnD8Pk/_new  2012-03-16 13:22:46.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-django-guardian
 #
-# Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{!?python_sitearch: %global python_sitearch %(%{__python} -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
 
 Name:           python-django-guardian
-Version:        1.0.3
+Version:        1.0.4
 Release:        0
 Url:            http://github.com/lukaszb/django-guardian
 Summary:        Implementation of per object permissions for Django 1.2

++++++ django-guardian-1.0.3.tar.bz2 -> django-guardian-1.0.4.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/CHANGES 
new/django-guardian-1.0.4/CHANGES
--- old/django-guardian-1.0.3/CHANGES   2011-07-25 11:53:27.000000000 +0200
+++ new/django-guardian-1.0.4/CHANGES   2012-03-12 13:45:12.000000000 +0100
@@ -1,5 +1,17 @@
+Release 1.0.4 (Mar 12, 2012)
+============================
+
+* Added ``accept_global_perms`` flag for decorators
+* Added missing *fieldset* closing tag at template
+* Fixed misleading comments at docstrings
+* Fixed broken grappelli tests
+* Fixed issue disallowing creation of RPM package
+* Started using tox for tests
+* Expanded orphaned object permissions documentation
+* Expanded configuration settings documentation
+
 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``
@@ -8,7 +20,7 @@
 * 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
@@ -17,14 +29,14 @@
 
 
 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!
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/PKG-INFO 
new/django-guardian-1.0.4/PKG-INFO
--- old/django-guardian-1.0.3/PKG-INFO  2011-07-25 11:57:51.000000000 +0200
+++ new/django-guardian-1.0.4/PKG-INFO  2012-03-12 13:45:45.000000000 +0100
@@ -1,9 +1,7 @@
 Metadata-Version: 1.0
 Name: django-guardian
-Version: 1.0.3
-Summary: 
-Implementation of per object permissions for Django 1.2.
-
+Version: 1.0.4
+Summary: Implementation of per object permissions for Django 1.2 or later.
 Home-page: http://github.com/lukaszb/django-guardian
 Author: Lukasz Balcerzak
 Author-email: lukaszbalcer...@gmail.com
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/django_guardian.egg-info/PKG-INFO 
new/django-guardian-1.0.4/django_guardian.egg-info/PKG-INFO
--- old/django-guardian-1.0.3/django_guardian.egg-info/PKG-INFO 2011-07-25 
11:57:50.000000000 +0200
+++ new/django-guardian-1.0.4/django_guardian.egg-info/PKG-INFO 2012-03-12 
13:45:43.000000000 +0100
@@ -1,9 +1,7 @@
 Metadata-Version: 1.0
 Name: django-guardian
-Version: 1.0.3
-Summary: 
-Implementation of per object permissions for Django 1.2.
-
+Version: 1.0.4
+Summary: Implementation of per object permissions for Django 1.2 or later.
 Home-page: http://github.com/lukaszb/django-guardian
 Author: Lukasz Balcerzak
 Author-email: lukaszbalcer...@gmail.com
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/django_guardian.egg-info/SOURCES.txt 
new/django-guardian-1.0.4/django_guardian.egg-info/SOURCES.txt
--- old/django-guardian-1.0.3/django_guardian.egg-info/SOURCES.txt      
2011-07-25 11:57:50.000000000 +0200
+++ new/django-guardian-1.0.4/django_guardian.egg-info/SOURCES.txt      
2012-03-12 13:45:45.000000000 +0100
@@ -37,6 +37,7 @@
 docs/develop/changes.rst
 docs/develop/example_project.rst
 docs/develop/index.rst
+docs/develop/supported-versions.rst
 docs/develop/testing.rst
 docs/theme/ADCtheme/LICENSE
 docs/theme/ADCtheme/layout.html
@@ -90,6 +91,7 @@
 guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage.html
 guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_group.html
 guardian/templates/admin/guardian/contrib/grappelli/obj_perms_manage_user.html
+guardian/templates/admin/guardian/model/action_add_obj_perms.html
 guardian/templates/admin/guardian/model/change_form.html
 guardian/templates/admin/guardian/model/field.html
 guardian/templates/admin/guardian/model/obj_perms_manage.html
@@ -99,6 +101,7 @@
 guardian/templatetags/guardian_tags.py
 guardian/tests/__init__.py
 guardian/tests/admin_test.py
+guardian/tests/conf_test.py
 guardian/tests/core_test.py
 guardian/tests/custompkmodel_test.py
 guardian/tests/decorators_test.py
@@ -110,4 +113,5 @@
 guardian/tests/urls.py
 guardian/tests/utils_test.py
 guardian/tests/templates/404.html
-guardian/tests/templates/500.html
\ No newline at end of file
+guardian/tests/templates/500.html
+guardian/tests/templates/dummy403.html
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/docs/api/guardian.decorators.rst 
new/django-guardian-1.0.4/docs/api/guardian.decorators.rst
--- old/django-guardian-1.0.3/docs/api/guardian.decorators.rst  2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/docs/api/guardian.decorators.rst  2011-11-16 
15:10:19.000000000 +0100
@@ -6,12 +6,16 @@
 .. automodule:: guardian.decorators
 
 
+.. _api-decorators-permission_required:
+
 permission_required
 -------------------
 
 .. autofunction:: guardian.decorators.permission_required
 
 
+.. _api-decorators-permission_required_or_403:
+
 permission_required_or_403
 --------------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/docs/configuration.rst 
new/django-guardian-1.0.4/docs/configuration.rst
--- old/django-guardian-1.0.3/docs/configuration.rst    2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/docs/configuration.rst    2011-11-19 
23:18:18.000000000 +0100
@@ -32,3 +32,59 @@
 We can change id to whatever we like. Project should be now ready to use object
 permissions.
  
+
+Optional settings
+=================
+
+In addition to requried ``ANONYMOUS_USER_ID`` setting, 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``.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/docs/develop/index.rst 
new/django-guardian-1.0.4/docs/develop/index.rst
--- old/django-guardian-1.0.3/docs/develop/index.rst    2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/docs/develop/index.rst    2011-11-19 
22:28:06.000000000 +0100
@@ -7,5 +7,6 @@
     
     example_project
     testing
+    supported-versions
     changes
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/docs/develop/supported-versions.rst 
new/django-guardian-1.0.4/docs/develop/supported-versions.rst
--- old/django-guardian-1.0.3/docs/develop/supported-versions.rst       
1970-01-01 01:00:00.000000000 +0100
+++ new/django-guardian-1.0.4/docs/develop/supported-versions.rst       
2011-11-19 23:27:12.000000000 +0100
@@ -0,0 +1,19 @@
+.. _supported-versions:
+
+Supported versions
+==================
+
+``django-guardian`` supports Python 2.6/2.7 and Django 1.2+. Also, we support
+``django-grappelli`` 2.3.5.
+
+Rules
+-----
+
+1. We would support both Python 2.7 and Python 2.6 (until Django drops support
+   for 2.6, if ever).
+2. We would support **two latest Django stable versions**. In example: once
+   Django 1.4 would become final, we are dropping support for Django 1.2 as
+   two last stable versions would be 1.3 and 1.4.
+3. Support for ``django-grappelli`` is somewhat experimental. Nevertheless,
+   our intention is **to support django-grappelli last stable version**.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/docs/develop/testing.rst 
new/django-guardian-1.0.4/docs/develop/testing.rst
--- old/django-guardian-1.0.3/docs/develop/testing.rst  2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/docs/develop/testing.rst  2011-11-19 
22:29:42.000000000 +0100
@@ -17,6 +17,10 @@
 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
+(lukaszbalcer...@gmail.com).
 
 Running tests
 -------------
@@ -71,7 +75,26 @@
     -------------------------------------------------------------------
     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.
+
+
 .. _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
Files old/django-guardian-1.0.3/docs/exts.pyc and 
new/django-guardian-1.0.4/docs/exts.pyc differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/docs/theme/ADCtheme/static/adctheme.css 
new/django-guardian-1.0.4/docs/theme/ADCtheme/static/adctheme.css
--- old/django-guardian-1.0.3/docs/theme/ADCtheme/static/adctheme.css   
2011-07-25 11:00:47.000000000 +0200
+++ new/django-guardian-1.0.4/docs/theme/ADCtheme/static/adctheme.css   
2011-11-16 15:09:08.000000000 +0100
@@ -391,9 +391,7 @@
 
 table.docutils {
     border-collapse: collapse;
-    border-top: 1px solid #919699;
-    border-left: 1px solid #919699;
-    border-right: 1px solid #919699;
+    border: 1px solid #919699;
     font-size:12px;
     padding:8px;
     text-align:left;
@@ -754,4 +752,4 @@
 }
 li p {
 margin-top:8px;
-}
\ No newline at end of file
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/docs/userguide/caveats.rst 
new/django-guardian-1.0.4/docs/userguide/caveats.rst
--- old/django-guardian-1.0.3/docs/userguide/caveats.rst        2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/docs/userguide/caveats.rst        2011-10-10 
10:34:42.000000000 +0200
@@ -20,11 +20,11 @@
 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. 
+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. 
+- or *ON DELETE* rules - which restricts removal of related objects.
 
 .. important::
 
@@ -34,10 +34,46 @@
 
 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.
+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.auth.models import User
+    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 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/
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/__init__.py 
new/django-guardian-1.0.4/guardian/__init__.py
--- old/django-guardian-1.0.3/guardian/__init__.py      2011-07-25 
11:52:07.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/__init__.py      2012-03-12 
13:38:54.000000000 +0100
@@ -1,7 +1,7 @@
 """
-Implementation of per object permissions for Django 1.2.
+Implementation of per object permissions for Django 1.2 or later.
 """
-VERSION = (1, 0, 3)
+VERSION = (1, 0, 4)
 
 __version__ = '.'.join((str(each) for each in VERSION[:4]))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/admin.py 
new/django-guardian-1.0.4/guardian/admin.py
--- old/django-guardian-1.0.3/guardian/admin.py 2011-07-25 10:11:25.000000000 
+0200
+++ new/django-guardian-1.0.4/guardian/admin.py 2011-12-02 16:52:39.000000000 
+0100
@@ -151,6 +151,7 @@
         related content.
         """
         context = {
+            'adminform': {'model_admin': self},
             'object': obj,
             'app_label': self.model._meta.app_label,
             'opts': self.model._meta,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/backends.py 
new/django-guardian-1.0.4/guardian/backends.py
--- old/django-guardian-1.0.3/guardian/backends.py      2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/backends.py      2012-01-27 
17:16:14.000000000 +0100
@@ -46,7 +46,7 @@
             user_obj = User.objects.get(pk=settings.ANONYMOUS_USER_ID)
 
         # Do not check any further if user is not active
-        if user_obj.is_active is not True:
+        if not user_obj.is_active:
             return False
 
         if len(perm.split('.')) > 1:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/conf/settings.py 
new/django-guardian-1.0.4/guardian/conf/settings.py
--- old/django-guardian-1.0.3/guardian/conf/settings.py 2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/conf/settings.py 2011-10-02 
00:14:36.000000000 +0200
@@ -7,3 +7,14 @@
         "ObjectPermissionBackend authorization backend you have to configure "
         "ANONYMOUS_USER_ID at your settings module")
 
+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)
+
+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()
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/decorators.py 
new/django-guardian-1.0.4/guardian/decorators.py
--- old/django-guardian-1.0.3/guardian/decorators.py    2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/decorators.py    2012-01-27 
17:16:14.000000000 +0100
@@ -1,16 +1,18 @@
-
 from django.conf import settings
 from django.contrib.auth import REDIRECT_FIELD_NAME
+from django.core.exceptions import PermissionDenied
 from django.http import HttpResponseForbidden, HttpResponseRedirect
 from django.utils.functional import wraps
 from django.utils.http import urlquote
 from django.db.models import Model, get_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.shortcuts import get_object_or_404, render_to_response
+from django.template import RequestContext, TemplateDoesNotExist
+from guardian.conf import settings as guardian_settings
 from guardian.exceptions import GuardianError
 
+
 def permission_required(perm, lookup_variables=None, **kwargs):
     """
     Decorator for views that checks whether a user has a particular permission
@@ -26,7 +28,14 @@
       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). Defaults to ``False``.
+      ``django.http.HttpResponseForbidden`` instance or rendered template -
+      see :setting:`GUARDIAN_RENDER_403`). 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::
 
@@ -50,6 +59,7 @@
     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)
+    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
@@ -92,8 +102,21 @@
 
             # Handles both original and with object provided permission check
             # as ``obj`` defaults to None
-            if not request.user.has_perm(perm, obj):
+            has_perm = accept_global_perms and request.user.has_perm(perm)
+            if not has_perm and not request.user.has_perm(perm, obj):
                 if return_403:
+                    if guardian_settings.RENDER_403:
+                        try:
+                            response = render_to_response(
+                                guardian_settings.TEMPLATE_403, {},
+                                RequestContext(request))
+                            response.status_code = 403
+                            return response
+                        except TemplateDoesNotExist, e:
+                            if settings.DEBUG:
+                                raise e
+                    elif guardian_settings.RAISE_403:
+                        raise PermissionDenied
                     return HttpResponseForbidden()
                 else:
                     path = urlquote(request.get_full_path())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/fixtures/tests.json 
new/django-guardian-1.0.4/guardian/fixtures/tests.json
--- old/django-guardian-1.0.3/guardian/fixtures/tests.json      2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/fixtures/tests.json      2012-03-12 
13:15:11.000000000 +0100
@@ -1,5 +1,21 @@
 [
     {
+        "pk": 1, 
+        "model": "auth.group", 
+        "fields": {
+            "name": "admins", 
+            "permissions": []
+        }
+    },
+    {
+        "pk": 2,
+        "model": "auth.group", 
+        "fields": {
+            "name": "jackGroup", 
+            "permissions": []
+        }
+    },
+    {
         "pk": -1, 
         "model": "auth.user", 
         "fields": {
@@ -34,21 +50,5 @@
             "email": "", 
             "date_joined": "2010-05-28 04:02:34"
         }
-    }, 
-    {
-        "pk": 1, 
-        "model": "auth.group", 
-        "fields": {
-            "name": "admins", 
-            "permissions": []
-        }
-    },
-    {
-        "pk": 2,
-        "model": "auth.group", 
-        "fields": {
-            "name": "jackGroup", 
-            "permissions": []
-        }
     }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/shortcuts.py 
new/django-guardian-1.0.4/guardian/shortcuts.py
--- old/django-guardian-1.0.3/guardian/shortcuts.py     2011-07-25 
11:41:17.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/shortcuts.py     2012-03-12 
13:15:11.000000000 +0100
@@ -246,7 +246,7 @@
                 groups[group] = get_perms(group, obj)
         return groups
 
-def get_objects_for_user(user, perms, klass=None, use_groups=True):
+def get_objects_for_user(user, perms, klass=None, use_groups=True, 
any_perm=False):
     """
     Returns queryset of objects for which a given ``user`` has *all*
     permissions present at ``perms``.
@@ -262,6 +262,7 @@
       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
 
     :raises MixedContentTypeError: when computed content type for ``perms``
       and/or ``klass`` clashes.
@@ -282,8 +283,10 @@
         
     The permission string can also be an iterable. Continuing with the 
previous example:
       
-        >>> get_objects_for_user(joe, ['auth.change_group', 
'auth.delete_group'], 'auth.delete_group'])
+        >>> 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)
+        [<Group some group>]
         >>> assign('auth.delete_group', joe, group)
         >>> get_objects_for_user(joe, ['auth.change_group', 
'auth.delete_group'])
         [<Group some group>]        
@@ -357,7 +360,7 @@
     pk_list = []
     for pk, group in groupby(data, keyfunc):
         obj_codenames = set((e[1] for e in group))
-        if codenames.issubset(obj_codenames):
+        if any_perm or codenames.issubset(obj_codenames):
             pk_list.append(pk)
 
     objects = queryset.filter(pk__in=pk_list)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/guardian/templates/admin/guardian/model/action_add_obj_perms.html
 
new/django-guardian-1.0.4/guardian/templates/admin/guardian/model/action_add_obj_perms.html
--- 
old/django-guardian-1.0.3/guardian/templates/admin/guardian/model/action_add_obj_perms.html
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/django-guardian-1.0.4/guardian/templates/admin/guardian/model/action_add_obj_perms.html
 2011-11-20 00:49:10.000000000 +0100
@@ -0,0 +1,17 @@
+{% extends "admin/change_form.html" %}
+
+{% block extrahead %}{{ block.super }}
+{{ form.media }}
+{% endblock %}
+
+{% block content %}
+
+<div id="content-main">
+
+<form action="." method="post">
+    {% csrf_token %}
+    {{ form }}
+</form>
+</div>
+
+{% endblock %}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/guardian/templates/admin/guardian/model/obj_perms_manage.html
 
new/django-guardian-1.0.4/guardian/templates/admin/guardian/model/obj_perms_manage.html
--- 
old/django-guardian-1.0.3/guardian/templates/admin/guardian/model/obj_perms_manage.html
     2011-07-25 10:11:25.000000000 +0200
+++ 
new/django-guardian-1.0.4/guardian/templates/admin/guardian/model/obj_perms_manage.html
     2012-03-12 13:17:08.000000000 +0100
@@ -125,6 +125,7 @@
         <div>
             <input name="submit_manage_group" type="submit" value="{% trans 
"Manage group" %}"/>
         </div>
+    </fieldset>
 </form>
 
 </div>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/guardian/templatetags/guardian_tags.py 
new/django-guardian-1.0.4/guardian/templatetags/guardian_tags.py
--- old/django-guardian-1.0.3/guardian/templatetags/guardian_tags.py    
2011-07-25 10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/templatetags/guardian_tags.py    
2011-10-02 00:14:36.000000000 +0200
@@ -60,6 +60,10 @@
             <a href="/pages/delete?target={{ flatpage.url }}">Remove page</a>
         {% endif %}
 
+    .. note::
+       Please remember that superusers would always get full list of 
permissions
+       for a given object.
+
     """
     bits = token.split_contents()
     format = '{% get_obj_perms user/group for obj as "context_var" %}'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/tests/__init__.py 
new/django-guardian-1.0.4/guardian/tests/__init__.py
--- old/django-guardian-1.0.3/guardian/tests/__init__.py        2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/tests/__init__.py        2011-10-10 
10:39:53.000000000 +0200
@@ -1,4 +1,5 @@
 from admin_test import *
+from conf_test import *
 from core_test import *
 from custompkmodel_test import *
 from decorators_test import *
@@ -8,4 +9,3 @@
 from utils_test import *
 from shortcuts_test import *
 from tags_test import *
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/tests/admin_test.py 
new/django-guardian-1.0.4/guardian/tests/admin_test.py
--- old/django-guardian-1.0.3/guardian/tests/admin_test.py      2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/tests/admin_test.py      2012-01-27 
17:16:14.000000000 +0100
@@ -347,12 +347,13 @@
         request = HttpRequest()
         request.user = joe
         qs = gma.queryset(request)
-        self.assertItemsEqual([e.pk for e in qs], [joe_entry.pk, 
jane_entry.pk])
+        self.assertEqual(sorted([e.pk for e in qs]),
+            sorted([joe_entry.pk, jane_entry.pk]))
 
 
 class GrappelliGuardedModelAdminTests(TestCase):
 
-    org_settings = copy.copy(settings)
+    org_installed_apps = copy.copy(settings.INSTALLED_APPS)
 
     def _get_gma(self, attrs=None, name=None, model=None):
         """
@@ -369,7 +370,7 @@
         settings.INSTALLED_APPS = ['grappelli'] + list(settings.INSTALLED_APPS)
 
     def tearDown(self):
-        globals()['settings'] = copy.copy(self.org_settings)
+        settings.INSTALLED_APPS = self.org_installed_apps
 
     def test_get_obj_perms_manage_template(self):
         gma = self._get_gma()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/tests/conf_test.py 
new/django-guardian-1.0.4/guardian/tests/conf_test.py
--- old/django-guardian-1.0.3/guardian/tests/conf_test.py       1970-01-01 
01:00:00.000000000 +0100
+++ new/django-guardian-1.0.4/guardian/tests/conf_test.py       2011-10-02 
00:14:36.000000000 +0200
@@ -0,0 +1,15 @@
+import mock
+from django.core.exceptions import ImproperlyConfigured
+from django.test import TestCase
+from guardian.conf import settings as guardian_settings
+
+
+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)
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/guardian/tests/decorators_test.py 
new/django-guardian-1.0.4/guardian/tests/decorators_test.py
--- old/django-guardian-1.0.3/guardian/tests/decorators_test.py 2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/tests/decorators_test.py 2012-01-27 
17:16:14.000000000 +0100
@@ -1,11 +1,14 @@
+import mock
 from django.test import TestCase
+from django.conf import settings
 from django.contrib.auth.models import User, Group, AnonymousUser
+from django.core.exceptions import PermissionDenied
 from django.http import HttpRequest
 from django.http import HttpResponse
 from django.http import HttpResponseForbidden
 from django.http import HttpResponseRedirect
 from django.shortcuts import get_object_or_404
-
+from django.template import TemplateDoesNotExist
 from guardian.decorators import permission_required, permission_required_or_403
 from guardian.exceptions import GuardianError
 from guardian.shortcuts import assign
@@ -17,8 +20,8 @@
 
     def setUp(self):
         self.anon = AnonymousUser()
-        self.user = User.objects.get(username='jack')
-        self.group = Group.objects.get(name='jackGroup')
+        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:
@@ -39,6 +42,68 @@
             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, '')
+            self.assertTrue(isinstance(response, HttpResponseForbidden))
+
+    @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, 'foobar403\n')
+
+    @mock.patch('guardian.conf.settings.RENDER_403', True)
+    def test_403_response_is_empty_if_template_cannot_be_found(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'):
+            response = dummy_view(request)
+            self.assertEqual(response.status_code, 403)
+            self.assertEqual(response.content, '')
+
+    @mock.patch('guardian.conf.settings.RENDER_403', True)
+    def test_403_response_raises_error_if_debug_is_turned_on(self):
+        org_DEBUG = settings.DEBUG
+        settings.DEBUG = True
+        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)
+        settings.DEBUG = org_DEBUG
+
+    @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)
+
     def test_anonymous_user_wrong_app(self):
 
         request = self._get_request(self.anon)
@@ -46,7 +111,7 @@
         @permission_required_or_403('not_installed_app.change_user')
         def dummy_view(request):
             return HttpResponse('dummy_view')
-        self.assertTrue(isinstance(dummy_view(request), HttpResponseForbidden))
+        self.assertEqual(dummy_view(request).status_code, 403)
 
     def test_anonymous_user_wrong_codename(self):
 
@@ -55,7 +120,7 @@
         @permission_required_or_403('auth.wrong_codename')
         def dummy_view(request):
             return HttpResponse('dummy_view')
-        self.assertTrue(isinstance(dummy_view(request), HttpResponseForbidden))
+        self.assertEqual(dummy_view(request).status_code, 403)
 
     def test_anonymous_user(self):
 
@@ -64,7 +129,7 @@
         @permission_required_or_403('auth.change_user')
         def dummy_view(request):
             return HttpResponse('dummy_view')
-        self.assertTrue(isinstance(dummy_view(request), HttpResponseForbidden))
+        self.assertEqual(dummy_view(request).status_code, 403)
 
     def test_wrong_lookup_variables_number(self):
 
@@ -104,6 +169,92 @@
             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 = 'auth.change_user'
+        joe, created = User.objects.get_or_create(username='joe')
+        assign(perm, self.user, obj=joe)
+
+        request = self._get_request(self.user)
+
+        @permission_required_or_403(perm, (
+            'auth.User', '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, 'dummy_view')
+
+    def test_user_has_obj_access_even_if_we_also_check_for_global(self):
+
+        perm = 'auth.change_user'
+        joe, created = User.objects.get_or_create(username='joe')
+        assign(perm, self.user, obj=joe)
+
+        request = self._get_request(self.user)
+
+        @permission_required_or_403(perm, (
+            'auth.User', '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, 'dummy_view')
+
+    def test_user_has_no_obj_perm_access(self):
+
+        perm = 'auth.change_user'
+        joe, created = User.objects.get_or_create(username='joe')
+
+        request = self._get_request(self.user)
+
+        @permission_required_or_403(perm, (
+            'auth.User', '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 = 'auth.change_user'
+        joe, created = User.objects.get_or_create(username='joe')
+        assign(perm, self.user)
+
+        request = self._get_request(self.user)
+
+        @permission_required_or_403(perm, (
+            'auth.User', '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 = 'auth.change_user'
+        joe, created = User.objects.get_or_create(username='joe')
+        assign(perm, self.user)
+
+        request = self._get_request(self.user)
+
+        @permission_required_or_403(perm, (
+            'auth.User', '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, 'dummy_view')
+
     def test_model_lookup(self):
 
         request = self._get_request(self.user)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/guardian/tests/shortcuts_test.py 
new/django-guardian-1.0.4/guardian/tests/shortcuts_test.py
--- old/django-guardian-1.0.3/guardian/tests/shortcuts_test.py  2011-07-25 
11:41:17.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/tests/shortcuts_test.py  2012-03-12 
13:15:11.000000000 +0100
@@ -481,6 +481,20 @@
             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('auth.change_group', self.user, groups[0])
+        assign('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')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-guardian-1.0.3/guardian/tests/templates/dummy403.html 
new/django-guardian-1.0.4/guardian/tests/templates/dummy403.html
--- old/django-guardian-1.0.3/guardian/tests/templates/dummy403.html    
1970-01-01 01:00:00.000000000 +0100
+++ new/django-guardian-1.0.4/guardian/tests/templates/dummy403.html    
2011-10-02 00:14:36.000000000 +0200
@@ -0,0 +1 @@
+foobar403
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/guardian/testsettings.py 
new/django-guardian-1.0.4/guardian/testsettings.py
--- old/django-guardian-1.0.3/guardian/testsettings.py  2011-07-25 
10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/guardian/testsettings.py  2012-01-27 
17:16:14.000000000 +0100
@@ -35,5 +35,18 @@
 TEMPLATE_DIRS = (
     os.path.join(os.path.dirname(__file__), 'tests', 'templates'),
 )
-print TEMPLATE_DIRS
+
+# Database specific
+
+if os.environ.get('GUARDIAN_TEST_DB_BACKEND') == 'mysql':
+    DATABASES['default']['ENGINE'] = 'django.db.backends.mysql'
+    DATABASES['default']['NAME'] = 'guardian_test'
+    DATABASES['default']['TEST_NAME'] = 'guardian_test'
+    DATABASES['default']['USER'] = os.environ.get('USER', 'root')
+
+if os.environ.get('GUARDIAN_TEST_DB_BACKEND') == 'postgresql':
+    DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2'
+    DATABASES['default']['NAME'] = 'guardian'
+    DATABASES['default']['TEST_NAME'] = 'guardian_test'
+    DATABASES['default']['USER'] = os.environ.get('USER', 'postgres')
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-guardian-1.0.3/setup.py 
new/django-guardian-1.0.4/setup.py
--- old/django-guardian-1.0.3/setup.py  2011-07-25 10:11:25.000000000 +0200
+++ new/django-guardian-1.0.4/setup.py  2011-10-02 00:14:36.000000000 +0200
@@ -18,7 +18,7 @@
     author = 'Lukasz Balcerzak',
     author_email = 'lukaszbalcer...@gmail.com',
     download_url='http://github.com/lukaszb/django-guardian/downloads',
-    description = guardian.__doc__,
+    description = guardian.__doc__.strip(),
     long_description = long_description,
     zip_safe = False,
     packages = find_packages(),

-- 
To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org
For additional commands, e-mail: opensuse-commit+h...@opensuse.org

Reply via email to