parent
ae9e3d83d7
commit
d1a621601d
42 changed files with 307 additions and 473 deletions
|
@ -78,11 +78,6 @@ class AuthManager:
|
||||||
hass, self._async_create_login_flow,
|
hass, self._async_create_login_flow,
|
||||||
self._async_finish_login_flow)
|
self._async_finish_login_flow)
|
||||||
|
|
||||||
@property
|
|
||||||
def active(self) -> bool:
|
|
||||||
"""Return if any auth providers are registered."""
|
|
||||||
return bool(self._providers)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def support_legacy(self) -> bool:
|
def support_legacy(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -14,6 +14,8 @@ from homeassistant.util.yaml import load_yaml, dump
|
||||||
DOMAIN = 'config'
|
DOMAIN = 'config'
|
||||||
DEPENDENCIES = ['http']
|
DEPENDENCIES = ['http']
|
||||||
SECTIONS = (
|
SECTIONS = (
|
||||||
|
'auth',
|
||||||
|
'auth_provider_homeassistant',
|
||||||
'automation',
|
'automation',
|
||||||
'config_entries',
|
'config_entries',
|
||||||
'core',
|
'core',
|
||||||
|
@ -58,10 +60,6 @@ async def async_setup(hass, config):
|
||||||
|
|
||||||
tasks = [setup_panel(panel_name) for panel_name in SECTIONS]
|
tasks = [setup_panel(panel_name) for panel_name in SECTIONS]
|
||||||
|
|
||||||
if hass.auth.active:
|
|
||||||
tasks.append(setup_panel('auth'))
|
|
||||||
tasks.append(setup_panel('auth_provider_homeassistant'))
|
|
||||||
|
|
||||||
for panel_name in ON_DEMAND:
|
for panel_name in ON_DEMAND:
|
||||||
if panel_name in hass.config.components:
|
if panel_name in hass.config.components:
|
||||||
tasks.append(setup_panel(panel_name))
|
tasks.append(setup_panel(panel_name))
|
||||||
|
|
|
@ -238,7 +238,7 @@ async def async_setup(hass, config):
|
||||||
if os.path.isdir(local):
|
if os.path.isdir(local):
|
||||||
hass.http.register_static_path("/local", local, not is_dev)
|
hass.http.register_static_path("/local", local, not is_dev)
|
||||||
|
|
||||||
index_view = IndexView(repo_path, js_version, hass.auth.active)
|
index_view = IndexView(repo_path, js_version)
|
||||||
hass.http.register_view(index_view)
|
hass.http.register_view(index_view)
|
||||||
hass.http.register_view(AuthorizeView(repo_path, js_version))
|
hass.http.register_view(AuthorizeView(repo_path, js_version))
|
||||||
|
|
||||||
|
@ -364,11 +364,10 @@ class IndexView(HomeAssistantView):
|
||||||
requires_auth = False
|
requires_auth = False
|
||||||
extra_urls = ['/states', '/states/{extra}']
|
extra_urls = ['/states', '/states/{extra}']
|
||||||
|
|
||||||
def __init__(self, repo_path, js_option, auth_active):
|
def __init__(self, repo_path, js_option):
|
||||||
"""Initialize the frontend view."""
|
"""Initialize the frontend view."""
|
||||||
self.repo_path = repo_path
|
self.repo_path = repo_path
|
||||||
self.js_option = js_option
|
self.js_option = js_option
|
||||||
self.auth_active = auth_active
|
|
||||||
self._template_cache = {}
|
self._template_cache = {}
|
||||||
|
|
||||||
def get_template(self, latest):
|
def get_template(self, latest):
|
||||||
|
@ -415,8 +414,6 @@ class IndexView(HomeAssistantView):
|
||||||
# do not try to auto connect on load
|
# do not try to auto connect on load
|
||||||
no_auth = '0'
|
no_auth = '0'
|
||||||
|
|
||||||
use_oauth = '1' if self.auth_active else '0'
|
|
||||||
|
|
||||||
template = await hass.async_add_job(self.get_template, latest)
|
template = await hass.async_add_job(self.get_template, latest)
|
||||||
|
|
||||||
extra_key = DATA_EXTRA_HTML_URL if latest else DATA_EXTRA_HTML_URL_ES5
|
extra_key = DATA_EXTRA_HTML_URL if latest else DATA_EXTRA_HTML_URL_ES5
|
||||||
|
@ -425,7 +422,7 @@ class IndexView(HomeAssistantView):
|
||||||
no_auth=no_auth,
|
no_auth=no_auth,
|
||||||
theme_color=MANIFEST_JSON['theme_color'],
|
theme_color=MANIFEST_JSON['theme_color'],
|
||||||
extra_urls=hass.data[extra_key],
|
extra_urls=hass.data[extra_key],
|
||||||
use_oauth=use_oauth
|
use_oauth='1'
|
||||||
)
|
)
|
||||||
|
|
||||||
return web.Response(text=template.render(**template_params),
|
return web.Response(text=template.render(**template_params),
|
||||||
|
|
|
@ -213,13 +213,7 @@ async def async_setup(hass, config):
|
||||||
embed_iframe=True,
|
embed_iframe=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Temporary. No refresh token tells supervisor to use API password.
|
await hassio.update_hass_api(config.get('http', {}), refresh_token.token)
|
||||||
if hass.auth.active:
|
|
||||||
token = refresh_token.token
|
|
||||||
else:
|
|
||||||
token = None
|
|
||||||
|
|
||||||
await hassio.update_hass_api(config.get('http', {}), token)
|
|
||||||
|
|
||||||
if 'homeassistant' in config:
|
if 'homeassistant' in config:
|
||||||
await hassio.update_hass_timezone(config['homeassistant'])
|
await hassio.update_hass_timezone(config['homeassistant'])
|
||||||
|
|
|
@ -200,14 +200,13 @@ class HomeAssistantHTTP:
|
||||||
if is_ban_enabled:
|
if is_ban_enabled:
|
||||||
setup_bans(hass, app, login_threshold)
|
setup_bans(hass, app, login_threshold)
|
||||||
|
|
||||||
if hass.auth.active and hass.auth.support_legacy:
|
if hass.auth.support_legacy:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"legacy_api_password support has been enabled. If you don't "
|
"legacy_api_password support has been enabled. If you don't "
|
||||||
"require it, remove the 'api_password' from your http config.")
|
"require it, remove the 'api_password' from your http config.")
|
||||||
|
|
||||||
setup_auth(app, trusted_networks, hass.auth.active,
|
setup_auth(app, trusted_networks,
|
||||||
support_legacy=hass.auth.support_legacy,
|
api_password if hass.auth.support_legacy else None)
|
||||||
api_password=api_password)
|
|
||||||
|
|
||||||
setup_cors(app, cors_origins)
|
setup_cors(app, cors_origins)
|
||||||
|
|
||||||
|
|
|
@ -41,29 +41,26 @@ def async_sign_path(hass, refresh_token_id, path, expiration):
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def setup_auth(app, trusted_networks, use_auth,
|
def setup_auth(app, trusted_networks, api_password):
|
||||||
support_legacy=False, api_password=None):
|
|
||||||
"""Create auth middleware for the app."""
|
"""Create auth middleware for the app."""
|
||||||
old_auth_warning = set()
|
old_auth_warning = set()
|
||||||
legacy_auth = (not use_auth or support_legacy) and api_password
|
|
||||||
|
|
||||||
@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 use_auth and (HTTP_HEADER_HA_AUTH in request.headers or
|
if (HTTP_HEADER_HA_AUTH in request.headers or
|
||||||
DATA_API_PASSWORD in request.query):
|
DATA_API_PASSWORD in request.query):
|
||||||
if request.path not in old_auth_warning:
|
if request.path not in old_auth_warning:
|
||||||
_LOGGER.log(
|
_LOGGER.log(
|
||||||
logging.INFO if support_legacy else logging.WARNING,
|
logging.INFO if api_password else logging.WARNING,
|
||||||
'You need to use a bearer token to access %s from %s',
|
'You need to use a bearer token to access %s from %s',
|
||||||
request.path, request[KEY_REAL_IP])
|
request.path, request[KEY_REAL_IP])
|
||||||
old_auth_warning.add(request.path)
|
old_auth_warning.add(request.path)
|
||||||
|
|
||||||
if (hdrs.AUTHORIZATION in request.headers and
|
if (hdrs.AUTHORIZATION in request.headers and
|
||||||
await async_validate_auth_header(
|
await async_validate_auth_header(request, api_password)):
|
||||||
request, api_password if legacy_auth else None)):
|
|
||||||
# it included both use_auth and api_password Basic auth
|
# it included both use_auth and api_password Basic auth
|
||||||
authenticated = True
|
authenticated = True
|
||||||
|
|
||||||
|
@ -73,7 +70,7 @@ def setup_auth(app, trusted_networks, use_auth,
|
||||||
await async_validate_signed_request(request)):
|
await async_validate_signed_request(request)):
|
||||||
authenticated = True
|
authenticated = True
|
||||||
|
|
||||||
elif (legacy_auth and HTTP_HEADER_HA_AUTH in request.headers and
|
elif (api_password and HTTP_HEADER_HA_AUTH in request.headers and
|
||||||
hmac.compare_digest(
|
hmac.compare_digest(
|
||||||
api_password.encode('utf-8'),
|
api_password.encode('utf-8'),
|
||||||
request.headers[HTTP_HEADER_HA_AUTH].encode('utf-8'))):
|
request.headers[HTTP_HEADER_HA_AUTH].encode('utf-8'))):
|
||||||
|
@ -82,7 +79,7 @@ def setup_auth(app, trusted_networks, use_auth,
|
||||||
request['hass_user'] = await legacy_api_password.async_get_user(
|
request['hass_user'] = await legacy_api_password.async_get_user(
|
||||||
app['hass'])
|
app['hass'])
|
||||||
|
|
||||||
elif (legacy_auth and DATA_API_PASSWORD in request.query and
|
elif (api_password and DATA_API_PASSWORD in request.query and
|
||||||
hmac.compare_digest(
|
hmac.compare_digest(
|
||||||
api_password.encode('utf-8'),
|
api_password.encode('utf-8'),
|
||||||
request.query[DATA_API_PASSWORD].encode('utf-8'))):
|
request.query[DATA_API_PASSWORD].encode('utf-8'))):
|
||||||
|
@ -98,11 +95,6 @@ def setup_auth(app, trusted_networks, use_auth,
|
||||||
break
|
break
|
||||||
authenticated = True
|
authenticated = True
|
||||||
|
|
||||||
elif not use_auth and api_password is None:
|
|
||||||
# If neither password nor auth_providers set,
|
|
||||||
# just always set authenticated=True
|
|
||||||
authenticated = True
|
|
||||||
|
|
||||||
request[KEY_AUTHENTICATED] = authenticated
|
request[KEY_AUTHENTICATED] = authenticated
|
||||||
return await handler(request)
|
return await handler(request)
|
||||||
|
|
||||||
|
|
|
@ -242,7 +242,7 @@ class HTML5PushCallbackView(HomeAssistantView):
|
||||||
# 2b. If decode is unsuccessful, return a 401.
|
# 2b. If decode is unsuccessful, return a 401.
|
||||||
|
|
||||||
target_check = jwt.decode(token, verify=False)
|
target_check = jwt.decode(token, verify=False)
|
||||||
if target_check[ATTR_TARGET] in self.registrations:
|
if target_check.get(ATTR_TARGET) in self.registrations:
|
||||||
possible_target = self.registrations[target_check[ATTR_TARGET]]
|
possible_target = self.registrations[target_check[ATTR_TARGET]]
|
||||||
key = possible_target[ATTR_SUBSCRIPTION][ATTR_KEYS][ATTR_AUTH]
|
key = possible_target[ATTR_SUBSCRIPTION][ATTR_KEYS][ATTR_AUTH]
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -14,10 +14,6 @@ STORAGE_VERSION = 1
|
||||||
@callback
|
@callback
|
||||||
def async_is_onboarded(hass):
|
def async_is_onboarded(hass):
|
||||||
"""Return if Home Assistant has been onboarded."""
|
"""Return if Home Assistant has been onboarded."""
|
||||||
# Temporarily: if auth not active, always set onboarded=True
|
|
||||||
if not hass.auth.active:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return hass.data.get(DOMAIN, True)
|
return hass.data.get(DOMAIN, True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ class AuthPhase:
|
||||||
self._send_message(auth_invalid_message(error_msg))
|
self._send_message(auth_invalid_message(error_msg))
|
||||||
raise Disconnect
|
raise Disconnect
|
||||||
|
|
||||||
if self._hass.auth.active and 'access_token' in msg:
|
if 'access_token' in msg:
|
||||||
self._logger.debug("Received access_token")
|
self._logger.debug("Received access_token")
|
||||||
refresh_token = \
|
refresh_token = \
|
||||||
await self._hass.auth.async_validate_access_token(
|
await self._hass.auth.async_validate_access_token(
|
||||||
|
@ -78,8 +78,7 @@ class AuthPhase:
|
||||||
return await self._async_finish_auth(
|
return await self._async_finish_auth(
|
||||||
refresh_token.user, refresh_token)
|
refresh_token.user, refresh_token)
|
||||||
|
|
||||||
elif ((not self._hass.auth.active or self._hass.auth.support_legacy)
|
elif self._hass.auth.support_legacy and 'api_password' in msg:
|
||||||
and 'api_password' in msg):
|
|
||||||
self._logger.debug("Received api_password")
|
self._logger.debug("Received api_password")
|
||||||
if validate_password(self._request, msg['api_password']):
|
if validate_password(self._request, msg['api_password']):
|
||||||
return await self._async_finish_auth(None, None)
|
return await self._async_finish_auth(None, None)
|
||||||
|
|
|
@ -21,7 +21,7 @@ NPR_NEWS_MP3_URL = "https://pd.npr.org/anon.npr-mp3/npr/news/newscast.mp3"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def alexa_client(loop, hass, aiohttp_client):
|
def alexa_client(loop, hass, hass_client):
|
||||||
"""Initialize a Home Assistant server for testing this module."""
|
"""Initialize a Home Assistant server for testing this module."""
|
||||||
@callback
|
@callback
|
||||||
def mock_service(call):
|
def mock_service(call):
|
||||||
|
@ -49,7 +49,7 @@ def alexa_client(loop, hass, aiohttp_client):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
return loop.run_until_complete(aiohttp_client(hass.http.app))
|
return loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
def _flash_briefing_req(client, briefing_id):
|
def _flash_briefing_req(client, briefing_id):
|
||||||
|
|
|
@ -114,8 +114,7 @@ async def test_ws_current_user(hass, hass_ws_client, hass_access_token):
|
||||||
user.credentials.append(credential)
|
user.credentials.append(credential)
|
||||||
assert len(user.credentials) == 1
|
assert len(user.credentials) == 1
|
||||||
|
|
||||||
with patch('homeassistant.auth.AuthManager.active', return_value=True):
|
client = await hass_ws_client(hass, hass_access_token)
|
||||||
client = await hass_ws_client(hass, hass_access_token)
|
|
||||||
|
|
||||||
await client.send_json({
|
await client.send_json({
|
||||||
'id': 5,
|
'id': 5,
|
||||||
|
|
|
@ -5,11 +5,11 @@ from homeassistant.bootstrap import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
|
|
||||||
async def test_events_http_api(hass, aiohttp_client):
|
async def test_events_http_api(hass, hass_client):
|
||||||
"""Test the calendar demo view."""
|
"""Test the calendar demo view."""
|
||||||
await async_setup_component(hass, 'calendar',
|
await async_setup_component(hass, 'calendar',
|
||||||
{'calendar': {'platform': 'demo'}})
|
{'calendar': {'platform': 'demo'}})
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await hass_client()
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
'/api/calendars/calendar.calendar_2')
|
'/api/calendars/calendar.calendar_2')
|
||||||
assert response.status == 400
|
assert response.status == 400
|
||||||
|
@ -24,11 +24,11 @@ async def test_events_http_api(hass, aiohttp_client):
|
||||||
assert events[0]['title'] == 'Future Event'
|
assert events[0]['title'] == 'Future Event'
|
||||||
|
|
||||||
|
|
||||||
async def test_calendars_http_api(hass, aiohttp_client):
|
async def test_calendars_http_api(hass, hass_client):
|
||||||
"""Test the calendar demo view."""
|
"""Test the calendar demo view."""
|
||||||
await async_setup_component(hass, 'calendar',
|
await async_setup_component(hass, 'calendar',
|
||||||
{'calendar': {'platform': 'demo'}})
|
{'calendar': {'platform': 'demo'}})
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await hass_client()
|
||||||
response = await client.get('/api/calendars')
|
response = await client.get('/api/calendars')
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
|
|
|
@ -7,7 +7,7 @@ from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_fetching_url(aioclient_mock, hass, aiohttp_client):
|
def test_fetching_url(aioclient_mock, hass, hass_client):
|
||||||
"""Test that it fetches the given url."""
|
"""Test that it fetches the given url."""
|
||||||
aioclient_mock.get('http://example.com', text='hello world')
|
aioclient_mock.get('http://example.com', text='hello world')
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ def test_fetching_url(aioclient_mock, hass, aiohttp_client):
|
||||||
'password': 'pass'
|
'password': 'pass'
|
||||||
}})
|
}})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ def test_fetching_url(aioclient_mock, hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_fetching_without_verify_ssl(aioclient_mock, hass, aiohttp_client):
|
def test_fetching_without_verify_ssl(aioclient_mock, hass, hass_client):
|
||||||
"""Test that it fetches the given url when ssl verify is off."""
|
"""Test that it fetches the given url when ssl verify is off."""
|
||||||
aioclient_mock.get('https://example.com', text='hello world')
|
aioclient_mock.get('https://example.com', text='hello world')
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ def test_fetching_without_verify_ssl(aioclient_mock, hass, aiohttp_client):
|
||||||
'verify_ssl': 'false',
|
'verify_ssl': 'false',
|
||||||
}})
|
}})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ def test_fetching_without_verify_ssl(aioclient_mock, hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_fetching_url_with_verify_ssl(aioclient_mock, hass, aiohttp_client):
|
def test_fetching_url_with_verify_ssl(aioclient_mock, hass, hass_client):
|
||||||
"""Test that it fetches the given url when ssl verify is explicitly on."""
|
"""Test that it fetches the given url when ssl verify is explicitly on."""
|
||||||
aioclient_mock.get('https://example.com', text='hello world')
|
aioclient_mock.get('https://example.com', text='hello world')
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ def test_fetching_url_with_verify_ssl(aioclient_mock, hass, aiohttp_client):
|
||||||
'verify_ssl': 'true',
|
'verify_ssl': 'true',
|
||||||
}})
|
}})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ def test_fetching_url_with_verify_ssl(aioclient_mock, hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_limit_refetch(aioclient_mock, hass, aiohttp_client):
|
def test_limit_refetch(aioclient_mock, hass, hass_client):
|
||||||
"""Test that it fetches the given url."""
|
"""Test that it fetches the given url."""
|
||||||
aioclient_mock.get('http://example.com/5a', text='hello world')
|
aioclient_mock.get('http://example.com/5a', text='hello world')
|
||||||
aioclient_mock.get('http://example.com/10a', text='hello world')
|
aioclient_mock.get('http://example.com/10a', text='hello world')
|
||||||
|
@ -94,7 +94,7 @@ def test_limit_refetch(aioclient_mock, hass, aiohttp_client):
|
||||||
'limit_refetch_to_url_change': True,
|
'limit_refetch_to_url_change': True,
|
||||||
}})
|
}})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
resp = yield from client.get('/api/camera_proxy/camera.config_test')
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ def test_limit_refetch(aioclient_mock, hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_camera_content_type(aioclient_mock, hass, aiohttp_client):
|
def test_camera_content_type(aioclient_mock, hass, hass_client):
|
||||||
"""Test generic camera with custom content_type."""
|
"""Test generic camera with custom content_type."""
|
||||||
svg_image = '<some image>'
|
svg_image = '<some image>'
|
||||||
urlsvg = 'https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg'
|
urlsvg = 'https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg'
|
||||||
|
@ -158,7 +158,7 @@ def test_camera_content_type(aioclient_mock, hass, aiohttp_client):
|
||||||
yield from async_setup_component(hass, 'camera', {
|
yield from async_setup_component(hass, 'camera', {
|
||||||
'camera': [cam_config_svg, cam_config_normal]})
|
'camera': [cam_config_svg, cam_config_normal]})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp_1 = yield from client.get('/api/camera_proxy/camera.config_test_svg')
|
resp_1 = yield from client.get('/api/camera_proxy/camera.config_test_svg')
|
||||||
assert aioclient_mock.call_count == 1
|
assert aioclient_mock.call_count == 1
|
||||||
|
|
|
@ -11,7 +11,7 @@ from tests.common import mock_registry
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_loading_file(hass, aiohttp_client):
|
def test_loading_file(hass, hass_client):
|
||||||
"""Test that it loads image from disk."""
|
"""Test that it loads image from disk."""
|
||||||
mock_registry(hass)
|
mock_registry(hass)
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ def test_loading_file(hass, aiohttp_client):
|
||||||
'file_path': 'mock.file',
|
'file_path': 'mock.file',
|
||||||
}})
|
}})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
m_open = mock.mock_open(read_data=b'hello')
|
m_open = mock.mock_open(read_data=b'hello')
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
|
@ -56,7 +56,7 @@ def test_file_not_readable(hass, caplog):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_camera_content_type(hass, aiohttp_client):
|
def test_camera_content_type(hass, hass_client):
|
||||||
"""Test local_file camera content_type."""
|
"""Test local_file camera content_type."""
|
||||||
cam_config_jpg = {
|
cam_config_jpg = {
|
||||||
'name': 'test_jpg',
|
'name': 'test_jpg',
|
||||||
|
@ -83,7 +83,7 @@ def test_camera_content_type(hass, aiohttp_client):
|
||||||
'camera': [cam_config_jpg, cam_config_png,
|
'camera': [cam_config_jpg, cam_config_png,
|
||||||
cam_config_svg, cam_config_noext]})
|
cam_config_svg, cam_config_noext]})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
image = 'hello'
|
image = 'hello'
|
||||||
m_open = mock.mock_open(read_data=image.encode())
|
m_open = mock.mock_open(read_data=image.encode())
|
||||||
|
|
|
@ -52,10 +52,10 @@ def setup_api(hass):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def cloud_client(hass, aiohttp_client):
|
def cloud_client(hass, hass_client):
|
||||||
"""Fixture that can fetch from the cloud client."""
|
"""Fixture that can fetch from the cloud client."""
|
||||||
with patch('homeassistant.components.cloud.Cloud.write_user_info'):
|
with patch('homeassistant.components.cloud.Cloud.write_user_info'):
|
||||||
yield hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
yield hass.loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
"""Test config entries API."""
|
"""Test config entries API."""
|
||||||
from unittest.mock import PropertyMock, patch
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.auth import models as auth_models
|
from homeassistant.auth import models as auth_models
|
||||||
|
@ -9,14 +7,6 @@ from homeassistant.components.config import auth as auth_config
|
||||||
from tests.common import MockGroup, MockUser, CLIENT_ID
|
from tests.common import MockGroup, MockUser, CLIENT_ID
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def auth_active(hass):
|
|
||||||
"""Mock that auth is active."""
|
|
||||||
with patch('homeassistant.auth.AuthManager.active',
|
|
||||||
PropertyMock(return_value=True)):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def setup_config(hass, aiohttp_client):
|
def setup_config(hass, aiohttp_client):
|
||||||
"""Fixture that sets up the auth provider homeassistant module."""
|
"""Fixture that sets up the auth provider homeassistant module."""
|
||||||
|
@ -37,7 +27,7 @@ async def test_list_requires_owner(hass, hass_ws_client, hass_access_token):
|
||||||
assert result['error']['code'] == 'unauthorized'
|
assert result['error']['code'] == 'unauthorized'
|
||||||
|
|
||||||
|
|
||||||
async def test_list(hass, hass_ws_client):
|
async def test_list(hass, hass_ws_client, hass_admin_user):
|
||||||
"""Test get users."""
|
"""Test get users."""
|
||||||
group = MockGroup().add_to_hass(hass)
|
group = MockGroup().add_to_hass(hass)
|
||||||
|
|
||||||
|
@ -80,8 +70,17 @@ async def test_list(hass, hass_ws_client):
|
||||||
result = await client.receive_json()
|
result = await client.receive_json()
|
||||||
assert result['success'], result
|
assert result['success'], result
|
||||||
data = result['result']
|
data = result['result']
|
||||||
assert len(data) == 3
|
assert len(data) == 4
|
||||||
assert data[0] == {
|
assert data[0] == {
|
||||||
|
'id': hass_admin_user.id,
|
||||||
|
'name': 'Mock User',
|
||||||
|
'is_owner': False,
|
||||||
|
'is_active': True,
|
||||||
|
'system_generated': False,
|
||||||
|
'group_ids': [group.id for group in hass_admin_user.groups],
|
||||||
|
'credentials': []
|
||||||
|
}
|
||||||
|
assert data[1] == {
|
||||||
'id': owner.id,
|
'id': owner.id,
|
||||||
'name': 'Test Owner',
|
'name': 'Test Owner',
|
||||||
'is_owner': True,
|
'is_owner': True,
|
||||||
|
@ -90,7 +89,7 @@ async def test_list(hass, hass_ws_client):
|
||||||
'group_ids': [group.id for group in owner.groups],
|
'group_ids': [group.id for group in owner.groups],
|
||||||
'credentials': [{'type': 'homeassistant'}]
|
'credentials': [{'type': 'homeassistant'}]
|
||||||
}
|
}
|
||||||
assert data[1] == {
|
assert data[2] == {
|
||||||
'id': system.id,
|
'id': system.id,
|
||||||
'name': 'Test Hass.io',
|
'name': 'Test Hass.io',
|
||||||
'is_owner': False,
|
'is_owner': False,
|
||||||
|
@ -99,7 +98,7 @@ async def test_list(hass, hass_ws_client):
|
||||||
'group_ids': [],
|
'group_ids': [],
|
||||||
'credentials': [],
|
'credentials': [],
|
||||||
}
|
}
|
||||||
assert data[2] == {
|
assert data[3] == {
|
||||||
'id': inactive.id,
|
'id': inactive.id,
|
||||||
'name': 'Inactive User',
|
'name': 'Inactive User',
|
||||||
'is_owner': False,
|
'is_owner': False,
|
||||||
|
|
|
@ -6,12 +6,12 @@ from homeassistant.bootstrap import async_setup_component
|
||||||
from homeassistant.components import config
|
from homeassistant.components import config
|
||||||
|
|
||||||
|
|
||||||
async def test_get_device_config(hass, aiohttp_client):
|
async def test_get_device_config(hass, hass_client):
|
||||||
"""Test getting device config."""
|
"""Test getting device config."""
|
||||||
with patch.object(config, 'SECTIONS', ['automation']):
|
with patch.object(config, 'SECTIONS', ['automation']):
|
||||||
await async_setup_component(hass, 'config', {})
|
await async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await hass_client()
|
||||||
|
|
||||||
def mock_read(path):
|
def mock_read(path):
|
||||||
"""Mock reading data."""
|
"""Mock reading data."""
|
||||||
|
@ -34,12 +34,12 @@ async def test_get_device_config(hass, aiohttp_client):
|
||||||
assert result == {'id': 'moon'}
|
assert result == {'id': 'moon'}
|
||||||
|
|
||||||
|
|
||||||
async def test_update_device_config(hass, aiohttp_client):
|
async def test_update_device_config(hass, hass_client):
|
||||||
"""Test updating device config."""
|
"""Test updating device config."""
|
||||||
with patch.object(config, 'SECTIONS', ['automation']):
|
with patch.object(config, 'SECTIONS', ['automation']):
|
||||||
await async_setup_component(hass, 'config', {})
|
await async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await hass_client()
|
||||||
|
|
||||||
orig_data = [
|
orig_data = [
|
||||||
{
|
{
|
||||||
|
@ -83,12 +83,12 @@ async def test_update_device_config(hass, aiohttp_client):
|
||||||
assert written[0] == orig_data
|
assert written[0] == orig_data
|
||||||
|
|
||||||
|
|
||||||
async def test_bad_formatted_automations(hass, aiohttp_client):
|
async def test_bad_formatted_automations(hass, hass_client):
|
||||||
"""Test that we handle automations without ID."""
|
"""Test that we handle automations without ID."""
|
||||||
with patch.object(config, 'SECTIONS', ['automation']):
|
with patch.object(config, 'SECTIONS', ['automation']):
|
||||||
await async_setup_component(hass, 'config', {})
|
await async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await hass_client()
|
||||||
|
|
||||||
orig_data = [
|
orig_data = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,11 +23,11 @@ def mock_test_component(hass):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client(hass, aiohttp_client):
|
def client(hass, hass_client):
|
||||||
"""Fixture that can interact with the config manager API."""
|
"""Fixture that can interact with the config manager API."""
|
||||||
hass.loop.run_until_complete(async_setup_component(hass, 'http', {}))
|
hass.loop.run_until_complete(async_setup_component(hass, 'http', {}))
|
||||||
hass.loop.run_until_complete(config_entries.async_setup(hass))
|
hass.loop.run_until_complete(config_entries.async_setup(hass))
|
||||||
yield hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
yield hass.loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -8,14 +8,14 @@ from tests.common import mock_coro
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_validate_config_ok(hass, aiohttp_client):
|
def test_validate_config_ok(hass, hass_client):
|
||||||
"""Test checking config."""
|
"""Test checking config."""
|
||||||
with patch.object(config, 'SECTIONS', ['core']):
|
with patch.object(config, 'SECTIONS', ['core']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
yield from asyncio.sleep(0.1, loop=hass.loop)
|
yield from asyncio.sleep(0.1, loop=hass.loop)
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
'homeassistant.components.config.core.async_check_ha_config_file',
|
'homeassistant.components.config.core.async_check_ha_config_file',
|
||||||
|
|
|
@ -9,12 +9,12 @@ from homeassistant.config import DATA_CUSTOMIZE
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_get_entity(hass, aiohttp_client):
|
def test_get_entity(hass, hass_client):
|
||||||
"""Test getting entity."""
|
"""Test getting entity."""
|
||||||
with patch.object(config, 'SECTIONS', ['customize']):
|
with patch.object(config, 'SECTIONS', ['customize']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
def mock_read(path):
|
def mock_read(path):
|
||||||
"""Mock reading data."""
|
"""Mock reading data."""
|
||||||
|
@ -38,12 +38,12 @@ def test_get_entity(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_entity(hass, aiohttp_client):
|
def test_update_entity(hass, hass_client):
|
||||||
"""Test updating entity."""
|
"""Test updating entity."""
|
||||||
with patch.object(config, 'SECTIONS', ['customize']):
|
with patch.object(config, 'SECTIONS', ['customize']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
orig_data = {
|
orig_data = {
|
||||||
'hello.beer': {
|
'hello.beer': {
|
||||||
|
@ -89,12 +89,12 @@ def test_update_entity(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_entity_invalid_key(hass, aiohttp_client):
|
def test_update_entity_invalid_key(hass, hass_client):
|
||||||
"""Test updating entity."""
|
"""Test updating entity."""
|
||||||
with patch.object(config, 'SECTIONS', ['customize']):
|
with patch.object(config, 'SECTIONS', ['customize']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.post(
|
resp = yield from client.post(
|
||||||
'/api/config/customize/config/not_entity', data=json.dumps({
|
'/api/config/customize/config/not_entity', data=json.dumps({
|
||||||
|
@ -105,12 +105,12 @@ def test_update_entity_invalid_key(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_entity_invalid_json(hass, aiohttp_client):
|
def test_update_entity_invalid_json(hass, hass_client):
|
||||||
"""Test updating entity."""
|
"""Test updating entity."""
|
||||||
with patch.object(config, 'SECTIONS', ['customize']):
|
with patch.object(config, 'SECTIONS', ['customize']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.post(
|
resp = yield from client.post(
|
||||||
'/api/config/customize/config/hello.beer', data='not json')
|
'/api/config/customize/config/hello.beer', data='not json')
|
||||||
|
|
|
@ -11,12 +11,12 @@ VIEW_NAME = 'api:config:group:config'
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_get_device_config(hass, aiohttp_client):
|
def test_get_device_config(hass, hass_client):
|
||||||
"""Test getting device config."""
|
"""Test getting device config."""
|
||||||
with patch.object(config, 'SECTIONS', ['group']):
|
with patch.object(config, 'SECTIONS', ['group']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
def mock_read(path):
|
def mock_read(path):
|
||||||
"""Mock reading data."""
|
"""Mock reading data."""
|
||||||
|
@ -40,12 +40,12 @@ def test_get_device_config(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_device_config(hass, aiohttp_client):
|
def test_update_device_config(hass, hass_client):
|
||||||
"""Test updating device config."""
|
"""Test updating device config."""
|
||||||
with patch.object(config, 'SECTIONS', ['group']):
|
with patch.object(config, 'SECTIONS', ['group']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
orig_data = {
|
orig_data = {
|
||||||
'hello.beer': {
|
'hello.beer': {
|
||||||
|
@ -89,12 +89,12 @@ def test_update_device_config(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_device_config_invalid_key(hass, aiohttp_client):
|
def test_update_device_config_invalid_key(hass, hass_client):
|
||||||
"""Test updating device config."""
|
"""Test updating device config."""
|
||||||
with patch.object(config, 'SECTIONS', ['group']):
|
with patch.object(config, 'SECTIONS', ['group']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.post(
|
resp = yield from client.post(
|
||||||
'/api/config/group/config/not a slug', data=json.dumps({
|
'/api/config/group/config/not a slug', data=json.dumps({
|
||||||
|
@ -105,12 +105,12 @@ def test_update_device_config_invalid_key(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_device_config_invalid_data(hass, aiohttp_client):
|
def test_update_device_config_invalid_data(hass, hass_client):
|
||||||
"""Test updating device config."""
|
"""Test updating device config."""
|
||||||
with patch.object(config, 'SECTIONS', ['group']):
|
with patch.object(config, 'SECTIONS', ['group']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.post(
|
resp = yield from client.post(
|
||||||
'/api/config/group/config/hello_beer', data=json.dumps({
|
'/api/config/group/config/hello_beer', data=json.dumps({
|
||||||
|
@ -121,12 +121,12 @@ def test_update_device_config_invalid_data(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_device_config_invalid_json(hass, aiohttp_client):
|
def test_update_device_config_invalid_json(hass, hass_client):
|
||||||
"""Test updating device config."""
|
"""Test updating device config."""
|
||||||
with patch.object(config, 'SECTIONS', ['group']):
|
with patch.object(config, 'SECTIONS', ['group']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
|
|
||||||
resp = yield from client.post(
|
resp = yield from client.post(
|
||||||
'/api/config/group/config/hello_beer', data='not json')
|
'/api/config/group/config/hello_beer', data='not json')
|
||||||
|
|
|
@ -34,13 +34,13 @@ def test_setup_check_env_works(hass, loop):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_get_suites(hass, aiohttp_client):
|
def test_get_suites(hass, hass_client):
|
||||||
"""Test getting suites."""
|
"""Test getting suites."""
|
||||||
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}), \
|
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}), \
|
||||||
patch.object(config, 'SECTIONS', ['hassbian']):
|
patch.object(config, 'SECTIONS', ['hassbian']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
resp = yield from client.get('/api/config/hassbian/suites')
|
resp = yield from client.get('/api/config/hassbian/suites')
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
result = yield from resp.json()
|
result = yield from resp.json()
|
||||||
|
@ -53,13 +53,13 @@ def test_get_suites(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_install_suite(hass, aiohttp_client):
|
def test_install_suite(hass, hass_client):
|
||||||
"""Test getting suites."""
|
"""Test getting suites."""
|
||||||
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}), \
|
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}), \
|
||||||
patch.object(config, 'SECTIONS', ['hassbian']):
|
patch.object(config, 'SECTIONS', ['hassbian']):
|
||||||
yield from async_setup_component(hass, 'config', {})
|
yield from async_setup_component(hass, 'config', {})
|
||||||
|
|
||||||
client = yield from aiohttp_client(hass.http.app)
|
client = yield from hass_client()
|
||||||
resp = yield from client.post(
|
resp = yield from client.post(
|
||||||
'/api/config/hassbian/suites/openzwave/install')
|
'/api/config/hassbian/suites/openzwave/install')
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
|
|
|
@ -16,12 +16,12 @@ VIEW_NAME = 'api:config:zwave:device_config'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client(loop, hass, aiohttp_client):
|
def client(loop, hass, hass_client):
|
||||||
"""Client to communicate with Z-Wave config views."""
|
"""Client to communicate with Z-Wave config views."""
|
||||||
with patch.object(config, 'SECTIONS', ['zwave']):
|
with patch.object(config, 'SECTIONS', ['zwave']):
|
||||||
loop.run_until_complete(async_setup_component(hass, 'config', {}))
|
loop.run_until_complete(async_setup_component(hass, 'config', {}))
|
||||||
|
|
||||||
return loop.run_until_complete(aiohttp_client(hass.http.app))
|
return loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -3,14 +3,12 @@ from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.auth.const import GROUP_ID_ADMIN, GROUP_ID_READ_ONLY
|
|
||||||
from homeassistant.auth.providers import legacy_api_password, homeassistant
|
|
||||||
from homeassistant.setup import async_setup_component
|
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 (
|
from homeassistant.components.websocket_api.auth import (
|
||||||
TYPE_AUTH, TYPE_AUTH_OK, TYPE_AUTH_REQUIRED)
|
TYPE_AUTH, TYPE_AUTH_OK, TYPE_AUTH_REQUIRED)
|
||||||
|
|
||||||
from tests.common import MockUser, CLIENT_ID, mock_coro
|
from tests.common import mock_coro
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
@ -22,35 +20,15 @@ def prevent_io():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hass_ws_client(aiohttp_client):
|
def hass_ws_client(aiohttp_client, hass_access_token):
|
||||||
"""Websocket client fixture connected to websocket server."""
|
"""Websocket client fixture connected to websocket server."""
|
||||||
async def create_client(hass, access_token=None):
|
async def create_client(hass, access_token=hass_access_token):
|
||||||
"""Create a websocket client."""
|
"""Create a websocket client."""
|
||||||
assert await async_setup_component(hass, 'websocket_api')
|
assert await async_setup_component(hass, 'websocket_api')
|
||||||
|
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await aiohttp_client(hass.http.app)
|
||||||
|
|
||||||
patches = []
|
with patch('homeassistant.components.http.auth.setup_auth'):
|
||||||
|
|
||||||
if access_token is None:
|
|
||||||
patches.append(patch(
|
|
||||||
'homeassistant.auth.AuthManager.active', return_value=False))
|
|
||||||
patches.append(patch(
|
|
||||||
'homeassistant.auth.AuthManager.support_legacy',
|
|
||||||
return_value=True))
|
|
||||||
patches.append(patch(
|
|
||||||
'homeassistant.components.websocket_api.auth.'
|
|
||||||
'validate_password', return_value=True))
|
|
||||||
else:
|
|
||||||
patches.append(patch(
|
|
||||||
'homeassistant.auth.AuthManager.active', return_value=True))
|
|
||||||
patches.append(patch(
|
|
||||||
'homeassistant.components.http.auth.setup_auth'))
|
|
||||||
|
|
||||||
for p in patches:
|
|
||||||
p.start()
|
|
||||||
|
|
||||||
try:
|
|
||||||
websocket = await client.ws_connect(URL)
|
websocket = await client.ws_connect(URL)
|
||||||
auth_resp = await websocket.receive_json()
|
auth_resp = await websocket.receive_json()
|
||||||
assert auth_resp['type'] == TYPE_AUTH_REQUIRED
|
assert auth_resp['type'] == TYPE_AUTH_REQUIRED
|
||||||
|
@ -69,76 +47,8 @@ def hass_ws_client(aiohttp_client):
|
||||||
auth_ok = await websocket.receive_json()
|
auth_ok = await websocket.receive_json()
|
||||||
assert auth_ok['type'] == TYPE_AUTH_OK
|
assert auth_ok['type'] == TYPE_AUTH_OK
|
||||||
|
|
||||||
finally:
|
|
||||||
for p in patches:
|
|
||||||
p.stop()
|
|
||||||
|
|
||||||
# wrap in client
|
# wrap in client
|
||||||
websocket.client = client
|
websocket.client = client
|
||||||
return websocket
|
return websocket
|
||||||
|
|
||||||
return create_client
|
return create_client
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def hass_access_token(hass, hass_admin_user):
|
|
||||||
"""Return an access token to access Home Assistant."""
|
|
||||||
refresh_token = hass.loop.run_until_complete(
|
|
||||||
hass.auth.async_create_refresh_token(hass_admin_user, CLIENT_ID))
|
|
||||||
yield hass.auth.async_create_access_token(refresh_token)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def hass_owner_user(hass, local_auth):
|
|
||||||
"""Return a Home Assistant admin user."""
|
|
||||||
return MockUser(is_owner=True).add_to_hass(hass)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def hass_admin_user(hass, local_auth):
|
|
||||||
"""Return a Home Assistant admin user."""
|
|
||||||
admin_group = hass.loop.run_until_complete(hass.auth.async_get_group(
|
|
||||||
GROUP_ID_ADMIN))
|
|
||||||
return MockUser(groups=[admin_group]).add_to_hass(hass)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def hass_read_only_user(hass, local_auth):
|
|
||||||
"""Return a Home Assistant read only user."""
|
|
||||||
read_only_group = hass.loop.run_until_complete(hass.auth.async_get_group(
|
|
||||||
GROUP_ID_READ_ONLY))
|
|
||||||
return MockUser(groups=[read_only_group]).add_to_hass(hass)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def legacy_auth(hass):
|
|
||||||
"""Load legacy API password provider."""
|
|
||||||
prv = legacy_api_password.LegacyApiPasswordAuthProvider(
|
|
||||||
hass, hass.auth._store, {
|
|
||||||
'type': 'legacy_api_password'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
hass.auth._providers[(prv.type, prv.id)] = prv
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def local_auth(hass):
|
|
||||||
"""Load local auth provider."""
|
|
||||||
prv = homeassistant.HassAuthProvider(
|
|
||||||
hass, hass.auth._store, {
|
|
||||||
'type': 'homeassistant'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
hass.auth._providers[(prv.type, prv.id)] = prv
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def hass_client(hass, aiohttp_client, hass_access_token):
|
|
||||||
"""Return an authenticated HTTP client."""
|
|
||||||
async def auth_client():
|
|
||||||
"""Return an authenticated client."""
|
|
||||||
return await aiohttp_client(hass.http.app, headers={
|
|
||||||
'Authorization': "Bearer {}".format(hass_access_token)
|
|
||||||
})
|
|
||||||
|
|
||||||
return auth_client
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ def _url(data=None):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def locative_client(loop, hass, aiohttp_client):
|
def locative_client(loop, hass, hass_client):
|
||||||
"""Locative mock client."""
|
"""Locative mock client."""
|
||||||
assert loop.run_until_complete(async_setup_component(
|
assert loop.run_until_complete(async_setup_component(
|
||||||
hass, device_tracker.DOMAIN, {
|
hass, device_tracker.DOMAIN, {
|
||||||
|
@ -29,7 +29,7 @@ def locative_client(loop, hass, aiohttp_client):
|
||||||
}))
|
}))
|
||||||
|
|
||||||
with patch('homeassistant.components.device_tracker.update_config'):
|
with patch('homeassistant.components.device_tracker.update_config'):
|
||||||
yield loop.run_until_complete(aiohttp_client(hass.http.app))
|
yield loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -13,7 +13,7 @@ from homeassistant.components.device_tracker.meraki import URL
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def meraki_client(loop, hass, aiohttp_client):
|
def meraki_client(loop, hass, hass_client):
|
||||||
"""Meraki mock client."""
|
"""Meraki mock client."""
|
||||||
assert loop.run_until_complete(async_setup_component(
|
assert loop.run_until_complete(async_setup_component(
|
||||||
hass, device_tracker.DOMAIN, {
|
hass, device_tracker.DOMAIN, {
|
||||||
|
@ -25,7 +25,7 @@ def meraki_client(loop, hass, aiohttp_client):
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
yield loop.run_until_complete(aiohttp_client(hass.http.app))
|
yield loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -59,8 +59,16 @@ def mock_http_client_with_urls(hass, aiohttp_client):
|
||||||
return hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
return hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_onboarded():
|
||||||
|
"""Mock that we're onboarded."""
|
||||||
|
with patch('homeassistant.components.onboarding.async_is_onboarded',
|
||||||
|
return_value=True):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_frontend_and_static(mock_http_client):
|
def test_frontend_and_static(mock_http_client, mock_onboarded):
|
||||||
"""Test if we can get the frontend."""
|
"""Test if we can get the frontend."""
|
||||||
resp = yield from mock_http_client.get('')
|
resp = yield from mock_http_client.get('')
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
|
@ -220,7 +228,7 @@ async def test_missing_themes(hass, hass_ws_client):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_extra_urls(mock_http_client_with_urls):
|
def test_extra_urls(mock_http_client_with_urls, mock_onboarded):
|
||||||
"""Test that extra urls are loaded."""
|
"""Test that extra urls are loaded."""
|
||||||
resp = yield from mock_http_client_with_urls.get('/states?latest')
|
resp = yield from mock_http_client_with_urls.get('/states?latest')
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
|
@ -229,7 +237,7 @@ def test_extra_urls(mock_http_client_with_urls):
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_extra_urls_es5(mock_http_client_with_urls):
|
def test_extra_urls_es5(mock_http_client_with_urls, mock_onboarded):
|
||||||
"""Test that es5 extra urls are loaded."""
|
"""Test that es5 extra urls are loaded."""
|
||||||
resp = yield from mock_http_client_with_urls.get('/states?es5')
|
resp = yield from mock_http_client_with_urls.get('/states?es5')
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
|
@ -280,7 +288,7 @@ async def test_get_translations(hass, hass_ws_client):
|
||||||
assert msg['result'] == {'resources': {'lang': 'nl'}}
|
assert msg['result'] == {'resources': {'lang': 'nl'}}
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_load(mock_http_client):
|
async def test_auth_load(mock_http_client, mock_onboarded):
|
||||||
"""Test auth component loaded by default."""
|
"""Test auth component loaded by default."""
|
||||||
resp = await mock_http_client.get('/auth/providers')
|
resp = await mock_http_client.get('/auth/providers')
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
|
|
|
@ -105,7 +105,7 @@ BEACON_EXIT_CAR = {
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def geofency_client(loop, hass, aiohttp_client):
|
def geofency_client(loop, hass, hass_client):
|
||||||
"""Geofency mock client."""
|
"""Geofency mock client."""
|
||||||
assert loop.run_until_complete(async_setup_component(
|
assert loop.run_until_complete(async_setup_component(
|
||||||
hass, DOMAIN, {
|
hass, DOMAIN, {
|
||||||
|
@ -116,7 +116,7 @@ def geofency_client(loop, hass, aiohttp_client):
|
||||||
loop.run_until_complete(hass.async_block_till_done())
|
loop.run_until_complete(hass.async_block_till_done())
|
||||||
|
|
||||||
with patch('homeassistant.components.device_tracker.update_config'):
|
with patch('homeassistant.components.device_tracker.update_config'):
|
||||||
yield loop.run_until_complete(aiohttp_client(hass.http.app))
|
yield loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
|
|
@ -89,8 +89,7 @@ def test_setup_api_push_api_data_server_host(hass, aioclient_mock):
|
||||||
async def test_setup_api_push_api_data_default(hass, aioclient_mock,
|
async def test_setup_api_push_api_data_default(hass, aioclient_mock,
|
||||||
hass_storage):
|
hass_storage):
|
||||||
"""Test setup with API push default data."""
|
"""Test setup with API push default data."""
|
||||||
with patch.dict(os.environ, MOCK_ENVIRON), \
|
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||||
patch('homeassistant.auth.AuthManager.active', return_value=True):
|
|
||||||
result = await async_setup_component(hass, 'hassio', {
|
result = await async_setup_component(hass, 'hassio', {
|
||||||
'http': {},
|
'http': {},
|
||||||
'hassio': {}
|
'hassio': {}
|
||||||
|
@ -130,20 +129,6 @@ async def test_setup_adds_admin_group_to_user(hass, aioclient_mock,
|
||||||
'version': 1
|
'version': 1
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch.dict(os.environ, MOCK_ENVIRON), \
|
|
||||||
patch('homeassistant.auth.AuthManager.active', return_value=True):
|
|
||||||
result = await async_setup_component(hass, 'hassio', {
|
|
||||||
'http': {},
|
|
||||||
'hassio': {}
|
|
||||||
})
|
|
||||||
assert result
|
|
||||||
|
|
||||||
assert user.is_admin
|
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_api_push_api_data_no_auth(hass, aioclient_mock,
|
|
||||||
hass_storage):
|
|
||||||
"""Test setup with API push default data."""
|
|
||||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||||
result = await async_setup_component(hass, 'hassio', {
|
result = await async_setup_component(hass, 'hassio', {
|
||||||
'http': {},
|
'http': {},
|
||||||
|
@ -151,11 +136,7 @@ async def test_setup_api_push_api_data_no_auth(hass, aioclient_mock,
|
||||||
})
|
})
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert aioclient_mock.call_count == 3
|
assert user.is_admin
|
||||||
assert not aioclient_mock.mock_calls[1][2]['ssl']
|
|
||||||
assert aioclient_mock.mock_calls[1][2]['password'] is None
|
|
||||||
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
|
|
||||||
assert aioclient_mock.mock_calls[1][2]['refresh_token'] is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_api_existing_hassio_user(hass, aioclient_mock,
|
async def test_setup_api_existing_hassio_user(hass, aioclient_mock,
|
||||||
|
@ -169,8 +150,7 @@ async def test_setup_api_existing_hassio_user(hass, aioclient_mock,
|
||||||
'hassio_user': user.id
|
'hassio_user': user.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
with patch.dict(os.environ, MOCK_ENVIRON), \
|
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||||
patch('homeassistant.auth.AuthManager.active', return_value=True):
|
|
||||||
result = await async_setup_component(hass, 'hassio', {
|
result = await async_setup_component(hass, 'hassio', {
|
||||||
'http': {},
|
'http': {},
|
||||||
'hassio': {}
|
'hassio': {}
|
||||||
|
|
|
@ -75,19 +75,10 @@ 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_without_password(app, aiohttp_client):
|
|
||||||
"""Test access without password."""
|
|
||||||
setup_auth(app, [], False, api_password=None)
|
|
||||||
client = await aiohttp_client(app)
|
|
||||||
|
|
||||||
resp = await client.get('/')
|
|
||||||
assert resp.status == 200
|
|
||||||
|
|
||||||
|
|
||||||
async def test_access_with_password_in_header(app, aiohttp_client,
|
async def test_access_with_password_in_header(app, aiohttp_client,
|
||||||
legacy_auth, hass):
|
legacy_auth, hass):
|
||||||
"""Test access with password in header."""
|
"""Test access with password in header."""
|
||||||
setup_auth(app, [], False, api_password=API_PASSWORD)
|
setup_auth(app, [], api_password=API_PASSWORD)
|
||||||
client = await aiohttp_client(app)
|
client = await aiohttp_client(app)
|
||||||
user = await legacy_api_password.async_get_user(hass)
|
user = await legacy_api_password.async_get_user(hass)
|
||||||
|
|
||||||
|
@ -107,7 +98,7 @@ async def test_access_with_password_in_header(app, aiohttp_client,
|
||||||
async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth,
|
async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth,
|
||||||
hass):
|
hass):
|
||||||
"""Test access with password in URL."""
|
"""Test access with password in URL."""
|
||||||
setup_auth(app, [], False, api_password=API_PASSWORD)
|
setup_auth(app, [], api_password=API_PASSWORD)
|
||||||
client = await aiohttp_client(app)
|
client = await aiohttp_client(app)
|
||||||
user = await legacy_api_password.async_get_user(hass)
|
user = await legacy_api_password.async_get_user(hass)
|
||||||
|
|
||||||
|
@ -131,7 +122,7 @@ async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth,
|
||||||
|
|
||||||
async def test_basic_auth_works(app, aiohttp_client, hass, legacy_auth):
|
async def test_basic_auth_works(app, aiohttp_client, hass, legacy_auth):
|
||||||
"""Test access with basic authentication."""
|
"""Test access with basic authentication."""
|
||||||
setup_auth(app, [], False, api_password=API_PASSWORD)
|
setup_auth(app, [], api_password=API_PASSWORD)
|
||||||
client = await aiohttp_client(app)
|
client = await aiohttp_client(app)
|
||||||
user = await legacy_api_password.async_get_user(hass)
|
user = await legacy_api_password.async_get_user(hass)
|
||||||
|
|
||||||
|
@ -164,7 +155,7 @@ async def test_basic_auth_works(app, aiohttp_client, hass, legacy_auth):
|
||||||
|
|
||||||
async def test_access_with_trusted_ip(app2, aiohttp_client, hass_owner_user):
|
async def test_access_with_trusted_ip(app2, aiohttp_client, hass_owner_user):
|
||||||
"""Test access with an untrusted ip address."""
|
"""Test access with an untrusted ip address."""
|
||||||
setup_auth(app2, TRUSTED_NETWORKS, False, api_password='some-pass')
|
setup_auth(app2, TRUSTED_NETWORKS, api_password='some-pass')
|
||||||
|
|
||||||
set_mock_ip = mock_real_ip(app2)
|
set_mock_ip = mock_real_ip(app2)
|
||||||
client = await aiohttp_client(app2)
|
client = await aiohttp_client(app2)
|
||||||
|
@ -190,7 +181,7 @@ async def test_auth_active_access_with_access_token_in_header(
|
||||||
hass, app, aiohttp_client, hass_access_token):
|
hass, app, aiohttp_client, hass_access_token):
|
||||||
"""Test access with access token in header."""
|
"""Test access with access token in header."""
|
||||||
token = hass_access_token
|
token = hass_access_token
|
||||||
setup_auth(app, [], True, api_password=None)
|
setup_auth(app, [], api_password=None)
|
||||||
client = await aiohttp_client(app)
|
client = await aiohttp_client(app)
|
||||||
refresh_token = await hass.auth.async_validate_access_token(
|
refresh_token = await hass.auth.async_validate_access_token(
|
||||||
hass_access_token)
|
hass_access_token)
|
||||||
|
@ -238,7 +229,7 @@ async def test_auth_active_access_with_access_token_in_header(
|
||||||
async def test_auth_active_access_with_trusted_ip(app2, aiohttp_client,
|
async def test_auth_active_access_with_trusted_ip(app2, aiohttp_client,
|
||||||
hass_owner_user):
|
hass_owner_user):
|
||||||
"""Test access with an untrusted ip address."""
|
"""Test access with an untrusted ip address."""
|
||||||
setup_auth(app2, TRUSTED_NETWORKS, True, api_password=None)
|
setup_auth(app2, TRUSTED_NETWORKS, None)
|
||||||
|
|
||||||
set_mock_ip = mock_real_ip(app2)
|
set_mock_ip = mock_real_ip(app2)
|
||||||
client = await aiohttp_client(app2)
|
client = await aiohttp_client(app2)
|
||||||
|
@ -260,31 +251,10 @@ async def test_auth_active_access_with_trusted_ip(app2, aiohttp_client,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_active_blocked_api_password_access(
|
|
||||||
app, aiohttp_client, legacy_auth):
|
|
||||||
"""Test access using api_password should be blocked when auth.active."""
|
|
||||||
setup_auth(app, [], True, api_password=API_PASSWORD)
|
|
||||||
client = await aiohttp_client(app)
|
|
||||||
|
|
||||||
req = await client.get(
|
|
||||||
'/', headers={HTTP_HEADER_HA_AUTH: API_PASSWORD})
|
|
||||||
assert req.status == 401
|
|
||||||
|
|
||||||
resp = await client.get('/', params={
|
|
||||||
'api_password': API_PASSWORD
|
|
||||||
})
|
|
||||||
assert resp.status == 401
|
|
||||||
|
|
||||||
req = await client.get(
|
|
||||||
'/',
|
|
||||||
auth=BasicAuth('homeassistant', API_PASSWORD))
|
|
||||||
assert req.status == 401
|
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_legacy_support_api_password_access(
|
async def test_auth_legacy_support_api_password_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(app, [], True, support_legacy=True, api_password=API_PASSWORD)
|
setup_auth(app, [], API_PASSWORD)
|
||||||
client = await aiohttp_client(app)
|
client = await aiohttp_client(app)
|
||||||
user = await legacy_api_password.async_get_user(hass)
|
user = await legacy_api_password.async_get_user(hass)
|
||||||
|
|
||||||
|
@ -320,7 +290,7 @@ async def test_auth_access_signed_path(
|
||||||
"""Test access with signed url."""
|
"""Test access with signed url."""
|
||||||
app.router.add_post('/', mock_handler)
|
app.router.add_post('/', mock_handler)
|
||||||
app.router.add_get('/another_path', mock_handler)
|
app.router.add_get('/another_path', mock_handler)
|
||||||
setup_auth(app, [], True, api_password=None)
|
setup_auth(app, [], None)
|
||||||
client = await aiohttp_client(app)
|
client = await aiohttp_client(app)
|
||||||
|
|
||||||
refresh_token = await hass.auth.async_validate_access_token(
|
refresh_token = await hass.auth.async_validate_access_token(
|
||||||
|
|
|
@ -9,7 +9,7 @@ import homeassistant.components.mailbox as mailbox
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_http_client(hass, aiohttp_client):
|
def mock_http_client(hass, hass_client):
|
||||||
"""Start the Hass HTTP component."""
|
"""Start the Hass HTTP component."""
|
||||||
config = {
|
config = {
|
||||||
mailbox.DOMAIN: {
|
mailbox.DOMAIN: {
|
||||||
|
@ -18,7 +18,7 @@ def mock_http_client(hass, aiohttp_client):
|
||||||
}
|
}
|
||||||
hass.loop.run_until_complete(
|
hass.loop.run_until_complete(
|
||||||
async_setup_component(hass, mailbox.DOMAIN, config))
|
async_setup_component(hass, mailbox.DOMAIN, config))
|
||||||
return hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
return hass.loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -49,7 +49,7 @@ REGISTER_URL = '/api/notify.html5'
|
||||||
PUBLISH_URL = '/api/notify.html5/callback'
|
PUBLISH_URL = '/api/notify.html5/callback'
|
||||||
|
|
||||||
|
|
||||||
async def mock_client(hass, aiohttp_client, registrations=None):
|
async def mock_client(hass, hass_client, registrations=None):
|
||||||
"""Create a test client for HTML5 views."""
|
"""Create a test client for HTML5 views."""
|
||||||
if registrations is None:
|
if registrations is None:
|
||||||
registrations = {}
|
registrations = {}
|
||||||
|
@ -62,7 +62,7 @@ async def mock_client(hass, aiohttp_client, registrations=None):
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return await aiohttp_client(hass.http.app)
|
return await hass_client()
|
||||||
|
|
||||||
|
|
||||||
class TestHtml5Notify:
|
class TestHtml5Notify:
|
||||||
|
@ -151,9 +151,9 @@ class TestHtml5Notify:
|
||||||
assert mock_wp.mock_calls[4][2]['gcm_key'] is None
|
assert mock_wp.mock_calls[4][2]['gcm_key'] is None
|
||||||
|
|
||||||
|
|
||||||
async def test_registering_new_device_view(hass, aiohttp_client):
|
async def test_registering_new_device_view(hass, hass_client):
|
||||||
"""Test that the HTML view works."""
|
"""Test that the HTML view works."""
|
||||||
client = await mock_client(hass, aiohttp_client)
|
client = await mock_client(hass, hass_client)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
||||||
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
||||||
|
@ -165,9 +165,9 @@ async def test_registering_new_device_view(hass, aiohttp_client):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_registering_new_device_expiration_view(hass, aiohttp_client):
|
async def test_registering_new_device_expiration_view(hass, hass_client):
|
||||||
"""Test that the HTML view works."""
|
"""Test that the HTML view works."""
|
||||||
client = await mock_client(hass, aiohttp_client)
|
client = await mock_client(hass, hass_client)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
||||||
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4))
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4))
|
||||||
|
@ -178,10 +178,10 @@ async def test_registering_new_device_expiration_view(hass, aiohttp_client):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_registering_new_device_fails_view(hass, aiohttp_client):
|
async def test_registering_new_device_fails_view(hass, hass_client):
|
||||||
"""Test subs. are not altered when registering a new device fails."""
|
"""Test subs. are not altered when registering a new device fails."""
|
||||||
registrations = {}
|
registrations = {}
|
||||||
client = await mock_client(hass, aiohttp_client, registrations)
|
client = await mock_client(hass, hass_client, registrations)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json',
|
with patch('homeassistant.components.notify.html5.save_json',
|
||||||
side_effect=HomeAssistantError()):
|
side_effect=HomeAssistantError()):
|
||||||
|
@ -191,10 +191,10 @@ async def test_registering_new_device_fails_view(hass, aiohttp_client):
|
||||||
assert registrations == {}
|
assert registrations == {}
|
||||||
|
|
||||||
|
|
||||||
async def test_registering_existing_device_view(hass, aiohttp_client):
|
async def test_registering_existing_device_view(hass, hass_client):
|
||||||
"""Test subscription is updated when registering existing device."""
|
"""Test subscription is updated when registering existing device."""
|
||||||
registrations = {}
|
registrations = {}
|
||||||
client = await mock_client(hass, aiohttp_client, registrations)
|
client = await mock_client(hass, hass_client, registrations)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
||||||
await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
||||||
|
@ -209,10 +209,10 @@ async def test_registering_existing_device_view(hass, aiohttp_client):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_registering_existing_device_fails_view(hass, aiohttp_client):
|
async def test_registering_existing_device_fails_view(hass, hass_client):
|
||||||
"""Test sub. is not updated when registering existing device fails."""
|
"""Test sub. is not updated when registering existing device fails."""
|
||||||
registrations = {}
|
registrations = {}
|
||||||
client = await mock_client(hass, aiohttp_client, registrations)
|
client = await mock_client(hass, hass_client, registrations)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
||||||
await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
||||||
|
@ -225,9 +225,9 @@ async def test_registering_existing_device_fails_view(hass, aiohttp_client):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_registering_new_device_validation(hass, aiohttp_client):
|
async def test_registering_new_device_validation(hass, hass_client):
|
||||||
"""Test various errors when registering a new device."""
|
"""Test various errors when registering a new device."""
|
||||||
client = await mock_client(hass, aiohttp_client)
|
client = await mock_client(hass, hass_client)
|
||||||
|
|
||||||
resp = await client.post(REGISTER_URL, data=json.dumps({
|
resp = await client.post(REGISTER_URL, data=json.dumps({
|
||||||
'browser': 'invalid browser',
|
'browser': 'invalid browser',
|
||||||
|
@ -249,13 +249,13 @@ async def test_registering_new_device_validation(hass, aiohttp_client):
|
||||||
assert resp.status == 400
|
assert resp.status == 400
|
||||||
|
|
||||||
|
|
||||||
async def test_unregistering_device_view(hass, aiohttp_client):
|
async def test_unregistering_device_view(hass, hass_client):
|
||||||
"""Test that the HTML unregister view works."""
|
"""Test that the HTML unregister view works."""
|
||||||
registrations = {
|
registrations = {
|
||||||
'some device': SUBSCRIPTION_1,
|
'some device': SUBSCRIPTION_1,
|
||||||
'other device': SUBSCRIPTION_2,
|
'other device': SUBSCRIPTION_2,
|
||||||
}
|
}
|
||||||
client = await mock_client(hass, aiohttp_client, registrations)
|
client = await mock_client(hass, hass_client, registrations)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
||||||
resp = await client.delete(REGISTER_URL, data=json.dumps({
|
resp = await client.delete(REGISTER_URL, data=json.dumps({
|
||||||
|
@ -270,10 +270,10 @@ async def test_unregistering_device_view(hass, aiohttp_client):
|
||||||
|
|
||||||
|
|
||||||
async def test_unregister_device_view_handle_unknown_subscription(
|
async def test_unregister_device_view_handle_unknown_subscription(
|
||||||
hass, aiohttp_client):
|
hass, hass_client):
|
||||||
"""Test that the HTML unregister view handles unknown subscriptions."""
|
"""Test that the HTML unregister view handles unknown subscriptions."""
|
||||||
registrations = {}
|
registrations = {}
|
||||||
client = await mock_client(hass, aiohttp_client, registrations)
|
client = await mock_client(hass, hass_client, registrations)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
with patch('homeassistant.components.notify.html5.save_json') as mock_save:
|
||||||
resp = await client.delete(REGISTER_URL, data=json.dumps({
|
resp = await client.delete(REGISTER_URL, data=json.dumps({
|
||||||
|
@ -286,13 +286,13 @@ async def test_unregister_device_view_handle_unknown_subscription(
|
||||||
|
|
||||||
|
|
||||||
async def test_unregistering_device_view_handles_save_error(
|
async def test_unregistering_device_view_handles_save_error(
|
||||||
hass, aiohttp_client):
|
hass, hass_client):
|
||||||
"""Test that the HTML unregister view handles save errors."""
|
"""Test that the HTML unregister view handles save errors."""
|
||||||
registrations = {
|
registrations = {
|
||||||
'some device': SUBSCRIPTION_1,
|
'some device': SUBSCRIPTION_1,
|
||||||
'other device': SUBSCRIPTION_2,
|
'other device': SUBSCRIPTION_2,
|
||||||
}
|
}
|
||||||
client = await mock_client(hass, aiohttp_client, registrations)
|
client = await mock_client(hass, hass_client, registrations)
|
||||||
|
|
||||||
with patch('homeassistant.components.notify.html5.save_json',
|
with patch('homeassistant.components.notify.html5.save_json',
|
||||||
side_effect=HomeAssistantError()):
|
side_effect=HomeAssistantError()):
|
||||||
|
@ -307,23 +307,23 @@ async def test_unregistering_device_view_handles_save_error(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_callback_view_no_jwt(hass, aiohttp_client):
|
async def test_callback_view_no_jwt(hass, hass_client):
|
||||||
"""Test that the notification callback view works without JWT."""
|
"""Test that the notification callback view works without JWT."""
|
||||||
client = await mock_client(hass, aiohttp_client)
|
client = await mock_client(hass, hass_client)
|
||||||
resp = await client.post(PUBLISH_URL, data=json.dumps({
|
resp = await client.post(PUBLISH_URL, data=json.dumps({
|
||||||
'type': 'push',
|
'type': 'push',
|
||||||
'tag': '3bc28d69-0921-41f1-ac6a-7a627ba0aa72'
|
'tag': '3bc28d69-0921-41f1-ac6a-7a627ba0aa72'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
assert resp.status == 401, resp.response
|
assert resp.status == 401
|
||||||
|
|
||||||
|
|
||||||
async def test_callback_view_with_jwt(hass, aiohttp_client):
|
async def test_callback_view_with_jwt(hass, hass_client):
|
||||||
"""Test that the notification callback view works with JWT."""
|
"""Test that the notification callback view works with JWT."""
|
||||||
registrations = {
|
registrations = {
|
||||||
'device': SUBSCRIPTION_1
|
'device': SUBSCRIPTION_1
|
||||||
}
|
}
|
||||||
client = await mock_client(hass, aiohttp_client, registrations)
|
client = await mock_client(hass, hass_client, registrations)
|
||||||
|
|
||||||
with patch('pywebpush.WebPusher') as mock_wp:
|
with patch('pywebpush.WebPusher') as mock_wp:
|
||||||
await hass.services.async_call('notify', 'notify', {
|
await hass.services.async_call('notify', 'notify', {
|
||||||
|
|
|
@ -38,8 +38,7 @@ async def test_setup_views_if_not_onboarded(hass):
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
assert onboarding.DOMAIN in hass.data
|
assert onboarding.DOMAIN in hass.data
|
||||||
|
|
||||||
with patch('homeassistant.auth.AuthManager.active', return_value=True):
|
assert not onboarding.async_is_onboarded(hass)
|
||||||
assert not onboarding.async_is_onboarded(hass)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_is_onboarded():
|
async def test_is_onboarded():
|
||||||
|
@ -47,17 +46,13 @@ async def test_is_onboarded():
|
||||||
hass = Mock()
|
hass = Mock()
|
||||||
hass.data = {}
|
hass.data = {}
|
||||||
|
|
||||||
with patch('homeassistant.auth.AuthManager.active', return_value=False):
|
assert onboarding.async_is_onboarded(hass)
|
||||||
assert onboarding.async_is_onboarded(hass)
|
|
||||||
|
|
||||||
with patch('homeassistant.auth.AuthManager.active', return_value=True):
|
hass.data[onboarding.DOMAIN] = True
|
||||||
assert onboarding.async_is_onboarded(hass)
|
assert onboarding.async_is_onboarded(hass)
|
||||||
|
|
||||||
hass.data[onboarding.DOMAIN] = True
|
hass.data[onboarding.DOMAIN] = False
|
||||||
assert onboarding.async_is_onboarded(hass)
|
assert not onboarding.async_is_onboarded(hass)
|
||||||
|
|
||||||
hass.data[onboarding.DOMAIN] = False
|
|
||||||
assert not onboarding.async_is_onboarded(hass)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_having_owner_finishes_user_step(hass, hass_storage):
|
async def test_having_owner_finishes_user_step(hass, hass_storage):
|
||||||
|
|
|
@ -591,18 +591,18 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
}, time_fired=event_time_fired)
|
}, time_fired=event_time_fired)
|
||||||
|
|
||||||
|
|
||||||
async def test_logbook_view(hass, aiohttp_client):
|
async def test_logbook_view(hass, hass_client):
|
||||||
"""Test the logbook view."""
|
"""Test the logbook view."""
|
||||||
await hass.async_add_job(init_recorder_component, hass)
|
await hass.async_add_job(init_recorder_component, hass)
|
||||||
await async_setup_component(hass, 'logbook', {})
|
await async_setup_component(hass, 'logbook', {})
|
||||||
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await hass_client()
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
'/api/logbook/{}'.format(dt_util.utcnow().isoformat()))
|
'/api/logbook/{}'.format(dt_util.utcnow().isoformat()))
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
|
|
||||||
async def test_logbook_view_period_entity(hass, aiohttp_client):
|
async def test_logbook_view_period_entity(hass, hass_client):
|
||||||
"""Test the logbook view with period and entity."""
|
"""Test the logbook view with period and entity."""
|
||||||
await hass.async_add_job(init_recorder_component, hass)
|
await hass.async_add_job(init_recorder_component, hass)
|
||||||
await async_setup_component(hass, 'logbook', {})
|
await async_setup_component(hass, 'logbook', {})
|
||||||
|
@ -617,7 +617,7 @@ async def test_logbook_view_period_entity(hass, aiohttp_client):
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
|
|
||||||
client = await aiohttp_client(hass.http.app)
|
client = await hass_client()
|
||||||
|
|
||||||
# Today time 00:00:00
|
# Today time 00:00:00
|
||||||
start = dt_util.utcnow().date()
|
start = dt_util.utcnow().date()
|
||||||
|
|
|
@ -7,14 +7,14 @@ import homeassistant.components.prometheus as prometheus
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def prometheus_client(loop, hass, aiohttp_client):
|
def prometheus_client(loop, hass, hass_client):
|
||||||
"""Initialize an aiohttp_client with Prometheus component."""
|
"""Initialize an hass_client with Prometheus component."""
|
||||||
assert loop.run_until_complete(async_setup_component(
|
assert loop.run_until_complete(async_setup_component(
|
||||||
hass,
|
hass,
|
||||||
prometheus.DOMAIN,
|
prometheus.DOMAIN,
|
||||||
{prometheus.DOMAIN: {}},
|
{prometheus.DOMAIN: {}},
|
||||||
))
|
))
|
||||||
return loop.run_until_complete(aiohttp_client(hass.http.app))
|
return loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -8,7 +8,7 @@ from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_http_client(loop, hass, aiohttp_client):
|
def mock_http_client(loop, hass, hass_client):
|
||||||
"""Set up test fixture."""
|
"""Set up test fixture."""
|
||||||
config = {
|
config = {
|
||||||
'rss_feed_template': {
|
'rss_feed_template': {
|
||||||
|
@ -21,7 +21,7 @@ def mock_http_client(loop, hass, aiohttp_client):
|
||||||
loop.run_until_complete(async_setup_component(hass,
|
loop.run_until_complete(async_setup_component(hass,
|
||||||
'rss_feed_template',
|
'rss_feed_template',
|
||||||
config))
|
config))
|
||||||
return loop.run_until_complete(aiohttp_client(hass.http.app))
|
return loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import ctypes
|
import ctypes
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import json
|
|
||||||
from unittest.mock import patch, PropertyMock
|
from unittest.mock import patch, PropertyMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -14,7 +13,7 @@ from homeassistant.components.tts.demo import DemoProvider
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
SERVICE_PLAY_MEDIA, MEDIA_TYPE_MUSIC, ATTR_MEDIA_CONTENT_ID,
|
SERVICE_PLAY_MEDIA, MEDIA_TYPE_MUSIC, ATTR_MEDIA_CONTENT_ID,
|
||||||
ATTR_MEDIA_CONTENT_TYPE, DOMAIN as DOMAIN_MP)
|
ATTR_MEDIA_CONTENT_TYPE, DOMAIN as DOMAIN_MP)
|
||||||
from homeassistant.setup import setup_component
|
from homeassistant.setup import setup_component, async_setup_component
|
||||||
|
|
||||||
from tests.common import (
|
from tests.common import (
|
||||||
get_test_home_assistant, get_test_instance_port, assert_setup_component,
|
get_test_home_assistant, get_test_instance_port, assert_setup_component,
|
||||||
|
@ -584,45 +583,45 @@ class TestTTS:
|
||||||
assert req.status_code == 200
|
assert req.status_code == 200
|
||||||
assert req.content == demo_data
|
assert req.content == demo_data
|
||||||
|
|
||||||
def test_setup_component_and_web_get_url(self):
|
|
||||||
"""Set up the demo platform and receive wrong file from web."""
|
async def test_setup_component_and_web_get_url(hass, hass_client):
|
||||||
config = {
|
"""Set up the demo platform and receive file from web."""
|
||||||
tts.DOMAIN: {
|
config = {
|
||||||
'platform': 'demo',
|
tts.DOMAIN: {
|
||||||
}
|
'platform': 'demo',
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
with assert_setup_component(1, tts.DOMAIN):
|
await async_setup_component(hass, tts.DOMAIN, config)
|
||||||
setup_component(self.hass, tts.DOMAIN, config)
|
|
||||||
|
|
||||||
self.hass.start()
|
client = await hass_client()
|
||||||
|
|
||||||
url = ("{}/api/tts_get_url").format(self.hass.config.api.base_url)
|
url = "/api/tts_get_url"
|
||||||
data = {'platform': 'demo',
|
data = {'platform': 'demo',
|
||||||
'message': "I person is on front of your door."}
|
'message': "I person is on front of your door."}
|
||||||
|
|
||||||
req = requests.post(url, data=json.dumps(data))
|
req = await client.post(url, json=data)
|
||||||
assert req.status_code == 200
|
assert req.status == 200
|
||||||
response = json.loads(req.text)
|
response = await req.json()
|
||||||
assert response.get('url') == (("{}/api/tts_proxy/265944c108cbb00b2a62"
|
assert response.get('url') == \
|
||||||
"1be5930513e03a0bb2cd_en_-_demo.mp3")
|
("{}/api/tts_proxy/265944c108cbb00b2a62"
|
||||||
.format(self.hass.config.api.base_url))
|
"1be5930513e03a0bb2cd_en_-_demo.mp3".format(hass.config.api.base_url))
|
||||||
|
|
||||||
def test_setup_component_and_web_get_url_bad_config(self):
|
|
||||||
"""Set up the demo platform and receive wrong file from web."""
|
async def test_setup_component_and_web_get_url_bad_config(hass, hass_client):
|
||||||
config = {
|
"""Set up the demo platform and receive wrong file from web."""
|
||||||
tts.DOMAIN: {
|
config = {
|
||||||
'platform': 'demo',
|
tts.DOMAIN: {
|
||||||
}
|
'platform': 'demo',
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
with assert_setup_component(1, tts.DOMAIN):
|
await async_setup_component(hass, tts.DOMAIN, config)
|
||||||
setup_component(self.hass, tts.DOMAIN, config)
|
|
||||||
|
|
||||||
self.hass.start()
|
client = await hass_client()
|
||||||
|
|
||||||
url = ("{}/api/tts_get_url").format(self.hass.config.api.base_url)
|
url = "/api/tts_get_url"
|
||||||
data = {'message': "I person is on front of your door."}
|
data = {'message': "I person is on front of your door."}
|
||||||
|
|
||||||
req = requests.post(url, data=data)
|
req = await client.post(url, json=data)
|
||||||
assert req.status_code == 400
|
assert req.status == 400
|
||||||
|
|
|
@ -13,7 +13,7 @@ from tests.common import mock_coro
|
||||||
from . import API_PASSWORD
|
from . import API_PASSWORD
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_via_msg(no_auth_websocket_client):
|
async def test_auth_via_msg(no_auth_websocket_client, legacy_auth):
|
||||||
"""Test authenticating."""
|
"""Test authenticating."""
|
||||||
await no_auth_websocket_client.send_json({
|
await no_auth_websocket_client.send_json({
|
||||||
'type': TYPE_AUTH,
|
'type': TYPE_AUTH,
|
||||||
|
@ -70,18 +70,16 @@ async def test_auth_active_with_token(hass, aiohttp_client, hass_access_token):
|
||||||
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.active') as auth_active:
|
auth_msg = await ws.receive_json()
|
||||||
auth_active.return_value = True
|
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
||||||
auth_msg = await ws.receive_json()
|
|
||||||
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
|
||||||
|
|
||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
'type': TYPE_AUTH,
|
'type': TYPE_AUTH,
|
||||||
'access_token': hass_access_token
|
'access_token': hass_access_token
|
||||||
})
|
})
|
||||||
|
|
||||||
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_OK
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_active_user_inactive(hass, aiohttp_client,
|
async def test_auth_active_user_inactive(hass, aiohttp_client,
|
||||||
|
@ -99,18 +97,16 @@ async def test_auth_active_user_inactive(hass, aiohttp_client,
|
||||||
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.active') as auth_active:
|
auth_msg = await ws.receive_json()
|
||||||
auth_active.return_value = True
|
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
||||||
auth_msg = await ws.receive_json()
|
|
||||||
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
|
||||||
|
|
||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
'type': TYPE_AUTH,
|
'type': TYPE_AUTH,
|
||||||
'access_token': hass_access_token
|
'access_token': hass_access_token
|
||||||
})
|
})
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_active_with_password_not_allow(hass, aiohttp_client):
|
async def test_auth_active_with_password_not_allow(hass, aiohttp_client):
|
||||||
|
@ -124,18 +120,16 @@ async def test_auth_active_with_password_not_allow(hass, aiohttp_client):
|
||||||
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.active',
|
auth_msg = await ws.receive_json()
|
||||||
return_value=True):
|
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
||||||
auth_msg = await ws.receive_json()
|
|
||||||
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
|
||||||
|
|
||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
'type': TYPE_AUTH,
|
'type': TYPE_AUTH,
|
||||||
'api_password': API_PASSWORD
|
'api_password': API_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
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_legacy_support_with_password(hass, aiohttp_client):
|
async def test_auth_legacy_support_with_password(hass, aiohttp_client):
|
||||||
|
@ -149,9 +143,7 @@ async def test_auth_legacy_support_with_password(hass, aiohttp_client):
|
||||||
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.active',
|
with patch('homeassistant.auth.AuthManager.support_legacy',
|
||||||
return_value=True),\
|
|
||||||
patch('homeassistant.auth.AuthManager.support_legacy',
|
|
||||||
return_value=True):
|
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
|
||||||
|
@ -176,15 +168,13 @@ async def test_auth_with_invalid_token(hass, aiohttp_client):
|
||||||
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.active') as auth_active:
|
auth_msg = await ws.receive_json()
|
||||||
auth_active.return_value = True
|
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
||||||
auth_msg = await ws.receive_json()
|
|
||||||
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
|
||||||
|
|
||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
'type': TYPE_AUTH,
|
'type': TYPE_AUTH,
|
||||||
'access_token': 'incorrect'
|
'access_token': 'incorrect'
|
||||||
})
|
})
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
"""Tests for WebSocket API commands."""
|
"""Tests for WebSocket API commands."""
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from async_timeout import timeout
|
from async_timeout import timeout
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
@ -201,18 +199,16 @@ async def test_call_service_context_with_user(hass, aiohttp_client,
|
||||||
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.active') as auth_active:
|
auth_msg = await ws.receive_json()
|
||||||
auth_active.return_value = True
|
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
||||||
auth_msg = await ws.receive_json()
|
|
||||||
assert auth_msg['type'] == TYPE_AUTH_REQUIRED
|
|
||||||
|
|
||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
'type': TYPE_AUTH,
|
'type': TYPE_AUTH,
|
||||||
'access_token': hass_access_token
|
'access_token': hass_access_token
|
||||||
})
|
})
|
||||||
|
|
||||||
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_OK
|
||||||
|
|
||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
'id': 5,
|
'id': 5,
|
||||||
|
@ -238,50 +234,6 @@ async def test_call_service_context_with_user(hass, aiohttp_client,
|
||||||
assert call.context.user_id == refresh_token.user.id
|
assert call.context.user_id == refresh_token.user.id
|
||||||
|
|
||||||
|
|
||||||
async def test_call_service_context_no_user(hass, aiohttp_client):
|
|
||||||
"""Test that connection without user sets context."""
|
|
||||||
assert await async_setup_component(hass, 'websocket_api', {
|
|
||||||
'http': {
|
|
||||||
'api_password': API_PASSWORD
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
calls = async_mock_service(hass, 'domain_test', 'test_service')
|
|
||||||
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,
|
|
||||||
'api_password': API_PASSWORD
|
|
||||||
})
|
|
||||||
|
|
||||||
auth_msg = await ws.receive_json()
|
|
||||||
assert auth_msg['type'] == TYPE_AUTH_OK
|
|
||||||
|
|
||||||
await ws.send_json({
|
|
||||||
'id': 5,
|
|
||||||
'type': commands.TYPE_CALL_SERVICE,
|
|
||||||
'domain': 'domain_test',
|
|
||||||
'service': 'test_service',
|
|
||||||
'service_data': {
|
|
||||||
'hello': 'world'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
msg = await ws.receive_json()
|
|
||||||
assert msg['success']
|
|
||||||
|
|
||||||
assert len(calls) == 1
|
|
||||||
call = calls[0]
|
|
||||||
assert call.domain == 'domain_test'
|
|
||||||
assert call.service == 'test_service'
|
|
||||||
assert call.data == {'hello': 'world'}
|
|
||||||
assert call.context.user_id is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_subscribe_requires_admin(websocket_client, hass_admin_user):
|
async def test_subscribe_requires_admin(websocket_client, hass_admin_user):
|
||||||
"""Test subscribing events without being admin."""
|
"""Test subscribing events without being admin."""
|
||||||
hass_admin_user.groups = []
|
hass_admin_user.groups = []
|
||||||
|
|
|
@ -10,10 +10,12 @@ import requests_mock as _requests_mock
|
||||||
|
|
||||||
from homeassistant import util
|
from homeassistant import util
|
||||||
from homeassistant.util import location
|
from homeassistant.util import location
|
||||||
|
from homeassistant.auth.const import GROUP_ID_ADMIN, GROUP_ID_READ_ONLY
|
||||||
|
from homeassistant.auth.providers import legacy_api_password, homeassistant
|
||||||
|
|
||||||
from tests.common import (
|
from tests.common import (
|
||||||
async_test_home_assistant, INSTANCES, async_mock_mqtt_component, mock_coro,
|
async_test_home_assistant, INSTANCES, async_mock_mqtt_component, mock_coro,
|
||||||
mock_storage as mock_storage)
|
mock_storage as mock_storage, MockUser, CLIENT_ID)
|
||||||
from tests.test_util.aiohttp import mock_aiohttp_client
|
from tests.test_util.aiohttp import mock_aiohttp_client
|
||||||
from tests.mock.zwave import MockNetwork, MockOption
|
from tests.mock.zwave import MockNetwork, MockOption
|
||||||
|
|
||||||
|
@ -133,3 +135,67 @@ def mock_device_tracker_conf():
|
||||||
side_effect=lambda *args: mock_coro(devices)
|
side_effect=lambda *args: mock_coro(devices)
|
||||||
):
|
):
|
||||||
yield devices
|
yield devices
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def hass_access_token(hass, hass_admin_user):
|
||||||
|
"""Return an access token to access Home Assistant."""
|
||||||
|
refresh_token = hass.loop.run_until_complete(
|
||||||
|
hass.auth.async_create_refresh_token(hass_admin_user, CLIENT_ID))
|
||||||
|
yield hass.auth.async_create_access_token(refresh_token)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def hass_owner_user(hass, local_auth):
|
||||||
|
"""Return a Home Assistant admin user."""
|
||||||
|
return MockUser(is_owner=True).add_to_hass(hass)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def hass_admin_user(hass, local_auth):
|
||||||
|
"""Return a Home Assistant admin user."""
|
||||||
|
admin_group = hass.loop.run_until_complete(hass.auth.async_get_group(
|
||||||
|
GROUP_ID_ADMIN))
|
||||||
|
return MockUser(groups=[admin_group]).add_to_hass(hass)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def hass_read_only_user(hass, local_auth):
|
||||||
|
"""Return a Home Assistant read only user."""
|
||||||
|
read_only_group = hass.loop.run_until_complete(hass.auth.async_get_group(
|
||||||
|
GROUP_ID_READ_ONLY))
|
||||||
|
return MockUser(groups=[read_only_group]).add_to_hass(hass)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def legacy_auth(hass):
|
||||||
|
"""Load legacy API password provider."""
|
||||||
|
prv = legacy_api_password.LegacyApiPasswordAuthProvider(
|
||||||
|
hass, hass.auth._store, {
|
||||||
|
'type': 'legacy_api_password'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
hass.auth._providers[(prv.type, prv.id)] = prv
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def local_auth(hass):
|
||||||
|
"""Load local auth provider."""
|
||||||
|
prv = homeassistant.HassAuthProvider(
|
||||||
|
hass, hass.auth._store, {
|
||||||
|
'type': 'homeassistant'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
hass.auth._providers[(prv.type, prv.id)] = prv
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def hass_client(hass, aiohttp_client, hass_access_token):
|
||||||
|
"""Return an authenticated HTTP client."""
|
||||||
|
async def auth_client():
|
||||||
|
"""Return an authenticated client."""
|
||||||
|
return await aiohttp_client(hass.http.app, headers={
|
||||||
|
'Authorization': "Bearer {}".format(hass_access_token)
|
||||||
|
})
|
||||||
|
|
||||||
|
return auth_client
|
||||||
|
|
|
@ -14,7 +14,7 @@ from tests.common import get_test_home_assistant
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def camera_client(hass, aiohttp_client):
|
def camera_client(hass, hass_client):
|
||||||
"""Fixture to fetch camera streams."""
|
"""Fixture to fetch camera streams."""
|
||||||
assert hass.loop.run_until_complete(async_setup_component(hass, 'camera', {
|
assert hass.loop.run_until_complete(async_setup_component(hass, 'camera', {
|
||||||
'camera': {
|
'camera': {
|
||||||
|
@ -23,7 +23,7 @@ def camera_client(hass, aiohttp_client):
|
||||||
'mjpeg_url': 'http://example.com/mjpeg_stream',
|
'mjpeg_url': 'http://example.com/mjpeg_stream',
|
||||||
}}))
|
}}))
|
||||||
|
|
||||||
yield hass.loop.run_until_complete(aiohttp_client(hass.http.app))
|
yield hass.loop.run_until_complete(hass_client())
|
||||||
|
|
||||||
|
|
||||||
class TestHelpersAiohttpClient(unittest.TestCase):
|
class TestHelpersAiohttpClient(unittest.TestCase):
|
||||||
|
|
|
@ -808,7 +808,6 @@ async def test_auth_provider_config(hass):
|
||||||
assert len(hass.auth.auth_providers) == 2
|
assert len(hass.auth.auth_providers) == 2
|
||||||
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
||||||
assert hass.auth.auth_providers[1].type == 'legacy_api_password'
|
assert hass.auth.auth_providers[1].type == 'legacy_api_password'
|
||||||
assert hass.auth.active is True
|
|
||||||
assert len(hass.auth.auth_mfa_modules) == 2
|
assert len(hass.auth.auth_mfa_modules) == 2
|
||||||
assert hass.auth.auth_mfa_modules[0].id == 'totp'
|
assert hass.auth.auth_mfa_modules[0].id == 'totp'
|
||||||
assert hass.auth.auth_mfa_modules[1].id == 'second'
|
assert hass.auth.auth_mfa_modules[1].id == 'second'
|
||||||
|
@ -830,7 +829,6 @@ async def test_auth_provider_config_default(hass):
|
||||||
|
|
||||||
assert len(hass.auth.auth_providers) == 1
|
assert len(hass.auth.auth_providers) == 1
|
||||||
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
||||||
assert hass.auth.active is True
|
|
||||||
assert len(hass.auth.auth_mfa_modules) == 1
|
assert len(hass.auth.auth_mfa_modules) == 1
|
||||||
assert hass.auth.auth_mfa_modules[0].id == 'totp'
|
assert hass.auth.auth_mfa_modules[0].id == 'totp'
|
||||||
|
|
||||||
|
@ -852,7 +850,6 @@ async def test_auth_provider_config_default_api_password(hass):
|
||||||
assert len(hass.auth.auth_providers) == 2
|
assert len(hass.auth.auth_providers) == 2
|
||||||
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
||||||
assert hass.auth.auth_providers[1].type == 'legacy_api_password'
|
assert hass.auth.auth_providers[1].type == 'legacy_api_password'
|
||||||
assert hass.auth.active is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_auth_provider_config_default_trusted_networks(hass):
|
async def test_auth_provider_config_default_trusted_networks(hass):
|
||||||
|
@ -873,7 +870,6 @@ async def test_auth_provider_config_default_trusted_networks(hass):
|
||||||
assert len(hass.auth.auth_providers) == 2
|
assert len(hass.auth.auth_providers) == 2
|
||||||
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
assert hass.auth.auth_providers[0].type == 'homeassistant'
|
||||||
assert hass.auth.auth_providers[1].type == 'trusted_networks'
|
assert hass.auth.auth_providers[1].type == 'trusted_networks'
|
||||||
assert hass.auth.active is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_disallowed_auth_provider_config(hass):
|
async def test_disallowed_auth_provider_config(hass):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue