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:
Paulus Schoutsen 2019-10-14 14:56:45 -07:00 committed by GitHub
parent 97478d1ef4
commit 3231e22ddf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 114 additions and 423 deletions

View File

@ -86,18 +86,6 @@ class AuthManager:
hass, self._async_create_login_flow, self._async_finish_login_flow 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 @property
def auth_providers(self) -> List[AuthProvider]: def auth_providers(self) -> List[AuthProvider]:
"""Return a list of available auth providers.""" """Return a list of available auth providers."""

View File

@ -64,13 +64,9 @@ async def async_from_config_dict(
) )
core_config = config.get(core.DOMAIN, {}) core_config = config.get(core.DOMAIN, {})
api_password = config.get("http", {}).get("api_password")
trusted_networks = config.get("http", {}).get("trusted_networks")
try: try:
await conf_util.async_process_ha_core_config( await conf_util.async_process_ha_core_config(hass, core_config)
hass, core_config, api_password, trusted_networks
)
except vol.Invalid as config_err: except vol.Invalid as config_err:
conf_util.async_log_exception(config_err, "homeassistant", core_config, hass) conf_util.async_log_exception(config_err, "homeassistant", core_config, hass)
return None return None

View File

@ -72,7 +72,11 @@ import voluptuous_serialize
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components.http import KEY_REAL_IP 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.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView from homeassistant.components.http.view import HomeAssistantView
from . import indieauth from . import indieauth
@ -185,6 +189,7 @@ class LoginFlowIndexView(HomeAssistantView):
return self.json_message("Handler does not support init", 400) return self.json_message("Handler does not support init", 400)
if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
await process_success_login(request)
result.pop("data") result.pop("data")
result["result"] = self._store_result(data["client_id"], result["result"]) result["result"] = self._store_result(data["client_id"], result["result"])
return self.json(result) return self.json(result)

View File

