Add class wide permissions and clean up migrations

This commit is contained in:
Skia 2015-12-09 10:33:55 +01:00
parent e3cbe70319
commit 77803596fb
22 changed files with 80 additions and 515 deletions

View File

@ -2,61 +2,107 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
import django.contrib.auth.models
import django.core.validators
import django.utils.timezone import django.utils.timezone
import django.core.validators
import django.db.models.deletion
from django.conf import settings
import django.contrib.auth.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('auth', '0006_require_contenttypes_0002'), ('auth', '__first__'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='User', name='User',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)), ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
('password', models.CharField(verbose_name='password', max_length=128)), ('password', models.CharField(verbose_name='password', max_length=128)),
('last_login', models.DateTimeField(verbose_name='last login', blank=True, null=True)), ('last_login', models.DateTimeField(null=True, blank=True, verbose_name='last login')),
('is_superuser', models.BooleanField(verbose_name='superuser status', help_text='Designates that this user has all permissions without explicitly assigning them.', default=False)), ('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', default=False, verbose_name='superuser status')),
('username', models.CharField(help_text='Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username', error_messages={'unique': 'A user with that username already exists.'}, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], max_length=254)), ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 254 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=254, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], unique=True, verbose_name='username')),
('first_name', models.CharField(verbose_name='first name', max_length=30)), ('first_name', models.CharField(verbose_name='first name', max_length=30)),
('last_name', models.CharField(verbose_name='last name', max_length=30)), ('last_name', models.CharField(verbose_name='last name', max_length=30)),
('email', models.EmailField(verbose_name='email address', max_length=254, unique=True)), ('email', models.EmailField(unique=True, verbose_name='email address', max_length=254)),
('date_of_birth', models.DateTimeField(verbose_name='date of birth')), ('date_of_birth', models.DateTimeField(verbose_name='date of birth')),
('nick_name', models.CharField(blank=True, max_length=30)), ('nick_name', models.CharField(blank=True, max_length=30)),
('is_staff', models.BooleanField(verbose_name='staff status', help_text='Designates whether the user can log into this admin site.', default=False)), ('is_staff', models.BooleanField(help_text='Designates whether the user can log into this admin site.', default=False, verbose_name='staff status')),
('is_active', models.BooleanField(verbose_name='active', help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', default=True)), ('is_active', models.BooleanField(help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', default=True, verbose_name='active')),
('date_joined', models.DateTimeField(verbose_name='date joined', default=django.utils.timezone.now)), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(related_name='user_set', related_query_name='user', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups', blank=True, to='auth.Group')),
('user_permissions', models.ManyToManyField(related_name='user_set', related_query_name='user', help_text='Specific permissions for this user.', verbose_name='user permissions', blank=True, to='auth.Permission')),
], ],
options={ options={
'verbose_name': 'user',
'verbose_name_plural': 'users', 'verbose_name_plural': 'users',
'permissions': (('change_prop_user', "Can change the user's properties (groups, ...)"), ('view_user', "Can view user's profile")),
'verbose_name': 'user',
}, },
managers=[ managers=[
('objects', django.contrib.auth.models.UserManager()), ('objects', django.contrib.auth.models.UserManager()),
], ],
), ),
migrations.CreateModel(
name='Group',
fields=[
('group_ptr', models.OneToOneField(primary_key=True, to='auth.Group', parent_link=True, serialize=False, auto_created=True)),
],
bases=('auth.group',),
),
migrations.CreateModel( migrations.CreateModel(
name='Page', name='Page',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)), ('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
('name', models.CharField(verbose_name='page name', max_length=30)), ('name', models.CharField(verbose_name='page name', max_length=30)),
('title', models.CharField(verbose_name='page title', blank=True, max_length=255)), ('full_name', models.CharField(blank=True, verbose_name='page name', max_length=255)),
('content', models.TextField(verbose_name='page content', blank=True)), ('edit_group', models.ManyToManyField(blank=True, to='core.Group', related_name='editable_page')),
('revision', models.PositiveIntegerField(verbose_name='current revision', default=1)), ('owner_group', models.ForeignKey(to='core.Group', default=1, related_name='owned_page')),
('is_locked', models.BooleanField(verbose_name='page mutex', default=False)), ('parent', models.ForeignKey(null=True, to='core.Page', on_delete=django.db.models.deletion.SET_NULL, blank=True, related_name='children')),
('parent', models.ForeignKey(related_name='children', to='core.Page', blank=True, on_delete=django.db.models.deletion.SET_NULL, null=True)), ('view_group', models.ManyToManyField(blank=True, to='core.Group', related_name='viewable_page')),
], ],
options={ options={
'permissions': (('can_edit', 'Can edit the page'), ('can_view', 'Can view the page')), 'permissions': (('change_prop_page', "Can change the page's properties (groups, ...)"), ('view_page', 'Can view the page')),
}, },
), ),
migrations.CreateModel(
name='PageRev',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
('title', models.CharField(blank=True, verbose_name='page title', max_length=255)),
('content', models.TextField(blank=True, verbose_name='page content')),
('date', models.DateTimeField(verbose_name='date', auto_now=True)),
('author', models.ForeignKey(related_name='page_rev', to=settings.AUTH_USER_MODEL)),
('page', models.ForeignKey(related_name='revisions', to='core.Page')),
],
options={
'ordering': ['date'],
},
),
migrations.AddField(
model_name='user',
name='edit_group',
field=models.ManyToManyField(blank=True, to='core.Group', related_name='editable_user'),
),
migrations.AddField(
model_name='user',
name='groups',
field=models.ManyToManyField(help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', to='auth.Group', blank=True, verbose_name='groups', related_query_name='user', related_name='user_set'),
),
migrations.AddField(
model_name='user',
name='owner_group',
field=models.ForeignKey(to='core.Group', default=1, related_name='owned_user'),
),
migrations.AddField(
model_name='user',
name='user_permissions',
field=models.ManyToManyField(help_text='Specific permissions for this user.', to='auth.Permission', blank=True, verbose_name='user permissions', related_query_name='user', related_name='user_set'),
),
migrations.AddField(
model_name='user',
name='view_group',
field=models.ManyToManyField(blank=True, to='core.Group', related_name='viewable_user'),
),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='page', name='page',
unique_together=set([('name', 'parent')]), unique_together=set([('name', 'parent')]),

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='page',
name='full_name',
field=models.CharField(blank=True, verbose_name='page name', max_length=255),
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_page_full_name'),
]
operations = [
migrations.AlterModelOptions(
name='page',
options={'permissions': ()},
),
]

