Make the acls app organizations aware.
This commit is contained in:
@@ -74,14 +74,14 @@ class AccessControlListManager(models.Manager):
|
||||
pass
|
||||
|
||||
user_roles = []
|
||||
for group in user.groups.all():
|
||||
for group in user.organization_groups.all():
|
||||
for role in group.roles.all():
|
||||
if set(stored_permissions).intersection(set(self.get_inherited_permissions(role=role, obj=obj))):
|
||||
return True
|
||||
|
||||
user_roles.append(role)
|
||||
|
||||
if not self.filter(content_type=ContentType.objects.get_for_model(obj), object_id=obj.pk, permissions__in=stored_permissions, role__in=user_roles).exists():
|
||||
if not self.model.on_organization.filter(content_type=ContentType.objects.get_for_model(obj), object_id=obj.pk, permissions__in=stored_permissions, role__in=user_roles).exists():
|
||||
raise PermissionDenied(ugettext('Insufficient access.'))
|
||||
|
||||
def filter_by_access(self, permission, user, queryset):
|
||||
@@ -89,7 +89,7 @@ class AccessControlListManager(models.Manager):
|
||||
return queryset
|
||||
|
||||
user_roles = []
|
||||
for group in user.groups.all():
|
||||
for group in user.organization_groups.all():
|
||||
for role in group.roles.all():
|
||||
user_roles.append(role)
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import organizations.shortcuts
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('organizations', '0002_add_data_default_organization'),
|
||||
('acls', '0002_auto_20150703_0513'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='accesscontrollist',
|
||||
name='organization',
|
||||
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||
),
|
||||
]
|
||||
@@ -8,6 +8,9 @@ from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from organizations.models import Organization
|
||||
from organizations.managers import CurrentOrganizationManager
|
||||
from organizations.shortcuts import get_current_organization
|
||||
from permissions.models import Role, StoredPermission
|
||||
|
||||
from .managers import AccessControlListManager
|
||||
@@ -30,6 +33,9 @@ class AccessControlList(models.Model):
|
||||
ct_field='content_type',
|
||||
fk_field='object_id',
|
||||
)
|
||||
organization = models.ForeignKey(
|
||||
Organization, default=get_current_organization
|
||||
)
|
||||
# TODO: limit choices to the permissions valid for the content_object
|
||||
permissions = models.ManyToManyField(
|
||||
StoredPermission, blank=True, related_name='acls',
|
||||
@@ -38,6 +44,7 @@ class AccessControlList(models.Model):
|
||||
role = models.ForeignKey(Role, related_name='acls', verbose_name=_('Role'))
|
||||
|
||||
objects = AccessControlListManager()
|
||||
on_organization = CurrentOrganizationManager()
|
||||
|
||||
class Meta:
|
||||
unique_together = ('content_type', 'object_id', 'role')
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from documents.models import Document, DocumentType
|
||||
from documents.permissions import permission_document_view
|
||||
from documents.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||
from organizations.utils import create_default_organization
|
||||
from permissions.classes import Permission
|
||||
from permissions.models import Role
|
||||
from permissions.tests.literals import TEST_ROLE_LABEL
|
||||
from user_management.models import MayanGroup
|
||||
from user_management.tests.literals import TEST_USER_USERNAME, TEST_GROUP
|
||||
|
||||
from ..models import AccessControlList
|
||||
@@ -21,11 +22,13 @@ TEST_DOCUMENT_TYPE_2 = 'test document type 2'
|
||||
@override_settings(OCR_AUTO_OCR=False)
|
||||
class PermissionTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.document_type_1 = DocumentType.objects.create(
|
||||
create_default_organization()
|
||||
|
||||
self.document_type_1 = DocumentType.on_organization.create(
|
||||
label=TEST_DOCUMENT_TYPE
|
||||
)
|
||||
|
||||
self.document_type_2 = DocumentType.objects.create(
|
||||
self.document_type_2 = DocumentType.on_organization.create(
|
||||
label=TEST_DOCUMENT_TYPE_2
|
||||
)
|
||||
|
||||
@@ -44,19 +47,19 @@ class PermissionTestCase(TestCase):
|
||||
file_object=file_object
|
||||
)
|
||||
|
||||
self.user = get_user_model().objects.create(
|
||||
self.user = get_user_model().on_organization.create(
|
||||
username=TEST_USER_USERNAME
|
||||
)
|
||||
self.group = Group.objects.create(name=TEST_GROUP)
|
||||
self.role = Role.objects.create(label=TEST_ROLE_LABEL)
|
||||
self.group = MayanGroup.on_organization.create(name=TEST_GROUP)
|
||||
self.role = Role.on_organization.create(label=TEST_ROLE_LABEL)
|
||||
|
||||
self.group.user_set.add(self.user)
|
||||
self.role.groups.add(self.group)
|
||||
self.role.organization_groups.add(self.group)
|
||||
|
||||
Permission.invalidate_cache()
|
||||
|
||||
def tearDown(self):
|
||||
for document_type in DocumentType.objects.all():
|
||||
for document_type in DocumentType.on_organization.all():
|
||||
document_type.delete()
|
||||
|
||||
def test_check_access_without_permissions(self):
|
||||
@@ -75,7 +78,7 @@ class PermissionTestCase(TestCase):
|
||||
)
|
||||
|
||||
def test_check_access_with_acl(self):
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_1, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
@@ -91,7 +94,7 @@ class PermissionTestCase(TestCase):
|
||||
def test_filtering_with_permissions(self):
|
||||
self.role.permissions.add(permission_document_view.stored_permission)
|
||||
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_1, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
@@ -104,7 +107,7 @@ class PermissionTestCase(TestCase):
|
||||
)
|
||||
|
||||
def test_check_access_with_inherited_acl(self):
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_type_1, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
@@ -118,12 +121,12 @@ class PermissionTestCase(TestCase):
|
||||
self.fail('PermissionDenied exception was not expected.')
|
||||
|
||||
def test_check_access_with_inherited_acl_and_local_acl(self):
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_type_1, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_3, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
@@ -139,7 +142,7 @@ class PermissionTestCase(TestCase):
|
||||
def test_filtering_with_inherited_permissions(self):
|
||||
self.role.permissions.add(permission_document_view.stored_permission)
|
||||
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_type_1, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
@@ -155,12 +158,12 @@ class PermissionTestCase(TestCase):
|
||||
def test_filtering_with_inherited_permissions_and_local_acl(self):
|
||||
self.role.permissions.add(permission_document_view.stored_permission)
|
||||
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_type_1, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document_3, role=self.role
|
||||
)
|
||||
acl.permissions.add(permission_document_view.stored_permission)
|
||||
|
||||
@@ -33,7 +33,7 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
||||
)
|
||||
|
||||
self.assertEquals(response.status_code, 403)
|
||||
self.assertEqual(AccessControlList.objects.count(), 0)
|
||||
self.assertEqual(AccessControlList.on_organization.count(), 0)
|
||||
|
||||
def test_acl_create_view_with_permission(self):
|
||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||
@@ -49,7 +49,7 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
||||
)
|
||||
|
||||
self.assertContains(response, text='created', status_code=200)
|
||||
self.assertEqual(AccessControlList.objects.count(), 1)
|
||||
self.assertEqual(AccessControlList.on_organization.count(), 1)
|
||||
|
||||
def test_acl_create_duplicate_view_with_permission(self):
|
||||
"""
|
||||
@@ -57,7 +57,7 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
||||
Result: Should redirect to existing ACL for object + role combination
|
||||
"""
|
||||
|
||||
acl = AccessControlList.objects.create(
|
||||
acl = AccessControlList.on_organization.create(
|
||||
content_object=self.document, role=self.role
|
||||
)
|
||||
|
||||
@@ -76,8 +76,8 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
||||
self.assertContains(
|
||||
response, text='vailable permissions', status_code=200
|
||||
)
|
||||
self.assertEqual(AccessControlList.objects.count(), 1)
|
||||
self.assertEqual(AccessControlList.objects.first().pk, acl.pk)
|
||||
self.assertEqual(AccessControlList.on_organization.count(), 1)
|
||||
self.assertEqual(AccessControlList.on_organization.first().pk, acl.pk)
|
||||
|
||||
def test_orphan_acl_create_view_with_permission(self):
|
||||
"""
|
||||
@@ -93,7 +93,7 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
||||
|
||||
recent_entry = self.document.add_as_recent_document_for_user(self.user)
|
||||
|
||||
content_type = ContentType.objects.get_for_model(recent_entry)
|
||||
content_type = ContentType.on_organization.get_for_model(recent_entry)
|
||||
|
||||
view_arguments = {
|
||||
'app_label': content_type.app_label,
|
||||
@@ -108,4 +108,4 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
||||
)
|
||||
|
||||
self.assertNotContains(response, text='optgroup', status_code=200)
|
||||
self.assertEqual(AccessControlList.objects.count(), 1)
|
||||
self.assertEqual(AccessControlList.on_organization.count(), 1)
|
||||
|
||||
@@ -26,7 +26,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class ACLCreateView(SingleObjectCreateView):
|
||||
fields = ('role',)
|
||||
model = AccessControlList
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.content_type = get_object_or_404(
|
||||
@@ -52,14 +51,9 @@ class ACLCreateView(SingleObjectCreateView):
|
||||
|
||||
return super(ACLCreateView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_instance_extra_data(self):
|
||||
return {
|
||||
'content_object': self.content_object
|
||||
}
|
||||
|
||||
def form_valid(self, form):
|
||||
try:
|
||||
acl = AccessControlList.objects.get(
|
||||
acl = AccessControlList.on_organization.get(
|
||||
content_type=self.content_type,
|
||||
object_id=self.content_object.pk,
|
||||
role=form.cleaned_data['role']
|
||||
@@ -79,6 +73,14 @@ class ACLCreateView(SingleObjectCreateView):
|
||||
) % self.content_object
|
||||
}
|
||||
|
||||
def get_instance_extra_data(self):
|
||||
return {
|
||||
'content_object': self.content_object
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return AccessControlList.on_organization.all()
|
||||
|
||||
def get_success_url(self):
|
||||
if self.object.pk:
|
||||
return reverse('acls:acl_permissions', args=(self.object.pk,))
|
||||
@@ -87,8 +89,6 @@ class ACLCreateView(SingleObjectCreateView):
|
||||
|
||||
|
||||
class ACLDeleteView(SingleObjectDeleteView):
|
||||
model = AccessControlList
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
acl = get_object_or_404(AccessControlList, pk=self.kwargs['pk'])
|
||||
|
||||
@@ -118,6 +118,9 @@ class ACLDeleteView(SingleObjectDeleteView):
|
||||
)
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return AccessControlList.on_organization.all()
|
||||
|
||||
|
||||
class ACLListView(SingleObjectListView):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -152,7 +155,7 @@ class ACLListView(SingleObjectListView):
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return AccessControlList.objects.filter(
|
||||
return AccessControlList.on_organization.filter(
|
||||
content_type=self.content_type, object_id=self.content_object.pk
|
||||
)
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.management.color import no_style
|
||||
from django.db import DEFAULT_DB_ALIAS, connections, router
|
||||
from django.db.models import signals
|
||||
|
||||
|
||||
def create_default_organization(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
|
||||
"""
|
||||
Creates the default Organization object.
|
||||
"""
|
||||
|
||||
try:
|
||||
Organization = apps.get_model('organizations', 'Organization')
|
||||
except LookupError:
|
||||
return
|
||||
|
||||
if not router.allow_migrate(using, Organization):
|
||||
return
|
||||
|
||||
if not Organization.objects.using(using).exists():
|
||||
# The default settings set ORGANIZATION_ID = 1, and some tests in Django's test
|
||||
# suite rely on this value. However, if database sequences are reused
|
||||
# (e.g. in the test suite after flush/syncdb), it isn't guaranteed that
|
||||
# the next id will be 1, so we coerce it. See #15573 and #16353. This
|
||||
# can also crop up outside of tests - see #15346.
|
||||
if verbosity >= 2:
|
||||
print("Creating default Organization object")
|
||||
Organization(pk=1, label='Default').save(using=using)
|
||||
|
||||
# We set an explicit pk instead of relying on auto-incrementation,
|
||||
# so we need to reset the database sequence. See #17415.
|
||||
sequence_sql = connections[using].ops.sequence_reset_sql(no_style(), [Organization])
|
||||
if sequence_sql:
|
||||
if verbosity >= 2:
|
||||
print('Resetting sequence')
|
||||
with connections[using].cursor() as cursor:
|
||||
for command in sequence_sql:
|
||||
cursor.execute(command)
|
||||
|
||||
Organization.objects.clear_cache()
|
||||
|
||||
|
||||
signals.post_migrate.connect(
|
||||
create_default_organization, sender=apps.get_app_config('organizations')
|
||||
)
|
||||
Reference in New Issue
Block a user