Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
36 changes: 36 additions & 0 deletions apps/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,3 +982,39 @@ def __init__(self, invitation):

def has_perm(self, perm, obj=None):
return perm == "accounts.add_projectuser"


class InternalAdmin(AnonymousUser):
"""internal admim user (he have access to all models)"""

def get_project_queryset(self):
return Project.objects.all()

def get_news_queryset(self):
return News.objects.all()

def get_event_queryset(self):
return Event.objects.all()

def get_instruction_queryset(self):
return Instruction.objects.all()

def get_user_queryset(self):
return ProjectUser.objects.all()

def get_people_group_queryset(self):
return PeopleGroup.objects.all()

def _query_function(self, queryset, *ar, **kw):
return queryset

get_project_related_queryset = _query_function
get_user_related_queryset = _query_function
get_people_group_related_queryset = _query_function
get_news_related_queryset = _query_function
get_instruction_related_queryset = _query_function
get_event_related_queryset = _query_function

def get_related_organizations(self) -> list["Organization"]:
"""Return the organizations related to this model."""
return list(Organization.objects.all())
6 changes: 6 additions & 0 deletions apps/accounts/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
from apps.commons.permissions import IgnoreCall


class ProjectNestedPermission(permissions.BasePermission):
def has_permission(self, request: Request, view: GenericViewSet) -> bool:
"""check "project" from NestedProjectMixins"""
return request.user.get_project_queryset().contains(view.project)


def HasBasePermission( # noqa: N802
codename: str, app: str = ""
) -> permissions.BasePermission:
Expand Down
45 changes: 6 additions & 39 deletions apps/accounts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ class UserLighterSerializer(serializers.ModelSerializer):
profile_picture = PrivacySettingProtectedMethodField(
privacy_field="profile_picture"
)
is_manager = serializers.BooleanField(required=False, read_only=True)
is_leader = serializers.BooleanField(required=False, read_only=True)
role = serializers.CharField(required=False, read_only=True)

class Meta:
model = ProjectUser
Expand All @@ -126,8 +125,7 @@ class Meta:
"pronouns",
"job",
"profile_picture",
"is_manager",
"is_leader",
"role",
]
fields = read_only_fields

Expand All @@ -144,8 +142,7 @@ def to_representation(self, instance: ProjectUser) -> dict[str, Any]:
return super().to_representation(instance)
return {
**AnonymousUser.serialize(with_permissions=False),
"is_manager": False,
"is_leader": False,
"role": None,
}


Expand Down Expand Up @@ -530,13 +527,6 @@ class PeopleGroupSerializer(
roles = serializers.SlugRelatedField(
many=True, slug_field="name", read_only=True, source="groups"
)
team = PeopleGroupAddTeamMembersSerializer(required=False, write_only=True)
featured_projects = serializers.PrimaryKeyRelatedField(
many=True,
write_only=True,
required=False,
queryset=Project.objects.all(),
)
tags = TagRelatedField(many=True, required=False)
sdgs = serializers.ListField(
child=serializers.IntegerField(min_value=1, max_value=17),
Expand All @@ -558,12 +548,6 @@ def get_hierarchy(self, obj: PeopleGroup) -> list[dict[str, str | int]]:
)
return [{"order": i, **h} for i, h in enumerate(hierarchy[::-1])]

def validate_featured_projects(self, projects: list[Project]) -> list[Project]:
request = self.context.get("request")
if not all(request.user.can_see_project(project) for project in projects):
raise FeaturedProjectPermissionDeniedError
return projects

def validate_organization(self, value):
if self.instance and self.instance.organization != value:
raise GroupOrganizationChangeError
Expand Down Expand Up @@ -604,31 +588,18 @@ def validate_parent(self, value):
return value

def create(self, validated_data):
team = validated_data.pop("team", {})
featured_projects = validated_data.pop("featured_projects", [])
locations = validated_data.pop("locations", [])

people_group = super(PeopleGroupSerializer, self).create(validated_data)
PeopleGroupAddTeamMembersSerializer().create(
{"people_group": people_group, **team}
)
PeopleGroupAddFeaturedProjectsSerializer().create(
{
"people_group": people_group,
"featured_projects": featured_projects,
}
)
people_group = super().create(validated_data)
PeopleGroupAddLocationsSerializer().create(
{"people_group": people_group, "locations": locations}
)
return people_group

def update(self, instance, validated_data):
validated_data.pop("team", {})
validated_data.pop("featured_projects", [])
validated_data.pop("locations", None)

return super(PeopleGroupSerializer, self).update(instance, validated_data)
return super().update(instance, validated_data)

class Meta:
model = PeopleGroup
Expand All @@ -649,8 +620,6 @@ class Meta:
"tags",
"locations",
"publication_status",
"team",
"featured_projects",
]


