265 lines
11 KiB
Python
265 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
|
|
# more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License version 3
|
|
# along with this program; if not, write to the Free Software Foundation, Inc., 51
|
|
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
# (c) 2016 Valentin Samir
|
|
"""Tests module for utils"""
|
|
import django
|
|
from django.test import TestCase, RequestFactory
|
|
from django.db import connection
|
|
|
|
import six
|
|
import warnings
|
|
import datetime
|
|
|
|
from cas_server import utils
|
|
|
|
|
|
class CheckPasswordCase(TestCase):
|
|
"""Tests for the utils function `utils.check_password`"""
|
|
|
|
def setUp(self):
|
|
"""Generate random bytes string that will be used ass passwords"""
|
|
self.password1 = utils.gen_saml_id()
|
|
self.password2 = utils.gen_saml_id()
|
|
if not isinstance(self.password1, bytes): # pragma: no cover executed only in python3
|
|
self.password1 = self.password1.encode("utf8")
|
|
self.password2 = self.password2.encode("utf8")
|
|
|
|
def test_setup(self):
|
|
"""check that generated password are bytes"""
|
|
self.assertIsInstance(self.password1, bytes)
|
|
self.assertIsInstance(self.password2, bytes)
|
|
|
|
def test_plain(self):
|
|
"""test the plain auth method"""
|
|
self.assertTrue(utils.check_password("plain", self.password1, self.password1, "utf8"))
|
|
self.assertFalse(utils.check_password("plain", self.password1, self.password2, "utf8"))
|
|
|
|
def test_plain_unicode(self):
|
|
"""test the plain auth method with unicode input"""
|
|
self.assertTrue(
|
|
utils.check_password(
|
|
"plain",
|
|
self.password1.decode("utf8"),
|
|
self.password1.decode("utf8"),
|
|
"utf8"
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
utils.check_password(
|
|
"plain",
|
|
self.password1.decode("utf8"),
|
|
self.password2.decode("utf8"),
|
|
"utf8"
|
|
)
|
|
)
|
|
|
|
def test_crypt(self):
|
|
"""test the crypt auth method"""
|
|
salts = ["$6$UVVAQvrMyXMF3FF3", "aa"]
|
|
hashed_password1 = []
|
|
for salt in salts:
|
|
if six.PY3:
|
|
hashed_password1.append(
|
|
utils.crypt.crypt(
|
|
self.password1.decode("utf8"),
|
|
salt
|
|
).encode("utf8")
|
|
)
|
|
else:
|
|
hashed_password1.append(utils.crypt.crypt(self.password1, salt))
|
|
|
|
for hp1 in hashed_password1:
|
|
self.assertTrue(utils.check_password("crypt", self.password1, hp1, "utf8"))
|
|
self.assertFalse(utils.check_password("crypt", self.password2, hp1, "utf8"))
|
|
|
|
with self.assertRaises(ValueError):
|
|
utils.check_password("crypt", self.password1, b"$truc$s$dsdsd", "utf8")
|
|
|
|
def test_ldap_password_valid(self):
|
|
"""test the ldap auth method with all the schemes"""
|
|
salt = b"UVVAQvrMyXMF3FF3"
|
|
schemes_salt = [b"{SMD5}", b"{SSHA}", b"{SSHA256}", b"{SSHA384}", b"{SSHA512}"]
|
|
schemes_nosalt = [b"{MD5}", b"{SHA}", b"{SHA256}", b"{SHA384}", b"{SHA512}"]
|
|
hashed_password1 = []
|
|
for scheme in schemes_salt:
|
|
hashed_password1.append(
|
|
utils.LdapHashUserPassword.hash(scheme, self.password1, salt, charset="utf8")
|
|
)
|
|
for scheme in schemes_nosalt:
|
|
hashed_password1.append(
|
|
utils.LdapHashUserPassword.hash(scheme, self.password1, charset="utf8")
|
|
)
|
|
hashed_password1.append(
|
|
utils.LdapHashUserPassword.hash(
|
|
b"{CRYPT}",
|
|
self.password1,
|
|
b"$6$UVVAQvrMyXMF3FF3",
|
|
charset="utf8"
|
|
)
|
|
)
|
|
for hp1 in hashed_password1:
|
|
self.assertIsInstance(hp1, bytes)
|
|
self.assertTrue(utils.check_password("ldap", self.password1, hp1, "utf8"))
|
|
self.assertFalse(utils.check_password("ldap", self.password2, hp1, "utf8"))
|
|
|
|
def test_ldap_password_fail(self):
|
|
"""test the ldap auth method with malformed hash or bad schemes"""
|
|
salt = b"UVVAQvrMyXMF3FF3"
|
|
schemes_salt = [b"{SMD5}", b"{SSHA}", b"{SSHA256}", b"{SSHA384}", b"{SSHA512}"]
|
|
schemes_nosalt = [b"{MD5}", b"{SHA}", b"{SHA256}", b"{SHA384}", b"{SHA512}"]
|
|
|
|
# first try to hash with bad parameters
|
|
with self.assertRaises(utils.LdapHashUserPassword.BadScheme):
|
|
utils.LdapHashUserPassword.hash(b"TOTO", self.password1)
|
|
for scheme in schemes_nosalt:
|
|
with self.assertRaises(utils.LdapHashUserPassword.BadScheme):
|
|
utils.LdapHashUserPassword.hash(scheme, self.password1, salt)
|
|
for scheme in schemes_salt:
|
|
with self.assertRaises(utils.LdapHashUserPassword.BadScheme):
|
|
utils.LdapHashUserPassword.hash(scheme, self.password1)
|
|
with self.assertRaises(utils.LdapHashUserPassword.BadSalt):
|
|
utils.LdapHashUserPassword.hash(b'{CRYPT}', self.password1, b"$truc$toto")
|
|
|
|
# then try to check hash with bad hashes
|
|
with self.assertRaises(utils.LdapHashUserPassword.BadHash):
|
|
utils.check_password("ldap", self.password1, b"TOTOssdsdsd", "utf8")
|
|
for scheme in schemes_salt:
|
|
# bad length
|
|
with self.assertRaises(utils.LdapHashUserPassword.BadHash):
|
|
utils.check_password("ldap", self.password1, scheme + b"dG90b3E8ZHNkcw==", "utf8")
|
|
# bad base64
|
|
with self.assertRaises(utils.LdapHashUserPassword.BadHash):
|
|
utils.check_password("ldap", self.password1, scheme + b"dG90b3E8ZHNkcw", "utf8")
|
|
|
|
def test_hex(self):
|
|
"""test all the hex_HASH method: the hashed password is a simple hash of the password"""
|
|
hashes = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]
|
|
hashed_password1 = []
|
|
for hash_scheme in hashes:
|
|
hashed_password1.append(
|
|
(
|
|
"hex_%s" % hash_scheme,
|
|
getattr(utils.hashlib, hash_scheme)(self.password1).hexdigest()
|
|
)
|
|
)
|
|
for (method, hp1) in hashed_password1:
|
|
self.assertTrue(utils.check_password(method, self.password1, hp1, "utf8"))
|
|
self.assertFalse(utils.check_password(method, self.password2, hp1, "utf8"))
|
|
|
|
def test_bad_method(self):
|
|
"""try to check password with a bad method, should raise a ValueError"""
|
|
with self.assertRaises(ValueError):
|
|
utils.check_password("test", self.password1, b"$truc$s$dsdsd", "utf8")
|
|
|
|
|
|
class UtilsTestCase(TestCase):
|
|
"""tests for some little utils functions"""
|
|
def test_import_attr(self):
|
|
"""
|
|
test the import_attr function. Feeded with a dotted path string, it should
|
|
import the dotted module and return that last componend of the dotted path
|
|
(function, class or variable)
|
|
"""
|
|
with self.assertRaises(ImportError):
|
|
utils.import_attr('toto.titi.tutu')
|
|
with self.assertRaises(AttributeError):
|
|
utils.import_attr('cas_server.utils.toto')
|
|
with self.assertRaises(ValueError):
|
|
utils.import_attr('toto')
|
|
if django.VERSION < (3, 2):
|
|
self.assertEqual(
|
|
utils.import_attr('cas_server.default_app_config'),
|
|
'cas_server.apps.CasAppConfig'
|
|
)
|
|
self.assertEqual(utils.import_attr(utils), utils)
|
|
|
|
def test_update_url(self):
|
|
"""
|
|
test the update_url function. Given an url with possible GET parameter and a dict
|
|
the function build a url with GET parameters updated by the dictionnary
|
|
"""
|
|
url1 = utils.update_url(u"https://www.example.com?toto=1", {u"tata": u"2"})
|
|
url2 = utils.update_url(b"https://www.example.com?toto=1", {b"tata": b"2"})
|
|
self.assertEqual(url1, u"https://www.example.com?tata=2&toto=1")
|
|
self.assertEqual(url2, u"https://www.example.com?tata=2&toto=1")
|
|
|
|
url3 = utils.update_url(u"https://www.example.com?toto=1", {u"toto": u"2"})
|
|
self.assertEqual(url3, u"https://www.example.com?toto=2")
|
|
|
|
def test_crypt_salt_is_valid(self):
|
|
"""test the function crypt_salt_is_valid who test if a crypt salt is valid"""
|
|
self.assertFalse(utils.crypt_salt_is_valid("")) # len 0
|
|
self.assertFalse(utils.crypt_salt_is_valid("a")) # len 1
|
|
self.assertFalse(utils.crypt_salt_is_valid("$$")) # start with $ followed by $
|
|
self.assertFalse(utils.crypt_salt_is_valid("$toto")) # start with $ but no secondary $
|
|
self.assertFalse(utils.crypt_salt_is_valid("$toto$toto")) # algorithm toto not known
|
|
|
|
def test_get_current_url(self):
|
|
"""test the function get_current_url"""
|
|
factory = RequestFactory()
|
|
request = factory.get('/truc/muche?test=1')
|
|
self.assertEqual(utils.get_current_url(request), 'http://testserver/truc/muche?test=1')
|
|
self.assertEqual(
|
|
utils.get_current_url(request, ignore_params={'test'}),
|
|
'http://testserver/truc/muche'
|
|
)
|
|
|
|
def test_get_tuple(self):
|
|
"""test the function get_tuple"""
|
|
test_tuple = (1, 2, 3)
|
|
for index, value in enumerate(test_tuple):
|
|
self.assertEqual(utils.get_tuple(test_tuple, index), value)
|
|
self.assertEqual(utils.get_tuple(test_tuple, 3), None)
|
|
self.assertEqual(utils.get_tuple(test_tuple, 3, 'toto'), 'toto')
|
|
self.assertEqual(utils.get_tuple(None, 3), None)
|
|
|
|
def test_last_version(self):
|
|
"""
|
|
test the function last_version. An internet connection is needed, if you do not have
|
|
one, this test will fail and you should ignore it.
|
|
"""
|
|
try:
|
|
# first check if pypi is available
|
|
utils.requests.get("https://pypi.org/simple/django-cas-server/")
|
|
except utils.requests.exceptions.RequestException:
|
|
warnings.warn(
|
|
(
|
|
"Pypi seems not available, perhaps you do not have internet access. "
|
|
"Consequently, the test cas_server.tests.test_utils.UtilsTestCase.test_last_"
|
|
"version is ignored"
|
|
),
|
|
RuntimeWarning
|
|
)
|
|
else:
|
|
version = utils.last_version()
|
|
self.assertIsInstance(version, six.text_type)
|
|
self.assertEqual(len(version.split('.')), 3)
|
|
|
|
# version is cached 24h so calling it a second time should return the save value
|
|
self.assertEqual(version, utils.last_version())
|
|
|
|
def test_dictfetchall(self):
|
|
"""test the function dictfetchall"""
|
|
with connection.cursor() as curs:
|
|
curs.execute("SELECT * FROM django_migrations")
|
|
results = utils.dictfetchall(curs)
|
|
self.assertIsInstance(results, list)
|
|
self.assertTrue(len(results) > 0)
|
|
for result in results:
|
|
self.assertIsInstance(result, dict)
|
|
self.assertIn('applied', result)
|
|
self.assertIsInstance(result['applied'], datetime.datetime)
|
|
|
|
def test_regexpr_validator(self):
|
|
"""test the function regexpr_validator"""
|
|
utils.regexpr_validator("^a$")
|
|
with self.assertRaises(utils.ValidationError):
|
|
utils.regexpr_validator("[")
|