Remove direct authentication via trusted networks or API password (#27656)
* Remove direct authentication via trusted networks and API password * Fix tests
This commit is contained in:
parent
97478d1ef4
commit
3231e22ddf
33 changed files with 114 additions and 423 deletions
|
@ -86,18 +86,6 @@ class AuthManager:
|
|||
hass, self._async_create_login_flow, self._async_finish_login_flow
|
||||
)
|
||||
|
||||
@property
|
||||
def support_legacy(self) -> bool:
|
||||
"""
|
||||
Return if legacy_api_password auth providers are registered.
|
||||
|
||||
Should be removed when we removed legacy_api_password auth providers.
|
||||
"""
|
||||
for provider_type, _ in self._providers:
|
||||
if provider_type == "legacy_api_password":
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def auth_providers(self) -> List[AuthProvider]:
|
||||
"""Return a list of available auth providers."""
|
||||
|
|
|
@ -64,13 +64,9 @@ async def async_from_config_dict(
|
|||
)
|
||||
|
||||
core_config = config.get(core.DOMAIN, {})
|
||||
api_password = config.get("http", {}).get("api_password")
|
||||
trusted_networks = config.get("http", {}).get("trusted_networks")
|
||||
|
||||
try:
|
||||
await conf_util.async_process_ha_core_config(
|
||||
hass, core_config, api_password, trusted_networks
|
||||
)
|
||||
await conf_util.async_process_ha_core_config(hass, core_config)
|
||||
except vol.Invalid as config_err:
|
||||
conf_util.async_log_exception(config_err, "homeassistant", core_config, hass)
|
||||
return None
|
||||
|
|
|
@ -72,7 +72,11 @@ import voluptuous_serialize
|
|||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.http import KEY_REAL_IP
|
||||
from homeassistant.components.http.ban import process_wrong_login, log_invalid_auth
|
||||
from homeassistant.components.http.ban import (
|
||||
process_wrong_login,
|
||||
process_success_login,
|
||||
log_invalid_auth,
|
||||
)
|
||||
from homeassistant.components.http.data_validator import RequestDataValidator
|
||||
from homeassistant.components.http.view import HomeAssistantView
|
||||
from . import indieauth
|
||||
|
@ -185,6 +189,7 @@ class LoginFlowIndexView(HomeAssistantView):
|
|||
return self.json_message("Handler does not support init", 400)
|
||||
|
||||
if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
|
||||
await process_success_login(request)
|
||||
result.pop("data")
|
||||
result["result"] = self._store_result(data["client_id"], result["result"])
|
||||
return self.json(result)
|
||||
|
|
|
@ -17,7 +17,6 @@ from homeassistant.const import (
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.util as hass_util
|
||||
from homeassistant.util import ssl as ssl_util
|
||||
from homeassistant.util.logging import HideSensitiveDataFilter
|
||||
|
||||
from .auth import setup_auth
|
||||
from .ban import setup_bans
|
||||
|
@ -32,7 +31,6 @@ from .view import HomeAssistantView # noqa
|
|||
|
||||
DOMAIN = "http"
|
||||
|
||||
CONF_API_PASSWORD = "api_password"
|
||||
CONF_SERVER_HOST = "server_host"
|
||||
CONF_SERVER_PORT = "server_port"
|
||||
CONF_BASE_URL = "base_url"
|
||||
|
@ -42,7 +40,6 @@ CONF_SSL_KEY = "ssl_key"
|
|||
CONF_CORS_ORIGINS = "cors_allowed_origins"
|
||||
CONF_USE_X_FORWARDED_FOR = "use_x_forwarded_for"
|
||||
CONF_TRUSTED_PROXIES = "trusted_proxies"
|
||||
CONF_TRUSTED_NETWORKS = "trusted_networks"
|
||||
CONF_LOGIN_ATTEMPTS_THRESHOLD = "login_attempts_threshold"
|
||||
CONF_IP_BAN_ENABLED = "ip_ban_enabled"
|
||||
CONF_SSL_PROFILE = "ssl_profile"
|
||||
|
@ -59,37 +56,8 @@ DEFAULT_CORS = "https://cast.home-assistant.io"
|
|||
NO_LOGIN_ATTEMPT_THRESHOLD = -1
|
||||
|
||||
|
||||
def trusted_networks_deprecated(value):
|
||||
"""Warn user trusted_networks config is deprecated."""
|
||||
if not value:
|
||||
return value
|
||||
|
||||
_LOGGER.warning(
|
||||
"Configuring trusted_networks via the http integration has been"
|
||||
" deprecated. Use the trusted networks auth provider instead."
|
||||
" For instructions, see https://www.home-assistant.io/docs/"
|
||||
"authentication/providers/#trusted-networks"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def api_password_deprecated(value):
|
||||
"""Warn user api_password config is deprecated."""
|
||||
if not value:
|
||||
return value
|
||||
|
||||
_LOGGER.warning(
|
||||
"Configuring api_password via the http integration has been"
|
||||
" deprecated. Use the legacy api password auth provider instead."
|
||||
" For instructions, see https://www.home-assistant.io/docs/"
|
||||
"authentication/providers/#legacy-api-password"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
HTTP_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_API_PASSWORD): vol.All(cv.string, api_password_deprecated),
|
||||
vol.Optional(CONF_SERVER_HOST, default=DEFAULT_SERVER_HOST): cv.string,
|
||||
vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port,
|
||||
vol.Optional(CONF_BASE_URL): cv.string,
|
||||
|
@ -103,9 +71,6 @@ HTTP_SCHEMA = vol.Schema(
|
|||
vol.Inclusive(CONF_TRUSTED_PROXIES, "proxy"): vol.All(
|
||||
cv.ensure_list, [ip_network]
|
||||
),
|
||||
vol.Optional(CONF_TRUSTED_NETWORKS, default=[]): vol.All(
|
||||
cv.ensure_list, [ip_network], trusted_networks_deprecated
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_LOGIN_ATTEMPTS_THRESHOLD, default=NO_LOGIN_ATTEMPT_THRESHOLD
|
||||
): vol.Any(cv.positive_int, NO_LOGIN_ATTEMPT_THRESHOLD),
|
||||
|
@ -149,7 +114,6 @@ async def async_setup(hass, config):
|
|||
if conf is None:
|
||||
conf = HTTP_SCHEMA({})
|
||||
|
||||
api_password = conf.get(CONF_API_PASSWORD)
|
||||
server_host = conf[CONF_SERVER_HOST]
|
||||
server_port = conf[CONF_SERVER_PORT]
|
||||
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
|
||||
|
@ -162,11 +126,6 @@ async def async_setup(hass, config):
|
|||
login_threshold = conf[CONF_LOGIN_ATTEMPTS_THRESHOLD]
|
||||
ssl_profile = conf[CONF_SSL_PROFILE]
|
||||
|
||||
if api_password is not None:
|
||||
logging.getLogger("aiohttp.access").addFilter(
|
||||
HideSensitiveDataFilter(api_password)
|
||||
)
|
||||
|
||||
server = HomeAssistantHTTP(
|
||||
hass,
|
||||
server_host=server_host,
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
"""Authentication for HTTP component."""
|
||||
import base64
|
||||
import logging
|
||||
|
||||
from aiohttp import hdrs
|
||||
from aiohttp.web import middleware
|
||||
import jwt
|
||||
|
||||
from homeassistant.auth.providers import legacy_api_password
|
||||
from homeassistant.auth.util import generate_secret
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
|
@ -52,16 +49,6 @@ def async_sign_path(hass, refresh_token_id, path, expiration):
|
|||
@callback
|
||||
def setup_auth(hass, app):
|
||||
"""Create auth middleware for the app."""
|
||||
old_auth_warning = set()
|
||||
|
||||
support_legacy = hass.auth.support_legacy
|
||||
if support_legacy:
|
||||
_LOGGER.warning("legacy_api_password support has been enabled.")
|
||||
|
||||
trusted_networks = []
|
||||
for prv in hass.auth.auth_providers:
|
||||
if prv.type == "trusted_networks":
|
||||
trusted_networks += prv.trusted_networks
|
||||
|
||||
async def async_validate_auth_header(request):
|
||||
"""
|
||||
|
@ -75,40 +62,16 @@ def setup_auth(hass, app):
|
|||
# If no space in authorization header
|
||||
return False
|
||||
|
||||
if auth_type == "Bearer":
|
||||
refresh_token = await hass.auth.async_validate_access_token(auth_val)
|
||||
if refresh_token is None:
|
||||
return False
|
||||
if auth_type != "Bearer":
|
||||
return False
|
||||
|
||||
request[KEY_HASS_USER] = refresh_token.user
|
||||
return True
|
||||
refresh_token = await hass.auth.async_validate_access_token(auth_val)
|
||||
|
||||
if auth_type == "Basic" and support_legacy:
|
||||
decoded = base64.b64decode(auth_val).decode("utf-8")
|
||||
try:
|
||||
username, password = decoded.split(":", 1)
|
||||
except ValueError:
|
||||
# If no ':' in decoded
|
||||
return False
|
||||
if refresh_token is None:
|
||||
return False
|
||||
|
||||
if username != "homeassistant":
|
||||
return False
|
||||
|
||||
user = await legacy_api_password.async_validate_password(hass, password)
|
||||
if user is None:
|
||||
return False
|
||||
|
||||
request[KEY_HASS_USER] = user
|
||||
_LOGGER.info(
|
||||
"Basic auth with api_password is going to deprecate,"
|
||||
" please use a bearer token to access %s from %s",
|
||||
request.path,
|
||||
request[KEY_REAL_IP],
|
||||
)
|
||||
old_auth_warning.add(request.path)
|
||||
return True
|
||||
|
||||
return False
|
||||
request[KEY_HASS_USER] = refresh_token.user
|
||||
return True
|
||||
|
||||
async def async_validate_signed_request(request):
|
||||
"""Validate a signed request."""
|
||||
|
@ -140,50 +103,16 @@ def setup_auth(hass, app):
|
|||
request[KEY_HASS_USER] = refresh_token.user
|
||||
return True
|
||||
|
||||
async def async_validate_trusted_networks(request):
|
||||
"""Test if request is from a trusted ip."""
|
||||
ip_addr = request[KEY_REAL_IP]
|
||||
|
||||
if not any(ip_addr in trusted_network for trusted_network in trusted_networks):
|
||||
return False
|
||||
|
||||
user = await hass.auth.async_get_owner()
|
||||
if user is None:
|
||||
return False
|
||||
|
||||
request[KEY_HASS_USER] = user
|
||||
return True
|
||||
|
||||
async def async_validate_legacy_api_password(request, password):
|
||||
"""Validate api_password."""
|
||||
user = await legacy_api_password.async_validate_password(hass, password)
|
||||
if user is None:
|
||||
return False
|
||||
|
||||
request[KEY_HASS_USER] = user
|
||||
return True
|
||||
|
||||
@middleware
|
||||
async def auth_middleware(request, handler):
|
||||
"""Authenticate as middleware."""
|
||||
authenticated = False
|
||||
|
||||
if HTTP_HEADER_HA_AUTH in request.headers or DATA_API_PASSWORD in request.query:
|
||||
if request.path not in old_auth_warning:
|
||||
_LOGGER.log(
|
||||
logging.INFO if support_legacy else logging.WARNING,
|
||||
"api_password is going to deprecate. You need to use a"
|
||||
" bearer token to access %s from %s",
|
||||
request.path,
|
||||
request[KEY_REAL_IP],
|
||||
)
|
||||
old_auth_warning.add(request.path)
|
||||
|
||||
if hdrs.AUTHORIZATION in request.headers and await async_validate_auth_header(
|
||||
request
|
||||
):
|
||||
# it included both use_auth and api_password Basic auth
|
||||
authenticated = True
|
||||
auth_type = "bearer token"
|
||||
|
||||
# We first start with a string check to avoid parsing query params
|
||||
# for every request.
|
||||
|
@ -193,39 +122,15 @@ def setup_auth(hass, app):
|
|||
and await async_validate_signed_request(request)
|
||||
):
|
||||
authenticated = True
|
||||
auth_type = "signed request"
|
||||
|
||||
elif trusted_networks and await async_validate_trusted_networks(request):
|
||||
if request.path not in old_auth_warning:
|
||||
# When removing this, don't forget to remove the print logic
|
||||
# in http/view.py
|
||||
request["deprecate_warning_message"] = (
|
||||
"Access from trusted networks without auth token is "
|
||||
"going to be removed in Home Assistant 0.96. Configure "
|
||||
"the trusted networks auth provider or use long-lived "
|
||||
"access tokens to access {} from {}".format(
|
||||
request.path, request[KEY_REAL_IP]
|
||||
)
|
||||
)
|
||||
old_auth_warning.add(request.path)
|
||||
authenticated = True
|
||||
|
||||
elif (
|
||||
support_legacy
|
||||
and HTTP_HEADER_HA_AUTH in request.headers
|
||||
and await async_validate_legacy_api_password(
|
||||
request, request.headers[HTTP_HEADER_HA_AUTH]
|
||||
if authenticated:
|
||||
_LOGGER.debug(
|
||||
"Authenticated %s for %s using %s",
|
||||
request[KEY_REAL_IP],
|
||||
request.path,
|
||||
auth_type,
|
||||
)
|
||||
):
|
||||
authenticated = True
|
||||
|
||||
elif (
|
||||
support_legacy
|
||||
and DATA_API_PASSWORD in request.query
|
||||
and await async_validate_legacy_api_password(
|
||||
request, request.query[DATA_API_PASSWORD]
|
||||
)
|
||||
):
|
||||
authenticated = True
|
||||
|
||||
request[KEY_AUTHENTICATED] = authenticated
|
||||
return await handler(request)
|
||||
|
|
|
@ -3,7 +3,7 @@ import aiohttp_cors
|
|||
from aiohttp.web_urldispatcher import Resource, ResourceRoute, StaticResource
|
||||
from aiohttp.hdrs import ACCEPT, CONTENT_TYPE, ORIGIN, AUTHORIZATION
|
||||
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH, HTTP_HEADER_X_REQUESTED_WITH
|
||||
from homeassistant.const import HTTP_HEADER_X_REQUESTED_WITH
|
||||
from homeassistant.core import callback
|
||||
|
||||
|
||||
|
@ -14,7 +14,6 @@ ALLOWED_CORS_HEADERS = [
|
|||
ACCEPT,
|
||||
HTTP_HEADER_X_REQUESTED_WITH,
|
||||
CONTENT_TYPE,
|
||||
HTTP_HEADER_HA_AUTH,
|
||||
AUTHORIZATION,
|
||||
]
|
||||
VALID_CORS_TYPES = (Resource, ResourceRoute, StaticResource)
|
||||
|
|
|
@ -17,7 +17,6 @@ from homeassistant.const import CONTENT_TYPE_JSON
|
|||
from homeassistant.core import Context, is_callback
|
||||
from homeassistant.helpers.json import JSONEncoder
|
||||
|
||||
from .ban import process_success_login
|
||||
from .const import KEY_AUTHENTICATED, KEY_HASS, KEY_REAL_IP
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -106,13 +105,8 @@ def request_handler_factory(view, handler):
|
|||
|
||||
authenticated = request.get(KEY_AUTHENTICATED, False)
|
||||
|
||||
if view.requires_auth:
|
||||
if authenticated:
|
||||
if "deprecate_warning_message" in request:
|
||||
_LOGGER.warning(request["deprecate_warning_message"])
|
||||
await process_success_login(request)
|
||||
else:
|
||||
raise HTTPUnauthorized()
|
||||
if view.requires_auth and not authenticated:
|
||||
raise HTTPUnauthorized()
|
||||
|
||||
_LOGGER.debug(
|
||||
"Serving %s to %s (auth: %s)",
|
||||
|
|
|
@ -3,7 +3,6 @@ import voluptuous as vol
|
|||
from voluptuous.humanize import humanize_error
|
||||
|
||||
from homeassistant.auth.models import RefreshToken, User
|
||||
from homeassistant.auth.providers import legacy_api_password
|
||||
from homeassistant.components.http.ban import process_wrong_login, process_success_login
|
||||
from homeassistant.const import __version__
|
||||
|
||||
|
@ -74,19 +73,6 @@ class AuthPhase:
|
|||
if refresh_token is not None:
|
||||
return await self._async_finish_auth(refresh_token.user, refresh_token)
|
||||
|
||||
elif self._hass.auth.support_legacy and "api_password" in msg:
|
||||
self._logger.info(
|
||||
"Received api_password, it is going to deprecate, please use"
|
||||
" access_token instead. For instructions, see https://"
|
||||
"developers.home-assistant.io/docs/en/external_api_websocket"
|
||||
".html#authentication-phase"
|
||||
)
|
||||
user = await legacy_api_password.async_validate_password(
|
||||
self._hass, msg["api_password"]
|
||||
)
|
||||
if user is not None:
|
||||
return await self._async_finish_auth(user, None)
|
||||
|
||||
self._send_message(auth_invalid_message("Invalid access token or password"))
|
||||
await process_wrong_login(self._request)
|
||||
raise Disconnect
|
||||
|
|
|
@ -468,12 +468,7 @@ def _format_config_error(ex: Exception, domain: str, config: Dict) -> str:
|
|||
return message
|
||||
|
||||
|
||||
async def async_process_ha_core_config(
|
||||
hass: HomeAssistant,
|
||||
config: Dict,
|
||||
api_password: Optional[str] = None,
|
||||
trusted_networks: Optional[Any] = None,
|
||||
) -> None:
|
||||
async def async_process_ha_core_config(hass: HomeAssistant, config: Dict) -> None:
|
||||
"""Process the [homeassistant] section from the configuration.
|
||||
|
||||
This method is a coroutine.
|
||||
|
@ -486,14 +481,6 @@ async def async_process_ha_core_config(
|
|||
|
||||
if auth_conf is None:
|
||||
auth_conf = [{"type": "homeassistant"}]
|
||||
if api_password:
|
||||
auth_conf.append(
|
||||
{"type": "legacy_api_password", "api_password": api_password}
|
||||
)
|
||||
if trusted_networks:
|
||||
auth_conf.append(
|
||||
{"type": "trusted_networks", "trusted_networks": trusted_networks}
|
||||
)
|
||||
|
||||
mfa_conf = config.get(
|
||||
CONF_AUTH_MFA_MODULES,
|
||||
|
|
|
@ -451,7 +451,6 @@ HTTP_SERVICE_UNAVAILABLE = 503
|
|||
HTTP_BASIC_AUTHENTICATION = "basic"
|
||||
HTTP_DIGEST_AUTHENTICATION = "digest"
|
||||
|
||||
HTTP_HEADER_HA_AUTH = "X-HA-access"
|
||||
HTTP_HEADER_X_REQUESTED_WITH = "X-Requested-With"
|
||||
|
||||
CONTENT_TYPE_JSON = "application/json"
|
||||
|
|
|
@ -30,7 +30,7 @@ async def async_setup_auth(
|
|||
hass, provider_configs, module_configs
|
||||
)
|
||||
ensure_auth_manager_loaded(hass.auth)
|
||||
await async_setup_component(hass, "auth", {"http": {"api_password": "bla"}})
|
||||
await async_setup_component(hass, "auth", {})
|
||||
if setup_api:
|
||||
await async_setup_component(hass, "api", {})
|
||||
return await aiohttp_client(hass.http.app)
|
||||
|
|
|
@ -103,7 +103,7 @@ def test_auth_code_store_expiration():
|
|||
|
||||
async def test_ws_current_user(hass, hass_ws_client, hass_access_token):
|
||||
"""Test the current user command with homeassistant creds."""
|
||||
assert await async_setup_component(hass, "auth", {"http": {"api_password": "bla"}})
|
||||
assert await async_setup_component(hass, "auth", {})
|
||||
|
||||
refresh_token = await hass.auth.async_validate_access_token(hass_access_token)
|
||||
user = refresh_token.user
|
||||
|
|
|
@ -40,7 +40,9 @@ def hass_ws_client(aiohttp_client, hass_access_token):
|
|||
assert auth_resp["type"] == TYPE_AUTH_REQUIRED
|
||||
|
||||
if access_token is None:
|
||||
await websocket.send_json({"type": TYPE_AUTH, "api_password": "bla"})
|
||||
await websocket.send_json(
|
||||
{"type": TYPE_AUTH, "access_token": "incorrect"}
|
||||
)
|
||||
else:
|
||||
await websocket.send_json(
|
||||
{"type": TYPE_AUTH, "access_token": access_token}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import asyncio
|
||||
import json
|
||||
|
||||
from aiohttp.hdrs import CONTENT_TYPE, AUTHORIZATION
|
||||
from aiohttp.hdrs import AUTHORIZATION
|
||||
import pytest
|
||||
|
||||
from homeassistant import core, const, setup
|
||||
|
@ -24,11 +24,6 @@ from . import DEMO_DEVICES
|
|||
|
||||
API_PASSWORD = "test1234"
|
||||
|
||||
HA_HEADERS = {
|
||||
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
||||
CONTENT_TYPE: const.CONTENT_TYPE_JSON,
|
||||
}
|
||||
|
||||
PROJECT_ID = "hasstest-1234"
|
||||
CLIENT_ID = "helloworld"
|
||||
ACCESS_TOKEN = "superdoublesecret"
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
"""Tests for Hassio component."""
|
||||
|
||||
API_PASSWORD = "pass1234"
|
||||
HASSIO_TOKEN = "123456"
|
||||
|
|
|
@ -9,7 +9,7 @@ from homeassistant.setup import async_setup_component
|
|||
from homeassistant.components.hassio.handler import HassIO, HassioAPIError
|
||||
|
||||
from tests.common import mock_coro
|
||||
from . import API_PASSWORD, HASSIO_TOKEN
|
||||
from . import HASSIO_TOKEN
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -39,23 +39,19 @@ def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock):
|
|||
side_effect=HassioAPIError(),
|
||||
):
|
||||
hass.state = CoreState.starting
|
||||
hass.loop.run_until_complete(
|
||||
async_setup_component(
|
||||
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
)
|
||||
hass.loop.run_until_complete(async_setup_component(hass, "hassio", {}))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hassio_client(hassio_stubs, hass, hass_client):
|
||||
"""Return a Hass.io HTTP client."""
|
||||
yield hass.loop.run_until_complete(hass_client())
|
||||
return hass.loop.run_until_complete(hass_client())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hassio_noauth_client(hassio_stubs, hass, aiohttp_client):
|
||||
"""Return a Hass.io HTTP client without auth."""
|
||||
yield hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
||||
return hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
@ -4,10 +4,8 @@ from unittest.mock import patch, Mock
|
|||
import pytest
|
||||
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
|
||||
from tests.common import mock_coro
|
||||
from . import API_PASSWORD
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -53,9 +51,7 @@ async def test_hassio_addon_panel_startup(hass, aioclient_mock, hassio_env):
|
|||
"homeassistant.components.hassio.addon_panel._register_panel",
|
||||
Mock(return_value=mock_coro()),
|
||||
) as mock_panel:
|
||||
await async_setup_component(
|
||||
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
await async_setup_component(hass, "hassio", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 3
|
||||
|
@ -98,9 +94,7 @@ async def test_hassio_addon_panel_api(hass, aioclient_mock, hassio_env, hass_cli
|
|||
"homeassistant.components.hassio.addon_panel._register_panel",
|
||||
Mock(return_value=mock_coro()),
|
||||
) as mock_panel:
|
||||
await async_setup_component(
|
||||
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
await async_setup_component(hass, "hassio", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 3
|
||||
|
@ -113,14 +107,10 @@ async def test_hassio_addon_panel_api(hass, aioclient_mock, hassio_env, hass_cli
|
|||
|
||||
hass_client = await hass_client()
|
||||
|
||||
resp = await hass_client.post(
|
||||
"/api/hassio_push/panel/test2", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
|
||||
)
|
||||
resp = await hass_client.post("/api/hassio_push/panel/test2")
|
||||
assert resp.status == 400
|
||||
|
||||
resp = await hass_client.post(
|
||||
"/api/hassio_push/panel/test1", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
|
||||
)
|
||||
resp = await hass_client.post("/api/hassio_push/panel/test1")
|
||||
assert resp.status == 200
|
||||
assert mock_panel.call_count == 2
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
"""The tests for the hassio component."""
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from tests.common import mock_coro
|
||||
from . import API_PASSWORD
|
||||
|
||||
|
||||
async def test_login_success(hass, hassio_client):
|
||||
|
@ -18,7 +16,6 @@ async def test_login_success(hass, hassio_client):
|
|||
resp = await hassio_client.post(
|
||||
"/api/hassio_auth",
|
||||
json={"username": "test", "password": "123456", "addon": "samba"},
|
||||
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
|
||||
)
|
||||
|
||||
# Check we got right response
|
||||
|
@ -36,7 +33,6 @@ async def test_login_error(hass, hassio_client):
|
|||
resp = await hassio_client.post(
|
||||
"/api/hassio_auth",
|
||||
json={"username": "test", "password": "123456", "addon": "samba"},
|
||||
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
|
||||
)
|
||||
|
||||
# Check we got right response
|
||||
|
@ -51,9 +47,7 @@ async def test_login_no_data(hass, hassio_client):
|
|||
"HassAuthProvider.async_validate_login",
|
||||
Mock(side_effect=HomeAssistantError()),
|
||||
) as mock_login:
|
||||
resp = await hassio_client.post(
|
||||
"/api/hassio_auth", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
|
||||
)
|
||||
resp = await hassio_client.post("/api/hassio_auth")
|
||||
|
||||
# Check we got right response
|
||||
assert resp.status == 400
|
||||
|
@ -68,9 +62,7 @@ async def test_login_no_username(hass, hassio_client):
|
|||
Mock(side_effect=HomeAssistantError()),
|
||||
) as mock_login:
|
||||
resp = await hassio_client.post(
|
||||
"/api/hassio_auth",
|
||||
json={"password": "123456", "addon": "samba"},
|
||||
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
|
||||
"/api/hassio_auth", json={"password": "123456", "addon": "samba"}
|
||||
)
|
||||
|
||||
# Check we got right response
|
||||
|
@ -93,7 +85,6 @@ async def test_login_success_extra(hass, hassio_client):
|
|||
"addon": "samba",
|
||||
"path": "/share",
|
||||
},
|
||||
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
|
||||
)
|
||||
|
||||
# Check we got right response
|
||||
|
|
|
@ -3,10 +3,9 @@ from unittest.mock import patch, Mock
|
|||
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.hassio.handler import HassioAPIError
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START, HTTP_HEADER_HA_AUTH
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
|
||||
from tests.common import mock_coro
|
||||
from . import API_PASSWORD
|
||||
|
||||
|
||||
async def test_hassio_discovery_startup(hass, aioclient_mock, hassio_client):
|
||||
|
@ -101,9 +100,7 @@ async def test_hassio_discovery_startup_done(hass, aioclient_mock, hassio_client
|
|||
Mock(return_value=mock_coro({"type": "abort"})),
|
||||
) as mock_mqtt:
|
||||
await hass.async_start()
|
||||
await async_setup_component(
|
||||
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
await async_setup_component(hass, "hassio", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert aioclient_mock.call_count == 2
|
||||
|
@ -151,7 +148,6 @@ async def test_hassio_discovery_webhook(hass, aioclient_mock, hassio_client):
|
|||
) as mock_mqtt:
|
||||
resp = await hassio_client.post(
|
||||
"/api/hassio_push/discovery/testuuid",
|
||||
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
|
||||
json={"addon": "mosquitto", "service": "mqtt", "uuid": "testuuid"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -4,19 +4,13 @@ from unittest.mock import patch
|
|||
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
|
||||
from . import API_PASSWORD
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_forward_request(hassio_client, aioclient_mock):
|
||||
"""Test fetching normal path."""
|
||||
aioclient_mock.post("http://127.0.0.1/beer", text="response")
|
||||
|
||||
resp = yield from hassio_client.post(
|
||||
"/api/hassio/beer", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
|
||||
)
|
||||
resp = yield from hassio_client.post("/api/hassio/beer")
|
||||
|
||||
# Check we got right response
|
||||
assert resp.status == 200
|
||||
|
@ -87,9 +81,7 @@ def test_forward_log_request(hassio_client, aioclient_mock):
|
|||
"""Test fetching normal log path doesn't remove ANSI color escape codes."""
|
||||
aioclient_mock.get("http://127.0.0.1/beer/logs", text="\033[32mresponse\033[0m")
|
||||
|
||||
resp = yield from hassio_client.get(
|
||||
"/api/hassio/beer/logs", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
|
||||
)
|
||||
resp = yield from hassio_client.get("/api/hassio/beer/logs")
|
||||
|
||||
# Check we got right response
|
||||
assert resp.status == 200
|
||||
|
@ -107,9 +99,7 @@ def test_bad_gateway_when_cannot_find_supervisor(hassio_client):
|
|||
"homeassistant.components.hassio.http.async_timeout.timeout",
|
||||
side_effect=asyncio.TimeoutError,
|
||||
):
|
||||
resp = yield from hassio_client.get(
|
||||
"/api/hassio/addons/test/info", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
|
||||
)
|
||||
resp = yield from hassio_client.get("/api/hassio/addons/test/info")
|
||||
assert resp.status == 502
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,10 @@ from aiohttp import web
|
|||
from homeassistant.components.http.const import KEY_REAL_IP
|
||||
|
||||
|
||||
# Relic from the past. Kept here so we can run negative tests.
|
||||
HTTP_HEADER_HA_AUTH = "X-HA-access"
|
||||
|
||||
|
||||
def mock_real_ip(app):
|
||||
"""Inject middleware to mock real IP.
|
||||
|
||||
|
|
|
@ -11,10 +11,8 @@ from homeassistant.auth.providers import trusted_networks
|
|||
from homeassistant.components.http.auth import setup_auth, async_sign_path
|
||||
from homeassistant.components.http.const import KEY_AUTHENTICATED
|
||||
from homeassistant.components.http.real_ip import setup_real_ip
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
from homeassistant.setup import async_setup_component
|
||||
from . import mock_real_ip
|
||||
|
||||
from . import mock_real_ip, HTTP_HEADER_HA_AUTH
|
||||
|
||||
API_PASSWORD = "test-password"
|
||||
|
||||
|
@ -87,29 +85,29 @@ async def test_auth_middleware_loaded_by_default(hass):
|
|||
assert len(mock_setup.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_access_with_password_in_header(app, aiohttp_client, legacy_auth, hass):
|
||||
async def test_cant_access_with_password_in_header(
|
||||
app, aiohttp_client, legacy_auth, hass
|
||||
):
|
||||
"""Test access with password in header."""
|
||||
setup_auth(hass, app)
|
||||
client = await aiohttp_client(app)
|
||||
user = await get_legacy_user(hass.auth)
|
||||
|
||||
req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD})
|
||||
assert req.status == 200
|
||||
assert await req.json() == {"user_id": user.id}
|
||||
assert req.status == 401
|
||||
|
||||
req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: "wrong-pass"})
|
||||
assert req.status == 401
|
||||
|
||||
|
||||
async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth, hass):
|
||||
async def test_cant_access_with_password_in_query(
|
||||
app, aiohttp_client, legacy_auth, hass
|
||||
):
|
||||
"""Test access with password in URL."""
|
||||
setup_auth(hass, app)
|
||||
client = await aiohttp_client(app)
|
||||
user = await get_legacy_user(hass.auth)
|
||||
|
||||
resp = await client.get("/", params={"api_password": API_PASSWORD})
|
||||
assert resp.status == 200
|
||||
assert await resp.json() == {"user_id": user.id}
|
||||
assert resp.status == 401
|
||||
|
||||
resp = await client.get("/")
|
||||
assert resp.status == 401
|
||||
|
@ -118,15 +116,13 @@ async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth, h
|
|||
assert resp.status == 401
|
||||
|
||||
|
||||
async def test_basic_auth_works(app, aiohttp_client, hass, legacy_auth):
|
||||
async def test_basic_auth_does_not_work(app, aiohttp_client, hass, legacy_auth):
|
||||
"""Test access with basic authentication."""
|
||||
setup_auth(hass, app)
|
||||
client = await aiohttp_client(app)
|
||||
user = await get_legacy_user(hass.auth)
|
||||
|
||||
req = await client.get("/", auth=BasicAuth("homeassistant", API_PASSWORD))
|
||||
assert req.status == 200
|
||||
assert await req.json() == {"user_id": user.id}
|
||||
assert req.status == 401
|
||||
|
||||
req = await client.get("/", auth=BasicAuth("wrong_username", API_PASSWORD))
|
||||
assert req.status == 401
|
||||
|
@ -138,7 +134,7 @@ async def test_basic_auth_works(app, aiohttp_client, hass, legacy_auth):
|
|||
assert req.status == 401
|
||||
|
||||
|
||||
async def test_access_with_trusted_ip(
|
||||
async def test_cannot_access_with_trusted_ip(
|
||||
hass, app2, trusted_networks_auth, aiohttp_client, hass_owner_user
|
||||
):
|
||||
"""Test access with an untrusted ip address."""
|
||||
|
@ -155,8 +151,7 @@ async def test_access_with_trusted_ip(
|
|||
for remote_addr in TRUSTED_ADDRESSES:
|
||||
set_mock_ip(remote_addr)
|
||||
resp = await client.get("/")
|
||||
assert resp.status == 200, "{} should be trusted".format(remote_addr)
|
||||
assert await resp.json() == {"user_id": hass_owner_user.id}
|
||||
assert resp.status == 401, "{} shouldn't be trusted".format(remote_addr)
|
||||
|
||||
|
||||
async def test_auth_active_access_with_access_token_in_header(
|
||||
|
@ -209,29 +204,24 @@ async def test_auth_active_access_with_trusted_ip(
|
|||
for remote_addr in TRUSTED_ADDRESSES:
|
||||
set_mock_ip(remote_addr)
|
||||
resp = await client.get("/")
|
||||
assert resp.status == 200, "{} should be trusted".format(remote_addr)
|
||||
assert await resp.json() == {"user_id": hass_owner_user.id}
|
||||
assert resp.status == 401, "{} shouldn't be trusted".format(remote_addr)
|
||||
|
||||
|
||||
async def test_auth_legacy_support_api_password_access(
|
||||
async def test_auth_legacy_support_api_password_cannot_access(
|
||||
app, aiohttp_client, legacy_auth, hass
|
||||
):
|
||||
"""Test access using api_password if auth.support_legacy."""
|
||||
setup_auth(hass, app)
|
||||
client = await aiohttp_client(app)
|
||||
user = await get_legacy_user(hass.auth)
|
||||
|
||||
req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD})
|
||||
assert req.status == 200
|
||||
assert await req.json() == {"user_id": user.id}
|
||||
assert req.status == 401
|
||||
|
||||
resp = await client.get("/", params={"api_password": API_PASSWORD})
|
||||
assert resp.status == 200
|
||||
assert await resp.json() == {"user_id": user.id}
|
||||
assert resp.status == 401
|
||||
|
||||
req = await client.get("/", auth=BasicAuth("homeassistant", API_PASSWORD))
|
||||
assert req.status == 200
|
||||
assert await req.json() == {"user_id": user.id}
|
||||
assert req.status == 401
|
||||
|
||||
|
||||
async def test_auth_access_signed_path(hass, app, aiohttp_client, hass_access_token):
|
||||
|
|
|
@ -148,6 +148,8 @@ async def test_failed_login_attempts_counter(hass, aiohttp_client):
|
|||
assert resp.status == 200
|
||||
assert app[KEY_FAILED_LOGIN_ATTEMPTS][remote_ip] == 2
|
||||
|
||||
# This used to check that with trusted networks we reset login attempts
|
||||
# We no longer support trusted networks.
|
||||
resp = await client.get("/auth_true")
|
||||
assert resp.status == 200
|
||||
assert remote_ip not in app[KEY_FAILED_LOGIN_ATTEMPTS]
|
||||
assert app[KEY_FAILED_LOGIN_ATTEMPTS][remote_ip] == 2
|
||||
|
|
|
@ -13,11 +13,12 @@ from aiohttp.hdrs import (
|
|||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.http.cors import setup_cors
|
||||
from homeassistant.components.http.view import HomeAssistantView
|
||||
|
||||
from . import HTTP_HEADER_HA_AUTH
|
||||
|
||||
|
||||
TRUSTED_ORIGIN = "https://home-assistant.io"
|
||||
|
||||
|
@ -91,13 +92,13 @@ async def test_cors_preflight_allowed(client):
|
|||
headers={
|
||||
ORIGIN: TRUSTED_ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD: "GET",
|
||||
ACCESS_CONTROL_REQUEST_HEADERS: "x-ha-access",
|
||||
ACCESS_CONTROL_REQUEST_HEADERS: "x-requested-with",
|
||||
},
|
||||
)
|
||||
|
||||
assert req.status == 200
|
||||
assert req.headers[ACCESS_CONTROL_ALLOW_ORIGIN] == TRUSTED_ORIGIN
|
||||
assert req.headers[ACCESS_CONTROL_ALLOW_HEADERS] == HTTP_HEADER_HA_AUTH.upper()
|
||||
assert req.headers[ACCESS_CONTROL_ALLOW_HEADERS] == "X-REQUESTED-WITH"
|
||||
|
||||
|
||||
async def test_cors_middleware_with_cors_allowed_view(hass):
|
||||
|
|
|
@ -133,7 +133,7 @@ async def test_not_log_password(hass, aiohttp_client, caplog, legacy_auth):
|
|||
|
||||
resp = await client.get("/api/", params={"api_password": "test-password"})
|
||||
|
||||
assert resp.status == 200
|
||||
assert resp.status == 401
|
||||
logs = caplog.text
|
||||
|
||||
# Ensure we don't log API passwords
|
||||
|
|
|
@ -57,12 +57,7 @@ class TestMQTT:
|
|||
|
||||
self.hass.config.api = MagicMock(api_password="api_password")
|
||||
assert setup_component(
|
||||
self.hass,
|
||||
mqtt.DOMAIN,
|
||||
{
|
||||
"http": {"api_password": "http_secret"},
|
||||
mqtt.DOMAIN: {CONF_PASSWORD: password},
|
||||
},
|
||||
self.hass, mqtt.DOMAIN, {mqtt.DOMAIN: {CONF_PASSWORD: password}}
|
||||
)
|
||||
self.hass.block_till_done()
|
||||
assert mock_mqtt.called
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
"""Tests for the websocket API."""
|
||||
API_PASSWORD = "test-password"
|
||||
|
|
|
@ -5,8 +5,6 @@ from homeassistant.setup import async_setup_component
|
|||
from homeassistant.components.websocket_api.http import URL
|
||||
from homeassistant.components.websocket_api.auth import TYPE_AUTH_REQUIRED
|
||||
|
||||
from . import API_PASSWORD
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def websocket_client(hass, hass_ws_client, hass_access_token):
|
||||
|
@ -17,11 +15,7 @@ def websocket_client(hass, hass_ws_client, hass_access_token):
|
|||
@pytest.fixture
|
||||
def no_auth_websocket_client(hass, loop, aiohttp_client):
|
||||
"""Websocket connection that requires authentication."""
|
||||
assert loop.run_until_complete(
|
||||
async_setup_component(
|
||||
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
)
|
||||
assert loop.run_until_complete(async_setup_component(hass, "websocket_api", {}))
|
||||
|
||||
client = loop.run_until_complete(aiohttp_client(hass.http.app))
|
||||
ws = loop.run_until_complete(client.ws_connect(URL))
|
||||
|
|
|
@ -17,21 +17,10 @@ from homeassistant.setup import async_setup_component
|
|||
|
||||
from tests.common import mock_coro
|
||||
|
||||
from . import API_PASSWORD
|
||||
|
||||
|
||||
async def test_auth_via_msg(no_auth_websocket_client, legacy_auth):
|
||||
"""Test authenticating."""
|
||||
await no_auth_websocket_client.send_json(
|
||||
{"type": TYPE_AUTH, "api_password": API_PASSWORD}
|
||||
)
|
||||
|
||||
msg = await no_auth_websocket_client.receive_json()
|
||||
|
||||
assert msg["type"] == TYPE_AUTH_OK
|
||||
|
||||
|
||||
async def test_auth_events(hass, no_auth_websocket_client, legacy_auth):
|
||||
async def test_auth_events(
|
||||
hass, no_auth_websocket_client, legacy_auth, hass_access_token
|
||||
):
|
||||
"""Test authenticating."""
|
||||
connected_evt = []
|
||||
hass.helpers.dispatcher.async_dispatcher_connect(
|
||||
|
@ -42,7 +31,7 @@ async def test_auth_events(hass, no_auth_websocket_client, legacy_auth):
|
|||
SIGNAL_WEBSOCKET_DISCONNECTED, lambda: disconnected_evt.append(1)
|
||||
)
|
||||
|
||||
await test_auth_via_msg(no_auth_websocket_client, legacy_auth)
|
||||
await test_auth_active_with_token(hass, no_auth_websocket_client, hass_access_token)
|
||||
|
||||
assert len(connected_evt) == 1
|
||||
assert not disconnected_evt
|
||||
|
@ -60,7 +49,7 @@ async def test_auth_via_msg_incorrect_pass(no_auth_websocket_client):
|
|||
return_value=mock_coro(),
|
||||
) as mock_process_wrong_login:
|
||||
await no_auth_websocket_client.send_json(
|
||||
{"type": TYPE_AUTH, "api_password": API_PASSWORD + "wrong"}
|
||||
{"type": TYPE_AUTH, "api_password": "wrong"}
|
||||
)
|
||||
|
||||
msg = await no_auth_websocket_client.receive_json()
|
||||
|
@ -110,31 +99,25 @@ async def test_pre_auth_only_auth_allowed(no_auth_websocket_client):
|
|||
assert msg["message"].startswith("Auth message incorrectly formatted")
|
||||
|
||||
|
||||
async def test_auth_active_with_token(hass, aiohttp_client, hass_access_token):
|
||||
async def test_auth_active_with_token(
|
||||
hass, no_auth_websocket_client, hass_access_token
|
||||
):
|
||||
"""Test authenticating with a token."""
|
||||
assert await async_setup_component(
|
||||
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
|
||||
assert await async_setup_component(hass, "websocket_api", {})
|
||||
|
||||
await no_auth_websocket_client.send_json(
|
||||
{"type": TYPE_AUTH, "access_token": hass_access_token}
|
||||
)
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
|
||||
async with client.ws_connect(URL) as ws:
|
||||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_REQUIRED
|
||||
|
||||
await ws.send_json({"type": TYPE_AUTH, "access_token": hass_access_token})
|
||||
|
||||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_OK
|
||||
auth_msg = await no_auth_websocket_client.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_OK
|
||||
|
||||
|
||||
async def test_auth_active_user_inactive(hass, aiohttp_client, hass_access_token):
|
||||
"""Test authenticating with a token."""
|
||||
refresh_token = await hass.auth.async_validate_access_token(hass_access_token)
|
||||
refresh_token.user.is_active = False
|
||||
assert await async_setup_component(
|
||||
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
assert await async_setup_component(hass, "websocket_api", {})
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
|
||||
|
@ -150,9 +133,7 @@ async def test_auth_active_user_inactive(hass, aiohttp_client, hass_access_token
|
|||
|
||||
async def test_auth_active_with_password_not_allow(hass, aiohttp_client):
|
||||
"""Test authenticating with a token."""
|
||||
assert await async_setup_component(
|
||||
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
assert await async_setup_component(hass, "websocket_api", {})
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
|
||||
|
@ -160,7 +141,7 @@ async def test_auth_active_with_password_not_allow(hass, aiohttp_client):
|
|||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_REQUIRED
|
||||
|
||||
await ws.send_json({"type": TYPE_AUTH, "api_password": API_PASSWORD})
|
||||
await ws.send_json({"type": TYPE_AUTH, "api_password": "some-password"})
|
||||
|
||||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_INVALID
|
||||
|
@ -168,28 +149,23 @@ async def test_auth_active_with_password_not_allow(hass, aiohttp_client):
|
|||
|
||||
async def test_auth_legacy_support_with_password(hass, aiohttp_client, legacy_auth):
|
||||
"""Test authenticating with a token."""
|
||||
assert await async_setup_component(
|
||||
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
assert await async_setup_component(hass, "websocket_api", {})
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
|
||||
async with client.ws_connect(URL) as ws:
|
||||
with patch("homeassistant.auth.AuthManager.support_legacy", return_value=True):
|
||||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_REQUIRED
|
||||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_REQUIRED
|
||||
|
||||
await ws.send_json({"type": TYPE_AUTH, "api_password": API_PASSWORD})
|
||||
await ws.send_json({"type": TYPE_AUTH, "api_password": "some-password"})
|
||||
|
||||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_OK
|
||||
auth_msg = await ws.receive_json()
|
||||
assert auth_msg["type"] == TYPE_AUTH_INVALID
|
||||
|
||||
|
||||
async def test_auth_with_invalid_token(hass, aiohttp_client):
|
||||
"""Test authenticating with a token."""
|
||||
assert await async_setup_component(
|
||||
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
assert await async_setup_component(hass, "websocket_api", {})
|
||||
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@ from homeassistant.setup import async_setup_component
|
|||
|
||||
from tests.common import async_mock_service
|
||||
|
||||
from . import API_PASSWORD
|
||||
|
||||
|
||||
async def test_call_service(hass, websocket_client):
|
||||
"""Test call service command."""
|
||||
|
@ -250,9 +248,7 @@ async def test_ping(websocket_client):
|
|||
|
||||
async def test_call_service_context_with_user(hass, aiohttp_client, hass_access_token):
|
||||
"""Test that the user is set in the service call context."""
|
||||
assert await async_setup_component(
|
||||
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
|
||||
)
|
||||
assert await async_setup_component(hass, "websocket_api", {})
|
||||
|
||||
calls = async_mock_service(hass, "domain_test", "test_service")
|
||||
client = await aiohttp_client(hass.http.app)
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
from homeassistant.bootstrap import async_setup_component
|
||||
|
||||
from tests.common import assert_setup_component
|
||||
from .test_auth import test_auth_via_msg
|
||||
from .test_auth import test_auth_active_with_token
|
||||
|
||||
|
||||
async def test_websocket_api(hass, no_auth_websocket_client, legacy_auth):
|
||||
async def test_websocket_api(
|
||||
hass, no_auth_websocket_client, hass_access_token, legacy_auth
|
||||
):
|
||||
"""Test API streams."""
|
||||
with assert_setup_component(1):
|
||||
await async_setup_component(
|
||||
|
@ -16,7 +18,7 @@ async def test_websocket_api(hass, no_auth_websocket_client, legacy_auth):
|
|||
state = hass.states.get("sensor.connected_clients")
|
||||
assert state.state == "0"
|
||||
|
||||
await test_auth_via_msg(no_auth_websocket_client, legacy_auth)
|
||||
await test_auth_active_with_token(hass, no_auth_websocket_client, hass_access_token)
|
||||
|
||||
state = hass.states.get("sensor.connected_clients")
|
||||
assert state.state == "1"
|
||||
|
|
|
@ -92,8 +92,8 @@ def test_secrets(isfile_patch, loop):
|
|||
|
||||
files = {
|
||||
get_test_config_dir(YAML_CONFIG_FILE): BASE_CONFIG
|
||||
+ ("http:\n" " api_password: !secret http_pw"),
|
||||
secrets_path: ("logger: debug\n" "http_pw: abc123"),
|
||||
+ ("http:\n" " cors_allowed_origins: !secret http_pw"),
|
||||
secrets_path: ("logger: debug\n" "http_pw: http://google.com"),
|
||||
}
|
||||
|
||||
with patch_yaml_files(files):
|
||||
|
@ -103,17 +103,15 @@ def test_secrets(isfile_patch, loop):
|
|||
assert res["except"] == {}
|
||||
assert res["components"].keys() == {"homeassistant", "http"}
|
||||
assert res["components"]["http"] == {
|
||||
"api_password": "abc123",
|
||||
"cors_allowed_origins": ["https://cast.home-assistant.io"],
|
||||
"cors_allowed_origins": ["http://google.com"],
|
||||
"ip_ban_enabled": True,
|
||||
"login_attempts_threshold": -1,
|
||||
"server_host": "0.0.0.0",
|
||||
"server_port": 8123,
|
||||
"trusted_networks": [],
|
||||
"ssl_profile": "modern",
|
||||
}
|
||||
assert res["secret_cache"] == {secrets_path: {"http_pw": "abc123"}}
|
||||
assert res["secrets"] == {"http_pw": "abc123"}
|
||||
assert res["secret_cache"] == {secrets_path: {"http_pw": "http://google.com"}}
|
||||
assert res["secrets"] == {"http_pw": "http://google.com"}
|
||||
assert normalize_yaml_files(res) == [
|
||||
".../configuration.yaml",
|
||||
".../secrets.yaml",
|
||||
|
|
|
@ -5,7 +5,6 @@ import copy
|
|||
import os
|
||||
import unittest.mock as mock
|
||||
from collections import OrderedDict
|
||||
from ipaddress import ip_network
|
||||
|
||||
import asynctest
|
||||
import pytest
|
||||
|
@ -876,48 +875,6 @@ async def test_auth_provider_config_default(hass):
|
|||
assert hass.auth.auth_mfa_modules[0].id == "totp"
|
||||
|
||||
|
||||
async def test_auth_provider_config_default_api_password(hass):
|
||||
"""Test loading default auth provider config with api password."""
|
||||
core_config = {
|
||||
"latitude": 60,
|
||||
"longitude": 50,
|
||||
"elevation": 25,
|
||||
"name": "Huis",
|
||||
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
"time_zone": "GMT",
|
||||
}
|
||||
if hasattr(hass, "auth"):
|
||||
del hass.auth
|
||||
await config_util.async_process_ha_core_config(hass, core_config, "pass")
|
||||
|
||||
assert len(hass.auth.auth_providers) == 2
|
||||
assert hass.auth.auth_providers[0].type == "homeassistant"
|
||||
assert hass.auth.auth_providers[1].type == "legacy_api_password"
|
||||
assert hass.auth.auth_providers[1].api_password == "pass"
|
||||
|
||||
|
||||
async def test_auth_provider_config_default_trusted_networks(hass):
|
||||
"""Test loading default auth provider config with trusted networks."""
|
||||
core_config = {
|
||||
"latitude": 60,
|
||||
"longitude": 50,
|
||||
"elevation": 25,
|
||||
"name": "Huis",
|
||||
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
"time_zone": "GMT",
|
||||
}
|
||||
if hasattr(hass, "auth"):
|
||||
del hass.auth
|
||||
await config_util.async_process_ha_core_config(
|
||||
hass, core_config, trusted_networks=["192.168.0.1"]
|
||||
)
|
||||
|
||||
assert len(hass.auth.auth_providers) == 2
|
||||
assert hass.auth.auth_providers[0].type == "homeassistant"
|
||||
assert hass.auth.auth_providers[1].type == "trusted_networks"
|
||||
assert hass.auth.auth_providers[1].trusted_networks[0] == ip_network("192.168.0.1")
|
||||
|
||||
|
||||
async def test_disallowed_auth_provider_config(hass):
|
||||
"""Test loading insecure example auth provider is disallowed."""
|
||||
core_config = {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue