Merge pull request #2370 from normanjaeckel/Issue2315

Migration for new permission matrix
This commit is contained in:
Norman Jäckel 2016-09-08 13:20:49 +02:00 committed by GitHub
commit b661a6e215
5 changed files with 121 additions and 30 deletions

View File

@ -1,5 +0,0 @@
from ..utils.exceptions import OpenSlidesError
class UsersError(OpenSlidesError):
pass

View File

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-08-01 14:54
from __future__ import unicode_literals
from django.db import migrations, models
def migrate_groups_and_user_permissions(apps, schema_editor):
"""
This function migrates the database to the new groups logic:
- Rename group 'Anonymous' (pk=1) to 'Default'
- Rename group 'Registered users' (pk=2) to 'Previous group Registered'
- Add all users who are not in any group to this group (pk=2)
- Add all permissions of 'Previous group Registered' to all other groups (except 'Default')
But only run this migration if:
- there are groups in the database,
- the name of the first group is 'Guests',
- the name of the second group is 'Registered users'.
"""
# Disconnect autoupdate. We do not want to trigger it here.
models.signals.post_save.disconnect(dispatch_uid='inform_changed_data_receiver')
User = apps.get_model('users', 'User')
Group = apps.get_model('auth', 'Group')
try:
group_default = Group.objects.get(pk=1)
group_registered = Group.objects.get(pk=2)
except Group.DoesNotExist:
# One of the groups does not exist. Just do nothing.
pass
else:
if group_default.name == 'Guests' and group_registered.name == 'Registered users':
# Rename groups pk 1 and 2.
group_default.name = 'Default'
group_default.save()
group_registered.name = 'Previous group Registered'
group_registered.save()
# Move users without groups to group pk 2.
users = User.objects.all()
for user in users:
if not user.groups.exists():
user.groups.add(group_registered)
# Copy permissions of group pk 2 to all other groups except pk 1.
groups = Group.objects.filter(pk__gt=2)
for group in groups:
for permission in group_registered.permissions.all():
group.permissions.add(permission)
class Migration(migrations.Migration):
dependencies = [
('users', '0003_user_number'),
]
operations = [
migrations.RunPython(migrate_groups_and_user_permissions),
]

View File

@ -5,16 +5,17 @@ from django.contrib.auth.models import (
AbstractBaseUser, AbstractBaseUser,
BaseUserManager, BaseUserManager,
Group, Group,
Permission,
PermissionsMixin, PermissionsMixin,
) )
from django.db import models from django.db import models
from django.db.models import Q
from openslides.utils.search import user_name_helper from openslides.utils.search import user_name_helper
from ..core.config import config from ..core.config import config
from ..utils.models import RESTModelMixin from ..utils.models import RESTModelMixin
from .access_permissions import UserAccessPermissions from .access_permissions import UserAccessPermissions
from .exceptions import UsersError
class UserManager(BaseUserManager): class UserManager(BaseUserManager):
@ -35,13 +36,17 @@ class UserManager(BaseUserManager):
""" """
Creates an user with the username 'admin'. If such a user already Creates an user with the username 'admin'. If such a user already
exists, resets it. The password is (re)set to 'admin'. The user exists, resets it. The password is (re)set to 'admin'. The user
becomes member of the group 'Staff' (pk=3). becomes member of the group 'Staff'. The two important permissions
'users.can_see_name' and 'users.can_manage' are added to this group,
so that the admin can manage all other permissions.
""" """
try: query_can_see_name = Q(content_type__app_label='users') & Q(codename='can_see_name')
staff = Group.objects.get(pk=3) query_can_manage = Q(content_type__app_label='users') & Q(codename='can_manage')
except Group.DoesNotExist:
raise UsersError("Admin user can not be created or reset because " staff, _ = Group.objects.get_or_create(name='Staff')
"the group 'Staff' (pk=3) is not available.") staff.permissions.add(Permission.objects.get(query_can_see_name))
staff.permissions.add(Permission.objects.get(query_can_manage))
admin, created = self.get_or_create( admin, created = self.get_or_create(
username='admin', username='admin',
defaults={'last_name': 'Administrator'}) defaults={'last_name': 'Administrator'})

View File