View File

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0006_require_contenttypes_0002'),
('core', '0003_auto_20151124_1021'),
]
operations = [
migrations.AddField(
model_name='page',
name='edit_group',
field=models.ForeignKey(null=True, to='auth.Group', related_name='editable_pages'),
),
migrations.AddField(
model_name='page',
name='owner_group',
field=models.ForeignKey(null=True, to='auth.Group', related_name='owned_pages'),
),
migrations.AddField(
model_name='page',
name='view_group',
field=models.ForeignKey(null=True, to='auth.Group', related_name='viewable_pages'),
),
]

View File

@ -1,29 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_auto_20151124_1033'),
]
operations = [
migrations.AlterField(
model_name='page',
name='edit_group',
field=models.ForeignKey(related_name='editable_pages', to='auth.Group', default=1),
),
migrations.AlterField(
model_name='page',
name='owner_group',
field=models.ForeignKey(related_name='owned_pages', to='auth.Group', default=1),
),
migrations.AlterField(
model_name='page',
name='view_group',
field=models.ForeignKey(related_name='viewable_pages', to='auth.Group', default=1),
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_auto_20151124_1219'),
]
operations = [
migrations.AlterModelOptions(
name='page',
options={'permissions': (('can_view', 'Can view the page'),)},
),
]

View File

@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0006_require_contenttypes_0002'),
('core', '0006_auto_20151125_0855'),
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
('group_ptr', models.OneToOneField(auto_created=True, to='auth.Group', primary_key=True, serialize=False, parent_link=True)),
],
bases=('auth.group',),
),
migrations.AlterField(
model_name='page',
name='edit_group',
field=models.ForeignKey(default=1, to='core.Group', related_name='editable_pages'),
),
migrations.AlterField(
model_name='page',
name='owner_group',
field=models.ForeignKey(default=1, to='core.Group', related_name='owned_pages'),
),
migrations.AlterField(
model_name='page',
name='view_group',
field=models.ForeignKey(default=1, to='core.Group', related_name='viewable_pages'),
),
]

