Remove deprecated credstash + keyring (#47033)

This commit is contained in:
Paulus Schoutsen 2021-02-25 00:48:19 -08:00 committed by GitHub
parent 72263abfa9
commit 633a7aeb22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 4 additions and 262 deletions

View file

@ -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"])

View file

@ -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}")

View file

@ -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")

View file

@ -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",

View file

@ -1,4 +1,2 @@
"""Constants.""" """Constants."""
SECRET_YAML = "secrets.yaml" SECRET_YAML = "secrets.yaml"
_SECRET_NAMESPACE = "homeassistant"

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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/"

View file

@ -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):