Merge branch 'master' into master

This commit is contained in:
Jannis Leidel 2020-02-07 11:04:41 +01:00 committed by GitHub
commit 2dcbffe297
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 956 additions and 727 deletions

6
.coveragerc Normal file
View file

@ -0,0 +1,6 @@
[run]
source = authority
branch = 1
[report]
omit = *tests*,*migrations*

3
.gitignore vendored
View file

@ -5,3 +5,6 @@ docs/build/*
.DS_Store
.tox/
dist/
build/
.coverage
.eggs/

View file

@ -1,26 +0,0 @@
syntax: glob
*.pyc
*.pyo
*~
*.swp
*.orig
*.kpf
*.egg-info
.project
.pydevproject
.DS_Store
MANIFEST
dist
build
dev.db
local_settings.py
parts/*
eggs/*
downloads/*
.installed.cfg
bin/*
develop-eggs/*.egg-link
example/example.db
docs/build
TODO
example/example.db

View file

@ -1,27 +1,28 @@
language: python
python: 3.5
sudo: false
cache:
directories:
- "~/.cache/pip"
env:
matrix:
- TOXENV=py27-dj18
- TOXENV=py33-dj18
- TOXENV=py34-dj18
- TOXENV=py35-dj18
install:
- pip install tox codecov
dist: xenial
python:
- 2.7
- 3.7
cache: pip
install: pip install tox-travis codecov
script: tox -v
after_success:
- codecov
deploy:
provider: pypi
user: jazzband
distributions: sdist bdist_wheel
password:
secure: XoXuSeFfJ+cM5/eCAAVuT6E/KsvuvMl9u9On5id6miq+wYqftpWiN9e/fjb3pDWkfZITooPeJBTpMJBGu+2Yc5i8JVGsYQgvP9dFqOH14P6NV4JU1ZF8iP7dpdWX3+h3K/QnqTvwwHmRF3mrDd/lnN9AB74bXLFYS282glJLVKM=
on:
tags: true
condition: "$TOXENV = py27-dj18"
repo: jazzband/django-authority
after_success: codecov
jobs:
fast_finish: true
include:
- stage: deploy
env:
python: 3.7
install: skip
script: skip
after_success: skip
deploy:
provider: pypi
user: jazzband
server: https://jazzband.co/projects/django-authority/upload
distributions: sdist bdist_wheel
password:
secure: TZJdf9naBzdkE+HDyhtoCIIc0ddEXLWW/VUD975gJUWQpOA69h4ZjCCQ6z21AZdrWczCUh6/cOLYYSoC9OeaHqF+Jzunn3iEomvdVRsW7SX7MAxTFUENQHF3S3j8fYlqIPWvNDOgxJ/AERisMUSZkRKWIYjEInYo31RoJ1ySN0w=
on:
tags: true
repo: jazzband/django-authority

View file

@ -1,4 +1,4 @@
Copyright (c) 2009, Jannis Leidel
Copyright (c) 2009-2019, Jannis Leidel
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -25,4 +25,4 @@ 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.
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -57,8 +57,32 @@ html version using the setup.py::
Changelog:
==========
0.14 (2020-02-07):
------------------
* Add Django 2.2 support
* Add Python 3.7 support
* Various fixes around the test harness.
0.13.1 (2018-01-28):
--------------------
* Minor fixes to the documentation and versioning.
0.13 (2018-01-28):
------------------
* Added support for Django 1.11
* Drop Support for Python 3.3
* Fixed a bug with template loader
0.12 (2017-01-10):
------------------
* Added support for Django 1.10
0.11 (2016-07-17):
-----------------
------------------
* Added Migration in order to support Django 1.8
@ -117,7 +141,9 @@ Changelog:
* Added ability to override form class in ``add_permission`` view.
* Added easy way to assign permissions via a permission instance, e.g.::
* Added easy way to assign permissions via a permission instance, e.g.:
.. code-block:: python
from django.contrib.auth.models import User
from mysite.articles.permissions import ArticlePermission
@ -145,13 +171,17 @@ Changelog:
* The templatetags have also been refactored to be easier to customize
which required a change in the template tag signature:
Old::
Old:
.. code-block:: html+django
{% permission_form flatpage %}
{% permission_form flatpage "flatpage_permission.top_secret" %}
{% permission_form OBJ PERMISSION_LABEL.CHECK_NAME %}
New::
New:
.. code-block:: html+django
{% permission_form for flatpage %}
{% permission_form for flatpage using "flatpage_permission.top_secret" %}

View file

@ -1,4 +1,10 @@
from django.utils.module_loading import autodiscover_modules
from pkg_resources import get_distribution, DistributionNotFound
try:
__version__ = get_distribution("django-authority").version
except DistributionNotFound:
# package is not installed
pass
LOADING = False
@ -13,4 +19,5 @@ def autodiscover():
return
LOADING = True
from django.utils.module_loading import autodiscover_modules
autodiscover_modules('permissions')

View file

@ -27,20 +27,20 @@ from authority.utils import get_choices_for
class PermissionInline(GenericTabularInline):
model = Permission
raw_id_fields = ('user', 'group', 'creator')
raw_id_fields = ("user", "group", "creator")
extra = 1
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'codename':
if db_field.name == "codename":
perm_choices = get_choices_for(self.parent_model)
kwargs['label'] = _('permission')
kwargs['widget'] = forms.Select(choices=perm_choices)
kwargs["label"] = _("permission")
kwargs["widget"] = forms.Select(choices=perm_choices)
return super(PermissionInline, self).formfield_for_dbfield(db_field, **kwargs)
class ActionPermissionInline(PermissionInline):
raw_id_fields = ()
template = 'admin/edit_inline/action_tabular.html'
template = "admin/edit_inline/action_tabular.html"
class ActionErrorList(forms.utils.ErrorList):
@ -56,9 +56,11 @@ def edit_permissions(modeladmin, request, queryset):
app_label = opts.app_label
# Check that the user has the permission to edit permissions
if not (request.user.is_superuser or
request.user.has_perm('authority.change_permission') or
request.user.has_perm('authority.change_foreign_permissions')):
if not (
request.user.is_superuser
or request.user.has_perm("authority.change_permission")
or request.user.has_perm("authority.change_foreign_permissions")
):
raise PermissionDenied
inline = ActionPermissionInline(queryset.model, modeladmin.admin_site)
@ -70,9 +72,10 @@ def edit_permissions(modeladmin, request, queryset):
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1:
prefix = "%s-%s" % (prefix, prefixes[prefix])
if request.POST.get('post'):
formset = FormSet(data=request.POST, files=request.FILES,
instance=obj, prefix=prefix)
if request.POST.get("post"):
formset = FormSet(
data=request.POST, files=request.FILES, instance=obj, prefix=prefix
)
else:
formset = FormSet(instance=obj, prefix=prefix)
formsets.append(formset)
@ -86,76 +89,90 @@ def edit_permissions(modeladmin, request, queryset):
media = media + inline_admin_formset.media
ordered_objects = opts.get_ordered_objects()
if request.POST.get('post'):
if request.POST.get("post"):
if all_valid(formsets):
for formset in formsets:
formset.save()
else:
modeladmin.message_user(request, '; '.join(
err.as_text() for formset in formsets for err in formset.errors
))
modeladmin.message_user(
request,
"; ".join(
err.as_text() for formset in formsets for err in formset.errors
),
)
# redirect to full request path to make sure we keep filter
return HttpResponseRedirect(request.get_full_path())
context = {
'errors': ActionErrorList(formsets),
'title': ugettext('Permissions for %s') % force_text(opts.verbose_name_plural),
'inline_admin_formsets': inline_admin_formsets,
'app_label': app_label,
'change': True,
'ordered_objects': ordered_objects,
'form_url': mark_safe(''),
'opts': opts,
'target_opts': queryset.model._meta,
'content_type_id': ContentType.objects.get_for_model(queryset.model).id,
'save_as': False,
'save_on_top': False,
'is_popup': False,
'media': mark_safe(media),
'show_delete': False,
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
'queryset': queryset,
"errors": ActionErrorList(formsets),
"title": ugettext("Permissions for %s") % force_text(opts.verbose_name_plural),
"inline_admin_formsets": inline_admin_formsets,
"app_label": app_label,
"change": True,
"ordered_objects": ordered_objects,
"form_url": mark_safe(""),
"opts": opts,
"target_opts": queryset.model._meta,
"content_type_id": ContentType.objects.get_for_model(queryset.model).id,
"save_as": False,
"save_on_top": False,
"is_popup": False,
"media": mark_safe(media),
"show_delete": False,
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
"queryset": queryset,
"object_name": force_text(opts.verbose_name),
}
template_name = getattr(modeladmin, 'permission_change_form_template', [
"admin/%s/%s/permission_change_form.html" % (app_label, opts.object_name.lower()),
"admin/%s/permission_change_form.html" % app_label,
"admin/permission_change_form.html"
])
template_name = getattr(
modeladmin,
"permission_change_form_template",
[
"admin/%s/%s/permission_change_form.html"
% (app_label, opts.object_name.lower()),
"admin/%s/permission_change_form.html" % app_label,
"admin/permission_change_form.html",
],
)
return render_to_response(template_name, context, request)
edit_permissions.short_description = _("Edit permissions for selected %(verbose_name_plural)s")
edit_permissions.short_description = _(
"Edit permissions for selected %(verbose_name_plural)s"
)
class PermissionAdmin(admin.ModelAdmin):
list_display = ('codename', 'content_type', 'user', 'group', 'approved')
list_filter = ('approved', 'content_type')
search_fields = ('user__username', 'group__name', 'codename')
raw_id_fields = ('user', 'group', 'creator')
generic_fields = ('content_object',)
actions = ['approve_permissions']
list_display = ("codename", "content_type", "user", "group", "approved")
list_filter = ("approved", "content_type")
search_fields = ("user__username", "group__name", "codename")
raw_id_fields = ("user", "group", "creator")
generic_fields = ("content_object",)
actions = ["approve_permissions"]
fieldsets = (
(None, {'fields': ('codename', ('content_type', 'object_id'))}),
(_('Permitted'), {'fields': ('approved', 'user', 'group')}),
(_('Creation'), {'fields': ('creator', 'date_requested', 'date_approved')}),
(None, {"fields": ("codename", ("content_type", "object_id"))}),
(_("Permitted"), {"fields": ("approved", "user", "group")}),
(_("Creation"), {"fields": ("creator", "date_requested", "date_approved")}),
)
def formfield_for_dbfield(self, db_field, **kwargs):
# For generic foreign keys marked as generic_fields we use a special widget
names = [f.fk_field
for f in self.model._meta.virtual_fields
if f.name in self.generic_fields]
names = [
f.fk_field
for f in self.model._meta.virtual_fields
if f.name in self.generic_fields
]
if db_field.name in names:
for gfk in self.model._meta.virtual_fields:
if gfk.fk_field == db_field.name:
kwargs['widget'] = GenericForeignKeyRawIdWidget(
gfk.ct_field, self.admin_site._registry.keys())
kwargs["widget"] = GenericForeignKeyRawIdWidget(
gfk.ct_field, self.admin_site._registry.keys()
)
break
return super(PermissionAdmin, self).formfield_for_dbfield(db_field, **kwargs)
def queryset(self, request):
user = request.user
if (user.is_superuser or
user.has_perm('permissions.change_foreign_permissions')):
if user.is_superuser or user.has_perm("permissions.change_foreign_permissions"):
return super(PermissionAdmin, self).queryset(request)
return super(PermissionAdmin, self).queryset(request).filter(creator=user)
@ -164,10 +181,14 @@ class PermissionAdmin(admin.ModelAdmin):
permission.approve(request.user)
message = ungettext(
"%(count)d permission successfully approved.",
"%(count)d permissions successfully approved.", len(queryset))
self.message_user(request, message % {'count': len(queryset)})
"%(count)d permissions successfully approved.",
len(queryset),
)
self.message_user(request, message % {"count": len(queryset)})
approve_permissions.short_description = _("Approve selected permissions")
admin.site.register(Permission, PermissionAdmin)
if actions:

View file

@ -5,7 +5,7 @@ from django.conf import settings
# 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')
user_model_label = getattr(settings, "AUTH_USER_MODEL", "auth.User")
try:
from django.contrib.auth import get_user_model

View file

@ -2,6 +2,7 @@ import inspect
from django.http import HttpResponseRedirect
from django.utils.http import urlquote
from django.utils.functional import wraps
from django.utils.six import string_types
from django.db.models import Model
from django.apps import apps
from django.shortcuts import get_object_or_404
@ -17,16 +18,16 @@ def permission_required(perm, *lookup_variables, **kwargs):
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
"""
login_url = kwargs.pop('login_url', settings.LOGIN_URL)
redirect_field_name = kwargs.pop('redirect_field_name', REDIRECT_FIELD_NAME)
redirect_to_login = kwargs.pop('redirect_to_login', True)
login_url = kwargs.pop("login_url", settings.LOGIN_URL)
redirect_field_name = kwargs.pop("redirect_field_name", REDIRECT_FIELD_NAME)
redirect_to_login = kwargs.pop("redirect_to_login", True)
def decorate(view_func):
def decorated(request, *args, **kwargs):
if request.user.is_authenticated():
if request.user.is_authenticated:
params = []
for lookup_variable in lookup_variables:
if isinstance(lookup_variable, basestring):
if isinstance(lookup_variable, string_types):
value = kwargs.get(lookup_variable, None)
if value is None:
continue
@ -36,17 +37,20 @@ def permission_required(perm, *lookup_variables, **kwargs):
value = kwargs.get(varname, None)
if value is None:
continue
if isinstance(model, basestring):
if isinstance(model, string_types):
model_class = apps.get_model(*model.split("."))
else:
model_class = model
if model_class is None:
raise ValueError(
"The given argument '%s' is not a valid model." % model)
if (inspect.isclass(model_class) and
not issubclass(model_class, Model)):
"The given argument '%s' is not a valid model." % model
)
if inspect.isclass(model_class) and not issubclass(
model_class, Model
):
raise ValueError(
'The argument %s needs to be a model.' % model)
"The argument %s needs to be a model." % model
)
obj = get_object_or_404(model_class, **{lookup: value})
params.append(obj)
check = get_check(request.user, perm)
@ -58,9 +62,11 @@ def permission_required(perm, *lookup_variables, **kwargs):
if redirect_to_login:
path = urlquote(request.get_full_path())
tup = login_url, redirect_field_name, path
return HttpResponseRedirect('%s?%s=%s' % tup)
return HttpResponseRedirect("%s?%s=%s" % tup)
return permission_denied(request)
return wraps(view_func)(decorated)
return decorate
@ -69,5 +75,5 @@ def permission_required_or_403(perm, *args, **kwargs):
Decorator that wraps the permission_required decorator and returns a
permission denied (403) page instead of redirecting to the login URL.
"""
kwargs['redirect_to_login'] = False
kwargs["redirect_to_login"] = False
return permission_required(perm, *args, **kwargs)

View file

@ -4,11 +4,11 @@ class AuthorityException(Exception):
class NotAModel(AuthorityException):
def __init__(self, object):
super(NotAModel, self).__init__(
"Not a model class or instance")
super(NotAModel, self).__init__("Not a model class or instance")
class UnsavedModelInstance(AuthorityException):
def __init__(self, object):
super(UnsavedModelInstance, self).__init__(
"Model instance has no pk, was it saved?")
"Model instance has no pk, was it saved?"
)

View file

@ -14,7 +14,7 @@ User = get_user_model()
class BasePermissionForm(forms.ModelForm):
codename = forms.CharField(label=_('Permission'))
codename = forms.CharField(label=_("Permission"))
class Meta:
model = Permission
@ -25,10 +25,10 @@ class BasePermissionForm(forms.ModelForm):
self.obj = obj
self.approved = approved
if obj and perm:
self.base_fields['codename'].widget = forms.HiddenInput()
self.base_fields["codename"].widget = forms.HiddenInput()
elif obj and (not perm or not approved):
perms = get_choices_for(self.obj)
self.base_fields['codename'].widget = forms.Select(choices=perms)
self.base_fields["codename"].widget = forms.Select(choices=perms)
super(BasePermissionForm, self).__init__(*args, **kwargs)
def save(self, request, commit=True, *args, **kwargs):
@ -41,14 +41,14 @@ class BasePermissionForm(forms.ModelForm):
class UserPermissionForm(BasePermissionForm):
user = forms.CharField(label=_('User'))
user = forms.CharField(label=_("User"))
class Meta(BasePermissionForm.Meta):
fields = ('user',)
fields = ("user",)
def __init__(self, *args, **kwargs):
if not kwargs.get('approved', False):
self.base_fields['user'].widget = forms.HiddenInput()
if not kwargs.get("approved", False):
self.base_fields["user"].widget = forms.HiddenInput()
super(UserPermissionForm, self).__init__(*args, **kwargs)
def clean_user(self):
@ -57,34 +57,41 @@ class UserPermissionForm(BasePermissionForm):
user = User.objects.get(username__iexact=username)
except User.DoesNotExist:
raise forms.ValidationError(
mark_safe(_("A user with that username does not exist.")))
mark_safe(_("A user with that username does not exist."))
)
check = permissions.BasePermission(user=user)
error_msg = None
if user.is_superuser:
error_msg = _("The user %(user)s do not need to request "
"access to any permission as it is a super user.")
error_msg = _(
"The user %(user)s do not need to request "
"access to any permission as it is a super user."
)
elif check.has_perm(self.perm, self.obj):
error_msg = _("The user %(user)s already has the permission "
"'%(perm)s' for %(object_name)s '%(obj)s'")
error_msg = _(
"The user %(user)s already has the permission "
"'%(perm)s' for %(object_name)s '%(obj)s'"
)
elif check.requested_perm(self.perm, self.obj):
error_msg = _("The user %(user)s already requested the permission"
" '%(perm)s' for %(object_name)s '%(obj)s'")
error_msg = _(
"The user %(user)s already requested the permission"
" '%(perm)s' for %(object_name)s '%(obj)s'"
)
if error_msg:
error_msg = error_msg % {
'object_name': self.obj._meta.object_name.lower(),
'perm': self.perm,
'obj': self.obj,
'user': user,
"object_name": self.obj._meta.object_name.lower(),
"perm": self.perm,
"obj": self.obj,
"user": user,
}
raise forms.ValidationError(mark_safe(error_msg))
return user
class GroupPermissionForm(BasePermissionForm):
group = forms.CharField(label=_('Group'))
group = forms.CharField(label=_("Group"))
class Meta(BasePermissionForm.Meta):
fields = ('group',)
fields = ("group",)
def clean_group(self):
groupname = self.cleaned_data["group"]
@ -92,14 +99,21 @@ class GroupPermissionForm(BasePermissionForm):
group = Group.objects.get(name__iexact=groupname)
except Group.DoesNotExist:
raise forms.ValidationError(
mark_safe(_("A group with that name does not exist.")))
mark_safe(_("A group with that name does not exist."))
)
check = permissions.BasePermission(group=group)
if check.has_perm(self.perm, self.obj):
raise forms.ValidationError(mark_safe(
_("This group already has the permission '%(perm)s' "
"for %(object_name)s '%(obj)s'") % {
'perm': self.perm,
'object_name': self.obj._meta.object_name.lower(),
'obj': self.obj,
}))
raise forms.ValidationError(
mark_safe(
_(
"This group already has the permission '%(perm)s' "
"for %(object_name)s '%(obj)s'"
)
% {
"perm": self.perm,
"object_name": self.obj._meta.object_name.lower(),
"obj": self.obj,
}
)
)
return group

View file

@ -4,7 +4,6 @@ from django.contrib.contenttypes.models import ContentType
class PermissionManager(models.Manager):
def get_content_type(self, obj):
return ContentType.objects.get_for_model(obj)
@ -12,46 +11,41 @@ class PermissionManager(models.Manager):
return self.filter(content_type=self.get_content_type(obj))
def for_object(self, obj, approved=True):
return self.get_for_model(obj).select_related(
'user', 'creator', 'group', 'content_type'
).filter(object_id=obj.id, approved=approved)
return (
self.get_for_model(obj)
.select_related("user", "creator", "group", "content_type")
.filter(object_id=obj.id, approved=approved)
)
def for_user(self, user, obj, check_groups=True):
perms = self.get_for_model(obj)
if not check_groups:
return perms.select_related('user', 'creator').filter(user=user)
return perms.select_related("user", "creator").filter(user=user)
# Hacking user to user__pk to workaround deepcopy bug:
# http://bugs.python.org/issue2460
# Which is triggered by django's deepcopy which backports that fix in
# Django 1.2
return perms.select_related(
'user',
'creator'
).prefetch_related(
'user__groups'
).filter(
Q(user__pk=user.pk) | Q(group__in=user.groups.all())
return (
perms.select_related("user", "creator")
.prefetch_related("user__groups")
.filter(Q(user__pk=user.pk) | Q(group__in=user.groups.all()))
)
def user_permissions(
self, user, perm, obj, approved=True, check_groups=True):
return self.for_user(
user,
obj,
check_groups,
).filter(
codename=perm,
approved=approved,
def user_permissions(self, user, perm, obj, approved=True, check_groups=True):
return self.for_user(user, obj, check_groups,).filter(
codename=perm, approved=approved,
)
def group_permissions(self, group, perm, obj, approved=True):
"""
Get objects that have Group perm permission on
"""
return self.get_for_model(obj).select_related(
'user', 'group', 'creator').filter(group=group, codename=perm,
approved=approved)
return (
self.get_for_model(obj)
.select_related("user", "group", "creator")
.filter(group=group, codename=perm, approved=approved)
)
def delete_objects_permissions(self, obj):
"""

View file

@ -9,35 +9,96 @@ from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
("auth", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0001_initial'),
("contenttypes", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='Permission',
name="Permission",
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('codename', models.CharField(max_length=100, verbose_name='codename')),
('object_id', models.PositiveIntegerField()),
('approved', models.BooleanField(default=False, help_text='Designates whether the permission has been approved and treated as active. Unselect this instead of deleting permissions.', verbose_name='approved')),
('date_requested', models.DateTimeField(default=datetime.datetime.now, verbose_name='date requested')),
('date_approved', models.DateTimeField(null=True, verbose_name='date approved', blank=True)),
('content_type', models.ForeignKey(related_name='row_permissions', to='contenttypes.ContentType')),
('creator', models.ForeignKey(related_name='created_permissions', blank=True, to=settings.AUTH_USER_MODEL, null=True)),
('group', models.ForeignKey(blank=True, to='auth.Group', null=True)),
('user', models.ForeignKey(related_name='granted_permissions', blank=True, to=settings.AUTH_USER_MODEL, null=True)),
(
"id",
models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True,
),
),
("codename", models.CharField(max_length=100, verbose_name="codename")),
("object_id", models.PositiveIntegerField()),
(
"approved",
models.BooleanField(
default=False,
help_text="Designates whether the permission has been approved and treated as active. Unselect this instead of deleting permissions.",
verbose_name="approved",
),
),
(
"date_requested",
models.DateTimeField(
default=datetime.datetime.now, verbose_name="date requested"
),
),
(
"date_approved",
models.DateTimeField(
null=True, verbose_name="date approved", blank=True
),
),
(
"content_type",
models.ForeignKey(
related_name="row_permissions",
to="contenttypes.ContentType",
on_delete=models.CASCADE,
),
),
(
"creator",
models.ForeignKey(
related_name="created_permissions",
blank=True,
to=settings.AUTH_USER_MODEL,
null=True,
on_delete=models.CASCADE,
),
),
(
"group",
models.ForeignKey(
blank=True, to="auth.Group", null=True, on_delete=models.CASCADE
),
),
(
"user",
models.ForeignKey(
related_name="granted_permissions",
blank=True,
to=settings.AUTH_USER_MODEL,
null=True,
on_delete=models.CASCADE,
),
),
],
options={
'verbose_name': 'permission',
'verbose_name_plural': 'permissions',
'permissions': (('change_foreign_permissions', 'Can change foreign permissions'), ('delete_foreign_permissions', 'Can delete foreign permissions'), ('approve_permission_requests', 'Can approve permission requests')),
"verbose_name": "permission",
"verbose_name_plural": "permissions",
"permissions": (
("change_foreign_permissions", "Can change foreign permissions"),
("delete_foreign_permissions", "Can delete foreign permissions"),
("approve_permission_requests", "Can approve permission requests"),
),
},
bases=(models.Model,),
),
migrations.AlterUniqueTogether(
name='permission',
unique_together=set([('codename', 'object_id', 'content_type', 'user', 'group')]),
name="permission",
unique_together=set(
[("codename", "object_id", "content_type", "user", "group")]
),
),
]

View file

@ -15,25 +15,41 @@ class Permission(models.Model):
This kind of permission is associated with a user/group and an object
of any content type.
"""
codename = models.CharField(_('codename'), max_length=100)
content_type = models.ForeignKey(ContentType, related_name="row_permissions")
codename = models.CharField(_("codename"), max_length=100)
content_type = models.ForeignKey(
ContentType, related_name="row_permissions", on_delete=models.CASCADE
)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
content_object = GenericForeignKey("content_type", "object_id")
user = models.ForeignKey(
user_model_label, null=True, blank=True, related_name='granted_permissions')
group = models.ForeignKey(Group, null=True, blank=True)
user_model_label,
null=True,
blank=True,
related_name="granted_permissions",
on_delete=models.CASCADE,
)
group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.CASCADE)
creator = models.ForeignKey(
user_model_label, null=True, blank=True, related_name='created_permissions')
user_model_label,
null=True,
blank=True,
related_name="created_permissions",
on_delete=models.CASCADE,
)
approved = models.BooleanField(
_('approved'),
_("approved"),
default=False,
help_text=_("Designates whether the permission has been approved and treated as active. "
"Unselect this instead of deleting permissions."))
help_text=_(
"Designates whether the permission has been approved and treated as active. "
"Unselect this instead of deleting permissions."
),
)
date_requested = models.DateTimeField(_('date requested'), default=datetime.now)
date_approved = models.DateTimeField(_('date approved'), blank=True, null=True)
date_requested = models.DateTimeField(_("date requested"), default=datetime.now)
date_approved = models.DateTimeField(_("date approved"), blank=True, null=True)
objects = PermissionManager()
@ -42,12 +58,12 @@ class Permission(models.Model):
class Meta:
unique_together = ("codename", "object_id", "content_type", "user", "group")
verbose_name = _('permission')
verbose_name_plural = _('permissions')
verbose_name = _("permission")
verbose_name_plural = _("permissions")
permissions = (
('change_foreign_permissions', 'Can change foreign permissions'),
('delete_foreign_permissions', 'Can delete foreign permissions'),
('approve_permission_requests', 'Can approve permission requests'),
("change_foreign_permissions", "Can change foreign permissions"),
("delete_foreign_permissions", "Can delete foreign permissions"),
("approve_permission_requests", "Can approve permission requests"),
)
def save(self, *args, **kwargs):

View file

@ -14,9 +14,9 @@ class PermissionMetaclass(type):
Used to generate the default set of permission checks "add", "change" and
"delete".
"""
def __new__(cls, name, bases, attrs):
new_class = super(
PermissionMetaclass, cls).__new__(cls, name, bases, attrs)
new_class = super(PermissionMetaclass, cls).__new__(cls, name, bases, attrs)
if not new_class.label:
new_class.label = "%s_permission" % new_class.__name__.lower()
new_class.label = slugify(new_class.label)
@ -31,11 +31,12 @@ class BasePermission(object):
"""
Base Permission class to be used to define app permissions.
"""
__metaclass__ = PermissionMetaclass
checks = ()
label = None
generic_checks = ['add', 'browse', 'change', 'delete']
generic_checks = ["add", "browse", "change", "delete"]
def __init__(self, user=None, group=None, *args, **kwargs):
self.user = user
@ -48,10 +49,7 @@ class BasePermission(object):
"""
if not self.user:
return {}, {}
group_pks = set(self.user.groups.values_list(
'pk',
flat=True,
))
group_pks = set(self.user.groups.values_list("pk", flat=True,))
perms = Permission.objects.filter(
Q(user__pk=self.user.pk) | Q(group__pk__in=group_pks),
)
@ -59,22 +57,26 @@ class BasePermission(object):
group_permissions = {}
for perm in perms:
if perm.user_id == self.user.pk:
user_permissions[(
perm.object_id,
perm.content_type_id,
perm.codename,
perm.approved,
)] = True
user_permissions[
(
perm.object_id,
perm.content_type_id,
perm.codename,
perm.approved,
)
] = True
# If the user has the permission do for something, but perm.user !=
# self.user then by definition that permission came from the
# group.
else:
group_permissions[(
perm.object_id,
perm.content_type_id,
perm.codename,
perm.approved,
)] = True
group_permissions[
(
perm.object_id,
perm.content_type_id,
perm.codename,
perm.approved,
)
] = True
return user_permissions, group_permissions
def _get_group_cached_perms(self):
@ -83,17 +85,12 @@ class BasePermission(object):
"""
if not self.group:
return {}
perms = Permission.objects.filter(
group=self.group,
)
perms = Permission.objects.filter(group=self.group,)
group_permissions = {}
for perm in perms:
group_permissions[(
perm.object_id,
perm.content_type_id,
perm.codename,
perm.approved,
)] = True
group_permissions[
(perm.object_id, perm.content_type_id, perm.codename, perm.approved,)
] = True
return group_permissions
def _prime_user_perm_caches(self):
@ -123,11 +120,7 @@ class BasePermission(object):
# Check to see if the cache has been primed.
if not self.user:
return {}
cache_filled = getattr(
self.user,
'_authority_perm_cache_filled',
False,
)
cache_filled = getattr(self.user, "_authority_perm_cache_filled", False,)
if cache_filled:
# Don't really like the name for this, but this matches how Django
# does it.
@ -145,11 +138,7 @@ class BasePermission(object):
# Check to see if the cache has been primed.
if not self.group:
return {}
cache_filled = getattr(
self.group,
'_authority_perm_cache_filled',
False,
)
cache_filled = getattr(self.group, "_authority_perm_cache_filled", False,)
if cache_filled:
# Don't really like the name for this, but this matches how Django
# does it.
@ -167,11 +156,7 @@ class BasePermission(object):
# Check to see if the cache has been primed.
if not self.user:
return {}
cache_filled = getattr(
self.user,
'_authority_perm_cache_filled',
False,
)
cache_filled = getattr(self.user, "_authority_perm_cache_filled", False,)
if cache_filled:
return self.user._authority_group_perm_cache
@ -194,7 +179,7 @@ class BasePermission(object):
@property
def use_smart_cache(self):
use_smart_cache = getattr(settings, 'AUTHORITY_USE_SMART_CACHE', True)
use_smart_cache = getattr(settings, "AUTHORITY_USE_SMART_CACHE", True)
return (self.user or self.group) and use_smart_cache
def has_user_perms(self, perm, obj, approved, check_groups=True):
@ -210,12 +195,7 @@ class BasePermission(object):
def _user_has_perms(cached_perms):
# Check to see if the permission is in the cache.
return cached_perms.get((
obj.pk,
content_type_pk,
perm,
approved,
))
return cached_perms.get((obj.pk, content_type_pk, perm, approved,))
# Check to see if the permission is in the cache.
if _user_has_perms(self._user_perm_cache):
@ -227,15 +207,13 @@ class BasePermission(object):
return False
# Actually hit the DB, no smart cache used.
return Permission.objects.user_permissions(
self.user,
perm,
obj,
approved,
check_groups,
).filter(
object_id=obj.pk,
).exists()
return (
Permission.objects.user_permissions(
self.user, perm, obj, approved, check_groups,
)
.filter(object_id=obj.pk,)
.exists()
)
def has_group_perms(self, perm, obj, approved):
"""
@ -249,24 +227,17 @@ class BasePermission(object):
def _group_has_perms(cached_perms):
# Check to see if the permission is in the cache.
return cached_perms.get((
obj.pk,
content_type_pk,
perm,
approved,
))
return cached_perms.get((obj.pk, content_type_pk, perm, approved,))
# Check to see if the permission is in the cache.
return _group_has_perms(self._group_perm_cache)
# Actually hit the DB, no smart cache used.
return Permission.objects.group_permissions(
self.group,
perm, obj,
approved,
).filter(
object_id=obj.pk,
).exists()
return (
Permission.objects.group_permissions(self.group, perm, obj, approved,)
.filter(object_id=obj.pk,)
.exists()
)
def has_perm(self, perm, obj, check_groups=True, approved=True):
"""
@ -305,25 +276,20 @@ class BasePermission(object):
return perms
def get_django_codename(
self, check, model_or_instance, generic=False, without_left=False):
self, check, model_or_instance, generic=False, without_left=False
):
if without_left:
perm = check
else:
perm = '%s.%s' % (model_or_instance._meta.app_label, check.lower())
perm = "%s.%s" % (model_or_instance._meta.app_label, check.lower())
if generic:
perm = '%s_%s' % (
perm,
model_or_instance._meta.object_name.lower(),
)
perm = "%s_%s" % (perm, model_or_instance._meta.object_name.lower(),)
return perm
def get_codename(self, check, model_or_instance, generic=False):
perm = '%s.%s' % (self.label, check.lower())
perm = "%s.%s" % (self.label, check.lower())
if generic:
perm = '%s_%s' % (
perm,
model_or_instance._meta.object_name.lower(),
)
perm = "%s_%s" % (perm, model_or_instance._meta.object_name.lower(),)
return perm
def assign(self, check=None, content_object=None, generic=False):
@ -345,7 +311,7 @@ class BasePermission(object):
content_objects = content_object
if not check:
checks = self.generic_checks + getattr(self, 'checks', [])
checks = self.generic_checks + getattr(self, "checks", [])
elif not isinstance(check, (list, tuple)):
checks = (check,)
else:
@ -364,11 +330,7 @@ class BasePermission(object):
for check in checks:
if isinstance(content_object, Model):
# make an authority per object permission
codename = self.get_codename(
check,
content_object,
generic,
)
codename = self.get_codename(check, content_object, generic,)
try:
perm = Permission.objects.get(
user=self.user,
@ -390,21 +352,16 @@ class BasePermission(object):
elif isinstance(content_object, ModelBase):
# make a Django permission
codename = self.get_django_codename(
check,
content_object,
generic,
without_left=True,
check, content_object, generic, without_left=True,
)
try:
perm = DjangoPermission.objects.get(codename=codename)
except DjangoPermission.DoesNotExist:
name = check
if '_' in name:
name = name[0:name.find('_')]
if "_" in name:
name = name[0 : name.find("_")]
perm = DjangoPermission(
name=name,
codename=codename,
content_type=content_type,
name=name, codename=codename, content_type=content_type,
)
perm.save()
self.user.user_permissions.add(perm)

View file

@ -19,6 +19,7 @@ class PermissionSite(object):
"""
A dictionary that contains permission instances and their labels.
"""
_registry = {}
_choices = {}
@ -32,7 +33,7 @@ class PermissionSite(object):
return [perm for perm in self._registry.values() if perm.model == model]
def get_check(self, user, label):
perm_label, check_name = label.split('.')
perm_label, check_name = label.split(".")
perm_cls = self.get_permission_by_label(perm_label)
if perm_cls is None:
return None
@ -52,8 +53,8 @@ class PermissionSite(object):
for perm in self.get_permissions_by_model(model_cls):
for name, check in getmembers(perm, ismethod):
if name in perm.checks:
signature = '%s.%s' % (perm.label, name)
label = getattr(check, 'short_description', signature)
signature = "%s.%s" % (perm.label, name)
label = getattr(check, "short_description", signature)
choices.append((signature, label))
self._choices[model_cls] = choices
return choices
@ -67,17 +68,23 @@ class PermissionSite(object):
if permission_class.label in self.get_labels():
raise ImproperlyConfigured(
"The name of %s conflicts with %s" % (
permission_class, self.get_permission_by_label(permission_class.label)))
"The name of %s conflicts with %s"
% (
permission_class,
self.get_permission_by_label(permission_class.label),
)
)
for model in model_or_iterable:
if model in self._registry:
raise AlreadyRegistered(
'The model %s is already registered' % model.__name__)
"The model %s is already registered" % model.__name__
)
if options:
options['__module__'] = __name__
options["__module__"] = __name__
permission_class = type(
"%sPermission" % model.__name__, (permission_class,), options)
"%sPermission" % model.__name__, (permission_class,), options
)
permission_class.model = model
self.setup(model, permission_class)
@ -88,7 +95,7 @@ class PermissionSite(object):
model_or_iterable = [model_or_iterable]
for model in model_or_iterable:
if model not in self._registry:
raise NotRegistered('The model %s is not registered' % model.__name__)
raise NotRegistered("The model %s is not registered" % model.__name__)
del self._registry[model]
def setup(self, model, permission):
@ -99,10 +106,10 @@ class PermissionSite(object):
func.__name__ = check_name
func.short_description = getattr(
check_func,
'short_description',
_("%(object_name)s permission '%(check)s'") % {
'object_name': model._meta.object_name,
'check': check_name})
"short_description",
_("%(object_name)s permission '%(check)s'")
% {"object_name": model._meta.object_name, "check": check_name},
)
setattr(permission, check_name, func)
else:
permission.generic_checks.append(check_name)
@ -111,11 +118,12 @@ class PermissionSite(object):
object_name = model._meta.object_name
func_name = "%s_%s" % (check_name, object_name.lower())
func.short_description = _("Can %(check)s this %(object_name)s") % {
'object_name': model._meta.object_name.lower(),
'check': check_name}
"object_name": model._meta.object_name.lower(),
"check": check_name,
}
func.check_name = check_name
if func_name not in permission.checks:
permission.checks = (list(permission.checks) + [func_name])
permission.checks = list(permission.checks) + [func_name]
setattr(permission, func_name, func)
setattr(model, "permissions", PermissionDescriptor())
@ -125,6 +133,7 @@ class PermissionSite(object):
if check_func and not granted:
return check_func(self, *args, **kwargs)
return granted
return check
@ -134,7 +143,9 @@ class PermissionDescriptor(object):
if obj:
return ContentType.objects.get_for_model(obj)
else:
raise Exception("Invalid arguments given to PermissionDescriptor.get_content_type")
raise Exception(
"Invalid arguments given to PermissionDescriptor.get_content_type"
)
def __get__(self, instance, owner):
if instance is None:
@ -142,6 +153,7 @@ class PermissionDescriptor(object):
ct = self.get_content_type(instance)
return ct.row_permissions.all()
site = PermissionSite()
get_check = site.get_check
get_choices_for = site.get_choices_for

View file

@ -1,6 +1,6 @@
from django import template
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.contrib.auth.models import AnonymousUser
from authority.utils import get_check
@ -16,27 +16,31 @@ register = template.Library()
@register.simple_tag
def url_for_obj(view_name, obj):
return reverse(view_name, kwargs={
'app_label': obj._meta.app_label,
'module_name': obj._meta.module_name,
'pk': obj.pk}
return reverse(
view_name,
kwargs={
"app_label": obj._meta.app_label,
"module_name": obj._meta.module_name,
"pk": obj.pk,
},
)
@register.simple_tag
def add_url_for_obj(obj):
return url_for_obj('authority-add-permission', obj)
return url_for_obj("authority-add-permission", obj)
@register.simple_tag
def request_url_for_obj(obj):
return url_for_obj('authority-add-permission-request', obj)
return url_for_obj("authority-add-permission-request", obj)
class ResolverNode(template.Node):
"""
A small wrapper that adds a convenient resolve method.
"""
def resolve(self, var, context):
"""Resolves a variable out of context if it's not in quotes"""
if var is None:
@ -49,7 +53,7 @@ class ResolverNode(template.Node):
@classmethod
def next_bit_for(cls, bits, key, if_none=None):
try:
return bits[bits.index(key)+1]
return bits[bits.index(key) + 1]
except ValueError:
return if_none
@ -58,18 +62,18 @@ class PermissionComparisonNode(ResolverNode):
"""
Implements a node to provide an "if user/group has permission on object"
"""
@classmethod
def handle_token(cls, parser, token):
bits = token.contents.split()
if 5 < len(bits) < 3:
raise template.TemplateSyntaxError(
"'%s' tag takes three, "
"four or five arguments" % bits[0]
"'%s' tag takes three, " "four or five arguments" % bits[0]
)
end_tag = 'endifhasperm'
nodelist_true = parser.parse(('else', end_tag))
end_tag = "endifhasperm"
nodelist_true = parser.parse(("else", end_tag))
token = parser.next_token()
if token.contents == 'else': # there is an 'else' clause in the tag
if token.contents == "else": # there is an 'else' clause in the tag
nodelist_false = parser.parse((end_tag,))
parser.delete_first_token()
else:
@ -107,13 +111,13 @@ class PermissionComparisonNode(ResolverNode):
return self.nodelist_true.render(context)
# If the app couldn't be found
except (ImproperlyConfigured, ImportError):
return ''
return ""
# If either variable fails to resolve, return nothing.
except template.VariableDoesNotExist:
return ''
return ""
# If the types don't permit comparison, return nothing.
except (TypeError, AttributeError):
return ''
return ""
return self.nodelist_false.render(context)
@ -141,15 +145,14 @@ def ifhasperm(parser, token):
class PermissionFormNode(ResolverNode):
@classmethod
def handle_token(cls, parser, token, approved):
bits = token.contents.split()
kwargs = {
'obj': cls.next_bit_for(bits, 'for'),
'perm': cls.next_bit_for(bits, 'using', None),
'template_name': cls.next_bit_for(bits, 'with', ''),
'approved': approved,
"obj": cls.next_bit_for(bits, "for"),
"perm": cls.next_bit_for(bits, "using", None),
"template_name": cls.next_bit_for(bits, "with", ""),
"approved": approved,
}
return cls(**kwargs)
@ -163,35 +166,39 @@ class PermissionFormNode(ResolverNode):
obj = self.resolve(self.obj, context)
perm = self.resolve(self.perm, context)
if self.template_name:
template_name = [self.resolve(o, context) for o in self.template_name.split(',')]
template_name = [
self.resolve(o, context) for o in self.template_name.split(",")
]
else:
template_name = 'authority/permission_form.html'
request = context['request']
template_name = "authority/permission_form.html"
request = context["request"]
extra_context = {}
if self.approved:
if (request.user.is_authenticated() and
request.user.has_perm('authority.add_permission')):
if request.user.is_authenticated and request.user.has_perm(
"authority.add_permission"
):
extra_context = {
'form_url': url_for_obj('authority-add-permission', obj),
'next': request.build_absolute_uri(),
'approved': self.approved,
'form': UserPermissionForm(perm, obj, approved=self.approved,
initial=dict(codename=perm)),
"form_url": url_for_obj("authority-add-permission", obj),
"next": request.build_absolute_uri(),
"approved": self.approved,
"form": UserPermissionForm(
perm, obj, approved=self.approved, initial=dict(codename=perm)
),
}
else:
if request.user.is_authenticated() and not request.user.is_superuser:
if request.user.is_authenticated and not request.user.is_superuser:
extra_context = {
'form_url': url_for_obj('authority-add-permission-request', obj),
'next': request.build_absolute_uri(),
'approved': self.approved,
'form': UserPermissionForm(
"form_url": url_for_obj("authority-add-permission-request", obj),
"next": request.build_absolute_uri(),
"approved": self.approved,
"form": UserPermissionForm(
perm,
obj,
approved=self.approved,
initial=dict(codename=perm, user=request.user.username)),
initial=dict(codename=perm, user=request.user.username),
),
}
return template.loader.render_to_string(template_name, extra_context,
request)
return template.loader.render_to_string(template_name, extra_context, request)
@register.tag
@ -226,16 +233,15 @@ def permission_request_form(parser, token):
class PermissionsForObjectNode(ResolverNode):
@classmethod
def handle_token(cls, parser, token, approved, name):
bits = token.contents.split()
tag_name = bits[0]
kwargs = {
'obj': cls.next_bit_for(bits, tag_name),
'user': cls.next_bit_for(bits, 'for'),
'var_name': cls.next_bit_for(bits, 'as', name),
'approved': approved,
"obj": cls.next_bit_for(bits, tag_name),
"user": cls.next_bit_for(bits, "for"),
"var_name": cls.next_bit_for(bits, "as", name),
"approved": approved,
}
return cls(**kwargs)
@ -256,7 +262,7 @@ class PermissionsForObjectNode(ResolverNode):
if isinstance(user, User):
perms = perms.filter(user=user)
context[var_name] = perms
return ''
return ""
@register.tag
@ -276,8 +282,9 @@ def get_permissions(parser, token):
{% get_permissions obj for request.user as "my_permissions" %}
"""
return PermissionsForObjectNode.handle_token(parser, token, approved=True,
name='"permissions"')
return PermissionsForObjectNode.handle_token(
parser, token, approved=True, name='"permissions"'
)
@register.tag
@ -297,23 +304,22 @@ def get_permission_requests(parser, token):
{% get_permission_requests obj for request.user as "my_permissions" %}
"""
return PermissionsForObjectNode.handle_token(parser, token,
approved=False,
name='"permission_requests"')
return PermissionsForObjectNode.handle_token(
parser, token, approved=False, name='"permission_requests"'
)
class PermissionForObjectNode(ResolverNode):
@classmethod
def handle_token(cls, parser, token, approved, name):
bits = token.contents.split()
tag_name = bits[0]
kwargs = {
'perm': cls.next_bit_for(bits, tag_name),
'user': cls.next_bit_for(bits, 'for'),
'objs': cls.next_bit_for(bits, 'and'),
'var_name': cls.next_bit_for(bits, 'as', name),
'approved': approved,
"perm": cls.next_bit_for(bits, tag_name),
"user": cls.next_bit_for(bits, "for"),
"objs": cls.next_bit_for(bits, "and"),
"var_name": cls.next_bit_for(bits, "as", name),
"approved": approved,
}
return cls(**kwargs)
@ -325,7 +331,7 @@ class PermissionForObjectNode(ResolverNode):
self.approved = approved
def render(self, context):
objs = [self.resolve(obj, context) for obj in self.objs.split(',')]
objs = [self.resolve(obj, context) for obj in self.objs.split(",")]
var_name = self.resolve(self.var_name, context)
perm = self.resolve(self.perm, context)
user = self.resolve(self.user, context)
@ -342,7 +348,7 @@ class PermissionForObjectNode(ResolverNode):
if granted:
break
context[var_name] = granted
return ''
return ""
@register.tag
@ -367,9 +373,9 @@ def get_permission(parser, token):
{% endif %}
"""
return PermissionForObjectNode.handle_token(parser, token,
approved=True,
name='"permission"')
return PermissionForObjectNode.handle_token(
parser, token, approved=True, name='"permission"'
)
@register.tag
@ -395,60 +401,66 @@ def get_permission_request(parser, token):
"""
return PermissionForObjectNode.handle_token(
parser, token, approved=False, name='"permission_request"')
parser, token, approved=False, name='"permission_request"'
)
def base_link(context, perm, view_name):
return {
'next': context['request'].build_absolute_uri(),
'url': reverse(view_name, kwargs={'permission_pk': perm.pk}),
"next": context["request"].build_absolute_uri(),
"url": reverse(view_name, kwargs={"permission_pk": perm.pk}),
}
@register.inclusion_tag('authority/permission_delete_link.html', takes_context=True)
@register.inclusion_tag("authority/permission_delete_link.html", takes_context=True)
def permission_delete_link(context, perm):
"""
Renders a html link to the delete view of the given permission. Returns
no content if the request-user has no permission to delete foreign
permissions.
"""
user = context['request'].user
if user.is_authenticated():
if (user.has_perm('authority.delete_foreign_permissions') or
user.pk == perm.creator.pk):
return base_link(context, perm, 'authority-delete-permission')
return {'url': None}
user = context["request"].user
if user.is_authenticated:
if (
user.has_perm("authority.delete_foreign_permissions")
or user.pk == perm.creator.pk
):
return base_link(context, perm, "authority-delete-permission")
return {"url": None}
@register.inclusion_tag('authority/permission_request_delete_link.html', takes_context=True)
@register.inclusion_tag(
"authority/permission_request_delete_link.html", takes_context=True
)
def permission_request_delete_link(context, perm):
"""
Renders a html link to the delete view of the given permission request.
Returns no content if the request-user has no permission to delete foreign
permissions.
"""
user = context['request'].user
if user.is_authenticated():
link_kwargs = base_link(context, perm,
'authority-delete-permission-request')
if user.has_perm('authority.delete_permission'):
link_kwargs['is_requestor'] = False
user = context["request"].user
if user.is_authenticated:
link_kwargs = base_link(context, perm, "authority-delete-permission-request")
if user.has_perm("authority.delete_permission"):
link_kwargs["is_requestor"] = False
return link_kwargs
if not perm.approved and perm.user == user:
link_kwargs['is_requestor'] = True
link_kwargs["is_requestor"] = True
return link_kwargs
return {'url': None}
return {"url": None}
@register.inclusion_tag('authority/permission_request_approve_link.html', takes_context=True)
@register.inclusion_tag(
"authority/permission_request_approve_link.html", takes_context=True
)
def permission_request_approve_link(context, perm):
"""
Renders a html link to the approve view of the given permission request.
Returns no content if the request-user has no permission to delete foreign
permissions.
"""
user = context['request'].user
if user.is_authenticated():
if user.has_perm('authority.approve_permission_requests'):
return base_link(context, perm, 'authority-approve-permission-request')
return {'url': None}
user = context["request"].user
if user.is_authenticated:
if user.has_perm("authority.approve_permission_requests"):
return base_link(context, perm, "authority-approve-permission-request")
return {"url": None}

View file

@ -16,18 +16,23 @@ from authority.forms import UserPermissionForm # noqa
User = get_user_model()
FIXTURES = ['tests_custom.json']
FIXTURES = ["tests_custom.json"]
QUERY = Q(email="jezdez@github.com")
class UserPermission(permissions.BasePermission):
checks = ('browse',)
label = 'user_permission'
checks = ("browse",)
label = "user_permission"
authority.utils.register(User, UserPermission)
class GroupPermission(permissions.BasePermission):
checks = ('browse',)
label = 'group_permission'
checks = ("browse",)
label = "group_permission"
authority.utils.register(Group, GroupPermission)
@ -43,6 +48,7 @@ class DjangoPermissionChecksTestCase(TestCase):
This permissions are given in the test case and not in the fixture, for
later reference.
"""
fixtures = FIXTURES
def setUp(self):
@ -56,7 +62,7 @@ class DjangoPermissionChecksTestCase(TestCase):
def test_add(self):
# setup
perm = DjangoPermission.objects.get(codename='add_user')
perm = DjangoPermission.objects.get(codename="add_user")
self.user.user_permissions.add(perm)
# test
@ -66,8 +72,8 @@ class DjangoPermissionChecksTestCase(TestCase):
perm = Permission(
user=self.user,
content_object=self.user,
codename='user_permission.delete_user',
approved=True
codename="user_permission.delete_user",
approved=True,
)
perm.save()
@ -83,6 +89,7 @@ class AssignBehaviourTest(TestCase):
- permission delete_user for him (test_delete),
- all existing codenames permissions: a/b/c/d (test_all),
"""
fixtures = FIXTURES
def setUp(self):
@ -90,16 +97,13 @@ class AssignBehaviourTest(TestCase):
self.check = UserPermission(self.user)
def test_add(self):
result = self.check.assign(check='add_user')
result = self.check.assign(check="add_user")
self.assertTrue(isinstance(result[0], DjangoPermission))
self.assertTrue(self.check.add_user())
def test_delete(self):
result = self.check.assign(
content_object=self.user,
check='delete_user',
)
result = self.check.assign(content_object=self.user, check="delete_user",)
self.assertTrue(isinstance(result[0], Permission))
self.assertFalse(self.check.delete_user())
@ -120,6 +124,7 @@ class GenericAssignBehaviourTest(TestCase):
- permission add (test_add),
- permission delete for him (test_delete),
"""
fixtures = FIXTURES
def setUp(self):
@ -127,16 +132,14 @@ class GenericAssignBehaviourTest(TestCase):
self.check = UserPermission(self.user)
def test_add(self):
result = self.check.assign(check='add', generic=True)
result = self.check.assign(check="add", generic=True)
self.assertTrue(isinstance(result[0], DjangoPermission))
self.assertTrue(self.check.add_user())
def test_delete(self):
result = self.check.assign(
content_object=self.user,
check='delete',
generic=True,
content_object=self.user, check="delete", generic=True,
)
self.assertTrue(isinstance(result[0], Permission))
@ -149,6 +152,7 @@ class AssignExceptionsTest(TestCase):
Tests that exceptions are thrown if assign() was called with inconsistent
arguments.
"""
fixtures = FIXTURES
def setUp(self):
@ -164,7 +168,7 @@ class AssignExceptionsTest(TestCase):
def test_not_model_content_object(self):
try:
self.check.assign(content_object='fail')
self.check.assign(content_object="fail")
except NotAModel:
return True
self.fail()
@ -174,6 +178,7 @@ class SmartCachingTestCase(TestCase):
"""
The base test case for all tests that have to do with smart caching.
"""
fixtures = FIXTURES
def setUp(self):
@ -198,21 +203,14 @@ class SmartCachingTestCase(TestCase):
# This is what the old, pre-cache system would check to see if a user
# had a given permission.
return Permission.objects.user_permissions(
self.user,
'foo',
self.user,
approved=True,
check_groups=True,
self.user, "foo", self.user, approved=True, check_groups=True,
)
def _old_group_permission_check(self):
# This is what the old, pre-cache system would check to see if a user
# had a given permission.
return Permission.objects.group_permissions(
self.group,
'foo',
self.group,
approved=True,
self.group, "foo", self.group, approved=True,
)
@ -237,20 +235,13 @@ class PerformanceTest(SmartCachingTestCase):
for _ in range(5):
# Need to assert it so the query actually gets executed.
assert not self.user_check.has_user_perms(
'foo',
self.user,
True,
False,
"foo", self.user, True, False,
)
def test_group_has_perms(self):
with self.assertNumQueries(2):
for _ in range(5):
assert not self.group_check.has_group_perms(
'foo',
self.group,
True,
)
assert not self.group_check.has_group_perms("foo", self.group, True,)
def test_has_user_perms_check_group(self):
# Regardless of the number groups permissions, it should only take one
@ -258,10 +249,7 @@ class PerformanceTest(SmartCachingTestCase):
# Content type and permissions (2 queries)
with self.assertNumQueries(3):
self.user_check.has_user_perms(
'foo',
self.user,
approved=True,
check_groups=True,
"foo", self.user, approved=True, check_groups=True,
)
def test_invalidate_user_permissions_cache(self):
@ -273,10 +261,7 @@ class PerformanceTest(SmartCachingTestCase):
with self.assertNumQueries(6):
for _ in range(5):
assert not self.user_check.has_user_perms(
'foo',
self.user,
True,
False,
"foo", self.user, True, False,
)
# Invalidate the cache to show that a query will be generated when
@ -287,10 +272,7 @@ class PerformanceTest(SmartCachingTestCase):
# One query to re generate the cache.
for _ in range(5):
assert not self.user_check.has_user_perms(
'foo',
self.user,
True,
False,
"foo", self.user, True, False,
)
def test_invalidate_group_permissions_cache(self):
@ -300,11 +282,7 @@ class PerformanceTest(SmartCachingTestCase):
# will need to do one query to get content type and one to get
with self.assertNumQueries(4):
for _ in range(5):
assert not self.group_check.has_group_perms(
'foo',
self.group,
True,
)
assert not self.group_check.has_group_perms("foo", self.group, True,)
# Invalidate the cache to show that a query will be generated when
# checking perms again.
@ -313,18 +291,14 @@ class PerformanceTest(SmartCachingTestCase):
# One query to re generate the cache.
for _ in range(5):
assert not self.group_check.has_group_perms(
'foo',
self.group,
True,
)
assert not self.group_check.has_group_perms("foo", self.group, True,)
def test_has_user_perms_check_group_multiple(self):
# Create a permission with just a group.
Permission.objects.create(
content_type=Permission.objects.get_content_type(User),
object_id=self.user.pk,
codename='foo',
codename="foo",
group=self.group,
approved=True,
)
@ -333,17 +307,17 @@ class PerformanceTest(SmartCachingTestCase):
# Check the number of queries.
with self.assertNumQueries(2):
assert self.user_check.has_user_perms('foo', self.user, True, True)
assert self.user_check.has_user_perms("foo", self.user, True, True)
# Create a second group.
new_group = Group.objects.create(name='new_group')
new_group = Group.objects.create(name="new_group")
new_group.user_set.add(self.user)
# Create a permission object for it.
Permission.objects.create(
content_type=Permission.objects.get_content_type(User),
object_id=self.user.pk,
codename='foo',
codename="foo",
group=new_group,
approved=True,
)
@ -352,7 +326,7 @@ class PerformanceTest(SmartCachingTestCase):
# Make sure it is the same number of queries.
with self.assertNumQueries(2):
assert self.user_check.has_user_perms('foo', self.user, True, True)
assert self.user_check.has_user_perms("foo", self.user, True, True)
class GroupPermissionCacheTestCase(SmartCachingTestCase):
@ -367,10 +341,7 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
# Use the new cached user perms to show that the user does not have the
# perms.
can_foo_with_group = self.user_check.has_user_perms(
'foo',
self.user,
approved=True,
check_groups=True,
"foo", self.user, approved=True, check_groups=True,
)
self.assertFalse(can_foo_with_group)
@ -378,7 +349,7 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
perm = Permission.objects.create(
content_type=Permission.objects.get_content_type(User),
object_id=self.user.pk,
codename='foo',
codename="foo",
group=self.group,
approved=True,
)
@ -390,10 +361,7 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
# Invalidate the cache.
self.user_check.invalidate_permissions_cache()
can_foo_with_group = self.user_check.has_user_perms(
'foo',
self.user,
approved=True,
check_groups=True,
"foo", self.user, approved=True, check_groups=True,
)
self.assertTrue(can_foo_with_group)
@ -401,9 +369,7 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
# Make sure calling has_user_perms on a permission that does not have a
# user does not throw any errors.
can_foo_with_group = self.group_check.has_group_perms(
'foo',
self.user,
approved=True,
"foo", self.user, approved=True,
)
self.assertFalse(can_foo_with_group)
@ -414,7 +380,7 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
perm = Permission.objects.create(
content_type=Permission.objects.get_content_type(Group),
object_id=self.group.pk,
codename='foo',
codename="foo",
group=self.group,
approved=True,
)
@ -427,8 +393,6 @@ class GroupPermissionCacheTestCase(SmartCachingTestCase):
self.group_check.invalidate_permissions_cache()
can_foo_with_group = self.group_check.has_group_perms(
'foo',
self.group,
approved=True,
"foo", self.group, approved=True,
)
self.assertTrue(can_foo_with_group)

View file

@ -1,26 +1,40 @@
from django.conf.urls import url
from authority.views import add_permission, delete_permission, approve_permission_request, \
delete_permission
from authority.views import (
add_permission,
delete_permission,
approve_permission_request,
delete_permission,
)
urlpatterns = [
url(r'^permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
url(
r"^permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$",
view=add_permission,
name="authority-add-permission",
kwargs={'approved': True}),
url(r'^permission/delete/(?P<permission_pk>\d+)/$',
kwargs={"approved": True},
),
url(
r"^permission/delete/(?P<permission_pk>\d+)/$",
view=delete_permission,
name="authority-delete-permission",
kwargs={'approved': True}),
url(r'^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
kwargs={"approved": True},
),
url(
r"^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$",
view=add_permission,
name="authority-add-permission-request",
kwargs={'approved': False}),
url(r'^request/approve/(?P<permission_pk>\d+)/$',
kwargs={"approved": False},
),
url(
r"^request/approve/(?P<permission_pk>\d+)/$",
view=approve_permission_request,
name="authority-approve-permission-request"),
url(r'^request/delete/(?P<permission_pk>\d+)/$',
name="authority-approve-permission-request",
),
url(
r"^request/delete/(?P<permission_pk>\d+)/$",
view=delete_permission,
name="authority-delete-permission-request",
kwargs={'approved': False}),
]
kwargs={"approved": False},
),
]

View file

@ -1,3 +1,7 @@
import sys
from authority.sites import site, get_check, get_choices_for, register, unregister # noqa
from authority.sites import (
site,
get_check,
get_choices_for,
register,
unregister,
) # noqa

View file

@ -11,54 +11,70 @@ from authority.templatetags.permissions import url_for_obj
def get_next(request, obj=None):
next = request.REQUEST.get('next')
next = request.REQUEST.get("next")
if not next:
if obj and hasattr(obj, 'get_absolute_url'):
if obj and hasattr(obj, "get_absolute_url"):
next = obj.get_absolute_url()
else:
next = '/'
next = "/"
return next
@login_required
def add_permission(request, app_label, module_name, pk, approved=False,
template_name='authority/permission_form.html',
extra_context=None, form_class=UserPermissionForm):
codename = request.POST.get('codename', None)
model = apps.get_model(app_label, module_name)
if model is None:
def add_permission(
request,
app_label,
module_name,
pk,
approved=False,
template_name="authority/permission_form.html",
extra_context=None,
form_class=UserPermissionForm,
):
codename = request.POST.get("codename", None)
try:
model = apps.get_model(app_label, module_name)
except LookupError:
return permission_denied(request)
obj = get_object_or_404(model, pk=pk)
next = get_next(request, obj)
if approved:
if not request.user.has_perm('authority.add_permission'):
if not request.user.has_perm("authority.add_permission"):
return HttpResponseRedirect(
url_for_obj('authority-add-permission-request', obj))
view_name = 'authority-add-permission'
url_for_obj("authority-add-permission-request", obj)
)
view_name = "authority-add-permission"
else:
view_name = 'authority-add-permission-request'
if request.method == 'POST':
view_name = "authority-add-permission-request"
if request.method == "POST":
if codename is None:
return HttpResponseForbidden(next)
form = form_class(data=request.POST, obj=obj, approved=approved,
perm=codename, initial=dict(codename=codename))
form = form_class(
data=request.POST,
obj=obj,
approved=approved,
perm=codename,
initial=dict(codename=codename),
)
if not approved:
# Limit permission request to current user
form.data['user'] = request.user
form.data["user"] = request.user
if form.is_valid():
form.save(request)
request.user.message_set.create(
message=_('You added a permission request.'))
message=_("You added a permission request.")
)
return HttpResponseRedirect(next)
else:
form = form_class(obj=obj, approved=approved, perm=codename,
initial=dict(codename=codename))
form = form_class(
obj=obj, approved=approved, perm=codename, initial=dict(codename=codename)
)
context = {
'form': form,
'form_url': url_for_obj(view_name, obj),
'next': next,
'perm': codename,
'approved': approved,
"form": form,
"form_url": url_for_obj(view_name, obj),
"next": next,
"perm": codename,
"approved": approved,
}
if extra_context:
context.update(extra_context)
@ -68,25 +84,27 @@ def add_permission(request, app_label, module_name, pk, approved=False,
@login_required
def approve_permission_request(request, permission_pk):
requested_permission = get_object_or_404(Permission, pk=permission_pk)
if request.user.has_perm('authority.approve_permission_requests'):
if request.user.has_perm("authority.approve_permission_requests"):
requested_permission.approve(request.user)
request.user.message_set.create(
message=_('You approved the permission request.'))
message=_("You approved the permission request.")
)
next = get_next(request, requested_permission)
return HttpResponseRedirect(next)
@login_required
def delete_permission(request, permission_pk, approved):
permission = get_object_or_404(Permission, pk=permission_pk,
approved=approved)
if (request.user.has_perm('authority.delete_foreign_permissions') or
request.user == permission.creator):
permission = get_object_or_404(Permission, pk=permission_pk, approved=approved)
if (
request.user.has_perm("authority.delete_foreign_permissions")
or request.user == permission.creator
):
permission.delete()
if approved:
msg = _('You removed the permission.')
msg = _("You removed the permission.")
else:
msg = _('You removed the permission request.')
msg = _("You removed the permission request.")
request.user.message_set.create(message=msg)
next = get_next(request)
return HttpResponseRedirect(next)
@ -102,11 +120,14 @@ def permission_denied(request, template_name=None, extra_context=None):
The path of the requested URL (e.g., '/app/pages/bad_page/')
"""
if template_name is None:
template_name = ('403.html', 'authority/403.html')
template_name = ("403.html", "authority/403.html")
context = {
'request_path': request.path,
"request_path": request.path,
}
if extra_context:
context.update(extra_context)
return HttpResponseForbidden(loader.render_to_string(template_name,
context, request))
return HttpResponseForbidden(
loader.render_to_string(
template_name=template_name, context=context, request=request,
)
)

View file

@ -28,14 +28,14 @@ class GenericForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
def render(self, name, value, attrs=None):
if attrs is None:
attrs = {}
related_url = '../../../'
related_url = "../../../"
params = self.url_parameters()
if params:
url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.iteritems()])
url = "?" + "&amp;".join(["%s=%s" % (k, v) for k, v in params.iteritems()])
else:
url = ''
if 'class' not in attrs:
attrs['class'] = 'vForeignKeyRawIdAdminField'
url = ""
if "class" not in attrs:
attrs["class"] = "vForeignKeyRawIdAdminField"
output = [forms.TextInput.render(self, name, value, attrs)]
output.append(
"""%(generic_script)s
@ -44,30 +44,41 @@ class GenericForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
id="lookup_id_%(name)s"
onclick="return showGenericRelatedObjectLookupPopup(
document.getElementById('id_%(ct_field)s'), this, '%(related)s%(url)s');">
""" % {
'generic_script': generic_script,
'related': related_url,
'url': url,
'name': name,
'ct_field': self.ct_field
})
"""
% {
"generic_script": generic_script,
"related": related_url,
"url": url,
"name": name,
"ct_field": self.ct_field,
}
)
output.append(
'<img src="%s/admin/img/selector-search.gif" width="16" height="16" alt="%s" /></a>'
% (settings.STATIC_URL, _('Lookup')))
% (settings.STATIC_URL, _("Lookup"))
)
from django.contrib.contenttypes.models import ContentType
content_types = """
<script type="text/javascript">
var content_types = new Array();
%s
</script>
""" % ('\n'.join([
"content_types[%s] = '%s/%s/';" % (
ContentType.objects.get_for_model(ct).id,
ct._meta.app_label,
ct._meta.object_name.lower()
) for ct in self.cts]))
return mark_safe(u''.join(output) + content_types)
""" % (
"\n".join(
[
"content_types[%s] = '%s/%s/';"
% (
ContentType.objects.get_for_model(ct).id,
ct._meta.app_label,
ct._meta.object_name.lower(),
)
for ct in self.cts
]
)
)
return mark_safe(u"".join(output) + content_types)
def url_parameters(self):
return {}

View file

@ -13,6 +13,7 @@
import os
import sys
from pkg_resources import get_distribution
# 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
@ -40,17 +41,16 @@ master_doc = 'index'
# General information about the project.
project = u'django-authority'
copyright = u'2009, the django-authority team'
copyright = u'2009-2019, Jannis Leidel'
# 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 = '0.9'
# The full version, including alpha/beta/rc tags.
release = '0.9dev'
release = get_distribution('django-authority').version
# The short X.Y version.
version = '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View file

@ -1,4 +1,4 @@
from example.settings import *
DEBUG=True
TEMPLATE_DEBUG=DEBUG
DEBUG = True
TEMPLATE_DEBUG = DEBUG

View file

@ -3,5 +3,6 @@ from django.utils.translation import ugettext_lazy as _
from authority.forms import UserPermissionForm
class SpecialUserPermissionForm(UserPermissionForm):
user = forms.CharField(label=_('Special user'), widget=forms.Textarea())
user = forms.CharField(label=_("Special user"), widget=forms.Textarea())

View file

@ -4,10 +4,11 @@ from django.utils.translation import ugettext_lazy as _
import authority
from authority.permissions import BasePermission
class FlatPagePermission(BasePermission):
"""
This class contains a bunch of checks:
1. the default checks 'add_flatpage', 'browse_flatpage',
'change_flatpage' and 'delete_flatpage'
2. the custom checks:
@ -40,13 +41,16 @@ class FlatPagePermission(BasePermission):
{% endifhasperm %}
"""
label = 'flatpage_permission'
checks = ('review', 'top_secret')
label = "flatpage_permission"
checks = ("review", "top_secret")
def top_secret(self, flatpage=None, lala=None):
if flatpage and flatpage.registration_required:
return self.browse_flatpage(obj=flatpage)
return False
top_secret.short_description=_('Is allowed to see top secret flatpages')
authority.register(FlatPage, FlatPagePermission)
top_secret.short_description = _("Is allowed to see top secret flatpages")
authority.sites.register(FlatPage, FlatPagePermission)

View file

@ -1,23 +1,19 @@
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".
Replace these with more appropriate tests for your application.
"""
from django.urls import reverse
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
from authority.compat import get_user_model
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}
class AddPermissionTestCase(TestCase):
def test_add_permission_permission_denied_is_403(self):
user = get_user_model().objects.create(username="foo", email="foo@example.com",)
user.set_password("pw")
user.save()
assert self.client.login(username="foo@example.com", password="pw")
url = reverse(
"authority-add-permission-request",
kwargs={"app_label": "foo", "module_name": "Bar", "pk": 1,},
)
r = self.client.get(url)
self.assertEqual(r.status_code, 403)

View file

@ -8,8 +8,9 @@ from authority.decorators import permission_required, permission_required_or_403
# @permission_required('flatpage_permission.top_secret',
# (FlatPage, 'url__contains', 'url'), (FlatPage, 'url__contains', 'lala'))
# use this to return a 403 page:
@permission_required_or_403('flatpage_permission.top_secret',
(FlatPage, 'url__contains', 'url'), 'lala')
@permission_required_or_403(
"flatpage_permission.top_secret", (FlatPage, "url__contains", "url"), "lala"
)
def top_secret(request, url, lala=None):
"""
A wrapping view that performs the permission check given in the decorator

View file

@ -7,13 +7,16 @@ from django.core.management import execute_from_command_line
try:
import settings as settings_mod # Assumed to be in the same directory.
except ImportError:
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.stderr.write(
"Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n"
% __file__
)
sys.exit(1)
sys.path.insert(0, settings_mod.PROJECT_ROOT)
sys.path.insert(0, settings_mod.PROJECT_ROOT + '/../')
sys.path.insert(0, settings_mod.PROJECT_ROOT + "/../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", 'example.settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
if __name__ == "__main__":
execute_from_command_line(sys.argv)

View file

@ -1,2 +1 @@
from example.settings import *

View file

@ -11,90 +11,93 @@ ADMINS = (
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(PROJECT_ROOT, 'example.db'),
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(PROJECT_ROOT, "example.db"),
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
"TEST": {"NAME": ":memory:", "ENGINE": "django.db.backends.sqlite3",},
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
CACHES = {"default": {"BACKEND": "django.core.cache.backends.dummy.DummyCache",}}
TIME_ZONE = 'America/Chicago'
TIME_ZONE = "America/Chicago"
LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = "en-us"
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media')
MEDIA_ROOT = os.path.join(PROJECT_ROOT, "media")
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = '/media/'
MEDIA_URL = "/media/"
# Don't share this with anybody.
SECRET_KEY = 'ljlv2lb2d&)#by6th=!v=03-c^(o4lop92i@z4b3f1&ve0yx6d'
SECRET_KEY = "ljlv2lb2d&)#by6th=!v=03-c^(o4lop92i@z4b3f1&ve0yx6d"
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
MIDDLEWARE = (
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
# 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
)
INTERNAL_IPS = ('127.0.0.1',)
INTERNAL_IPS = ("127.0.0.1",)
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.request',
)
TEMPLATE_CONTEXT_PROCESSORS = ()
ROOT_URLCONF = 'example.urls'
ROOT_URLCONF = "example.urls"
SITE_ID = 1
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.flatpages',
'django.contrib.admin',
'authority',
'example.exampleapp',
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.flatpages",
"django.contrib.messages",
"django.contrib.admin",
"authority",
"example.exampleapp",
)
if VERSION >= (1, 5):
INSTALLED_APPS = INSTALLED_APPS + ('example.users',)
AUTH_USER_MODEL = 'users.User'
INSTALLED_APPS = INSTALLED_APPS + ("example.users",)
AUTH_USER_MODEL = "users.User"
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
)
TEMPLATE_DIRS = (
os.path.join(PROJECT_ROOT, "templates"),
)
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
# insert your TEMPLATE_DIRS here
],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
# Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this
# list if you haven't customized them:
"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",
],
},
},
]
# Use local_settings.py for things to override privately
try:
from local_settings import * # noqa
except ImportError:
pass
if VERSION >= (1, 6):
TEST_RUNNER = 'django.test.runner.DiscoverRunner'

View file

@ -1,39 +1,44 @@
try:
from django.conf.urls import patterns, include, handler500, url
except ImportError: # django < 1.4
from django.conf.urls.defaults import patterns, include, handler500, url
import django.contrib.auth.views
from django.conf.urls import include, handler500, url
from django.conf import settings
from django.contrib import admin
import authority
admin.autodiscover()
authority.autodiscover()
handler500 # Pyflakes
import authority.views
import authority.urls
import example.exampleapp.views
from exampleapp.forms import SpecialUserPermissionForm
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
#('^admin/', include(admin.site.urls)),
url(r'^authority/permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
view='authority.views.add_permission',
authority.autodiscover()
handler500 # flake8
urlpatterns = (
url(
r"^authority/permission/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$", # noqa
view=authority.views.add_permission,
name="authority-add-permission",
kwargs={'approved': True, 'form_class': SpecialUserPermissionForm}
kwargs={"approved": True, "form_class": SpecialUserPermissionForm},
),
url(r'^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$',
view='authority.views.add_permission',
url(
r"^request/add/(?P<app_label>[\w\-]+)/(?P<module_name>[\w\-]+)/(?P<pk>\d+)/$", # noqa
view=authority.views.add_permission,
name="authority-add-permission-request",
kwargs={'approved': False, 'form_class': SpecialUserPermissionForm}
kwargs={"approved": False, "form_class": SpecialUserPermissionForm},
),
url(r"^authority/", include(authority.urls)),
url(r"^accounts/login/$", django.contrib.auth.views.LoginView.as_view()),
url(
r"^(?P<url>[\/0-9A-Za-z]+)$",
example.exampleapp.views.top_secret,
{"lala": "oh yeah!"},
),
(r'^authority/', include('authority.urls')),
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
url(r'^(?P<url>[\/0-9A-Za-z]+)$', 'example.exampleapp.views.top_secret', {'lala': 'oh yeah!'}),
)
if settings.DEBUG:
urlpatterns += patterns('',
(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
'document_root': settings.MEDIA_ROOT,
}),
urlpatterns += (
url(
r"^media/(?P<path>.*)$",
django.views.static.serve,
{"document_root": settings.MEDIA_ROOT,},
),
)

View file

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.22 on 2019-07-12 16:01
from __future__ import unicode_literals
import django.contrib.auth.models
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
("auth", "0008_alter_user_username_max_length"),
]
operations = [
migrations.CreateModel(
name="User",
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"
),
),
(
"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(max_length=100)),
("first_name", models.CharField(max_length=50)),
("last_name", models.CharField(max_length=50)),
("email", models.EmailField(max_length=254, unique=True)),
("greeting_message", models.TextField()),
("is_staff", models.BooleanField(default=False)),
("is_active", models.BooleanField(default=True)),
(
"date_joined",
models.DateTimeField(default=django.utils.timezone.now),
),
(
"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,},
managers=[("objects", django.contrib.auth.models.UserManager()),],
),
]

View file

View file

@ -1,12 +1,14 @@
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.contrib.auth.models import UserManager
from django.db import models
from django.utils import timezone
class User(AbstractBaseUser, PermissionsMixin):
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["first_name", "last_name"]
username = models.CharField(max_length=100)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
@ -14,3 +16,5 @@ class User(AbstractBaseUser, PermissionsMixin):
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
objects = UserManager()

View file

@ -5,42 +5,36 @@ from setuptools import setup, find_packages
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
name='django-authority',
version='0.11',
name="django-authority",
use_scm_version=True,
description=(
"A Django app that provides generic per-object-permissions "
"for Django's auth app."
),
long_description=read('README.rst'),
author='Jannis Leidel',
author_email='jannis@leidel.info',
license='BSD',
url='https://github.com/jazzband/django-authority/',
packages=find_packages(),
long_description=read("README.rst"),
long_description_content_type="text/x-rst",
author="Jannis Leidel",
author_email="jannis@leidel.info",
license="BSD",
url="https://github.com/jazzband/django-authority/",
packages=find_packages(exclude=("example", "example.*")),
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Framework :: Django',
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Framework :: Django",
],
install_requires=['django'],
package_data = {
'authority': [
'fixtures/*.json',
'templates/authority/*.html',
'templates/admin/edit_inline/action_tabular.html',
'templates/admin/permission_change_form.html',
]
},
install_requires=["django"],
setup_requires=["setuptools_scm"],
include_package_data=True,
zip_safe=False,
)

28
tox.ini
View file

@ -3,17 +3,25 @@ skipsdist = True
usedevelop = True
minversion = 1.8
envlist =
py{27,33,34,35}-dj{18,19,110}
py27-dj111
py37-dj{111,22}
py37-check
[testenv]
basepython =
py27: python2.7
py33: python3.3
py34: python3.4
py35: python3.5
usedevelop = true
commands = python example/manage.py test authority
commands =
coverage run -a example/manage.py test authority exampleapp
coverage report
deps =
dj18: Django<1.9
dj19: Django<1.10
dj110: Django<1.11
coverage
dj111: Django>=1.11,<2.0
dj22: Django>=2.2,<2.3
[testenv:py37-check]
deps =
twine
wheel
commands =
python setup.py sdist bdist_wheel
twine check dist/*