View File

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_auto_20151126_1613'),
]
operations = [
migrations.RemoveField(
model_name='page',
name='view_group',
),
migrations.AddField(
model_name='page',
name='view_group',
field=models.ManyToManyField(default=1, to='core.Group', related_name='viewable_pages'),
),
]

View File

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0008_auto_20151127_1007'),
]
operations = [
migrations.RemoveField(
model_name='page',
name='edit_group',
),
migrations.AddField(
model_name='page',
name='edit_group',
field=models.ManyToManyField(to='core.Group', related_name='editable_pages', default=1),
),
]

View File

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0009_auto_20151127_1007'),
]
operations = [
migrations.RemoveField(
model_name='page',
name='edit_group',
),
migrations.RemoveField(
model_name='page',
name='owner_group',
),
migrations.RemoveField(
model_name='page',
name='view_group',
),
]

View File

@ -1,29 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0010_auto_20151127_1503'),
]
operations = [
migrations.AddField(
model_name='page',
name='edit_group',
field=models.ManyToManyField(to='core.Group', related_name='editable_object', null=True),
),
migrations.AddField(
model_name='page',
name='owner_group',
field=models.ForeignKey(related_name='owned_object', default=1, to='core.Group'),
),
migrations.AddField(
model_name='page',
name='view_group',
field=models.ManyToManyField(to='core.Group', related_name='viewable_object', null=True),
),
]

View File

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0011_auto_20151127_1504'),
]
operations = [
migrations.AlterField(
model_name='page',
name='edit_group',
field=models.ManyToManyField(related_name='editable_object', to='core.Group'),
),
migrations.AlterField(
model_name='page',
name='view_group',
field=models.ManyToManyField(related_name='viewable_object', to='core.Group'),
),
]

View File

@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('core', '0012_auto_20151127_1504'),
]
operations = [
migrations.CreateModel(
name='PageRev',
fields=[
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, blank=True, verbose_name='page title')),
('content', models.TextField(blank=True, verbose_name='page content')),
('date', models.DateTimeField(auto_now=True, verbose_name='date')),
('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='page_rev')),
],
options={
'ordering': ['date'],
},
),
migrations.RemoveField(
model_name='page',
name='content',
),
migrations.RemoveField(
model_name='page',
name='title',
),
migrations.AddField(
model_name='pagerev',
name='page',
field=models.ForeignKey(to='core.Page', related_name='revisions'),
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_auto_20151202_0814'),
]
operations = [
migrations.RemoveField(
model_name='page',
name='revision',
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0014_remove_page_revision'),
]
operations = [
migrations.RemoveField(
model_name='page',
name='is_locked',
),
]

View File

@ -1,44 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0015_remove_page_is_locked'),
]
operations = [
migrations.AddField(
model_name='user',
name='edit_group',
field=models.ManyToManyField(to='core.Group', related_name='editable_user'),
),
migrations.AddField(
model_name='user',
name='owner_group',
field=models.ForeignKey(related_name='owned_user', to='core.Group', default=1),
),
migrations.AddField(
model_name='user',
name='view_group',
field=models.ManyToManyField(to='core.Group', related_name='viewable_user'),
),
migrations.AlterField(
model_name='page',
name='edit_group',
field=models.ManyToManyField(to='core.Group', related_name='editable_page'),
),
migrations.AlterField(
model_name='page',
name='owner_group',
field=models.ForeignKey(related_name='owned_page', to='core.Group', default=1),
),
migrations.AlterField(
model_name='page',
name='view_group',
field=models.ManyToManyField(to='core.Group', related_name='viewable_page'),
),
]

View File

