Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion contentcuration/contentcuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1894,6 +1894,38 @@ class Meta:
def __str__(self):
return self.name

@classmethod
def filter_edit_queryset(cls, queryset, user):
if user.is_anonymous:
return queryset.none()

if user.is_admin:
return queryset

return queryset.filter(
user_roles__user=user,
user_roles__role=ORGANIZATION_ADMIN,
user_roles__status=ORGANIZATION_ROLE_STATUS_ACTIVE,
).distinct()

@classmethod
def filter_view_queryset(cls, queryset, user):
if user.is_anonymous:
return queryset.none()

if user.is_admin:
return queryset

return queryset.filter(
user_roles__user=user,
user_roles__role__in=[
ORGANIZATION_ADMIN,
ORGANIZATION_EDITOR,
ORGANIZATION_VIEWER,
],
user_roles__status=ORGANIZATION_ROLE_STATUS_ACTIVE,
).distinct()


class OrganizationRole(models.Model):
"""
Expand Down Expand Up @@ -3788,7 +3820,14 @@ def filter_edit_queryset(cls, queryset, user):
return queryset

return queryset.filter(
Q(email__iexact=user.email) | Q(sender=user) | Q(channel__editors=user)
Q(email__iexact=user.email)
| Q(sender=user)
| Q(channel__editors=user)
| Q(
organization__user_roles__user=user,
organization__user_roles__role=ORGANIZATION_ADMIN,
organization__user_roles__status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)
).distinct()

@classmethod
Expand All @@ -3803,6 +3842,15 @@ def filter_view_queryset(cls, queryset, user):
| Q(sender=user)
| Q(channel__editors=user)
| Q(channel__viewers=user)
| Q(
organization__user_roles__user=user,
organization__user_roles__role__in=[
ORGANIZATION_ADMIN,
ORGANIZATION_EDITOR,
ORGANIZATION_VIEWER,
],
organization__user_roles__status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)
).distinct()


Expand Down
170 changes: 170 additions & 0 deletions contentcuration/contentcuration/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
from contentcuration.constants import channel_history
from contentcuration.constants import community_library_submission
from contentcuration.constants import user_history
from contentcuration.constants.organization_roles import ORGANIZATION_ADMIN
from contentcuration.constants.organization_roles import ORGANIZATION_EDITOR
from contentcuration.constants.organization_roles import (
ORGANIZATION_ROLE_STATUS_ACTIVE,
)
from contentcuration.constants.organization_roles import (
ORGANIZATION_ROLE_STATUS_PENDING,
)
from contentcuration.constants.organization_roles import ORGANIZATION_VIEWER
from contentcuration.models import AssessmentItem
from contentcuration.models import AuditedSpecialPermissionsLicense
from contentcuration.models import Change
Expand All @@ -34,6 +43,8 @@
from contentcuration.models import Language
from contentcuration.models import License
from contentcuration.models import object_storage_name
from contentcuration.models import Organization
from contentcuration.models import OrganizationRole
from contentcuration.models import RecommendationsEvent
from contentcuration.models import RecommendationsInteractionEvent
from contentcuration.models import User
Expand Down Expand Up @@ -309,6 +320,165 @@ def create_change(server_rev, applied):
self.assertEqual(channel.get_server_rev(), 2)


class OrganizationTestCase(PermissionQuerysetTestCase):
@property
def base_queryset(self):
return Organization.objects.all()

def test_filter_edit_queryset__admin_role(self):
organization = testdata.organization()
user = testdata.user()

queryset = Organization.filter_edit_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=organization.id)

OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_ADMIN,
status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)
queryset = Organization.filter_edit_queryset(self.base_queryset, user=user)
self.assertQuerysetContains(queryset, pk=organization.id)

def test_filter_edit_queryset__editor_role_cannot_edit(self):
organization = testdata.organization()
user = testdata.user()
OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_EDITOR,
status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)

queryset = Organization.filter_edit_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=organization.id)

def test_filter_edit_queryset__pending_admin_cannot_edit(self):
organization = testdata.organization()
user = testdata.user()
OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_ADMIN,
status=ORGANIZATION_ROLE_STATUS_PENDING,
)

queryset = Organization.filter_edit_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=organization.id)

def test_filter_edit_queryset__anonymous(self):
organization = testdata.organization()

queryset = Organization.filter_edit_queryset(
self.base_queryset, user=self.anonymous_user
)
self.assertQuerysetDoesNotContain(queryset, pk=organization.id)

def test_filter_view_queryset__viewer_role(self):
organization = testdata.organization()
user = testdata.user()

queryset = Organization.filter_view_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=organization.id)

OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_VIEWER,
status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)
queryset = Organization.filter_view_queryset(self.base_queryset, user=user)
self.assertQuerysetContains(queryset, pk=organization.id)

def test_filter_view_queryset__anonymous(self):
organization = testdata.organization()

queryset = Organization.filter_view_queryset(
self.base_queryset, user=self.anonymous_user
)
self.assertQuerysetDoesNotContain(queryset, pk=organization.id)


class InvitationOrganizationTestCase(PermissionQuerysetTestCase):
@property
def base_queryset(self):
return Invitation.objects.all()

def _make_org_invitation(self):
organization = testdata.organization()
invitee = testdata.user(email="org-invitee@le.com")
invitation = Invitation.objects.create(
email=invitee.email, organization=organization
)
return organization, invitation

def test_filter_edit_queryset__organization_admin(self):
organization, invitation = self._make_org_invitation()
user = testdata.user()

queryset = Invitation.filter_edit_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=invitation.id)

OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_ADMIN,
status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)
queryset = Invitation.filter_edit_queryset(self.base_queryset, user=user)
self.assertQuerysetContains(queryset, pk=invitation.id)

def test_filter_edit_queryset__organization_editor_cannot_edit(self):
organization, invitation = self._make_org_invitation()
user = testdata.user()
OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_EDITOR,
status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)

queryset = Invitation.filter_edit_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=invitation.id)

def test_filter_view_queryset__organization_editor(self):
organization, invitation = self._make_org_invitation()
user = testdata.user()

queryset = Invitation.filter_view_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=invitation.id)

OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_EDITOR,
status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)
queryset = Invitation.filter_view_queryset(self.base_queryset, user=user)
self.assertQuerysetContains(queryset, pk=invitation.id)

def test_filter_view_queryset__organization_viewer(self):
organization, invitation = self._make_org_invitation()
user = testdata.user()
OrganizationRole.objects.create(
user=user,
organization=organization,
role=ORGANIZATION_VIEWER,
status=ORGANIZATION_ROLE_STATUS_ACTIVE,
)

queryset = Invitation.filter_view_queryset(self.base_queryset, user=user)
self.assertQuerysetContains(queryset, pk=invitation.id)

def test_filter_view_queryset__unrelated_user(self):
organization, invitation = self._make_org_invitation()
user = testdata.user()

queryset = Invitation.filter_view_queryset(self.base_queryset, user=user)
self.assertQuerysetDoesNotContain(queryset, pk=invitation.id)


class ContentNodeTestCase(PermissionQuerysetTestCase):
@property
def base_queryset(self):
Expand Down
16 changes: 16 additions & 0 deletions contentcuration/contentcuration/tests/testdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
from contentcuration.constants import (
community_library_submission as community_library_submission_constants,
)
from contentcuration.constants.organization_roles import ORGANIZATION_ADMIN
from contentcuration.constants.organization_roles import (
ORGANIZATION_ROLE_STATUS_ACTIVE,
)
from contentcuration.tests.utils import mixer


Expand Down Expand Up @@ -253,6 +257,18 @@ def channel(name="testchannel"):
return channel


def organization(name="Test Organization"):
return cc.Organization.objects.create(name=name)


def organization_role(
user, organization, role=ORGANIZATION_ADMIN, status=ORGANIZATION_ROLE_STATUS_ACTIVE
):
return cc.OrganizationRole.objects.create(
user=user, organization=organization, role=role, status=status
)


def random_string(chars=10):
"""
Generate a random string
Expand Down
Loading
Loading