Compare commits

..

8 Commits

Author SHA1 Message Date
Ludeeus
c51496ad2f add missing id 2021-04-13 13:06:32 +00:00
Ludeeus
fbe409337b Add parrent_id 2021-04-13 12:55:20 +00:00
Ludeeus
443a43cc5b hex is str 2021-04-13 12:48:38 +00:00
Ludeeus
0e25fad1c0 Merge branch 'main' of github.com:home-assistant/supervisor into context 2021-04-13 12:44:45 +00:00
Ludeeus
488a2327fb Add name setter 2020-12-05 12:58:49 +00:00
Ludeeus
b99ed631c5 Add data to job 2020-12-05 12:44:17 +00:00
Ludeeus
726dd3a8f9 Merge branch 'main' of github.com:home-assistant/supervisor into context 2020-12-05 12:29:11 +00:00
Ludeeus
b94810d044 init 2020-11-27 16:10:39 +00:00
472 changed files with 9462 additions and 12276 deletions

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.9
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.8
ENV DEBIAN_FRONTEND=noninteractive

View File

@@ -1,6 +1,8 @@
name: Bug Report Form
description: Report an issue related to the Home Assistant Supervisor.
about: Report an issue related to the Home Assistant Supervisor.
labels: bug
title: ""
issue_body: true
body:
- type: markdown
attributes:
@@ -88,11 +90,17 @@ body:
label: Anything in the Supervisor logs that might be useful for us?
description: >
The Supervisor logs can be found in the Supervisor panel -> System tab.
render: txt
- type: textarea
value: |
```txt
# Put your logs below this line
```
- type: markdown
attributes:
label: Additional information
description: >
value: |
## Additional information
- type: markdown
attributes:
value: |
If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here, by
dragging and dropping files in the field below.
Please note, you can attach screenshots or screen recordings here.

View File

@@ -35,7 +35,7 @@ on:
env:
BUILD_NAME: supervisor
BUILD_TYPE: supervisor
WHEELS_TAG: 3.9-alpine3.13
WHEELS_TAG: 3.8-alpine3.13
jobs:
init:
@@ -49,7 +49,7 @@ jobs:
requirements: ${{ steps.requirements.outputs.changed }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
with:
fetch-depth: 0
@@ -71,7 +71,7 @@ jobs:
- name: Check if requirements files changed
id: requirements
run: |
if [[ "${{ steps.changed_files.outputs.all }}" =~ (requirements.txt|build.json) ]]; then
if [[ "${{ steps.changed_files.outputs.all }}" =~ requirements.txt ]]; then
echo "::set-output name=changed::true"
fi
@@ -84,7 +84,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
with:
fetch-depth: 0
@@ -94,7 +94,7 @@ jobs:
with:
tag: ${{ env.WHEELS_TAG }}
arch: ${{ matrix.arch }}
wheels-host: wheels.hass.io
wheels-host: ${{ secrets.WHEELS_HOST }}
wheels-key: ${{ secrets.WHEELS_KEY }}
wheels-user: wheels
apk: "build-base;libffi-dev;openssl-dev;cargo"
@@ -109,14 +109,14 @@ jobs:
- name: Login to DockerHub
if: needs.init.outputs.publish == 'true'
uses: docker/login-action@v1.10.0
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: needs.init.outputs.publish == 'true'
uses: docker/login-action@v1.10.0
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ secrets.GIT_USER }}
@@ -127,7 +127,7 @@ jobs:
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
- name: Build supervisor
uses: home-assistant/builder@2021.06.2
uses: home-assistant/builder@2021.04.0
with:
args: |
$BUILD_ARGS \
@@ -135,6 +135,7 @@ jobs:
--target /data \
--with-codenotary "${{ secrets.VCN_USER }}" "${{ secrets.VCN_PASSWORD }}" "${{ secrets.VCN_ORG }}" \
--validate-from "${{ secrets.VCN_ORG }}" \
--validate-cache "${{ secrets.VCN_ORG }}" \
--generic ${{ needs.init.outputs.version }}
codenotary:
@@ -144,7 +145,7 @@ jobs:
steps:
- name: Checkout the repository
if: needs.init.outputs.publish == 'true'
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
with:
fetch-depth: 0
@@ -170,7 +171,7 @@ jobs:
steps:
- name: Checkout the repository
if: needs.init.outputs.publish == 'true'
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Initialize git
if: needs.init.outputs.publish == 'true'
@@ -191,14 +192,13 @@ jobs:
run_supervisor:
runs-on: ubuntu-latest
name: Run the Supervisor
needs: ["build", "codenotary", "init"]
needs: ["build", "codenotary"]
steps:
- name: Checkout the repository
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Build the Supervisor
if: needs.init.outputs.publish != 'true'
uses: home-assistant/builder@2021.06.2
uses: home-assistant/builder@2021.04.0
with:
args: |
--test \
@@ -206,12 +206,6 @@ jobs:
--target /data \
--generic runner
- name: Pull Supervisor
if: needs.init.outputs.publish == 'true'
run: |
docker pull homeassistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }}
docker tag homeassistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }} homeassistant/amd64-hassio-supervisor:runner
- name: Create the Supervisor
run: |
mkdir -p /tmp/supervisor/data
@@ -237,63 +231,22 @@ jobs:
SUPERVISOR=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' hassio_supervisor)
ping="error"
while [ "$ping" != "ok" ]; do
ping=$(curl -sSL "http://$SUPERVISOR/supervisor/ping" | jq -r '.result')
ping=$(curl -sSL "http://$SUPERVISOR/supervisor/ping" | jq -r .result)
sleep 5
done
docker logs hassio_supervisor
- name: Check the Supervisor
run: |
echo "Checking supervisor info"
test=$(docker exec hassio_cli ha supervisor info --no-progress --raw-json | jq -r '.result')
test=$(docker exec hassio_cli ha supervisor info --no-progress --raw-json | jq -r .result)
if [ "$test" != "ok" ];then
docker logs hassio_supervisor
exit 1
fi
echo "Checking supervisor network info"
test=$(docker exec hassio_cli ha network info --no-progress --raw-json | jq -r '.result')
test=$(docker exec hassio_cli ha network info --no-progress --raw-json | jq -r .result)
if [ "$test" != "ok" ];then
docker logs hassio_supervisor
exit 1
fi
- name: Check the Store / Addon
run: |
echo "Install Core SSH Add-on"
test=$(docker exec hassio_cli ha addons install core_ssh --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ];then
docker logs hassio_supervisor
exit 1
fi
echo "Start Core SSH Add-on"
test=$(docker exec hassio_cli ha addons start core_ssh --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ];then
docker logs hassio_supervisor
exit 1
fi
- name: Check the Supervisor code sign
if: needs.init.outputs.publish == 'true'
run: |
echo "Enable Content-Trust"
test=$(docker exec hassio_cli ha supervisor options --content-trust=true --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ];then
docker logs hassio_supervisor
exit 1
fi
echo "Run supervisor health check"
test=$(docker exec hassio_cli ha resolution healthcheck --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ];then
docker logs hassio_supervisor
exit 1
fi
echo "Check supervisor unhealthy"
test=$(docker exec hassio_cli ha resolution info --no-progress --raw-json | jq -r '.data.unhealthy[]')
if [ "$test" != "" ];then
docker logs hassio_supervisor
exit 1
fi

