Remove deprecated credstash + keyring (#47033)
This commit is contained in:
parent
72263abfa9
commit
633a7aeb22
10 changed files with 4 additions and 262 deletions
|
@ -141,12 +141,7 @@ def run(script_args: List) -> int:
|
||||||
if sval is None:
|
if sval is None:
|
||||||
print(" -", skey + ":", color("red", "not found"))
|
print(" -", skey + ":", color("red", "not found"))
|
||||||
continue
|
continue
|
||||||
print(
|
print(" -", skey + ":", sval)
|
||||||
" -",
|
|
||||||
skey + ":",
|
|
||||||
sval,
|
|
||||||
color("cyan", "[from:", flatsecret.get(skey, "keyring") + "]"),
|
|
||||||
)
|
|
||||||
|
|
||||||
return len(res["except"])
|
return len(res["except"])
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
"""Script to get, put and delete secrets stored in credstash."""
|
|
||||||
import argparse
|
|
||||||
import getpass
|
|
||||||
|
|
||||||
from homeassistant.util.yaml import _SECRET_NAMESPACE
|
|
||||||
|
|
||||||
# mypy: allow-untyped-defs
|
|
||||||
|
|
||||||
REQUIREMENTS = ["credstash==1.15.0"]
|
|
||||||
|
|
||||||
|
|
||||||
def run(args):
|
|
||||||
"""Handle credstash script."""
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description=(
|
|
||||||
"Modify Home Assistant secrets in credstash."
|
|
||||||
"Use the secrets in configuration files with: "
|
|
||||||
"!secret <name>"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
parser.add_argument("--script", choices=["credstash"])
|
|
||||||
parser.add_argument(
|
|
||||||
"action",
|
|
||||||
choices=["get", "put", "del", "list"],
|
|
||||||
help="Get, put or delete a secret, or list all available secrets",
|
|
||||||
)
|
|
||||||
parser.add_argument("name", help="Name of the secret", nargs="?", default=None)
|
|
||||||
parser.add_argument(
|
|
||||||
"value", help="The value to save when putting a secret", nargs="?", default=None
|
|
||||||
)
|
|
||||||
|
|
||||||
# pylint: disable=import-error, no-member, import-outside-toplevel
|
|
||||||
import credstash
|
|
||||||
|
|
||||||
args = parser.parse_args(args)
|
|
||||||
table = _SECRET_NAMESPACE
|
|
||||||
|
|
||||||
try:
|
|
||||||
credstash.listSecrets(table=table)
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
credstash.createDdbTable(table=table)
|
|
||||||
|
|
||||||
if args.action == "list":
|
|
||||||
secrets = [i["name"] for i in credstash.listSecrets(table=table)]
|
|
||||||
deduped_secrets = sorted(set(secrets))
|
|
||||||
|
|
||||||
print("Saved secrets:")
|
|
||||||
for secret in deduped_secrets:
|
|
||||||
print(secret)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if args.name is None:
|
|
||||||
parser.print_help()
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if args.action == "put":
|
|
||||||
if args.value:
|
|
||||||
the_secret = args.value
|
|
||||||
else:
|
|
||||||
the_secret = getpass.getpass(f"Please enter the secret for {args.name}: ")
|
|
||||||
current_version = credstash.getHighestVersion(args.name, table=table)
|
|
||||||
credstash.putSecret(
|
|
||||||
args.name, the_secret, version=int(current_version) + 1, table=table
|
|
||||||
)
|
|
||||||
print(f"Secret {args.name} put successfully")
|
|
||||||
elif args.action == "get":
|
|
||||||
the_secret = credstash.getSecret(args.name, table=table)
|
|
||||||
if the_secret is None:
|
|
||||||
print(f"Secret {args.name} not found")
|
|
||||||
else:
|
|
||||||
print(f"Secret {args.name}={the_secret}")
|
|
||||||
elif args.action == "del":
|
|
||||||
credstash.deleteSecrets(args.name, table=table)
|
|
||||||
print(f"Deleted secret {args.name}")
|
|
|
@ -1,62 +0,0 @@
|
||||||
"""Script to get, set and delete secrets stored in the keyring."""
|
|
||||||
import argparse
|
|
||||||
import getpass
|
|
||||||
import os
|
|
||||||
|
|
||||||
from homeassistant.util.yaml import _SECRET_NAMESPACE
|
|
||||||
|
|
||||||
# mypy: allow-untyped-defs
|
|
||||||
REQUIREMENTS = ["keyring==21.2.0", "keyrings.alt==3.4.0"]
|
|
||||||
|
|
||||||
|
|
||||||
def run(args):
|
|
||||||
"""Handle keyring script."""
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description=(
|
|
||||||
"Modify Home Assistant secrets in the default keyring. "
|
|
||||||
"Use the secrets in configuration files with: "
|
|
||||||
"!secret <name>"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
parser.add_argument("--script", choices=["keyring"])
|
|
||||||
parser.add_argument(
|
|
||||||
"action",
|
|
||||||
choices=["get", "set", "del", "info"],
|
|
||||||
help="Get, set or delete a secret",
|
|
||||||
)
|
|
||||||
parser.add_argument("name", help="Name of the secret", nargs="?", default=None)
|
|
||||||
|
|
||||||
import keyring # pylint: disable=import-outside-toplevel
|
|
||||||
|
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
from keyring.util import platform_ as platform
|
|
||||||
|
|
||||||
args = parser.parse_args(args)
|
|
||||||
|
|
||||||
if args.action == "info":
|
|
||||||
keyr = keyring.get_keyring()
|
|
||||||
print("Keyring version {}\n".format(REQUIREMENTS[0].split("==")[1]))
|
|
||||||
print(f"Active keyring : {keyr.__module__}")
|
|
||||||
config_name = os.path.join(platform.config_root(), "keyringrc.cfg")
|
|
||||||
print(f"Config location : {config_name}")
|
|
||||||
print(f"Data location : {platform.data_root()}\n")
|
|
||||||
elif args.name is None:
|
|
||||||
parser.print_help()
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if args.action == "set":
|
|
||||||
entered_secret = getpass.getpass(f"Please enter the secret for {args.name}: ")
|
|
||||||
keyring.set_password(_SECRET_NAMESPACE, args.name, entered_secret)
|
|
||||||
print(f"Secret {args.name} set successfully")
|
|
||||||
elif args.action == "get":
|
|
||||||
the_secret = keyring.get_password(_SECRET_NAMESPACE, args.name)
|
|
||||||
if the_secret is None:
|
|
||||||
print(f"Secret {args.name} not found")
|
|
||||||
else:
|
|
||||||
print(f"Secret {args.name}={the_secret}")
|
|
||||||
elif args.action == "del":
|
|
||||||
try:
|
|
||||||
keyring.delete_password(_SECRET_NAMESPACE, args.name)
|
|
||||||
print(f"Deleted secret {args.name}")
|
|
||||||
except keyring.errors.PasswordDeleteError:
|
|
||||||
print(f"Secret {args.name} not found")
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""YAML utility functions."""
|
"""YAML utility functions."""
|
||||||
from .const import _SECRET_NAMESPACE, SECRET_YAML
|
from .const import SECRET_YAML
|
||||||
from .dumper import dump, save_yaml
|
from .dumper import dump, save_yaml
|
||||||
from .input import UndefinedSubstitution, extract_inputs, substitute
|
from .input import UndefinedSubstitution, extract_inputs, substitute
|
||||||
from .loader import clear_secret_cache, load_yaml, parse_yaml, secret_yaml
|
from .loader import clear_secret_cache, load_yaml, parse_yaml, secret_yaml
|
||||||
|
@ -7,7 +7,6 @@ from .objects import Input
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"SECRET_YAML",
|
"SECRET_YAML",
|
||||||
"_SECRET_NAMESPACE",
|
|
||||||
"Input",
|
"Input",
|
||||||
"dump",
|
"dump",
|
||||||
"save_yaml",
|
"save_yaml",
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
"""Constants."""
|
"""Constants."""
|
||||||
SECRET_YAML = "secrets.yaml"
|
SECRET_YAML = "secrets.yaml"
|
||||||
|
|
||||||
_SECRET_NAMESPACE = "homeassistant"
|
|
||||||
|
|
|
@ -10,20 +10,9 @@ import yaml
|
||||||
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from .const import _SECRET_NAMESPACE, SECRET_YAML
|
from .const import SECRET_YAML
|
||||||
from .objects import Input, NodeListClass, NodeStrClass
|
from .objects import Input, NodeListClass, NodeStrClass
|
||||||
|
|
||||||
try:
|
|
||||||
import keyring
|
|
||||||
except ImportError:
|
|
||||||
keyring = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
import credstash
|
|
||||||
except ImportError:
|
|
||||||
credstash = None
|
|
||||||
|
|
||||||
|
|
||||||
# mypy: allow-untyped-calls, no-warn-return-any
|
# mypy: allow-untyped-calls, no-warn-return-any
|
||||||
|
|
||||||
JSON_TYPE = Union[List, Dict, str] # pylint: disable=invalid-name
|
JSON_TYPE = Union[List, Dict, str] # pylint: disable=invalid-name
|
||||||
|
@ -32,9 +21,6 @@ DICT_T = TypeVar("DICT_T", bound=Dict) # pylint: disable=invalid-name
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
__SECRET_CACHE: Dict[str, JSON_TYPE] = {}
|
__SECRET_CACHE: Dict[str, JSON_TYPE] = {}
|
||||||
|
|
||||||
CREDSTASH_WARN = False
|
|
||||||
KEYRING_WARN = False
|
|
||||||
|
|
||||||
|
|
||||||
def clear_secret_cache() -> None:
|
def clear_secret_cache() -> None:
|
||||||
"""Clear the secret cache.
|
"""Clear the secret cache.
|
||||||
|
@ -299,43 +285,6 @@ def secret_yaml(loader: SafeLineLoader, node: yaml.nodes.Node) -> JSON_TYPE:
|
||||||
if not os.path.exists(secret_path) or len(secret_path) < 5:
|
if not os.path.exists(secret_path) or len(secret_path) < 5:
|
||||||
break # Somehow we got past the .homeassistant config folder
|
break # Somehow we got past the .homeassistant config folder
|
||||||
|
|
||||||
if keyring:
|
|
||||||
# do some keyring stuff
|
|
||||||
pwd = keyring.get_password(_SECRET_NAMESPACE, node.value)
|
|
||||||
if pwd:
|
|
||||||
global KEYRING_WARN # pylint: disable=global-statement
|
|
||||||
|
|
||||||
if not KEYRING_WARN:
|
|
||||||
KEYRING_WARN = True
|
|
||||||
_LOGGER.warning(
|
|
||||||
"Keyring is deprecated and will be removed in March 2021."
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER.debug("Secret %s retrieved from keyring", node.value)
|
|
||||||
return pwd
|
|
||||||
|
|
||||||
global credstash # pylint: disable=invalid-name, global-statement
|
|
||||||
|
|
||||||
if credstash:
|
|
||||||
# pylint: disable=no-member
|
|
||||||
try:
|
|
||||||
pwd = credstash.getSecret(node.value, table=_SECRET_NAMESPACE)
|
|
||||||
if pwd:
|
|
||||||
global CREDSTASH_WARN # pylint: disable=global-statement
|
|
||||||
|
|
||||||
if not CREDSTASH_WARN:
|
|
||||||
CREDSTASH_WARN = True
|
|
||||||
_LOGGER.warning(
|
|
||||||
"Credstash is deprecated and will be removed in March 2021."
|
|
||||||
)
|
|
||||||
_LOGGER.debug("Secret %s retrieved from credstash", node.value)
|
|
||||||
return pwd
|
|
||||||
except credstash.ItemNotFound:
|
|
||||||
pass
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
# Catch if package installed and no config
|
|
||||||
credstash = None
|
|
||||||
|
|
||||||
raise HomeAssistantError(f"Secret {node.value} not defined")
|
raise HomeAssistantError(f"Secret {node.value} not defined")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -448,9 +448,6 @@ construct==2.10.56
|
||||||
# homeassistant.components.coronavirus
|
# homeassistant.components.coronavirus
|
||||||
coronavirus==1.1.1
|
coronavirus==1.1.1
|
||||||
|
|
||||||
# homeassistant.scripts.credstash
|
|
||||||
# credstash==1.15.0
|
|
||||||
|
|
||||||
# homeassistant.components.datadog
|
# homeassistant.components.datadog
|
||||||
datadog==0.15.0
|
datadog==0.15.0
|
||||||
|
|
||||||
|
@ -844,12 +841,6 @@ kaiterra-async-client==0.0.2
|
||||||
# homeassistant.components.keba
|
# homeassistant.components.keba
|
||||||
keba-kecontact==1.1.0
|
keba-kecontact==1.1.0
|
||||||
|
|
||||||
# homeassistant.scripts.keyring
|
|
||||||
keyring==21.2.0
|
|
||||||
|
|
||||||
# homeassistant.scripts.keyring
|
|
||||||
keyrings.alt==3.4.0
|
|
||||||
|
|
||||||
# homeassistant.components.kiwi
|
# homeassistant.components.kiwi
|
||||||
kiwiki-client==0.1.1
|
kiwiki-client==0.1.1
|
||||||
|
|
||||||
|
|
|
@ -239,9 +239,6 @@ construct==2.10.56
|
||||||
# homeassistant.components.coronavirus
|
# homeassistant.components.coronavirus
|
||||||
coronavirus==1.1.1
|
coronavirus==1.1.1
|
||||||
|
|
||||||
# homeassistant.scripts.credstash
|
|
||||||
# credstash==1.15.0
|
|
||||||
|
|
||||||
# homeassistant.components.datadog
|
# homeassistant.components.datadog
|
||||||
datadog==0.15.0
|
datadog==0.15.0
|
||||||
|
|
||||||
|
@ -455,12 +452,6 @@ influxdb==5.2.3
|
||||||
# homeassistant.components.verisure
|
# homeassistant.components.verisure
|
||||||
jsonpath==0.82
|
jsonpath==0.82
|
||||||
|
|
||||||
# homeassistant.scripts.keyring
|
|
||||||
keyring==21.2.0
|
|
||||||
|
|
||||||
# homeassistant.scripts.keyring
|
|
||||||
keyrings.alt==3.4.0
|
|
||||||
|
|
||||||
# homeassistant.components.konnected
|
# homeassistant.components.konnected
|
||||||
konnected==1.2.0
|
konnected==1.2.0
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ COMMENT_REQUIREMENTS = (
|
||||||
"blinkt",
|
"blinkt",
|
||||||
"bluepy",
|
"bluepy",
|
||||||
"bme680",
|
"bme680",
|
||||||
"credstash",
|
|
||||||
"decora",
|
"decora",
|
||||||
"decora_wifi",
|
"decora_wifi",
|
||||||
"envirophat",
|
"envirophat",
|
||||||
|
@ -47,7 +46,7 @@ COMMENT_REQUIREMENTS = (
|
||||||
"VL53L1X2",
|
"VL53L1X2",
|
||||||
)
|
)
|
||||||
|
|
||||||
IGNORE_PIN = ("colorlog>2.1,<3", "keyring>=9.3,<10.0", "urllib3")
|
IGNORE_PIN = ("colorlog>2.1,<3", "urllib3")
|
||||||
|
|
||||||
URL_PIN = (
|
URL_PIN = (
|
||||||
"https://developers.home-assistant.io/docs/"
|
"https://developers.home-assistant.io/docs/"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""Test Home Assistant yaml loader."""
|
"""Test Home Assistant yaml loader."""
|
||||||
import io
|
import io
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
@ -15,14 +14,6 @@ from homeassistant.util.yaml import loader as yaml_loader
|
||||||
from tests.common import get_test_config_dir, patch_yaml_files
|
from tests.common import get_test_config_dir, patch_yaml_files
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def mock_credstash():
|
|
||||||
"""Mock credstash so it doesn't connect to the internet."""
|
|
||||||
with patch.object(yaml_loader, "credstash") as mock_credstash:
|
|
||||||
mock_credstash.getSecret.return_value = None
|
|
||||||
yield mock_credstash
|
|
||||||
|
|
||||||
|
|
||||||
def test_simple_list():
|
def test_simple_list():
|
||||||
"""Test simple list."""
|
"""Test simple list."""
|
||||||
conf = "config:\n - simple\n - list"
|
conf = "config:\n - simple\n - list"
|
||||||
|
@ -294,20 +285,6 @@ def load_yaml(fname, string):
|
||||||
return load_yaml_config_file(fname)
|
return load_yaml_config_file(fname)
|
||||||
|
|
||||||
|
|
||||||
class FakeKeyring:
|
|
||||||
"""Fake a keyring class."""
|
|
||||||
|
|
||||||
def __init__(self, secrets_dict):
|
|
||||||
"""Store keyring dictionary."""
|
|
||||||
self._secrets = secrets_dict
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
def get_password(self, domain, name):
|
|
||||||
"""Retrieve password."""
|
|
||||||
assert domain == yaml._SECRET_NAMESPACE
|
|
||||||
return self._secrets.get(name)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSecrets(unittest.TestCase):
|
class TestSecrets(unittest.TestCase):
|
||||||
"""Test the secrets parameter in the yaml utility."""
|
"""Test the secrets parameter in the yaml utility."""
|
||||||
|
|
||||||
|
@ -395,27 +372,6 @@ class TestSecrets(unittest.TestCase):
|
||||||
"http:\n api_password: !secret test",
|
"http:\n api_password: !secret test",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_secrets_keyring(self):
|
|
||||||
"""Test keyring fallback & get_password."""
|
|
||||||
yaml_loader.keyring = None # Ensure its not there
|
|
||||||
yaml_str = "http:\n api_password: !secret http_pw_keyring"
|
|
||||||
with pytest.raises(HomeAssistantError):
|
|
||||||
load_yaml(self._yaml_path, yaml_str)
|
|
||||||
|
|
||||||
yaml_loader.keyring = FakeKeyring({"http_pw_keyring": "yeah"})
|
|
||||||
_yaml = load_yaml(self._yaml_path, yaml_str)
|
|
||||||
assert {"http": {"api_password": "yeah"}} == _yaml
|
|
||||||
|
|
||||||
@patch.object(yaml_loader, "credstash")
|
|
||||||
def test_secrets_credstash(self, mock_credstash):
|
|
||||||
"""Test credstash fallback & get_password."""
|
|
||||||
mock_credstash.getSecret.return_value = "yeah"
|
|
||||||
yaml_str = "http:\n api_password: !secret http_pw_credstash"
|
|
||||||
_yaml = load_yaml(self._yaml_path, yaml_str)
|
|
||||||
log = logging.getLogger()
|
|
||||||
log.error(_yaml["http"])
|
|
||||||
assert {"api_password": "yeah"} == _yaml["http"]
|
|
||||||
|
|
||||||
def test_secrets_logger_removed(self):
|
def test_secrets_logger_removed(self):
|
||||||
"""Ensure logger: debug was removed."""
|
"""Ensure logger: debug was removed."""
|
||||||
with pytest.raises(HomeAssistantError):
|
with pytest.raises(HomeAssistantError):
|
||||||
|
|
Loading…
Add table
Reference in a new issue