@ -17,7 +17,6 @@ from homeassistant.const import (
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
import homeassistant.util as hass_util import homeassistant.util as hass_util
from homeassistant.util import ssl as ssl_util from homeassistant.util import ssl as ssl_util
from homeassistant.util.logging import HideSensitiveDataFilter
from .auth import setup_auth from .auth import setup_auth
from .ban import setup_bans from .ban import setup_bans
@ -32,7 +31,6 @@ from .view import HomeAssistantView # noqa
DOMAIN = "http" DOMAIN = "http"
CONF_API_PASSWORD = "api_password"
CONF_SERVER_HOST = "server_host" CONF_SERVER_HOST = "server_host"
CONF_SERVER_PORT = "server_port" CONF_SERVER_PORT = "server_port"
CONF_BASE_URL = "base_url" CONF_BASE_URL = "base_url"
@ -42,7 +40,6 @@ CONF_SSL_KEY = "ssl_key"
CONF_CORS_ORIGINS = "cors_allowed_origins" CONF_CORS_ORIGINS = "cors_allowed_origins"
CONF_USE_X_FORWARDED_FOR = "use_x_forwarded_for" CONF_USE_X_FORWARDED_FOR = "use_x_forwarded_for"
CONF_TRUSTED_PROXIES = "trusted_proxies" CONF_TRUSTED_PROXIES = "trusted_proxies"
CONF_TRUSTED_NETWORKS = "trusted_networks"
CONF_LOGIN_ATTEMPTS_THRESHOLD = "login_attempts_threshold" CONF_LOGIN_ATTEMPTS_THRESHOLD = "login_attempts_threshold"
CONF_IP_BAN_ENABLED = "ip_ban_enabled" CONF_IP_BAN_ENABLED = "ip_ban_enabled"
CONF_SSL_PROFILE = "ssl_profile" CONF_SSL_PROFILE = "ssl_profile"
@ -59,37 +56,8 @@ DEFAULT_CORS = "https://cast.home-assistant.io"
NO_LOGIN_ATTEMPT_THRESHOLD = -1 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( 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_HOST, default=DEFAULT_SERVER_HOST): cv.string,
vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port, vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port,
vol.Optional(CONF_BASE_URL): cv.string, vol.Optional(CONF_BASE_URL): cv.string,
@ -103,9 +71,6 @@ HTTP_SCHEMA = vol.Schema(
vol.Inclusive(CONF_TRUSTED_PROXIES, "proxy"): vol.All( vol.Inclusive(CONF_TRUSTED_PROXIES, "proxy"): vol.All(
cv.ensure_list, [ip_network] cv.ensure_list, [ip_network]
), ),
vol.Optional(CONF_TRUSTED_NETWORKS, default=[]): vol.All(
cv.ensure_list, [ip_network], trusted_networks_deprecated
),
vol.Optional( vol.Optional(
CONF_LOGIN_ATTEMPTS_THRESHOLD, default=NO_LOGIN_ATTEMPT_THRESHOLD CONF_LOGIN_ATTEMPTS_THRESHOLD, default=NO_LOGIN_ATTEMPT_THRESHOLD
): vol.Any(cv.positive_int, 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: if conf is None:
conf = HTTP_SCHEMA({}) conf = HTTP_SCHEMA({})
api_password = conf.get(CONF_API_PASSWORD)
server_host = conf[CONF_SERVER_HOST] server_host = conf[CONF_SERVER_HOST]
server_port = conf[CONF_SERVER_PORT] server_port = conf[CONF_SERVER_PORT]
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE) ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
@ -162,11 +126,6 @@ async def async_setup(hass, config):
login_threshold = conf[CONF_LOGIN_ATTEMPTS_THRESHOLD] login_threshold = conf[CONF_LOGIN_ATTEMPTS_THRESHOLD]
ssl_profile = conf[CONF_SSL_PROFILE] ssl_profile = conf[CONF_SSL_PROFILE]
if api_password is not None:
logging.getLogger("aiohttp.access").addFilter(
HideSensitiveDataFilter(api_password)
)
server = HomeAssistantHTTP( server = HomeAssistantHTTP(
hass, hass,
server_host=server_host, server_host=server_host,

View File

@ -1,14 +1,11 @@
"""Authentication for HTTP component.""" """Authentication for HTTP component."""
import base64
import logging import logging
from aiohttp import hdrs from aiohttp import hdrs
from aiohttp.web import middleware from aiohttp.web import middleware
import jwt import jwt
from homeassistant.auth.providers import legacy_api_password
from homeassistant.auth.util import generate_secret from homeassistant.auth.util import generate_secret
from homeassistant.const import HTTP_HEADER_HA_AUTH
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
@ -52,16 +49,6 @@ def async_sign_path(hass, refresh_token_id, path, expiration):
@callback @callback
def setup_auth(hass, app): def setup_auth(hass, app):
"""Create auth middleware for the 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): async def async_validate_auth_header(request):
""" """
@ -75,40 +62,16 @@ def setup_auth(hass, app):
# If no space in authorization header # If no space in authorization header
return False return False
if auth_type == "Bearer": if auth_type != "Bearer":
refresh_token = await hass.auth.async_validate_access_token(auth_val) return False
if refresh_token is None:
return False
request[KEY_HASS_USER] = refresh_token.user refresh_token = await hass.auth.async_validate_access_token(auth_val)
return True
if auth_type == "Basic" and support_legacy: if refresh_token is None:
decoded = base64.b64decode(auth_val).decode("utf-8") return False
try:
username, password = decoded.split(":", 1)
except ValueError:
# If no ':' in decoded
return False
if username != "homeassistant": request[KEY_HASS_USER] = refresh_token.user
return False return True
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
async def async_validate_signed_request(request): async def async_validate_signed_request(request):
"""Validate a signed request.""" """Validate a signed request."""
@ -140,50 +103,16 @@ def setup_auth(hass, app):
request[KEY_HASS_USER] = refresh_token.user request[KEY_HASS_USER] = refresh_token.user
return True 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 @middleware
async def auth_middleware(request, handler): async def auth_middleware(request, handler):
"""Authenticate as middleware.""" """Authenticate as middleware."""
authenticated = False 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( if hdrs.AUTHORIZATION in request.headers and await async_validate_auth_header(
request request
): ):
# it included both use_auth and api_password Basic auth
authenticated = True authenticated = True
auth_type = "bearer token"
# We first start with a string check to avoid parsing query params # We first start with a string check to avoid parsing query params
# for every request. # for every request.
@ -193,39 +122,15 @@ def setup_auth(hass, app):
and await async_validate_signed_request(request) and await async_validate_signed_request(request)
): ):
authenticated = True authenticated = True
auth_type = "signed request"
elif trusted_networks and await async_validate_trusted_networks(request): if authenticated:
if request.path not in old_auth_warning: _LOGGER.debug(
# When removing this, don't forget to remove the print logic "Authenticated %s for %s using %s",
# in http/view.py request[KEY_REAL_IP],
request["deprecate_warning_message"] = ( request.path,
"Access from trusted networks without auth token is " auth_type,
"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]
) )
):
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 request[KEY_AUTHENTICATED] = authenticated
return await handler(request) return await handler(request)

View File

@ -3,7 +3,7 @@ import aiohttp_cors
from aiohttp.web_urldispatcher import Resource, ResourceRoute, StaticResource from aiohttp.web_urldispatcher import Resource, ResourceRoute, StaticResource
from aiohttp.hdrs import ACCEPT, CONTENT_TYPE, ORIGIN, AUTHORIZATION 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 from homeassistant.core import callback
@ -14,7 +14,6 @@ ALLOWED_CORS_HEADERS = [
ACCEPT, ACCEPT,
HTTP_HEADER_X_REQUESTED_WITH, HTTP_HEADER_X_REQUESTED_WITH,
CONTENT_TYPE, CONTENT_TYPE,
HTTP_HEADER_HA_AUTH,
AUTHORIZATION, AUTHORIZATION,
] ]
VALID_CORS_TYPES = (Resource, ResourceRoute, StaticResource) VALID_CORS_TYPES = (Resource, ResourceRoute, StaticResource)

View File

@ -17,7 +17,6 @@ from homeassistant.const import CONTENT_TYPE_JSON
from homeassistant.core import Context, is_callback from homeassistant.core import Context, is_callback
from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.json import JSONEncoder
from .ban import process_success_login
from .const import KEY_AUTHENTICATED, KEY_HASS, KEY_REAL_IP from .const import KEY_AUTHENTICATED, KEY_HASS, KEY_REAL_IP
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -106,13 +105,8 @@ def request_handler_factory(view, handler):
authenticated = request.get(KEY_AUTHENTICATED, False) authenticated = request.get(KEY_AUTHENTICATED, False)
if view.requires_auth: if view.requires_auth and not authenticated:
if authenticated: raise HTTPUnauthorized()
if "deprecate_warning_message" in request:
_LOGGER.warning(request["deprecate_warning_message"])
await process_success_login(request)
else:
raise HTTPUnauthorized()
_LOGGER.debug( _LOGGER.debug(
"Serving %s to %s (auth: %s)", "Serving %s to %s (auth: %s)",

View File

@ -3,7 +3,6 @@ import voluptuous as vol
from voluptuous.humanize import humanize_error from voluptuous.humanize import humanize_error
from homeassistant.auth.models import RefreshToken, User 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.components.http.ban import process_wrong_login, process_success_login
from homeassistant.const import __version__ from homeassistant.const import __version__
@ -74,19 +73,6 @@ class AuthPhase:
if refresh_token is not None: if refresh_token is not None:
return await self._async_finish_auth(refresh_token.user, refresh_token) 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")) self._send_message(auth_invalid_message("Invalid access token or password"))
await process_wrong_login(self._request) await process_wrong_login(self._request)
raise Disconnect raise Disconnect

View File

@ -468,12 +468,7 @@ def _format_config_error(ex: Exception, domain: str, config: Dict) -> str:
return message return message
async def async_process_ha_core_config( async def async_process_ha_core_config(hass: HomeAssistant, config: Dict) -> None:
hass: HomeAssistant,
config: Dict,
api_password: Optional[str] = None,
trusted_networks: Optional[Any] = None,
) -> None:
"""Process the [homeassistant] section from the configuration. """Process the [homeassistant] section from the configuration.
This method is a coroutine. This method is a coroutine.
@ -486,14 +481,6 @@ async def async_process_ha_core_config(
if auth_conf is None: if auth_conf is None:
auth_conf = [{"type": "homeassistant"}] 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( mfa_conf = config.get(
CONF_AUTH_MFA_MODULES, CONF_AUTH_MFA_MODULES,

View File

@ -451,7 +451,6 @@ HTTP_SERVICE_UNAVAILABLE = 503
HTTP_BASIC_AUTHENTICATION = "basic" HTTP_BASIC_AUTHENTICATION = "basic"
HTTP_DIGEST_AUTHENTICATION = "digest" HTTP_DIGEST_AUTHENTICATION = "digest"
HTTP_HEADER_HA_AUTH = "X-HA-access"
HTTP_HEADER_X_REQUESTED_WITH = "X-Requested-With" HTTP_HEADER_X_REQUESTED_WITH = "X-Requested-With"
CONTENT_TYPE_JSON = "application/json" CONTENT_TYPE_JSON = "application/json"

View File

@ -30,7 +30,7 @@ async def async_setup_auth(
hass, provider_configs, module_configs hass, provider_configs, module_configs
) )
ensure_auth_manager_loaded(hass.auth) 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: if setup_api:
await async_setup_component(hass, "api", {}) await async_setup_component(hass, "api", {})
return await aiohttp_client(hass.http.app) return await aiohttp_client(hass.http.app)

View File

@ -103,7 +103,7 @@ def test_auth_code_store_expiration():
async def test_ws_current_user(hass, hass_ws_client, hass_access_token): async def test_ws_current_user(hass, hass_ws_client, hass_access_token):
"""Test the current user command with homeassistant creds.""" """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) refresh_token = await hass.auth.async_validate_access_token(hass_access_token)
user = refresh_token.user user = refresh_token.user

View File

@ -40,7 +40,9 @@ def hass_ws_client(aiohttp_client, hass_access_token):
assert auth_resp["type"] == TYPE_AUTH_REQUIRED assert auth_resp["type"] == TYPE_AUTH_REQUIRED
if access_token is None: 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: else:
await websocket.send_json( await websocket.send_json(
{"type": TYPE_AUTH, "access_token": access_token} {"type": TYPE_AUTH, "access_token": access_token}

View File

@ -3,7 +3,7 @@
import asyncio import asyncio
import json import json
from aiohttp.hdrs import CONTENT_TYPE, AUTHORIZATION from aiohttp.hdrs import AUTHORIZATION
import pytest import pytest
from homeassistant import core, const, setup from homeassistant import core, const, setup
@ -24,11 +24,6 @@ from . import DEMO_DEVICES
API_PASSWORD = "test1234" API_PASSWORD = "test1234"
HA_HEADERS = {
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
CONTENT_TYPE: const.CONTENT_TYPE_JSON,
}
PROJECT_ID = "hasstest-1234" PROJECT_ID = "hasstest-1234"
CLIENT_ID = "helloworld" CLIENT_ID = "helloworld"
ACCESS_TOKEN = "superdoublesecret" ACCESS_TOKEN = "superdoublesecret"

View File

@ -1,4 +1,3 @@
"""Tests for Hassio component.""" """Tests for Hassio component."""
API_PASSWORD = "pass1234"
HASSIO_TOKEN = "123456" HASSIO_TOKEN = "123456"

View File

@ -9,7 +9,7 @@ from homeassistant.setup import async_setup_component
from homeassistant.components.hassio.handler import HassIO, HassioAPIError from homeassistant.components.hassio.handler import HassIO, HassioAPIError
from tests.common import mock_coro from tests.common import mock_coro
from . import API_PASSWORD, HASSIO_TOKEN from . import HASSIO_TOKEN
@pytest.fixture @pytest.fixture
@ -39,23 +39,19 @@ def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock):
side_effect=HassioAPIError(), side_effect=HassioAPIError(),
): ):
hass.state = CoreState.starting hass.state = CoreState.starting
hass.loop.run_until_complete( hass.loop.run_until_complete(async_setup_component(hass, "hassio", {}))
async_setup_component(
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
)
)
@pytest.fixture @pytest.fixture
def hassio_client(hassio_stubs, hass, hass_client): def hassio_client(hassio_stubs, hass, hass_client):
"""Return a Hass.io HTTP 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 @pytest.fixture
def hassio_noauth_client(hassio_stubs, hass, aiohttp_client): def hassio_noauth_client(hassio_stubs, hass, aiohttp_client):
"""Return a Hass.io HTTP client without auth.""" """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 @pytest.fixture

View File

@ -4,10 +4,8 @@ from unittest.mock import patch, Mock
import pytest import pytest
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.const import HTTP_HEADER_HA_AUTH
from tests.common import mock_coro from tests.common import mock_coro
from . import API_PASSWORD
@pytest.fixture(autouse=True) @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", "homeassistant.components.hassio.addon_panel._register_panel",
Mock(return_value=mock_coro()), Mock(return_value=mock_coro()),
) as mock_panel: ) as mock_panel:
await async_setup_component( await async_setup_component(hass, "hassio", {})
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
)
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count == 3 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", "homeassistant.components.hassio.addon_panel._register_panel",
Mock(return_value=mock_coro()), Mock(return_value=mock_coro()),
) as mock_panel: ) as mock_panel:
await async_setup_component( await async_setup_component(hass, "hassio", {})
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
)
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count == 3 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() hass_client = await hass_client()
resp = await hass_client.post( resp = await hass_client.post("/api/hassio_push/panel/test2")
"/api/hassio_push/panel/test2", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
)
assert resp.status == 400 assert resp.status == 400
resp = await hass_client.post( resp = await hass_client.post("/api/hassio_push/panel/test1")
"/api/hassio_push/panel/test1", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
)
assert resp.status == 200 assert resp.status == 200
assert mock_panel.call_count == 2 assert mock_panel.call_count == 2

