Files
edx-platform/common/djangoapps/student/tests/test_password_policy.py
Zainab Amir 8cc5f13daf Add rate limit to registration endpoint (#27060)
Currently the registration endpoint has no rate limit. Added a new ratelimit
variable to support the change, it's value is set to 60/7d.

VAN-302
2021-03-25 16:28:30 +05:00

268 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
This test file will verify proper password policy enforcement, which is an option feature
"""
import json
from django.test import TestCase
from django.test.client import RequestFactory
from django.test.utils import override_settings
from django.urls import reverse
from common.djangoapps.util.password_policy_validators import create_validator_config
@override_settings(RATELIMIT_ENABLE=False)
class TestPasswordPolicy(TestCase):
"""
Go through some password policy tests to make sure things are properly working
"""
def setUp(self):
super().setUp()
self.url = reverse('create_account')
self.request_factory = RequestFactory()
self.url_params = {
'username': 'username',
'email': 'foo_bar@bar.com',
'name': 'username',
'terms_of_service': 'true',
'honor_code': 'true',
}
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.MinimumLengthValidator', {'min_length': 6}) # lint-amnesty, pylint: disable=line-too-long
])
def test_password_length_too_short(self):
self.url_params['password'] = 'aaa'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] ==\
'This password is too short. It must contain at least 6 characters.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.MinimumLengthValidator', {'min_length': 6}) # lint-amnesty, pylint: disable=line-too-long
])
def test_password_length_long_enough(self):
self.url_params['password'] = 'ThisIsALongerPassword'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.MaximumLengthValidator', {'max_length': 12}) # lint-amnesty, pylint: disable=line-too-long
])
def test_password_length_too_long(self):
self.url_params['password'] = 'ThisPasswordIsWayTooLong'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] ==\
'This password is too long. It must contain no more than 12 characters.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.UppercaseValidator', {'min_upper': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_password_not_enough_uppercase(self):
self.url_params['password'] = 'thisshouldfail'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] == 'This password must contain at least 3 uppercase letters.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.UppercaseValidator', {'min_upper': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_password_enough_uppercase(self):
self.url_params['password'] = 'ThisShouldPass'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.LowercaseValidator', {'min_lower': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_password_not_enough_lowercase(self):
self.url_params['password'] = 'THISSHOULDFAIL'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] == 'This password must contain at least 3 lowercase letters.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.LowercaseValidator', {'min_lower': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_password_enough_lowercase(self):
self.url_params['password'] = 'ThisShouldPass'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.PunctuationValidator', {'min_punctuation': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_not_enough_punctuations(self):
self.url_params['password'] = 'thisshouldfail'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] == 'This password must contain at least 3 punctuation marks.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.PunctuationValidator', {'min_punctuation': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_enough_punctuations(self):
self.url_params['password'] = 'Th!sSh.uldPa$*'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.NumericValidator', {'min_numeric': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_not_enough_numeric_characters(self):
# The unicode ២ is the number 2 in Khmer and the ٧ is the Arabic-Indic number 7
self.url_params['password'] = 'thisShouldFail២٧'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] == 'This password must contain at least 3 numbers.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.NumericValidator', {'min_numeric': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_enough_numeric_characters(self):
# The unicode ២ is the number 2 in Khmer
self.url_params['password'] = 'thisShouldPass២33'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.AlphabeticValidator', {'min_alphabetic': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_not_enough_alphabetic_characters(self):
self.url_params['password'] = '123456ab'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] == 'This password must contain at least 3 letters.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.AlphabeticValidator', {'min_alphabetic': 3}) # lint-amnesty, pylint: disable=line-too-long
])
def test_enough_alphabetic_characters(self):
self.url_params['password'] = '𝒯𝓗Ï𝓼𝒫å𝓼𝓼𝔼𝓼'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.MinimumLengthValidator', {'min_length': 3}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.UppercaseValidator', {'min_upper': 3}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.NumericValidator', {'min_numeric': 3}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.PunctuationValidator', {'min_punctuation': 3}), # lint-amnesty, pylint: disable=line-too-long
])
def test_multiple_errors_fail(self):
self.url_params['password'] = 'thisshouldfail'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
error_strings = [
"This password must contain at least 3 uppercase letters.",
"This password must contain at least 3 numbers.",
"This password must contain at least 3 punctuation marks.",
]
for i in range(3):
assert obj['password'][i]['user_message'] == error_strings[i]
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.MinimumLengthValidator', {'min_length': 3}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.UppercaseValidator', {'min_upper': 3}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.LowercaseValidator', {'min_lower': 3}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.NumericValidator', {'min_numeric': 3}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.PunctuationValidator', {'min_punctuation': 3}), # lint-amnesty, pylint: disable=line-too-long
])
def test_multiple_errors_pass(self):
self.url_params['password'] = 'tH1s Sh0u!d P3#$!'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('django.contrib.auth.password_validation.CommonPasswordValidator')
])
def test_common_password_fail(self):
self.url_params['password'] = 'password'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] == 'This password is too common.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('django.contrib.auth.password_validation.CommonPasswordValidator')
])
def test_common_password_pass(self):
self.url_params['password'] = 'this_is_ok'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('common.djangoapps.util.password_policy_validators.MinimumLengthValidator', {'min_length': 6}), # lint-amnesty, pylint: disable=line-too-long
create_validator_config('common.djangoapps.util.password_policy_validators.MaximumLengthValidator', {'max_length': 75}), # lint-amnesty, pylint: disable=line-too-long
])
def test_with_unicode(self):
self.url_params['password'] = '四節比分和七年前'
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']
@override_settings(RATELIMIT_ENABLE=False)
class TestUsernamePasswordNonmatch(TestCase):
"""
Test that registration username and password fields differ
"""
def setUp(self):
super().setUp()
self.url = reverse('create_account')
self.url_params = {
'username': 'username',
'email': 'foo_bar@bar.com',
'name': 'username',
'terms_of_service': 'true',
'honor_code': 'true',
}
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('django.contrib.auth.password_validation.UserAttributeSimilarityValidator')
])
def test_with_username_password_match(self):
self.url_params['username'] = "foobar"
self.url_params['password'] = "foobar"
response = self.client.post(self.url, self.url_params)
assert response.status_code == 400
obj = json.loads(response.content.decode('utf-8'))
assert obj['password'][0]['user_message'] == 'The password is too similar to the username.'
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('django.contrib.auth.password_validation.UserAttributeSimilarityValidator')
])
def test_with_username_password_nonmatch(self):
self.url_params['username'] = "foobar"
self.url_params['password'] = "nonmatch"
response = self.client.post(self.url, self.url_params)
assert response.status_code == 200
obj = json.loads(response.content.decode('utf-8'))
assert obj['success']