Expand Down Expand Up @@ -825,8 +794,6 @@ def to_representation(self, instance):
return {
**AnonymousUser.serialize(with_permissions=False),
"current_org_role": None,
"is_manager": False,
"is_leader": False,
}

def _validate_role(
Expand Down Expand Up @@ -964,7 +931,7 @@ def create(self, validated_data):
"natural_ratio",
]
}
instance = super(UserSerializer, self).create(validated_data)
instance = super().create(validated_data)
if profile_picture["file"]:
image = Image(
name=profile_picture["file"].name,
Expand Down
88 changes: 48 additions & 40 deletions apps/accounts/tests/views/test_people_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,17 +317,18 @@ def test_create_people_group(self, role, expected_code):
"description": faker.text(),
"email": faker.email(),
"parent": parent.id,
"team": {
"members": [m.id for m in members],
"managers": [r.id for r in managers],
"leaders": [r.id for r in leaders],
},
"featured_projects": [p.pk for p in projects],
"locations": locations,
}
response = self.client.post(
reverse("PeopleGroup-list", args=(organization.code,)), payload
)
team = {
"members": [m.id for m in members],
"managers": [r.id for r in managers],
"leaders": [r.id for r in leaders],
}
featured_projects = {"featured_projects": [p.pk for p in projects]}

self.assertEqual(response.status_code, expected_code)
if expected_code == status.HTTP_201_CREATED:
self.assertEqual(response.data["name"], payload["name"])
Expand All @@ -336,6 +337,24 @@ def test_create_people_group(self, role, expected_code):
self.assertEqual(response.data["organization"], organization.code)
self.assertEqual(response.data["hierarchy"][0]["id"], parent.id)
self.assertEqual(response.data["hierarchy"][0]["slug"], parent.slug)

rsp2 = self.client.post(
reverse(
"PeopleGroup-add-member",
args=(organization.code, response.data["id"]),
),
team,
)
self.assertEqual(rsp2.status_code, status.HTTP_204_NO_CONTENT)
rsp3 = self.client.post(
reverse(
"PeopleGroup-add-featured-project",
args=(organization.code, response.data["id"]),
),
featured_projects,
)
self.assertEqual(rsp3.status_code, status.HTTP_204_NO_CONTENT)