View File

@ -1,11 +1,9 @@
"""The tests for the hassio component.""" """The tests for the hassio component."""
from unittest.mock import patch, Mock from unittest.mock import patch, Mock
from homeassistant.const import HTTP_HEADER_HA_AUTH
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from tests.common import mock_coro from tests.common import mock_coro
from . import API_PASSWORD
async def test_login_success(hass, hassio_client): 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( resp = await hassio_client.post(
"/api/hassio_auth", "/api/hassio_auth",
json={"username": "test", "password": "123456", "addon": "samba"}, json={"username": "test", "password": "123456", "addon": "samba"},
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
) )
# Check we got right response # Check we got right response
@ -36,7 +33,6 @@ async def test_login_error(hass, hassio_client):
resp = await hassio_client.post( resp = await hassio_client.post(
"/api/hassio_auth", "/api/hassio_auth",
json={"username": "test", "password": "123456", "addon": "samba"}, json={"username": "test", "password": "123456", "addon": "samba"},
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
) )
# Check we got right response # Check we got right response
@ -51,9 +47,7 @@ async def test_login_no_data(hass, hassio_client):
"HassAuthProvider.async_validate_login", "HassAuthProvider.async_validate_login",
Mock(side_effect=HomeAssistantError()), Mock(side_effect=HomeAssistantError()),
) as mock_login: ) as mock_login:
resp = await hassio_client.post( resp = await hassio_client.post("/api/hassio_auth")
"/api/hassio_auth", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
)
# Check we got right response # Check we got right response
assert resp.status == 400 assert resp.status == 400
@ -68,9 +62,7 @@ async def test_login_no_username(hass, hassio_client):
Mock(side_effect=HomeAssistantError()), Mock(side_effect=HomeAssistantError()),
) as mock_login: ) as mock_login:
resp = await hassio_client.post( resp = await hassio_client.post(
"/api/hassio_auth", "/api/hassio_auth", json={"password": "123456", "addon": "samba"}
json={"password": "123456", "addon": "samba"},
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
) )
# Check we got right response # Check we got right response
@ -93,7 +85,6 @@ async def test_login_success_extra(hass, hassio_client):
"addon": "samba", "addon": "samba",
"path": "/share", "path": "/share",
}, },
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
) )
# Check we got right response # Check we got right response

