Add and use HTTP_FORBIDDEN constant (#33839)

This commit is contained in:
springstan 2020-04-09 17:41:17 +02:00 committed by GitHub
parent 70ee9d7f26
commit bc26be3c11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 60 additions and 37 deletions

View file

@ -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.ban import log_invalid_auth
from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView 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.core import HomeAssistant, callback
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
@ -313,7 +313,7 @@ class TokenView(HomeAssistantView):
if not user.is_active: if not user.is_active:
return self.json( return self.json(
{"error": "access_denied", "error_description": "User is not active"}, {"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) refresh_token = await hass.auth.async_create_refresh_token(user, client_id)

View file

@ -8,6 +8,7 @@ from homeassistant.components.notify import PLATFORM_SCHEMA, BaseNotificationSer
from homeassistant.const import ( from homeassistant.const import (
CONF_ACCESS_TOKEN, CONF_ACCESS_TOKEN,
CONF_USERNAME, CONF_USERNAME,
HTTP_FORBIDDEN,
HTTP_INTERNAL_SERVER_ERROR, HTTP_INTERNAL_SERVER_ERROR,
) )
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -39,7 +40,7 @@ class FreeSMSNotificationService(BaseNotificationService):
_LOGGER.error("At least one parameter is missing") _LOGGER.error("At least one parameter is missing")
elif resp.status_code == 402: elif resp.status_code == 402:
_LOGGER.error("Too much SMS send in a few time") _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") _LOGGER.error("Wrong Username/Password")
elif resp.status_code == HTTP_INTERNAL_SERVER_ERROR: elif resp.status_code == HTTP_INTERNAL_SERVER_ERROR:
_LOGGER.error("Server error, try later") _LOGGER.error("Server error, try later")

View file

@ -9,7 +9,7 @@ import pymelcloud
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries 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 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) await self.async_set_unique_id(username)
self._abort_if_unique_id_configured({CONF_TOKEN: token}) self._abort_if_unique_id_configured({CONF_TOKEN: token})
return self.async_create_entry( 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( async def _create_client(
@ -40,7 +40,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Create client.""" """Create client."""
if password is None and token is None: if password is None and token is None:
raise ValueError( raise ValueError(
"Invalid internal state. Called without either password or token", "Invalid internal state. Called without either password or token"
) )
try: try:
@ -57,7 +57,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self.hass.helpers.aiohttp_client.async_get_clientsession(), self.hass.helpers.aiohttp_client.async_get_clientsession(),
) )
except ClientResponseError as err: 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="invalid_auth")
return self.async_abort(reason="cannot_connect") return self.async_abort(reason="cannot_connect")
except (asyncio.TimeoutError, ClientError): except (asyncio.TimeoutError, ClientError):

View file

@ -6,6 +6,7 @@ import voluptuous as vol
from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView from homeassistant.components.http.view import HomeAssistantView
from homeassistant.const import HTTP_FORBIDDEN
from homeassistant.core import callback from homeassistant.core import callback
from .const import ( from .const import (
@ -95,7 +96,7 @@ class UserOnboardingView(_BaseOnboardingView):
async with self._lock: async with self._lock:
if self._async_is_done(): 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) provider = _async_get_hass_provider(hass)
await provider.async_initialize() await provider.async_initialize()
@ -147,7 +148,9 @@ class CoreConfigOnboardingView(_BaseOnboardingView):
async with self._lock: async with self._lock:
if self._async_is_done(): 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) await self._async_mark_done(hass)
@ -173,7 +176,9 @@ class IntegrationOnboardingView(_BaseOnboardingView):
async with self._lock: async with self._lock:
if self._async_is_done(): 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) await self._async_mark_done(hass)

View file

@ -9,7 +9,7 @@ from pysmartapp.event import EVENT_TYPE_DEVICE
from pysmartthings import Attribute, Capability, SmartThings from pysmartthings import Attribute, Capability, SmartThings
from homeassistant.config_entries import ConfigEntry 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.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import ( 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 hass.data[DOMAIN][DATA_BROKERS][entry.entry_id] = broker
except ClientResponseError as ex: except ClientResponseError as ex:
if ex.status in (401, 403): if ex.status in (401, HTTP_FORBIDDEN):
_LOGGER.exception( _LOGGER.exception(
"Unable to setup configuration entry '%s' - please reconfigure the integration", "Unable to setup configuration entry '%s' - please reconfigure the integration",
entry.title, entry.title,
@ -182,7 +182,7 @@ async def async_get_entry_scenes(entry: ConfigEntry, api):
try: try:
return await api.scenes(location_id=entry.data[CONF_LOCATION_ID]) return await api.scenes(location_id=entry.data[CONF_LOCATION_ID])
except ClientResponseError as ex: except ClientResponseError as ex:
if ex.status == 403: if ex.status == HTTP_FORBIDDEN:
_LOGGER.exception( _LOGGER.exception(
"Unable to load scenes for configuration entry '%s' because the access token does not have the required access", "Unable to load scenes for configuration entry '%s' because the access token does not have the required access",
entry.title, entry.title,
@ -209,12 +209,12 @@ async def async_remove_entry(hass: HomeAssistantType, entry: ConfigEntry) -> Non
"""Perform clean-up when entry is being removed.""" """Perform clean-up when entry is being removed."""
api = SmartThings(async_get_clientsession(hass), entry.data[CONF_ACCESS_TOKEN]) 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] installed_app_id = entry.data[CONF_INSTALLED_APP_ID]
try: try:
await api.delete_installed_app(installed_app_id) await api.delete_installed_app(installed_app_id)
except ClientResponseError as ex: except ClientResponseError as ex:
if ex.status == 403: if ex.status == HTTP_FORBIDDEN:
_LOGGER.debug( _LOGGER.debug(
"Installed app %s has already been removed", "Installed app %s has already been removed",
installed_app_id, 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) _LOGGER.debug("Removed installed app %s", installed_app_id)
# Remove the app if not referenced by other entries, which if already # 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) all_entries = hass.config_entries.async_entries(DOMAIN)
app_id = entry.data[CONF_APP_ID] app_id = entry.data[CONF_APP_ID]
app_count = sum(1 for entry in all_entries if entry.data[CONF_APP_ID] == 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: try:
await api.delete_app(app_id) await api.delete_app(app_id)
except ClientResponseError as ex: 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) _LOGGER.debug("App %s has already been removed", app_id, exc_info=True)
else: else:
raise raise

View file

@ -6,7 +6,7 @@ from pysmartthings import APIResponseError, AppOAuth, SmartThings
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries 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 homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import ( from .const import (
@ -122,7 +122,7 @@ class SmartThingsFlowHandler(config_entries.ConfigFlow):
except ClientResponseError as ex: except ClientResponseError as ex:
if ex.status == 401: if ex.status == 401:
errors[CONF_ACCESS_TOKEN] = "token_unauthorized" errors[CONF_ACCESS_TOKEN] = "token_unauthorized"
elif ex.status == 403: elif ex.status == HTTP_FORBIDDEN:
errors[CONF_ACCESS_TOKEN] = "token_forbidden" errors[CONF_ACCESS_TOKEN] = "token_forbidden"
else: else:
errors["base"] = "app_setup_error" errors["base"] = "app_setup_error"

View file

@ -527,6 +527,7 @@ HTTP_CREATED = 201
HTTP_MOVED_PERMANENTLY = 301 HTTP_MOVED_PERMANENTLY = 301
HTTP_BAD_REQUEST = 400 HTTP_BAD_REQUEST = 400
HTTP_UNAUTHORIZED = 401 HTTP_UNAUTHORIZED = 401
HTTP_FORBIDDEN = 403
HTTP_NOT_FOUND = 404 HTTP_NOT_FOUND = 404
HTTP_METHOD_NOT_ALLOWED = 405 HTTP_METHOD_NOT_ALLOWED = 405
HTTP_UNPROCESSABLE_ENTITY = 422 HTTP_UNPROCESSABLE_ENTITY = 422

View file

@ -7,7 +7,13 @@ from asynctest import patch
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components.airly.const import DOMAIN from homeassistant.components.airly.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER 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 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.""" """Test that errors are shown when API key is invalid."""
with patch( with patch(
"airly._private._RequestsHandler.get", "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( result = await hass.config_entries.flow.async_init(

View file

@ -12,6 +12,7 @@ from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_PARTS_PER_MILLION,
HTTP_FORBIDDEN,
HTTP_INTERNAL_SERVER_ERROR, HTTP_INTERNAL_SERVER_ERROR,
TEMP_CELSIUS, TEMP_CELSIUS,
UNIT_PERCENTAGE, UNIT_PERCENTAGE,
@ -71,7 +72,7 @@ async def test_setup_permanent_error(hass, aioclient_mock):
"""Expected failures caused by permanent errors in API response.""" """Expected failures caused by permanent errors in API response."""
fake_async_add_entities = MagicMock() fake_async_add_entities = MagicMock()
errors = [400, 401, 403] errors = [400, 401, HTTP_FORBIDDEN]
for error in errors: for error in errors:
aioclient_mock.get(re.compile("api.foobot.io/v2/owner/.*"), status=error) aioclient_mock.get(re.compile("api.foobot.io/v2/owner/.*"), status=error)
result = await foobot.async_setup_platform( result = await foobot.async_setup_platform(

View file

@ -20,6 +20,7 @@ from homeassistant.components.http.ban import (
setup_bans, setup_bans,
) )
from homeassistant.components.http.view import request_handler_factory from homeassistant.components.http.view import request_handler_factory
from homeassistant.const import HTTP_FORBIDDEN
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from . import mock_real_ip 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: for remote_addr in BANNED_IPS:
set_real_ip(remote_addr) set_real_ip(remote_addr)
resp = await client.get("/") resp = await client.get("/")
assert resp.status == 403 assert resp.status == HTTP_FORBIDDEN
@pytest.mark.parametrize( @pytest.mark.parametrize(
"remote_addr, bans, status", "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( async def test_access_from_supervisor_ip(
remote_addr, bans, status, hass, aiohttp_client, hassio_env 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) mock_real_ip(app)(remote_addr)
with patch( 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) 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") m_open.assert_called_once_with(hass.config.path(IP_BANS_FILE), "a")
resp = await client.get("/") resp = await client.get("/")
assert resp.status == 403 assert resp.status == HTTP_FORBIDDEN
assert m_open.call_count == 1 assert m_open.call_count == 1

View file

@ -8,7 +8,7 @@ import pytest
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.melcloud.const import DOMAIN 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 from tests.common import MockConfigEntry
@ -92,7 +92,7 @@ async def test_form_errors(hass, mock_login, mock_get_devices, error, reason):
"error,message", "error,message",
[ [
(401, "invalid_auth"), (401, "invalid_auth"),
(403, "invalid_auth"), (HTTP_FORBIDDEN, "invalid_auth"),
(HTTP_INTERNAL_SERVER_ERROR, "cannot_connect"), (HTTP_INTERNAL_SERVER_ERROR, "cannot_connect"),
], ],
) )

View file

@ -6,6 +6,7 @@ import pytest
from homeassistant.components import onboarding from homeassistant.components import onboarding
from homeassistant.components.onboarding import const, views from homeassistant.components.onboarding import const, views
from homeassistant.const import HTTP_FORBIDDEN
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from . import mock_storage 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): 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) 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): async def test_onboarding_integration(hass, hass_storage, hass_client):

View file

@ -15,7 +15,7 @@ from homeassistant.components.smartthings.const import (
CONF_REFRESH_TOKEN, CONF_REFRESH_TOKEN,
DOMAIN, 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 homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry, mock_coro 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") request_info = Mock(real_url="http://example.com")
smartthings_mock.apps.side_effect = ClientResponseError( 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())}) result = await flow.async_step_user({"access_token": str(uuid4())})

View file

@ -17,7 +17,7 @@ from homeassistant.components.smartthings.const import (
SIGNAL_SMARTTHINGS_UPDATE, SIGNAL_SMARTTHINGS_UPDATE,
SUPPORTED_PLATFORMS, 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.exceptions import ConfigEntryNotReady
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.setup import async_setup_component 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.installed_app.return_value = installed_app
smartthings_mock.devices.return_value = [device] smartthings_mock.devices.return_value = [device]
smartthings_mock.scenes.side_effect = ClientResponseError( 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 = Mock()
mock_token.access_token = str(uuid4()) 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") request_info = Mock(real_url="http://example.com")
# Arrange # Arrange
smartthings_mock.delete_installed_app.side_effect = ClientResponseError( 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( 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 # Act
await smartthings.async_remove_entry(hass, config_entry) await smartthings.async_remove_entry(hass, config_entry)

View file

@ -8,6 +8,7 @@ from homeassistant.components.media_player.const import (
SERVICE_PLAY_MEDIA, SERVICE_PLAY_MEDIA,
) )
import homeassistant.components.tts as tts import homeassistant.components.tts as tts
from homeassistant.const import HTTP_FORBIDDEN
from homeassistant.setup import setup_component from homeassistant.setup import setup_component
from tests.common import assert_setup_component, get_test_home_assistant, mock_service from tests.common import assert_setup_component, get_test_home_assistant, mock_service
@ -198,7 +199,7 @@ class TestTTSYandexPlatform:
"speed": 1, "speed": 1,
} }
aioclient_mock.get( 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"}} config = {tts.DOMAIN: {"platform": "yandextts", "api_key": "1234567xx"}}