diff --git a/.coveragerc b/.coveragerc index 6f38ccdf9..df1f6cc5f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,3 +5,4 @@ omit = cadasta/*/__init__.py cadasta/config/* cadasta/manage.py + cadasta/core/management/commands/* diff --git a/cadasta/accounts/manager.py b/cadasta/accounts/manager.py index c5edade7e..f7558586f 100644 --- a/cadasta/accounts/manager.py +++ b/cadasta/accounts/manager.py @@ -16,7 +16,7 @@ def get_from_username_or_email(self, identifier=None): "User with username or email {} does not exist" ).format(identifier) raise self.model.DoesNotExist(error) - elif users_count > 1: + else: error = _( "More than one user found for username or email {}" ).format(identifier) diff --git a/cadasta/accounts/serializers.py b/cadasta/accounts/serializers.py index 7668c9b82..f58b2999f 100644 --- a/cadasta/accounts/serializers.py +++ b/cadasta/accounts/serializers.py @@ -52,9 +52,9 @@ class Meta: 'last_name', 'email', 'email_verified', + 'last_login', ) extra_kwargs = { - 'password': {'write_only': True}, 'email': {'required': True, 'unique': True}, 'email_verified': {'read_only': True} } diff --git a/cadasta/accounts/tests/factories.py b/cadasta/accounts/tests/factories.py index 976b4dcc4..652513c83 100644 --- a/cadasta/accounts/tests/factories.py +++ b/cadasta/accounts/tests/factories.py @@ -1,9 +1,10 @@ import factory +from core.tests.factories import ExtendedFactory from ..models import User -class UserFactory(factory.django.DjangoModelFactory): +class UserFactory(ExtendedFactory): class Meta: model = User diff --git a/cadasta/accounts/tests/test_manager.py b/cadasta/accounts/tests/test_manager.py index 4e89e71f5..351f7cfc6 100644 --- a/cadasta/accounts/tests/test_manager.py +++ b/cadasta/accounts/tests/test_manager.py @@ -9,7 +9,9 @@ class UserManagerTest(TestCase): def test_get_from_usernamel(self): user = UserFactory.create() - found = User.objects.get_from_username_or_email(identifier=user.username) + found = User.objects.get_from_username_or_email( + identifier=user.username + ) assert found == user @@ -28,4 +30,6 @@ def test_mulitple_users_found(self): UserFactory.create(email='user@example.com') with raises(User.MultipleObjectsReturned): - User.objects.get_from_username_or_email(identifier='user@example.com') + User.objects.get_from_username_or_email( + identifier='user@example.com' + ) diff --git a/cadasta/accounts/tests/test_serializers.py b/cadasta/accounts/tests/test_serializers.py index d2973923b..6be2d52e9 100644 --- a/cadasta/accounts/tests/test_serializers.py +++ b/cadasta/accounts/tests/test_serializers.py @@ -2,40 +2,45 @@ from datetime import datetime from django.test import TestCase from django.utils.translation import gettext as _ +from rest_framework.test import APIRequestFactory, force_authenticate +from rest_framework.request import Request -from ..serializers import RegistrationSerializer, AccountLoginSerializer +from ..serializers import ( + RegistrationSerializer, UserSerializer, AccountLoginSerializer +) from ..models import User from ..exceptions import EmailNotVerifiedError from .factories import UserFactory +BASIC_TEST_DATA = { + 'username': 'imagine71', + 'email': 'john@beatles.uk', + 'password': 'iloveyoko79', + 'first_name': 'John', + 'last_name': 'Lennon', +} + + class RegistrationSerializerTest(TestCase): def test_field_serialization(self): user = UserFactory.build() serializer = RegistrationSerializer(user) - self.assertIn('email_verified', serializer.data) - self.assertNotIn('password', serializer.data) + assert 'email_verified' in serializer.data + assert 'password' not in serializer.data def test_create_with_valid_data(self): - data = { - 'username': 'imagine71', - 'email': 'john@beatles.uk', - 'password': 'iloveyoko79', - 'first_name': 'John', - 'last_name': 'Lennon', - } - - serializer = RegistrationSerializer(data=data) - self.assertTrue(serializer.is_valid()) + serializer = RegistrationSerializer(data=BASIC_TEST_DATA) + assert serializer.is_valid() serializer.save() - self.assertEqual(User.objects.count(), 1) + assert User.objects.count() == 1 user_obj = User.objects.first() - self.assertTrue(user_obj.check_password(data['password'])) - self.assertTrue(user_obj.is_active) - self.assertFalse(user_obj.email_verified) + assert user_obj.check_password(BASIC_TEST_DATA['password']) + assert user_obj.is_active + assert not user_obj.email_verified def test_create_without_email(self): """Serialiser should be invalid when no email address is provided.""" @@ -49,7 +54,7 @@ def test_create_without_email(self): } serializer = RegistrationSerializer(data=data) - self.assertFalse(serializer.is_valid()) + assert not serializer.is_valid() def test_create_with_existing_email(self): """Serialiser should be invalid when another user with the same email @@ -69,11 +74,62 @@ def test_create_with_existing_email(self): } serializer = RegistrationSerializer(data=data) - self.assertFalse(serializer.is_valid()) - self.assertIn( - _("Another user is already registered with this email address"), - serializer._errors['email'], + assert not serializer.is_valid() + assert (_("Another user is already registered with this email address") + in serializer._errors['email']) + + +class UserSerializerTest(TestCase): + def test_field_serialization(self): + user = UserFactory.build() + serializer = UserSerializer(user) + assert 'email_verified' in serializer.data + assert 'password' not in serializer.data + + def test_create_with_valid_data(self): + data = { + 'username': 'imagine71', + 'email': 'john@beatles.uk', + 'password': 'iloveyoko79', + 'first_name': 'John', + 'last_name': 'Lennon', + 'last_login': '2016-01-01 23:00:00' + } + serializer = UserSerializer(data=data) + assert serializer.is_valid() + + serializer.save() + assert User.objects.count() == 1 + + user_obj = User.objects.first() + assert user_obj.is_active + assert not user_obj.email_verified + + def test_update_username_fails(self): + serializer = UserSerializer(data=BASIC_TEST_DATA) + assert serializer.is_valid() + serializer.save() + user = User.objects.first() + other_user = UserFactory.create() + update_data = {'username': 'bad-update'} + request = APIRequestFactory().patch('/user/imagine71', update_data) + force_authenticate(request, user=other_user) + serializer2 = UserSerializer( + user, update_data, context={'request': Request(request)} ) + assert not serializer2.is_valid() + assert serializer2.errors['username'] == ['Cannot update username'] + + def test_update_last_login_fails(self): + serializer = UserSerializer(data=BASIC_TEST_DATA) + assert serializer.is_valid() + user = serializer.save() + update_data1 = {'username': 'imagine71', + 'email': 'john@beatles.uk', + 'last_login': '2016-01-01 23:00:00'} + serializer2 = UserSerializer(user, data=update_data1) + assert not serializer2.is_valid() + assert serializer2.errors['last_login'] == ['Cannot update last_login'] class AccountLoginSerializerTest(TestCase): diff --git a/cadasta/accounts/tests/test_urls_api.py b/cadasta/accounts/tests/test_urls_api.py index 470ae33fe..5376956bb 100644 --- a/cadasta/accounts/tests/test_urls_api.py +++ b/cadasta/accounts/tests/test_urls_api.py @@ -10,28 +10,28 @@ def test_account_user(self): assert reverse(version_ns('accounts:user')) == version_url('/account/') resolved = resolve(version_url('/account/')) - self.assertEqual(resolved.func.__name__, api.AccountUser.__name__) + assert resolved.func.__name__ == api.AccountUser.__name__ def test_account_register(self): assert (reverse(version_ns('accounts:register')) == version_url('/account/register/')) resolved = resolve(version_url('/account/register/')) - self.assertEqual(resolved.func.__name__, api.AccountRegister.__name__) + assert resolved.func.__name__ == api.AccountRegister.__name__ def test_account_login(self): assert (reverse(version_ns('accounts:login')) == version_url('/account/login/')) resolved = resolve(version_url('/account/login/')) - self.assertEqual(resolved.func.__name__, api.AccountLogin.__name__) + assert resolved.func.__name__ == api.AccountLogin.__name__ def test_account_activate(self): assert (reverse(version_ns('accounts:activate')) == version_url('/account/activate/')) resolved = resolve(version_url('/account/activate/')) - self.assertEqual(resolved.func.__name__, api.AccountVerify.__name__) + assert resolved.func.__name__ == api.AccountVerify.__name__ def test_password_reset(self): self.assertEqual( @@ -40,4 +40,4 @@ def test_password_reset(self): ) resolved = resolve(version_url('/account/password/reset/')) - self.assertEqual(resolved.func.__name__, api.PasswordReset.__name__) + assert resolved.func.__name__ == api.PasswordReset.__name__ diff --git a/cadasta/accounts/tests/test_views_api.py b/cadasta/accounts/tests/test_views_api.py index f3eccde75..abc55154b 100644 --- a/cadasta/accounts/tests/test_views_api.py +++ b/cadasta/accounts/tests/test_views_api.py @@ -16,109 +16,69 @@ class AccountUserTest(TestCase): + def _put(self, user, data, versioned=True, status=None, mails=None): + if versioned: + url = '/v1/account/' + else: + url = '/account/' + request = APIRequestFactory().put(url, data) + force_authenticate(request, user=user) + response = AccountUser.as_view()(request).render() + if status is not None: + assert response.status_code == status + if mails is not None: + assert len(mail.outbox) == mails + def test_update_email_address(self): """Service should send a verification email when the user updates their email.""" - user = UserFactory.create(**{ - 'username': 'imagine71', - 'email': 'john@beatles.uk' - }) - - data = { - 'email': 'boss@beatles.uk', - 'username': 'imagine71' - } - - request = APIRequestFactory().put('/account/', data) - force_authenticate(request, user=user) - response = AccountUser.as_view()(request).render() - self.assertEqual(response.status_code, 200) - self.assertEqual(len(mail.outbox), 1) + user = UserFactory.create(username='imagine71', + email='john@beatles.uk') + data = {'email': 'boss@beatles.uk', 'username': 'imagine71'} + self._put(user, data, versioned=False, status=200, mails=1) def test_keep_email_address(self): """Service should not send a verification email when the user does not their email.""" - user = UserFactory.create(**{ - 'username': 'imagine71', - 'email': 'john@beatles.uk', - }) - - data = { - 'email': 'john@beatles.uk', - 'username': 'imagine71' - } - - request = APIRequestFactory().put('/v1/account/', data) - force_authenticate(request, user=user) - response = AccountUser.as_view()(request).render() - self.assertEqual(response.status_code, 200) - self.assertEqual(len(mail.outbox), 0) + user = UserFactory.create(username='imagine71', + email='john@beatles.uk') + data = {'email': 'john@beatles.uk', 'username': 'imagine71'} + self._put(user, data, status=200, mails=0) def test_update_with_existing_email(self): - UserFactory.create(**{ - 'email': 'boss@beatles.uk' - }) - - user = UserFactory.create(**{ - 'email': 'john@beatles.uk', - }) - - data = { - 'email': 'boss@beatles.uk', - 'username': user.username - } - - request = APIRequestFactory().put('/v1/account/', data) - force_authenticate(request, user=user) - response = AccountUser.as_view()(request).render() - self.assertEqual(response.status_code, 400) - self.assertEqual(len(mail.outbox), 0) - + UserFactory.create(email='boss@beatles.uk') + user = UserFactory.create(email='john@beatles.uk') + data = {'email': 'boss@beatles.uk', 'username': user.username} + self._put(user, data, status=400, mails=0) user.refresh_from_db() - self.assertEqual(user.email, 'john@beatles.uk') + assert user.email == 'john@beatles.uk' def test_update_username(self): - user = UserFactory.create(**{ - 'username': 'imagine71' - }) - - data = { - 'email': user.email, - 'username': 'john' - } - - request = APIRequestFactory().put('/v1/account/', data) - force_authenticate(request, user=user) - response = AccountUser.as_view()(request).render() - self.assertEqual(response.status_code, 200) - + user = UserFactory.create(username='imagine71') + data = {'email': user.email, 'username': 'john'} + self._put(user, data, status=200) user.refresh_from_db() - self.assertEqual(user.username, 'john') + assert user.username == 'john' def test_update_with_existing_username(self): - UserFactory.create(**{ - 'username': 'boss' - }) - - user = UserFactory.create(**{ - 'username': 'john' - }) - - data = { - 'email': user.email, - 'username': 'boss' - } - - request = APIRequestFactory().put('/v1/account/', data) - force_authenticate(request, user=user) - response = AccountUser.as_view()(request).render() - self.assertEqual(response.status_code, 400) - + UserFactory.create(username='boss') + user = UserFactory.create(username='john') + data = {'email': user.email, 'username': 'boss'} + self._put(user, data, status=400) user.refresh_from_db() - self.assertEqual(user.username, 'john') + assert user.username == 'john' class AccountSignupTest(TestCase): + def _post(self, data, status=None, count=None): + url = '/v1/account/register/' + request = APIRequestFactory().post(url, data) + response = AccountRegister.as_view()(request).render() + if status is not None: + assert response.status_code == status + if count is not None: + assert User.objects.count() == count + def test_user_signs_up(self): data = { 'username': 'imagine71', @@ -127,11 +87,7 @@ def test_user_signs_up(self): 'first_name': 'John', 'last_name': 'Lennon', } - - request = APIRequestFactory().post('/v1/account/register/', data) - response = AccountRegister.as_view()(request).render() - self.assertEqual(response.status_code, 201) - self.assertEqual(User.objects.count(), 1) + self._post(data, status=201, count=1) def test_user_signs_up_with_invalid(self): """The server should respond with an 404 error code when a user tries @@ -142,11 +98,7 @@ def test_user_signs_up_with_invalid(self): 'first_name': 'John', 'last_name': 'Lennon', } - - request = APIRequestFactory().post('/v1/account/register/', data) - response = AccountRegister.as_view()(request).render() - self.assertEqual(response.status_code, 400) - self.assertEqual(User.objects.count(), 0) + self._post(data, status=400, count=0) class AccountLoginTest(TestCase): @@ -157,24 +109,30 @@ def setUp(self): 'password': 'iloveyoko79' }) + def _post(self, data, status=None, token=None): + url = '/v1/account/login/' + request = APIRequestFactory().post(url, data) + response = AccountLogin.as_view()(request).render() + content = None + if len(response.content) > 0: + content = response.content.decode("utf-8") + if status is not None: + assert response.status_code == status + if token is not None: + if token: + assert 'auth_token' in content + else: + assert 'auth_token' not in content + def test_successful_login(self): """The view should return a token to authenticate API calls""" - request = APIRequestFactory().post('/v1/account/login/', { - 'username': 'imagine71', - 'password': 'iloveyoko79' - }) - response = AccountLogin.as_view()(request).render() - self.assertEqual(response.status_code, 200) - self.assertIn('auth_token', response.content.decode("utf-8")) + self._post({'username': 'imagine71', 'password': 'iloveyoko79'}, + status=200, token=True) def test_unsuccessful_login(self): """The view should return a token to authenticate API calls""" - request = APIRequestFactory().post('/v1/account/login/', { - 'username': 'imagine71', - 'password': 'iloveyoko78' - }) - response = AccountLogin.as_view()(request).render() - self.assertEqual(response.status_code, 400) + self._post({'username': 'imagine71', 'password': 'iloveyoko78'}, + status=400) def test_login_with_unverified_email(self): """The view should return an error message if the User.verify_email_by @@ -182,15 +140,9 @@ def test_login_with_unverified_email(self): sent to the user.""" self.user.verify_email_by = datetime.now() self.user.save() - - request = APIRequestFactory().post('/v1/account/login/', { - 'username': 'imagine71', - 'password': 'iloveyoko79' - }) - response = AccountLogin.as_view()(request).render() - self.assertEqual(response.status_code, 400) - self.assertNotIn('auth_token', response.content.decode("utf-8")) - self.assertEqual(len(mail.outbox), 1) + self._post({'username': 'imagine71', 'password': 'iloveyoko79'}, + status=400, token=False) + assert len(mail.outbox) == 1 class AccountVerifyTest(TestCase): @@ -207,7 +159,7 @@ def test_activate_account(self): 'token': token }) response = AccountVerify.as_view()(request).render() - self.assertEqual(response.status_code, 200) + assert response.status_code == 200 user.refresh_from_db() - self.assertTrue(user.email_verified) + assert user.email_verified diff --git a/cadasta/accounts/tests/test_views_default.py b/cadasta/accounts/tests/test_views_default.py index 277414e29..19eeb081b 100644 --- a/cadasta/accounts/tests/test_views_default.py +++ b/cadasta/accounts/tests/test_views_default.py @@ -3,6 +3,7 @@ from django.contrib.auth.models import AnonymousUser from django.template.loader import render_to_string from django.template import RequestContext +from django.contrib import messages from django.contrib.messages.storage.fallback import FallbackStorage from accounts.tests.factories import UserFactory @@ -66,3 +67,21 @@ def test_update_profile_when_no_user_is_signed_in(self): response = self.view(self.request) assert response.status_code == 302 assert '/account/login/' in response['location'] + + def test_update_profile_duplicate_email(self): + user1 = UserFactory.create(username='John', + first_name='John', + last_name='Lennon') + user2 = UserFactory.create(username='Bill') + setattr(self.request, 'user', user2) + setattr(self.request, 'method', 'POST') + setattr(self.request, 'POST', { + 'username': 'Bill', + 'email': user1.email, + 'first_name': 'Bill', + 'last_name': 'Bloggs', + }) + + self.view(self.request) + msgs = messages.get_messages(self.request) + assert list(msgs)[0].message == 'Failed to update profile information' diff --git a/cadasta/core/fixtures.py b/cadasta/core/fixtures.py new file mode 100644 index 000000000..82ef0748f --- /dev/null +++ b/cadasta/core/fixtures.py @@ -0,0 +1,251 @@ +import os.path +import factory +from faker import Factory +from django.contrib.gis.geos import GEOSGeometry +from django.conf import settings +from datetime import datetime, timezone + +from accounts.models import User +from organization.models import Organization, Project, OrganizationRole +from tutelary.models import Policy, Role, PolicyInstance, RolePolicyAssign + +from accounts.tests.factories import UserFactory +from organization.tests.factories import OrganizationFactory, ProjectFactory +from core.tests.factories import PolicyFactory, RoleFactory + + +class FixturesData: + def add_test_users(self): + users = [] + # the first two named users will have superuser access + named_users = [ + {'username': 'iross', 'email': 'iross@cadasta.org', + 'first_name': 'Ian', 'last_name': 'Ross'}, + {'username': 'oroick', 'email': 'oroick@cadasta.org', + 'first_name': 'Oliver', 'last_name': 'Roick'}] + # add user's with names in languages that need to be tested. + languages = ['el_GR', 'ja_JP', 'hi_IN', 'hr_HR', 'lt_LT'] + named_users.append({ + 'first_name': 'עזרא', + 'last_name': 'ברש' + }) + for lang in languages: + fake = Factory.create(lang) + named_users.append({ + 'first_name': fake.first_name(), + 'last_name': fake.last_name() + }) + for n in range(20): + if n < len(named_users): + users.append(UserFactory.create( + **named_users[n], + password='password', + email_verified=True, + last_login=datetime.now(tz=timezone.utc), + is_active=True, + )) + else: + users.append(UserFactory.create( + password='password', + is_active=(n < 8), + first_name=factory.Faker('first_name'), + last_name=factory.Faker('last_name'), + )) + print('Successfully added test users.') + return users + + def add_test_users_and_roles(self): + users = FixturesData.add_test_users(self) + orgs = Organization.objects.all() + + PolicyFactory.set_directory( + os.path.join(settings.BASE_DIR, 'permissions') + ) + + pols = {} + for pol in ['default', 'superuser', 'org-admin', 'project-manager', + 'data-collector', 'project-user']: + pols[pol] = PolicyFactory.create(name=pol, file=pol + '.json') + + roles = {} + roles['superuser'] = RoleFactory.create( + name='superuser', + policies=[pols['default'], pols['superuser']] + ) + users[0].assign_policies(roles['superuser']) + users[1].assign_policies(roles['superuser']) + + for i in [0, 1, 3, 4, 7, 10]: + admin = i == 3 or i == 4 + OrganizationRole.objects.create( + organization=orgs[0], user=users[i], admin=admin + ) + + for i in [0, 1, 2, 4, 8, 10]: + admin = i == 2 or i == 4 + OrganizationRole.objects.create( + organization=orgs[1], user=users[i], admin=admin + ) + + print("{} and {} have superuser policies.".format(users[0], users[1])) + + def add_test_organizations(self): + orgs = [] + orgs.append(OrganizationFactory.create( + name='Habitat for Humanity (Test)', slug='habitat-for-humanity', + description="""Habitat for Humanity is a nonprofit, ecumenical Christian ministry + that builds with people in need regardless of race or religion. Since + 1976, Habitat has helped 6.8 million people find strength, stability + and independence through safe, decent and affordable shelter.""", + urls=['http://www.habitat.org'], + logo='https://s3.amazonaws.com/cadasta-dev-tmp/logos/h4h.png', + contacts=[{'email': 'info@habitat.org'}] + )) + orgs.append(OrganizationFactory.create( + name='Cadasta (Test)', slug='cadasta', + description="""Cadasta Foundation is dedicated to the support, continued + development and growth of the Cadasta Platform – an innovative, open + source suite of tools for the collection and management of ownership, + occupancy, and spatial data that meets the unique challenges of this + process in much of the world.""", + urls=['http://www.cadasta.org'], + logo='https://s3.amazonaws.com/cadasta-dev-tmp/logos/cadasta.png', + contacts=[{'email': 'info@cadasta.org'}] + )) + + print('\nSuccessfully added organizations {}'. + format(Organization.objects.all())) + + def add_test_projects(self): + projs = [] + orgs = Organization.objects.filter(name__contains='Test') + + projs.append(ProjectFactory.create( + name='Kibera Test Project', + project_slug='kibera', + description="""This is a test project. This is a test project. + This is a test project. This is a test project. This is a test + project. This is a test project. This is a test project. This + is a test project. This is a test project.""", + organization=orgs[0], + country='KE', + extent=('SRID=4326;' + 'POLYGON ((-5.1031494140625000 8.1299292850467957, ' + '-5.0482177734375000 7.6837733211111425, ' + '-4.6746826171875000 7.8252894725496338, ' + '-4.8641967773437491 8.2278005261522775, ' + '-5.1031494140625000 8.1299292850467957))') + )) + projs.append(ProjectFactory.create( + name='H4H Test Project', + project_slug='h4h-test-project', + description="""This is a test project. This is a test project. + This is a test project. This is a test project. This is a test + project. This is a test project. This is a test project. This + is a test project. This is a test project.""", + organization=orgs[0], + country='PH', + extent=('SRID=4326;' + 'POLYGON ((-63.6328125000000000 44.7233201889582475, ' + '-63.3691406250000000 45.3830192789906519, ' + '-61.6992187500000000 45.6140374113509282, ' + '-61.1059570312500000 45.2439534226232425, ' + '-63.6328125000000000 44.7233201889582475))') + )) + projs.append(ProjectFactory.create( + name='Cadasta Indonesia Test Project', + project_slug='cadasta-indonesia-test-project', + description="""This is another test project. This is another test + project. This is another test project. This is another test + project. This is another test project. This is a test project. + This is another test project. This is another test project. + This is another test project.""", + organization=orgs[1], + country='ID', + extent=('SRID=4326;' + 'POLYGON ((-57.0520019531250000 -1.0793428942462329, ' + '-56.7553710937499929 -0.6646579437921112, ' + '-56.3790893554687500 -1.1562325507679554, ' + '-56.3186645507812429 -1.4774973547127075, ' + '-56.8405151367187500 -1.4500404973607948, ' + '-57.0520019531250000 -1.0793428942462329))') + )) + projs.append(ProjectFactory.create( + name='Cadasta Myanmar Test Project', + project_slug='cadasta-myanmar-test-project', + description=""""This is another test project. This is another test + project. This is another test project. This is another test + project. This is another test project. This is a test project. + This is another test project. This is another test project. + This is another test project.""", + organization=orgs[1], + country='MM' + )) + projs.append(ProjectFactory.create( + name='London 1', + project_slug='london-1', + description=""""This is another test project. This is another test + project. This is another test project. This is another test + project. This is another test project. This is a test project. + This is another test project. This is another test project. + This is another test project.""", + organization=orgs[1], + country='MM', + extent=GEOSGeometry( + '{"type": "Polygon",' + '"coordinates": [[[-0.17329216003417966,51.51194758264939],' + '[-0.17303466796874997,51.511092905004745],' + '[-0.1709747314453125,51.51023821132554],' + '[-0.17037391662597656,51.507406923983446],' + '[-0.1746654510498047,51.50211782162702],' + '[-0.1533794403076172,51.503239803730864],' + '[-0.15226364135742185,51.505964502406805],' + '[-0.15913009643554688,51.51322956905176],' + '[-0.17329216003417966,51.51194758264939]]]}') + )) + projs.append(ProjectFactory.create( + name='London 2', + project_slug='london-2', + description=""""This is another test project. This is another test + project. This is another test project. This is another test + project. This is another test project. This is a test project. + This is another test project. This is another test project. + This is another test project.""", + organization=orgs[1], + country='MM', + extent=GEOSGeometry( + '{"type": "Polygon",' + '"coordinates": [[[-0.1878833770751953,51.509864277798705],' + '[-0.18393516540527344,51.50201096474784],' + '[-0.17500877380371094,51.501690392607],' + '[-0.17226219177246094,51.50671243040582],' + '[-0.171661376953125,51.51152024583139],' + '[-0.18642425537109375,51.509864277798705],' + '[-0.1878833770751953,51.509864277798705]]]}') + )) + print('\nSuccessfully added organizations {}'. + format(Project.objects.all())) + + def delete_test_organizations(self): + orgs = Organization.objects.filter(name__contains='Test') + for org in orgs: + org.delete() + + PolicyInstance.objects.all().delete() + RolePolicyAssign.objects.all().delete() + Policy.objects.all().delete() + Role.objects.all().delete() + + def delete_test_users(self): + users = User.objects.filter(username__startswith='testuser') + for user in users: + user.delete() + # Specified named users. + named_users = ['iross', 'oroick'] + for user in named_users: + if User.objects.filter(username=user).exists(): + User.objects.get(username=user).delete() + + def delete_test_projects(self): + projs = Project.objects.filter(name__contains='Test Project') + projs.delete() diff --git a/cadasta/core/management/commands/loadfixtures.py b/cadasta/core/management/commands/loadfixtures.py index f19226c77..35c6cefeb 100644 --- a/cadasta/core/management/commands/loadfixtures.py +++ b/cadasta/core/management/commands/loadfixtures.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand -from core.tests.factories import FixturesData +from core.fixtures import FixturesData class Command(BaseCommand): @@ -18,15 +18,15 @@ def add_arguments(self, parser): ) def handle(self, *args, **options): - data = FixturesData + data = FixturesData() if options['delete']: - data.delete_test_users(self) - data.delete_test_organizations(self) - data.delete_test_projects(self) + data.delete_test_users() + data.delete_test_organizations() + data.delete_test_projects() else: - data.add_test_organizations(self) - data.add_test_users_and_roles(self) - data.add_test_projects(self) + data.add_test_organizations() + data.add_test_users_and_roles() + data.add_test_projects() self.stdout.write(self.style.SUCCESS("All test data loaded.")) diff --git a/cadasta/core/mixins.py b/cadasta/core/mixins.py index 4a0824990..e1fc2b665 100644 --- a/cadasta/core/mixins.py +++ b/cadasta/core/mixins.py @@ -9,7 +9,7 @@ class PermissionRequiredMixin(mixins.PermissionRequiredMixin): def handle_no_permission(self): msg = super().handle_no_permission() messages.add_message(self.request, messages.WARNING, - msg[0] if len(msg) > 0 + msg[0] if len(msg) > 0 and len(msg[0]) > 0 else _("PERMISSION DENIED")) return redirect(self.request.META.get('HTTP_REFERER', '/')) diff --git a/cadasta/core/models.py b/cadasta/core/models.py index 2ca8bb9c6..e787f4b77 100644 --- a/cadasta/core/models.py +++ b/cadasta/core/models.py @@ -13,14 +13,13 @@ def save(self, *args, **kwargs): if not self.id: kwargs['force_insert'] = True - while True: + ok = False + while not ok: self.id = random_id() if not type(self).objects.filter(pk=self.id).exists(): + ok = True super(RandomIDModel, self).save(*args, **kwargs) - break - else: - continue else: super(RandomIDModel, self).save(*args, **kwargs) diff --git a/cadasta/core/tests/factories.py b/cadasta/core/tests/factories.py index 830e38eb0..f0cd55d1c 100644 --- a/cadasta/core/tests/factories.py +++ b/cadasta/core/tests/factories.py @@ -1,15 +1,16 @@ import os.path import factory -from faker import Factory -from django.contrib.gis.geos import GEOSGeometry -from datetime import datetime, timezone -from accounts.models import User -from organization.models import Organization, Project, OrganizationRole -from tutelary.models import Policy, Role, PolicyInstance, RolePolicyAssign +from tutelary.models import Policy, Role -from accounts.tests.factories import UserFactory -from organization.tests.factories import OrganizationFactory, ProjectFactory + +class ExtendedFactory(factory.django.DjangoModelFactory): + @classmethod + def create_from_kwargs(cls, kwargs_list): + objs = [] + for kwargs in kwargs_list: + objs.append(cls.create(**kwargs)) + return objs class PolicyFactory(factory.django.DjangoModelFactory): @@ -30,215 +31,3 @@ def _adjust_kwargs(cls, **kwargs): class RoleFactory(factory.django.DjangoModelFactory): class Meta: model = Role - - -class FixturesData(): - def add_test_users(self): - users = [] - # the first two named users will have superuser access - named_users = [ - {'username': 'iross', 'email': 'iross@cadasta.org', - 'first_name': 'Ian', 'last_name': 'Ross'}, - {'username': 'oroick', 'email': 'oroick@cadasta.org', - 'first_name': 'Oliver', 'last_name': 'Roick'}] - # add user's with names in languages that need to be tested. - languages = ['el_GR', 'ja_JP', 'hi_IN', 'hr_HR', 'lt_LT'] - named_users.append({ - 'first_name': 'עזרא', - 'last_name': 'ברש' - }) - for lang in languages: - fake = Factory.create(lang) - named_users.append({ - 'first_name': fake.first_name(), - 'last_name': fake.last_name() - }) - for n in range(20): - if n < len(named_users): - users.append(UserFactory.create( - **named_users[n], - password='password', - email_verified=True, - last_login=datetime.now(tz=timezone.utc), - is_active=True, - )) - else: - users.append(UserFactory.create( - password='password', - is_active=(n < 8), - first_name=factory.Faker('first_name'), - last_name=factory.Faker('last_name'), - )) - self.stdout.write(self.style.SUCCESS('Successfully added test users.')) - return users - - def add_test_users_and_roles(self): - users = FixturesData.add_test_users(self) - orgs = Organization.objects.all() - - PolicyFactory.set_directory('config/permissions') - - pols = {} - for pol in ['default', 'superuser', 'org-admin', 'project-manager', - 'data-collector', 'project-user']: - pols[pol] = PolicyFactory.create(name=pol, file=pol + '.json') - - roles = {} - roles['superuser'] = RoleFactory.create( - name='superuser', - policies=[pols['default'], pols['superuser']] - ) - for org in ['habitat-for-humanity', 'cadasta']: - for abbrev, pol in [('oa', 'org-admin')]: - roles[org + '-' + abbrev] = RoleFactory.create( - name=org + '-' + abbrev, - policies=[pols['default'], pols[pol]], - variables={'organization': org}) - - users[0].assign_policies(roles['superuser']) - users[1].assign_policies(roles['superuser']) - - for i in [0, 1, 3, 4, 7, 10]: - admin = i == 3 or i == 4 - OrganizationRole.objects.create( - organization=orgs[0], user=users[i], admin=admin - ) - - for i in [0, 1, 2, 4, 8, 10]: - admin = i == 2 or i == 4 - OrganizationRole.objects.create( - organization=orgs[1], user=users[i], admin=admin - ) - - self.stdout.write(self.style.SUCCESS( - "{} and {} have superuser policies." - .format(users[0], users[1]))) - - def add_test_organizations(self): - orgs = [] - orgs.append(OrganizationFactory.create( - name='Habitat for Humanity (Test)', slug='habitat-for-humanity', - description="""Habitat for Humanity is a nonprofit, ecumenical Christian ministry - that builds with people in need regardless of race or religion. Since - 1976, Habitat has helped 6.8 million people find strength, stability - and independence through safe, decent and affordable shelter.""", - urls=['http://www.habitat.org'], - logo='https://s3.amazonaws.com/cadasta-dev-tmp/logos/h4h.png', - contacts=[{'email': 'info@habitat.org'}] - )) - orgs.append(OrganizationFactory.create( - name='Cadasta (Test)', slug='cadasta', - description="""Cadasta Foundation is dedicated to the support, continued - development and growth of the Cadasta Platform – an innovative, open - source suite of tools for the collection and management of ownership, - occupancy, and spatial data that meets the unique challenges of this - process in much of the world.""", - urls=['http://www.cadasta.org'], - logo='https://s3.amazonaws.com/cadasta-dev-tmp/logos/cadasta.png', - contacts=[{'email': 'info@cadasta.org'}] - )) - - self.stdout.write(self.style.SUCCESS( - '\nSuccessfully added organizations {}' - .format(Organization.objects.all()))) - - def add_test_projects(self): - projs = [] - orgs = Organization.objects.filter(name__contains='Test') - - projs.append(ProjectFactory.create( - name='Kibera Test Project', - project_slug='kibera', - description="""This is a test project. This is a test project. - This is a test project. This is a test project. This is a test - project. This is a test project. This is a test project. This - is a test project. This is a test project.""", - organization=orgs[0], - country='KE', - extent='SRID=4326;POLYGON ((-5.1031494140625000 8.1299292850467957, -5.0482177734375000 7.6837733211111425, -4.6746826171875000 7.8252894725496338, -4.8641967773437491 8.2278005261522775, -5.1031494140625000 8.1299292850467957))' - )) - projs.append(ProjectFactory.create( - name='H4H Test Project', - project_slug='h4h-test-project', - description="""This is a test project. This is a test project. - This is a test project. This is a test project. This is a test - project. This is a test project. This is a test project. This - is a test project. This is a test project.""", - organization=orgs[0], - country='PH', - extent='SRID=4326;POLYGON ((-63.6328125000000000 44.7233201889582475, -63.3691406250000000 45.3830192789906519, -61.6992187500000000 45.6140374113509282, -61.1059570312500000 45.2439534226232425, -63.6328125000000000 44.7233201889582475))' - )) - projs.append(ProjectFactory.create( - name='Cadasta Indonesia Test Project', - project_slug='cadasta-indonesia-test-project', - description="""This is another test project. This is another test - project. This is another test project. This is another test - project. This is another test project. This is a test project. - This is another test project. This is another test project. - This is another test project.""", - organization=orgs[1], - country='ID', - extent='SRID=4326;POLYGON ((-57.0520019531250000 -1.0793428942462329, -56.7553710937499929 -0.6646579437921112, -56.3790893554687500 -1.1562325507679554, -56.3186645507812429 -1.4774973547127075, -56.8405151367187500 -1.4500404973607948, -57.0520019531250000 -1.0793428942462329))' - )) - projs.append(ProjectFactory.create( - name='Cadasta Myanmar Test Project', - project_slug='cadasta-myanmar-test-project', - description=""""This is another test project. This is another test - project. This is another test project. This is another test - project. This is another test project. This is a test project. - This is another test project. This is another test project. - This is another test project.""", - organization=orgs[1], - country='MM' - )) - projs.append(ProjectFactory.create( - name='London 1', - project_slug='london-1', - description=""""This is another test project. This is another test - project. This is another test project. This is another test - project. This is another test project. This is a test project. - This is another test project. This is another test project. - This is another test project.""", - organization=orgs[1], - country='MM', - extent=GEOSGeometry('{"type": "Polygon","coordinates": [[[-0.17329216003417966,51.51194758264939],[-0.17303466796874997,51.511092905004745],[-0.1709747314453125,51.51023821132554],[-0.17037391662597656,51.507406923983446],[-0.1746654510498047,51.50211782162702],[-0.1533794403076172,51.503239803730864],[-0.15226364135742185,51.505964502406805],[-0.15913009643554688,51.51322956905176],[-0.17329216003417966,51.51194758264939]]]}') - )) - projs.append(ProjectFactory.create( - name='London 2', - project_slug='london-2', - description=""""This is another test project. This is another test - project. This is another test project. This is another test - project. This is another test project. This is a test project. - This is another test project. This is another test project. - This is another test project.""", - organization=orgs[1], - country='MM', - extent=GEOSGeometry('{"type": "Polygon","coordinates": [[[-0.1878833770751953,51.509864277798705],[-0.18393516540527344,51.50201096474784],[-0.17500877380371094,51.501690392607],[-0.17226219177246094,51.50671243040582],[-0.171661376953125,51.51152024583139],[-0.18642425537109375,51.509864277798705],[-0.1878833770751953,51.509864277798705]]]}') - )) - self.stdout.write(self.style.SUCCESS( - '\nSuccessfully added organizations {}' - .format(Project.objects.all()))) - - def delete_test_organizations(self): - orgs = Organization.objects.filter(name__contains='Test') - for org in orgs: - org.delete() - - PolicyInstance.objects.all().delete() - RolePolicyAssign.objects.all().delete() - Policy.objects.all().delete() - Role.objects.all().delete() - - def delete_test_users(self): - users = User.objects.filter(username__startswith='testuser') - for user in users: - user.delete() - # Specified named users. - named_users = ['iross', 'oroick'] - for user in named_users: - if User.objects.filter(username=user).exists(): - User.objects.get(username=user).delete() - - def delete_test_projects(self): - projs = Project.objects.filter(name__contains='Test Project') - projs.delete() diff --git a/cadasta/core/tests/test_fixtures.py b/cadasta/core/tests/test_fixtures.py new file mode 100644 index 000000000..459a53e3a --- /dev/null +++ b/cadasta/core/tests/test_fixtures.py @@ -0,0 +1,30 @@ +from django.test import TestCase + +from ..fixtures import FixturesData +from tutelary.models import Policy +from accounts.models import User +from organization.models import Organization, Project + + +class FixturesTest(TestCase): + def test_fixture_setup(self): + data = FixturesData() + data.delete_test_users() + data.delete_test_organizations() + data.delete_test_projects() + data.add_test_organizations() + data.add_test_users_and_roles() + data.add_test_projects() + + assert User.objects.count() == 20 + assert Policy.objects.count() == 6 + assert Organization.objects.count() == 2 + assert Project.objects.count() == 6 + + data.delete_test_users() + data.delete_test_organizations() + data.delete_test_projects() + + assert User.objects.count() == 0 + assert Organization.objects.count() == 0 + assert Project.objects.count() == 0 diff --git a/cadasta/core/tests/test_models.py b/cadasta/core/tests/test_models.py index fd34ae168..8fe611fed 100644 --- a/cadasta/core/tests/test_models.py +++ b/cadasta/core/tests/test_models.py @@ -1,3 +1,4 @@ +import random from django.test import TestCase from ..models import RandomIDModel @@ -13,4 +14,13 @@ class RandomIDModelTest(TestCase): def test_save(self): instance = MyTestModel() instance.save() - self.assertIsNotNone(instance.id) + assert instance.id is not None + + def test_duplicate_ids(self): + random.seed(a=10) + instance1 = MyTestModel() + instance1.save() + random.seed(a=10) + instance2 = MyTestModel() + instance2.save() + assert instance1.id != instance2.id diff --git a/cadasta/core/tests/test_views_api.py b/cadasta/core/tests/test_views_api.py index ef944d2c0..80a80b37f 100644 --- a/cadasta/core/tests/test_views_api.py +++ b/cadasta/core/tests/test_views_api.py @@ -31,7 +31,8 @@ def test_set_exception_with_NotFound(self): def test_evaluate_json(self): response_data = { 'contacts': [ - '{"name": "This field is required.", "url": "\'blah\' is not a \'uri\'"}', + '{"name": "This field is required.", ' + '"url": "\'blah\' is not a \'uri\'"}', '{"name": "This field is required."}', ], 'field': "Something went wrong" @@ -41,7 +42,8 @@ def test_evaluate_json(self): expected = { 'contacts': [ - {'name': "This field is required.", 'url': "\'blah\' is not a \'uri\'"}, + {'name': "This field is required.", + 'url': "\'blah\' is not a \'uri\'"}, {'name': "This field is required."}, ], 'field': "Something went wrong" diff --git a/cadasta/organization/forms.py b/cadasta/organization/forms.py index 40ddb35b6..004083d97 100644 --- a/cadasta/organization/forms.py +++ b/cadasta/organization/forms.py @@ -11,8 +11,7 @@ from accounts.models import User from .models import Organization, OrganizationRole, ProjectRole from .choices import ADMIN_CHOICES, ROLE_CHOICES - -FORM_CHOICES = ROLE_CHOICES + (('Pb', 'Public User'),) +FORM_CHOICES = ROLE_CHOICES + (('Pb', _('Public User')),) class OrganizationForm(forms.ModelForm): @@ -43,7 +42,6 @@ def clean_contacts(self): return contacts def save(self, *args, **kwargs): - print('save') instance = super(OrganizationForm, self).save(commit=False) create = not instance.id @@ -169,16 +167,18 @@ def __init__(self, organization, *args, **kwargs): if organization is not None: self.organization = organization org = Organization.objects.get(slug=organization) - su_role = Role.objects.get(name='superuser') - oa_role = Role.objects.get(name=organization + '-oa') + try: + su_role = Role.objects.get(name='superuser') + except Role.DoesNotExist: + su_role = None self.members = [] for user, idx in zip(org.users.all(), itertools.count()): - is_admin = False - for pol in user.assigned_policies(): - if (isinstance(pol, Role) and - (pol == su_role or pol == oa_role)): + is_admin = any([isinstance(pol, Role) and pol == su_role + for pol in user.assigned_policies()]) + if not is_admin: + if OrganizationRole.objects.get(organization=org, + user=user).admin: is_admin = True - break f = None if not is_admin: f = forms.ChoiceField(choices=ROLE_CHOICES) diff --git a/cadasta/organization/models.py b/cadasta/organization/models.py index 6ea3076f8..30567f620 100644 --- a/cadasta/organization/models.py +++ b/cadasta/organization/models.py @@ -7,7 +7,7 @@ import django.contrib.gis.db.models as gismodels from tutelary.decorators import permissioned_model -from tutelary.models import Policy +from tutelary.models import Policy, Role from core.models import RandomIDModel from .validators import validate_contact diff --git a/cadasta/organization/serializers.py b/cadasta/organization/serializers.py index 91f562d9f..a55c9c3b5 100644 --- a/cadasta/organization/serializers.py +++ b/cadasta/organization/serializers.py @@ -79,8 +79,8 @@ def get_org(self, object): def get_url(self, object): return reverse( 'organization:project-dashboard', - kwargs={ 'organization': object.organization.slug, - 'project': object.project_slug }) + kwargs={'organization': object.organization.slug, + 'project': object.project_slug}) class EntityUserSerializer(serializers.Serializer): diff --git a/cadasta/organization/tests/factories.py b/cadasta/organization/tests/factories.py index 1f1af5ae6..2d7e76c89 100644 --- a/cadasta/organization/tests/factories.py +++ b/cadasta/organization/tests/factories.py @@ -1,9 +1,10 @@ import factory +from core.tests.factories import ExtendedFactory from ..models import Organization, OrganizationRole, Project, ProjectRole -class OrganizationFactory(factory.django.DjangoModelFactory): +class OrganizationFactory(ExtendedFactory): class Meta: model = Organization @@ -24,7 +25,7 @@ def add_users(self, create, users, **kwargs): OrganizationRole.objects.create(organization=self, user=u) -class ProjectFactory(factory.django.DjangoModelFactory): +class ProjectFactory(ExtendedFactory): class Meta: model = Project diff --git a/cadasta/organization/tests/test_forms.py b/cadasta/organization/tests/test_forms.py index 719e36d0b..3e0a2d59d 100644 --- a/cadasta/organization/tests/test_forms.py +++ b/cadasta/organization/tests/test_forms.py @@ -10,6 +10,12 @@ class OrganzationAddTest(TestCase): + def _save(self, data, count=1): + form = forms.OrganizationForm(data, user=UserFactory.create()) + form.save() + assert form.is_valid() is True + assert Organization.objects.count() == count + def test_add_organization(self): data = { 'name': 'Org', @@ -17,16 +23,27 @@ def test_add_organization(self): 'urls': '', 'contacts': '' } - form = forms.OrganizationForm(data, user=UserFactory.create()) - form.save() - - assert form.is_valid() is True - assert Organization.objects.count() == 1 - + self._save(data) org = Organization.objects.first() assert org.slug == 'org' assert OrganizationRole.objects.filter(organization=org).count() == 1 + def test_unique_slugs(self): + data = { + 'name': 'Org', + 'description': 'Org description #1', + 'urls': '', + 'contacts': '' + } + self._save(data) + org1 = Organization.objects.first() + assert org1.slug == 'org' + data['description'] = 'Org description #2' + self._save(data, count=2) + orgs = Organization.objects.all() + assert len(orgs) == 2 + assert orgs[0].slug != orgs[1].slug + def test_add_organization_with_url(self): data = { 'name': 'Org', @@ -34,12 +51,7 @@ def test_add_organization_with_url(self): 'urls': 'http://example.com', 'contacts': '' } - form = forms.OrganizationForm(data, user=UserFactory.create()) - form.save() - - assert form.is_valid() is True - assert Organization.objects.count() == 1 - + self._save(data) org = Organization.objects.first() assert org.urls == ['http://example.com'] @@ -48,22 +60,12 @@ def test_add_organization_with_contact(self): 'name': 'Org', 'description': 'Org description', 'urls': 'http://example.com', - 'contacts': json.dumps([{ - 'name': 'Ringo Starr', - 'tel': '555-5555' - }]) + 'contacts': json.dumps([{'name': 'Ringo Starr', + 'tel': '555-5555'}]) } - form = forms.OrganizationForm(data, user=UserFactory.create()) - form.save() - - assert form.is_valid() is True - assert Organization.objects.count() == 1 - + self._save(data) org = Organization.objects.first() - assert org.contacts == [{ - 'name': 'Ringo Starr', - 'tel': '555-5555' - }] + assert org.contacts == [{'name': 'Ringo Starr', 'tel': '555-5555'}] def test_update_organization(self): org = OrganizationFactory.create(**{'slug': 'some-org'}) @@ -86,43 +88,32 @@ def test_update_organization(self): class AddOrganizationMemberFormTest(TestCase): - def test_add_with_username(self): + def _save(self, identifier=None, identifier_field=None, ok=True): org = OrganizationFactory.create() user = UserFactory.create() - - data = {'identifier': user.username} + if identifier_field is not None: + identifier = getattr(user, identifier_field) + data = {'identifier': identifier} form = forms.AddOrganizationMemberForm(data, organization=org) + if ok: + form.save() + assert form.is_valid() is True + assert OrganizationRole.objects.filter( + organization=org, user=user).count() == 1 + else: + with raises(ValueError): + form.save() + assert form.is_valid() is False + assert OrganizationRole.objects.count() == 0 - form.save() - - assert form.is_valid() is True - assert OrganizationRole.objects.filter( - organization=org, user=user).count() == 1 + def test_add_with_username(self): + self._save(identifier_field='username') def test_add_with_email(self): - org = OrganizationFactory.create() - user = UserFactory.create() - - data = {'identifier': user.email} - form = forms.AddOrganizationMemberForm(data, organization=org) - - form.save() - - assert form.is_valid() is True - assert OrganizationRole.objects.filter( - organization=org, user=user).count() == 1 + self._save(identifier_field='email') def test_add_non_existing_user(self): - org = OrganizationFactory.create() - - data = {'identifier': 'some-user'} - form = forms.AddOrganizationMemberForm(data, organization=org) - - with raises(ValueError): - form.save() - - assert form.is_valid() is False - assert OrganizationRole.objects.count() == 0 + self._save(identifier='some-user', ok=False) class EditOrganizationMemberFormTest(TestCase): diff --git a/cadasta/organization/tests/test_models.py b/cadasta/organization/tests/test_models.py index 3f2323fe8..c1f2f0264 100644 --- a/cadasta/organization/tests/test_models.py +++ b/cadasta/organization/tests/test_models.py @@ -21,93 +21,56 @@ def test_has_random_id(self): class OrganizationRoleTest(TestCase): - def test_assign_new_admin(self): - Policy.objects.create( + def setUp(self): + self.oa_policy = Policy.objects.create( name='org-admin', body=open(PERMISSIONS_DIR + 'org-admin.json').read() ) + self.user = UserFactory.create() + self.org = OrganizationFactory.create(add_users=[self.user]) + self.no_user_org = OrganizationFactory.create() + + def _get_role(self): + return OrganizationRole.objects.get(organization=self.org, + user=self.user) + + def _admin_role(self, before, assign, after): + assert self.user.has_perm('org.update', self.org) is before + role = self._get_role() + role.admin = assign + role.save() + assert self.user.has_perm('org.update', self.org) is after - org = OrganizationFactory.create() - user = UserFactory.create() - + def test_assign_new_admin(self): OrganizationRole.objects.create( - organization=org, user=user, admin=True) - assert user.has_perm('org.update', org) is True + organization=self.no_user_org, user=self.user, admin=True) + assert self.user.has_perm('org.update', self.no_user_org) is True def test_keep_admin_role(self): - policy = Policy.objects.create( - name='org-admin', - body=open(PERMISSIONS_DIR + 'org-admin.json').read() - ) - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - user.assign_policies((policy, {'organization': org.slug})) - - assert user.has_perm('org.update', org) is True - role = OrganizationRole.objects.get(organization=org, user=user) - role.admin = True - role.save() - assert user.has_perm('org.update', org) is True + self.user.assign_policies((self.oa_policy, + {'organization': self.org.slug})) + self._admin_role(True, True, True) def test_keep_non_admin_role(self): - Policy.objects.create( - name='org-admin', - body=open(PERMISSIONS_DIR + 'org-admin.json').read() - ) - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - - assert user.has_perm('org.update', org) is False - role = OrganizationRole.objects.get(organization=org, user=user) - role.admin = False - role.save() - assert user.has_perm('org.update', org) is False + self._admin_role(False, False, False) def test_add_admin_role(self): - Policy.objects.create( - name='org-admin', - body=open(PERMISSIONS_DIR + 'org-admin.json').read() - ) - - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - - assert user.has_perm('org.update', org) is False - role = OrganizationRole.objects.get(organization=org, user=user) - role.admin = True - role.save() - assert user.has_perm('org.update', org) is True + self._admin_role(False, True, True) def test_remove_admin_role(self): - policy = Policy.objects.create( - name='org-admin', - body=open(PERMISSIONS_DIR + 'org-admin.json').read() - ) - - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - - user.assign_policies((policy, {'organization': org.slug})) - - assert user.has_perm('org.update', org) is True - role = OrganizationRole.objects.get(organization=org, user=user) - role.admin = False - role.save() - assert user.has_perm('org.update', org) is False + self.user.assign_policies((self.oa_policy, + {'organization': self.org.slug})) + self._admin_role(True, False, False) def test_delete_project_roles(self): - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - ProjectFactory.create_batch(2, add_users=[user], - **{'organization': org}) - ProjectFactory.create_batch(2, add_users=[user]) + ProjectFactory.create_batch(2, add_users=[self.user], + **{'organization': self.org}) + ProjectFactory.create_batch(2, add_users=[self.user]) + assert ProjectRole.objects.filter(user=self.user).count() == 4 - assert ProjectRole.objects.filter(user=user).count() == 4 - - role = OrganizationRole.objects.get(organization=org, user=user) + role = self._get_role() role.delete() - - assert ProjectRole.objects.filter(user=user).count() == 2 + assert ProjectRole.objects.filter(user=self.user).count() == 2 class ProjectTest(TestCase): @@ -121,108 +84,63 @@ def test_has_random_id(self): class ProjectRoleTest(TestCase): - def test_assign_new_manager(self): - project = ProjectFactory.create() - user = UserFactory.create() + def setUp(self): + self.project = ProjectFactory.create() + self.user = UserFactory.create() - ProjectRole.objects.create( - project=project, user=user, role='PM') - assert user.has_perm('project.edit', project) is True + def _has(self, action, state): + assert self.user.has_perm(action, self.project) is state - def test_add_manager_role(self): - project = ProjectFactory.create() - user = UserFactory.create() + def _add_role(self, role): + return ProjectRole.objects.create( + project=self.project, user=self.user, role=role) - assert user.has_perm('project.edit', project) is False - ProjectRole.objects.create( - project=project, user=user, role='PM') - assert user.has_perm('project.edit', project) is True - - def test_keep_manager_role(self): - project = ProjectFactory.create() - user = UserFactory.create() - - role = ProjectRole.objects.create( - project=project, user=user, role='PM') - - assert user.has_perm('project.edit', project) is True - role.manager = True + def _change_role(self, action, before_role, before, after_role, after): + role = self._add_role(before_role) + self._has(action, before) + role.role = after_role role.save() - assert user.has_perm('project.edit', project) is True + self._has(action, after) - def test_keep_non_manager_role(self): - project = ProjectFactory.create() - user = UserFactory.create() + def _change_manager(self, action, role, before, manager, after): + role = self._add_role(role) + self._has(action, before) + role.manager = manager + role.save() + self._has(action, after) - role = ProjectRole.objects.create( - project=project, user=user, role='PU') + def test_assign_new_manager(self): + self._add_role('PM') + self._has('project.edit', True) - assert user.has_perm('project.edit', project) is False - role.manager = False - role.save() - assert user.has_perm('project.edit', project) is False + def test_add_manager_role(self): + self._has('project.edit', False) + self._add_role('PM') + self._has('project.edit', True) - def test_remove_manager_role(self): - project = ProjectFactory.create() - user = UserFactory.create() + def test_keep_manager_role(self): + self._change_manager('project.edit', 'PM', True, True, True) - role = ProjectRole.objects.create( - project=project, user=user, role='PM') + def test_keep_non_manager_role(self): + self._change_manager('project.edit', 'PU', False, False, False) - assert user.has_perm('project.edit', project) is True - role.role = 'PU' - role.save() - assert user.has_perm('project.edit', project) is False + def test_remove_manager_role(self): + self._change_role('project.edit', 'PM', True, 'PU', False) def test_assign_new_collector(self): - project = ProjectFactory.create() - user = UserFactory.create() - - ProjectRole.objects.create( - project=project, user=user, role='DC') - assert user.has_perm('project.resources.add', project) is True + self._add_role('DC') + self._has('project.resources.add', True) def test_add_collector_role(self): - project = ProjectFactory.create() - user = UserFactory.create() - - assert user.has_perm('project.resources.add', project) is False - ProjectRole.objects.create( - project=project, user=user, role='DC') - assert user.has_perm('project.resources.add', project) is True + self._has('project.resources.add', False) + self._add_role('DC') + self._has('project.resources.add', True) def test_keep_collector_role(self): - project = ProjectFactory.create() - user = UserFactory.create() - - role = ProjectRole.objects.create( - project=project, user=user, role='DC') - - assert user.has_perm('project.resources.add', project) is True - role.role = 'DC' - role.save() - assert user.has_perm('project.resources.add', project) is True + self._change_role('project.resources.add', 'DC', True, 'DC', True) def test_keep_non_collector_role(self): - project = ProjectFactory.create() - user = UserFactory.create() - - role = ProjectRole.objects.create( - project=project, user=user, role='PU') - - assert user.has_perm('project.resources.add', project) is False - role.role = 'PU' - role.save() - assert user.has_perm('project.resources.add', project) is False + self._change_role('project.resources.add', 'PU', False, 'PU', False) def test_remove_collector_role(self): - project = ProjectFactory.create() - user = UserFactory.create() - - role = ProjectRole.objects.create( - project=project, user=user, role='DC') - - assert user.has_perm('project.resources.add', project) is True - role.role = 'PU' - role.save() - assert user.has_perm('project.resources.add', project) is False + self._change_role('project.resources.add', 'DC', True, 'PU', False) diff --git a/cadasta/organization/tests/test_serializers.py b/cadasta/organization/tests/test_serializers.py index 79cc0f150..76e339a80 100644 --- a/cadasta/organization/tests/test_serializers.py +++ b/cadasta/organization/tests/test_serializers.py @@ -20,15 +20,11 @@ def test_slug_field_is_set(self): user = UserFactory.create() setattr(request, 'user', user) - org_data = { - 'name': 'Test Organization', - } + org_data = {'name': 'Test Organization'} serializer = serializers.OrganizationSerializer( data=org_data, - context={ - 'request': request - } + context={'request': request} ) serializer.is_valid(raise_exception=True) serializer.save() @@ -39,14 +35,8 @@ def test_slug_field_is_set(self): organization=org_instance).count() == 1 def test_slug_field_is_unique(self): - OrganizationFactory.create(**{ - 'slug': 'org-slug' - }) - - org_data = { - 'name': 'Org Slug', - 'slug': 'org-slug' - } + OrganizationFactory.create(slug='org-slug') + org_data = {'name': 'Org Slug', 'slug': 'org-slug'} serializer = serializers.OrganizationSerializer(data=org_data) assert not serializer.is_valid() @@ -72,12 +62,9 @@ def test_organization_is_set(self): 'name': 'Project', 'organization': organization } - context = { - 'organization': organization - } serializer = serializers.ProjectSerializer( data=project_data, - context=context + context={'organization': organization} ) serializer.is_valid(raise_exception=True) serializer.save() @@ -94,21 +81,19 @@ def test_method_fields_work(self): assert test_data['properties']['org'] == project.organization.name assert test_data['properties']['url'] == reverse( 'organization:project-dashboard', - kwargs={ 'organization': project.organization.slug, - 'project': project.project_slug }) + kwargs={'organization': project.organization.slug, + 'project': project.project_slug}) class OrganizationUserSerializerTest(TestCase): def test_to_represenation(self): user = UserFactory.create() org = OrganizationFactory.create(add_users=[user]) - serializer = serializers.OrganizationUserSerializer( user, - context={ - 'organization': org - } + context={'organization': org} ) + assert serializer.data['username'] == user.username assert serializer.data['email'] == user.email assert serializer.data['role'] == 'User' @@ -118,17 +103,10 @@ def test_list_to_representation(self): org_admin = UserFactory.create() org = OrganizationFactory.create(add_users=users) OrganizationRole.objects.create( - user=org_admin, - organization=org, - admin=True + user=org_admin, organization=org, admin=True ) - serializer = serializers.OrganizationUserSerializer( - org.users.all(), - many=True, - context={ - 'organization': org - } + org.users.all(), many=True, context={'organization': org} ) assert len(serializer.data) == 3 @@ -142,12 +120,7 @@ def test_list_to_representation(self): def test_set_roles_with_username(self): user = UserFactory.create() org = OrganizationFactory.create() - - data = { - 'username': user.username, - 'role': 'Admin' - } - + data = {'username': user.username, 'role': 'Admin'} serializer = serializers.OrganizationUserSerializer( data=data, context={ @@ -166,12 +139,7 @@ def test_set_roles_with_username(self): def test_set_roles_with_email(self): user = UserFactory.create() org = OrganizationFactory.create() - - data = { - 'username': user.email, - 'role': 'Admin' - } - + data = {'username': user.email, 'role': 'Admin'} serializer = serializers.OrganizationUserSerializer( data=data, context={ @@ -189,17 +157,9 @@ def test_set_roles_with_email(self): def test_set_roles_for_user_that_does_not_exist(self): org = OrganizationFactory.create() - - data = { - 'username': 'some-user', - 'role': 'Admin' - } - + data = {'username': 'some-user', 'role': 'Admin'} serializer = serializers.OrganizationUserSerializer( - data=data, - context={ - 'organization': org - } + data=data, context={'organization': org} ) with pytest.raises(ValidationError): @@ -208,21 +168,30 @@ def test_set_roles_for_user_that_does_not_exist(self): 'does not exist').format(username='some-user') in serializer.errors['username']) + def test_set_roles_for_duplicate_username(self): + org = OrganizationFactory.create() + user1 = UserFactory.create(email='some-user@some.com') + UserFactory.create(email='some-user@some.com') + data = {'username': user1.email, 'role': 'Admin'} + serializer = serializers.OrganizationUserSerializer( + data=data, context={'organization': org} + ) + + with pytest.raises(ValidationError): + serializer.is_valid(raise_exception=True) + print(serializer.errors) + assert (_('More than one user found for username or email ' + '{email}').format(email='some-user@some.com') + in serializer.errors['username']) + def test_update_roles_for_user(self): user = UserFactory.create() org = OrganizationFactory.create(add_users=[user]) - - data = { - 'role': 'Admin' - } - serializer = serializers.OrganizationUserSerializer( user, - data=data, + data={'role': 'Admin'}, partial=True, - context={ - 'organization': org - } + context={'organization': org} ) serializer.is_valid(raise_exception=True) serializer.save() @@ -235,12 +204,8 @@ class ProjectUserSerializerTest(TestCase): def test_to_represenation(self): user = UserFactory.create() project = ProjectFactory.create(add_users=[user]) - serializer = serializers.ProjectUserSerializer( - user, - context={ - 'project': project - } + user, context={'project': project} ) assert serializer.data['username'] == user.username @@ -252,17 +217,12 @@ def test_list_to_representation(self): prj_admin = UserFactory.create() project = ProjectFactory.create(add_users=users) ProjectRole.objects.create( - user=prj_admin, - project=project, - role='PM' + user=prj_admin, project=project, role='PM' ) - serializer = serializers.ProjectUserSerializer( project.users.all(), many=True, - context={ - 'project': project - } + context={'project': project} ) assert len(serializer.data) == 3 @@ -277,12 +237,7 @@ def test_set_roles_for_existing_user(self): user = UserFactory.create() org = OrganizationFactory.create(add_users=[user]) project = ProjectFactory.create(**{'organization': org}) - - data = { - 'username': user.username, - 'role': 'DC' - } - + data = {'username': user.username, 'role': 'DC'} serializer = serializers.ProjectUserSerializer( data=data, context={'project': project} @@ -296,17 +251,10 @@ def test_set_roles_for_existing_user(self): def test_set_roles_for_user_who_is_not_an_org_member(self): user = UserFactory.create() project = ProjectFactory.create() - - data = { - 'username': user.username, - 'role': 'DC' - } - + data = {'username': user.username, 'role': 'DC'} serializer = serializers.ProjectUserSerializer( data=data, - context={ - 'project': project - } + context={'project': project} ) with pytest.raises(ValidationError): @@ -317,17 +265,10 @@ def test_set_roles_for_user_who_is_not_an_org_member(self): def test_set_roles_for_user_that_does_not_exist(self): project = ProjectFactory.create() - - data = { - 'username': 'some-user', - 'role': 'DC' - } - + data = {'username': 'some-user', 'role': 'DC'} serializer = serializers.ProjectUserSerializer( data=data, - context={ - 'project': project - } + context={'project': project} ) with pytest.raises(ValidationError): @@ -339,17 +280,12 @@ def test_set_roles_for_user_that_does_not_exist(self): def test_update_roles_for_user(self): user = UserFactory.create() project = ProjectFactory.create(add_users=[user]) - - data = { - 'role': 'PM' - } + data = {'role': 'PM'} serializer = serializers.ProjectUserSerializer( user, partial=True, data=data, - context={ - 'project': project - } + context={'project': project} ) serializer.is_valid(raise_exception=True) serializer.save() diff --git a/cadasta/organization/tests/test_urls_api.py b/cadasta/organization/tests/test_urls_api.py index deb443f85..ec6827e0c 100644 --- a/cadasta/organization/tests/test_urls_api.py +++ b/cadasta/organization/tests/test_urls_api.py @@ -7,62 +7,41 @@ class OrganizationUrlTest(TestCase): def test_organization_list(self): - self.assertEqual( - reverse(version_ns('organization:list')), - version_url('/organizations/') - ) + assert (reverse(version_ns('organization:list')) == + version_url('/organizations/')) resolved = resolve(version_url('/organizations/')) - self.assertEqual( - resolved.func.__name__, - api.OrganizationList.__name__) + assert resolved.func.__name__ == api.OrganizationList.__name__ def test_organization_detail(self): - self.assertEqual( - reverse( - version_ns('organization:detail'), - kwargs={'slug': 'org-slug'}), - version_url('/organizations/org-slug/') - ) + assert (reverse(version_ns('organization:detail'), + kwargs={'slug': 'org-slug'}) == + version_url('/organizations/org-slug/')) resolved = resolve(version_url('/organizations/org-slug/')) - self.assertEqual( - resolved.func.__name__, - api.OrganizationDetail.__name__) - self.assertEqual(resolved.kwargs['slug'], 'org-slug') + assert resolved.func.__name__ == api.OrganizationDetail.__name__ + assert resolved.kwargs['slug'] == 'org-slug' def test_organization_users(self): - self.assertEqual( - reverse( - version_ns('organization:users'), - kwargs={'slug': 'org-slug'}), - version_url('/organizations/org-slug/users/') - ) + assert (reverse(version_ns('organization:users'), + kwargs={'slug': 'org-slug'}) == + version_url('/organizations/org-slug/users/')) resolved = resolve(version_url('/organizations/org-slug/users/')) - self.assertEqual( - resolved.func.__name__, - api.OrganizationUsers.__name__) - self.assertEqual(resolved.kwargs['slug'], 'org-slug') + assert resolved.func.__name__ == api.OrganizationUsers.__name__ + assert resolved.kwargs['slug'] == 'org-slug' def test_organization_users_detail(self): - self.assertEqual( - reverse(version_ns('organization:users_detail'), - kwargs={ - 'slug': 'org-slug', - 'username': 'n_smith' - }), - version_url('/organizations/org-slug/users/n_smith/') - ) + assert (reverse(version_ns('organization:users_detail'), + kwargs={'slug': 'org-slug', 'username': 'n_smith'}) == + version_url('/organizations/org-slug/users/n_smith/')) resolved = resolve( version_url('/organizations/org-slug/users/n_smith/')) - self.assertEqual( - resolved.func.__name__, - api.OrganizationUsersDetail.__name__) - self.assertEqual(resolved.kwargs['slug'], 'org-slug') - self.assertEqual(resolved.kwargs['username'], 'n_smith') + assert resolved.func.__name__ == api.OrganizationUsersDetail.__name__ + assert resolved.kwargs['slug'] == 'org-slug' + assert resolved.kwargs['username'] == 'n_smith' class ProjectUrlTest(TestCase): diff --git a/cadasta/organization/tests/test_views_api.py b/cadasta/organization/tests/test_views_api.py deleted file mode 100644 index ef4194abb..000000000 --- a/cadasta/organization/tests/test_views_api.py +++ /dev/null @@ -1,1636 +0,0 @@ -import json -from datetime import datetime, timedelta, timezone - -from django.test import TestCase -from django.utils.translation import gettext as _ -from django.http import QueryDict -from django.contrib.auth.models import AnonymousUser -from rest_framework.test import APIRequestFactory, force_authenticate -from rest_framework.exceptions import PermissionDenied -from tutelary.models import Policy, assign_user_policies - -from accounts.tests.factories import UserFactory -from .factories import OrganizationFactory, ProjectFactory, clause -from ..models import Organization, OrganizationRole, Project, ProjectRole -from ..views import api - - -class OrganizationListAPITest(TestCase): - def setUp(self): - clauses = { - 'clause': [ - clause('allow', ['org.list']), - clause('allow', ['org.view'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_full_list(self): - """ - It should return all organizations. - """ - OrganizationFactory.create_batch(2) - request = APIRequestFactory().get('/v1/organizations/') - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 2 - assert 'users' not in content[0] - - def test_list_only_one_organization_is_authorized(self): - """ - It should return all organizations. - """ - OrganizationFactory.create() - OrganizationFactory.create(**{'slug': 'unauthorized'}) - - clauses = { - 'clause': [ - clause('allow', ['org.list']), - clause('allow', ['org.view'], ['organization/*']), - clause('deny', ['org.view'], ['organization/unauthorized']) - ] - } - - policy = Policy.objects.create( - name='deny', - body=json.dumps(clauses)) - assign_user_policies(self.user, policy) - - request = APIRequestFactory().get('/v1/organizations/') - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 1 - assert content[0]['slug'] != 'unauthorized' - - def test_full_list_with_unautorized_user(self): - """ - It should 403 "You do not have permission to perform this action." - """ - OrganizationFactory.create_batch(2) - request = APIRequestFactory().get('/v1/organizations/') - force_authenticate(request, user=AnonymousUser()) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 0 - - def test_filter_active(self): - """ - It should return only one active organization. - """ - OrganizationFactory.create(**{'archived': True}) - OrganizationFactory.create(**{'archived': False}) - - request = APIRequestFactory().get('/v1/organizations/?archived=True') - setattr(request, 'GET', QueryDict('archived=True')) - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 1 - - def test_search_filter(self): - """ - It should return only two matching organizations. - """ - OrganizationFactory.create(**{'name': 'A Match'}) - OrganizationFactory.create(**{'description': 'something that matches'}) - OrganizationFactory.create(**{'name': 'Excluded'}) - - request = APIRequestFactory().get('/v1/organizations/?search=match') - setattr(request, 'GET', QueryDict('search=match')) - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 2 - - for org in content: - assert org['name'] != 'Excluded' - - def test_ordering(self): - OrganizationFactory.create(**{'name': 'A'}) - OrganizationFactory.create(**{'name': 'C'}) - OrganizationFactory.create(**{'name': 'B'}) - - request = APIRequestFactory().get('/v1/organizations/?ordering=name') - setattr(request, 'GET', QueryDict('ordering=name')) - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 3 - - prev_name = '' - for org in content: - if prev_name: - assert org['name'] > prev_name - - prev_name = org['name'] - - def test_reverse_ordering(self): - OrganizationFactory.create(**{'name': 'A'}) - OrganizationFactory.create(**{'name': 'C'}) - OrganizationFactory.create(**{'name': 'B'}) - - request = APIRequestFactory().get('/v1/organizations/?ordering=-name') - setattr(request, 'GET', QueryDict('ordering=-name')) - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 3 - - prev_name = '' - for org in content: - if prev_name: - assert org['name'] < prev_name - - prev_name = org['name'] - - -class OrganizationCreateAPITest(TestCase): - def setUp(self): - clauses = { - 'clause': [ - clause('allow', ['org.*']), - clause('allow', ['org.*'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_create_valid_organization(self): - data = { - 'name': 'Org Name', - 'description': 'Org description' - } - request = APIRequestFactory().post('/v1/organizations/', data) - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - - assert response.status_code == 201 - assert Organization.objects.count() == 1 - - def test_create_invalid_organization(self): - data = { - 'description': 'Org description' - } - request = APIRequestFactory().post('/v1/organizations/', data) - force_authenticate(request, user=self.user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 400 - assert content['name'][0] == 'This field is required.' - assert Organization.objects.count() == 0 - - def test_create_organization_with_unauthorized_user(self): - clauses = { - 'clause': [ - clause('allow', ['org.list']), - clause('allow', ['org.view'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - unauthorized_user = UserFactory.create() - assign_user_policies(unauthorized_user, policy) - - data = { - 'name': 'new_org', - 'description': 'Org description' - } - request = APIRequestFactory().post('/v1/organizations/', data) - force_authenticate(request, user=unauthorized_user) - - response = api.OrganizationList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - assert Organization.objects.count() == 0 - - -class OrganizationDetailTest(TestCase): - def setUp(self): - self.view = api.OrganizationDetail.as_view() - - clauses = { - 'clause': [ - clause('allow', ['org.*']), - clause('allow', ['org.*'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_get_organization(self): - org = OrganizationFactory.create(**{'slug': 'org'}) - request = APIRequestFactory().get( - '/v1/organizations/{slug}/'.format(slug=org.slug), - ) - force_authenticate(request, user=self.user) - response = self.view(request, slug=org.slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert content['id'] == org.id - assert 'users' in content - - def test_get_organization_with_unauthorized_user(self): - org = OrganizationFactory.create(**{'slug': 'org'}) - request = APIRequestFactory().get( - '/v1/organizations/{slug}/'.format(slug=org.slug), - ) - force_authenticate(request, user=AnonymousUser()) - response = self.view(request, slug=org.slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - - def test_get_organization_that_does_not_exist(self): - request = APIRequestFactory().get('/v1/organizations/some-org/') - force_authenticate(request, user=self.user) - - response = self.view(request, slug='some-org').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == "Organization not found." - - def test_valid_update(self): - org = OrganizationFactory.create(**{'slug': 'org'}) - - data = {'name': 'Org Name'} - request = APIRequestFactory().patch( - '/v1/organizations/{slug}/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=org.slug).render() - org.refresh_from_db() - - assert response.status_code == 200 - assert org.name == data.get('name') - - def test_update_with_unauthorized_user(self): - org = OrganizationFactory.create(**{'name': 'Org name', 'slug': 'org'}) - - data = {'name': 'Org Name'} - request = APIRequestFactory().patch( - '/v1/organizations/{slug}/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=AnonymousUser()) - - response = self.view(request, slug=org.slug).render() - org.refresh_from_db() - - assert response.status_code == 403 - assert org.name == 'Org name' - - def test_invalid_update(self): - org = OrganizationFactory.create(**{'name': 'Org name', 'slug': 'org'}) - - data = {'name': ''} - request = APIRequestFactory().patch( - '/v1/organizations/{slug}/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=org.slug).render() - content = json.loads(response.content.decode('utf-8')) - org.refresh_from_db() - - assert response.status_code == 400 - assert org.name == 'Org name' - assert content['name'][0] == 'This field may not be blank.' - - def test_archive(self): - org = OrganizationFactory.create(**{'name': 'Org name', 'slug': 'org'}) - - data = {'archived': True} - request = APIRequestFactory().patch( - '/v1/organizations/{slug}/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=org.slug).render() - org.refresh_from_db() - - assert response.status_code == 200 - assert org.archived - - def test_archive_with_unauthorized_user(self): - org = OrganizationFactory.create(**{'slug': 'org'}) - - clauses = { - 'clause': [ - clause('allow', ['org.update'], ['organization/*']), - clause('deny', ['org.archive'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - assign_user_policies(self.user, policy) - - data = {'archived': True} - request = APIRequestFactory().patch( - '/v1/organizations/{slug}/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=org.slug).render() - org.refresh_from_db() - - assert response.status_code == 403 - assert not org.archived - - def test_unarchive(self): - org = OrganizationFactory.create(**{'slug': 'org', 'archived': True}) - - data = {'archived': False} - request = APIRequestFactory().patch( - '/v1/organizations/{slug}/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=org.slug).render() - org.refresh_from_db() - - assert response.status_code == 200 - assert not org.archived - - def test_unarchive_unauthorized_user(self): - org = OrganizationFactory.create(**{'slug': 'org', 'archived': True}) - - clauses = { - 'clause': [ - clause('allow', ['org.update'], ['organization/*']), - clause('deny', ['org.unarchive'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - assign_user_policies(self.user, policy) - - data = {'archived': False} - request = APIRequestFactory().patch( - '/v1/organizations/{slug}/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=org.slug).render() - org.refresh_from_db() - - assert response.status_code == 403 - assert org.archived - - -class OrganizationUsersTest(TestCase): - def setUp(self): - self.view = api.OrganizationUsers.as_view() - - clauses = { - 'clause': [ - clause('allow', ['org.*']), - clause('allow', ['org.*', 'org.*.*'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_get_users(self): - org_users = UserFactory.create_batch(2) - other_user = UserFactory.create() - - org = OrganizationFactory.create(add_users=org_users) - request = APIRequestFactory().get( - '/v1/organizations/{slug}/users/'.format(slug=org.slug) - ) - force_authenticate(request, user=self.user) - response = self.view(request, slug=org.slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 2 - assert other_user.username not in [u['username'] for u in content] - - def test_get_users_with_unauthorized_user(self): - org = OrganizationFactory.create() - request = APIRequestFactory().get( - '/v1/organizations/{slug}/users/'.format(slug=org.slug) - ) - force_authenticate(request, user=AnonymousUser()) - response = self.view(request, slug=org.slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - - def test_add_user(self): - org_users = UserFactory.create_batch(2) - new_user = UserFactory.create() - data = {'username': new_user.username} - - org = OrganizationFactory.create(add_users=org_users) - request = APIRequestFactory().post( - '/v1/organizations/{slug}/users/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - response = self.view(request, slug=org.slug).render() - - assert response.status_code == 201 - assert org.users.count() == 3 - - def test_add_user_with_unauthorized_user(self): - org_users = UserFactory.create_batch(2) - new_user = UserFactory.create() - data = {'username': new_user.username} - - org = OrganizationFactory.create(add_users=org_users) - request = APIRequestFactory().post( - '/v1/organizations/{slug}/users/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=AnonymousUser()) - response = self.view(request, slug=org.slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - assert org.users.count() == 2 - - def test_add_user_that_does_not_exist(self): - org_users = UserFactory.create_batch(2) - data = {'username': 'some_username'} - - org = OrganizationFactory.create(add_users=org_users) - request = APIRequestFactory().post( - '/v1/organizations/{slug}/users/'.format(slug=org.slug), - data - ) - force_authenticate(request, user=self.user) - response = self.view(request, slug=org.slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 400 - assert org.users.count() == 2 - assert (_('User with username or email some_username does not exist') - in content['username']) - - def test_add_user_to_organization_that_does_not_exist(self): - new_user = UserFactory.create() - data = {'username': new_user.username} - - request = APIRequestFactory().post( - '/v1/organizations/some-org/users/', - data - ) - force_authenticate(request, user=self.user) - response = self.view(request, slug='some-org').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == _("Organization not found.") - - -class OrganizationUsersDetailTest(TestCase): - def setUp(self): - self.view = api.OrganizationUsersDetail.as_view() - - clauses = { - 'clause': [ - clause('allow', ['org.*']), - clause('allow', ['org.*', 'org.*.*'], ['organization/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_get_user(self): - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - - request = APIRequestFactory().get( - '/v1/organizations/{org}/users/{username}'.format( - org=org.slug, - username=user.username) - ) - force_authenticate(request, user=self.user) - response = self.view( - request, - slug=org.slug, - username=user.username).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert content['username'] == user.username - - def test_update_user(self): - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - - request = APIRequestFactory().patch( - '/v1/organizations/{org}/users/{username}'.format( - org=org.slug, - username=user.username), - data={ - 'roles': { - 'admin': True - } - }, - format='json' - ) - force_authenticate(request, user=self.user) - response = self.view( - request, - slug=org.slug, - username=user.username).render() - assert response.status_code == 200 - assert org.users.count() == 1 - - role = OrganizationRole.objects.get(organization=org, user=user) - assert role.admin is True - - def test_remove_user(self): - user = UserFactory.create() - user_to_remove = UserFactory.create() - org = OrganizationFactory.create(add_users=[user, user_to_remove]) - - request = APIRequestFactory().delete( - '/v1/organizations/{org}/users/{username}'.format( - org=org.slug, - username=user_to_remove.username) - ) - force_authenticate(request, user=self.user) - response = self.view( - request, - slug=org.slug, - username=user_to_remove.username).render() - assert response.status_code == 204 - assert org.users.count() == 1 - assert user_to_remove not in org.users.all() - - def test_remove_with_unauthorized_user(self): - user = UserFactory.create() - user_to_remove = UserFactory.create() - org = OrganizationFactory.create(add_users=[user, user_to_remove]) - - request = APIRequestFactory().delete( - '/v1/organizations/{org}/users/{username}'.format( - org=org.slug, - username=user_to_remove.username) - ) - force_authenticate(request, user=AnonymousUser()) - response = self.view( - request, - slug=org.slug, - username=user_to_remove.username).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert org.users.count() == 2 - assert content['detail'] == PermissionDenied.default_detail - - def test_remove_user_that_does_not_exist(self): - user = UserFactory.create() - org = OrganizationFactory.create(add_users=[user]) - - request = APIRequestFactory().delete( - '/v1/organizations/{org}/users/{username}'.format( - org=org.slug, - username='some_username') - ) - force_authenticate(request, user=self.user) - response = self.view( - request, - slug=org.slug, - username='some_username').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert org.users.count() == 1 - assert content['detail'] == "User not found." - - def test_remove_user_from_organization_that_does_not_exist(self): - user = UserFactory.create() - - request = APIRequestFactory().delete( - '/v1/organizations/{org}/users/{username}'.format( - org='some-org', - username=user.username) - ) - force_authenticate(request, user=self.user) - response = self.view( - request, - slug='some-org', - username=user.username).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == "Organization not found." - - -class ProjectUsersAPITest(TestCase): - def setUp(self): - clause = { - 'clause': [ - { - "effect": "allow", - "object": ["*"], - "action": ["org.*"] - }, { - 'effect': 'allow', - 'object': ['organization/*'], - 'action': ['org.*', "org.*.*"] - }, { - 'effect': 'allow', - 'object': ['project/*/*'], - 'action': ['project.*', 'project.*.*'] - } - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clause)) - self.user = UserFactory.create() - self.user.assign_policies(policy) - - self.view = api.ProjectUsers.as_view() - - def _get(self, org, prj, user=AnonymousUser()): - request = APIRequestFactory().get( - '/v1/organizations/{org}/projects/{prj}/users/'.format( - org=org, - prj=prj - ) - ) - force_authenticate(request, user=user) - return self.view(request, slug=org, project_id=prj).render() - - def _post(self, org, prj, data={}, user=AnonymousUser()): - request = APIRequestFactory().post( - '/v1/organizations/{org}/projects/{prj}/users/'.format( - org=org, - prj=prj - ), - data, - format='json' - ) - force_authenticate(request, user=user) - - return self.view(request, slug=org, project_id=prj).render() - - def test_full_list(self): - """ - It should return all organizations. - """ - prj_users = UserFactory.create_batch(2) - other_user = UserFactory.create() - - project = ProjectFactory.create(add_users=prj_users) - response = self._get( - org=project.organization.slug, - prj=project.id, - user=self.user - ) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 2 - assert other_user.username not in [u['username'] for u in content] - - def test_full_list_with_unauthorized_user(self): - project = ProjectFactory.create() - response = self._get( - org=project.organization.slug, - prj=project.id - ) - - assert response.status_code == 403 - - def test_get_full_list_organization_does_not_exist(self): - project = ProjectFactory.create() - response = self._get( - org='some-org', - prj=project.id, - user=self.user - ) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == "Project not found." - - def test_get_full_list_project_does_not_exist(self): - organization = OrganizationFactory.create() - response = self._get( - org=organization.slug, - prj='123abd', - user=self.user - ) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == _("Project not found.") - - def test_add_user(self): - user_to_add = UserFactory.create() - org = OrganizationFactory.create(add_users=[user_to_add]) - project = ProjectFactory.create(**{'organization': org}) - response = self._post( - org=project.organization.slug, - prj=project.id, - user=self.user, - data={'username': user_to_add.username} - ) - - assert response.status_code == 201 - assert project.users.count() == 1 - - def test_add_user_with_unauthorized_user(self): - user_to_add = UserFactory.create() - project = ProjectFactory.create() - response = self._post( - org=project.organization.slug, - prj=project.id, - data={'username': user_to_add.username} - ) - - assert response.status_code == 403 - assert project.users.count() == 0 - - def test_add_user_with_invalid_data(self): - project = ProjectFactory.create() - response = self._post( - org=project.organization.slug, - prj=project.id, - user=self.user, - data={'username': 'some-user'} - ) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 400 - assert project.users.count() == 0 - assert (_('User with username or email some-user does not exist') - in content['username']) - - -class ProjectUsersDetailTest(TestCase): - def setUp(self): - clause = { - 'clause': [ - { - "effect": "allow", - "object": ["*"], - "action": ["org.*"] - }, { - 'effect': 'allow', - 'object': ['organization/*'], - 'action': ['org.*', "org.*.*"] - }, { - 'effect': 'allow', - 'object': ['project/*/*'], - 'action': ['project.*', 'project.*.*'] - } - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clause)) - self.user = UserFactory.create() - self.user.assign_policies(policy) - - self.view = api.ProjectUsersDetail.as_view() - - def _get(self, org, prj, user, auth=AnonymousUser()): - request = APIRequestFactory().get( - '/v1/organizations/{org}/projects/{prj}/users/{user}'.format( - org=org, - prj=prj, - user=user - ) - ) - force_authenticate(request, user=auth) - return self.view( - request, slug=org, project_id=prj, username=user).render() - - def _patch(self, org, prj, user, data, auth=AnonymousUser()): - request = APIRequestFactory().patch( - '/v1/organizations/{org}/projects/{prj}/users/{user}'.format( - org=org, - prj=prj, - user=user - ), - data, - format='json' - ) - force_authenticate(request, user=auth) - return self.view( - request, slug=org, project_id=prj, username=user).render() - - def _delete(self, org, prj, user, auth=AnonymousUser()): - request = APIRequestFactory().delete( - '/v1/organizations/{org}/projects/{prj}/users/{user}'.format( - org=org, - prj=prj, - user=user - ) - ) - force_authenticate(request, user=auth) - return self.view( - request, slug=org, project_id=prj, username=user).render() - - def test_get_user(self): - user = UserFactory.create() - project = ProjectFactory.create(add_users=[user]) - - response = self._get( - org=project.organization.slug, - prj=project.id, - user=user.username, - auth=self.user) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert content['username'] == user.username - - def test_get_user_with_unauthorized_user(self): - user = UserFactory.create() - project = ProjectFactory.create(add_users=[user]) - - response = self._get( - org=project.organization.slug, - prj=project.id, - user=user.username) - - assert response.status_code == 403 - - def test_get_user_that_does_not_exist(self): - user = UserFactory.create() - project = ProjectFactory.create() - - response = self._get( - org=project.organization.slug, - prj=project.id, - user=user.username, - auth=self.user) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == _("User not found.") - - def test_get_user_from_org_that_does_not_exist(self): - user = UserFactory.create() - project = ProjectFactory.create() - - response = self._get( - org='some-org', - prj=project.id, - user=user.username, - auth=self.user) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == _("Project not found.") - - def test_get_user_from_project_that_does_not_exist(self): - user = UserFactory.create() - project = ProjectFactory.create() - - response = self._get( - org=project.organization.slug, - prj='abc123', - user=user.username, - auth=self.user) - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == _("Project not found.") - - def test_update_user(self): - user = UserFactory.create() - project = ProjectFactory.create(add_users=[user]) - - data = {'role': 'PM'} - - response = self._patch( - org=project.organization.slug, - prj=project.id, - user=user.username, - data=data, - auth=self.user) - - assert response.status_code == 200 - role = ProjectRole.objects.get(project=project, user=user) - assert role.role == 'PM' - - def test_update_user_with_unauthorized_user(self): - user = UserFactory.create() - project = ProjectFactory.create(add_users=[user]) - - data = {'role': 'PM'} - - response = self._patch( - org=project.organization.slug, - prj=project.id, - user=user.username, - data=data) - - assert response.status_code == 403 - role = ProjectRole.objects.get(project=project, user=user) - assert role.role == 'PU' - - def test_delete_user(self): - user = UserFactory.create() - project = ProjectFactory.create(add_users=[user]) - - response = self._delete( - org=project.organization.slug, - prj=project.id, - user=user.username, - auth=self.user) - - assert response.status_code == 204 - assert project.users.count() == 0 - - def test_delete_user_with_unauthorized_user(self): - user = UserFactory.create() - project = ProjectFactory.create(add_users=[user]) - - response = self._delete( - org=project.organization.slug, - prj=project.id, - user=user.username) - - assert response.status_code == 403 - assert project.users.count() == 1 - - -class UserListAPITest(TestCase): - def setUp(self): - clauses = { - 'clause': [ - clause('allow', ['user.list']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_full_list(self): - """ - It should return all users. - """ - UserFactory.create_batch(2) - request = APIRequestFactory().get('/v1/users/') - force_authenticate(request, user=self.user) - - response = api.UserAdminList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 3 - - def test_full_list_organizations(self): - """ - It should return all users with their organizations. - """ - user1, user2 = UserFactory.create_batch(2) - o0 = OrganizationFactory.create(add_users=[user1, user2]) - o1 = OrganizationFactory.create(add_users=[user1]) - o2 = OrganizationFactory.create(add_users=[user2]) - request = APIRequestFactory().get('/v1/users/') - force_authenticate(request, user=self.user) - - response = api.UserAdminList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 3 - assert 'organizations' in content[0] - assert content[0]['organizations'] == [] - assert 'organizations' in content[1] - assert {'id': o0.id, 'name': o0.name} in content[1]['organizations'] - assert {'id': o1.id, 'name': o1.name} in content[1]['organizations'] - assert 'organizations' in content[2] - assert {'id': o0.id, 'name': o0.name} in content[2]['organizations'] - assert {'id': o2.id, 'name': o2.name} in content[2]['organizations'] - - def test_full_list_with_unautorized_user(self): - """ - It should 403 "You do not have permission to perform this action." - """ - UserFactory.create_batch(2) - request = APIRequestFactory().get('/v1/users/') - force_authenticate(request, user=AnonymousUser()) - - response = api.UserAdminList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - - def test_filter_active(self): - """ - It should return only one active user (plus the "setup" user). - """ - UserFactory.create(**{'is_active': True}) - UserFactory.create(**{'is_active': False}) - - request = APIRequestFactory().get('/v1/users/?is_active=True') - setattr(request, 'GET', QueryDict('is_active=True')) - force_authenticate(request, user=self.user) - - response = api.UserAdminList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 2 - - def test_search_filter(self): - """ - It should return only two matching users. - """ - UserFactory.create(**{'last_name': 'Match'}) - UserFactory.create(**{'username': 'ivegotamatch'}) - UserFactory.create(**{'username': 'excluded'}) - - request = APIRequestFactory().get('/v1/users/?search=match') - setattr(request, 'GET', QueryDict('search=match')) - force_authenticate(request, user=self.user) - - response = api.UserAdminList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 2 - - for user in content: - assert user['username'] != 'excluded' - - def test_ordering(self): - UserFactory.create(**{'username': 'A'}) - UserFactory.create(**{'username': 'C'}) - UserFactory.create(**{'username': 'B'}) - - request = APIRequestFactory().get('/v1/users/?ordering=username') - setattr(request, 'GET', QueryDict('ordering=username')) - force_authenticate(request, user=self.user) - - response = api.UserAdminList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 4 - - prev_username = '' - for user in content: - if prev_username: - assert user['username'] > prev_username - - prev_username = user['username'] - - def test_reverse_ordering(self): - UserFactory.create(**{'username': 'A'}) - UserFactory.create(**{'username': 'C'}) - UserFactory.create(**{'username': 'B'}) - - request = APIRequestFactory().get('/v1/users/?ordering=-username') - setattr(request, 'GET', QueryDict('ordering=-username')) - force_authenticate(request, user=self.user) - - response = api.UserAdminList.as_view()(request).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 4 - - prev_username = '' - for org in content: - if prev_username: - assert org['username'] < prev_username - - prev_username = org['username'] - - -class UserDetailAPITest(TestCase): - def setUp(self): - self.view = api.UserAdminDetail.as_view() - - clauses = { - 'clause': [ - clause('allow', ['user.*']), - clause('allow', ['user.*'], ['user/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_get_user(self): - user = UserFactory.create(**{'username': 'test-user'}) - request = APIRequestFactory().get( - '/v1/users/{username}/'.format(username=user.username), - ) - force_authenticate(request, user=self.user) - response = self.view(request, username=user.username).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert content['username'] == user.username - assert 'organizations' in content - - def test_get_user_with_unauthorized_user(self): - user = UserFactory.create(**{'username': 'test-user'}) - request = APIRequestFactory().get( - '/v1/users/{username}/'.format(username=user.username), - ) - force_authenticate(request, user=AnonymousUser()) - response = self.view(request, username=user.username).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - - def test_get_user_that_does_not_exist(self): - request = APIRequestFactory().get('/v1/users/some-user/') - force_authenticate(request, user=self.user) - - response = self.view(request, username='some-user').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == "User not found." - - def test_valid_update(self): - user = UserFactory.create(**{'username': 'test-user'}) - assert user.is_active - - data = {'is_active': False} - request = APIRequestFactory().patch( - '/v1/users/{username}/'.format(username=user.username), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, username=user.username).render() - user.refresh_from_db() - - assert response.status_code == 200 - assert user.is_active == data.get('is_active') - - def test_update_with_unauthorized_user(self): - user = UserFactory.create(**{'last_name': 'Smith', - 'username': 'test-user'}) - - data = {'last_name': 'Jones'} - request = APIRequestFactory().patch( - '/v1/users/{username}/'.format(username=user.username), - data - ) - force_authenticate(request, user=AnonymousUser()) - - response = self.view(request, username=user.username).render() - user.refresh_from_db() - - assert response.status_code == 403 - assert user.last_name == 'Smith' - - def test_invalid_update(self): - t1 = datetime(12, 10, 30, tzinfo=timezone.utc) - t2 = t1 + timedelta(seconds=10) - user = UserFactory.create(**{'last_login': t1, - 'username': 'test-user'}) - - data = {'last_login': t2} - request = APIRequestFactory().patch( - '/v1/users/{username}/'.format(username=user.username), - data - ) - force_authenticate(request, user=self.user) - - response = self.view(request, username=user.username).render() - content = json.loads(response.content.decode('utf-8')) - user.refresh_from_db() - - assert response.status_code == 400 - assert user.last_login == t1 - assert content['last_login'][0] == _('Cannot update last_login') - - -class ProjectListAPITest(TestCase): - def setUp(self): - clause = { - 'clause': [ - { - 'effect': 'allow', - 'object': ['organization/*'], - 'action': ['project.list'] - } - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clause)) - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_full_list(self): - """ - It should return all projects. - """ - organization = OrganizationFactory.create(**{'slug': 'habitat'}) - ProjectFactory.create_batch(2, **{'organization': organization}) - ProjectFactory.create_batch(2) - request = APIRequestFactory().get( - '/v1/organizations/habitat/projects/' - ) - force_authenticate(request, user=self.user) - - response = api.ProjectList.as_view()(request, - slug='habitat').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 2 - - for project in content: - assert project.get('organization').get('id') == organization.id - - def test_full_list_with_unautorized_user(self): - """ - It should 403 "You do not have permission to perform this action." - """ - OrganizationFactory.create(**{'slug': 'habitat'}) - request = APIRequestFactory().get( - '/v1/organizations/habitat/projects/' - ) - force_authenticate(request, user=AnonymousUser()) - - response = api.ProjectList.as_view()(request, - slug='habitat').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - - def test_filter_active(self): - """ - It should return only one active project. - """ - organization = OrganizationFactory.create(**{'slug': 'habitat'}) - ProjectFactory.create(**{'organization': organization, - 'archived': True}) - ProjectFactory.create(**{'organization': organization, - 'archived': False}) - - request = APIRequestFactory().get( - '/v1/organizations/habitat/projects/?archived=True' - ) - setattr(request, 'GET', QueryDict('archived=True')) - force_authenticate(request, user=self.user) - - response = api.ProjectList.as_view()(request, - slug='habitat').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 1 - - def test_search_filter(self): - """ - It should return only two matching projects. - """ - organization = OrganizationFactory.create(**{'slug': 'namati'}) - ProjectFactory.create(**{'name': 'opdp', 'organization': - organization}) - - request = APIRequestFactory().get( - '/v1/organizations/namati/projects/?search=opdp' - ) - setattr(request, 'GET', QueryDict('search=opdp')) - force_authenticate(request, user=self.user) - - response = api.ProjectList.as_view()(request, - slug='namati').render() - content = json.loads(response.content.decode('utf-8')) - - print(response.content.decode('utf-8')) - assert response.status_code == 200 - assert len(content) == 1 - - for project in content: - assert project['name'] == 'opdp' - - def test_ordering(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - ProjectFactory.create(**{'name': 'A', 'organization': organization}) - ProjectFactory.create(**{'name': 'B', 'organization': organization}) - ProjectFactory.create(**{'name': 'C', 'organization': organization}) - - request = APIRequestFactory().get( - '/v1/organizations/namati/projects/?ordering=name' - ) - setattr(request, 'GET', QueryDict('ordering=name')) - force_authenticate(request, user=self.user) - - response = api.ProjectList.as_view()(request, - slug='namati').render() - content = json.loads(response.content.decode('utf-8')) - - print(response.content.decode('utf-8')) - assert response.status_code == 200 - assert len(content) == 3 - - prev_name = '' - for org in content: - if prev_name: - assert org['name'] > prev_name - - prev_name = org['name'] - - def test_reverse_ordering(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - ProjectFactory.create(**{'name': 'A', 'organization': organization}) - ProjectFactory.create(**{'name': 'C', 'organization': organization}) - ProjectFactory.create(**{'name': 'B', 'organization': organization}) - - request = APIRequestFactory().get( - '/v1/organizations/namati/projects/?ordering=-name' - ) - setattr(request, 'GET', QueryDict('ordering=-name')) - force_authenticate(request, user=self.user) - - response = api.ProjectList.as_view()(request, slug='namati').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert len(content) == 3 - - prev_name = '' - for org in content: - if prev_name: - assert org['name'] < prev_name - - prev_name = org['name'] - - -class ProjectCreateAPITest(TestCase): - def setUp(self): - clauses = { - 'clause': [ - clause('allow', ['org.*']), - clause('allow', ['org.*', 'org.*.*', 'project.*'], - ['organization/*']), - clause('allow', ['project.*'], ['project/*/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_create_valid_project(self): - OrganizationFactory.create(**{'slug': 'habitat'}) - data = { - 'name': 'Project', - 'description': 'Project description', - } - request = APIRequestFactory().post( - '/v1/organizations/habitat/projects/', data) - force_authenticate(request, user=self.user) - - response = api.ProjectList.as_view()(request, - slug='habitat').render() - print(response.content.decode('utf-8')) - assert response.status_code == 201 - assert Project.objects.count() == 1 - - def test_create_invalid_project(self): - OrganizationFactory.create(**{'slug': 'namati'}) - data = { - 'description': 'Project description' - } - request = APIRequestFactory().post( - '/v1/organizations/namati/projects/', data) - force_authenticate(request, user=self.user) - - response = api.ProjectList.as_view()(request, slug='namati').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 400 - assert content['name'][0] == _('This field is required.') - assert Project.objects.count() == 0 - - -class ProjectDetailTest(TestCase): - def setUp(self): - self.view = api.ProjectDetail.as_view() - clauses = { - 'clause': [ - clause('allow', ['org.*']), - clause('allow', ['org.*', 'org.*.*', 'project.*'], - ['organization/*']), - clause('allow', ['project.*'], ['project/*/*']) - ] - } - - policy = Policy.objects.create( - name='default', - body=json.dumps(clauses)) - - self.user = UserFactory.create() - assign_user_policies(self.user, policy) - - def test_get_project(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - project = ProjectFactory.create(**{'project_slug': 'project', - 'organization': organization}) - request = APIRequestFactory().get( - '/v1/organizations/namati/projects/{slug}'.format( - slug=project.project_slug)) - force_authenticate(request, user=self.user) - response = self.view(request, slug=organization.slug, - project_slug=project.project_slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 200 - assert content['id'] == project.id - assert 'users' in content - - def test_get_project_with_unauthorized_user(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - project = ProjectFactory.create(**{'project_slug': 'project', - 'organization': organization}) - - request = APIRequestFactory().get( - '/v1/organizations/namati/projects/{slug}/'.format( - slug=project.project_slug)) - force_authenticate(request, user=AnonymousUser()) - response = self.view(request, slug=organization.slug, - project_slug=project.project_slug).render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 403 - assert content['detail'] == PermissionDenied.default_detail - - def test_get_project_that_does_not_exist(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - ProjectFactory.create(**{'project_slug': 'namati-project', - 'organization': organization}) - request = APIRequestFactory().get( - '/v1/organizations/namati/projects/some-project/') - force_authenticate(request, user=self.user) - response = self.view(request, - slug='namati', - project_slug='some-project').render() - content = json.loads(response.content.decode('utf-8')) - - assert response.status_code == 404 - assert content['detail'] == _("Project not found.") - - def test_valid_update(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - project = ProjectFactory.create(**{'project_slug': 'namati-project', - 'organization': organization}) - data = {'name': 'OPDP'} - request = APIRequestFactory().patch( - '/v1/organizations/namati/projects/{slug}/'.format( - slug=project.project_slug), data) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=organization.slug, - project_slug=project.project_slug).render() - project.refresh_from_db() - - assert response.status_code == 200 - assert project.name == data.get('name') - - def test_update_with_unauthorized_user(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - project = ProjectFactory.create(**{'name': 'OPDP', - 'project_slug': 'namati-project', - 'organization': organization}) - data = {'name': 'OPDP'} - request = APIRequestFactory().patch( - '/v1/organizations/namati/projects/{slug}/'.format( - slug=project.project_slug), data) - force_authenticate(request, user=AnonymousUser()) - - response = self.view(request, slug=organization.slug, - project_slug=project.project_slug).render() - project.refresh_from_db() - - assert response.status_code == 403 - assert project.name == "OPDP" - - def test_invalid_update(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - project = ProjectFactory.create(**{'name': 'OPDP', - 'project_slug': 'namati-project', - 'organization': organization}) - data = {'name': ''} - request = APIRequestFactory().patch( - '/v1/organizations/namati/projects/{slug}/'.format( - slug=project.project_slug), data) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=organization.slug, - project_slug=project.project_slug).render() - content = json.loads(response.content.decode('utf-8')) - project.refresh_from_db() - - assert response.status_code == 400 - assert project.name == 'OPDP' - assert content['name'][0] == _('This field may not be blank.') - - def test_archive(self): - organization = OrganizationFactory.create(**{'slug': 'namati'}) - project = ProjectFactory.create(**{'project_slug': 'namati-project', - 'organization': organization}) - data = {'archived': 'True'} - request = APIRequestFactory().patch( - '/v1/organizations/namati/projects/{slug}/'.format( - slug=project.project_slug), data) - force_authenticate(request, user=self.user) - - response = self.view(request, slug=organization.slug, - project_slug=project.project_slug).render() - project.refresh_from_db() - - assert response.status_code == 200 - assert project.archived diff --git a/cadasta/organization/tests/test_views_api_organizations.py b/cadasta/organization/tests/test_views_api_organizations.py new file mode 100644 index 000000000..bbece13da --- /dev/null +++ b/cadasta/organization/tests/test_views_api_organizations.py @@ -0,0 +1,488 @@ +import json + +from django.test import TestCase +from django.utils.translation import gettext as _ +from django.http import QueryDict +from django.contrib.auth.models import AnonymousUser +from rest_framework.test import APIRequestFactory, force_authenticate +from rest_framework.exceptions import PermissionDenied +from tutelary.models import Policy, assign_user_policies + +from accounts.tests.factories import UserFactory +from .factories import OrganizationFactory, clause +from ..models import Organization, OrganizationRole +from ..views import api + + +class OrganizationListAPITest(TestCase): + def setUp(self): + clauses = { + 'clause': [ + clause('allow', ['org.list']), + clause('allow', ['org.view'], ['organization/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + clauses = { + 'clause': [ + clause('allow', ['org.list']), + clause('allow', ['org.view'], ['organization/*']), + clause('deny', ['org.view'], ['organization/unauthorized']) + ] + } + policy = Policy.objects.create(name='deny', body=json.dumps(clauses)) + self.unauth_user = UserFactory.create() + assign_user_policies(self.unauth_user, policy) + + def _get(self, user=None, query=None, status=None, length=None): + if user is None: + user = self.user + url = '/v1/organizations/' + if query is not None: + url += '?' + query + request = APIRequestFactory().get(url) + if query is not None: + setattr(request, 'GET', QueryDict(query)) + force_authenticate(request, user=user) + response = api.OrganizationList.as_view()(request).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if length is not None: + assert len(content) == length + return content + + def test_full_list(self): + """ + It should return all organizations. + """ + OrganizationFactory.create_batch(2) + content = self._get(status=200, length=2) + assert 'users' not in content[0] + + def test_list_only_one_organization_is_authorized(self): + """ + It should return all authorized organizations. + """ + OrganizationFactory.create_from_kwargs([{}, {'slug': 'unauthorized'}]) + content = self._get(user=self.unauth_user, status=200, length=1) + assert content[0]['slug'] != 'unauthorized' + + def test_full_list_with_unauthorized_user(self): + """ + It should return an empty organization list. + """ + OrganizationFactory.create_batch(2) + self._get(user=AnonymousUser(), status=200, length=0) + + def test_filter_active(self): + """ + It should return only one archived organization. + """ + OrganizationFactory.create_from_kwargs( + [{'archived': True}, {'archived': False}] + ) + content = self._get(query='archived=True', status=200, length=1) + assert content[0]['archived'] is True + + def test_search_filter(self): + """ + It should return only two matching organizations. + """ + OrganizationFactory.create_from_kwargs([ + {'name': 'A Match'}, + {'description': 'something that matches'}, + {'name': 'Excluded'} + ]) + content = self._get(query='search=match', status=200, length=2) + assert not any([org['name'] == 'Excluded' for org in content]) + + def test_ordering(self): + OrganizationFactory.create_from_kwargs([ + {'name': 'A'}, {'name': 'C'}, {'name': 'B'} + ]) + content = self._get(query='ordering=name', status=200, length=3) + names = [org['name'] for org in content] + assert(names == sorted(names)) + + def test_reverse_ordering(self): + OrganizationFactory.create_from_kwargs([ + {'name': 'A'}, {'name': 'C'}, {'name': 'B'} + ]) + content = self._get(query='ordering=-name', status=200, length=3) + names = [org['name'] for org in content] + assert(names == sorted(names, reverse=True)) + + +class OrganizationCreateAPITest(TestCase): + def setUp(self): + clauses = { + 'clause': [ + clause('allow', ['org.*']), + clause('allow', ['org.*'], ['organization/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + clauses = { + 'clause': [ + clause('allow', ['org.list']), + clause('allow', ['org.view'], ['organization/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.unauth_user = UserFactory.create() + assign_user_policies(self.unauth_user, policy) + + def _post(self, data, user=None, status=None, count=None): + if user is None: + user = self.user + url = '/v1/organizations/' + request = APIRequestFactory().post(url, data) + force_authenticate(request, user=user) + response = api.OrganizationList.as_view()(request).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if count is not None: + assert Organization.objects.count() == count + return content + + def test_create_valid_organization(self): + data = {'name': 'Org Name', 'description': 'Org description'} + self._post(data, status=201, count=1) + + def test_create_invalid_organization(self): + data = {'description': 'Org description'} + content = self._post(data, status=400, count=0) + assert content['name'][0] == 'This field is required.' + + def test_create_organization_with_unauthorized_user(self): + data = {'name': 'new_org', 'description': 'Org description'} + content = self._post(data, user=self.unauth_user, status=403, count=0) + assert content['detail'] == PermissionDenied.default_detail + + +class OrganizationDetailAPITest(TestCase): + def setUp(self): + self.view = api.OrganizationDetail.as_view() + + clauses = { + 'clause': [ + clause('allow', ['org.*']), + clause('allow', ['org.*'], ['organization/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + clauses = { + 'clause': [ + clause('allow', ['org.update'], ['organization/*']), + clause('deny', ['org.archive'], ['organization/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.unauth_user = UserFactory.create() + assign_user_policies(self.unauth_user, policy) + + def _get(self, slug, user=None, status=None): + if user is None: + user = self.user + url = '/v1/organizations/{slug}/' + request = APIRequestFactory().get(url.format(slug=slug)) + force_authenticate(request, user=user) + response = self.view(request, slug=slug).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def _patch(self, slug, data, user=None, status=None): + if user is None: + user = self.user + url = '/v1/organizations/{slug}/' + request = APIRequestFactory().patch(url.format(slug=slug), data) + force_authenticate(request, user=user) + response = self.view(request, slug=slug).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def test_get_organization(self): + org = OrganizationFactory.create(**{'slug': 'org'}) + content = self._get(org.slug, status=200) + assert content['id'] == org.id + assert 'users' in content + + def test_get_organization_with_unauthorized_user(self): + org = OrganizationFactory.create(**{'slug': 'org'}) + content = self._get(org.slug, user=AnonymousUser(), status=403) + assert content['detail'] == PermissionDenied.default_detail + + def test_get_organization_that_does_not_exist(self): + content = self._get('some-org', status=404) + assert content['detail'] == "Organization not found." + + def test_valid_update(self): + org = OrganizationFactory.create(**{'slug': 'org'}) + data = {'name': 'Org Name'} + self._patch(org.slug, data, status=200) + org.refresh_from_db() + assert org.name == data.get('name') + + def test_update_with_unauthorized_user(self): + org = OrganizationFactory.create(**{'name': 'Org name', 'slug': 'org'}) + data = {'name': 'Org Name'} + self._patch(org.slug, data, user=AnonymousUser(), status=403) + org.refresh_from_db() + assert org.name == 'Org name' + + def test_invalid_update(self): + org = OrganizationFactory.create(**{'name': 'Org name', 'slug': 'org'}) + data = {'name': ''} + content = self._patch(org.slug, data, status=400) + org.refresh_from_db() + assert org.name == 'Org name' + assert content['name'][0] == 'This field may not be blank.' + + def test_archive(self): + org = OrganizationFactory.create(**{'name': 'Org name', 'slug': 'org'}) + data = {'archived': True} + self._patch(org.slug, data, status=200) + org.refresh_from_db() + assert org.archived + + def test_archive_with_unauthorized_user(self): + org = OrganizationFactory.create(**{'slug': 'org'}) + data = {'archived': True} + self._patch(org.slug, data, user=self.unauth_user, status=403) + org.refresh_from_db() + assert not org.archived + + def test_unarchive(self): + org = OrganizationFactory.create(**{'slug': 'org', 'archived': True}) + data = {'archived': False} + self._patch(org.slug, data, status=200) + org.refresh_from_db() + assert not org.archived + + def test_unarchive_unauthorized_user(self): + org = OrganizationFactory.create(**{'slug': 'org', 'archived': True}) + data = {'archived': False} + self._patch(org.slug, data, user=self.unauth_user, status=403) + org.refresh_from_db() + assert org.archived + + +class OrganizationUsersAPITest(TestCase): + def setUp(self): + self.view = api.OrganizationUsers.as_view() + + clauses = { + 'clause': [ + clause('allow', ['org.*']), + clause('allow', ['org.*', 'org.*.*'], ['organization/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + def _get(self, org, user=None, status=None, length=None): + if user is None: + user = self.user + url = '/v1/organizations/{slug}/users/' + request = APIRequestFactory().get(url.format(slug=org.slug)) + force_authenticate(request, user=user) + response = self.view(request, slug=org.slug).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if length is not None: + assert len(content) == length + return content + + def _post(self, org_or_slug, data, user=None, status=None, count=None): + if isinstance(org_or_slug, Organization): + slug = org_or_slug.slug + else: + slug = org_or_slug + if user is None: + user = self.user + url = '/v1/organizations/{slug}/users/' + request = APIRequestFactory().post(url.format(slug=slug), data) + force_authenticate(request, user=user) + response = self.view(request, slug=slug).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if count is not None and isinstance(org_or_slug, Organization): + assert org_or_slug.users.count() == count + return content + + def create_normal_org(self): + org_users = UserFactory.create_batch(2) + return OrganizationFactory.create(add_users=org_users) + + def test_get_users(self): + org = self.create_normal_org() + other_user = UserFactory.create() + content = self._get(org, status=200, length=2) + assert other_user.username not in [u['username'] for u in content] + + def test_get_users_with_unauthorized_user(self): + org = OrganizationFactory.create() + content = self._get(org, user=AnonymousUser(), status=403) + assert content['detail'] == PermissionDenied.default_detail + + def test_add_user(self): + org = self.create_normal_org() + new_user = UserFactory.create() + self._post(org, {'username': new_user.username}, status=201, count=3) + + def test_add_user_with_unauthorized_user(self): + org = self.create_normal_org() + new_user = UserFactory.create() + content = self._post(org, {'username': new_user.username}, + user=AnonymousUser(), status=403, count=2) + assert content['detail'] == PermissionDenied.default_detail + + def test_add_user_that_does_not_exist(self): + org = self.create_normal_org() + content = self._post(org, {'username': 'some_username'}, + status=400, count=2) + assert (_('User with username or email some_username does not exist') + in content['username']) + + def test_add_user_to_organization_that_does_not_exist(self): + new_user = UserFactory.create() + content = self._post('some-org', {'username': new_user.username}, + status=404) + assert content['detail'] == _("Organization not found.") + + +class OrganizationUsersDetailAPITest(TestCase): + def setUp(self): + self.view = api.OrganizationUsersDetail.as_view() + clauses = { + 'clause': [ + clause('allow', ['org.*']), + clause('allow', ['org.*', 'org.*.*'], ['organization/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + def _get(self, org, username, user=None, status=None): + if user is None: + user = self.user + url = '/v1/organizations/{org}/users/{username}' + request = APIRequestFactory().get(url.format(org=org.slug, + username=username)) + force_authenticate(request, user=user) + response = self.view(request, slug=org.slug, + username=username).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def _patch(self, org, username, data, status=None, count=None): + url = '/v1/organizations/{org}/users/{username}' + request = APIRequestFactory().patch( + url.format(org=org.slug, username=username), + data=data, format='json') + force_authenticate(request, user=self.user) + response = self.view(request, slug=org.slug, + username=username).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if count is not None: + assert org.users.count() == count + return content + + def _delete(self, org_or_slug, username, user=None, + status=None, count=None): + if isinstance(org_or_slug, Organization): + slug = org_or_slug.slug + else: + slug = org_or_slug + if user is None: + user = self.user + url = '/v1/organizations/{org}/users/{username}' + request = APIRequestFactory().delete(url.format(org=slug, + username=username)) + force_authenticate(request, user=user) + response = self.view(request, slug=slug, username=username).render() + content = None + if len(response.content) > 0: + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if count is not None and isinstance(org_or_slug, Organization): + assert org_or_slug.users.count() == count + return content + + def test_get_user(self): + user = UserFactory.create() + org = OrganizationFactory.create(add_users=[user]) + content = self._get(org, user.username, status=200) + assert content['username'] == user.username + + def test_update_user(self): + user = UserFactory.create() + org = OrganizationFactory.create(add_users=[user]) + self._patch(org, user.username, data={'roles': {'admin': True}}, + status=200, count=1) + role = OrganizationRole.objects.get(organization=org, user=user) + assert role.admin is True + + def test_remove_user(self): + user = UserFactory.create() + user_to_remove = UserFactory.create() + org = OrganizationFactory.create(add_users=[user, user_to_remove]) + self._delete(org, user_to_remove.username, status=204, count=1) + assert user_to_remove not in org.users.all() + + def test_remove_with_unauthorized_user(self): + user = UserFactory.create() + user_to_remove = UserFactory.create() + org = OrganizationFactory.create(add_users=[user, user_to_remove]) + content = self._delete(org, user_to_remove.username, + user=AnonymousUser(), status=403, count=2) + assert content['detail'] == PermissionDenied.default_detail + + def test_remove_user_that_does_not_exist(self): + user = UserFactory.create() + org = OrganizationFactory.create(add_users=[user]) + content = self._delete(org, 'some_username', status=404, count=1) + assert content['detail'] == "User not found." + + def test_remove_user_from_organization_that_does_not_exist(self): + user = UserFactory.create() + content = self._delete('some-org', user.username, status=404) + assert content['detail'] == "Organization not found." diff --git a/cadasta/organization/tests/test_views_api_projects.py b/cadasta/organization/tests/test_views_api_projects.py new file mode 100644 index 000000000..3029f1c69 --- /dev/null +++ b/cadasta/organization/tests/test_views_api_projects.py @@ -0,0 +1,523 @@ +import json + +from django.test import TestCase +from django.utils.translation import gettext as _ +from django.http import QueryDict +from django.contrib.auth.models import AnonymousUser +from rest_framework.test import APIRequestFactory, force_authenticate +from rest_framework.exceptions import PermissionDenied +from tutelary.models import Policy, assign_user_policies + +from accounts.tests.factories import UserFactory +from .factories import OrganizationFactory, ProjectFactory, clause +from ..models import Project, ProjectRole +from ..views import api + + +class ProjectUsersAPITest(TestCase): + def setUp(self): + self.view = api.ProjectUsers.as_view() + + clause = { + 'clause': [ + { + "effect": "allow", + "object": ["*"], + "action": ["org.*"] + }, { + 'effect': 'allow', + 'object': ['organization/*'], + 'action': ['org.*', "org.*.*"] + }, { + 'effect': 'allow', + 'object': ['project/*/*'], + 'action': ['project.*', 'project.*.*'] + } + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clause)) + self.user = UserFactory.create() + self.user.assign_policies(policy) + + def _get(self, org, prj, user=None, status=None, length=None): + if user is None: + user = self.user + url = '/v1/organizations/{org}/projects/{prj}/users/' + request = APIRequestFactory().get(url.format(org=org, prj=prj)) + force_authenticate(request, user=user) + response = self.view(request, slug=org, project_id=prj).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if length is not None: + assert len(content) == length + return content + + def _post(self, org, project, data, user=None, status=None, count=None): + if user is None: + user = self.user + prj = project.id + url = '/v1/organizations/{org}/projects/{prj}/users/' + request = APIRequestFactory().post( + url.format(org=org, prj=prj), data, format='json' + ) + force_authenticate(request, user=user) + response = self.view(request, slug=org, project_id=prj).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if count is not None: + assert project.users.count() == count + return content + + def test_full_list(self): + """ + It should return all organizations. + """ + prj_users = UserFactory.create_batch(2) + other_user = UserFactory.create() + project = ProjectFactory.create(add_users=prj_users) + content = self._get(project.organization.slug, project.id, + status=200, length=2) + assert other_user.username not in [u['username'] for u in content] + + def test_full_list_with_unauthorized_user(self): + project = ProjectFactory.create() + self._get(project.organization.slug, project.id, + user=AnonymousUser(), status=403) + + def test_get_full_list_organization_does_not_exist(self): + project = ProjectFactory.create() + content = self._get('some-org', project.id, status=404) + assert content['detail'] == "Project not found." + + def test_get_full_list_project_does_not_exist(self): + organization = OrganizationFactory.create() + content = self._get(organization.slug, '123abd', status=404) + assert content['detail'] == _("Project not found.") + + def test_add_user(self): + user_to_add = UserFactory.create() + org = OrganizationFactory.create(add_users=[user_to_add]) + project = ProjectFactory.create(**{'organization': org}) + self._post(project.organization.slug, project, + {'username': user_to_add.username}, status=201, count=1) + + def test_add_user_with_unauthorized_user(self): + user_to_add = UserFactory.create() + project = ProjectFactory.create() + self._post(project.organization.slug, project, + {'username': user_to_add.username}, + user=AnonymousUser(), status=403, count=0) + + def test_add_user_with_invalid_data(self): + project = ProjectFactory.create() + content = self._post(project.organization.slug, project, + {'username': 'some-user'}, status=400, count=0) + assert (_('User with username or email some-user does not exist') + in content['username']) + + +class ProjectUsersDetailTest(TestCase): + def setUp(self): + self.view = api.ProjectUsersDetail.as_view() + + clause = { + 'clause': [ + { + "effect": "allow", + "object": ["*"], + "action": ["org.*"] + }, { + 'effect': 'allow', + 'object': ['organization/*'], + 'action': ['org.*', "org.*.*"] + }, { + 'effect': 'allow', + 'object': ['project/*/*'], + 'action': ['project.*', 'project.*.*'] + } + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clause)) + self.user = UserFactory.create() + self.user.assign_policies(policy) + + def _get(self, org, prj, user, auth=None, status=None): + if auth is None: + auth = self.user + url = '/v1/organizations/{org}/projects/{prj}/users/{user}' + request = APIRequestFactory().get( + url.format(org=org, prj=prj, user=user) + ) + force_authenticate(request, user=auth) + response = self.view(request, slug=org, project_id=prj, + username=user).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def _patch(self, org, prj, user, data, auth=None, status=None): + if auth is None: + auth = self.user + url = '/v1/organizations/{org}/projects/{prj}/users/{user}' + request = APIRequestFactory().patch( + url.format(org=org, prj=prj, user=user), + data, format='json' + ) + force_authenticate(request, user=auth) + response = self.view(request, slug=org, project_id=prj, + username=user).render() + content = None + if len(response.content) > 0: + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def _delete(self, org, project, user, auth=None, status=None, count=None): + if auth is None: + auth = self.user + prj = project.id + url = '/v1/organizations/{org}/projects/{prj}/users/{user}' + request = APIRequestFactory().delete( + url.format(org=org, prj=prj, user=user) + ) + force_authenticate(request, user=auth) + response = self.view(request, slug=org, project_id=prj, + username=user).render() + if status is not None: + assert response.status_code == status + if count is not None: + assert project.users.count() == count + + def test_get_user(self): + user = UserFactory.create() + project = ProjectFactory.create(add_users=[user]) + content = self._get(project.organization.slug, project.id, + user.username, status=200) + assert content['username'] == user.username + + def test_get_user_with_unauthorized_user(self): + user = UserFactory.create() + project = ProjectFactory.create(add_users=[user]) + self._get(project.organization.slug, project.id, user.username, + auth=AnonymousUser(), status=403) + + def test_get_user_that_does_not_exist(self): + user = UserFactory.create() + project = ProjectFactory.create() + content = self._get(project.organization.slug, project.id, + user.username, status=404) + assert content['detail'] == _("User not found.") + + def test_get_user_from_org_that_does_not_exist(self): + user = UserFactory.create() + project = ProjectFactory.create() + content = self._get('some-org', project.id, user.username, + status=404) + assert content['detail'] == _("Project not found.") + + def test_get_user_from_project_that_does_not_exist(self): + user = UserFactory.create() + project = ProjectFactory.create() + content = self._get(project.organization.slug, 'abc123', + user.username, status=404) + assert content['detail'] == _("Project not found.") + + def test_update_user(self): + user = UserFactory.create() + project = ProjectFactory.create(add_users=[user]) + self._patch(project.organization.slug, project.id, + user.username, {'role': 'PM'}, status=200) + role = ProjectRole.objects.get(project=project, user=user) + assert role.role == 'PM' + + def test_update_user_with_unauthorized_user(self): + user = UserFactory.create() + project = ProjectFactory.create(add_users=[user]) + self._patch(project.organization.slug, project.id, user.username, + {'role': 'PM'}, auth=AnonymousUser(), status=403) + role = ProjectRole.objects.get(project=project, user=user) + assert role.role == 'PU' + + def test_delete_user(self): + user = UserFactory.create() + project = ProjectFactory.create(add_users=[user]) + self._delete(project.organization.slug, project, user.username, + status=204, count=0) + + def test_delete_user_with_unauthorized_user(self): + user = UserFactory.create() + project = ProjectFactory.create(add_users=[user]) + self._delete(project.organization.slug, project, user.username, + auth=AnonymousUser(), status=403, count=1) + + +class ProjectListAPITest(TestCase): + def setUp(self): + clause = { + 'clause': [ + { + 'effect': 'allow', + 'object': ['organization/*'], + 'action': ['project.list'] + } + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clause)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + def _get(self, org, user=None, query=None, status=None, length=None): + if user is None: + user = self.user + url = '/v1/organizations/{org}/projects/' + if query is not None: + url += '?' + query + request = APIRequestFactory().get(url.format(org=org)) + if query is not None: + setattr(request, 'GET', QueryDict(query)) + force_authenticate(request, user=user) + response = api.ProjectList.as_view()(request, slug=org).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if length is not None: + assert len(content) == length + return content + + def test_full_list(self): + """ + It should return all projects. + """ + organization = OrganizationFactory.create(**{'slug': 'habitat'}) + ProjectFactory.create_batch(2, **{'organization': organization}) + ProjectFactory.create_batch(2) + content = self._get('habitat', status=200, length=2) + assert all([proj.get('organization').get('id') == organization.id + for proj in content]) + + def test_full_list_with_unautorized_user(self): + """ + It should 403 "You do not have permission to perform this action." + """ + OrganizationFactory.create(**{'slug': 'habitat'}) + content = self._get('habitat', user=AnonymousUser(), status=403) + assert content['detail'] == PermissionDenied.default_detail + + def test_filter_active(self): + """ + It should return only one active project. + """ + organization = OrganizationFactory.create(**{'slug': 'habitat'}) + ProjectFactory.create(**{'organization': organization, + 'archived': True}) + ProjectFactory.create(**{'organization': organization, + 'archived': False}) + self._get('habitat', query='archived=True', status=200, length=1) + + def test_search_filter(self): + """ + It should return only two matching projects. + """ + organization = OrganizationFactory.create(**{'slug': 'namati'}) + ProjectFactory.create(**{'name': 'opdp', 'organization': + organization}) + content = self._get('namati', query='search=opdp', + status=200, length=1) + assert all([proj['name'] == 'opdp' for proj in content]) + + def test_ordering(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + ProjectFactory.create_from_kwargs([ + {'name': 'A', 'organization': organization}, + {'name': 'B', 'organization': organization}, + {'name': 'C', 'organization': organization} + ]) + content = self._get('namati', query='ordering=name', + status=200, length=3) + names = [proj['name'] for proj in content] + assert names == sorted(names) + + def test_reverse_ordering(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + ProjectFactory.create_from_kwargs([ + {'name': 'A', 'organization': organization}, + {'name': 'C', 'organization': organization}, + {'name': 'B', 'organization': organization} + ]) + content = self._get('namati', query='ordering=-name', + status=200, length=3) + names = [proj['name'] for proj in content] + assert names == sorted(names, reverse=True) + + +class ProjectCreateAPITest(TestCase): + def setUp(self): + clauses = { + 'clause': [ + clause('allow', ['org.*']), + clause('allow', ['org.*', 'org.*.*', 'project.*'], + ['organization/*']), + clause('allow', ['project.*'], ['project/*/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + def _post(self, org, data, user=None, status=None, count=None): + if user is None: + user = self.user + url = '/v1/organizations/{org}/projects/' + request = APIRequestFactory().post(url.format(org=org), data) + force_authenticate(request, user=user) + response = api.ProjectList.as_view()(request, slug=org).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if count is not None: + assert Project.objects.count() == count + return content + + def test_create_valid_project(self): + OrganizationFactory.create(**{'slug': 'habitat'}) + data = { + 'name': 'Project', + 'description': 'Project description', + } + self._post('habitat', data, status=201, count=1) + + def test_create_invalid_project(self): + OrganizationFactory.create(**{'slug': 'namati'}) + data = { + 'description': 'Project description' + } + content = self._post('namati', data, status=400, count=0) + assert content['name'][0] == _('This field is required.') + + +class ProjectDetailTest(TestCase): + def setUp(self): + self.view = api.ProjectDetail.as_view() + clauses = { + 'clause': [ + clause('allow', ['org.*']), + clause('allow', ['org.*', 'org.*.*', 'project.*'], + ['organization/*']), + clause('allow', ['project.*'], ['project/*/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + def _get(self, org, slug, user=None, status=None): + if user is None: + user = self.user + url = '/v1/organizations/{org}/projects/{slug}' + request = APIRequestFactory().get(url.format(org=org, slug=slug)) + force_authenticate(request, user=user) + response = self.view(request, slug=org, project_slug=slug).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def _patch(self, org, slug, data, user=None, status=None): + if user is None: + user = self.user + url = '/v1/organizations/{org}/projects/{slug}/' + request = APIRequestFactory().patch(url.format(org=org, slug=slug), + data) + force_authenticate(request, user=user) + response = self.view(request, slug=org, project_slug=slug).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def test_get_project(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + project = ProjectFactory.create(**{'project_slug': 'project', + 'organization': organization}) + content = self._get('namati', project.project_slug, status=200) + assert content['id'] == project.id + assert 'users' in content + + def test_get_project_with_unauthorized_user(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + project = ProjectFactory.create(**{'project_slug': 'project', + 'organization': organization}) + content = self._get('namati', project.project_slug, + user=AnonymousUser(), status=403) + assert content['detail'] == PermissionDenied.default_detail + + def test_get_project_that_does_not_exist(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + ProjectFactory.create(**{'project_slug': 'namati-project', + 'organization': organization}) + content = self._get('namati', 'some-project', status=404) + assert content['detail'] == _("Project not found.") + + def test_valid_update(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + project = ProjectFactory.create(**{'project_slug': 'namati-project', + 'organization': organization}) + self._patch('namati', project.project_slug, {'name': 'OPDP'}, + status=200) + project.refresh_from_db() + assert project.name == 'OPDP' + + def test_update_with_unauthorized_user(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + project = ProjectFactory.create(**{'name': 'OPDP', + 'project_slug': 'namati-project', + 'organization': organization}) + self._patch('namati', project.project_slug, {'name': 'OPDP'}, + user=AnonymousUser(), status=403) + project.refresh_from_db() + assert project.name == "OPDP" + + def test_invalid_update(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + project = ProjectFactory.create(**{'name': 'OPDP', + 'project_slug': 'namati-project', + 'organization': organization}) + content = self._patch('namati', project.project_slug, {'name': ''}, + status=400) + project.refresh_from_db() + assert project.name == 'OPDP' + assert content['name'][0] == _('This field may not be blank.') + + def test_archive(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + project = ProjectFactory.create(**{'project_slug': 'namati-project', + 'organization': organization}) + self._patch('namati', project.project_slug, {'archived': True}, + status=200) + project.refresh_from_db() + assert project.archived + + def test_unarchive(self): + organization = OrganizationFactory.create(**{'slug': 'namati'}) + project = ProjectFactory.create(**{'project_slug': 'namati-project', + 'organization': organization, + 'archived': True}) + self._patch('namati', project.project_slug, {'archived': False}, + status=200) + project.refresh_from_db() + assert not project.archived diff --git a/cadasta/organization/tests/test_views_api_users.py b/cadasta/organization/tests/test_views_api_users.py new file mode 100644 index 000000000..2c741825b --- /dev/null +++ b/cadasta/organization/tests/test_views_api_users.py @@ -0,0 +1,201 @@ +import json +from datetime import datetime, timedelta, timezone + +from django.test import TestCase +from django.utils.translation import gettext as _ +from django.http import QueryDict +from django.contrib.auth.models import AnonymousUser +from rest_framework.test import APIRequestFactory, force_authenticate +from rest_framework.exceptions import PermissionDenied +from tutelary.models import Policy, assign_user_policies + +from accounts.tests.factories import UserFactory +from .factories import OrganizationFactory, clause +from ..views import api + + +class UserListAPITest(TestCase): + def setUp(self): + clauses = { + 'clause': [ + clause('allow', ['user.list']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + def _get(self, user=None, query=None, status=None, length=None): + if user is None: + user = self.user + url = '/v1/users/' + if query is not None: + url += '?' + query + request = APIRequestFactory().get(url) + if query is not None: + setattr(request, 'GET', QueryDict(query)) + force_authenticate(request, user=user) + response = api.UserAdminList.as_view()(request).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + if length is not None: + assert len(content) == length + return content + + def test_full_list(self): + """ + It should return all users. + """ + UserFactory.create_batch(2) + self._get(status=200, length=3) + + def test_full_list_organizations(self): + """ + It should return all users with their organizations. + """ + user1, user2 = UserFactory.create_batch(2) + o0 = OrganizationFactory.create(add_users=[user1, user2]) + o1 = OrganizationFactory.create(add_users=[user1]) + o2 = OrganizationFactory.create(add_users=[user2]) + content = self._get(status=200, length=3) + assert 'organizations' in content[0] + assert content[0]['organizations'] == [] + assert 'organizations' in content[1] + assert {'id': o0.id, 'name': o0.name} in content[1]['organizations'] + assert {'id': o1.id, 'name': o1.name} in content[1]['organizations'] + assert 'organizations' in content[2] + assert {'id': o0.id, 'name': o0.name} in content[2]['organizations'] + assert {'id': o2.id, 'name': o2.name} in content[2]['organizations'] + + def test_full_list_with_unautorized_user(self): + """ + It should 403 "You do not have permission to perform this action." + """ + UserFactory.create_batch(2) + content = self._get(user=AnonymousUser(), status=403) + assert content['detail'] == PermissionDenied.default_detail + + def test_filter_active(self): + """ + It should return only one active user (plus the "setup" user). + """ + UserFactory.create_from_kwargs([ + {'is_active': True}, {'is_active': False} + ]) + self._get(query='is_active=True', status=200, length=2) + + def test_search_filter(self): + """ + It should return only two matching users. + """ + UserFactory.create_from_kwargs([ + {'last_name': 'Match'}, + {'username': 'ivegotamatch'}, + {'username': 'excluded'} + ]) + content = self._get(query='search=match', status=200, length=2) + assert not any([user['username'] == 'excluded' for user in content]) + + def test_ordering(self): + UserFactory.create_from_kwargs([ + {'username': 'A'}, {'username': 'C'}, {'username': 'B'} + ]) + content = self._get(query='ordering=username', status=200, length=4) + usernames = [user['username'] for user in content] + assert(usernames == sorted(usernames)) + + def test_reverse_ordering(self): + UserFactory.create_from_kwargs([ + {'username': 'A'}, {'username': 'C'}, {'username': 'B'} + ]) + content = self._get(query='ordering=-username', status=200, length=4) + usernames = [user['username'] for user in content] + assert(usernames == sorted(usernames, reverse=True)) + + +class UserDetailAPITest(TestCase): + def setUp(self): + self.view = api.UserAdminDetail.as_view() + + clauses = { + 'clause': [ + clause('allow', ['user.*']), + clause('allow', ['user.*'], ['user/*']) + ] + } + policy = Policy.objects.create( + name='default', + body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, policy) + + def _get(self, username, user=None, status=None): + if user is None: + user = self.user + url = '/v1/users/{username}/' + request = APIRequestFactory().get(url.format(username=username)) + force_authenticate(request, user=user) + response = self.view(request, username=username).render() + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def _patch(self, username, data, user=None, status=None): + if user is None: + user = self.user + url = '/v1/users/{username}/' + request = APIRequestFactory().patch(url.format(username=username), + data) + force_authenticate(request, user=user) + response = self.view(request, username=username).render() + content = None + if len(response.content) > 0: + content = json.loads(response.content.decode('utf-8')) + if status is not None: + assert response.status_code == status + return content + + def test_get_user(self): + user = UserFactory.create(**{'username': 'test-user'}) + content = self._get(user.username, status=200) + assert content['username'] == user.username + assert 'organizations' in content + + def test_get_user_with_unauthorized_user(self): + user = UserFactory.create(**{'username': 'test-user'}) + content = self._get(user.username, user=AnonymousUser(), status=403) + assert content['detail'] == PermissionDenied.default_detail + + def test_get_user_that_does_not_exist(self): + content = self._get('some-user', status=404) + assert content['detail'] == "User not found." + + def test_valid_update(self): + user = UserFactory.create(**{'username': 'test-user'}) + assert user.is_active + data = {'is_active': False} + self._patch(user.username, data, status=200) + user.refresh_from_db() + assert user.is_active == data.get('is_active') + + def test_update_with_unauthorized_user(self): + user = UserFactory.create(**{'last_name': 'Smith', + 'username': 'test-user'}) + self._patch(user.username, {'last_name': 'Jones'}, + user=AnonymousUser(), status=403) + user.refresh_from_db() + assert user.last_name == 'Smith' + + def test_invalid_update(self): + t1 = datetime(12, 10, 30, tzinfo=timezone.utc) + t2 = t1 + timedelta(seconds=10) + user = UserFactory.create(**{'last_login': t1, + 'username': 'test-user'}) + content = self._patch(user.username, {'last_login': t2}, status=400) + user.refresh_from_db() + assert user.last_login == t1 + assert content['last_login'][0] == _('Cannot update last_login') diff --git a/cadasta/organization/tests/test_views_default.py b/cadasta/organization/tests/test_views_default_organizations.py similarity index 87% rename from cadasta/organization/tests/test_views_default.py rename to cadasta/organization/tests/test_views_default_organizations.py index 343b0bd54..82beae9c6 100644 --- a/cadasta/organization/tests/test_views_default.py +++ b/cadasta/organization/tests/test_views_default_organizations.py @@ -13,7 +13,7 @@ from ..views import default from ..models import Organization, OrganizationRole from .. import forms -from .factories import OrganizationFactory, ProjectFactory, clause +from .factories import OrganizationFactory, clause class OrganizationListTest(TestCase): @@ -46,8 +46,7 @@ def test_get_with_user(self): expected = render_to_string( 'organization/organization_list.html', - {'object_list': self.orgs, - 'user': self.request.user}, + {'object_list': self.orgs, 'user': self.request.user}, request=self.request) assert response.status_code == 200 @@ -69,12 +68,32 @@ def setUp(self): self.policy = Policy.objects.create( name='allow', body=json.dumps(clauses)) + self.user = UserFactory.create() + assign_user_policies(self.user, self.policy) + + def _get(self, status=None): + response = self.view(self.request) + if status is not None: + assert response.status_code == status + return response + + def _post(self, status=None, count=None): + setattr(self.request, 'method', 'POST') + setattr(self.request, 'POST', { + 'name': 'Org', + 'description': 'Some description', + 'url': 'http://example.com' + }) + response = self.view(self.request) + if status is not None: + assert response.status_code == status + if count is not None: + assert Organization.objects.count() == count + return response def test_get_with_authorized_user(self): - user = UserFactory.create() - assign_user_policies(user, self.policy) - setattr(self.request, 'user', user) - response = self.view(self.request).render() + setattr(self.request, 'user', self.user) + response = self._get(status=200).render() content = response.content.decode('utf-8') context = RequestContext(self.request) @@ -85,46 +104,21 @@ def test_get_with_authorized_user(self): context ) - assert response.status_code == 200 assert expected == content def test_post_with_authorized_user(self): - user = UserFactory.create() - assign_user_policies(user, self.policy) - - setattr(self.request, 'user', user) - setattr(self.request, 'method', 'POST') - setattr(self.request, 'POST', { - 'name': 'Org', - 'description': 'Some description', - 'url': 'http://example.com' - }) - - response = self.view(self.request) - - assert Organization.objects.count() == 1 + setattr(self.request, 'user', self.user) + response = self._post(status=302, count=1) org = Organization.objects.first() - - assert response.status_code == 302 assert '/organizations/{}/'.format(org.slug) in response['location'] def test_get_with_unauthenticated_user(self): - response = self.view(self.request) - assert response.status_code == 302 + response = self._get(status=302) assert '/account/login/' in response['location'] def test_post_with_unauthenticated_user(self): - setattr(self.request, 'method', 'POST') - setattr(self.request, 'POST', { - 'name': 'Org', - 'description': 'Some description', - 'url': 'http://example.com' - }) - - response = self.view(self.request) - assert response.status_code == 302 + response = self._post(status=302, count=0) assert '/account/login/' in response['location'] - assert Organization.objects.count() == 0 class OrganizationDashboardTest(TestCase): @@ -132,7 +126,6 @@ def setUp(self): self.view = default.OrganizationDashboard.as_view() self.request = HttpRequest() setattr(self.request, 'method', 'GET') - setattr(self.request, 'user', AnonymousUser()) self.org = OrganizationFactory.create() @@ -150,11 +143,18 @@ def setUp(self): self.messages = FallbackStorage(self.request) setattr(self.request, '_messages', self.messages) + self.user = UserFactory.create() + setattr(self.request, 'user', self.user) + + def _get(self, slug, status=None): + response = self.view(self.request, slug=slug) + if status is not None: + assert response.status_code == status + return response + def test_get_with_authorized_user(self): - user = UserFactory.create() - assign_user_policies(user, self.policy) - setattr(self.request, 'user', user) - response = self.view(self.request, slug=self.org.slug).render() + assign_user_policies(self.user, self.policy) + response = self._get(self.org.slug, status=200).render() content = response.content.decode('utf-8') context = RequestContext(self.request) @@ -164,21 +164,15 @@ def test_get_with_authorized_user(self): context ) - assert response.status_code == 200 assert expected == content def test_get_with_unauthorized_user(self): - user = UserFactory.create() - setattr(self.request, 'user', user) - response = self.view(self.request, slug=self.org.slug) - - assert response.status_code == 302 - print(response['location']) + self._get(self.org.slug, status=302) assert ("You don't have permission to access this organization" in [str(m) for m in get_messages(self.request)]) -class OrganzationEditTest(TestCase): +class OrganizationEditTest(TestCase): def setUp(self): self.view = default.OrganizationEdit.as_view() self.request = HttpRequest() @@ -295,7 +289,7 @@ def test_post_with_unauthenticated_user(self): assert self.org.description != 'Some description' -class OrganzationArchiveTest(TestCase): +class OrganizationArchiveTest(TestCase): def setUp(self): self.view = default.OrganizationArchive.as_view() self.request = HttpRequest() @@ -351,7 +345,7 @@ def test_archive_with_unauthenticated_user(self): assert self.org.archived is False -class OrganzationUnarchiveTest(TestCase): +class OrganizationUnarchiveTest(TestCase): def setUp(self): self.view = default.OrganizationUnarchive.as_view() self.request = HttpRequest() @@ -690,6 +684,20 @@ def test_post_with_unauthenticated_user(self): user=self.member) assert role.admin is False + def test_post_with_invalid_form(self): + user = UserFactory.create() + assign_user_policies(user, self.policy) + setattr(self.request, 'user', user) + setattr(self.request, 'method', 'POST') + setattr(self.request, 'POST', {'org_role': 'X'}) + + response = self.view(self.request, slug=self.org.slug, + username=self.member.username).render() + assert response.status_code == 200 + errors = response.context_data['form']['org_role'].errors + assert len(errors) == 1 + assert 'X is not one of the available choices' in errors[0] + class OrganizationMembersRemoveTest(TestCase): def setUp(self): @@ -756,67 +764,3 @@ def test_get_with_unauthenticated_user(self): assert response.status_code == 302 assert '/account/login/' in response['location'] assert role is True - - -class ProjectListTest(TestCase): - def setUp(self): - self.view = default.ProjectList.as_view() - self.request = HttpRequest() - setattr(self.request, 'method', 'GET') - setattr(self.request, 'user', AnonymousUser()) - - self.ok_org1 = OrganizationFactory.create( - name='OK org 1', slug='org1' - ) - self.ok_org2 = OrganizationFactory.create( - name='OK org 2', slug='org2' - ) - self.unath_org = OrganizationFactory.create( - name='Unauthorized org', slug='unauth-org' - ) - self.projs = [] - self.projs += ProjectFactory.create_batch(2, organization=self.ok_org1) - self.projs += ProjectFactory.create_batch(2, organization=self.ok_org2) - ProjectFactory.create( - name='Unauthorized project', - project_slug='unauth-proj', - organization=self.ok_org2 - ) - ProjectFactory.create( - name='Project in unauthorized org', - project_slug='proj-in-unath-org', - organization=self.unath_org - ) - - clauses = { - 'clause': [ - clause('allow', ['project.list'], ['organization/*']), - clause('allow', ['project.view'], ['project/*/*']), - clause('deny', ['project.view'], ['project/unauth-org/*']), - clause('deny', ['project.view'], ['project/*/unauth-proj']) - ] - } - self.policy = Policy.objects.create( - name='allow', - body=json.dumps(clauses)) - - def test_get_with_user(self): - user = UserFactory.create() - assign_user_policies(user, self.policy) - setattr(self.request, 'user', user) - response = self.view(self.request).render() - content = response.content.decode('utf-8') - - expected = render_to_string( - 'organization/project_list.html', - {'object_list': self.projs, - 'add_allowed': True, - 'user': self.request.user}, - request=self.request) - - assert response.status_code == 200 - assert expected == content - - -class ProjectAddTest(TestCase): - pass diff --git a/cadasta/organization/tests/test_views_default_projects.py b/cadasta/organization/tests/test_views_default_projects.py new file mode 100644 index 000000000..d010f4bd4 --- /dev/null +++ b/cadasta/organization/tests/test_views_default_projects.py @@ -0,0 +1,293 @@ +import json +from django.test import TestCase +from django.core.urlresolvers import reverse +from django.http import HttpRequest, Http404 +from django.contrib.auth.models import AnonymousUser +from django.template.loader import render_to_string +from django.template import RequestContext +from django.contrib.messages.storage.fallback import FallbackStorage +from django.contrib.messages.api import get_messages +import pytest + +from tutelary.models import Policy, assign_user_policies + +from accounts.tests.factories import UserFactory +from organization.models import OrganizationRole, Project, ProjectRole +from ..views import default +from .factories import OrganizationFactory, ProjectFactory, clause + + +class ProjectListTest(TestCase): + def setUp(self): + self.view = default.ProjectList.as_view() + self.request = HttpRequest() + setattr(self.request, 'method', 'GET') + setattr(self.request, 'user', AnonymousUser()) + + self.ok_org1 = OrganizationFactory.create( + name='OK org 1', slug='org1' + ) + self.ok_org2 = OrganizationFactory.create( + name='OK org 2', slug='org2' + ) + self.unath_org = OrganizationFactory.create( + name='Unauthorized org', slug='unauth-org' + ) + self.projs = [] + self.projs += ProjectFactory.create_batch(2, organization=self.ok_org1) + self.projs += ProjectFactory.create_batch(2, organization=self.ok_org2) + ProjectFactory.create( + name='Unauthorized project', + project_slug='unauth-proj', + organization=self.ok_org2 + ) + ProjectFactory.create( + name='Project in unauthorized org', + project_slug='proj-in-unath-org', + organization=self.unath_org + ) + + clauses = { + 'clause': [ + clause('allow', ['project.list'], ['organization/*']), + clause('allow', ['project.view'], ['project/*/*']), + clause('deny', ['project.view'], ['project/unauth-org/*']), + clause('deny', ['project.view'], ['project/*/unauth-proj']) + ] + } + self.policy = Policy.objects.create( + name='allow', + body=json.dumps(clauses)) + + def test_get_with_user(self): + user = UserFactory.create() + assign_user_policies(user, self.policy) + setattr(self.request, 'user', user) + response = self.view(self.request).render() + content = response.content.decode('utf-8') + + expected = render_to_string( + 'organization/project_list.html', + {'object_list': self.projs, + 'add_allowed': True, + 'user': self.request.user}, + request=self.request) + + assert response.status_code == 200 + assert expected == content + + +class ProjectDashboardTest(TestCase): + def setUp(self): + self.view = default.ProjectDashboard.as_view() + self.request = HttpRequest() + setattr(self.request, 'method', 'GET') + + self.project1 = ProjectFactory.create() + self.project2 = ProjectFactory.create( + extent=('SRID=4326;' + 'POLYGON ((-5.1031494140625000 8.1299292850467957, ' + '-5.0482177734375000 7.6837733211111425, ' + '-4.6746826171875000 7.8252894725496338, ' + '-4.8641967773437491 8.2278005261522775, ' + '-5.1031494140625000 8.1299292850467957))') + ) + + clauses = { + 'clause': [ + clause('allow', ['project.list']), + clause('allow', ['project.view'], ['project/*/*']) + ] + } + self.policy = Policy.objects.create( + name='allow', + body=json.dumps(clauses)) + + setattr(self.request, 'session', 'session') + self.messages = FallbackStorage(self.request) + setattr(self.request, '_messages', self.messages) + + self.user = UserFactory.create() + setattr(self.request, 'user', self.user) + + def _get(self, project, status=None): + response = self.view(self.request, + organization=project.organization.slug, + project=project.project_slug) + if status is not None: + assert response.status_code == status + return response + + def test_get_with_authorized_user(self): + assign_user_policies(self.user, self.policy) + response = self._get(self.project1, status=200).render() + content = response.content.decode('utf-8') + + context = RequestContext(self.request) + context['object'] = self.project1 + context['project'] = self.project1 + expected = render_to_string( + 'organization/project_dashboard.html', + context, request=self.request + ) + + assert expected == content + + def test_get_with_unauthorized_user(self): + self._get(self.project1, status=302) + assert ("You don't have permission to access this project" + in [str(m) for m in get_messages(self.request)]) + + def test_get_non_existent_project(self): + assign_user_policies(self.user, self.policy) + with pytest.raises(Http404): + self.view(self.request, + organization='some-org', project='some-project') + + def test_get_with_project_extent(self): + assign_user_policies(self.user, self.policy) + response = self._get(self.project2, status=200).render() + content = response.content.decode('utf-8') + + context = RequestContext(self.request) + context['object'] = self.project2 + context['project'] = self.project2 + default.assign_project_extent_context(context, self.project2) + expected = render_to_string( + 'organization/project_dashboard.html', + context, request=self.request + ) + + assert expected == content + + +class ProjectAddTest(TestCase): + def setUp(self): + self.view = default.ProjectAddWizard.as_view() + self.request = HttpRequest() + setattr(self.request, 'method', 'GET') + + clauses = { + 'clause': [ + clause('allow', ['project.create'], ['organization/*']) + ] + } + self.policy = Policy.objects.create( + name='allow', + body=json.dumps(clauses)) + + setattr(self.request, 'session', 'session') + self.messages = FallbackStorage(self.request) + setattr(self.request, '_messages', self.messages) + + self.user = UserFactory.create() + self.unauth_user = UserFactory.create() + setattr(self.request, 'user', self.user) + assign_user_policies(self.user, self.policy) + + self.org = OrganizationFactory.create( + name='Test Org', slug='test-org' + ) + self.users = UserFactory.create_from_kwargs([ + {'username': 'org_admin_1'}, + {'username': 'org_admin_2'}, + {'username': 'org_member_1'}, + {'username': 'org_member_2'}, + {'username': 'org_member_3'}, + {'username': 'org_non_member_1'}, + {'username': 'org_non_member_2'}, + {'username': 'org_non_member_3'}, + {'username': 'org_non_member_4'}]) + for idx in range(5): + OrganizationRole.objects.create(organization=self.org, + user=self.users[idx], + admin=(idx < 2)) + + def _get(self, status=None, check_content=False, login_redirect=False): + response = self.client.get(reverse('project:add')) + if status is not None: + assert response.status_code == status + if login_redirect: + assert '/account/login/' in response['location'] + if check_content: + content = response.content.decode('utf-8') + expected = render_to_string( + 'organization/project_add_extents.html', + context=response.context_data, + request=response.wsgi_request + ) + assert expected == content + + def test_initial_get_valid(self): + self.client.force_login(self.user) + self._get(status=200, check_content=True) + + def test_initial_get_with_unauthorized_user(self): + self.client.force_login(self.unauth_user) + self._get(status=200, check_content=True) + + def test_initial_get_with_unauthenticated_user(self): + self._get(status=302, login_redirect=True) + + EXTENTS_POST_DATA = { + 'project_add_wizard-current_step': 'extents', + 'extents-location': + '{"type":"Polygon",' + '"coordinates":[[[11.524186134338379,47.253077373726235],' + '[11.524186134338379,47.264669556186654],' + '[11.544098854064941,47.264669556186654],' + '[11.544098854064941,47.253077373726235],' + '[11.524186134338379,47.253077373726235]]]}' + } + DETAILS_POST_DATA = { + 'project_add_wizard-current_step': 'details', + 'details-organization': 'test-org', + 'details-name': 'Test Project', + 'details-description': 'This is a test project', + 'details-public': 'true', + 'details-url': 'http://www.test.org' + } + PERMISSIONS_POST_DATA = { + 'project_add_wizard-current_step': 'permissions', + 'org_member_1': 'PM', + 'org_member_2': 'DC', + 'org_member_3': 'PU' + } + PERMISSIONS_POST_DATA_BAD = { + 'project_add_wizard-current_step': 'permissions', + 'org_member_1': 'PM', + 'org_member_2': 'DC', + 'org_member_3': 'PU', + 'bad_user': 'PU' + } + + def test_full_flow_valid(self): + self.client.force_login(self.user) + extents_response = self.client.post( + reverse('project:add'), self.EXTENTS_POST_DATA + ) + assert extents_response.status_code == 200 + details_response = self.client.post( + reverse('project:add'), self.DETAILS_POST_DATA + ) + assert details_response.status_code == 200 + permissions_response = self.client.post( + reverse('project:add'), self.PERMISSIONS_POST_DATA + ) + assert permissions_response.status_code == 302 + assert ('/organizations/test-org/projects/test-project/' in + permissions_response['location']) + + proj = Project.objects.get(organization=self.org, name='Test Project') + assert proj.project_slug == 'test-project' + assert proj.description == 'This is a test project' + for r in ProjectRole.objects.filter(project=proj): + if r.user.username == 'org_member_1': + assert r.role == 'PM' + elif r.user.username == 'org_member_2': + assert r.role == 'DC' + elif r.user.username == 'org_member_3': + assert r.role == 'PU' + else: + assert False + # assert proj.public diff --git a/cadasta/organization/tests/test_views_default_users.py b/cadasta/organization/tests/test_views_default_users.py new file mode 100644 index 000000000..631d91301 --- /dev/null +++ b/cadasta/organization/tests/test_views_default_users.py @@ -0,0 +1,160 @@ +import json +from django.test import TestCase +from django.http import HttpRequest, Http404 +from django.contrib.auth.models import AnonymousUser +from django.template.loader import render_to_string +from django.contrib.messages.storage.fallback import FallbackStorage +from django.contrib.messages import get_messages +import pytest + +from tutelary.models import Policy, assign_user_policies + +from accounts.tests.factories import UserFactory +from organization.tests.factories import OrganizationFactory +from ..views import default +from .factories import clause + + +USER_CLAUSES = { + 'clause': [ + clause('allow', ['user.list']), + clause('allow', ['user.update'], ['user/*']) + ] +} + + +class UserListTest(TestCase): + def setUp(self): + self.view = default.UserList.as_view() + self.request = HttpRequest() + setattr(self.request, 'method', 'GET') + + setattr(self.request, 'session', 'session') + self.messages = FallbackStorage(self.request) + setattr(self.request, '_messages', self.messages) + + self.users = UserFactory.create_batch(3) + self.org1 = OrganizationFactory.create(add_users=[self.users[0]]) + self.org2 = OrganizationFactory.create( + add_users=[self.users[0], self.users[1]] + ) + + self.policy = Policy.objects.create( + name='allow', body=json.dumps(USER_CLAUSES) + ) + + self.user = UserFactory.create() + self.users.append(self.user) + setattr(self.request, 'user', self.user) + + def test_get_with_user(self): + assign_user_policies(self.user, self.policy) + response = self.view(self.request).render() + content = response.content.decode('utf-8') + + dummy = default.UserList() + dummy.object_list = self.users + dummy.get_context_data() + expected = render_to_string( + 'organization/user_list.html', + {'object_list': self.users, 'user': self.request.user}, + request=self.request) + + assert response.status_code == 200 + assert expected == content + + def test_get_with_unauthorized_user(self): + response = self.view(self.request) + assert response.status_code == 302 + + def test_get_with_unauthenticated_user(self): + setattr(self.request, 'user', AnonymousUser()) + response = self.view(self.request) + assert response.status_code == 302 + assert '/account/login/' in response['location'] + + +class UserActivationTest(TestCase): + def setUp(self): + self.request = HttpRequest() + setattr(self.request, 'method', 'POST') + + setattr(self.request, 'session', 'session') + self.messages = FallbackStorage(self.request) + setattr(self.request, '_messages', self.messages) + + self.policy = Policy.objects.create( + name='allow', body=json.dumps(USER_CLAUSES) + ) + self.users = UserFactory.create_from_kwargs([ + {'is_active': True}, + {'is_active': True}, + {'is_active': False} + ]) + self.user = UserFactory.create(is_active=True) + setattr(self.request, 'user', self.user) + self.users.append(self.user) + + def _post(self, user_index_or_dummy, before, set, after, + status=None, user_redirect=False, login_redirect=False, + fail_message=False, raises_404=False): + view = default.UserActivation.as_view(new_state=set) + username = user_index_or_dummy + mod_user = None + if not isinstance(user_index_or_dummy, str): + mod_user = self.users[user_index_or_dummy] + username = mod_user.username + assert mod_user.is_active is before + if raises_404: + with pytest.raises(Http404): + response = view(self.request, user=username) + else: + response = view(self.request, user=username) + if status is not None: + assert response.status_code == status + if user_redirect: + assert '/users/' in response['location'] + if login_redirect: + assert '/account/login/' in response['location'] + if fail_message: + assert ("You don't have permission to update user details" in + [str(m) for m in get_messages(self.request)]) + if not isinstance(user_index_or_dummy, str): + mod_user.refresh_from_db() + assert mod_user.is_active is after + + def test_activate_valid(self): + self.user.assign_policies(self.policy) + self._post(2, False, True, True, status=302, user_redirect=True) + self._post(0, True, True, True, status=302, user_redirect=True) + + def test_deactivate_valid(self): + self.user.assign_policies(self.policy) + self._post(1, True, False, False, status=302, user_redirect=True) + self._post(2, False, False, False, status=302, user_redirect=True) + + def test_activate_nonexistent_user(self): + self.user.assign_policies(self.policy) + self._post('baduser', False, True, True, raises_404=True) + + def test_deactivate_nonexistent_user(self): + self.user.assign_policies(self.policy) + self._post('baduser', True, False, False, raises_404=True) + + def test_activate_unauthorized(self): + self._post(2, False, True, False, status=302, fail_message=True) + self._post(0, True, True, True, status=302, fail_message=True) + + def test_deactivate_unauthorized(self): + self._post(1, True, False, True, status=302, fail_message=True) + self._post(2, False, False, False, status=302, fail_message=True) + + def test_activate_unauthenticated(self): + setattr(self.request, 'user', AnonymousUser()) + self._post(2, False, True, False, status=302, login_redirect=True) + self._post(0, True, True, True, status=302, login_redirect=True) + + def test_deactivate_unauthenticated(self): + setattr(self.request, 'user', AnonymousUser()) + self._post(1, True, False, True, status=302, login_redirect=True) + self._post(2, False, False, False, status=302, login_redirect=True) diff --git a/cadasta/organization/views/default.py b/cadasta/organization/views/default.py index 4d8f0d9c4..dc8a74bd1 100644 --- a/cadasta/organization/views/default.py +++ b/cadasta/organization/views/default.py @@ -1,8 +1,7 @@ from django.http import Http404 from django.db import transaction import django.views.generic as generic -from django.shortcuts import redirect -from django.contrib import messages +from django.shortcuts import redirect, get_object_or_404 from django.core.urlresolvers import reverse from django.utils.text import slugify from django.utils.translation import gettext as _ @@ -215,7 +214,7 @@ def get(self, *args, **kwargs): return self.post(*args, **kwargs) -class UserList(PermissionRequiredMixin, generic.ListView): +class UserList(LoginPermissionRequiredMixin, generic.ListView): model = User template_name = 'organization/user_list.html' permission_required = 'user.list' @@ -233,19 +232,18 @@ def get_context_data(self, **kwargs): return context -class UserActivation(generic.View): +class UserActivation(LoginPermissionRequiredMixin, generic.View): + permission_required = 'user.update' permission_denied_message = error_messages.USERS_UPDATE new_state = None + def get_perms_objects(self): + return [get_object_or_404(User, username=self.kwargs['user'])] + def post(self, request, user): - userobj = User.objects.get(username=user) - if userobj is not None: - userobj.is_active = self.new_state - userobj.save() - else: - msg = _("Wasn't able to modify user state " - "for user '{username}'").format(username=user) - messages.add_message(request, messages.ERROR, msg) + userobj = get_object_or_404(User, username=user) + userobj.is_active = self.new_state + userobj.save() return redirect('user:list') @@ -261,9 +259,21 @@ def get_context_data(self, **kwargs): return context -class ProjectDashboard(generic.DetailView): +def assign_project_extent_context(context, project): + ext = project.extent.extent + context['extent'] = True + context['wlon'] = ext[0] + context['slat'] = ext[1] + context['elon'] = ext[2] + context['nlat'] = ext[3] + context['boundary'] = list(map(lambda t: [t[1], t[0]], + project.extent.coords[0])) + + +class ProjectDashboard(PermissionRequiredMixin, generic.DetailView): model = Project template_name = 'organization/project_dashboard.html' + permission_required = 'project.view' permission_denied_message = error_messages.PROJ_VIEW def get_context_data(self, **kwargs): @@ -274,15 +284,7 @@ def get_context_data(self, **kwargs): if self.object.extent is None: context['extent'] = False else: - print(self.object.extent.coords) - ext = self.object.extent.extent - context['extent'] = True - context['wlon'] = ext[0] - context['slat'] = ext[1] - context['elon'] = ext[2] - context['nlat'] = ext[3] - context['boundary'] = list(map(lambda t: [t[1], t[0]], - self.object.extent.coords[0])) + assign_project_extent_context(context, self.object) return context def get_object(self, queryset=None): @@ -308,7 +310,18 @@ def get_object(self, queryset=None): } -class ProjectAddWizard(wizard.SessionWizardView): +def add_wizard_permission_required(self, view, request): + if request.method != 'POST': + return () + session = request.session.get('wizard_project_add_wizard', None) + if session is None or 'details' not in session['step_data']: + return () + else: + return 'project.create' + + +class ProjectAddWizard(LoginPermissionRequiredMixin, wizard.SessionWizardView): + permission_required = add_wizard_permission_required form_list = PROJECT_ADD_FORMS def __init__(self, *args, **kwargs): @@ -316,6 +329,14 @@ def __init__(self, *args, **kwargs): self.members = None super().__init__(*args, **kwargs) + def get_perms_objects(self): + session = self.request.session.get('wizard_project_add_wizard', None) + if session is None or 'details' not in session['step_data']: + return [] + else: + slug = session['step_data']['details']['details-organization'][0] + return [Organization.objects.get(slug=slug)] + def get_template_names(self): return [PROJECT_ADD_TEMPLATES[self.steps.current]] @@ -369,20 +390,21 @@ def done(self, form_list, form_dict, **kwargs): url = form_data[1]['details-url'] # private = form_data[1]['details-public'] != 'on' org = Organization.objects.get(slug=organization) - su_role = Role.objects.get(name='superuser') - oa_role = Role.objects.get(name=organization + '-oa') + try: + su_role = Role.objects.get(name='superuser') + except: + su_role = None usernames = [] for user in org.users.all(): - is_admin = False - for pol in user.assigned_policies(): - if (isinstance(pol, Role) and - (pol == su_role or pol == oa_role)): + is_admin = any([isinstance(pol, Role) and pol == su_role + for pol in user.assigned_policies()]) + if not is_admin: + if OrganizationRole.objects.get(organization=org, + user=user).admin: is_admin = True - break if not is_admin: usernames.append(user.username) user_roles = [(k, form_data[2][k]) for k in usernames] - print('name:', name) with transaction.atomic(): project = Project.objects.create( @@ -391,7 +413,6 @@ def done(self, form_list, form_dict, **kwargs): ) for username, role in user_roles: user = User.objects.get(username=username) - print(user, role) ProjectRole.objects.create( project=project, user=user, role=role ) diff --git a/cadasta/test-data/images/cadasta.png b/cadasta/test-data/images/cadasta.png deleted file mode 100644 index 69c34ac33..000000000 Binary files a/cadasta/test-data/images/cadasta.png and /dev/null differ diff --git a/cadasta/test-data/images/h4h.png b/cadasta/test-data/images/h4h.png deleted file mode 100644 index 9c6cf9a64..000000000 Binary files a/cadasta/test-data/images/h4h.png and /dev/null differ diff --git a/cadasta/test-data/load-data b/cadasta/test-data/load-data deleted file mode 100755 index 10b6887f3..000000000 --- a/cadasta/test-data/load-data +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -../manage.py shell <