diff --git a/lib/main/base_views.py b/lib/main/base_views.py index e8d543a87c..e8480fdc8b 100644 --- a/lib/main/base_views.py +++ b/lib/main/base_views.py @@ -124,8 +124,12 @@ class BaseSubList(BaseList): return Response(status=status.HTTP_400_BAD_REQUEST, data=ser.errors) # ask the usual access control settings - if not self.__class__.model.can_user_add(request.user, ser.init_data): - raise PermissionDenied() + if self.__class__.model in [ User ]: + if not UserHelper.can_user_add(request.user, ser.init_data): + raise PermissionDenied() + else: + if not self.__class__.model.can_user_add(request.user, ser.init_data): + raise PermissionDenied() # save the object through the serializer, reload and returned the saved object deserialized obj = ser.save() @@ -140,10 +144,42 @@ class BaseSubList(BaseList): if self.__class__.parent_model != User: - if not obj.__class__.can_user_read(request.user, obj): - raise PermissionDenied() + + # FIXME: refactor into smaller functions + + if obj.__class__ in [ User]: + if self.__class__.parent_model == Organization: + # user can't inject an organization because it's not part of the user + # model so we have to cheat here. This may happen for other cases + # where we are creating a user immediately on a subcollection + # when that user does not already exist. Relations will work post-save. + organization = Organization.objects.get(pk=request.DATA[inject_primary_key]) + if not request.user.is_superuser: + if not organization.admins.filter(pk=request.user.pk).count() > 0: + raise PermissionDenied() + else: + raise exceptions.NotImplementedError() + else: + if not obj.__class__.can_user_read(request.user, obj): + raise PermissionDenied() if not self.__class__.parent_model.can_user_attach(request.user, main, obj, self.__class__.relationship, request.DATA): raise PermissionDenied() + + # FIXME: manual attachment code neccessary for users here, move this into the main code. + # this is because users don't have FKs into what they are attaching. (also refactor) + + if self.__class__.parent_model == Organization: + organization = Organization.objects.get(pk=request.DATA[inject_primary_key]) + import lib.main.views + if self.__class__ == lib.main.views.OrganizationsUsersList: + organization.users.add(obj) + organization.save() + elif self.__class__ == lib.main.views.OrganizationsAdminsList: + organization.admins.add(obj) + organization.save() + + + else: if not UserHelper.can_user_read(request.user, obj): raise PermissionDenied() diff --git a/lib/main/models/__init__.py b/lib/main/models/__init__.py index 57ceb24f5e..977244a355 100644 --- a/lib/main/models/__init__.py +++ b/lib/main/models/__init__.py @@ -130,6 +130,14 @@ class UserHelper(object): matching_orgs = obj.organizations.filter(admins__in = [user]).count() return matching_orgs + @classmethod + def can_user_add(cls, user, data): + # TODO: reuse. make helper functions like "is user an org admin" + # apply throughout permissions code + if user.is_superuser: + return True + return user.admin_of_organizations.count() > 0 + @classmethod def can_user_attach(cls, user, obj, sub_obj, relationship_type, data): if type(sub_obj) != User: @@ -193,7 +201,9 @@ class PrimordialModel(models.Model): # in order to attach something you also be able to read what you are attaching if type(sub_obj) == User: - return UserHelper.can_user_read(user, sub_obj) + # we already check that the user is an admin or org admin up in base_views.py + # because the user doesn't have the attributes on it directly to tie it to the org + return True else: return sub_obj.__class__.can_user_read(user, sub_obj) diff --git a/lib/main/tests/organizations.py b/lib/main/tests/organizations.py index 0502e4ce00..3f9a595500 100644 --- a/lib/main/tests/organizations.py +++ b/lib/main/tests/organizations.py @@ -270,6 +270,15 @@ class OrganizationsTest(BaseTest): users = self.get(url, expect=200, auth=self.get_normal_credentials()) self.assertEqual(users['count'], 1) + # post a completely new user to verify we can add users to the subcollection directly + new_user = dict(username='NewUser9000') + which_org = self.normal_django_user.admin_of_organizations.all()[0] + url = '/api/v1/organizations/%s/users/' % (which_org.pk) + posted = self.post(url, new_user, expect=201, auth=self.get_normal_credentials()) + + all_users = self.get(url, expect=200, auth=self.get_normal_credentials()) + self.assertEqual(all_users['count'], 2) + def test_post_item_subobjects_admins(self): url = '/api/v1/organizations/2/admins/'