@ -10,9 +10,9 @@ def create_builtin_groups_and_admin(**kwargs):
Creates the builtin user: admin. Creates the builtin user: admin.
""" """
# Check whether the group pk's 1 to 4 are free. # Check whether there are groups in the database.
if Group.objects.filter(pk__in=range(1, 5)).exists(): if Group.objects.exists():
# Do completely nothing if there are already some of our groups in the database. # Do completely nothing if there are already some groups in the database.
return return
permission_strings = ( permission_strings = (

View File

@ -286,30 +286,26 @@ class UserManagerGeneratePassword(TestCase):
for _ in range(8)]) for _ in range(8)])
@patch('openslides.users.models.Permission')
@patch('openslides.users.models.Group') @patch('openslides.users.models.Group')
class UserManagerCreateOrResetAdminUser(TestCase): class UserManagerCreateOrResetAdminUser(TestCase):
def test_get_admin_group(self, mock_group): def test_add_admin_group(self, mock_group, mock_permission):
""" """
Tests that the Group with pk=3 is added to the admin. Tests that the Group with name='Staff' is added to the admin.
""" """
def mock_side_effect(pk):
if pk == 3:
result = 'mock_staff'
else:
result = ''
return result
admin_user = MagicMock() admin_user = MagicMock()
manager = UserManager() manager = UserManager()
manager.get_or_create = MagicMock(return_value=(admin_user, False)) manager.get_or_create = MagicMock(return_value=(admin_user, False))
mock_group.objects.get.side_effect = mock_side_effect
staff_group = MagicMock(name="Staff")
mock_group.objects.get_or_create = MagicMock(return_value=(staff_group, True))
mock_permission.get = MagicMock()
manager.create_or_reset_admin_user() manager.create_or_reset_admin_user()
mock_group.objects.get.assert_called_once_with(pk=3) admin_user.groups.add.assert_called_once_with(staff_group)
admin_user.groups.add.assert_called_once_with('mock_staff')
def test_password_set_to_admin(self, mock_group): def test_password_set_to_admin(self, mock_group, mock_permission):
""" """
Tests that the password of the admin is set to 'admin'. Tests that the password of the admin is set to 'admin'.
""" """
@ -317,6 +313,10 @@ class UserManagerCreateOrResetAdminUser(TestCase):
manager = UserManager() manager = UserManager()
manager.get_or_create = MagicMock(return_value=(admin_user, False)) manager.get_or_create = MagicMock(return_value=(admin_user, False))
staff_group = MagicMock(name="Staff")
mock_group.objects.get_or_create = MagicMock(return_value=(staff_group, True))
mock_permission.get = MagicMock()
manager.create_or_reset_admin_user() manager.create_or_reset_admin_user()
self.assertEqual( self.assertEqual(
@ -325,7 +325,7 @@ class UserManagerCreateOrResetAdminUser(TestCase):
admin_user.save.assert_called_once_with() admin_user.save.assert_called_once_with()
@patch('openslides.users.models.User') @patch('openslides.users.models.User')
def test_return_value(self, mock_user, mock_group): def test_return_value(self, mock_user, mock_group, mock_permission):
""" """
Tests that the method returns True when a user is created. Tests that the method returns True when a user is created.
""" """
@ -334,6 +334,10 @@ class UserManagerCreateOrResetAdminUser(TestCase):
manager.get_or_create = MagicMock(return_value=(admin_user, True)) manager.get_or_create = MagicMock(return_value=(admin_user, True))
manager.model = mock_user manager.model = mock_user
staff_group = MagicMock(name="Staff")
mock_group.objects.get_or_create = MagicMock(return_value=(staff_group, True))
mock_permission.get = MagicMock()
self.assertEqual( self.assertEqual(
manager.create_or_reset_admin_user(), manager.create_or_reset_admin_user(),
True, True,
@ -341,7 +345,7 @@ class UserManagerCreateOrResetAdminUser(TestCase):
"new user is created.") "new user is created.")
@patch('openslides.users.models.User') @patch('openslides.users.models.User')
def test_attributes_of_created_user(self, mock_user, mock_group): def test_attributes_of_created_user(self, mock_user, mock_group, mock_permission):
""" """
Tests username and last_name of the created admin user. Tests username and last_name of the created admin user.
""" """
@ -350,6 +354,10 @@ class UserManagerCreateOrResetAdminUser(TestCase):
manager.get_or_create = MagicMock(return_value=(admin_user, True)) manager.get_or_create = MagicMock(return_value=(admin_user, True))
manager.model = mock_user manager.model = mock_user
staff_group = MagicMock(name="Staff")
mock_group.objects.get_or_create = MagicMock(return_value=(staff_group, True))
mock_permission.get = MagicMock()
manager.create_or_reset_admin_user() manager.create_or_reset_admin_user()
self.assertEqual( self.assertEqual(
@ -360,3 +368,24 @@ class UserManagerCreateOrResetAdminUser(TestCase):
admin_user.last_name, admin_user.last_name,
'Administrator', 'Administrator',
"The last_name of a new created admin should be 'Administrator'.") "The last_name of a new created admin should be 'Administrator'.")
def test_get_permissions(self, mock_group, mock_permission):
"""
Tests if two permissions are get
"""
admin_user = MagicMock()
manager = UserManager()
manager.get_or_create = MagicMock(return_value=(admin_user, True))
staff_group = MagicMock(name="Staff")
mock_group.objects.get_or_create = MagicMock(return_value=(staff_group, True))
permission_mock = MagicMock(name="test permission")
mock_permission.objects.get = MagicMock(return_value=permission_mock)
manager.create_or_reset_admin_user()
self.assertEqual(
mock_permission.objects.get.call_count,
2,
"Permission.get should be called two times")