Skip to content

Commit

Permalink
Merge pull request #20 from ProjetSigma/feature/group_invites
Browse files Browse the repository at this point in the history
Feature/group invites
  • Loading branch information
TheBirdie committed Feb 1, 2016
2 parents d1397a5 + d33ab4d commit a78a43a
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 70 deletions.
4 changes: 0 additions & 4 deletions sigma_core/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,6 @@ def has_object_update_permission(self, request):
"""
return request.user.can_modify_group_infos(self)

@allow_staff_or_superuser
def has_object_invite_permission(self, request):
return request.user.can_invite(self)


class GroupAcknowledgment(models.Model):
subgroup = models.ForeignKey(Group, related_name='group_parents')
Expand Down
6 changes: 1 addition & 5 deletions sigma_core/models/group_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def has_create_permission(request):
group_id = request.data.get('group', None)
if group_id:
request.group = Group.objects.get(pk=group_id)
return request.group.can_anyone_join()
return request.group.can_anyone_join() or request.user.is_invited_to_group_id(group_id)
return True
except Group.DoesNotExist:
raise Http404()
Expand All @@ -75,7 +75,3 @@ def has_object_write_permission(self, request):
TODO: implement that.
"""
return True

@allow_staff_or_superuser
def has_object_accept_join_request_permission(self, request):
return request.user.can_accept_join_requests(self.group)
5 changes: 5 additions & 0 deletions sigma_core/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def can_invite(self, group):

def can_accept_join_requests(self, group):
from sigma_core.models.group_member import GroupMember
if self.is_sigma_admin():
return True
mem = self.get_group_membership(group)
return mem is not None and mem.perm_rank >= group.req_rank_accept_join_requests

Expand All @@ -114,6 +116,9 @@ def has_group_admin_perm(self, group):
mem = self.get_group_membership(group)
return mem is not None and mem.perm_rank == Group.ADMINISTRATOR_RANK

def is_invited_to_group_id(self, groupId):
return self.invited_to_groups.filter(pk=groupId).exists()

###############
# Permissions #
###############
Expand Down
15 changes: 10 additions & 5 deletions sigma_core/tests/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ def test_invite_unauthed(self):
response = self.client.put((self.group_url + "invite/") % self.groups[1].id, self.invite_data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

# def test_invite_forbidden(self):
# # Client has not perms to invite
# self.client.force_authenticate(user=self.users[1])
# response = self.client.put((self.group_url + "invite/") % self.groups[1].id, self.invite_data)
# self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_invite_forbidden(self):
# Client has not perms to invite
self.client.force_authenticate(user=self.users[1])
response = self.client.put((self.group_url + "invite/") % self.groups[1].id, self.invite_data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_invite_ok(self):
# Client has perms to invite
Expand All @@ -108,6 +108,11 @@ def test_invite_ok(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn(self.groups[1], reload(self.users[0]).invited_to_groups.all())

def test_invite_duplicate(self):
self.test_invite_ok()
response = self.client.put((self.group_url + "invite/") % self.groups[1].id, self.invite_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)

#### Create requests
def test_create_unauthed(self):
# Client is not authenticated
Expand Down
157 changes: 107 additions & 50 deletions sigma_core/tests/test_group_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,52 +63,48 @@ def setUpTestData(self):

# Routes
self.members_url = "/group-member/"
self.member_url = self.members_url + "%d/"
self.accept_join_requests_url = self.members_url + "%d/accept_join_request/"

# Group with membership request
self.group = GroupFactory()
self.group.default_member_rank = 0
self.group.req_rank_accept_join_requests = 5
self.group.save()
self.group = GroupFactory(default_member_rank=0, req_rank_accept_join_requests=5)

# Users already in group
self.users = UserFactory.create_batch(3)
# Associated GroupMember
self.group_member1 = GroupMember(user=self.users[0], group=self.group, perm_rank=Group.ADMINISTRATOR_RANK) # can validate requests
self.group_member2 = GroupMember(user=self.users[1], group=self.group, perm_rank=1) # cannot validate requests
self.group_member3 = GroupMember(user=self.users[2], group=self.group, perm_rank=0) # request to be validated
self.group_member1 = GroupMemberFactory(user=self.users[0], group=self.group, perm_rank=Group.ADMINISTRATOR_RANK) # can validate requests
self.group_member2 = GroupMemberFactory(user=self.users[1], group=self.group, perm_rank=1) # cannot validate requests
self.group_member3 = GroupMemberFactory(user=self.users[2], group=self.group, perm_rank=0) # request to be validated

# Testing user
self.user = UserFactory()

# Misc
self.new_membership_data = {"group": self.group.id, "user": self.user.id}

def test_create_not_authed(self):
self.client.force_authenticate(user=None)
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_create_success(self):
# Succesful attempt to request group membership
self.client.force_authenticate(user=self.user)
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['perm_rank'], self.group.default_member_rank)

def test_validate_forbidden(self):
# Attempt to validate a request but not enough permission
self.client.force_authenticate(user=self.users[1])
response = self.client.put(self.member_url + "accept_join_request/" % self.group_member3.id, {})
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(response.data['perm_rank'], 0)

def test_validate_success(self):
# Succesful attempt to validate a request
self.client.force_authenticate(user=self.users[0])
response = self.client.put(self.member_url + "accept_join_request/" % self.group_member3.id, {})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['perm_rank'], 1)
def test_create_not_authed(self):
self.client.force_authenticate(user=None)
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_create_success(self):
# Succesful attempt to request group membership
self.client.force_authenticate(user=self.user)
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['perm_rank'], self.group.default_member_rank)

