Skip to content

Commit

Permalink
Merge pull request #31 from candidco/feature/none-for-key-not-found
Browse files Browse the repository at this point in the history
Export env variables and return None for inexistent key
  • Loading branch information
tacrocha authored Feb 8, 2023
2 parents 698341e + ff484fb commit 8d8a457
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 5 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ DATABASES = {
}
```

If `export_env_variables` is set to `True`, each secret will also be exported as an environment variable, with the uppercase key as the variable name, e.g.:

```python
from confidential import SecretsManager
import os

secrets = SecretManager(
secrets_file=".secrets/production.json",
secrets_file_default=".secrets/defaults.json", # Overridable defaults you can use in common environments
region_name="us-east-1",
export_env_variables=True, # Optionally, export secrets as environment variables. Default is False.
)

# If the key of a secret is `api_key`, then the following is true:
assert secrets["api_key"] == os.environ.get("API_KEY")
```

Trying to access an inexisting key returns `None`. On previous versions, it would throw an exception.

# Testing

First, install all dependencies:
Expand Down
14 changes: 10 additions & 4 deletions confidential/secrets_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@
import logging
import os
import pprint
from botocore.exceptions import ClientError

from confidential.secrets_manager_decrypter import SecretsManagerDecrypter
from confidential.parameter_store_decrypter import ParameterStoreDecrypter
from confidential.exceptions import PermissionError
from confidential.utils import merge

log = logging.getLogger(__name__)


class SecretsManager:
def __init__(self, secrets_file=None, secrets_file_default=None, region_name=None, profile_name=None):
def __init__(self, secrets_file=None, secrets_file_default=None, region_name=None, profile_name=None, export_env_variables=False):
session = boto3.session.Session(profile_name=profile_name)

self.session = session
Expand All @@ -29,13 +27,16 @@ def __init__(self, secrets_file=None, secrets_file_default=None, region_name=Non

self.secrets = merge(secrets_defaults, secrets)

if export_env_variables:
self.export_env_variables(self.secrets)

def __getitem__(self, key):
"""
Allows us to do <SecretsManager>["foo"] instead of <SecretsManager>.secrets.get("foo")
"""
value = self.secrets.get(key)
if value is None:
raise Exception(f"Value for '{key}' was not found in the secrets file", self.secrets)
log.warning(f"Value for '{key}' was not found in the secrets file. Returning 'None'.")
return value

@staticmethod
Expand Down Expand Up @@ -94,6 +95,11 @@ def parse_secrets_file(self, path_to_file) -> dict:

return config

def export_env_variables(self, secrets):
for key in secrets:
uppercase_key = key.upper()
os.environ[uppercase_key] = str(secrets[key])


@click.command()
@click.argument("secrets_file", type=click.Path(exists=True))
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "confidential"
version = "2.4.0"
version = "2.5.0"
description = "Manage secrets in your projects using AWS Secrets Manager"
authors = [
"Daniel van Flymen <[email protected]>",
Expand Down
13 changes: 13 additions & 0 deletions tests/test_secrets_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
import os

from confidential import SecretsManager
from confidential.exceptions import PermissionError
Expand All @@ -25,6 +26,18 @@ def test_happy_path(secrets_file):
assert secrets["nested_object_parameter"] == {"ping": "pong"}
assert secrets["nested_parameter_key"] == {"temp_c": 3, "snow_fall_cm": 20, "some_parameter": "cold" }

def test_secrets_exported_to_env_vars(secrets_file):
with secrets_file(foo="bar", ping="pong") as f:
secrets = SecretsManager(f, region_name="us-west-1", export_env_variables=True)

assert os.environ.get("FOO") == "bar"
assert secrets["foo"] == os.environ.get("FOO")

assert os.environ.get("PING") == "pong"
assert secrets["ping"] == os.environ.get("PING")

assert os.environ.get("blah") == None
assert secrets["blah"] == os.environ.get("BLAH")

@pytest.mark.parametrize("secret_value_response", [{"FakeKey": "FakeValue"}, {"SecretString": None}])
def test_missing_secret_string_raises_permission_error(secret_value_response, mocker, secrets_file):
Expand Down

0 comments on commit 8d8a457

Please sign in to comment.