"""Tests for HTTP session.""" from collections.abc import Callable import logging from typing import Any from unittest.mock import patch from aiohttp import web from aiohttp.test_utils import make_mocked_request import pytest from homeassistant.auth.session import SESSION_ID from homeassistant.components.http.session import ( COOKIE_NAME, HomeAssistantCookieStorage, ) from homeassistant.core import HomeAssistant def fake_request_with_strict_connection_cookie(cookie_value: str) -> web.Request: """Return a fake request with a strict connection cookie.""" request = make_mocked_request( "GET", "/", headers={"Cookie": f"{COOKIE_NAME}={cookie_value}"} ) assert COOKIE_NAME in request.cookies return request @pytest.fixture def cookie_storage(hass: HomeAssistant) -> HomeAssistantCookieStorage: """Fixture for the cookie storage.""" return HomeAssistantCookieStorage(hass) def _encrypt_cookie_data(cookie_storage: HomeAssistantCookieStorage, data: Any) -> str: """Encrypt cookie data.""" cookie_data = cookie_storage._encoder(data).encode("utf-8") return cookie_storage._fernet.encrypt(cookie_data).decode("utf-8") @pytest.mark.parametrize( "func", [ lambda _: "invalid", lambda storage: _encrypt_cookie_data(storage, "bla"), lambda storage: _encrypt_cookie_data(storage, None), ], ) async def test_load_session_modified_cookies( cookie_storage: HomeAssistantCookieStorage, caplog: pytest.LogCaptureFixture, func: Callable[[HomeAssistantCookieStorage], str], ) -> None: """Test that on modified cookies the session is empty and the request will be logged for ban.""" request = fake_request_with_strict_connection_cookie(func(cookie_storage)) with patch( "homeassistant.components.http.session.process_wrong_login", ) as mock_process_wrong_login: session = await cookie_storage.load_session(request) assert session.empty assert ( "homeassistant.components.http.session", logging.WARNING, "Cannot decrypt/parse cookie value", ) in caplog.record_tuples mock_process_wrong_login.assert_called() async def test_load_session_validate_session( hass: HomeAssistant, cookie_storage: HomeAssistantCookieStorage, ) -> None: """Test load session validates the session.""" session = await cookie_storage.new_session() session[SESSION_ID] = "bla" request = fake_request_with_strict_connection_cookie( _encrypt_cookie_data(cookie_storage, cookie_storage._get_session_data(session)) ) with patch.object( hass.auth.session, "async_validate_strict_connection_session", return_value=True ) as mock_validate: session = await cookie_storage.load_session(request) assert not session.empty assert session[SESSION_ID] == "bla" mock_validate.assert_called_with(session) # verify lru_cache is working mock_validate.reset_mock() await cookie_storage.load_session(request) mock_validate.assert_not_called() session = await cookie_storage.new_session() session[SESSION_ID] = "something" request = fake_request_with_strict_connection_cookie( _encrypt_cookie_data(cookie_storage, cookie_storage._get_session_data(session)) ) with patch.object( hass.auth.session, "async_validate_strict_connection_session", return_value=False, ): session = await cookie_storage.load_session(request) assert session.empty assert SESSION_ID not in session assert session._changed