From 6e15d7a9135f05dd8e2c05f6b596da205a0b9ee2 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 27 Mar 2013 18:17:21 -0400 Subject: [PATCH] Ability to add child groups to groups. --- lib/main/models/__init__.py | 10 ++++++ lib/main/serializers.py | 2 +- lib/main/tests/inventory.py | 72 ++++++++++++++++++++++++++++--------- lib/main/views.py | 32 +++++++++++++++++ lib/urls.py | 2 ++ 5 files changed, 101 insertions(+), 17 deletions(-) diff --git a/lib/main/models/__init__.py b/lib/main/models/__init__.py index 01f905a946..4e670995da 100644 --- a/lib/main/models/__init__.py +++ b/lib/main/models/__init__.py @@ -421,6 +421,16 @@ class Group(CommonModelNameNotUnique): inventory = Inventory.objects.get(pk=data['inventory']) return Inventory._has_permission_types(user, inventory, PERMISSION_TYPES_ALLOWING_INVENTORY_WRITE) + + @classmethod + def can_user_administrate(cls, user, obj): + # here this controls whether the user can attach subgroups + return Inventory._has_permission_types(user, obj.inventory, PERMISSION_TYPES_ALLOWING_INVENTORY_WRITE) + + @classmethod + def can_user_read(cls, user, obj): + return Inventory.can_user_read(user, obj.inventory) + def get_absolute_url(self): import lib.urls return reverse(lib.urls.views_GroupsDetail, args=(self.pk,)) diff --git a/lib/main/serializers.py b/lib/main/serializers.py index c3f422aa27..41a2ef88ed 100644 --- a/lib/main/serializers.py +++ b/lib/main/serializers.py @@ -114,7 +114,7 @@ class GroupSerializer(BaseSerializer): related = serializers.SerializerMethodField('get_related') class Meta: - model = Host + model = Group fields = ('url', 'id', 'name', 'description', 'creation_date', 'inventory') def get_related(self, obj): diff --git a/lib/main/tests/inventory.py b/lib/main/tests/inventory.py index f2935469b4..c96f9a1292 100644 --- a/lib/main/tests/inventory.py +++ b/lib/main/tests/inventory.py @@ -159,6 +159,8 @@ class InventoryTest(BaseTest): new_group_c = dict(name='web4', inventory=inv.pk) new_group_d = dict(name='web5', inventory=inv.pk) new_group_e = dict(name='web6', inventory=inv.pk) + groups = '/api/v1/groups/' + data0 = self.post(groups, data=invalid, expect=400, auth=self.get_super_credentials()) data0 = self.post(groups, data=new_group_a, expect=201, auth=self.get_super_credentials()) @@ -176,7 +178,7 @@ class InventoryTest(BaseTest): # permission_type = PERM_INVENTORY_WRITE #) group_data3 = self.post(groups, data=new_group_c, expect=201, auth=self.get_other_credentials()) - + # hostnames must be unique inside an organization group_data4 = self.post(groups, data=new_group_c, expect=400, auth=self.get_other_credentials()) @@ -268,44 +270,82 @@ class InventoryTest(BaseTest): ################################################### # VARIABLES -> GROUPS + + vars_a = dict(asdf=7777, dog='droopy', cat='battlecat', unstructured=dict(a=[1,1,1],b=dict(x=1,y=2))) + vars_b = dict(asdf=8888, dog='snoopy', cat='cheshire', unstructured=dict(a=[2,2,2],b=dict(x=3,y=4))) + vars_c = dict(asdf=9999, dog='pluto', cat='five', unstructured=dict(a=[3,3,3],b=dict(z=5))) + groups = Group.objects.all() + + vdata1_url = "/api/v1/groups/%s/variable_data/" % (groups[0].pk) + vdata2_url = "/api/v1/groups/%s/variable_data/" % (groups[1].pk) # a super user can associate variable objects with groups + got = self.get(vdata1_url, expect=200, auth=self.get_super_credentials()) + self.assertEquals(got, {}) + put = self.put(vdata1_url, data=vars_a, expect=200, auth=self.get_super_credentials()) + self.assertEquals(put, vars_a) # an org admin can associate variable objects with groups + put = self.put(vdata1_url, data=vars_b, expect=200, auth=self.get_normal_credentials()) # a normal user cannot associate variable objects with groups + put = self.put(vdata1_url, data=vars_b, expect=403, auth=self.get_nobody_credentials()) # a normal user with inventory edit permissions can associate variable objects with groups + put = self.put(vdata1_url, data=vars_c, expect=200, auth=self.get_normal_credentials()) + self.assertEquals(put, vars_c) #################################################### # SUBGROUPS + groups = Group.objects.all() + + # just some more groups for kicks + inv = Inventory.objects.get(pk=1) + Group.objects.create(name='group-X1', inventory=inv) + Group.objects.create(name='group-X2', inventory=inv) + Group.objects.create(name='group-X3', inventory=inv) + Group.objects.create(name='group-X4', inventory=inv) + Group.objects.create(name='group-X5', inventory=inv) + + Permission.objects.create( + inventory = inv, + user = self.other_django_user, + permission_type = PERM_INVENTORY_WRITE + ) + # a super user can set subgroups + subgroups_url = '/api/v1/groups/1/children/' + child_url = '/api/v1/groups/2/' + subgroups_url2 = '/api/v1/groups/3/children/' + subgroups_url3 = '/api/v1/groups/4/children/' + subgroups_url4 = '/api/v1/groups/5/children/' + got = self.get(child_url, expect=200, auth=self.get_super_credentials()) + self.post(subgroups_url, data=got, expect=204, auth=self.get_super_credentials()) + kids = Group.objects.get(pk=1).children.all() + self.assertEqual(len(kids), 1) + checked = self.get(subgroups_url, expect=200, auth=self.get_super_credentials()) + self.assertEquals(checked['count'], 1) # an org admin can set subgroups - + self.post(subgroups_url2, data=got, expect=204, auth=self.get_normal_credentials()) + # double post causes conflict error + self.post(subgroups_url2, data=got, expect=409, auth=self.get_normal_credentials()) + checked = self.get(subgroups_url2, expect=200, auth=self.get_normal_credentials()) + # a normal user cannot set subgroups + self.post(subgroups_url3, data=got, expect=403, auth=self.get_nobody_credentials()) # a normal user with inventory edit permissions can associate subgroups + self.post(subgroups_url3, data=got, expect=204, auth=self.get_other_credentials()) + checked = self.get(subgroups_url3, expect=200, auth=self.get_normal_credentials()) + self.assertEqual(checked['count'], 1) - # FIXME: go back and put in GET requests after all the post stuff - - ######################################################### - # GROUP CHILDREN ACCESS - - # a super user can see the children attached to a group - - # a org admin can see the children attached to a group - - # a user who is on a team who has read permissions on an inventory can see the children attached to a group - - # a regular user cannot see children attached to a group - ######################################################### # DISASSOCIATION TESTS # hosts from inventory # groups from inventory - # subgroups from groups + # children from groups # others? ######################################################### diff --git a/lib/main/views.py b/lib/main/views.py index 753f85e394..eaa825e6f4 100644 --- a/lib/main/views.py +++ b/lib/main/views.py @@ -366,6 +366,38 @@ class GroupsList(BaseList): ).distinct() return admin_of | has_user_perms | has_team_perms +class GroupsChildrenList(BaseSubList): + + model = Group + serializer_class = GroupSerializer + permission_classes = (CustomRbac,) + parent_model = Group + relationship = 'children' + postable = True + inject_primary_key_on_post_as = 'parent' + + def _get_queryset(self): + + # FIXME: this is the mostly the same as GroupsList, share code similar to how done with Host and Group objects. + + parent = Group.objects.get(pk=self.kwargs['pk']) + + # FIXME: verify read permissions on this object are still required at a higher level + + base = parent.children + if self.request.user.is_superuser: + return base.all() + admin_of = base.filter(inventory__organization__admins__in = [ self.request.user ]).distinct() + has_user_perms = base.filter( + inventory__permissions__user__in = [ self.request.user ], + inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ, + ).distinct() + has_team_perms = base.filter( + inventory__permissions__team__in = self.request.user.teams.all(), + inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ, + ).distinct() + return admin_of | has_user_perms | has_team_perms + class GroupsDetail(BaseDetail): model = Group diff --git a/lib/urls.py b/lib/urls.py index 807093d54e..e0bfeeef16 100644 --- a/lib/urls.py +++ b/lib/urls.py @@ -53,6 +53,7 @@ views_InventoryGroupsList = views.InventoryGroupsList.as_view() views_GroupsList = views.GroupsList.as_view() views_GroupsDetail = views.GroupsDetail.as_view() views_GroupsVariableDetail = views.GroupsVariableDetail.as_view() +views_GroupsChildrenList = views.GroupsChildrenList.as_view() # host service views_HostsList = views.HostsList.as_view() @@ -117,6 +118,7 @@ urlpatterns = patterns('', # group service url(r'^api/v1/groups/$', views_GroupsList), url(r'^api/v1/groups/(?P[0-9]+)/$', views_GroupsDetail), + url(r'^api/v1/groups/(?P[0-9]+)/children/$', views_GroupsChildrenList), # variable data url(r'^api/v1/hosts/(?P[0-9]+)/variable_data/$', views_HostsVariableDetail),