diff --git a/homeassistant/components/auth/__init__.py b/homeassistant/components/auth/__init__.py index 6733361f5c3..4c540cab843 100644 --- a/homeassistant/components/auth/__init__.py +++ b/homeassistant/components/auth/__init__.py @@ -132,7 +132,7 @@ from homeassistant.components.http.auth import async_sign_path from homeassistant.components.http.ban import log_invalid_auth from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.view import HomeAssistantView -from homeassistant.const import HTTP_OK +from homeassistant.const import HTTP_FORBIDDEN, HTTP_OK from homeassistant.core import HomeAssistant, callback from homeassistant.loader import bind_hass from homeassistant.util import dt as dt_util @@ -313,7 +313,7 @@ class TokenView(HomeAssistantView): if not user.is_active: return self.json( {"error": "access_denied", "error_description": "User is not active"}, - status_code=403, + status_code=HTTP_FORBIDDEN, ) refresh_token = await hass.auth.async_create_refresh_token(user, client_id) diff --git a/homeassistant/components/free_mobile/notify.py b/homeassistant/components/free_mobile/notify.py index c28b56271b9..a2bee1f0037 100644 --- a/homeassistant/components/free_mobile/notify.py +++ b/homeassistant/components/free_mobile/notify.py @@ -8,6 +8,7 @@ from homeassistant.components.notify import PLATFORM_SCHEMA, BaseNotificationSer from homeassistant.const import ( CONF_ACCESS_TOKEN, CONF_USERNAME, + HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR, ) import homeassistant.helpers.config_validation as cv @@ -39,7 +40,7 @@ class FreeSMSNotificationService(BaseNotificationService): _LOGGER.error("At least one parameter is missing") elif resp.status_code == 402: _LOGGER.error("Too much SMS send in a few time") - elif resp.status_code == 403: + elif resp.status_code == HTTP_FORBIDDEN: _LOGGER.error("Wrong Username/Password") elif resp.status_code == HTTP_INTERNAL_SERVER_ERROR: _LOGGER.error("Server error, try later") diff --git a/homeassistant/components/melcloud/config_flow.py b/homeassistant/components/melcloud/config_flow.py index 6bda8cc3c28..ed6fc31c414 100644 --- a/homeassistant/components/melcloud/config_flow.py +++ b/homeassistant/components/melcloud/config_flow.py @@ -9,7 +9,7 @@ import pymelcloud import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME +from homeassistant.const import CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME, HTTP_FORBIDDEN from .const import DOMAIN # pylint: disable=unused-import @@ -27,7 +27,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(username) self._abort_if_unique_id_configured({CONF_TOKEN: token}) return self.async_create_entry( - title=username, data={CONF_USERNAME: username, CONF_TOKEN: token}, + title=username, data={CONF_USERNAME: username, CONF_TOKEN: token} ) async def _create_client( @@ -40,7 +40,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Create client.""" if password is None and token is None: raise ValueError( - "Invalid internal state. Called without either password or token", + "Invalid internal state. Called without either password or token" ) try: @@ -57,7 +57,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self.hass.helpers.aiohttp_client.async_get_clientsession(), ) except ClientResponseError as err: - if err.status == 401 or err.status == 403: + if err.status == 401 or err.status == HTTP_FORBIDDEN: return self.async_abort(reason="invalid_auth") return self.async_abort(reason="cannot_connect") except (asyncio.TimeoutError, ClientError): diff --git a/homeassistant/components/onboarding/views.py b/homeassistant/components/onboarding/views.py index fa859861fb7..32a71ce14e4 100644 --- a/homeassistant/components/onboarding/views.py +++ b/homeassistant/components/onboarding/views.py @@ -6,6 +6,7 @@ import voluptuous as vol from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.view import HomeAssistantView +from homeassistant.const import HTTP_FORBIDDEN from homeassistant.core import callback from .const import ( @@ -95,7 +96,7 @@ class UserOnboardingView(_BaseOnboardingView): async with self._lock: if self._async_is_done(): - return self.json_message("User step already done", 403) + return self.json_message("User step already done", HTTP_FORBIDDEN) provider = _async_get_hass_provider(hass) await provider.async_initialize() @@ -147,7 +148,9 @@ class CoreConfigOnboardingView(_BaseOnboardingView): async with self._lock: if self._async_is_done(): - return self.json_message("Core config step already done", 403) + return self.json_message( + "Core config step already done", HTTP_FORBIDDEN + ) await self._async_mark_done(hass) @@ -173,7 +176,9 @@ class IntegrationOnboardingView(_BaseOnboardingView): async with self._lock: if self._async_is_done(): - return self.json_message("Integration step already done", 403) + return self.json_message( + "Integration step already done", HTTP_FORBIDDEN + ) await self._async_mark_done(hass) diff --git a/homeassistant/components/smartthings/__init__.py b/homeassistant/components/smartthings/__init__.py index a1ea4f98c85..97a7d32a9c1 100644 --- a/homeassistant/components/smartthings/__init__.py +++ b/homeassistant/components/smartthings/__init__.py @@ -9,7 +9,7 @@ from pysmartapp.event import EVENT_TYPE_DEVICE from pysmartthings import Attribute, Capability, SmartThings from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.const import CONF_ACCESS_TOKEN, HTTP_FORBIDDEN from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.dispatcher import ( @@ -145,7 +145,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): hass.data[DOMAIN][DATA_BROKERS][entry.entry_id] = broker except ClientResponseError as ex: - if ex.status in (401, 403): + if ex.status in (401, HTTP_FORBIDDEN): _LOGGER.exception( "Unable to setup configuration entry '%s' - please reconfigure the integration", entry.title, @@ -182,7 +182,7 @@ async def async_get_entry_scenes(entry: ConfigEntry, api): try: return await api.scenes(location_id=entry.data[CONF_LOCATION_ID]) except ClientResponseError as ex: - if ex.status == 403: + if ex.status == HTTP_FORBIDDEN: _LOGGER.exception( "Unable to load scenes for configuration entry '%s' because the access token does not have the required access", entry.title, @@ -209,12 +209,12 @@ async def async_remove_entry(hass: HomeAssistantType, entry: ConfigEntry) -> Non """Perform clean-up when entry is being removed.""" api = SmartThings(async_get_clientsession(hass), entry.data[CONF_ACCESS_TOKEN]) - # Remove the installed_app, which if already removed raises a 403 error. + # Remove the installed_app, which if already removed raises a HTTP_FORBIDDEN error. installed_app_id = entry.data[CONF_INSTALLED_APP_ID] try: await api.delete_installed_app(installed_app_id) except ClientResponseError as ex: - if ex.status == 403: + if ex.status == HTTP_FORBIDDEN: _LOGGER.debug( "Installed app %s has already been removed", installed_app_id, @@ -225,7 +225,7 @@ async def async_remove_entry(hass: HomeAssistantType, entry: ConfigEntry) -> Non _LOGGER.debug("Removed installed app %s", installed_app_id) # Remove the app if not referenced by other entries, which if already - # removed raises a 403 error. + # removed raises a HTTP_FORBIDDEN error. all_entries = hass.config_entries.async_entries(DOMAIN) app_id = entry.data[CONF_APP_ID] app_count = sum(1 for entry in all_entries if entry.data[CONF_APP_ID] == app_id) @@ -239,7 +239,7 @@ async def async_remove_entry(hass: HomeAssistantType, entry: ConfigEntry) -> Non try: await api.delete_app(app_id) except ClientResponseError as ex: - if ex.status == 403: + if ex.status == HTTP_FORBIDDEN: _LOGGER.debug("App %s has already been removed", app_id, exc_info=True) else: raise diff --git a/homeassistant/components/smartthings/config_flow.py b/homeassistant/components/smartthings/config_flow.py index 54c9f815008..249635f9b2a 100644 --- a/homeassistant/components/smartthings/config_flow.py +++ b/homeassistant/components/smartthings/config_flow.py @@ -6,7 +6,7 @@ from pysmartthings import APIResponseError, AppOAuth, SmartThings import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.const import CONF_ACCESS_TOKEN, HTTP_FORBIDDEN from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( @@ -122,7 +122,7 @@ class SmartThingsFlowHandler(config_entries.ConfigFlow): except ClientResponseError as ex: if ex.status == 401: errors[CONF_ACCESS_TOKEN] = "token_unauthorized" - elif ex.status == 403: + elif ex.status == HTTP_FORBIDDEN: errors[CONF_ACCESS_TOKEN] = "token_forbidden" else: errors["base"] = "app_setup_error" diff --git a/homeassistant/const.py b/homeassistant/const.py index b0176d1c004..42b944e9fb3 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -527,6 +527,7 @@ HTTP_CREATED = 201 HTTP_MOVED_PERMANENTLY = 301 HTTP_BAD_REQUEST = 400 HTTP_UNAUTHORIZED = 401 +HTTP_FORBIDDEN = 403 HTTP_NOT_FOUND = 404 HTTP_METHOD_NOT_ALLOWED = 405 HTTP_UNPROCESSABLE_ENTITY = 422 diff --git a/tests/components/airly/test_config_flow.py b/tests/components/airly/test_config_flow.py index 1f14e96ed37..83e7d5d210e 100644 --- a/tests/components/airly/test_config_flow.py +++ b/tests/components/airly/test_config_flow.py @@ -7,7 +7,13 @@ from asynctest import patch from homeassistant import data_entry_flow from homeassistant.components.airly.const import DOMAIN from homeassistant.config_entries import SOURCE_USER -from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME +from homeassistant.const import ( + CONF_API_KEY, + CONF_LATITUDE, + CONF_LONGITUDE, + CONF_NAME, + HTTP_FORBIDDEN, +) from tests.common import MockConfigEntry, load_fixture @@ -33,7 +39,9 @@ async def test_invalid_api_key(hass): """Test that errors are shown when API key is invalid.""" with patch( "airly._private._RequestsHandler.get", - side_effect=AirlyError(403, {"message": "Invalid authentication credentials"}), + side_effect=AirlyError( + HTTP_FORBIDDEN, {"message": "Invalid authentication credentials"} + ), ): result = await hass.config_entries.flow.async_init( diff --git a/tests/components/foobot/test_sensor.py b/tests/components/foobot/test_sensor.py index 10cc3eb47b6..37a0310d163 100644 --- a/tests/components/foobot/test_sensor.py +++ b/tests/components/foobot/test_sensor.py @@ -12,6 +12,7 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, + HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR, TEMP_CELSIUS, UNIT_PERCENTAGE, @@ -71,7 +72,7 @@ async def test_setup_permanent_error(hass, aioclient_mock): """Expected failures caused by permanent errors in API response.""" fake_async_add_entities = MagicMock() - errors = [400, 401, 403] + errors = [400, 401, HTTP_FORBIDDEN] for error in errors: aioclient_mock.get(re.compile("api.foobot.io/v2/owner/.*"), status=error) result = await foobot.async_setup_platform( diff --git a/tests/components/http/test_ban.py b/tests/components/http/test_ban.py index 28be5cc45c3..ddf08de42b4 100644 --- a/tests/components/http/test_ban.py +++ b/tests/components/http/test_ban.py @@ -20,6 +20,7 @@ from homeassistant.components.http.ban import ( setup_bans, ) from homeassistant.components.http.view import request_handler_factory +from homeassistant.const import HTTP_FORBIDDEN from homeassistant.setup import async_setup_component from . import mock_real_ip @@ -55,12 +56,16 @@ async def test_access_from_banned_ip(hass, aiohttp_client): for remote_addr in BANNED_IPS: set_real_ip(remote_addr) resp = await client.get("/") - assert resp.status == 403 + assert resp.status == HTTP_FORBIDDEN @pytest.mark.parametrize( "remote_addr, bans, status", - list(zip(BANNED_IPS_WITH_SUPERVISOR, [1, 1, 0], [403, 403, 401])), + list( + zip( + BANNED_IPS_WITH_SUPERVISOR, [1, 1, 0], [HTTP_FORBIDDEN, HTTP_FORBIDDEN, 401] + ) + ), ) async def test_access_from_supervisor_ip( remote_addr, bans, status, hass, aiohttp_client, hassio_env @@ -78,7 +83,7 @@ async def test_access_from_supervisor_ip( mock_real_ip(app)(remote_addr) with patch( - "homeassistant.components.http.ban.async_load_ip_bans_config", return_value=[], + "homeassistant.components.http.ban.async_load_ip_bans_config", return_value=[] ): client = await aiohttp_client(app) @@ -151,7 +156,7 @@ async def test_ip_bans_file_creation(hass, aiohttp_client): m_open.assert_called_once_with(hass.config.path(IP_BANS_FILE), "a") resp = await client.get("/") - assert resp.status == 403 + assert resp.status == HTTP_FORBIDDEN assert m_open.call_count == 1 diff --git a/tests/components/melcloud/test_config_flow.py b/tests/components/melcloud/test_config_flow.py index 4cd64afa053..c936807484a 100644 --- a/tests/components/melcloud/test_config_flow.py +++ b/tests/components/melcloud/test_config_flow.py @@ -8,7 +8,7 @@ import pytest from homeassistant import config_entries from homeassistant.components.melcloud.const import DOMAIN -from homeassistant.const import HTTP_INTERNAL_SERVER_ERROR +from homeassistant.const import HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR from tests.common import MockConfigEntry @@ -92,7 +92,7 @@ async def test_form_errors(hass, mock_login, mock_get_devices, error, reason): "error,message", [ (401, "invalid_auth"), - (403, "invalid_auth"), + (HTTP_FORBIDDEN, "invalid_auth"), (HTTP_INTERNAL_SERVER_ERROR, "cannot_connect"), ], ) diff --git a/tests/components/onboarding/test_views.py b/tests/components/onboarding/test_views.py index c7c9782e9a8..91ed8d7ae5c 100644 --- a/tests/components/onboarding/test_views.py +++ b/tests/components/onboarding/test_views.py @@ -6,6 +6,7 @@ import pytest from homeassistant.components import onboarding from homeassistant.components.onboarding import const, views +from homeassistant.const import HTTP_FORBIDDEN from homeassistant.setup import async_setup_component from . import mock_storage @@ -65,7 +66,7 @@ async def test_onboarding_user_already_done(hass, hass_storage, aiohttp_client): }, ) - assert resp.status == 403 + assert resp.status == HTTP_FORBIDDEN async def test_onboarding_user(hass, hass_storage, aiohttp_client): @@ -179,7 +180,7 @@ async def test_onboarding_user_race(hass, hass_storage, aiohttp_client): res1, res2 = await asyncio.gather(resp1, resp2) - assert sorted([res1.status, res2.status]) == [200, 403] + assert sorted([res1.status, res2.status]) == [200, HTTP_FORBIDDEN] async def test_onboarding_integration(hass, hass_storage, hass_client): diff --git a/tests/components/smartthings/test_config_flow.py b/tests/components/smartthings/test_config_flow.py index 0d533910b54..cd43659fccb 100644 --- a/tests/components/smartthings/test_config_flow.py +++ b/tests/components/smartthings/test_config_flow.py @@ -15,7 +15,7 @@ from homeassistant.components.smartthings.const import ( CONF_REFRESH_TOKEN, DOMAIN, ) -from homeassistant.const import HTTP_NOT_FOUND +from homeassistant.const import HTTP_FORBIDDEN, HTTP_NOT_FOUND from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry, mock_coro @@ -103,7 +103,7 @@ async def test_token_forbidden(hass, smartthings_mock): request_info = Mock(real_url="http://example.com") smartthings_mock.apps.side_effect = ClientResponseError( - request_info=request_info, history=None, status=403 + request_info=request_info, history=None, status=HTTP_FORBIDDEN ) result = await flow.async_step_user({"access_token": str(uuid4())}) diff --git a/tests/components/smartthings/test_init.py b/tests/components/smartthings/test_init.py index 4093f3753f1..78142ae3fc4 100644 --- a/tests/components/smartthings/test_init.py +++ b/tests/components/smartthings/test_init.py @@ -17,7 +17,7 @@ from homeassistant.components.smartthings.const import ( SIGNAL_SMARTTHINGS_UPDATE, SUPPORTED_PLATFORMS, ) -from homeassistant.const import HTTP_INTERNAL_SERVER_ERROR +from homeassistant.const import HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.setup import async_setup_component @@ -155,7 +155,7 @@ async def test_scenes_unauthorized_loads_platforms( smartthings_mock.installed_app.return_value = installed_app smartthings_mock.devices.return_value = [device] smartthings_mock.scenes.side_effect = ClientResponseError( - request_info=request_info, history=None, status=403 + request_info=request_info, history=None, status=HTTP_FORBIDDEN ) mock_token = Mock() mock_token.access_token = str(uuid4()) @@ -307,10 +307,10 @@ async def test_remove_entry_already_deleted(hass, config_entry, smartthings_mock request_info = Mock(real_url="http://example.com") # Arrange smartthings_mock.delete_installed_app.side_effect = ClientResponseError( - request_info=request_info, history=None, status=403 + request_info=request_info, history=None, status=HTTP_FORBIDDEN ) smartthings_mock.delete_app.side_effect = ClientResponseError( - request_info=request_info, history=None, status=403 + request_info=request_info, history=None, status=HTTP_FORBIDDEN ) # Act await smartthings.async_remove_entry(hass, config_entry) diff --git a/tests/components/yandextts/test_tts.py b/tests/components/yandextts/test_tts.py index 279ed1fbd01..b1bf1bc8ab5 100644 --- a/tests/components/yandextts/test_tts.py +++ b/tests/components/yandextts/test_tts.py @@ -8,6 +8,7 @@ from homeassistant.components.media_player.const import ( SERVICE_PLAY_MEDIA, ) import homeassistant.components.tts as tts +from homeassistant.const import HTTP_FORBIDDEN from homeassistant.setup import setup_component from tests.common import assert_setup_component, get_test_home_assistant, mock_service @@ -198,7 +199,7 @@ class TestTTSYandexPlatform: "speed": 1, } aioclient_mock.get( - self._base_url, status=403, content=b"test", params=url_param + self._base_url, status=HTTP_FORBIDDEN, content=b"test", params=url_param ) config = {tts.DOMAIN: {"platform": "yandextts", "api_key": "1234567xx"}}