diff --git a/aboutcode/api_auth/models.py b/aboutcode/api_auth/models.py index a3fefeaf..2160437e 100644 --- a/aboutcode/api_auth/models.py +++ b/aboutcode/api_auth/models.py @@ -57,16 +57,18 @@ def generate_key(cls): """Generate a plain (not encrypted) key.""" return secrets.token_hex(32) + def set_key(self, plain_key): + """Set the prefix and hashed key from a plain key. Does not save.""" + self.prefix = plain_key[: self.PREFIX_LENGTH] + self.key_hash = make_password(plain_key) + @classmethod def create_token(cls, user): """Generate a new token for the given user and return the plain key once.""" plain_key = cls.generate_key() - prefix = plain_key[: cls.PREFIX_LENGTH] - cls.objects.create( - user=user, - prefix=prefix, - key_hash=make_password(plain_key), - ) + token = cls(user=user) + token.set_key(plain_key) + token.save() return plain_key @classmethod diff --git a/dje/tests/test_api_auth.py b/dje/tests/test_api_auth.py index c3859e1d..ec4867de 100644 --- a/dje/tests/test_api_auth.py +++ b/dje/tests/test_api_auth.py @@ -6,6 +6,7 @@ # See https://aboutcode.org for more information about AboutCode FOSS projects. # +from django.contrib.auth.hashers import check_password from django.db.utils import IntegrityError from django.test import TestCase @@ -80,3 +81,16 @@ def test_api_auth_api_token_authentication_authenticate_credentials(self): self.base_user.save() with self.assertRaisesMessage(AuthenticationFailed, "User inactive or deleted."): api_token_auth.authenticate_credentials(plain_key=plain_key) + + def test_api_auth_api_token_model_set_key(self): + plain_key = APIToken.generate_key() + token = APIToken(user=self.base_user) + token.set_key(plain_key) + + self.assertEqual(plain_key[: APIToken.PREFIX_LENGTH], token.prefix) + self.assertEqual(59, len(token.key_hash)) + self.assertTrue(check_password(plain_key, token.key_hash)) + # set_key must not save the instance + self.assertEqual(0, APIToken.objects.count()) + token.save() + self.assertEqual(1, APIToken.objects.count())