View File

@ -3,10 +3,9 @@ from unittest.mock import patch, Mock
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.components.hassio.handler import HassioAPIError 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 tests.common import mock_coro
from . import API_PASSWORD
async def test_hassio_discovery_startup(hass, aioclient_mock, hassio_client): 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"})), Mock(return_value=mock_coro({"type": "abort"})),
) as mock_mqtt: ) as mock_mqtt:
await hass.async_start() await hass.async_start()
await async_setup_component( await async_setup_component(hass, "hassio", {})
hass, "hassio", {"http": {"api_password": API_PASSWORD}}
)
await hass.async_block_till_done() await hass.async_block_till_done()
assert aioclient_mock.call_count == 2 assert aioclient_mock.call_count == 2
@ -151,7 +148,6 @@ async def test_hassio_discovery_webhook(hass, aioclient_mock, hassio_client):
) as mock_mqtt: ) as mock_mqtt:
resp = await hassio_client.post( resp = await hassio_client.post(
"/api/hassio_push/discovery/testuuid", "/api/hassio_push/discovery/testuuid",
headers={HTTP_HEADER_HA_AUTH: API_PASSWORD},
json={"addon": "mosquitto", "service": "mqtt", "uuid": "testuuid"}, json={"addon": "mosquitto", "service": "mqtt", "uuid": "testuuid"},
) )
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -4,19 +4,13 @@ from unittest.mock import patch
import pytest import pytest
from homeassistant.const import HTTP_HEADER_HA_AUTH
from . import API_PASSWORD
@asyncio.coroutine @asyncio.coroutine
def test_forward_request(hassio_client, aioclient_mock): def test_forward_request(hassio_client, aioclient_mock):
"""Test fetching normal path.""" """Test fetching normal path."""
aioclient_mock.post("http://127.0.0.1/beer", text="response") aioclient_mock.post("http://127.0.0.1/beer", text="response")
resp = yield from hassio_client.post( resp = yield from hassio_client.post("/api/hassio/beer")
"/api/hassio/beer", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
)
# Check we got right response # Check we got right response
assert resp.status == 200 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.""" """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") aioclient_mock.get("http://127.0.0.1/beer/logs", text="\033[32mresponse\033[0m")
resp = yield from hassio_client.get( resp = yield from hassio_client.get("/api/hassio/beer/logs")
"/api/hassio/beer/logs", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
)
# Check we got right response # Check we got right response
assert resp.status == 200 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", "homeassistant.components.hassio.http.async_timeout.timeout",
side_effect=asyncio.TimeoutError, side_effect=asyncio.TimeoutError,
): ):
resp = yield from hassio_client.get( resp = yield from hassio_client.get("/api/hassio/addons/test/info")
"/api/hassio/addons/test/info", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}
)
assert resp.status == 502 assert resp.status == 502

