core: make is_superuser a group property, remove from user

This commit is contained in:
Jens Langhammer 2020-09-15 22:37:31 +02:00
parent 0325847c22
commit 0a5e14a352
15 changed files with 112 additions and 24 deletions

View file

@ -12,7 +12,7 @@ class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["username", "name", "email", "is_staff", "is_active", "attributes"]
fields = ["username", "name", "email", "is_active", "attributes"]
widgets = {
"name": forms.TextInput,
"attributes": CodeMirrorWidget,

View file

@ -50,7 +50,7 @@
</td>
<td role="cell">
<span>
{{ group.user_set.all|length }}
{{ group.users.all|length }}
</span>
</td>
<td>

View file

@ -8,7 +8,7 @@ from django.test import Client, TestCase
from django.urls.exceptions import NoReverseMatch
from passbook.admin.urls import urlpatterns
from passbook.core.models import User
from passbook.core.models import Group, User
from passbook.lib.utils.reflection import get_apps
@ -16,7 +16,9 @@ class TestAdmin(TestCase):
"""Generic admin tests"""
def setUp(self):
self.user = User.objects.create_superuser(username="test")
self.user = User.objects.create_user(username="test")
self.user.pb_groups.add(Group.objects.filter(is_superuser=True).first())
self.user.save()
self.client = Client()
self.client.force_login(self.user)

View file

@ -11,7 +11,7 @@ class GroupSerializer(ModelSerializer):
class Meta:
model = Group
fields = ["pk", "name", "parent", "user_set", "attributes"]
fields = ["pk", "name", "is_superuser", "parent", "users", "attributes"]
class GroupViewSet(ModelViewSet):

View file

@ -1,5 +1,5 @@
"""User API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.serializers import BooleanField, ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.core.models import User
@ -8,10 +8,12 @@ from passbook.core.models import User
class UserSerializer(ModelSerializer):
"""User Serializer"""
is_superuser = BooleanField(read_only=True)
class Meta:
model = User
fields = ["pk", "username", "name", "email"]
fields = ["pk", "username", "name", "is_superuser", "email"]
class UserViewSet(ModelViewSet):

View file

@ -18,21 +18,19 @@ class GroupForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.initial["members"] = self.instance.user_set.values_list(
"pk", flat=True
)
self.initial["members"] = self.instance.users.values_list("pk", flat=True)
def save(self, *args, **kwargs):
instance = super().save(*args, **kwargs)
if instance.pk:
instance.user_set.clear()
instance.user_set.add(*self.cleaned_data["members"])
instance.users.clear()
instance.users.add(*self.cleaned_data["members"])
return instance
class Meta:
model = Group
fields = ["name", "parent", "members", "attributes"]
fields = ["name", "is_superuser", "parent", "members", "attributes"]
widgets = {
"name": forms.TextInput(),
"attributes": CodeMirrorWidget,

View file

@ -1,7 +1,7 @@
# Generated by Django 3.0.6 on 2020-05-23 16:40
from django.apps.registry import Apps
from django.db import migrations
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
@ -15,8 +15,6 @@ def create_default_user(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
username="pbadmin", email="root@localhost", name="passbook Default Admin"
)
pbadmin.set_password("pbadmin") # noqa # nosec
pbadmin.is_superuser = True
pbadmin.is_staff = True
pbadmin.save()
@ -27,5 +25,15 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RemoveField(model_name="user", name="is_superuser",),
migrations.RemoveField(model_name="user", name="is_staff",),
migrations.RunPython(create_default_user),
migrations.AddField(
model_name="user",
name="is_superuser",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="user", name="is_staff", field=models.BooleanField(default=False)
),
]

View file

@ -0,0 +1,44 @@
# Generated by Django 3.1.1 on 2020-09-15 19:53
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def create_default_admin_group(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Group = apps.get_model("passbook_core", "Group")
User = apps.get_model("passbook_core", "User")
# Creates a default admin group
group, _ = Group.objects.using(db_alias).get_or_create(
is_superuser=True, defaults={"name": "passbook Admins",}
)
group.users.add(User.objects.get(username="pbadmin"))
group.save()
class Migration(migrations.Migration):
dependencies = [
("passbook_core", "0008_auto_20200824_1532"),
]
operations = [
migrations.RemoveField(model_name="user", name="is_superuser",),
migrations.RemoveField(model_name="user", name="is_staff",),
migrations.AlterField(
model_name="user",
name="pb_groups",
field=models.ManyToManyField(
related_name="users", to="passbook_core.Group"
),
),
migrations.AddField(
model_name="group",
name="is_superuser",
field=models.BooleanField(
default=False, help_text="Users added to this group will be superusers."
),
),
migrations.RunPython(create_default_admin_group),
]

View file

@ -4,6 +4,7 @@ from typing import Any, Optional, Type
from uuid import uuid4
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import UserManager as DjangoUserManager
from django.db import models
from django.db.models import Q, QuerySet
from django.forms import ModelForm
@ -34,7 +35,12 @@ class Group(models.Model):
"""Custom Group model which supports a basic hierarchy"""
group_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.CharField(_("name"), max_length=80)
is_superuser = models.BooleanField(
default=False, help_text=_("Users added to this group will be superusers.")
)
parent = models.ForeignKey(
"Group",
blank=True,
@ -52,6 +58,14 @@ class Group(models.Model):
unique_together = (("name", "parent",),)
class UserManager(DjangoUserManager):
"""Custom user manager that doesn't assign is_superuser and is_staff"""
def create_user(self, username, email=None, password=None, **extra_fields):
"""Custom user manager that doesn't assign is_superuser and is_staff"""
return self._create_user(username, email, password, **extra_fields)
class User(GuardianUserMixin, AbstractUser):
"""Custom User model to allow easier adding o f user-based settings"""
@ -59,11 +73,23 @@ class User(GuardianUserMixin, AbstractUser):
name = models.TextField(help_text=_("User's display name."))
sources = models.ManyToManyField("Source", through="UserSourceConnection")
pb_groups = models.ManyToManyField("Group")
pb_groups = models.ManyToManyField("Group", related_name="users")
password_change_date = models.DateTimeField(auto_now_add=True)
attributes = models.JSONField(default=dict, blank=True)
objects = UserManager()
@property
def is_superuser(self) -> bool:
"""Get supseruser status based on membership in a group with superuser status"""
return self.pb_groups.filter(is_superuser=True).exists()
@property
def is_staff(self) -> bool:
"""superuser == staff user"""
return self.is_superuser
def set_password(self, password):
if self.pk:
password_changed.send(sender=self, user=self, password=password)

View file

@ -13,7 +13,7 @@ class TestOverviewViews(TestCase):
def setUp(self):
super().setUp()
self.user = User.objects.create_superuser(
self.user = User.objects.create_user(
username="unittest user",
email="unittest@example.com",
password="".join(

View file

@ -13,7 +13,7 @@ class TestUserViews(TestCase):
def setUp(self):
super().setUp()
self.user = User.objects.create_superuser(
self.user = User.objects.create_user(
username="unittest user",
email="unittest@example.com",
password="".join(

View file

@ -30,7 +30,7 @@ class GroupMembershipPolicy(Policy):
return GroupMembershipPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
return PolicyResult(self.group.user_set.filter(pk=request.user.pk).exists())
return PolicyResult(self.group.users.filter(pk=request.user.pk).exists())
class Meta:

View file

@ -24,7 +24,7 @@ class TestGroupMembershipPolicy(TestCase):
def test_valid(self):
"""user in group"""
group = Group.objects.create(name="test")
group.user_set.add(get_anonymous_user())
group.users.add(get_anonymous_user())
group.save()
policy: GroupMembershipPolicy = GroupMembershipPolicy.objects.create(
group=group

View file

@ -151,7 +151,7 @@ class Connector:
group_cache[group_dn] = groups.first()
group = group_cache[group_dn]
users = User.objects.filter(attributes__ldap_uniq=uniq)
group.user_set.add(*list(users))
group.users.add(*list(users))
# Now that all users are added, lets write everything
for _, group in group_cache.items():
group.save()

View file

@ -5922,7 +5922,7 @@ definitions:
required:
- name
- parent
- user_set
- users
type: object
properties:
pk:
@ -5935,11 +5935,15 @@ definitions:
type: string
maxLength: 80
minLength: 1
is_superuser:
title: Is superuser
description: Users added to this group will be superusers.
type: boolean
parent:
title: Parent
type: string
format: uuid
user_set:
users:
type: array
items:
type: integer
@ -5970,6 +5974,10 @@ definitions:
description: User's display name.
type: string
minLength: 1
is_superuser:
title: Is superuser
type: boolean
readOnly: true
email:
title: Email address
type: string