| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- from django.test import override_settings
- from django.urls import reverse
- from core.models import ObjectType
- from users.models import Group, ObjectPermission, Token, User
- from utilities.data import deepmerge
- from utilities.testing import APIViewTestCases, APITestCase, create_test_user
- class AppTest(APITestCase):
- def test_root(self):
- url = reverse('users-api:api-root')
- response = self.client.get(f'{url}?format=api', **self.header)
- self.assertEqual(response.status_code, 200)
- class UserTest(APIViewTestCases.APIViewTestCase):
- model = User
- brief_fields = ['display', 'id', 'url', 'username']
- validation_excluded_fields = ['password']
- bulk_update_data = {
- 'email': 'test@example.com',
- }
- @classmethod
- def setUpTestData(cls):
- permissions = (
- ObjectPermission(name='Permission 1', actions=['view']),
- ObjectPermission(name='Permission 2', actions=['view']),
- ObjectPermission(name='Permission 3', actions=['view']),
- )
- ObjectPermission.objects.bulk_create(permissions)
- permissions[0].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'site'))
- permissions[1].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'location'))
- permissions[2].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'rack'))
- users = (
- User(username='User1', password='password1'),
- User(username='User2', password='password2'),
- User(username='User3', password='password3'),
- )
- User.objects.bulk_create(users)
- cls.create_data = [
- {
- 'username': 'User4',
- 'password': 'password4',
- 'permissions': [permissions[0].pk],
- },
- {
- 'username': 'User5',
- 'password': 'password5',
- 'permissions': [permissions[1].pk],
- },
- {
- 'username': 'User6',
- 'password': 'password6',
- 'permissions': [permissions[2].pk],
- },
- ]
- def test_that_password_is_changed(self):
- """
- Test that password is changed
- """
- obj_perm = ObjectPermission(
- name='Test permission',
- actions=['change']
- )
- obj_perm.save()
- obj_perm.users.add(self.user)
- obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
- user_credentials = {
- 'username': 'newuser',
- 'password': 'abc123',
- }
- user = User.objects.create_user(**user_credentials)
- data = {
- 'password': 'newpassword'
- }
- url = reverse('users-api:user-detail', kwargs={'pk': user.id})
- response = self.client.patch(url, data, format='json', **self.header)
- self.assertEqual(response.status_code, 200)
- user.refresh_from_db()
- self.assertTrue(user.check_password(data['password']))
- @override_settings(AUTH_PASSWORD_VALIDATORS=[{
- 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
- 'OPTIONS': {'min_length': 8}
- }])
- def test_password_validation_enforced(self):
- """
- Test that any configured password validation rules (AUTH_PASSWORD_VALIDATORS) are enforced.
- """
- self.add_permissions('users.add_user')
- data = {
- 'username': 'new_user',
- 'password': 'foo',
- }
- url = reverse('users-api:user-list')
- # Password too short
- response = self.client.post(url, data, format='json', **self.header)
- self.assertEqual(response.status_code, 400)
- # Password long enough
- data['password'] = 'foobar123'
- response = self.client.post(url, data, format='json', **self.header)
- self.assertEqual(response.status_code, 201)
- class GroupTest(APIViewTestCases.APIViewTestCase):
- model = Group
- brief_fields = ['description', 'display', 'id', 'name', 'url']
- @classmethod
- def setUpTestData(cls):
- permissions = (
- ObjectPermission(name='Permission 1', actions=['view']),
- ObjectPermission(name='Permission 2', actions=['view']),
- ObjectPermission(name='Permission 3', actions=['view']),
- )
- ObjectPermission.objects.bulk_create(permissions)
- permissions[0].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'site'))
- permissions[1].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'location'))
- permissions[2].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'rack'))
- groups = (
- Group(name='Group 1'),
- Group(name='Group 2'),
- Group(name='Group 3'),
- )
- Group.objects.bulk_create(groups)
- cls.create_data = [
- {
- 'name': 'Group 4',
- 'permissions': [permissions[0].pk],
- },
- {
- 'name': 'Group 5',
- 'permissions': [permissions[1].pk],
- },
- {
- 'name': 'Group 6',
- 'permissions': [permissions[2].pk],
- },
- ]
- def model_to_dict(self, instance, *args, **kwargs):
- # Overwrite permissions attr to work around the serializer field having a different name
- data = super().model_to_dict(instance, *args, **kwargs)
- data['permissions'] = list(instance.object_permissions.values_list('id', flat=True))
- return data
- def test_bulk_update_objects(self):
- """
- Disabled test. There's no attribute we can set in bulk for Groups.
- """
- return
- class TokenTest(
- # No GraphQL support for Token
- APIViewTestCases.GetObjectViewTestCase,
- APIViewTestCases.ListObjectsViewTestCase,
- APIViewTestCases.CreateObjectViewTestCase,
- APIViewTestCases.UpdateObjectViewTestCase,
- APIViewTestCases.DeleteObjectViewTestCase
- ):
- model = Token
- brief_fields = ['description', 'display', 'id', 'key', 'url', 'write_enabled']
- bulk_update_data = {
- 'description': 'New description',
- }
- def setUp(self):
- super().setUp()
- # Apply grant_token permission to enable the creation of Tokens for other Users
- self.add_permissions('users.grant_token')
- @classmethod
- def setUpTestData(cls):
- users = (
- create_test_user('User1'),
- create_test_user('User2'),
- create_test_user('User3'),
- )
- tokens = (
- Token(user=users[0]),
- Token(user=users[1]),
- Token(user=users[2]),
- )
- # Use save() instead of bulk_create() to ensure keys get automatically generated
- for token in tokens:
- token.save()
- cls.create_data = [
- {
- 'user': users[0].pk,
- },
- {
- 'user': users[1].pk,
- },
- {
- 'user': users[2].pk,
- },
- ]
- def test_provision_token_valid(self):
- """
- Test the provisioning of a new REST API token given a valid username and password.
- """
- user_credentials = {
- 'username': 'user1',
- 'password': 'abc123',
- }
- user = User.objects.create_user(**user_credentials)
- data = {
- **user_credentials,
- 'description': 'My API token',
- 'expires': '2099-12-31T23:59:59Z',
- }
- url = reverse('users-api:token_provision')
- response = self.client.post(url, data, format='json', **self.header)
- self.assertEqual(response.status_code, 201)
- self.assertIn('key', response.data)
- self.assertEqual(len(response.data['key']), 40)
- self.assertEqual(response.data['description'], data['description'])
- self.assertEqual(response.data['expires'], data['expires'])
- token = Token.objects.get(user=user)
- self.assertEqual(token.key, response.data['key'])
- def test_provision_token_invalid(self):
- """
- Test the behavior of the token provisioning view when invalid credentials are supplied.
- """
- data = {
- 'username': 'nonexistentuser',
- 'password': 'abc123',
- }
- url = reverse('users-api:token_provision')
- response = self.client.post(url, data, format='json', **self.header)
- self.assertEqual(response.status_code, 403)
- def test_provision_token_other_user(self):
- """
- Test provisioning a Token for a different User with & without the grant_token permission.
- """
- # Clear grant_token permission assigned by setUpTestData
- ObjectPermission.objects.filter(users=self.user).delete()
- self.add_permissions('users.add_token')
- user2 = User.objects.create_user(username='testuser2')
- data = {
- 'user': user2.id,
- }
- url = reverse('users-api:token-list')
- # Attempt to create a new Token for User2 *without* the grant_token permission
- response = self.client.post(url, data, format='json', **self.header)
- self.assertEqual(response.status_code, 403)
- # Assign grant_token permission and successfully create a new Token for User2
- self.add_permissions('users.grant_token')
- response = self.client.post(url, data, format='json', **self.header)
- self.assertEqual(response.status_code, 201)
- class ObjectPermissionTest(
- # No GraphQL support for ObjectPermission
- APIViewTestCases.GetObjectViewTestCase,
- APIViewTestCases.ListObjectsViewTestCase,
- APIViewTestCases.CreateObjectViewTestCase,
- APIViewTestCases.UpdateObjectViewTestCase,
- APIViewTestCases.DeleteObjectViewTestCase
- ):
- model = ObjectPermission
- brief_fields = ['actions', 'description', 'display', 'enabled', 'id', 'name', 'object_types', 'url']
- @classmethod
- def setUpTestData(cls):
- groups = (
- Group(name='Group 1'),
- Group(name='Group 2'),
- Group(name='Group 3'),
- )
- Group.objects.bulk_create(groups)
- users = (
- User(username='User1', is_active=True),
- User(username='User2', is_active=True),
- User(username='User3', is_active=True),
- )
- User.objects.bulk_create(users)
- object_type = ObjectType.objects.get(app_label='dcim', model='device')
- for i in range(3):
- objectpermission = ObjectPermission(
- name=f'Permission {i + 1}',
- actions=['view', 'add', 'change', 'delete'],
- constraints={'name': f'TEST{i + 1}'}
- )
- objectpermission.save()
- objectpermission.object_types.add(object_type)
- objectpermission.groups.add(groups[i])
- objectpermission.users.add(users[i])
- cls.create_data = [
- {
- 'name': 'Permission 4',
- 'object_types': ['dcim.site'],
- 'groups': [groups[0].pk],
- 'users': [users[0].pk],
- 'actions': ['view', 'add', 'change', 'delete'],
- 'constraints': {'name': 'TEST4'},
- },
- {
- 'name': 'Permission 5',
- 'object_types': ['dcim.site'],
- 'groups': [groups[1].pk],
- 'users': [users[1].pk],
- 'actions': ['view', 'add', 'change', 'delete'],
- 'constraints': {'name': 'TEST5'},
- },
- {
- 'name': 'Permission 6',
- 'object_types': ['dcim.site'],
- 'groups': [groups[2].pk],
- 'users': [users[2].pk],
- 'actions': ['view', 'add', 'change', 'delete'],
- 'constraints': {'name': 'TEST6'},
- },
- ]
- cls.bulk_update_data = {
- 'description': 'New description',
- }
- class UserConfigTest(APITestCase):
- def test_get(self):
- """
- Retrieve user configuration via GET request.
- """
- userconfig = self.user.config
- url = reverse('users-api:userconfig-list')
- response = self.client.get(url, **self.header)
- self.assertEqual(response.data, {})
- data = {
- "a": 123,
- "b": 456,
- "c": 789,
- }
- userconfig.data = data
- userconfig.save()
- response = self.client.get(url, **self.header)
- self.assertEqual(response.data, data)
- def test_patch(self):
- """
- Set user config via PATCH requests.
- """
- userconfig = self.user.config
- url = reverse('users-api:userconfig-list')
- data = {
- "a": {
- "a1": "X",
- "a2": "Y",
- },
- "b": {
- "b1": "Z",
- }
- }
- response = self.client.patch(url, data=data, format='json', **self.header)
- self.assertDictEqual(response.data, data)
- userconfig.refresh_from_db()
- self.assertDictEqual(userconfig.data, data)
- update_data = {
- "c": 123
- }
- response = self.client.patch(url, data=update_data, format='json', **self.header)
- new_data = deepmerge(data, update_data)
- self.assertDictEqual(response.data, new_data)
- userconfig.refresh_from_db()
- self.assertDictEqual(userconfig.data, new_data)
|