View File

@ -6,6 +6,10 @@ from aiohttp import web
from homeassistant.components.http.const import KEY_REAL_IP 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): def mock_real_ip(app):
"""Inject middleware to mock real IP. """Inject middleware to mock real IP.

View File

@ -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.auth import setup_auth, async_sign_path
from homeassistant.components.http.const import KEY_AUTHENTICATED from homeassistant.components.http.const import KEY_AUTHENTICATED
from homeassistant.components.http.real_ip import setup_real_ip 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 homeassistant.setup import async_setup_component
from . import mock_real_ip from . import mock_real_ip, HTTP_HEADER_HA_AUTH
API_PASSWORD = "test-password" API_PASSWORD = "test-password"
@ -87,29 +85,29 @@ async def test_auth_middleware_loaded_by_default(hass):
assert len(mock_setup.mock_calls) == 1 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.""" """Test access with password in header."""
setup_auth(hass, app) setup_auth(hass, app)
client = await aiohttp_client(app) client = await aiohttp_client(app)
user = await get_legacy_user(hass.auth)
req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}) req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD})
assert req.status == 200 assert req.status == 401
assert await req.json() == {"user_id": user.id}
req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: "wrong-pass"}) req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: "wrong-pass"})
assert req.status == 401 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.""" """Test access with password in URL."""
setup_auth(hass, app) setup_auth(hass, app)
client = await aiohttp_client(app) client = await aiohttp_client(app)
user = await get_legacy_user(hass.auth)
resp = await client.get("/", params={"api_password": API_PASSWORD}) resp = await client.get("/", params={"api_password": API_PASSWORD})
assert resp.status == 200 assert resp.status == 401
assert await resp.json() == {"user_id": user.id}
resp = await client.get("/") resp = await client.get("/")
assert resp.status == 401 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 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.""" """Test access with basic authentication."""
setup_auth(hass, app) setup_auth(hass, app)
client = await aiohttp_client(app) client = await aiohttp_client(app)
user = await get_legacy_user(hass.auth)
req = await client.get("/", auth=BasicAuth("homeassistant", API_PASSWORD)) req = await client.get("/", auth=BasicAuth("homeassistant", API_PASSWORD))
assert req.status == 200 assert req.status == 401
assert await req.json() == {"user_id": user.id}
req = await client.get("/", auth=BasicAuth("wrong_username", API_PASSWORD)) req = await client.get("/", auth=BasicAuth("wrong_username", API_PASSWORD))
assert req.status == 401 assert req.status == 401
@ -138,7 +134,7 @@ async def test_basic_auth_works(app, aiohttp_client, hass, legacy_auth):
assert req.status == 401 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 hass, app2, trusted_networks_auth, aiohttp_client, hass_owner_user
): ):
"""Test access with an untrusted ip address.""" """Test access with an untrusted ip address."""
@ -155,8 +151,7 @@ async def test_access_with_trusted_ip(
for remote_addr in TRUSTED_ADDRESSES: for remote_addr in TRUSTED_ADDRESSES:
set_mock_ip(remote_addr) set_mock_ip(remote_addr)
resp = await client.get("/") resp = await client.get("/")
assert resp.status == 200, "{} should be trusted".format(remote_addr) assert resp.status == 401, "{} shouldn't be trusted".format(remote_addr)
assert await resp.json() == {"user_id": hass_owner_user.id}
async def test_auth_active_access_with_access_token_in_header( 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: for remote_addr in TRUSTED_ADDRESSES:
set_mock_ip(remote_addr) set_mock_ip(remote_addr)
resp = await client.get("/") resp = await client.get("/")
assert resp.status == 200, "{} should be trusted".format(remote_addr) assert resp.status == 401, "{} shouldn't be trusted".format(remote_addr)
assert await resp.json() == {"user_id": hass_owner_user.id}
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 app, aiohttp_client, legacy_auth, hass
): ):
"""Test access using api_password if auth.support_legacy.""" """Test access using api_password if auth.support_legacy."""
setup_auth(hass, app) setup_auth(hass, app)
client = await aiohttp_client(app) client = await aiohttp_client(app)
user = await get_legacy_user(hass.auth)
req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD}) req = await client.get("/", headers={HTTP_HEADER_HA_AUTH: API_PASSWORD})
assert req.status == 200 assert req.status == 401
assert await req.json() == {"user_id": user.id}
resp = await client.get("/", params={"api_password": API_PASSWORD}) resp = await client.get("/", params={"api_password": API_PASSWORD})
assert resp.status == 200 assert resp.status == 401
assert await resp.json() == {"user_id": user.id}
req = await client.get("/", auth=BasicAuth("homeassistant", API_PASSWORD)) req = await client.get("/", auth=BasicAuth("homeassistant", API_PASSWORD))
assert req.status == 200 assert req.status == 401
assert await req.json() == {"user_id": user.id}
async def test_auth_access_signed_path(hass, app, aiohttp_client, hass_access_token): async def test_auth_access_signed_path(hass, app, aiohttp_client, hass_access_token):

View File

@ -148,6 +148,8 @@ async def test_failed_login_attempts_counter(hass, aiohttp_client):
assert resp.status == 200 assert resp.status == 200
assert app[KEY_FAILED_LOGIN_ATTEMPTS][remote_ip] == 2 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") resp = await client.get("/auth_true")
assert resp.status == 200 assert resp.status == 200
assert remote_ip not in app[KEY_FAILED_LOGIN_ATTEMPTS] assert app[KEY_FAILED_LOGIN_ATTEMPTS][remote_ip] == 2

View File

@ -13,11 +13,12 @@ from aiohttp.hdrs import (
) )
import pytest import pytest
from homeassistant.const import HTTP_HEADER_HA_AUTH
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.components.http.cors import setup_cors from homeassistant.components.http.cors import setup_cors
from homeassistant.components.http.view import HomeAssistantView from homeassistant.components.http.view import HomeAssistantView
from . import HTTP_HEADER_HA_AUTH
TRUSTED_ORIGIN = "https://home-assistant.io" TRUSTED_ORIGIN = "https://home-assistant.io"
@ -91,13 +92,13 @@ async def test_cors_preflight_allowed(client):
headers={ headers={
ORIGIN: TRUSTED_ORIGIN, ORIGIN: TRUSTED_ORIGIN,
ACCESS_CONTROL_REQUEST_METHOD: "GET", 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.status == 200
assert req.headers[ACCESS_CONTROL_ALLOW_ORIGIN] == TRUSTED_ORIGIN 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): async def test_cors_middleware_with_cors_allowed_view(hass):

View File

@ -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"}) resp = await client.get("/api/", params={"api_password": "test-password"})
assert resp.status == 200 assert resp.status == 401
logs = caplog.text logs = caplog.text
# Ensure we don't log API passwords # Ensure we don't log API passwords

View File

@ -57,12 +57,7 @@ class TestMQTT:
self.hass.config.api = MagicMock(api_password="api_password") self.hass.config.api = MagicMock(api_password="api_password")
assert setup_component( assert setup_component(
self.hass, self.hass, mqtt.DOMAIN, {mqtt.DOMAIN: {CONF_PASSWORD: password}}
mqtt.DOMAIN,
{
"http": {"api_password": "http_secret"},
mqtt.DOMAIN: {CONF_PASSWORD: password},
},
) )
self.hass.block_till_done() self.hass.block_till_done()
assert mock_mqtt.called assert mock_mqtt.called

View File

@ -1,2 +1 @@
"""Tests for the websocket API.""" """Tests for the websocket API."""
API_PASSWORD = "test-password"

View File

@ -5,8 +5,6 @@ from homeassistant.setup import async_setup_component
from homeassistant.components.websocket_api.http import URL from homeassistant.components.websocket_api.http import URL
from homeassistant.components.websocket_api.auth import TYPE_AUTH_REQUIRED from homeassistant.components.websocket_api.auth import TYPE_AUTH_REQUIRED
from . import API_PASSWORD
@pytest.fixture @pytest.fixture
def websocket_client(hass, hass_ws_client, hass_access_token): 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 @pytest.fixture
def no_auth_websocket_client(hass, loop, aiohttp_client): def no_auth_websocket_client(hass, loop, aiohttp_client):
"""Websocket connection that requires authentication.""" """Websocket connection that requires authentication."""
assert loop.run_until_complete( assert loop.run_until_complete(async_setup_component(hass, "websocket_api", {}))
async_setup_component(
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
)
)
client = loop.run_until_complete(aiohttp_client(hass.http.app)) client = loop.run_until_complete(aiohttp_client(hass.http.app))
ws = loop.run_until_complete(client.ws_connect(URL)) ws = loop.run_until_complete(client.ws_connect(URL))

View File

@ -17,21 +17,10 @@ from homeassistant.setup import async_setup_component
from tests.common import mock_coro from tests.common import mock_coro
from . import API_PASSWORD
async def test_auth_events(
async def test_auth_via_msg(no_auth_websocket_client, legacy_auth): hass, no_auth_websocket_client, legacy_auth, hass_access_token
"""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):
"""Test authenticating.""" """Test authenticating."""
connected_evt = [] connected_evt = []
hass.helpers.dispatcher.async_dispatcher_connect( 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) 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 len(connected_evt) == 1
assert not disconnected_evt assert not disconnected_evt
@ -60,7 +49,7 @@ async def test_auth_via_msg_incorrect_pass(no_auth_websocket_client):
return_value=mock_coro(), return_value=mock_coro(),
) as mock_process_wrong_login: ) as mock_process_wrong_login:
await no_auth_websocket_client.send_json( 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() 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") 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.""" """Test authenticating with a token."""
assert await async_setup_component( assert await async_setup_component(hass, "websocket_api", {})
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
await no_auth_websocket_client.send_json(
{"type": TYPE_AUTH, "access_token": hass_access_token}
) )
client = await aiohttp_client(hass.http.app) auth_msg = await no_auth_websocket_client.receive_json()
assert auth_msg["type"] == TYPE_AUTH_OK
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
async def test_auth_active_user_inactive(hass, aiohttp_client, hass_access_token): async def test_auth_active_user_inactive(hass, aiohttp_client, hass_access_token):
"""Test authenticating with a token.""" """Test authenticating with a token."""
refresh_token = await hass.auth.async_validate_access_token(hass_access_token) refresh_token = await hass.auth.async_validate_access_token(hass_access_token)
refresh_token.user.is_active = False refresh_token.user.is_active = False
assert await async_setup_component( assert await async_setup_component(hass, "websocket_api", {})
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
)
client = await aiohttp_client(hass.http.app) 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): async def test_auth_active_with_password_not_allow(hass, aiohttp_client):
"""Test authenticating with a token.""" """Test authenticating with a token."""
assert await async_setup_component( assert await async_setup_component(hass, "websocket_api", {})
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
)
client = await aiohttp_client(hass.http.app) 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() auth_msg = await ws.receive_json()
assert auth_msg["type"] == TYPE_AUTH_REQUIRED 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() auth_msg = await ws.receive_json()
assert auth_msg["type"] == TYPE_AUTH_INVALID 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): async def test_auth_legacy_support_with_password(hass, aiohttp_client, legacy_auth):
"""Test authenticating with a token.""" """Test authenticating with a token."""
assert await async_setup_component( assert await async_setup_component(hass, "websocket_api", {})
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
)
client = await aiohttp_client(hass.http.app) client = await aiohttp_client(hass.http.app)
async with client.ws_connect(URL) as ws: async with client.ws_connect(URL) as ws:
with patch("homeassistant.auth.AuthManager.support_legacy", return_value=True): auth_msg = await ws.receive_json()
auth_msg = await ws.receive_json() assert auth_msg["type"] == TYPE_AUTH_REQUIRED
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() auth_msg = await ws.receive_json()
assert auth_msg["type"] == TYPE_AUTH_OK assert auth_msg["type"] == TYPE_AUTH_INVALID
async def test_auth_with_invalid_token(hass, aiohttp_client): async def test_auth_with_invalid_token(hass, aiohttp_client):
"""Test authenticating with a token.""" """Test authenticating with a token."""
assert await async_setup_component( assert await async_setup_component(hass, "websocket_api", {})
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
)
client = await aiohttp_client(hass.http.app) client = await aiohttp_client(hass.http.app)

