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
268 lines
14 KiB
Python
268 lines
14 KiB
Python
"""
|
||
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']
|