Skip to content
50 changes: 49 additions & 1 deletion contentcuration/contentcuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,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 @@ -3807,7 +3839,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 @@ -3822,6 +3861,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