View File

@@ -8,7 +8,7 @@ on:
pull_request: ~
env:
DEFAULT_PYTHON: 3.9
DEFAULT_PYTHON: 3.8
PRE_COMMIT_HOME: ~/.cache/pre-commit
jobs:
@@ -18,11 +18,11 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]
python-version: [3.8]
name: Prepare Python ${{ matrix.python-version }} dependencies
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v2.2.2
@@ -30,7 +30,7 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -47,7 +47,7 @@ jobs:
pip install -r requirements.txt -r requirements_tests.txt
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -66,7 +66,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -74,7 +74,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -95,7 +95,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Register hadolint problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/hadolint.json"
@@ -110,7 +110,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -118,7 +118,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -130,7 +130,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -154,7 +154,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -162,7 +162,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -186,7 +186,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -194,7 +194,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -206,7 +206,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -227,7 +227,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -235,7 +235,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -247,7 +247,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -271,7 +271,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -279,7 +279,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -303,7 +303,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -311,7 +311,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -323,7 +323,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -343,11 +343,11 @@ jobs:
needs: prepare
strategy:
matrix:
python-version: [3.9]
python-version: [3.8]
name: Run tests Python ${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2.2.2
id: python
@@ -359,7 +359,7 @@ jobs:
bash <(curl https://getvcn.codenotary.com -L)
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -394,7 +394,7 @@ jobs:
-o console_output_style=count \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@v2.2.4
uses: actions/upload-artifact@v2.2.3
with:
name: coverage-${{ matrix.python-version }}
path: .coverage
@@ -405,7 +405,7 @@ jobs:
needs: pytest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
id: python
@@ -413,7 +413,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.5
with:
path: venv
key: |
@@ -432,4 +432,4 @@ jobs:
coverage report
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.5.2
uses: codecov/codecov-action@v1.3.2

View File

@@ -11,7 +11,7 @@ jobs:
name: Release Drafter
steps:
- name: Checkout the repository
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
with:
fetch-depth: 0

View File

@@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.3.4
uses: actions/checkout@v2
- name: Sentry Release
uses: getsentry/action-release@v1.1.5
uses: getsentry/action-release@v1.1
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}

View File

@@ -9,7 +9,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3.0.19
- uses: actions/stale@v3.0.18
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 21.6b0
rev: 20.8b1
hooks:
- id: black
args:

16
.vscode/tasks.json vendored
View File

@@ -4,7 +4,7 @@
{
"label": "Run Supervisor",
"type": "shell",
"command": "./scripts/supervisor.sh",
"command": "./scripts/run-supervisor.sh",
"group": {
"kind": "test",
"isDefault": true
@@ -15,6 +15,20 @@
},
"problemMatcher": []
},
{
"label": "Build Supervisor",
"type": "shell",
"command": "./scripts/build-supervisor.sh",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new"
},
"problemMatcher": []
},
{
"label": "Run Supervisor CLI",
"type": "shell",

View File

@@ -2,23 +2,17 @@
"image": "homeassistant/{arch}-hassio-supervisor",
"shadow_repository": "ghcr.io/home-assistant",
"build_from": {
"aarch64": "ghcr.io/home-assistant/aarch64-base-python:3.9-alpine3.13",
"armhf": "ghcr.io/home-assistant/armhf-base-python:3.9-alpine3.13",
"armv7": "ghcr.io/home-assistant/armv7-base-python:3.9-alpine3.13",
"amd64": "ghcr.io/home-assistant/amd64-base-python:3.9-alpine3.13",
"i386": "ghcr.io/home-assistant/i386-base-python:3.9-alpine3.13"
"aarch64": "ghcr.io/home-assistant/aarch64-base-python:3.8-alpine3.13",
"armhf": "ghcr.io/home-assistant/armhf-base-python:3.8-alpine3.13",
"armv7": "ghcr.io/home-assistant/armv7-base-python:3.8-alpine3.13",
"amd64": "ghcr.io/home-assistant/amd64-base-python:3.8-alpine3.13",
"i386": "ghcr.io/home-assistant/i386-base-python:3.8-alpine3.13"
},
"args": {
"VCN_VERSION": "0.9.4"
},
"labels": {
"io.hass.type": "supervisor",
"org.opencontainers.image.title": "Home Assistant Supervisor",
"org.opencontainers.image.description": "Container-based system for managing Home Assistant Core installation",
"org.opencontainers.image.source": "https://github.com/home-assistant/supervisor",
"org.opencontainers.image.authors": "The Home Assistant Authors",
"org.opencontainers.image.url": "https://www.home-assistant.io/",
"org.opencontainers.image.documentation": "https://www.home-assistant.io/docs/",
"org.opencontainers.image.licenses": "Apache License 2.0"
"org.opencontainers.image.source": "https://github.com/home-assistant/supervisor"
}
}

View File

@@ -37,7 +37,6 @@ disable=
too-many-return-statements,
too-many-statements,
unused-argument,
consider-using-with
[EXCEPTIONS]
overgeneral-exceptions=Exception

View File

@@ -1,20 +1,20 @@
aiohttp==3.7.4.post0
async_timeout==3.0.1
atomicwrites==1.4.0
attrs==21.2.0
awesomeversion==21.6.0
brotlipy==0.7.0
attrs==20.3.0
awesomeversion==21.4.0
brotli==1.0.9
cchardet==2.1.7
ciso8601==2.1.3
colorlog==5.0.1
colorlog==4.8.0
cpe==1.2.1
cryptography==3.4.6
debugpy==1.3.0
debugpy==1.2.1
docker==5.0.0
gitpython==3.1.18
jinja2==3.0.1
pulsectl==21.5.18
gitpython==3.1.14
jinja2==2.11.3
pulsectl==21.3.4
pytz==2021.1
pyudev==0.22.0
ruamel.yaml==0.15.100
sentry-sdk==1.1.0
sentry-sdk==1.0.0
voluptuous==0.12.1

View File

@@ -1,14 +1,14 @@
black==21.6b0
black==20.8b1
codecov==2.1.11
coverage==5.5
flake8-docstrings==1.6.0
flake8==3.9.2
pre-commit==2.13.0
pydocstyle==6.1.1
pylint==2.8.3
flake8==3.9.0
pre-commit==2.12.0
pydocstyle==6.0.0
pylint==2.7.4
pytest-aiohttp==0.3.0
pytest-asyncio==0.12.0 # NB!: Versions over 0.12.0 breaks pytest-aiohttp (https://github.com/aio-libs/pytest-aiohttp/issues/16)
pytest-cov==2.12.1
pytest-cov==2.11.1
pytest-timeout==1.4.2
pytest==6.2.4
pyupgrade==2.19.4
pytest==6.2.3
pyupgrade==2.12.0

