Merge pull request #2149 from home-assistant/dev

Release 249
This commit is contained in:
Pascal Vizeli 2020-10-19 16:44:30 +02:00 committed by GitHub
commit 20bb890a27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 566 additions and 429 deletions

View File

@ -429,4 +429,4 @@ jobs:
coverage report
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.0.13
uses: codecov/codecov-action@v1.0.14

@ -1 +1 @@
Subproject commit 713e0579f8ccc73d1ea6c287d45a61372e5f39b7
Subproject commit eec4a91ad8c3f8904c5c3dc5996946977988d0e1

View File

@ -221,7 +221,11 @@ class RestAPI(CoreSysAttributes):
api_auth.coresys = self.coresys
self.webapp.add_routes(
[web.post("/auth", api_auth.auth), web.post("/auth/reset", api_auth.reset)]
[
web.post("/auth", api_auth.auth),
web.post("/auth/reset", api_auth.reset),
web.delete("/auth/cache", api_auth.cache),
]
)
def _register_supervisor(self) -> None:

View File

@ -86,3 +86,8 @@ class APIAuth(CoreSysAttributes):
await asyncio.shield(
self.sys_auth.change_password(body[ATTR_USERNAME], body[ATTR_PASSWORD])
)
@api_process
async def cache(self, request: web.Request) -> None:
"""Process cache reset request."""
self.sys_auth.reset_data()

View File