people_group = PeopleGroup.objects.get(id=response.json()["id"])
for member in members:
self.assertIn(member, people_group.members.all())
Expand Down Expand Up @@ -749,15 +768,15 @@ def test_assign_role_on_project_group_member_changer(self, project_role):
people_group.leaders.add(self.user_3)
payload = {project_role: [people_group.id]}
response = self.client.post(
reverse("Project-add-member", args=(self.project.id,)), payload
reverse("Project-member-add-member", args=(self.project.id,)), payload
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.project.refresh_from_db()
for user in [self.user_1, self.user_2, self.user_3]:
self.assertIn(user, getattr(self.project, f"{project_role}_users").all())
payload = {"people_groups": [people_group.id]}
response = self.client.post(
reverse("Project-remove-member", args=(self.project.id,)), payload
reverse("Project-member-remove-member", args=(self.project.id,)), payload
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.project.refresh_from_db()
Expand Down Expand Up @@ -1039,19 +1058,21 @@ def setUpTestData(cls):
cls.organization = OrganizationFactory()
cls.superadmin = UserFactory(groups=[get_superadmins_group()])

def members_by_role(self, members: list, role: GroupData.Role):
return [member["id"] for member in members if member["role"] == role.value]

def test_annotate_members(self):
people_group = PeopleGroupFactory(
publication_status=PeopleGroup.PublicationStatus.PUBLIC,
organization=self.organization,
)
leaders_managers = UserFactory.create_batch(2)
leaders = UserFactory.create_batch(2)
managers = UserFactory.create_batch(2)
leaders_members = UserFactory.create_batch(2)
members = UserFactory.create_batch(2)

people_group.managers.add(*managers, *leaders_managers)
people_group.members.add(*members, *leaders_members)
people_group.leaders.add(*leaders_managers, *leaders_members)
people_group.managers.set(managers)
people_group.members.set(members)
people_group.leaders.set(leaders)

response = self.client.get(
reverse(
Expand All @@ -1064,33 +1085,20 @@ def test_annotate_members(self):
content = response.json()
results = content["results"]

batch_1 = results[:2]
batch_1_ids = [user["id"] for user in batch_1]
leaders_managers_ids = [user.id for user in leaders_managers]
self.assertEqual(leaders_managers_ids.sort(), batch_1_ids.sort())
self.assertTrue(all(user["is_manager"] is True for user in batch_1))
self.assertTrue(all(user["is_leader"] is True for user in batch_1))

batch_2 = results[2:4]
batch_2_ids = [user["id"] for user in batch_2]
leaders_members_ids = [user.id for user in leaders_members]
self.assertEqual(leaders_members_ids.sort(), batch_2_ids.sort())
self.assertTrue(all(user["is_manager"] is False for user in batch_2))
self.assertTrue(all(user["is_leader"] is True for user in batch_2))

batch_3 = results[4:6]
batch_3_ids = [user["id"] for user in batch_3]
managers_ids = [user.id for user in managers]
self.assertEqual(managers_ids.sort(), batch_3_ids.sort())
self.assertTrue(all(user["is_manager"] is True for user in batch_3))
self.assertTrue(all(user["is_leader"] is False for user in batch_3))

batch_4 = results[6:]
batch_4_ids = [user["id"] for user in batch_4]
members_ids = [user.id for user in members]
self.assertEqual(members_ids.sort(), batch_4_ids.sort())
self.assertTrue(all(user["is_manager"] is False for user in batch_4))
self.assertTrue(all(user["is_leader"] is False for user in batch_4))
self.assertListEqual(
sorted(self.members_by_role(results, GroupData.Role.MANAGERS)),
sorted([user.id for user in managers]),
)

self.assertListEqual(
sorted(self.members_by_role(results, GroupData.Role.LEADERS)),
sorted([user.id for user in leaders]),
)

self.assertListEqual(
sorted(self.members_by_role(results, GroupData.Role.MEMBERS)),
sorted([user.id for user in members]),
)

def test_root_group_creation(self):
organization = OrganizationFactory()
Expand Down
33 changes: 20 additions & 13 deletions apps/accounts/tests/views/test_user_publication_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,40 +133,46 @@ def test_list_users(self, role, expected_users):

@parameterized.expand(
[
(TestRoles.ANONYMOUS, ("public", None, None)),
(TestRoles.DEFAULT, ("public", None, None)),
(TestRoles.ANONYMOUS, ("public",)),
(TestRoles.DEFAULT, ("public",)),
(TestRoles.SUPERADMIN, ("public", "private", "org")),
(TestRoles.ORG_ADMIN, ("public", "private", "org")),
(TestRoles.ORG_FACILITATOR, ("public", "private", "org")),
(TestRoles.ORG_USER, ("public", "org", None)),
(TestRoles.ORG_VIEWER, ("public", "org", None)),
(TestRoles.ORG_USER, ("public", "org")),
(TestRoles.ORG_VIEWER, ("public", "org")),
]
)
def test_view_project_members(self, role, expected_users):
organization = self.organization
user = self.get_parameterized_test_user(role, instances=[organization])
self.client.force_authenticate(user)
response = self.client.get(reverse("Project-detail", args=(self.project.pk,)))
response = self.client.get(
reverse("Project-member-list", args=(self.project.pk,))
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
content = response.json()
self.assertEqual(len(content["team"]["members"]), len(expected_users))

members = content["results"]

self.assertEqual(len(members), len(expected_users))
self.assertEqual(
{user["id"] for user in content["team"]["members"]},
{user["id"] for user in members},
{
self.users[user_type].id if user_type in expected_users else None
self.users[user_type].id
for user_type in self.users.keys()
if user_type in expected_users
},
)

@parameterized.expand(
[
(TestRoles.ANONYMOUS, ("public", None, None)),
(TestRoles.DEFAULT, ("public", None, None)),
(TestRoles.ANONYMOUS, ("public",)),
(TestRoles.DEFAULT, ("public",)),
(TestRoles.SUPERADMIN, ("public", "private", "org")),
(TestRoles.ORG_ADMIN, ("public", "private", "org")),
(TestRoles.ORG_FACILITATOR, ("public", "private", "org")),
(TestRoles.ORG_USER, ("public", "org", None)),
(TestRoles.ORG_VIEWER, ("public", "org", None)),
(TestRoles.ORG_USER, ("public", "org")),
(TestRoles.ORG_VIEWER, ("public", "org")),
]
)
def test_view_people_group_members(self, role, expected_users):
Expand All @@ -185,8 +191,9 @@ def test_view_people_group_members(self, role, expected_users):
self.assertEqual(
{user["id"] for user in content},
{
self.users[user_type].id if user_type in expected_users else None
self.users[user_type].id
for user_type in self.users.keys()
if user_type in expected_users
},
)

Expand Down
Loading
Loading