def test_validate_forbidden(self):
# Attempt to validate a request but not enough permission
self.client.force_authenticate(user=self.users[1])
response = self.client.put(self.accept_join_requests_url % self.group_member3.id)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_validate_success(self):
# Succesful attempt to validate a request
self.client.force_authenticate(user=self.users[0])
response = self.client.put(self.accept_join_requests_url % self.group_member3.id)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['perm_rank'], 1)


class InvitationGroupMemberCreationTests(APITestCase):
Expand All @@ -118,26 +114,87 @@ def setUpTestData(self):

# Routes
self.members_url = "/group-member/"
self.member_url = self.members_url + "%d/"

# Group with invitation only
self.group = GroupFactory()
self.group.req_rank_invite = 5
self.group.save()
self.group = GroupFactory(req_rank_invite=5, default_member_rank=-1, visibility=Group.VIS_PRIVATE)

# Testing user
self.user = UserFactory()
self.users = UserFactory.create_batch(2)
self.memberships = [
None,
GroupMemberFactory(user=self.users[1], group=self.group, perm_rank=self.group.req_rank_invite)
]

# Misc
self.new_membership_data = {"user": self.user.id, "group": self.group.id}

def test_create_not_authed(self):
self.client.force_authenticate(user=None)
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_create_forbidden(self):
# Attempt to get group membership
self.client.force_authenticate(user=self.user)
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.new_membership_data = {"group": self.group.id, "user": self.users[0].id}

def test_create_not_authed(self):
self.client.force_authenticate(user=None)
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_create_forbidden(self):
# Can't join this Group
self.client.force_authenticate(user=self.users[0])
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_create_already_group_member(self):
self.client.force_authenticate(user=self.users[1])
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_create_self_forbidden(self):
self.client.force_authenticate(user=self.users[1])
response = self.client.post(self.members_url, self.new_membership_data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)


class InvitationGroupMemberInvitationWorkflowTests(APITestCase):
@classmethod
def setUpTestData(self):
super(APITestCase, self).setUpTestData()

# Routes
self.members_url = "/group-member/"
self.group_invite_url = "/group/%d/invite/";
self.group_invite_accept_url = "/group/%d/invite_accept/";

# Group with invitation only
self.group = GroupFactory(req_rank_invite=5, default_member_rank=-1)

# Testing user
self.users = UserFactory.create_batch(2)
self.memberships = [
None,
GroupMemberFactory(user=self.users[1], group=self.group, perm_rank=self.group.req_rank_invite)
]

def test_invite_not_authed(self):
self.client.force_authenticate(user=None)
response = self.client.put(self.group_invite_url % (self.group.id), {"user": self.users[0].id} )
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_invite_forbidden(self):
# Attempt to get group membership
self.client.force_authenticate(user=self.users[0])
response = self.client.put(self.group_invite_url % (self.group.id), {"user": self.users[0].id} )
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_invite_already_group_member(self):
# Attempt to get group membership
self.client.force_authenticate(user=self.users[1])
response = self.client.put(self.group_invite_url % (self.group.id), {"user": self.users[1].id} )
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_invite_ok(self):
# User0 invites User1 to group
self.client.force_authenticate(user=self.users[1])
response = self.client.put(self.group_invite_url % (self.group.id), {"user": self.users[0].id} )
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_invite_accept(self):
self.test_invite_ok()
self.client.force_authenticate(user=self.users[0])
response = self.client.post(self.members_url, {"user": self.users[0].id, "group": self.group.id})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
22 changes: 16 additions & 6 deletions sigma_core/views/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from sigma_core.models.user import User
from sigma_core.models.group import Group
from sigma_core.models.group_member import GroupMember
from sigma_core.serializers.group import GroupSerializer


Expand All @@ -30,13 +31,22 @@ def invite(self, request, pk=None):
try:
group = Group.objects.get(pk=pk)
user = User.objects.get(pk=request.data.get('user', None))
if not request.user.can_invite(group):
return Response(status=status.HTTP_403_FORBIDDEN)

# Already group member ?
try:
GroupMember.objects.get(user=user.id, group=group.id)
return Response("Already Group member", status=status.HTTP_400_BAD_REQUEST)
except GroupMember.DoesNotExist:
pass

group.invited_users.add(user)
# user.notify() # TODO: Notification
s = GroupSerializer(group)
return Response(s.data, status=status.HTTP_200_OK)

except Group.DoesNotExist:
raise Http404("Group %d not found" % pk)
except User.DoesNotExist:
raise Http404("User %d not found" % request.data.get('user', None))

group.invited_users.add(user)
# TODO: notify user of this invitation

s = GroupSerializer(group)
return Response(s.data, status=status.HTTP_200_OK)
3 changes: 3 additions & 0 deletions sigma_core/views/group_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ def accept_join_request(self, request, pk=None):
except GroupMember.DoesNotExist:
raise Http404()

if not request.user.can_accept_join_requests(gm.group):
return Response(status=status.HTTP_403_FORBIDDEN)

gm.perm_rank = 1 # default_perm_rank should be 0, so validation is to set perm_rank to 1
gm.save()

Expand Down

0 comments on commit a78a43a

Please sign in to comment.