@ -1,9 +1,9 @@
try {
new Function("import('/api/hassio/app/frontend_latest/entrypoint.cbedde60.js')")();
new Function("import('/api/hassio/app/frontend_latest/entrypoint.f7e7035c.js')")();
} catch (err) {
var el = document.createElement('script');
el.src = '/api/hassio/app/frontend_es5/entrypoint.96d2427f.js';
el.src = '/api/hassio/app/frontend_es5/entrypoint.c862ef13.js';
document.body.appendChild(el);
}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"chunk.58899b77fdb7b6ef0605.js","sources":["webpack://home-assistant-frontend/chunk.58899b77fdb7b6ef0605.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"chunk.b3f75e68bf3aecce0738.js","sources":["webpack://home-assistant-frontend/chunk.b3f75e68bf3aecce0738.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"chunk.ce88ac273ca23dcb7f96.js","sources":["webpack://home-assistant-frontend/chunk.ce88ac273ca23dcb7f96.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"chunk.e924fa955086625d80ba.js","sources":["webpack://home-assistant-frontend/chunk.e924fa955086625d80ba.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"entrypoint.96d2427f.js","sources":["webpack://home-assistant-frontend/entrypoint.96d2427f.js"],"mappings":";AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"entrypoint.c862ef13.js","sources":["webpack://home-assistant-frontend/entrypoint.c862ef13.js"],"mappings":";AAAA","sourceRoot":""}

View File

@ -1,3 +1,3 @@
{
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.96d2427f.js"
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.c862ef13.js"
}

View File

@ -1 +1 @@
{"version":3,"file":"chunk.b0454f2cd9e4fa593b0b.js","sources":["webpack://home-assistant-frontend/chunk.b0454f2cd9e4fa593b0b.js"],"mappings":"AAAA;;;AAyMA;AACA;;;AAGA;;;;;AAKA;;AAEA;AAEA;AACA;;;;;;;AAQA;;;;;AAKA;;AAEA;AAEA;AACA;;;;;;;AAQA;;;;;AAOA;;;;;;;;;;;;;;;;AAuBA;AAymBA;;AAEA;;AAEA;AACA;;AAIA;AAkJA;;;;AAIA;;AAEA;AACA;;;AAGA;;;;AAIA;AACA;;;;;;AAQA;;;;;;;;;;;;;;;;;;;;;;AA6HA;;;AAyGA;AACA;;;;;;;;AAQA;;AAGA;;;AAGA;;AAEA;AACA;;;;AAIA;;;;;;;AAQA;;;AAGA;;;;;AAvCA;;;;;;;;;;;;;;;AA8KA;;;AA+EA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AApBA;;;;;;;;;;;AA4CA;;;AAwGA;;AAEA;;;;AARA;;;;;;;;;;;;AAiCA;;AAwBA;AACA;AACA;;;AAMA;;;;AA0IA;;;AAMA;AACA;;;AAGA;;AAEA;;AAKA;;AAEA;;AAEA;;AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6EA;AAiLA;;;;AAIA;AACA;AACA;AACA;;;AAGA;;;;;;;;AAQA;AACA;AACA;;;;AAIA;AACA;;;AAGA;;;AAGA;AACA;;;;;;;AAOA;;;;;;;;;AASA;;AAEA;AACA;;;;AAIA;;AAEA;;;;AAIA;;;AAGA;;;;AAIA;AACA;AACA;;;AAGA;;;;;;AAMA;;AAEA;AACA;;;;AAIA;;;AAGA;;AAEA;;AAEA;AACA;AAIA;;;;;;AAMA;;AAEA;AACA;;AAEA;AAKA;;AAEA;;;;AAIA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;AAGA;;AAEA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;AACA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;;AAMA;;;AAGA;;;AAGA;;AAEA;;;;;;;;AAQA;AACA;;;;;AAKA;AACA;;;;;;;;AAQA;AACA;;;;AAIA;AACA;AACA;;;;;;;;;AASA;AACA;;;;AAIA;AACA;AACA;;;;;AAKA;;;AAGA;AACA;AACA;;;;AAIA;AACA;AACA;;;;;;;;AAQA;AACA;;;;AAIA;;AAEA;AACA;;;AAGA;AACA;;;AAGA;AACA;;;;;;AAMA;AACA;;;;AAIA;AACA;;;;AAIA;;AAEA;;;;;;;;;;AAUA;AACA;AACA;;;AAGA;;;AAGA;;;;AAIA;;;AAGA;AACA;;;;AAIA;AACA;AACA;;;;;;AAMA;AACA;;;;;;;;AAQA;;;;AAIA;;;;AAIA;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAocA;;;AAoFA;AACA;AACA;;;AARA;;;;;;AA4BA;AAkGA;;AAEA;;AAEA;AACA;AACA;;;AAGA;;;AAKA;;;;;;;;;AAgBA;;;AAiGA;AACA;;;AAPA;;;;;;AA2BA;;AAmQA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;;;AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA","sourceRoot":""}
{"version":3,"file":"chunk.2fb2417488a6c7184d5f.js","sources":["webpack://home-assistant-frontend/chunk.2fb2417488a6c7184d5f.js"],"mappings":"AAAA;;;AAyMA;AACA;;;AAGA;;;;;AAKA;;AAEA;AAEA;AACA;;;;;;;AAQA;;;;;AAKA;;AAEA;AAEA;AACA;;;;;;;AAQA;;;;;AAOA;;;;;;;;;;;;;;;;AAuBA;AAymBA;;AAEA;;AAEA;AACA;;AAIA;AA0JA;;;;AAIA;;AAEA;AACA;;;AAGA;;;;AAIA;AACA;;;;;;AAQA;;;;;;;;;;;;;;;;;;;;;;AA6HA;;;AAyGA;AACA;;;;;;;;AAQA;;AAGA;;;AAGA;;AAEA;AACA;;;;AAIA;;;;;;;AAQA;;;AAGA;;;;;AAvCA;;;;;;;;;;;;;;;AA8KA;;;AA+EA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AApBA;;;;;;;;;;;AA4CA;;;AAwGA;;AAEA;;;;AARA;;;;;;;;;;;;AAiCA;;AAwBA;AACA;AACA;;;AAMA;;;;AA0IA;;;AAMA;AACA;;;AAGA;;AAEA;;AAKA;;AAEA;;AAEA;;AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6EA;AAiLA;;;;AAIA;AACA;AACA;AACA;;;AAGA;;;;;;;;AAQA;AACA;AACA;;;;AAIA;AACA;;;AAGA;;;AAGA;AACA;;;;;;;AAOA;;;;;;;;;AASA;;AAEA;AACA;;;;AAIA;;AAEA;;;;AAIA;;;AAGA;;;;AAIA;AACA;AACA;;;AAGA;;;;;;AAMA;;AAEA;AACA;;;;AAIA;;;AAGA;;AAEA;;AAEA;AACA;AAIA;;;;;;AAMA;;AAEA;AACA;;AAEA;AAKA;;AAEA;;;;AAIA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;AAGA;;AAEA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;AACA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;AAKA;;AAEA;AACA;;AAEA;;;;;;AAMA;;;AAGA;;;AAGA;;AAEA;;;;;;;;AAQA;AACA;;;;;AAKA;AACA;;;;;;;;AAQA;AACA;;;;AAIA;AACA;AACA;;;;;;;;;AASA;AACA;;;;AAIA;AACA;AACA;;;;;AAKA;;;AAGA;AACA;AACA;;;;AAIA;AACA;AACA;;;;;;;;AAQA;AACA;;;;AAIA;;AAEA;AACA;;;AAGA;AACA;;;AAGA;AACA;;;;;;AAMA;AACA;;;;AAIA;AACA;;;;AAIA;;AAEA;;;;;;;;;;AAUA;AACA;AACA;;;AAGA;;;AAGA;;;;AAIA;;;AAGA;AACA;;;;AAIA;AACA;AACA;;;;;;AAMA;AACA;;;;;;;;AAQA;;;;AAIA;;;;AAIA;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAocA;;;AAoFA;AACA;AACA;;;AARA;;;;;;AA4BA;AAkGA;;AAEA;;AAEA;AACA;AACA;;;AAGA;;;AAKA;;;;;;;;;AAgBA;;;AAiGA;AACA;;;AAPA;;;;;;AA2BA;;AAmQA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;;;AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA","sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":3,"file":"chunk.5a3a0f397a7ce1a5a319.js","sources":["webpack://home-assistant-frontend/chunk.5a3a0f397a7ce1a5a319.js"],"mappings":"AAAA;AAwOA;;;;AAIA;;;AAGA;;;;;AAKA;AACA;AACA;;;;AAIA;AACA;;AAIA;;AAEA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;;;AAKA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;;AAGA;;;;;AAKA;;;AAGA;;;AAGA;;AAEA;;;AAGA;;;AAGA;AACA;AACA;;;;AAIA;;;;;;AApGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwJA","sourceRoot":""}
{"version":3,"file":"chunk.81c4b013b6645dfdeef0.js","sources":["webpack://home-assistant-frontend/chunk.81c4b013b6645dfdeef0.js"],"mappings":"AAAA;AA2OA;;;;AAIA;;;AAGA;;;;;AAKA;AACA;AACA;;;;AAIA;AACA;;AAIA;;AAEA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;AAGA;;AAGA;AACA;;AAEA;;;;AAKA;AACA;;;;;AAKA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;;AAGA;;;;;AAKA;;;AAGA;;;AAGA;;AAEA;;;AAGA;;;AAGA;AACA;AACA;;;;AAIA;;;;;;AApGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwJA","sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
{
"entrypoint.js": "/api/hassio/app/frontend_latest/entrypoint.cbedde60.js"
"entrypoint.js": "/api/hassio/app/frontend_latest/entrypoint.f7e7035c.js"
}

View File

@ -82,6 +82,7 @@ ADDONS_ROLE_ACCESS = {
r"^(?:"
r"|/addons(?:/[^/]+/(?!security).+|/reload)?"
r"|/audio/.+"
r"|/auth/cache"
r"|/cli/.+"
r"|/core/.+"
r"|/dns/.+"

View File

@ -1,6 +1,8 @@
"""Manage SSO for Add-ons with Home Assistant user."""
import asyncio
import hashlib
import logging
from typing import Dict, Optional
from .addons.addon import Addon
from .const import ATTR_ADDON, ATTR_PASSWORD, ATTR_USERNAME, FILE_HASSIO_AUTH
@ -20,16 +22,21 @@ class Auth(JsonConfig, CoreSysAttributes):
super().__init__(FILE_HASSIO_AUTH, SCHEMA_AUTH_CONFIG)
self.coresys: CoreSys = coresys
def _check_cache(self, username: str, password: str) -> bool:
self._running: Dict[str, asyncio.Task] = {}
def _check_cache(self, username: str, password: str) -> Optional[bool]:
"""Check password in cache."""
username_h = self._rehash(username)
password_h = self._rehash(password, username)
if username_h not in self._data:
_LOGGER.debug("Username '%s' not is in cache", username)
return None
# check cache
if self._data.get(username_h) == password_h:
_LOGGER.debug("Username '%s' is in cache", username)
return True
_LOGGER.warning("Username '%s' not is in cache", username)
return False
def _update_cache(self, username: str, password: str) -> None:
@ -61,11 +68,29 @@ class Auth(JsonConfig, CoreSysAttributes):
raise AuthError()
_LOGGER.info("Auth request from '%s' for '%s'", addon.slug, username)
# Get from cache
cache_hit = self._check_cache(username, password)
# Check API state
if not await self.sys_homeassistant.api.check_api_state():
_LOGGER.debug("Home Assistant not running, checking cache")
return self._check_cache(username, password)
return cache_hit is True
# No cache hit
if cache_hit is None:
return await self._backend_login(addon, username, password)
# Home Assistant Core take over 1-2sec to validate it
# Let's use the cache and update the cache in background
if username not in self._running:
self._running[username] = self.sys_create_task(
self._backend_login(addon, username, password)
)
return cache_hit
async def _backend_login(self, addon: Addon, username: str, password: str) -> bool:
"""Check username login on core."""
try:
async with self.sys_homeassistant.api.make_request(
"post",
@ -87,6 +112,8 @@ class Auth(JsonConfig, CoreSysAttributes):
return False
except HomeAssistantAPIError:
_LOGGER.error("Can't request auth on Home Assistant!")
finally:
self._running.pop(username, None)
raise AuthError()

View File

@ -3,7 +3,7 @@ from enum import Enum
from ipaddress import ip_network
from pathlib import Path
SUPERVISOR_VERSION = "248"
SUPERVISOR_VERSION = "249"
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
URL_HASSIO_APPARMOR = "https://version.home-assistant.io/apparmor.txt"

View File

@ -12,7 +12,7 @@ from ..exceptions import (
MulticastError,
ObserverError,
)
from ..resolution.const import MINIMUM_FREE_SPACE_THRESHOLD, IssueType
from ..resolution.const import MINIMUM_FREE_SPACE_THRESHOLD, ContextType, IssueType
_LOGGER: logging.Logger = logging.getLogger(__name__)
@ -128,10 +128,14 @@ class Tasks(CoreSysAttributes):
)
continue
if self.sys_host.info.free_space > MINIMUM_FREE_SPACE_THRESHOLD:
_LOGGER.warning("Not enough free space, pausing add-on updates")
_LOGGER.info("Aviable free space is %s", self.sys_host.info.free_space)
self.sys_resolution.issues = IssueType.FREE_SPACE
if self.sys_host.info.free_space < MINIMUM_FREE_SPACE_THRESHOLD:
_LOGGER.warning(
"Not enough free space, pausing add-on updates - available space %f",
self.sys_host.info.free_space,
)
self.sys_resolution.create_issue(
IssueType.FREE_SPACE, ContextType.SYSTEM
)
return
# Run Add-on update sequential
@ -152,10 +156,12 @@ class Tasks(CoreSysAttributes):
_LOGGER.warning("Ignore Supervisor updates on dev channel!")
return
if self.sys_host.info.free_space > MINIMUM_FREE_SPACE_THRESHOLD:
_LOGGER.warning("Not enough free space, pausing supervisor update")
_LOGGER.info("Aviable free space is %s", self.sys_host.info.free_space)
self.sys_resolution.issues = IssueType.FREE_SPACE
if self.sys_host.info.free_space < MINIMUM_FREE_SPACE_THRESHOLD:
_LOGGER.warning(
"Not enough free space, pausing supervisor update - available space %s",
self.sys_host.info.free_space,
)
self.sys_resolution.create_issue(IssueType.FREE_SPACE, ContextType.SYSTEM)
return
_LOGGER.info(

View File

@ -98,8 +98,10 @@ class SnapshotManager(CoreSysAttributes):
# Already exists?
if snapshot.slug in self.snapshots_obj:
_LOGGER.error("Snapshot %s already exists!", snapshot.slug)
return None
_LOGGER.warning(
"Snapshot %s already exists! overwriting snapshot", snapshot.slug
)
self.remove(self.get(snapshot.slug))
# Move snapshot to backup
tar_origin = Path(self.sys_config.path_backup, f"{snapshot.slug}.tar")

View File

@ -107,6 +107,9 @@ async def coresys(loop, docker, dbus, network_manager, aiohttp_client) -> CoreSy
# Mock save json
coresys_obj.ingress.save_data = MagicMock()
coresys_obj.auth.save_data = MagicMock()
coresys_obj.updater.save_data = MagicMock()
coresys_obj.config.save_data = MagicMock()
# Mock test client
coresys_obj.arch._default_arch = "amd64"

86
tests/test_auth.py Normal file
View File

@ -0,0 +1,86 @@
"""Test auth object."""
import asyncio
from unittest.mock import AsyncMock, MagicMock
import pytest
# pylint: disable=protected-access
@pytest.fixture(name="mock_auth_backend", autouse=True)
def mock_auth_backend_fixture(coresys):
"""Fix auth backend request."""
mock_auth_backend = AsyncMock()
coresys.auth._backend_login = mock_auth_backend
yield mock_auth_backend
@pytest.fixture(name="mock_api_state", autouse=True)
def mock_api_state_fixture(coresys):
"""Fix auth backend request."""
mock_api_state = AsyncMock()
coresys.homeassistant.api.check_api_state = mock_api_state
yield mock_api_state
@pytest.mark.asyncio
async def test_auth_request_with_backend(coresys, mock_auth_backend, mock_api_state):
"""Make simple auth request."""
addon = MagicMock()
mock_auth_backend.return_value = True
mock_api_state.return_value = True
assert await coresys.auth.check_login(addon, "username", "password")
assert mock_auth_backend.called
@pytest.mark.asyncio
async def test_auth_request_without_backend(coresys, mock_auth_backend, mock_api_state):
"""Make simple auth without request."""
addon = MagicMock()
mock_auth_backend.return_value = True
mock_api_state.return_value = False
assert not await coresys.auth.check_login(addon, "username", "password")
assert not mock_auth_backend.called
@pytest.mark.asyncio
async def test_auth_request_without_backend_cache(
coresys, mock_auth_backend, mock_api_state
):
"""Make simple auth without request."""
addon = MagicMock()
mock_auth_backend.return_value = True
mock_api_state.return_value = False
coresys.auth._update_cache("username", "password")
assert await coresys.auth.check_login(addon, "username", "password")
assert not mock_auth_backend.called
@pytest.mark.asyncio
async def test_auth_request_with_backend_cache_update(
coresys, mock_auth_backend, mock_api_state
):
"""Make simple auth without request and cache update."""
addon = MagicMock()
mock_auth_backend.return_value = False
mock_api_state.return_value = True
coresys.auth._update_cache("username", "password")
assert await coresys.auth.check_login(addon, "username", "password")
await asyncio.sleep(0)
assert mock_auth_backend.called
coresys.auth._dismatch_cache("username", "password")
assert not await coresys.auth.check_login(addon, "username", "password")