View File

@ -14,8 +14,6 @@ from homeassistant.setup import async_setup_component
from tests.common import async_mock_service from tests.common import async_mock_service
from . import API_PASSWORD
async def test_call_service(hass, websocket_client): async def test_call_service(hass, websocket_client):
"""Test call service command.""" """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): 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.""" """Test that the user is set in the service call context."""
assert await async_setup_component( assert await async_setup_component(hass, "websocket_api", {})
hass, "websocket_api", {"http": {"api_password": API_PASSWORD}}
)
calls = async_mock_service(hass, "domain_test", "test_service") calls = async_mock_service(hass, "domain_test", "test_service")
client = await aiohttp_client(hass.http.app) client = await aiohttp_client(hass.http.app)

View File

@ -3,10 +3,12 @@
from homeassistant.bootstrap import async_setup_component from homeassistant.bootstrap import async_setup_component
from tests.common import assert_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.""" """Test API streams."""
with assert_setup_component(1): with assert_setup_component(1):
await async_setup_component( 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") state = hass.states.get("sensor.connected_clients")
assert state.state == "0" 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") state = hass.states.get("sensor.connected_clients")
assert state.state == "1" assert state.state == "1"

View File

@ -92,8 +92,8 @@ def test_secrets(isfile_patch, loop):
files = { files = {
get_test_config_dir(YAML_CONFIG_FILE): BASE_CONFIG get_test_config_dir(YAML_CONFIG_FILE): BASE_CONFIG
+ ("http:\n" " api_password: !secret http_pw"), + ("http:\n" " cors_allowed_origins: !secret http_pw"),
secrets_path: ("logger: debug\n" "http_pw: abc123"), secrets_path: ("logger: debug\n" "http_pw: http://google.com"),
} }
with patch_yaml_files(files): with patch_yaml_files(files):
@ -103,17 +103,15 @@ def test_secrets(isfile_patch, loop):
assert res["except"] == {} assert res["except"] == {}
assert res["components"].keys() == {"homeassistant", "http"} assert res["components"].keys() == {"homeassistant", "http"}
assert res["components"]["http"] == { assert res["components"]["http"] == {
"api_password": "abc123", "cors_allowed_origins": ["http://google.com"],
"cors_allowed_origins": ["https://cast.home-assistant.io"],
"ip_ban_enabled": True, "ip_ban_enabled": True,
"login_attempts_threshold": -1, "login_attempts_threshold": -1,
"server_host": "0.0.0.0", "server_host": "0.0.0.0",
"server_port": 8123, "server_port": 8123,
"trusted_networks": [],
"ssl_profile": "modern", "ssl_profile": "modern",
} }
assert res["secret_cache"] == {secrets_path: {"http_pw": "abc123"}} assert res["secret_cache"] == {secrets_path: {"http_pw": "http://google.com"}}
assert res["secrets"] == {"http_pw": "abc123"} assert res["secrets"] == {"http_pw": "http://google.com"}
assert normalize_yaml_files(res) == [ assert normalize_yaml_files(res) == [
".../configuration.yaml", ".../configuration.yaml",
".../secrets.yaml", ".../secrets.yaml",

View File

@ -5,7 +5,6 @@ import copy
import os import os
import unittest.mock as mock import unittest.mock as mock
from collections import OrderedDict from collections import OrderedDict
from ipaddress import ip_network
import asynctest import asynctest
import pytest import pytest
@ -876,48 +875,6 @@ async def test_auth_provider_config_default(hass):
assert hass.auth.auth_mfa_modules[0].id == "totp" 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): async def test_disallowed_auth_provider_config(hass):
"""Test loading insecure example auth provider is disallowed.""" """Test loading insecure example auth provider is disallowed."""
core_config = { core_config = {