Secrets¶
behave-data resolves sensitive values from different backends without hardcoding them in your feature files.
Placeholder syntax¶
env:VAR_NAME— read environment variablefile:path.txt— read from filesecret:name— resolve from configured secret backendplain text — returned as-is
Environment variables¶
Scenario: Use API token
Given I call the API with token "env:API_TOKEN"
from behave_data import DataManager
dm = DataManager()
token = dm.resolve("env:API_TOKEN")
Files¶
dm = DataManager(config=Config(secret_path="secrets/"))
token = dm.resolve("file:api_token.txt")
secret_path is prepended to relative file paths.
Secret backend¶
Configure the backend in behave_data.yml:
secret_backend: env
Or in code:
from behave_data import Config, DataManager
dm = DataManager(Config(secret_backend="env"))
Backends¶
Backend |
Source |
Extra |
|---|---|---|
|
Read from |
— |
|
Read from environment variables |
— |
|
HashiCorp Vault KV v2 |
|
|
AWS Secrets Manager |
|
HashiCorp Vault¶
from behave_data import Config, DataManager
dm = DataManager(Config(secret_backend="vault"))
value = dm.resolve("secret:my-secret")
Requires VAULT_ADDR and VAULT_TOKEN environment variables.
AWS Secrets Manager¶
dm = DataManager(Config(secret_backend="aws"))
value = dm.resolve("secret:my-secret")
Uses boto3 default credentials.
Masking secrets¶
Values resolved via secret: are automatically masked by DataManager.mask():
value = dm.resolve("secret:API_TOKEN") # "abc123"
masked = dm.mask(value) # "***"
This is useful when printing or logging.
Using in tables¶
| name:str | token:str |
| Alice | secret:TOKEN_A |
| Bob | secret:TOKEN_B |
users = typed_wrap(context.table).typed_dicts()
for user in users:
token = context.data.resolve(user["token"])
print(context.data.mask(token)) # ***
Reference to fixture¶
value = dm.resolve("ref:admin_user")
# Resolves the admin_user fixture
Passthrough for non-strings¶
Integers, lists, and other non-string values are returned unchanged:
dm.resolve(42) # 42
dm.resolve([1, 2, 3]) # [1, 2, 3]