diff --git a/lib/main/models/__init__.py b/lib/main/models/__init__.py index 998d046179..fc4e94d7f2 100644 --- a/lib/main/models/__init__.py +++ b/lib/main/models/__init__.py @@ -175,6 +175,10 @@ class Project(CommonModel): scm_type = models.CharField(max_length=64) default_playbook = models.CharField(max_length=1024) + def get_absolute_url(self): + import lib.urls + return reverse(lib.urls.views_ProjectsDetail, args=(self.pk,)) + class Permission(CommonModel): ''' A permission allows a user, project, or team to be able to use an inventory source. diff --git a/lib/main/tests.py b/lib/main/tests.py index fa103a5d44..19cdbd3fda 100644 --- a/lib/main/tests.py +++ b/lib/main/tests.py @@ -34,6 +34,12 @@ class BaseTest(django.test.TestCase): results.append(Organization.objects.create(name="org%s" % x, description="org%s" % x)) return results + def make_projects(self, count=1): + results = [] + for x in range(0, count): + results.append(Project.objects.create(name="proj%s" % x, description="proj%s" % x)) + return results + def check_pagination_and_size(self, data, desired_count, previous=None, next=None): self.assertEquals(data['count'], desired_count) self.assertEquals(data['previous'], previous) @@ -113,7 +119,21 @@ class OrganizationsTest(BaseTest): def setUp(self): self.setup_users() + self.organizations = self.make_organizations(10) + self.projects = self.make_projects(10) + + # add projects to organizations in a more or less arbitrary way + for project in self.projects[0:2]: + self.organizations[0].projects.add(project) + for project in self.projects[3:8]: + self.organizations[1].projects.add(project) + for project in self.projects[9:10]: + self.organizations[2].projects.add(project) + self.organizations[0].projects.add(self.projects[-1]) + self.organizations[9].projects.add(self.projects[-2]) + + # get the URL for various organization records self.a_detail_url = "%s%s" % (self.collection(), self.organizations[0].pk) self.b_detail_url = "%s%s" % (self.collection(), self.organizations[1].pk) self.c_detail_url = "%s%s" % (self.collection(), self.organizations[2].pk) @@ -178,13 +198,35 @@ class OrganizationsTest(BaseTest): # other user isn't a user or admin of anything, and similarly can't get in data = self.get(urls[0], expect=403, auth=self.get_other_credentials()) - - # FIXME: make sure related resource URLs are given here. (organizations/users, organizations/admins, organizations/projects) - # TODO: also implement those resources def test_get_item_subobjects_projects(self): - pass + + # first get all the URLs + orgs = self.get(self.collection(), expect=200, auth=self.get_super_credentials()) + # find projects attached to the first org + projects0_url = orgs['results'][0]['related']['projects'] + projects1_url = orgs['results'][1]['related']['projects'] + projects9_url = orgs['results'][9]['related']['projects'] + + self.get(projects0_url, expect=401, auth=None) + self.get(projects0_url, expect=401, auth=self.get_invalid_credentials()) + + # normal user is just a member of the first org, but can't see any projects yet + projects0a = self.get(projects0_url, expect=200, auth=self.get_normal_credentials()) + self.assertEquals(projects0a['count'], 0) + + # however in the second org, he's an admin and should see all of them + projects1a = self.get(projects1_url, expect=200, auth=self.get_normal_credentials()) + self.assertEquals(projects1a['count'], 5) + projects1b = self.get(projects1_url, expect=200, auth=self.get_other_credentials()) + self.assertEquals(projects1b['count'], 0) + + # superuser should be able to read anything + projects9a = self.get(projects9_url, expect=200, auth=self.get_super_credentials()) + self.assertEquals(projects9a['count'], 1) + + def test_get_item_subobjects_users(self): pass diff --git a/lib/main/views.py b/lib/main/views.py index 4a2e309361..405ec56773 100644 --- a/lib/main/views.py +++ b/lib/main/views.py @@ -3,7 +3,6 @@ from django.views.decorators.csrf import csrf_exempt from lib.main.models import * from lib.main.serializers import * from lib.main.rbac import * -from django.contrib.auth.models import AnonymousUser from django.core.exceptions import PermissionDenied from rest_framework import mixins from rest_framework import generics @@ -25,7 +24,6 @@ class BaseList(generics.ListCreateAPIView): raise exceptions.NotImplementedError def get_queryset(self): - return self._get_queryset().filter(active=True) class BaseDetail(generics.RetrieveUpdateDestroyAPIView): @@ -104,7 +102,7 @@ class OrganizationsUsersList(BaseList): def _get_queryset(self): # FIXME: - base = Users.objects.all(organizations__pk__in = [ 'FIXME' ]) + base = Users.objects.all(organizations__pk__in = [ self.kwargs.get('pk') ]) if self.request.user.is_superuser: return base.all() return base.objects.filter( @@ -125,7 +123,7 @@ class OrganizationsAdminsList(BaseList): def _get_queryset(self): # FIXME - base = Users.objects.all(admin_of_organizations__pk__in = [ 'FIXME' ]) + base = User.objects.all(admin_of_organizations__pk__in = [ self.kwargs.get('pk') ]) if self.request.user.is_superuser: return base.all() @@ -136,24 +134,53 @@ class OrganizationsAdminsList(BaseList): class OrganizationsProjectsList(BaseList): + model = Project + serializer_class = ProjectSerializer + permission_classes = (CustomRbac,) + # I can see the projects from the organization if: # I'm the superuser - # I am a member of the project # I am a an administrator of the organization + # I am a member of a team on the project def _get_queryset(self): - # FIXME: - base = Projects.objects.filter(organizations__in = [ 'FIXME' ]) + base = Project.objects.filter(organizations__in = [ self.kwargs.get('pk') ]) if self.request.user.is_superuser: return base.all() return base.filter( - organizations__organization__admins__in = [ self.request.user.application_user ] + organizations__admins__in = [ self.request.user.application_user ] ).distinct() | base.filter( - users__in = [ self.request.user.application_user ] + teams__users__in = [ self.request.user.application_user ] ).distinct() class OrganizationsTagsList(BaseList): # FIXME: guts & tests pass +class ProjectsDetail(BaseDetail): + + model = Project + serializer_class = ProjectSerializer + permission_classes = (CustomRbac,) + + def item_permissions_check(self, request, obj): + + # to get, must be in a team assigned to this project + # or be an org admin of an org this project is in + + raise exceptions.NotImplementedError() + + #is_admin = request.user.application_user in obj.admins.all() + #is_user = request.user.application_user in obj.users.all() + # + #if request.method == 'GET': + # return is_admin or is_user + #elif request.method in [ 'PUT' ]: + # return is_admin + #return False + + def delete_permissions_check(self, request, obj): + # FIXME: logic TBD + raise exceptions.NotImplementedError() + #return request.user.application_user in obj.admins.all() diff --git a/lib/urls.py b/lib/urls.py index d19c305cc6..c3e6b40d57 100644 --- a/lib/urls.py +++ b/lib/urls.py @@ -11,6 +11,22 @@ views_OrganizationsAdminsList = views.OrganizationsAdminsList.as_view() views_OrganizationsProjectsList = views.OrganizationsProjectsList.as_view() views_OrganizationsTagsList = views.OrganizationsTagsList.as_view() +# FIXME: add entries for all of these: + +# projects service +views_ProjectsDetail = views.OrganizationsDetail.as_view() + +# audit trail service +# team service +# inventory service +# group service +# host service +# inventory variable service +# log data services +# events services +# jobs services +# tags service + urlpatterns = patterns('', url(r'', include('lib.web.urls')), @@ -27,16 +43,28 @@ urlpatterns = patterns('', # FIXME: implement: # users service + # projects service + url(r'^api/v1/projects/(?P[0-9]+)/$', views_ProjectsDetail), + # audit trail service + # team service + # inventory service + # group service + # host service + # inventory variable service + # log data services + # events services + # jobs services + # tags service )