Compare commits

..

32 Commits
123 ... 128

Author SHA1 Message Date
Pascal Vizeli
1dc9f35e12 Merge pull request #674 from home-assistant/dev
Release 128
2018-08-29 10:13:57 +02:00
Pascal Vizeli
051b63c7cc Fix access token property (#673)
* Fix access token property

* revert
2018-08-28 17:04:39 +02:00
Pascal Vizeli
aac4b9b24a Snapshot/Restore Home-Assistant token (#672)
* Snapshot/Restore Home-Assistant token

* Encrypt token & check api

* fix lint
2018-08-28 16:32:17 +02:00
Paulus Schoutsen
1a208a20b6 Handle access token expiration (#671) 2018-08-28 12:14:40 +02:00
Pascal Vizeli
b1e8722ead Update: pycryptodome to 3.6.6 (#670) 2018-08-28 12:04:32 +02:00
Pascal Vizeli
a66af6e903 Update aiohttp to 3.4.0 (#668)
Update: aiohttp to 3.4.0
2018-08-28 01:18:38 +02:00
Pascal Vizeli
0c345fc615 Bump version 128 2018-08-19 22:05:42 +02:00
Pascal Vizeli
087b082a6b Merge pull request #660 from home-assistant/dev
Release 127
2018-08-19 22:03:49 +02:00
Pascal Vizeli
0b85209eae Detect running record migration (#659)
* Detect running record migration

* Fix order

* Change order second one
2018-08-19 21:58:19 +02:00
Pascal Vizeli
d81bc7de46 Change rating 1-6 (#658) 2018-08-19 18:17:14 +02:00
Pascal Vizeli
e3a99b9f89 Fix /share inside whitelist (#657) 2018-08-18 15:05:18 +02:00
Pascal Vizeli
5d319b37ea Bump verison 127 2018-08-16 23:38:57 +02:00
Pascal Vizeli
9f25606986 Merge pull request #653 from home-assistant/dev
Release 126
2018-08-16 23:38:24 +02:00
Pascal Vizeli
ecd12732ee New generation of security and access (#652)
* New generation of security and access

* Update const.py

* Update validate.py

* Update addon.py

* Update validate.py

* Fix name

* Allow access

* Fix

* add logs

* change message

* add rating

* fix lint

* fix lint

* fix

* Fix
2018-08-16 22:49:08 +02:00
Pascal Vizeli
85fbde8e36 Fix Dockerfile 2018-08-16 01:42:56 +02:00
Pascal Vizeli
6e6c2c3efb Change timezone handling (#641)
* Change timezone handling

* Update dt.py

* Update homeassistant.py

* fix

* Use new timezone

* fix handling

* fix regex

* fix regex

* Rename old config

* fix lint

* simplify

* fix regex

* fix

* cleanup

* cleanup

* fix

* fix find

* mm
2018-08-16 01:40:20 +02:00
Pascal Vizeli
0d4a808449 Improve docker build cache for supervisor (#651) 2018-08-15 23:52:52 +02:00
Pascal Vizeli
087f746647 update docker API to 3.5.0 (#650) 2018-08-15 22:05:13 +02:00
Pascal Vizeli
640d66ad1a Update uvloop 0.11.2 (#648) 2018-08-15 21:38:57 +02:00
Pascal Vizeli
f5f5ed83af Bump version 126 2018-08-09 14:38:34 +02:00
Pascal Vizeli
95f01a1161 Merge pull request #640 from home-assistant/dev
Release 125
2018-08-09 14:37:56 +02:00
Pascal Vizeli
b84e7e7d94 Allow to reset token (#639)
* Allow to reset token

* Update homeassistant.py
2018-08-09 14:37:00 +02:00
Pascal Vizeli
5d7018f3f0 Bump version 125 2018-08-09 01:05:21 +02:00
Pascal Vizeli
d87a85ceb5 Merge pull request #636 from home-assistant/dev
Release 124
2018-08-09 01:03:47 +02:00
Pascal Vizeli
9ab6e80b6f Cleanup logging (#637)
* Cleanup logging

* simplify
2018-08-09 01:03:00 +02:00
Pascal Vizeli
78e91e859e Add add-on support for docker sock ro (#635)
* Add add-on support for docker sock ro

* fix
2018-08-09 00:42:33 +02:00
Pascal Vizeli
9eee8eade6 Fix gpio mapping on amd64 systems (#634) 2018-08-09 00:29:20 +02:00
Pascal Vizeli
124ce0b8b7 Update voluptuous 0.11.5 (#622) 2018-08-09 00:06:49 +02:00
Pascal Vizeli
00e7d96472 Fix new auth system (#633)
* Fix new auth system

* Update exceptions.py

* Update exceptions.py

* Update homeassistant.py

* Update homeassistant.py

* Update homeassistant.py

* Fix some API Errors

* fix lint
2018-08-09 00:05:08 +02:00
Pascal Vizeli
398815efd8 Bump version to 124 2018-08-08 19:22:12 +02:00
Pascal Vizeli
bdc2bdcf56 Merge pull request #631 from ndarilek/dev
Add SYS_RESOURCE to list of valid privileges
2018-08-07 17:06:15 +02:00
Nolan Darilek
68eafb0a7d Add SYS_RESOURCE to list of valid privileges 2018-08-07 03:21:21 +00:00
23 changed files with 300 additions and 91 deletions

5
API.md
View File

@@ -478,10 +478,14 @@ Get all available addons.
"changelog": "bool", "changelog": "bool",
"hassio_api": "bool", "hassio_api": "bool",
"homeassistant_api": "bool", "homeassistant_api": "bool",
"full_access": "bool",
"protected": "bool",
"rating": "1-6",
"stdin": "bool", "stdin": "bool",
"webui": "null|http(s)://[HOST]:port/xy/zx", "webui": "null|http(s)://[HOST]:port/xy/zx",
"gpio": "bool", "gpio": "bool",
"devicetree": "bool", "devicetree": "bool",
"docker_api": "bool",
"audio": "bool", "audio": "bool",
"audio_input": "null|0,0", "audio_input": "null|0,0",
"audio_output": "null|0,0", "audio_output": "null|0,0",
@@ -506,6 +510,7 @@ Get all available addons.
"CONTAINER": "port|[ip, port]" "CONTAINER": "port|[ip, port]"
}, },
"options": {}, "options": {},
"protected": "bool",
"audio_output": "null|0,0", "audio_output": "null|0,0",
"audio_input": "null|0,0" "audio_input": "null|0,0"
} }

View File

@@ -1,15 +1,17 @@
ARG BUILD_FROM ARG BUILD_FROM
FROM $BUILD_FROM FROM $BUILD_FROM
# Setup base # Install base
COPY requirements.txt /usr/src/
RUN apk add --no-cache \ RUN apk add --no-cache \
git \ git \
socat \ socat \
glib \ glib \
libstdc++ \ libstdc++ \
eudev-libs \ eudev-libs
&& apk add --no-cache --virtual .build-dependencies \
# Install requirements
COPY requirements.txt /usr/src/
RUN apk add --no-cache --virtual .build-dependencies \
make \ make \
g++ \ g++ \
&& pip3 install --no-cache-dir -r /usr/src/requirements.txt \ && pip3 install --no-cache-dir -r /usr/src/requirements.txt \

View File

@@ -25,8 +25,9 @@ from ..const import (
ATTR_HASSIO_API, ATTR_AUDIO, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT, ATTR_HASSIO_API, ATTR_AUDIO, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT,
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY, ATTR_HOST_IPC, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY, ATTR_HOST_IPC,
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_DISCOVERY, ATTR_SERVICES, ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_DISCOVERY, ATTR_SERVICES,
ATTR_APPARMOR, ATTR_DEVICETREE, SECURITY_PROFILE, SECURITY_DISABLE, ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API, ATTR_FULL_ACCESS,
SECURITY_DEFAULT) ATTR_PROTECTED,
SECURITY_PROFILE, SECURITY_DISABLE, SECURITY_DEFAULT)
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..docker.addon import DockerAddon from ..docker.addon import DockerAddon
from ..utils.json import write_json_file, read_json_file from ..utils.json import write_json_file, read_json_file
@@ -201,6 +202,18 @@ class Addon(CoreSysAttributes):
return self._data.cache[self._id][ATTR_VERSION] return self._data.cache[self._id][ATTR_VERSION]
return self.version_installed return self.version_installed
@property
def protected(self):
"""Return if addon is in protected mode."""
if self.is_installed:
return self._data.user[self._id][ATTR_PROTECTED]
return True
@protected.setter
def protected(self, value):
"""Set addon in protected mode."""
self._data.user[self._id][ATTR_PROTECTED] = value
@property @property
def startup(self): def startup(self):
"""Return startup type of addon.""" """Return startup type of addon."""
@@ -335,6 +348,11 @@ class Addon(CoreSysAttributes):
"""Return if the add-on don't support hass labels.""" """Return if the add-on don't support hass labels."""
return self._mesh.get(ATTR_LEGACY) return self._mesh.get(ATTR_LEGACY)
@property
def access_docker_api(self):
"""Return if the add-on need read-only docker API access."""
return self._mesh.get(ATTR_DOCKER_API)
@property @property
def access_hassio_api(self): def access_hassio_api(self):
"""Return True if the add-on access to hassio api.""" """Return True if the add-on access to hassio api."""
@@ -355,6 +373,11 @@ class Addon(CoreSysAttributes):
"""Return True if the add-on access to gpio interface.""" """Return True if the add-on access to gpio interface."""
return self._mesh[ATTR_GPIO] return self._mesh[ATTR_GPIO]
@property
def with_full_access(self):
"""Return True if the add-on want full access to hardware."""
return self._mesh[ATTR_FULL_ACCESS]
@property @property
def with_devicetree(self): def with_devicetree(self):
"""Return True if the add-on read access to devicetree.""" """Return True if the add-on read access to devicetree."""

View File

@@ -4,11 +4,53 @@ import hashlib
import logging import logging
import re import re
from ..const import (
SECURITY_DISABLE, SECURITY_PROFILE, PRIVILEGED_NET_ADMIN,
PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO)
RE_SHA1 = re.compile(r"[a-f0-9]{8}") RE_SHA1 = re.compile(r"[a-f0-9]{8}")
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def rating_security(addon):
"""Return 1-5 for security rating.
1 = not secure
5 = high secure
"""
rating = 5
# AppArmor
if addon.apparmor == SECURITY_DISABLE:
rating += -1
elif addon.apparmor == SECURITY_PROFILE:
rating += 1
# API Access
if addon.access_hassio_api or addon.access_homeassistant_api:
rating += -1
# Privileged options
if addon.privileged in (PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN,
PRIVILEGED_SYS_RAWIO):
rating += -1
# Not secure Networking
if addon.host_network:
rating += -1
# Full Access
if addon.with_full_access:
rating += -2
# Docker Access
if addon.access_docker_api:
rating = 1
return max(min(6, rating), 1)
def get_hash_from_repository(name): def get_hash_from_repository(name):
"""Generate a hash from repository.""" """Generate a hash from repository."""
key = name.lower().encode() key = name.lower().encode()

View File

@@ -18,7 +18,11 @@ from ..const import (
ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API, ATTR_BUILD_FROM, ATTR_SQUASH, ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API, ATTR_BUILD_FROM, ATTR_SQUASH,
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY, ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY,
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_SERVICES, ATTR_DISCOVERY, ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_SERVICES, ATTR_DISCOVERY,
ATTR_APPARMOR, ATTR_DEVICETREE) ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API, ATTR_PROTECTED,
ATTR_FULL_ACCESS,
PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO,
PRIVILEGED_IPC_LOCK, PRIVILEGED_SYS_TIME, PRIVILEGED_SYS_NICE,
PRIVILEGED_SYS_RESOURCE)
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -58,12 +62,13 @@ STARTUP_ALL = [
] ]
PRIVILEGED_ALL = [ PRIVILEGED_ALL = [
"NET_ADMIN", PRIVILEGED_NET_ADMIN,
"SYS_ADMIN", PRIVILEGED_SYS_ADMIN,
"SYS_RAWIO", PRIVILEGED_SYS_RAWIO,
"IPC_LOCK", PRIVILEGED_IPC_LOCK,
"SYS_TIME", PRIVILEGED_SYS_TIME,
"SYS_NICE" PRIVILEGED_SYS_NICE,
PRIVILEGED_SYS_RESOURCE,
] ]
BASE_IMAGE = { BASE_IMAGE = {
@@ -109,6 +114,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): vol.Coerce(str)}, vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): vol.Coerce(str)},
vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGED_ALL)], vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGED_ALL)],
vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(), vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(),
vol.Optional(ATTR_FULL_ACCESS, default=False): vol.Boolean(),
vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(), vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(),
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(), vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(), vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
@@ -116,6 +122,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(), vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
vol.Optional(ATTR_STDIN, default=False): vol.Boolean(), vol.Optional(ATTR_STDIN, default=False): vol.Boolean(),
vol.Optional(ATTR_LEGACY, default=False): vol.Boolean(), vol.Optional(ATTR_LEGACY, default=False): vol.Boolean(),
vol.Optional(ATTR_DOCKER_API, default=False): vol.Boolean(),
vol.Optional(ATTR_SERVICES): [vol.Match(RE_SERVICE)], vol.Optional(ATTR_SERVICES): [vol.Match(RE_SERVICE)],
vol.Optional(ATTR_DISCOVERY): [vol.Match(RE_DISCOVERY)], vol.Optional(ATTR_DISCOVERY): [vol.Match(RE_DISCOVERY)],
vol.Required(ATTR_OPTIONS): dict, vol.Required(ATTR_OPTIONS): dict,
@@ -168,6 +175,7 @@ SCHEMA_ADDON_USER = vol.Schema({
vol.Optional(ATTR_NETWORK): DOCKER_PORTS, vol.Optional(ATTR_NETWORK): DOCKER_PORTS,
vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE, vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE,
vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE, vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE,
vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(),
}, extra=vol.REMOVE_EXTRA) }, extra=vol.REMOVE_EXTRA)

View File

@@ -6,6 +6,7 @@ import voluptuous as vol
from voluptuous.humanize import humanize_error from voluptuous.humanize import humanize_error
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
from ..addons.utils import rating_security
from ..const import ( from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS,
ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY, ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY,
@@ -17,10 +18,12 @@ from ..const import (
ATTR_CHANGELOG, ATTR_HOST_IPC, ATTR_HOST_DBUS, ATTR_LONG_DESCRIPTION, ATTR_CHANGELOG, ATTR_HOST_IPC, ATTR_HOST_DBUS, ATTR_LONG_DESCRIPTION,
ATTR_CPU_PERCENT, ATTR_MEMORY_LIMIT, ATTR_MEMORY_USAGE, ATTR_NETWORK_TX, ATTR_CPU_PERCENT, ATTR_MEMORY_LIMIT, ATTR_MEMORY_USAGE, ATTR_NETWORK_TX,
ATTR_NETWORK_RX, ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_ICON, ATTR_SERVICES, ATTR_NETWORK_RX, ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_ICON, ATTR_SERVICES,
ATTR_DISCOVERY, ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DISCOVERY, ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API,
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT) ATTR_FULL_ACCESS, ATTR_PROTECTED, ATTR_RATING,
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT, REQUEST_FROM)
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..validate import DOCKER_PORTS, ALSA_DEVICE from ..validate import DOCKER_PORTS, ALSA_DEVICE
from ..exceptions import APINotSupportedError
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -35,6 +38,7 @@ SCHEMA_OPTIONS = vol.Schema({
vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(), vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(),
vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE, vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_DEVICE,
vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE, vol.Optional(ATTR_AUDIO_INPUT): ALSA_DEVICE,
vol.Optional(ATTR_PROTECTED): vol.Boolean(),
}) })
@@ -116,6 +120,8 @@ class APIAddons(CoreSysAttributes):
ATTR_REPOSITORY: addon.repository, ATTR_REPOSITORY: addon.repository,
ATTR_LAST_VERSION: addon.last_version, ATTR_LAST_VERSION: addon.last_version,
ATTR_STATE: await addon.state(), ATTR_STATE: await addon.state(),
ATTR_PROTECTED: addon.protected,
ATTR_RATING: rating_security(addon),
ATTR_BOOT: addon.boot, ATTR_BOOT: addon.boot,
ATTR_OPTIONS: addon.options, ATTR_OPTIONS: addon.options,
ATTR_URL: addon.url, ATTR_URL: addon.url,
@@ -126,6 +132,7 @@ class APIAddons(CoreSysAttributes):
ATTR_HOST_IPC: addon.host_ipc, ATTR_HOST_IPC: addon.host_ipc,
ATTR_HOST_DBUS: addon.host_dbus, ATTR_HOST_DBUS: addon.host_dbus,
ATTR_PRIVILEGED: addon.privileged, ATTR_PRIVILEGED: addon.privileged,
ATTR_FULL_ACCESS: addon.with_full_access,
ATTR_APPARMOR: addon.apparmor, ATTR_APPARMOR: addon.apparmor,
ATTR_DEVICES: self._pretty_devices(addon), ATTR_DEVICES: self._pretty_devices(addon),
ATTR_ICON: addon.with_icon, ATTR_ICON: addon.with_icon,
@@ -137,6 +144,7 @@ class APIAddons(CoreSysAttributes):
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api, ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
ATTR_GPIO: addon.with_gpio, ATTR_GPIO: addon.with_gpio,
ATTR_DEVICETREE: addon.with_devicetree, ATTR_DEVICETREE: addon.with_devicetree,
ATTR_DOCKER_API: addon.access_docker_api,
ATTR_AUDIO: addon.with_audio, ATTR_AUDIO: addon.with_audio,
ATTR_AUDIO_INPUT: addon.audio_input, ATTR_AUDIO_INPUT: addon.audio_input,
ATTR_AUDIO_OUTPUT: addon.audio_output, ATTR_AUDIO_OUTPUT: addon.audio_output,
@@ -149,6 +157,11 @@ class APIAddons(CoreSysAttributes):
"""Store user options for addon.""" """Store user options for addon."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
# Have Access
if addon.slug == request[REQUEST_FROM]:
_LOGGER.error("Add-on can't self modify his options!")
raise APINotSupportedError()
addon_schema = SCHEMA_OPTIONS.extend({ addon_schema = SCHEMA_OPTIONS.extend({
vol.Optional(ATTR_OPTIONS): vol.Any(None, addon.schema), vol.Optional(ATTR_OPTIONS): vol.Any(None, addon.schema),
}) })
@@ -167,6 +180,9 @@ class APIAddons(CoreSysAttributes):
addon.audio_input = body[ATTR_AUDIO_INPUT] addon.audio_input = body[ATTR_AUDIO_INPUT]
if ATTR_AUDIO_OUTPUT in body: if ATTR_AUDIO_OUTPUT in body:
addon.audio_output = body[ATTR_AUDIO_OUTPUT] addon.audio_output = body[ATTR_AUDIO_OUTPUT]
if ATTR_PROTECTED in body:
_LOGGER.warning("Protected flag changing for %s!", addon.slug)
addon.protected = body[ATTR_PROTECTED]
addon.save_data() addon.save_data()
return True return True

View File

@@ -21,17 +21,16 @@ _LOGGER = logging.getLogger(__name__)
SCHEMA_OPTIONS = vol.Schema({ SCHEMA_OPTIONS = vol.Schema({
vol.Optional(ATTR_BOOT): vol.Boolean(), vol.Optional(ATTR_BOOT): vol.Boolean(),
vol.Inclusive(ATTR_IMAGE, 'custom_hass'): vol.Inclusive(ATTR_IMAGE, 'custom_hass'):
vol.Any(None, vol.Coerce(str)), vol.Maybe(vol.Coerce(str)),
vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'):
vol.Any(None, DOCKER_IMAGE), vol.Any(None, DOCKER_IMAGE),
vol.Optional(ATTR_PORT): NETWORK_PORT, vol.Optional(ATTR_PORT): NETWORK_PORT,
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)), vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_SSL): vol.Boolean(), vol.Optional(ATTR_SSL): vol.Boolean(),
vol.Optional(ATTR_WATCHDOG): vol.Boolean(), vol.Optional(ATTR_WATCHDOG): vol.Boolean(),
vol.Optional(ATTR_WAIT_BOOT): vol.Optional(ATTR_WAIT_BOOT):
vol.All(vol.Coerce(int), vol.Range(min=60)), vol.All(vol.Coerce(int), vol.Range(min=60)),
# Required once we enforce user system vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_REFRESH_TOKEN): str,
}) })
SCHEMA_VERSION = vol.Schema({ SCHEMA_VERSION = vol.Schema({

View File

@@ -59,6 +59,8 @@ class APIProxy(CoreSysAttributes):
except HomeAssistantAuthError: except HomeAssistantAuthError:
_LOGGER.error("Authenticate error on API for request %s", path) _LOGGER.error("Authenticate error on API for request %s", path)
except HomeAssistantAPIError:
_LOGGER.error("Error on API for request %s", path)
except aiohttp.ClientError as err: except aiohttp.ClientError as err:
_LOGGER.error("Client error on API %s request %s", path, err) _LOGGER.error("Client error on API %s request %s", path, err)
except asyncio.TimeoutError: except asyncio.TimeoutError:
@@ -148,11 +150,12 @@ class APIProxy(CoreSysAttributes):
self.sys_homeassistant.access_token = None self.sys_homeassistant.access_token = None
return await self._websocket_client() return await self._websocket_client()
_LOGGER.error( raise HomeAssistantAuthError()
"Failed authentication to Home-Assistant websocket: %s", data)
except (RuntimeError, HomeAssistantAPIError) as err: except (RuntimeError, ValueError) as err:
_LOGGER.error("Client error on websocket API %s.", err) _LOGGER.error("Client error on websocket API %s.", err)
except HomeAssistantAuthError as err:
_LOGGER.error("Failed authentication to HomeAssistant websocket")
raise HTTPBadGateway() raise HTTPBadGateway()

View File

@@ -30,10 +30,10 @@ def api_process(method):
"""Return api information.""" """Return api information."""
try: try:
answer = await method(api, *args, **kwargs) answer = await method(api, *args, **kwargs)
except RuntimeError as err:
return api_return_error(message=str(err))
except HassioError: except HassioError:
return api_return_error() return api_return_error()
except RuntimeError as err:
return api_return_error(message=str(err))
if isinstance(answer, dict): if isinstance(answer, dict):
return api_return_ok(data=answer) return api_return_ok(data=answer)

View File

@@ -66,10 +66,11 @@ def initialize_system_data(coresys):
config = coresys.config config = coresys.config
# homeassistant config folder # homeassistant config folder
if not config.path_config.is_dir(): if not config.path_homeassistant.is_dir():
_LOGGER.info( _LOGGER.info(
"Create Home-Assistant config folder %s", config.path_config) "Create Home-Assistant config folder %s",
config.path_config.mkdir() config.path_homeassistant)
config.path_homeassistant.mkdir()
# hassio ssl folder # hassio ssl folder
if not config.path_ssl.is_dir(): if not config.path_ssl.is_dir():

View File

@@ -2,8 +2,11 @@
from datetime import datetime from datetime import datetime
import logging import logging
import os import os
import re
from pathlib import Path, PurePath from pathlib import Path, PurePath
import pytz
from .const import ( from .const import (
FILE_HASSIO_CONFIG, HASSIO_DATA, ATTR_TIMEZONE, ATTR_ADDONS_CUSTOM_LIST, FILE_HASSIO_CONFIG, HASSIO_DATA, ATTR_TIMEZONE, ATTR_ADDONS_CUSTOM_LIST,
ATTR_LAST_BOOT, ATTR_WAIT_BOOT) ATTR_LAST_BOOT, ATTR_WAIT_BOOT)
@@ -29,6 +32,8 @@ APPARMOR_DATA = PurePath("apparmor")
DEFAULT_BOOT_TIME = datetime.utcfromtimestamp(0).isoformat() DEFAULT_BOOT_TIME = datetime.utcfromtimestamp(0).isoformat()
RE_TIMEZONE = re.compile(r"time_zone: (?P<timezone>[\w/\-+]+)")
class CoreConfig(JsonConfig): class CoreConfig(JsonConfig):
"""Hold all core config data.""" """Hold all core config data."""
@@ -40,7 +45,21 @@ class CoreConfig(JsonConfig):
@property @property
def timezone(self): def timezone(self):
"""Return system timezone.""" """Return system timezone."""
return self._data[ATTR_TIMEZONE] config_file = Path(self.path_homeassistant, 'configuration.yaml')
try:
assert config_file.exists()
configuration = config_file.read_text()
data = RE_TIMEZONE.search(configuration)
assert data
timezone = data.group('timezone')
pytz.timezone(timezone)
except (pytz.exceptions.UnknownTimeZoneError, OSError, AssertionError):
_LOGGER.debug("Can't parse HomeAssistant timezone")
return self._data[ATTR_TIMEZONE]
return timezone
@timezone.setter @timezone.setter
def timezone(self, value): def timezone(self, value):
@@ -83,12 +102,12 @@ class CoreConfig(JsonConfig):
return PurePath(os.environ['SUPERVISOR_SHARE']) return PurePath(os.environ['SUPERVISOR_SHARE'])
@property @property
def path_extern_config(self): def path_extern_homeassistant(self):
"""Return config path extern for docker.""" """Return config path extern for docker."""
return str(PurePath(self.path_extern_hassio, HOMEASSISTANT_CONFIG)) return str(PurePath(self.path_extern_hassio, HOMEASSISTANT_CONFIG))
@property @property
def path_config(self): def path_homeassistant(self):
"""Return config path inside supervisor.""" """Return config path inside supervisor."""
return Path(HASSIO_DATA, HOMEASSISTANT_CONFIG) return Path(HASSIO_DATA, HOMEASSISTANT_CONFIG)

View File

@@ -2,7 +2,7 @@
from pathlib import Path from pathlib import Path
from ipaddress import ip_network from ipaddress import ip_network
HASSIO_VERSION = '123' HASSIO_VERSION = '128'
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons" URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
URL_HASSIO_VERSION = \ URL_HASSIO_VERSION = \
@@ -178,6 +178,10 @@ ATTR_HASSOS_CLI = 'hassos_cli'
ATTR_VERSION_CLI = 'version_cli' ATTR_VERSION_CLI = 'version_cli'
ATTR_VERSION_CLI_LATEST = 'version_cli_latest' ATTR_VERSION_CLI_LATEST = 'version_cli_latest'
ATTR_REFRESH_TOKEN = 'refresh_token' ATTR_REFRESH_TOKEN = 'refresh_token'
ATTR_DOCKER_API = 'docker_api'
ATTR_FULL_ACCESS = 'full_access'
ATTR_PROTECTED = 'protected'
ATTR_RATING = 'rating'
SERVICE_MQTT = 'mqtt' SERVICE_MQTT = 'mqtt'
@@ -226,6 +230,14 @@ SECURITY_PROFILE = 'profile'
SECURITY_DEFAULT = 'default' SECURITY_DEFAULT = 'default'
SECURITY_DISABLE = 'disable' SECURITY_DISABLE = 'disable'
PRIVILEGED_NET_ADMIN = 'NET_ADMIN'
PRIVILEGED_SYS_ADMIN = 'SYS_ADMIN'
PRIVILEGED_SYS_RAWIO = 'SYS_RAWIO'
PRIVILEGED_IPC_LOCK = 'IPC_LOCK'
PRIVILEGED_SYS_TIME = 'SYS_TIME'
PRIVILEGED_SYS_NICE = 'SYS_NICE'
PRIVILEGED_SYS_RESOURCE = 'SYS_RESOURCE'
FEATURES_SHUTDOWN = 'shutdown' FEATURES_SHUTDOWN = 'shutdown'
FEATURES_REBOOT = 'reboot' FEATURES_REBOOT = 'reboot'
FEATURES_HASSOS = 'hassos' FEATURES_HASSOS = 'hassos'

View File

@@ -66,6 +66,11 @@ class CoreSys:
"""Return True if we run dev modus.""" """Return True if we run dev modus."""
return self._updater.channel == CHANNEL_DEV return self._updater.channel == CHANNEL_DEV
@property
def timezone(self):
"""Return timezone."""
return self._config.timezone
@property @property
def loop(self): def loop(self):
"""Return loop object.""" """Return loop object."""

View File

@@ -1,6 +1,7 @@
"""Init file for HassIO addon docker object.""" """Init file for HassIO addon docker object."""
import logging import logging
import os import os
from pathlib import Path
import docker import docker
import requests import requests
@@ -66,6 +67,11 @@ class DockerAddon(DockerInterface):
return 'host' return 'host'
return None return None
@property
def full_access(self):
"""Return True if full access is enabled."""
return not self.addon.protected and self.addon.with_full_access
@property @property
def hostname(self): def hostname(self):
"""Return slug/id of addon.""" """Return slug/id of addon."""
@@ -85,7 +91,7 @@ class DockerAddon(DockerInterface):
return { return {
**addon_env, **addon_env,
ENV_TIME: self.sys_config.timezone, ENV_TIME: self.sys_timezone,
ENV_TOKEN: self.addon.uuid, ENV_TOKEN: self.addon.uuid,
} }
@@ -172,7 +178,7 @@ class DockerAddon(DockerInterface):
# setup config mappings # setup config mappings
if MAP_CONFIG in addon_mapping: if MAP_CONFIG in addon_mapping:
volumes.update({ volumes.update({
str(self.sys_config.path_extern_config): { str(self.sys_config.path_extern_homeassistant): {
'bind': "/config", 'mode': addon_mapping[MAP_CONFIG] 'bind': "/config", 'mode': addon_mapping[MAP_CONFIG]
}}) }})
@@ -204,14 +210,14 @@ class DockerAddon(DockerInterface):
# GPIO support # GPIO support
if self.addon.with_gpio: if self.addon.with_gpio:
volumes.update({ for gpio_path in ("/sys/class/gpio", "/sys/devices/platform/soc"):
"/sys/class/gpio": { if not Path(gpio_path).exists():
'bind': "/sys/class/gpio", 'mode': 'rw' continue
}, volumes.update({
"/sys/devices/platform/soc": { gpio_path: {
'bind': "/sys/devices/platform/soc", 'mode': 'rw' 'bind': gpio_path, 'mode': 'rw'
}, },
}) })
# DeviceTree support # DeviceTree support
if self.addon.with_devicetree: if self.addon.with_devicetree:
@@ -221,6 +227,14 @@ class DockerAddon(DockerInterface):
}, },
}) })
# Docker API support
if not self.addon.protected and self.addon.access_docker_api:
volumes.update({
"/var/run/docker.sock": {
'bind': "/var/run/docker.sock", 'mode': 'ro'
},
})
# Host dbus system # Host dbus system
if self.addon.host_dbus: if self.addon.host_dbus:
volumes.update({ volumes.update({
@@ -245,6 +259,11 @@ class DockerAddon(DockerInterface):
if self._is_running(): if self._is_running():
return True return True
# Security check
if not self.addon.protected:
_LOGGER.warning(
"%s run with disabled proteced mode!", self.addon.name)
# cleanup # cleanup
self._stop() self._stop()
@@ -254,6 +273,7 @@ class DockerAddon(DockerInterface):
hostname=self.hostname, hostname=self.hostname,
detach=True, detach=True,
init=True, init=True,
privileged=self.full_access,
ipc_mode=self.ipc, ipc_mode=self.ipc,
stdin_open=self.addon.with_stdin, stdin_open=self.addon.with_stdin,
network_mode=self.network_mode, network_mode=self.network_mode,

View File

@@ -61,11 +61,11 @@ class DockerHomeAssistant(DockerInterface):
network_mode='host', network_mode='host',
environment={ environment={
'HASSIO': self.sys_docker.network.supervisor, 'HASSIO': self.sys_docker.network.supervisor,
ENV_TIME: self.sys_config.timezone, ENV_TIME: self.sys_timezone,
ENV_TOKEN: self.sys_homeassistant.uuid, ENV_TOKEN: self.sys_homeassistant.uuid,
}, },
volumes={ volumes={
str(self.sys_config.path_extern_config): str(self.sys_config.path_extern_homeassistant):
{'bind': '/config', 'mode': 'rw'}, {'bind': '/config', 'mode': 'rw'},
str(self.sys_config.path_extern_ssl): str(self.sys_config.path_extern_ssl):
{'bind': '/ssl', 'mode': 'ro'}, {'bind': '/ssl', 'mode': 'ro'},
@@ -95,13 +95,15 @@ class DockerHomeAssistant(DockerInterface):
stdout=True, stdout=True,
stderr=True, stderr=True,
environment={ environment={
ENV_TIME: self.sys_config.timezone, ENV_TIME: self.sys_timezone,
}, },
volumes={ volumes={
str(self.sys_config.path_extern_config): str(self.sys_config.path_extern_homeassistant):
{'bind': '/config', 'mode': 'rw'}, {'bind': '/config', 'mode': 'rw'},
str(self.sys_config.path_extern_ssl): str(self.sys_config.path_extern_ssl):
{'bind': '/ssl', 'mode': 'ro'}, {'bind': '/ssl', 'mode': 'ro'},
str(self.sys_config.path_extern_share):
{'bind': '/share', 'mode': 'ro'},
} }
) )

View File

@@ -1,7 +1,4 @@
"""Core Exceptions.""" """Core Exceptions."""
import asyncio
import aiohttp
class HassioError(Exception): class HassioError(Exception):
@@ -26,14 +23,13 @@ class HomeAssistantUpdateError(HomeAssistantError):
pass pass
class HomeAssistantAuthError(HomeAssistantError): class HomeAssistantAPIError(HomeAssistantError):
"""Home Assistant Auth API exception.""" """Home Assistant API exception."""
pass pass
class HomeAssistantAPIError( class HomeAssistantAuthError(HomeAssistantAPIError):
HomeAssistantAuthError, asyncio.TimeoutError, aiohttp.ClientError): """Home Assistant Auth API exception."""
"""Home Assistant API exception."""
pass pass
@@ -80,6 +76,19 @@ class HostServiceError(HostError):
class HostAppArmorError(HostError): class HostAppArmorError(HostError):
"""Host apparmor functions fails.""" """Host apparmor functions fails."""
pass
# API
class APIError(HassioError):
"""API errors."""
pass
class APINotSupportedError(HassioNotSupportedError):
"""API not supported error."""
pass
# utils/gdbus # utils/gdbus

View File

@@ -1,9 +1,11 @@
"""HomeAssistant control object.""" """HomeAssistant control object."""
import asyncio import asyncio
from contextlib import asynccontextmanager, suppress from contextlib import asynccontextmanager, suppress
from datetime import datetime, timedelta
import logging import logging
import os import os
import re import re
from pathlib import Path
import socket import socket
import time import time
@@ -45,6 +47,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
self._error_state = False self._error_state = False
# We don't persist access tokens. Instead we fetch new ones when needed # We don't persist access tokens. Instead we fetch new ones when needed
self.access_token = None self.access_token = None
self._access_token_expires = None
async def load(self): async def load(self):
"""Prepare HomeAssistant object.""" """Prepare HomeAssistant object."""
@@ -351,11 +354,12 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
async def ensure_access_token(self): async def ensure_access_token(self):
"""Ensures there is an access token.""" """Ensures there is an access token."""
if self.access_token is not None: if (self.access_token is not None and
self._access_token_expires > datetime.utcnow()):
return return
with suppress(asyncio.TimeoutError, aiohttp.ClientError): with suppress(asyncio.TimeoutError, aiohttp.ClientError):
async with self.sys_websession_ssl.get( async with self.sys_websession_ssl.post(
f"{self.api_url}/auth/token", f"{self.api_url}/auth/token",
timeout=30, timeout=30,
data={ data={
@@ -364,14 +368,14 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
} }
) as resp: ) as resp:
if resp.status != 200: if resp.status != 200:
_LOGGER.error("Authenticate problem with HomeAssistant!") _LOGGER.error("Can't update HomeAssistant access token!")
raise HomeAssistantAuthError() raise HomeAssistantAuthError()
_LOGGER.info("Updated HomeAssistant API token")
tokens = await resp.json() tokens = await resp.json()
self.access_token = tokens['access_token'] self.access_token = tokens['access_token']
return self._access_token_expires = \
datetime.utcnow() + timedelta(seconds=tokens['expires_in'])
_LOGGER.error("Can't update HomeAssistant access token!")
raise HomeAssistantAPIError()
@asynccontextmanager @asynccontextmanager
async def make_request(self, method, path, json=None, content_type=None, async def make_request(self, method, path, json=None, content_type=None,
@@ -394,15 +398,20 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
await self.ensure_access_token() await self.ensure_access_token()
headers[hdrs.AUTHORIZATION] = f'Bearer {self.access_token}' headers[hdrs.AUTHORIZATION] = f'Bearer {self.access_token}'
async with getattr(self.sys_websession_ssl, method)( try:
url, data=data, timeout=timeout, json=json, headers=headers async with getattr(self.sys_websession_ssl, method)(
) as resp: url, data=data, timeout=timeout, json=json,
# Access token expired headers=headers
if resp.status == 401 and self.refresh_token: ) as resp:
self.access_token = None # Access token expired
continue if resp.status == 401 and self.refresh_token:
yield resp self.access_token = None
return continue
yield resp
return
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
_LOGGER.error("Error on call %s: %s", url, err)
break
raise HomeAssistantAPIError() raise HomeAssistantAPIError()
@@ -433,6 +442,9 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
async def _block_till_run(self): async def _block_till_run(self):
"""Block until Home-Assistant is booting up or startup timeout.""" """Block until Home-Assistant is booting up or startup timeout."""
start_time = time.monotonic() start_time = time.monotonic()
migration_progress = False
migration_file = Path(
self.sys_config.path_homeassistant, '.migration_progress')
def check_port(): def check_port():
"""Check if port is mapped.""" """Check if port is mapped."""
@@ -448,21 +460,39 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
pass pass
return False return False
while time.monotonic() - start_time < self.wait_boot: while True:
await asyncio.sleep(10)
# 1
# Check if Container is is_running
if not await self.instance.is_running():
_LOGGER.error("HomeAssistant is crashed!")
break
# 2
# Check if API response # Check if API response
if await self.sys_run_in_executor(check_port): if await self.sys_run_in_executor(check_port):
_LOGGER.info("Detect a running HomeAssistant instance") _LOGGER.info("Detect a running HomeAssistant instance")
self._error_state = False self._error_state = False
return return
# wait and don't hit the system # 3
await asyncio.sleep(10) # Running DB Migration
if migration_file.exists():
if not migration_progress:
migration_progress = True
_LOGGER.info("HomeAssistant record migration in progress")
continue
elif migration_progress:
migration_progress = False # Reset start time
start_time = time.monotonic()
_LOGGER.info("HomeAssistant record migration done")
# Check if Container is is_running # 4
if not await self.instance.is_running(): # Timeout
_LOGGER.error("Home Assistant is crashed!") if time.monotonic() - start_time > self.wait_boot:
_LOGGER.warning("Don't wait anymore of HomeAssistant startup!")
break break
_LOGGER.warning("Don't wait anymore of HomeAssistant startup!")
self._error_state = True self._error_state = True
raise HomeAssistantError() raise HomeAssistantError()

View File

@@ -293,6 +293,7 @@ class SnapshotManager(CoreSysAttributes):
# Stop Home-Assistant if they will be restored later # Stop Home-Assistant if they will be restored later
if homeassistant and FOLDER_HOMEASSISTANT in folders: if homeassistant and FOLDER_HOMEASSISTANT in folders:
await self.sys_homeassistant.stop() await self.sys_homeassistant.stop()
snapshot.restore_homeassistant()
# Process folders # Process folders
if folders: if folders:
@@ -304,7 +305,6 @@ class SnapshotManager(CoreSysAttributes):
if homeassistant: if homeassistant:
_LOGGER.info("Restore %s run Home-Assistant", _LOGGER.info("Restore %s run Home-Assistant",
snapshot.slug) snapshot.slug)
snapshot.restore_homeassistant()
task_hass = self.sys_create_task( task_hass = self.sys_create_task(
self.sys_homeassistant.update( self.sys_homeassistant.update(
snapshot.homeassistant_version)) snapshot.homeassistant_version))
@@ -322,12 +322,20 @@ class SnapshotManager(CoreSysAttributes):
_LOGGER.info("Restore %s old add-ons", snapshot.slug) _LOGGER.info("Restore %s old add-ons", snapshot.slug)
await snapshot.restore_addons(addon_list) await snapshot.restore_addons(addon_list)
# make sure homeassistant run agen # Make sure homeassistant run agen
if task_hass: if task_hass:
_LOGGER.info("Restore %s wait for Home-Assistant", _LOGGER.info("Restore %s wait for Home-Assistant",
snapshot.slug) snapshot.slug)
await task_hass await task_hass
await self.sys_homeassistant.start()
# Do we need start HomeAssistant?
if not await self.sys_homeassistant.is_running():
await self.sys_homeassistant.start()
# Check If we can access to API / otherwise restart
if not await self.sys_homeassistant.check_api_state():
_LOGGER.warning("Need restart HomeAssistant for API")
await self.sys_homeassistant.restart()
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
_LOGGER.exception("Restore %s error", snapshot.slug) _LOGGER.exception("Restore %s error", snapshot.slug)

View File

@@ -20,7 +20,7 @@ from ..const import (
ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_VERSION, ATTR_TYPE, ATTR_IMAGE, ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_VERSION, ATTR_TYPE, ATTR_IMAGE,
ATTR_PORT, ATTR_SSL, ATTR_PASSWORD, ATTR_WATCHDOG, ATTR_BOOT, ATTR_CRYPTO, ATTR_PORT, ATTR_SSL, ATTR_PASSWORD, ATTR_WATCHDOG, ATTR_BOOT, ATTR_CRYPTO,
ATTR_LAST_VERSION, ATTR_PROTECTED, ATTR_WAIT_BOOT, ATTR_SIZE, ATTR_LAST_VERSION, ATTR_PROTECTED, ATTR_WAIT_BOOT, ATTR_SIZE,
CRYPTO_AES128) ATTR_REFRESH_TOKEN, CRYPTO_AES128)
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..utils.json import write_json_file from ..utils.json import write_json_file
from ..utils.tar import SecureTarFile from ..utils.tar import SecureTarFile
@@ -387,6 +387,8 @@ class Snapshot(CoreSysAttributes):
# API/Proxy # API/Proxy
self.homeassistant[ATTR_PORT] = self.sys_homeassistant.api_port self.homeassistant[ATTR_PORT] = self.sys_homeassistant.api_port
self.homeassistant[ATTR_SSL] = self.sys_homeassistant.api_ssl self.homeassistant[ATTR_SSL] = self.sys_homeassistant.api_ssl
self.homeassistant[ATTR_REFRESH_TOKEN] = \
self._encrypt_data(self.sys_homeassistant.refresh_token)
self.homeassistant[ATTR_PASSWORD] = \ self.homeassistant[ATTR_PASSWORD] = \
self._encrypt_data(self.sys_homeassistant.api_password) self._encrypt_data(self.sys_homeassistant.api_password)
@@ -405,6 +407,8 @@ class Snapshot(CoreSysAttributes):
# API/Proxy # API/Proxy
self.sys_homeassistant.api_port = self.homeassistant[ATTR_PORT] self.sys_homeassistant.api_port = self.homeassistant[ATTR_PORT]
self.sys_homeassistant.api_ssl = self.homeassistant[ATTR_SSL] self.sys_homeassistant.api_ssl = self.homeassistant[ATTR_SSL]
self.sys_homeassistant.refresh_token = \
self._decrypt_data(self.homeassistant[ATTR_REFRESH_TOKEN])
self.sys_homeassistant.api_password = \ self.sys_homeassistant.api_password = \
self._decrypt_data(self.homeassistant[ATTR_PASSWORD]) self._decrypt_data(self.homeassistant[ATTR_PASSWORD])

View File

@@ -7,6 +7,7 @@ from ..const import (
ATTR_VERSION, ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_TYPE, ATTR_IMAGE, ATTR_VERSION, ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_TYPE, ATTR_IMAGE,
ATTR_PASSWORD, ATTR_PORT, ATTR_SSL, ATTR_WATCHDOG, ATTR_BOOT, ATTR_SIZE, ATTR_PASSWORD, ATTR_PORT, ATTR_SSL, ATTR_WATCHDOG, ATTR_BOOT, ATTR_SIZE,
ATTR_LAST_VERSION, ATTR_WAIT_BOOT, ATTR_PROTECTED, ATTR_CRYPTO, ATTR_LAST_VERSION, ATTR_WAIT_BOOT, ATTR_PROTECTED, ATTR_CRYPTO,
ATTR_REFRESH_TOKEN,
FOLDER_SHARE, FOLDER_HOMEASSISTANT, FOLDER_ADDONS, FOLDER_SSL, FOLDER_SHARE, FOLDER_HOMEASSISTANT, FOLDER_ADDONS, FOLDER_SSL,
SNAPSHOT_FULL, SNAPSHOT_PARTIAL, CRYPTO_AES128) SNAPSHOT_FULL, SNAPSHOT_PARTIAL, CRYPTO_AES128)
from ..validate import NETWORK_PORT, REPOSITORIES, DOCKER_IMAGE from ..validate import NETWORK_PORT, REPOSITORIES, DOCKER_IMAGE
@@ -40,6 +41,7 @@ SCHEMA_SNAPSHOT = vol.Schema({
vol.Optional(ATTR_SSL, default=False): vol.Boolean(), vol.Optional(ATTR_SSL, default=False): vol.Boolean(),
vol.Optional(ATTR_PORT, default=8123): NETWORK_PORT, vol.Optional(ATTR_PORT, default=8123): NETWORK_PORT,
vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(), vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(),
vol.Optional(ATTR_WAIT_BOOT, default=600): vol.Optional(ATTR_WAIT_BOOT, default=600):
vol.All(vol.Coerce(int), vol.Range(min=60)), vol.All(vol.Coerce(int), vol.Range(min=60)),

View File

@@ -9,7 +9,6 @@ UTC = pytz.utc
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
FREEGEOIP_URL = "https://freegeoip.net/json/"
# Copyright (c) Django Software Foundation and individual contributors. # Copyright (c) Django Software Foundation and individual contributors.
# All rights reserved. # All rights reserved.

View File

@@ -1,13 +1,13 @@
attr==0.3.1 attr==0.3.1
async_timeout==3.0.0 async_timeout==3.0.0
aiohttp==3.3.2 aiohttp==3.4.0
docker==3.4.0 docker==3.5.0
colorlog==3.1.2 colorlog==3.1.2
voluptuous==0.11.1 voluptuous==0.11.5
gitpython==2.1.10 gitpython==2.1.10
pytz==2018.4 pytz==2018.4
pyudev==0.21.0 pyudev==0.21.0
pycryptodome==3.6.4 pycryptodome==3.6.6
cpe==1.2.1 cpe==1.2.1
uvloop==0.11.1 uvloop==0.11.2
cchardet==2.1.1 cchardet==2.1.1

View File

@@ -4,7 +4,7 @@ envlist = lint
[testenv] [testenv]
deps = deps =
flake8==3.5.0 flake8==3.5.0
pylint==2.0.0 pylint==2.1.1
-r{toxinidir}/requirements.txt -r{toxinidir}/requirements.txt
[testenv:lint] [testenv:lint]