mirror of
https://github.com/ansible/awx.git
synced 2026-02-12 07:04:45 -03:30
Remove archaic monkey patches (#15338) Remove some attached methods from User model Test user-org sublist URLs we did not test before
277 lines
11 KiB
Python
277 lines
11 KiB
Python
from datetime import date
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from django.contrib.sessions.middleware import SessionMiddleware
|
|
from django.test.utils import override_settings
|
|
|
|
from awx.main.models import User
|
|
from awx.api.versioning import reverse
|
|
|
|
|
|
#
|
|
# user creation
|
|
#
|
|
|
|
EXAMPLE_USER_DATA = {"username": "affable", "first_name": "a", "last_name": "a", "email": "a@a.com", "is_superuser": False, "password": "r$TyKiOCb#ED"}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_create(post, admin):
|
|
response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 201
|
|
assert not response.data['is_superuser']
|
|
assert not response.data['is_system_auditor']
|
|
|
|
|
|
# Disable local password checks to ensure that any ValidationError originates from the Django validators.
|
|
@override_settings(
|
|
LOCAL_PASSWORD_MIN_LENGTH=1,
|
|
LOCAL_PASSWORD_MIN_DIGITS=0,
|
|
LOCAL_PASSWORD_MIN_UPPER=0,
|
|
LOCAL_PASSWORD_MIN_SPECIAL=0,
|
|
)
|
|
@pytest.mark.django_db
|
|
def test_user_create_with_django_password_validation_basic(post, admin):
|
|
"""Test if the Django password validators are applied correctly."""
|
|
with override_settings(
|
|
AUTH_PASSWORD_VALIDATORS=[
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
},
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
'OPTIONS': {
|
|
'min_length': 3,
|
|
},
|
|
},
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
},
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
},
|
|
],
|
|
):
|
|
# This user should fail the UserAttrSimilarity, MinLength and CommonPassword validators.
|
|
user_attrs = (
|
|
{
|
|
"password": "Password", # NOSONAR
|
|
"username": "Password",
|
|
"is_superuser": False,
|
|
},
|
|
)
|
|
print(f"Create user with invalid password {user_attrs=}")
|
|
response = post(reverse('api:user_list'), user_attrs, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 400
|
|
# This user should pass all Django validators.
|
|
user_attrs = {
|
|
"password": "r$TyKiOCb#ED", # NOSONAR
|
|
"username": "TestUser",
|
|
"is_superuser": False,
|
|
}
|
|
print(f"Create user with valid password {user_attrs=}")
|
|
response = post(reverse('api:user_list'), user_attrs, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 201
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"user_attrs,validators,expected_status_code",
|
|
[
|
|
# Test password similarity with username.
|
|
(
|
|
{"password": "TestUser1", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
|
],
|
|
400,
|
|
),
|
|
(
|
|
{"password": "abc", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
|
],
|
|
201,
|
|
),
|
|
# Test password min length criterion.
|
|
(
|
|
{"password": "TooShort", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 9}},
|
|
],
|
|
400,
|
|
),
|
|
(
|
|
{"password": "LongEnough", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 9}},
|
|
],
|
|
201,
|
|
),
|
|
# Test password is too common criterion.
|
|
(
|
|
{"password": "Password", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
|
|
],
|
|
400,
|
|
),
|
|
(
|
|
{"password": "aEArV$5Vkdw", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
|
|
],
|
|
201,
|
|
),
|
|
# Test if password is only numeric.
|
|
(
|
|
{"password": "1234567890", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
|
|
],
|
|
400,
|
|
),
|
|
(
|
|
{"password": "abc4567890", "username": "TestUser1", "is_superuser": False}, # NOSONAR
|
|
[
|
|
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
|
|
],
|
|
201,
|
|
),
|
|
],
|
|
)
|
|
# Disable local password checks to ensure that any ValidationError originates from the Django validators.
|
|
@override_settings(
|
|
LOCAL_PASSWORD_MIN_LENGTH=1,
|
|
LOCAL_PASSWORD_MIN_DIGITS=0,
|
|
LOCAL_PASSWORD_MIN_UPPER=0,
|
|
LOCAL_PASSWORD_MIN_SPECIAL=0,
|
|
)
|
|
@pytest.mark.django_db
|
|
def test_user_create_with_django_password_validation_ext(post, delete, admin, user_attrs, validators, expected_status_code):
|
|
"""Test the functionality of the single Django password validators."""
|
|
#
|
|
default_parameters = {
|
|
# Default values for input parameters which are None.
|
|
"user_attrs": {
|
|
"password": "r$TyKiOCb#ED", # NOSONAR
|
|
"username": "DefaultUser",
|
|
"is_superuser": False,
|
|
},
|
|
"validators": [
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
},
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
'OPTIONS': {
|
|
'min_length': 8,
|
|
},
|
|
},
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
},
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
},
|
|
],
|
|
}
|
|
user_attrs = user_attrs if user_attrs is not None else default_parameters["user_attrs"]
|
|
validators = validators if validators is not None else default_parameters["validators"]
|
|
with override_settings(AUTH_PASSWORD_VALIDATORS=validators):
|
|
response = post(reverse('api:user_list'), user_attrs, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == expected_status_code
|
|
# Delete user if it was created succesfully.
|
|
if response.status_code == 201:
|
|
response = delete(reverse('api:user_detail', kwargs={'pk': response.data['id']}), admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 204
|
|
else:
|
|
# Catch the unexpected behavior that sometimes the user is written
|
|
# into the database before the validation fails. This actually can
|
|
# happen if UserSerializer.validate instantiates User(**attrs)!
|
|
username = user_attrs['username']
|
|
assert not User.objects.filter(username=username)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_fail_double_create_user(post, admin):
|
|
response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 201
|
|
|
|
response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 400
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_creating_user_retains_session(post, admin):
|
|
'''
|
|
Creating a new user should not refresh a new session id for the current user.
|
|
'''
|
|
with mock.patch('awx.api.serializers.update_session_auth_hash') as update_session_auth_hash:
|
|
response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin)
|
|
assert response.status_code == 201
|
|
assert not update_session_auth_hash.called
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_updating_own_password_refreshes_session(patch, admin):
|
|
'''
|
|
Updating your own password should refresh the session id.
|
|
'''
|
|
with mock.patch('awx.api.serializers.update_session_auth_hash') as update_session_auth_hash:
|
|
# Attention: If the Django password validator `CommonPasswordValidator`
|
|
# is active, this test case will fail because this validator raises on
|
|
# password 'newpassword'. Consider changing the hard-coded password to
|
|
# something uncommon.
|
|
patch(reverse('api:user_detail', kwargs={'pk': admin.pk}), {'password': 'newpassword'}, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert update_session_auth_hash.called
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_create_delete_create_user(post, delete, admin):
|
|
response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 201
|
|
|
|
response = delete(reverse('api:user_detail', kwargs={'pk': response.data['id']}), admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert response.status_code == 204
|
|
|
|
response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
print(response.data)
|
|
assert response.status_code == 201
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_cannot_update_last_login(patch, admin):
|
|
assert admin.last_login is None
|
|
patch(reverse('api:user_detail', kwargs={'pk': admin.pk}), {'last_login': '2020-03-13T16:39:47.303016Z'}, admin, middleware=SessionMiddleware(mock.Mock()))
|
|
assert User.objects.get(pk=admin.pk).last_login is None
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_verify_attribute_created(admin, get):
|
|
assert admin.created == admin.date_joined
|
|
resp = get(reverse('api:user_detail', kwargs={'pk': admin.pk}), admin)
|
|
assert resp.data['created'] == admin.date_joined
|
|
|
|
past = date(2020, 1, 1).isoformat()
|
|
for op, count in (('gt', 1), ('lt', 0)):
|
|
resp = get(reverse('api:user_list') + f'?created__{op}={past}', admin)
|
|
assert resp.data['count'] == count
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_org_not_shown_in_admin_user_sublists(admin_user, get, organization):
|
|
for view_name in ('user_admin_of_organizations_list', 'user_organizations_list'):
|
|
url = reverse(f'api:{view_name}', kwargs={'pk': admin_user.pk})
|
|
r = get(url, user=admin_user, expect=200)
|
|
assert organization.pk not in [org['id'] for org in r.data['results']]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_admin_user_not_shown_in_org_users(admin_user, get, organization):
|
|
for view_name in ('organization_users_list', 'organization_admins_list'):
|
|
url = reverse(f'api:{view_name}', kwargs={'pk': organization.pk})
|
|
r = get(url, user=admin_user, expect=200)
|
|
assert admin_user.pk not in [u['id'] for u in r.data['results']]
|