@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0016_auto_20151203_1514'),
]
operations = [
migrations.AlterField(
model_name='page',
name='edit_group',
field=models.ManyToManyField(blank=True, related_name='editable_page', to='core.Group'),
),
migrations.AlterField(
model_name='page',
name='view_group',
field=models.ManyToManyField(blank=True, related_name='viewable_page', to='core.Group'),
),
migrations.AlterField(
model_name='user',
name='edit_group',
field=models.ManyToManyField(blank=True, related_name='editable_user', to='core.Group'),
),
migrations.AlterField(
model_name='user',
name='view_group',
field=models.ManyToManyField(blank=True, related_name='viewable_user', to='core.Group'),
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0017_auto_20151203_1530'),
]
operations = [
migrations.AlterModelOptions(
name='user',
options={'permissions': (('can_change_prop', "Can change the user's properties (groups, ...)"),), 'verbose_name': 'user', 'verbose_name_plural': 'users'},
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0018_auto_20151208_1558'),
]
operations = [
migrations.AlterModelOptions(
name='user',
options={'permissions': (('change_prop', "Can change the user's properties (groups, ...)"),), 'verbose_name_plural': 'users', 'verbose_name': 'user'},
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0019_auto_20151208_1604'),
]
operations = [
migrations.AlterModelOptions(
name='user',
options={'verbose_name_plural': 'users', 'permissions': (('change_prop_user', "Can change the user's properties (groups, ...)"),), 'verbose_name': 'user'},
),
]

View File

@ -72,8 +72,12 @@ class User(AbstractBaseUser, PermissionsMixin):
class Meta: class Meta:
verbose_name = _('user') verbose_name = _('user')
verbose_name_plural = _('users') verbose_name_plural = _('users')
# Add permissions like this to allow automatic permission validation in is_owner&co
# model: change_prop_<class_name>
# view_<class_name>
permissions = ( permissions = (
("change_prop_user", "Can change the user's properties (groups, ...)"), ("change_prop_user", "Can change the user's properties (groups, ...)"),
("view_user", "Can view user's profile"),
) )
def get_absolute_url(self): def get_absolute_url(self):
@ -142,14 +146,10 @@ class User(AbstractBaseUser, PermissionsMixin):
""" """
Determine if the object is owned by the user Determine if the object is owned by the user
""" """
# TODO: add permission (class) scale validation, to allow some groups other than superuser to manipulate
# all objects of a class if they are in the right group
# example: something like user.has_perm("change_"+obj.__class__)
if not hasattr(obj, "owner_group"): if not hasattr(obj, "owner_group"):
return False return False
print(str(obj.__class__)) if (self.is_superuser or self.groups.filter(name=obj.owner_group.name).exists() or
print(str(obj.__class__).lower().split('.')[-1]) self.has_perm(obj.__class__.__module__.split('.')[0]+".change_prop_"+obj.__class__.__name__.lower())):
if self.is_superuser or self.groups.filter(name=obj.owner_group.name).exists() or self.has_perm("change_prop_"+str(obj.__class__).lower().split('.')[-1]):
return True return True
return False return False
@ -166,6 +166,8 @@ class User(AbstractBaseUser, PermissionsMixin):
return True return True
if isinstance(obj, User) and obj == self: if isinstance(obj, User) and obj == self:
return True return True
if self.has_perm(obj.__class__.__module__.split('.')[0]+".change_"+obj.__class__.__name__.lower()):
return True
return False return False
def can_view(self, obj): def can_view(self, obj):
@ -179,6 +181,8 @@ class User(AbstractBaseUser, PermissionsMixin):
for g in obj.view_group.all(): for g in obj.view_group.all():
if self.groups.filter(name=g.name).exists(): if self.groups.filter(name=g.name).exists():
return True return True
if self.has_perm(obj.__class__.__module__.split('.')[0]+".view_"+obj.__class__.__name__.lower()):
return True
return False return False
class LockError(Exception): class LockError(Exception):
@ -218,7 +222,8 @@ class Page(models.Model):
class Meta: class Meta:
unique_together = ('name', 'parent') unique_together = ('name', 'parent')
permissions = ( permissions = (
("can_view", "Can view the page"), ("change_prop_page", "Can change the page's properties (groups, ...)"),
("view_page", "Can view the page"),
) )
@staticmethod @staticmethod

View File

@ -8,7 +8,6 @@
<h3>User Profile</h3> <h3>User Profile</h3>
<p><a href="{% url 'core:user_list' %}">Back to list</a></p> <p><a href="{% url 'core:user_list' %}">Back to list</a></p>
{{ perms.core }}
<ul> <ul>
{% if user.id == profile.id %} {% if user.id == profile.id %}
<li><a href="{% url 'core:user_tools' %}">Tools</a></li> <li><a href="{% url 'core:user_tools' %}">Tools</a></li>