28
scripts/build-supervisor.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
source "${BASH_SOURCE[0]%/*}/common.sh"
set -eE
DOCKER_TIMEOUT=30
DOCKER_PID=0
function build_supervisor() {
docker pull homeassistant/amd64-builder:dev
docker run --rm \
--privileged \
-v /run/docker.sock:/run/docker.sock \
-v "$(pwd):/data" \
homeassistant/amd64-builder:dev \
--generic latest \
--target /data \
--test \
--amd64 \
--no-cache
}
echo "Build Supervisor"
start_docker
trap "stop_docker" ERR
build_supervisor

View File

@@ -4,9 +4,6 @@ function start_docker() {
local starttime
local endtime
update-alternatives --set iptables /usr/sbin/iptables-legacy || echo "Fails adjust iptables"
update-alternatives --set ip6tables /usr/sbin/iptables-legacy || echo "Fails adjust ip6tables"
echo "Starting docker."
dockerd 2> /dev/null &
DOCKER_PID=$!

View File

@@ -1,26 +1,12 @@
#!/bin/bash
source "${BASH_SOURCE[0]%/*}/common.sh"
source "${BASH_SOURCE[0]%/*}/build-supervisor.sh"
set -eE
DOCKER_TIMEOUT=30
DOCKER_PID=0
function build_supervisor() {
docker pull homeassistant/amd64-builder:dev
docker run --rm \
--privileged \
-v /run/docker.sock:/run/docker.sock \
-v "$(pwd):/data" \
homeassistant/amd64-builder:dev \
--generic latest \
--target /data \
--test \
--amd64 \
--no-cache
}
function cleanup_docker() {
echo "Cleaning up stopped containers..."

View File

@@ -65,7 +65,6 @@ from ..utils import check_port
from ..utils.apparmor import adjust_profile
from ..utils.json import read_json_file, write_json_file
from ..utils.tar import atomic_contents_add, secure_path
from .const import SnapshotAddonMode
from .model import AddonModel, Data
from .options import AddonOptions
from .utils import remove_data
@@ -427,20 +426,30 @@ class Addon(AddonModel):
@property
def devices(self) -> Set[Device]:
"""Extract devices from add-on options."""
options_schema = self.schema
with suppress(vol.Invalid):
options_schema.validate(self.options)
raw_schema = self.data[ATTR_SCHEMA]
if isinstance(raw_schema, bool) or not raw_schema:
return set()
return options_schema.devices
# Validate devices
options_validator = AddonOptions(self.coresys, raw_schema, self.name, self.slug)
with suppress(vol.Invalid):
options_validator(self.options)
return options_validator.devices
@property
def pwned(self) -> Set[str]:
"""Extract pwned data for add-on options."""
options_schema = self.schema
with suppress(vol.Invalid):
options_schema.validate(self.options)
raw_schema = self.data[ATTR_SCHEMA]
if isinstance(raw_schema, bool) or not raw_schema:
return set()
return options_schema.pwned
# Validate devices
options_validator = AddonOptions(self.coresys, raw_schema, self.name, self.slug)
with suppress(vol.Invalid):
options_validator(self.options)
return options_validator.pwned
def save_persist(self) -> None:
"""Save data of add-on."""
@@ -454,7 +463,7 @@ class Addon(AddonModel):
application = RE_WATCHDOG.match(url)
# extract arguments
t_port = int(application.group("t_port"))
t_port = application.group("t_port")
t_proto = application.group("t_proto")
s_prefix = application.group("s_prefix") or ""
s_suffix = application.group("s_suffix") or ""
@@ -478,8 +487,8 @@ class Addon(AddonModel):
# Make HTTP request
try:
url = f"{proto}://{self.ip_address}:{port}{s_suffix}"
async with self.sys_websession.get(
url, timeout=WATCHDOG_TIMEOUT, ssl=False
async with self.sys_websession_ssl.get(
url, timeout=WATCHDOG_TIMEOUT
) as req:
if req.status < 300:
return True
@@ -494,7 +503,7 @@ class Addon(AddonModel):
await self.sys_homeassistant.secrets.reload()
try:
options = self.schema.validate(self.options)
options = self.schema(self.options)
write_json_file(self.path_options, options)
except vol.Invalid as ex:
_LOGGER.error(
@@ -679,25 +688,8 @@ class Addon(AddonModel):
except DockerError as err:
raise AddonsError() from err
async def _snapshot_command(self, command: str) -> None:
try:
command_return = await self.instance.run_inside(command)
if command_return.exit_code != 0:
_LOGGER.error(
"Pre-/Post-Snapshot command returned error code: %s",
command_return.exit_code,
)
raise AddonsError()
except DockerError as err:
_LOGGER.error(
"Failed running pre-/post-snapshot command %s: %s", command, err
)
raise AddonsError() from err
async def snapshot(self, tar_file: tarfile.TarFile) -> None:
"""Snapshot state of an add-on."""
is_running = await self.is_running()
with TemporaryDirectory(dir=self.sys_config.path_tmp) as temp:
temp_path = Path(temp)
@@ -747,32 +739,12 @@ class Addon(AddonModel):
arcname="data",
)
if (
is_running
and self.snapshot_mode == SnapshotAddonMode.HOT
and self.snapshot_pre is not None
):
await self._snapshot_command(self.snapshot_pre)
elif is_running and self.snapshot_mode == SnapshotAddonMode.COLD:
_LOGGER.info("Shutdown add-on %s for cold snapshot", self.slug)
await self.instance.stop()
try:
_LOGGER.info("Building snapshot for add-on %s", self.slug)
await self.sys_run_in_executor(_write_tarfile)
except (tarfile.TarError, OSError) as err:
_LOGGER.error("Can't write tarfile %s: %s", tar_file, err)
raise AddonsError() from err
finally:
if (
is_running
and self.snapshot_mode == SnapshotAddonMode.HOT
and self.snapshot_post is not None
):
await self._snapshot_command(self.snapshot_post)
elif is_running and self.snapshot_mode is SnapshotAddonMode.COLD:
_LOGGER.info("Starting add-on %s again", self.slug)
await self.start()
_LOGGER.info("Finish snapshot for addon %s", self.slug)

View File

@@ -9,7 +9,6 @@ from awesomeversion import AwesomeVersion
from ..const import (
ATTR_ARGS,
ATTR_BUILD_FROM,
ATTR_LABELS,
ATTR_SQUASH,
FILE_SUFFIX_CONFIGURATION,
META_ADDON,
@@ -47,12 +46,9 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
@property
def base_image(self) -> str:
"""Return base image for this add-on."""
if not self._data[ATTR_BUILD_FROM]:
return f"ghcr.io/home-assistant/{self.sys_arch.default}-base:latest"
# Evaluate correct base image
arch = self.sys_arch.match(list(self._data[ATTR_BUILD_FROM].keys()))
return self._data[ATTR_BUILD_FROM][arch]
return self._data[ATTR_BUILD_FROM].get(
self.sys_arch.default, f"homeassistant/{self.sys_arch.default}-base:latest"
)
@property
def squash(self) -> bool:
@@ -64,11 +60,6 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
"""Return additional Docker build arguments."""
return self._data[ATTR_ARGS]
@property
def additional_labels(self) -> Dict[str, str]:
"""Return additional Docker labels."""
return self._data[ATTR_LABELS]
@property
def is_valid(self) -> bool:
"""Return true if the build env is valid."""
@@ -85,7 +76,7 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
"path": str(self.addon.path_location),
"tag": f"{self.addon.image}:{version!s}",
"pull": True,
"forcerm": not self.sys_dev,
"forcerm": True,
"squash": self.squash,
"labels": {
"io.hass.version": version,
@@ -93,7 +84,6 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
"io.hass.type": META_ADDON,
"io.hass.name": self._fix_label("name"),
"io.hass.description": self._fix_label("description"),
**self.additional_labels,
},
"buildargs": {
"BUILD_FROM": self.base_image,

View File

@@ -1,12 +0,0 @@
"""Add-on static data."""
from enum import Enum
class SnapshotAddonMode(str, Enum):
"""Snapshot mode of an Add-on."""
HOT = "hot"
COLD = "cold"
ATTR_SNAPSHOT = "snapshot"

View File

@@ -4,8 +4,7 @@ from pathlib import Path
from typing import Any, Awaitable, Dict, List, Optional
from awesomeversion import AwesomeVersion, AwesomeVersionException
from supervisor.addons.const import SnapshotAddonMode
import voluptuous as vol
from ..const import (
ATTR_ADVANCED,
@@ -32,7 +31,6 @@ from ..const import (
ATTR_HOST_PID,
ATTR_IMAGE,
ATTR_INGRESS,
ATTR_INGRESS_STREAM,
ATTR_INIT,
ATTR_JOURNALD,
ATTR_KERNEL_MODULES,
@@ -54,8 +52,6 @@ from ..const import (
ATTR_SERVICES,
ATTR_SLUG,
ATTR_SNAPSHOT_EXCLUDE,
ATTR_SNAPSHOT_POST,
ATTR_SNAPSHOT_PRE,
ATTR_STAGE,
ATTR_STARTUP,
ATTR_STDIN,
@@ -79,7 +75,6 @@ from ..const import (
)
from ..coresys import CoreSys, CoreSysAttributes
from ..docker.const import Capabilities
from .const import ATTR_SNAPSHOT
from .options import AddonOptions, UiOptions
from .validate import RE_SERVICE, RE_VOLUME
@@ -364,21 +359,6 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return Exclude list for snapshot."""
return self.data.get(ATTR_SNAPSHOT_EXCLUDE, [])
@property
def snapshot_pre(self) -> Optional[str]:
"""Return pre-snapshot command."""
return self.data.get(ATTR_SNAPSHOT_PRE)
@property
def snapshot_post(self) -> Optional[str]:
"""Return post-snapshot command."""
return self.data.get(ATTR_SNAPSHOT_POST)
@property
def snapshot_mode(self) -> SnapshotAddonMode:
"""Return if snapshot is hot/cold."""
return self.data[ATTR_SNAPSHOT]
@property
def default_init(self) -> bool:
"""Return True if the add-on have no own init."""
@@ -399,11 +379,6 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return True if the add-on access support ingress."""
return None
@property
def ingress_stream(self) -> bool:
"""Return True if post requests to ingress should be streamed."""
return self.data[ATTR_INGRESS_STREAM]
@property
def with_gpio(self) -> bool:
"""Return True if the add-on access to GPIO interface."""
@@ -557,16 +532,18 @@ class AddonModel(CoreSysAttributes, ABC):
return Path(self.path_location, "apparmor.txt")
@property
def schema(self) -> AddonOptions:
"""Return Addon options validation object."""
def schema(self) -> vol.Schema:
"""Create a schema for add-on options."""
raw_schema = self.data[ATTR_SCHEMA]
if isinstance(raw_schema, bool):
raw_schema = {}
return AddonOptions(self.coresys, raw_schema, self.name, self.slug)
return vol.Schema(
vol.All(dict, AddonOptions(self.coresys, raw_schema, self.name, self.slug))
)
@property
def schema_ui(self) -> Optional[List[Dict[any, any]]]:
def schema_ui(self) -> Optional[List[Dict[str, Any]]]:
"""Create a UI schema for add-on options."""
raw_schema = self.data[ATTR_SCHEMA]

View File

@@ -69,11 +69,6 @@ class AddonOptions(CoreSysAttributes):
self._name = name
self._slug = slug
@property
def validate(self) -> vol.Schema:
"""Create a schema for add-on options."""
return vol.Schema(vol.All(dict, self))
def __call__(self, struct):
"""Create schema validator for add-ons options."""
options = {}

View File

@@ -7,8 +7,6 @@ import uuid
import voluptuous as vol
from supervisor.addons.const import SnapshotAddonMode
from ..const import (
ARCH_ALL,
ATTR_ACCESS_TOKEN,
@@ -45,12 +43,10 @@ from ..const import (
ATTR_INGRESS_ENTRY,
ATTR_INGRESS_PANEL,
ATTR_INGRESS_PORT,
ATTR_INGRESS_STREAM,
ATTR_INGRESS_TOKEN,
ATTR_INIT,
ATTR_JOURNALD,
ATTR_KERNEL_MODULES,
ATTR_LABELS,
ATTR_LEGACY,
ATTR_LOCATON,
ATTR_MACHINE,
@@ -71,8 +67,6 @@ from ..const import (
ATTR_SERVICES,
ATTR_SLUG,
ATTR_SNAPSHOT_EXCLUDE,
ATTR_SNAPSHOT_POST,
ATTR_SNAPSHOT_PRE,
ATTR_SQUASH,
ATTR_STAGE,
ATTR_STARTUP,
@@ -110,7 +104,6 @@ from ..validate import (
uuid_match,
version_tag,
)
from .const import ATTR_SNAPSHOT
from .options import RE_SCHEMA_ELEMENT
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -128,7 +121,6 @@ SCHEMA_ELEMENT = vol.Match(RE_SCHEMA_ELEMENT)
RE_MACHINE = re.compile(
r"^!?(?:"
r"|intel-nuc"
r"|generic-x86-64"
r"|odroid-c2"
r"|odroid-c4"
r"|odroid-n2"
@@ -165,14 +157,6 @@ def _warn_addon_config(config: Dict[str, Any]):
name,
)
if config.get(ATTR_SNAPSHOT, SnapshotAddonMode.HOT) == SnapshotAddonMode.COLD and (
config.get(ATTR_SNAPSHOT_POST) or config.get(ATTR_SNAPSHOT_PRE)
):
_LOGGER.warning(
"Add-on which only support COLD backups trying to use post/pre commands. Please report this to the maintainer of %s",
name,
)
return config
@@ -260,7 +244,6 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
network_port, vol.Equal(0)
),
vol.Optional(ATTR_INGRESS_ENTRY): str,
vol.Optional(ATTR_INGRESS_STREAM, default=False): vol.Boolean(),
vol.Optional(ATTR_PANEL_ICON, default="mdi:puzzle"): str,
vol.Optional(ATTR_PANEL_TITLE): str,
vol.Optional(ATTR_PANEL_ADMIN, default=True): vol.Boolean(),
@@ -295,11 +278,6 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Optional(ATTR_SERVICES): [vol.Match(RE_SERVICE)],
vol.Optional(ATTR_DISCOVERY): [valid_discovery_service],
vol.Optional(ATTR_SNAPSHOT_EXCLUDE): [str],
vol.Optional(ATTR_SNAPSHOT_PRE): str,
vol.Optional(ATTR_SNAPSHOT_POST): str,
vol.Optional(ATTR_SNAPSHOT, default=SnapshotAddonMode.HOT): vol.Coerce(
SnapshotAddonMode
),
vol.Optional(ATTR_OPTIONS, default={}): dict,
vol.Optional(ATTR_SCHEMA, default={}): vol.Any(
vol.Schema(
@@ -339,8 +317,9 @@ SCHEMA_BUILD_CONFIG = vol.Schema(
{vol.In(ARCH_ALL): vol.Match(RE_DOCKER_IMAGE_BUILD)}
),
vol.Optional(ATTR_SQUASH, default=False): vol.Boolean(),
vol.Optional(ATTR_ARGS, default=dict): vol.Schema({str: str}),
vol.Optional(ATTR_LABELS, default=dict): vol.Schema({str: str}),
vol.Optional(ATTR_ARGS, default=dict): vol.Schema(
{vol.Coerce(str): vol.Coerce(str)}
),
},
extra=vol.REMOVE_EXTRA,
)

View File

@@ -19,14 +19,13 @@ from .host import APIHost
from .info import APIInfo
from .ingress import APIIngress
from .jobs import APIJobs
from .middleware.security import SecurityMiddleware
from .multicast import APIMulticast
from .network import APINetwork
from .observer import APIObserver
from .os import APIOS
from .proxy import APIProxy
from .resolution import APIResoulution
from .security import APISecurity
from .security import SecurityMiddleware
from .services import APIServices
from .snapshots import APISnapshots
from .store import APIStore
@@ -83,7 +82,6 @@ class RestAPI(CoreSysAttributes):
self._register_snapshots()
self._register_supervisor()
self._register_store()
self._register_security()
await self.start()
@@ -148,18 +146,6 @@ class RestAPI(CoreSysAttributes):
]
)
def _register_security(self) -> None:
"""Register Security functions."""
api_security = APISecurity()
api_security.coresys = self.coresys
self.webapp.add_routes(
[
web.get("/security/info", api_security.info),
web.post("/security/options", api_security.options),
]
)
def _register_jobs(self) -> None:
"""Register Jobs functions."""
api_jobs = APIJobs()
@@ -223,6 +209,7 @@ class RestAPI(CoreSysAttributes):
[
web.get("/hardware/info", api_hardware.info),
web.get("/hardware/audio", api_hardware.audio),
web.post("/hardware/trigger", api_hardware.trigger),
]
)

View File

@@ -71,7 +71,6 @@ from ..const import (
ATTR_OPTIONS,
ATTR_PRIVILEGED,
ATTR_PROTECTED,
ATTR_PWNED,
ATTR_RATING,
ATTR_REPOSITORIES,
ATTR_REPOSITORY,
@@ -105,8 +104,9 @@ from ..const import (
from ..coresys import CoreSysAttributes
from ..docker.stats import DockerStats
from ..exceptions import APIError, APIForbidden, PwnedError, PwnedSecret
from ..utils.pwned import check_pwned_password
from ..validate import docker_ports
from .utils import api_process, api_process_raw, api_validate, json_loads
from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -338,38 +338,30 @@ class APIAddons(CoreSysAttributes):
async def options_validate(self, request: web.Request) -> None:
"""Validate user options for add-on."""
addon = self._extract_addon_installed(request)
data = {ATTR_MESSAGE: "", ATTR_VALID: True, ATTR_PWNED: False}
options = await request.json(loads=json_loads) or addon.options
data = {ATTR_MESSAGE: "", ATTR_VALID: True}
# Validate config
options_schema = addon.schema
try:
options_schema.validate(options)
addon.schema(addon.options)
except vol.Invalid as ex:
data[ATTR_MESSAGE] = humanize_error(options, ex)
data[ATTR_MESSAGE] = humanize_error(addon.options, ex)
data[ATTR_VALID] = False
if not self.sys_security.pwned:
return data
# Validate security
if self.sys_config.force_security:
for secret in addon.pwned:
try:
await check_pwned_password(self.sys_websession, secret)
continue
except PwnedSecret:
data[ATTR_MESSAGE] = "Add-on use pwned secrets!"
except PwnedError as err:
data[
ATTR_MESSAGE
] = f"Error happening on pwned secrets check: {err!s}!"
# Pwned check
for secret in options_schema.pwned:
try:
await self.sys_security.verify_secret(secret)
continue
except PwnedSecret:
data[ATTR_PWNED] = True
except PwnedError:
data[ATTR_PWNED] = None
break
if self.sys_security.force and data[ATTR_PWNED] in (None, True):
data[ATTR_VALID] = False
if data[ATTR_PWNED] is None:
data[ATTR_MESSAGE] = "Error happening on pwned secrets check!"
else:
data[ATTR_MESSAGE] = "Add-on uses pwned secrets!"
data[ATTR_VALID] = False
break
return data
@@ -382,7 +374,7 @@ class APIAddons(CoreSysAttributes):
addon = self._extract_addon_installed(request)
try:
return addon.schema.validate(addon.options)
return addon.schema(addon.options)
except vol.Invalid:
raise APIError("Invalid configuration data for the add-on") from None

View File

@@ -1,6 +0,0 @@
"""Const for API."""
ATTR_USE_RTC = "use_rtc"
ATTR_USE_NTP = "use_ntp"
ATTR_DT_UTC = "dt_utc"
ATTR_DT_SYNCHRONIZED = "dt_synchronized"

View File

@@ -1,6 +1,6 @@
"""Init file for Supervisor hardware RESTful API."""
import logging
from typing import Any, Dict
from typing import Any, Awaitable, Dict
from aiohttp import web
@@ -58,3 +58,8 @@ class APIHardware(CoreSysAttributes):
},
}
}
@api_process
async def trigger(self, request: web.Request) -> Awaitable[None]:
"""Trigger a udev device reload."""
_LOGGER.debug("Ignoring DEPRECATED hardware trigger function call.")

View File

@@ -21,11 +21,9 @@ from ..const import (
ATTR_OPERATING_SYSTEM,
ATTR_SERVICES,
ATTR_STATE,
ATTR_TIMEZONE,
CONTENT_TYPE_BINARY,
)
from ..coresys import CoreSysAttributes
from .const import ATTR_DT_SYNCHRONIZED, ATTR_DT_UTC, ATTR_USE_NTP, ATTR_USE_RTC
from .utils import api_process, api_process_raw, api_validate
SERVICE = "service"
@@ -51,11 +49,6 @@ class APIHost(CoreSysAttributes):
ATTR_HOSTNAME: self.sys_host.info.hostname,
ATTR_KERNEL: self.sys_host.info.kernel,
ATTR_OPERATING_SYSTEM: self.sys_host.info.operating_system,
ATTR_TIMEZONE: self.sys_host.info.timezone,
ATTR_DT_UTC: self.sys_host.info.dt_utc,
ATTR_DT_SYNCHRONIZED: self.sys_host.info.dt_synchronized,
ATTR_USE_NTP: self.sys_host.info.use_ntp,
ATTR_USE_RTC: self.sys_host.info.use_rtc,
}
@api_process

View File

@@ -48,5 +48,5 @@ class APIInfo(CoreSysAttributes):
ATTR_SUPPORTED: self.sys_core.supported,
ATTR_CHANNEL: self.sys_updater.channel,
ATTR_LOGGING: self.sys_config.logging,
ATTR_TIMEZONE: self.sys_timezone,
ATTR_TIMEZONE: self.sys_config.timezone,
}

View File

@@ -5,7 +5,7 @@ import logging
from typing import Any, Dict, Union
import aiohttp
from aiohttp import ClientTimeout, hdrs, web
from aiohttp import hdrs, web
from aiohttp.web_exceptions import (
HTTPBadGateway,
HTTPServiceUnavailable,
@@ -162,18 +162,9 @@ class APIIngress(CoreSysAttributes):
) -> Union[web.Response, web.StreamResponse]:
"""Ingress route for request."""
url = self._create_url(addon, path)
data = await request.read()
source_header = _init_header(request, addon)
# Passing the raw stream breaks requests for some webservers
# since we just need it for POST requests really, for all other methods
# we read the bytes and pass that to the request to the add-on
# add-ons needs to add support with that in the configuration
data = (
request.content
if request.method == "POST" and addon.ingress_stream
else await request.read()
)
async with self.sys_websession.request(
request.method,
url,
@@ -181,7 +172,6 @@ class APIIngress(CoreSysAttributes):
params=request.query,
allow_redirects=False,
data=data,
timeout=ClientTimeout(total=None),
) as result:
headers = _response_header(result)
@@ -229,7 +219,6 @@ def _init_header(
if name in (
hdrs.CONTENT_LENGTH,
hdrs.CONTENT_ENCODING,
hdrs.TRANSFER_ENCODING,
hdrs.SEC_WEBSOCKET_EXTENSIONS,
hdrs.SEC_WEBSOCKET_PROTOCOL,
hdrs.SEC_WEBSOCKET_VERSION,

View File

@@ -1 +0,0 @@
"""API middleware for aiohttp."""

View File

@@ -1,206 +0,0 @@
"""Handle security part of this API."""
import logging
import re
from aiohttp.web import Request, RequestHandler, Response, middleware
from aiohttp.web_exceptions import HTTPForbidden, HTTPUnauthorized
from ...const import (
REQUEST_FROM,
ROLE_ADMIN,
ROLE_BACKUP,
ROLE_DEFAULT,
ROLE_HOMEASSISTANT,
ROLE_MANAGER,
CoreState,
)
from ...coresys import CoreSys, CoreSysAttributes
from ..utils import api_return_error, excract_supervisor_token
_LOGGER: logging.Logger = logging.getLogger(__name__)
# fmt: off
# Block Anytime
BLACKLIST = re.compile(
r"^(?:"
r"|/homeassistant/api/hassio/.*"
r"|/core/api/hassio/.*"
r")$"
)
# Free to call or have own security concepts
NO_SECURITY_CHECK = re.compile(
r"^(?:"
r"|/homeassistant/api/.*"
r"|/homeassistant/websocket"
r"|/core/api/.*"
r"|/core/websocket"
r"|/supervisor/ping"
r")$"
)
# Observer allow API calls
OBSERVER_CHECK = re.compile(
r"^(?:"
r"|/.+/info"
r")$"
)
# Can called by every add-on
ADDONS_API_BYPASS = re.compile(
r"^(?:"
r"|/addons/self/(?!security|update)[^/]+"
r"|/addons/self/options/config"
r"|/info"
r"|/services.*"
r"|/discovery.*"
r"|/auth"
r")$"
)
# Policy role add-on API access
ADDONS_ROLE_ACCESS = {
ROLE_DEFAULT: re.compile(
r"^(?:"
r"|/.+/info"
r")$"
),
ROLE_HOMEASSISTANT: re.compile(
r"^(?:"
r"|/.+/info"
r"|/core/.+"
r"|/homeassistant/.+"
r")$"
),
ROLE_BACKUP: re.compile(
r"^(?:"
r"|/.+/info"
r"|/snapshots.*"
r")$"
),
ROLE_MANAGER: re.compile(
r"^(?:"
r"|/.+/info"
r"|/addons(?:/[^/]+/(?!security).+|/reload)?"
r"|/audio/.+"
r"|/auth/cache"
r"|/cli/.+"
r"|/core/.+"
r"|/dns/.+"
r"|/docker/.+"
r"|/jobs/.+"
r"|/hardware/.+"
r"|/hassos/.+"
r"|/homeassistant/.+"
r"|/host/.+"
r"|/multicast/.+"
r"|/network/.+"
r"|/observer/.+"
r"|/os/.+"
r"|/resolution/.+"
r"|/snapshots.*"
r"|/store.*"
r"|/supervisor/.+"
r"|/security/.+"
r")$"
),
ROLE_ADMIN: re.compile(
r".*"
),
}
# fmt: on
class SecurityMiddleware(CoreSysAttributes):
"""Security middleware functions."""
def __init__(self, coresys: CoreSys):
"""Initialize security middleware."""
self.coresys: CoreSys = coresys
@middleware
async def system_validation(
self, request: Request, handler: RequestHandler
) -> Response:
"""Check if core is ready to response."""
if self.sys_core.state not in (
CoreState.STARTUP,
CoreState.RUNNING,
CoreState.FREEZE,
):
return api_return_error(
message=f"System is not ready with state: {self.sys_core.state.value}"
)
return await handler(request)
@middleware
async def token_validation(
self, request: Request, handler: RequestHandler
) -> Response:
"""Check security access of this layer."""
request_from = None
supervisor_token = excract_supervisor_token(request)
# Blacklist
if BLACKLIST.match(request.path):
_LOGGER.error("%s is blacklisted!", request.path)
raise HTTPForbidden()
# Ignore security check
if NO_SECURITY_CHECK.match(request.path):
_LOGGER.debug("Passthrough %s", request.path)
return await handler(request)
# Not token
if not supervisor_token:
_LOGGER.warning("No API token provided for %s", request.path)
raise HTTPUnauthorized()
# Home-Assistant
if supervisor_token == self.sys_homeassistant.supervisor_token:
_LOGGER.debug("%s access from Home Assistant", request.path)
request_from = self.sys_homeassistant
# Host
if supervisor_token == self.sys_plugins.cli.supervisor_token:
_LOGGER.debug("%s access from Host", request.path)
request_from = self.sys_host
# Observer
if supervisor_token == self.sys_plugins.observer.supervisor_token:
if not OBSERVER_CHECK.match(request.url):
_LOGGER.warning("%s invalid Observer access", request.path)
raise HTTPForbidden()
_LOGGER.debug("%s access from Observer", request.path)
request_from = self.sys_plugins.observer
# Add-on
addon = None
if supervisor_token and not request_from:
addon = self.sys_addons.from_token(supervisor_token)
# Check Add-on API access
if addon and ADDONS_API_BYPASS.match(request.path):
_LOGGER.debug("Passthrough %s from %s", request.path, addon.slug)
request_from = addon
elif addon and addon.access_hassio_api:
# Check Role
if ADDONS_ROLE_ACCESS[addon.hassio_role].match(request.path):
_LOGGER.info("%s access from %s", request.path, addon.slug)
request_from = addon
else:
_LOGGER.warning("%s no role for %s", request.path, addon.slug)
elif addon:
_LOGGER.warning(
"%s missing API permission for %s", addon.slug, request.path
)
if request_from:
request[REQUEST_FROM] = request_from
return await handler(request)
_LOGGER.error("Invalid token for access %s", request.path)
raise HTTPForbidden()

View File

@@ -1,9 +1,9 @@
try {
new Function("import('/api/hassio/app/frontend_latest/entrypoint.0d3c68f7.js')")();
new Function("import('/api/hassio/app/frontend_latest/entrypoint.4050b348.js')")();
} catch (err) {
var el = document.createElement('script');
el.src = '/api/hassio/app/frontend_es5/entrypoint.9e377d5a.js';
el.src = '/api/hassio/app/frontend_es5/entrypoint.bcf8e8ff.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.022ea7e862bfe9729114.js","sources":["webpack://home-assistant-frontend/chunk.022ea7e862bfe9729114.js"],"mappings":"AAAA","sourceRoot":""}

View File

@@ -1,2 +0,0 @@
(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([[534],{68441:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l=new WeakMap;t.default=function(e){var t=l.get(e);return t||(t=Object.create(null),l.set(e,t)),t}},78643:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PluralRules=void 0;var a=l(87480),n=l(43965),r=a.__importDefault(l(68441));function o(e,t){if(!(e instanceof u))throw new TypeError("Method Intl.PluralRules.prototype."+t+" called on incompatible receiver "+String(e))}function i(e,t,l,a){var n=a.IntegerDigits,r=a.NumberOfFractionDigits,o=a.FractionDigits;return u.localeData[e].fn(r?n+"."+o:n,"ordinal"===t)}var u=function(){function e(t,l){if(!(this&&this instanceof e?this.constructor:void 0))throw new TypeError("Intl.PluralRules must be called with 'new'");return n.InitializePluralRules(this,t,l,{availableLocales:e.availableLocales,relevantExtensionKeys:e.relevantExtensionKeys,localeData:e.localeData,getDefaultLocale:e.getDefaultLocale,getInternalSlots:r.default})}return e.prototype.resolvedOptions=function(){o(this,"resolvedOptions");var t=Object.create(null),l=r.default(this);return t.locale=l.locale,t.type=l.type,["minimumIntegerDigits","minimumFractionDigits","maximumFractionDigits","minimumSignificantDigits","maximumSignificantDigits"].forEach((function(e){var a=l[e];void 0!==a&&(t[e]=a)})),t.pluralCategories=a.__spreadArray([],e.localeData[t.locale].categories[t.type]),t},e.prototype.select=function(e){o(this,"select");var t=n.ToNumber(e);return n.ResolvePlural(this,t,{getInternalSlots:r.default,PluralRuleSelect:i})},e.prototype.toString=function(){return"[object Intl.PluralRules]"},e.supportedLocalesOf=function(t,l){return n.SupportedLocales(e.availableLocales,n.CanonicalizeLocaleList(t),l)},e.__addLocaleData=function(){for(var t=[],l=0;l<arguments.length;l++)t[l]=arguments[l];for(var a=0,n=t;a<n.length;a++){var r=n[a],o=r.data,i=r.locale;e.localeData[i]=o,e.availableLocales.add(i),e.__defaultLocale||(e.__defaultLocale=i)}},e.getDefaultLocale=function(){return e.__defaultLocale},e.localeData={},e.availableLocales=new Set,e.__defaultLocale="",e.relevantExtensionKeys=[],e.polyfilled=!0,e}();t.PluralRules=u;try{"undefined"!=typeof Symbol&&Object.defineProperty(u.prototype,Symbol.toStringTag,{value:"Intl.PluralRules",writable:!1,enumerable:!1,configurable:!0});try{Object.defineProperty(u,"length",{value:0,writable:!1,enumerable:!1,configurable:!0})}catch(c){}Object.defineProperty(u.prototype.constructor,"length",{value:0,writable:!1,enumerable:!1,configurable:!0}),Object.defineProperty(u.supportedLocalesOf,"length",{value:1,writable:!1,enumerable:!1,configurable:!0})}catch(s){}},25534:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=l(78643);l(27815).shouldPolyfill()&&Object.defineProperty(Intl,"PluralRules",{value:a.PluralRules,writable:!0,enumerable:!1,configurable:!0})},27815:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.shouldPolyfill=void 0,t.shouldPolyfill=function(){return"undefined"==typeof Intl||!("PluralRules"in Intl)||"one"===new Intl.PluralRules("en",{minimumFractionDigits:2}).select(1)}}}]);
//# sourceMappingURL=chunk.1000be27cd087c21fa92.js.map

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,2 +0,0 @@
!function(){"use strict";var r,t={5425:function(r,t,e){var n=e(91107);e(58556);function o(r,t){return function(r){if(Array.isArray(r))return r}(r)||function(r,t){var e=r&&("undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"]);if(null==e)return;var n,o,i=[],u=!0,a=!1;try{for(e=e.call(r);!(u=(n=e.next()).done)&&(i.push(n.value),!t||i.length!==t);u=!0);}catch(f){a=!0,o=f}finally{try{u||null==e.return||e.return()}finally{if(a)throw o}}return i}(r,t)||function(r,t){if(!r)return;if("string"==typeof r)return i(r,t);var e=Object.prototype.toString.call(r).slice(8,-1);"Object"===e&&r.constructor&&(e=r.constructor.name);if("Map"===e||"Set"===e)return Array.from(r);if("Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e))return i(r,t)}(r,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(r,t){(null==t||t>r.length)&&(t=r.length);for(var e=0,n=new Array(t);e<t;e++)n[e]=r[e];return n}var u={filterData:function(r,t,e){return e=e.toUpperCase(),r.filter((function(r){return Object.entries(t).some((function(t){var n=o(t,2),i=n[0],u=n[1];return!(!u.filterable||!String(u.filterKey?r[i][u.filterKey]:r[i]).toUpperCase().includes(e))}))}))},sortData:function(r,t,e,n){return r.sort((function(r,o){var i=1;"desc"===e&&(i=-1);var u=t.filterKey?r[n][t.filterKey]:r[n],a=t.filterKey?o[n][t.filterKey]:o[n];return"string"==typeof u&&(u=u.toUpperCase()),"string"==typeof a&&(a=a.toUpperCase()),void 0===u&&void 0!==a?1:void 0===a&&void 0!==u?-1:u<a?-1*i:u>a?1*i:0}))}};(0,n.Jj)(u)}},e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={exports:{}};return t[r](o,o.exports,n),o.exports}n.m=t,n.x=function(){return n(5425)},n.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return n.d(t,{a:t}),t},n.d=function(r,t){for(var e in t)n.o(t,e)&&!n.o(r,e)&&Object.defineProperty(r,e,{enumerable:!0,get:t[e]})},n.f={},n.e=function(r){return Promise.all(Object.keys(n.f).reduce((function(t,e){return n.f[e](r,t),t}),[]))},n.u=function(r){return"chunk.aa4c3f9e4936a956fbc9.js"},n.o=function(r,t){return Object.prototype.hasOwnProperty.call(r,t)},r=n.x,n.x=function(){return n.e(354).then(r)},n.p="/api/hassio/app/frontend_es5/",function(){var r={425:1,477:1};n.f.i=function(t,e){r[t]||importScripts(""+n.u(t))};var t=self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[],e=t.push.bind(t);t.push=function(t){var o=t[0],i=t[1],u=t[2];for(var a in i)n.o(i,a)&&(n.m[a]=i[a]);for(u&&u(n);o.length;)r[o.pop()]=1;e(t)}}();n.x()}();
//# sourceMappingURL=chunk.232ece16c0cd5fc459b7.js.map

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,2 +0,0 @@
!function(){"use strict";var r,t={5425:function(r,t,e){var n=e(91107);e(58556);function o(r,t){return function(r){if(Array.isArray(r))return r}(r)||function(r,t){var e=r&&("undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"]);if(null==e)return;var n,o,i=[],u=!0,a=!1;try{for(e=e.call(r);!(u=(n=e.next()).done)&&(i.push(n.value),!t||i.length!==t);u=!0);}catch(f){a=!0,o=f}finally{try{u||null==e.return||e.return()}finally{if(a)throw o}}return i}(r,t)||function(r,t){if(!r)return;if("string"==typeof r)return i(r,t);var e=Object.prototype.toString.call(r).slice(8,-1);"Object"===e&&r.constructor&&(e=r.constructor.name);if("Map"===e||"Set"===e)return Array.from(r);if("Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e))return i(r,t)}(r,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(r,t){(null==t||t>r.length)&&(t=r.length);for(var e=0,n=new Array(t);e<t;e++)n[e]=r[e];return n}var u={filterData:function(r,t,e){return e=e.toUpperCase(),r.filter((function(r){return Object.entries(t).some((function(t){var n=o(t,2),i=n[0],u=n[1];return!(!u.filterable||!String(u.filterKey?r[i][u.filterKey]:r[i]).toUpperCase().includes(e))}))}))},sortData:function(r,t,e,n){return r.sort((function(r,o){var i=1;"desc"===e&&(i=-1);var u=t.filterKey?r[n][t.filterKey]:r[n],a=t.filterKey?o[n][t.filterKey]:o[n];return"string"==typeof u&&(u=u.toUpperCase()),"string"==typeof a&&(a=a.toUpperCase()),void 0===u&&void 0!==a?1:void 0===a&&void 0!==u?-1:u<a?-1*i:u>a?1*i:0}))}};(0,n.Jj)(u)}},e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={exports:{}};return t[r](o,o.exports,n),o.exports}n.m=t,n.x=function(){return n(5425)},n.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return n.d(t,{a:t}),t},n.d=function(r,t){for(var e in t)n.o(t,e)&&!n.o(r,e)&&Object.defineProperty(r,e,{enumerable:!0,get:t[e]})},n.f={},n.e=function(r){return Promise.all(Object.keys(n.f).reduce((function(t,e){return n.f[e](r,t),t}),[]))},n.u=function(r){return"chunk.aa4c3f9e4936a956fbc9.js"},n.o=function(r,t){return Object.prototype.hasOwnProperty.call(r,t)},r=n.x,n.x=function(){return n.e(354).then(r)},n.p="/api/hassio/app/frontend_es5/",function(){var r={477:1,425:1};n.f.i=function(t,e){r[t]||importScripts(""+n.u(t))};var t=self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[],e=t.push.bind(t);t.push=function(t){var o=t[0],i=t[1],u=t[2];for(var a in i)n.o(i,a)&&(n.m[a]=i[a]);for(u&&u(n);o.length;)r[o.pop()]=1;e(t)}}();n.x()}();
//# sourceMappingURL=chunk.38bf45e75c5f9e1064ab.js.map

View File

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

View File

@@ -1,2 +0,0 @@
!function(){"use strict";var n,t={14971:function(n,t,e){var r,o,i=e(91107),a=e(9902),u=e.n(a),f=(e(58556),e(62173)),s={renderMarkdown:function(n,t){var e,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return r||(r=Object.assign({},(0,f.getDefaultWhiteList)(),{"ha-icon":["icon"],"ha-svg-icon":["path"]})),i.allowSvg?(o||(o=Object.assign({},r,{svg:["xmlns","height","width"],path:["transform","stroke","d"],img:["src"]})),e=o):e=r,(0,f.filterXSS)(u()(n,t),{whiteList:e})}};(0,i.Jj)(s)}},e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={exports:{}};return t[n].call(o.exports,o,o.exports,r),o.exports}r.m=t,r.x=function(){return r(14971)},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,{a:t}),t},r.d=function(n,t){for(var e in t)r.o(t,e)&&!r.o(n,e)&&Object.defineProperty(n,e,{enumerable:!0,get:t[e]})},r.f={},r.e=function(n){return Promise.all(Object.keys(r.f).reduce((function(t,e){return r.f[e](n,t),t}),[]))},r.u=function(n){return"chunk."+{354:"aa4c3f9e4936a956fbc9",468:"b9d30baa8281f2ef1718"}[n]+".js"},r.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},n=r.x,r.x=function(){return Promise.all([r.e(354),r.e(468)]).then(n)},r.p="/api/hassio/app/frontend_es5/",function(){var n={971:1};r.f.i=function(t,e){n[t]||importScripts(""+r.u(t))};var t=self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[],e=t.push.bind(t);t.push=function(t){var o=t[0],i=t[1],a=t[2];for(var u in i)r.o(i,u)&&(r.m[u]=i[u]);for(a&&a(r);o.length;)n[o.pop()]=1;e(t)}}();r.x()}();
//# sourceMappingURL=chunk.390a8f1bf0c6addf0c23.js.map

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
part of the polymer project is also subject to an additional IP rights grant
found at http://polymer.github.io/PATENTS.txt
*/

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */

View File

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

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More