import logging import typing import unittest import unittest.mock from stapler.page import Page from stapler.params import Parameters from stapler.token_manager import TokenManager from . import BaseTestCase class TestTokenManager(BaseTestCase): EMPTY_SALT_HASH = "5f88941ac5e26c430d97411ac1103af7a35c753f14aec088fbf34801c099135a" SALT_HASH = "d71b1f52657c77d00b2a8c59b8d12d13c1c1bb2bcfbb85d2a9b804c36ad57a70" SECRET_HASH = "38df428b309308e48c3687e7f90bda0e9cf253568c21ec754a0e076ab4ab6423" # noqa: S105 @typing.override def setUp(self) -> None: self.registry = self.new_mock() self.token_manager = TokenManager( Parameters(data_dir=self.get_tmp_dir(), token_salt="salt"), # noqa: S106 self.registry, pbkdf2_iterations=1, ) self.token_manager.logger = unittest.mock.Mock(logging.Logger) self.tmp_tokens_file = self.tmp_path / TokenManager.FILE super().setUp() def test_init_no_hashes(self) -> None: self.seal_mocks() self.token_manager.init() self.assert_file_content(self.tmp_tokens_file, self.SALT_HASH) self.assertEqual(self.tmp_tokens_file.stat().st_mode, 0o100600) self.assertListEqual(self.token_manager.token_hashes, []) def test_init_weak_salt(self) -> None: self.token_manager.token_salt = b"" self.seal_mocks() self.token_manager.init() self.assert_file_content( self.tmp_tokens_file, self.EMPTY_SALT_HASH, ) self.assertListEqual(self.token_manager.token_hashes, []) def test_init_load_hashes(self) -> None: with self.tmp_tokens_file.open(mode="w") as file: file.write(self.SALT_HASH + "\n" + self.SECRET_HASH) self.seal_mocks() self.token_manager.init() self.assertListEqual(self.token_manager.token_hashes, [self.SECRET_HASH]) def test_init_invalid_salt(self) -> None: with self.tmp_tokens_file.open(mode="w") as file: file.write(self.EMPTY_SALT_HASH + "\n" + self.SECRET_HASH) self.seal_mocks() self.token_manager.init() self.assertListEqual(self.token_manager.token_hashes, []) def test_is_valid(self) -> None: self.seal_mocks() self.token_manager.token_hashes = [self.SECRET_HASH] assert self.token_manager.is_valid("secret") def test_is_valid_fail(self) -> None: self.seal_mocks() assert not self.token_manager.is_valid("secret") def test_is_valid_for_path(self) -> None: with ( self.mock_call( self.registry.get_from_path, ["test_1"], Page("test_1", token_hash=self.SECRET_HASH), ), self.seal_mocks(), ): assert self.token_manager.is_valid_for_path("secret", "test_1") def test_is_valid_for_path_no_token(self) -> None: with ( self.mock_call( self.registry.get_from_path, ["test_1"], Page("test_1"), ), self.seal_mocks(), ): assert self.token_manager.is_valid_for_path("secret", "test_1") def test_is_valid_for_path_no_page(self) -> None: with ( self.mock_call( self.registry.get_from_path, ["test_1"], ), self.seal_mocks(), ): assert self.token_manager.is_valid_for_path("secret", "test_1") def test_is_valid_for_path_fail(self) -> None: with ( self.mock_call( self.registry.get_from_path, ["test_1"], Page("test_1", token_hash=self.SALT_HASH), ), self.seal_mocks(), ): assert not self.token_manager.is_valid_for_path("secret", "test_1") def test_set_token(self) -> None: with ( self.mock_call( self.registry.set_token_hash, ["test_1", self.SECRET_HASH], ), self.seal_mocks(), ): self.token_manager.set_token("test_1", "secret") def test_detect_file_change(self) -> None: self.tmp_tokens_file.touch() self.seal_mocks() assert self.token_manager.detect_file_change() def test_detect_file_change_nothing(self) -> None: self.tmp_tokens_file.touch() self.token_manager.last_file_change = self.tmp_tokens_file.stat().st_mtime self.seal_mocks() assert not self.token_manager.detect_file_change() @unittest.mock.patch("secrets.token_hex") def test_new_token(self, mock_token_hex: unittest.mock.Mock) -> None: mock_token_hex.return_value = "secret" self.seal_mocks() self.token_manager.new_token() self.assertListEqual(self.token_manager.token_hashes, [self.SECRET_HASH]) self.assert_file_content(self.tmp_tokens_file, self.SALT_HASH, self.SECRET_HASH)