1
0
mirror of synced 2026-01-01 09:02:59 -05:00
Files
airbyte/airbyte-cdk/python/airbyte_cdk/utils/airbyte_secrets_utils.py
Serhii Chvaliuk 4e6cb05759 CDK: Improve filter_secrets skip empty string (#15684)
* Improve `filter_secrets` skip empty string

Signed-off-by: Sergey Chvalyuk <grubberr@gmail.com>
2022-08-16 19:28:57 +03:00

73 lines
2.8 KiB
Python

#
# Copyright (c) 2022 Airbyte, Inc., all rights reserved.
#
from typing import Any, List, Mapping
import dpath.util
def get_secret_paths(spec: Mapping[str, Any]) -> List[List[str]]:
paths = []
def traverse_schema(schema_item: Any, path: List[str]):
"""
schema_item can be any property or value in the originally input jsonschema, depending on how far down the recursion stack we go
path is the path to that schema item in the original input
for example if we have the input {'password': {'type': 'string', 'airbyte_secret': True}} then the arguments will evolve
as follows:
schema_item=<whole_object>, path=[]
schema_item={'type': 'string', 'airbyte_secret': True}, path=['password']
schema_item='string', path=['password', 'type']
schema_item=True, path=['password', 'airbyte_secret']
"""
if isinstance(schema_item, dict):
for k, v in schema_item.items():
traverse_schema(v, [*path, k])
elif isinstance(schema_item, list):
for i in schema_item:
traverse_schema(i, path)
else:
if path[-1] == "airbyte_secret" and schema_item is True:
filtered_path = [p for p in path[:-1] if p not in ["properties", "oneOf"]]
paths.append(filtered_path)
traverse_schema(spec, [])
return paths
def get_secrets(connection_specification: Mapping[str, Any], config: Mapping[str, Any]) -> List[Any]:
"""
Get a list of secret values from the source config based on the source specification
:type connection_specification: the connection_specification field of an AirbyteSpecification i.e the JSONSchema definition
"""
secret_paths = get_secret_paths(connection_specification.get("properties", {}))
result = []
for path in secret_paths:
try:
result.append(dpath.util.get(config, path))
except KeyError:
# Since we try to get paths to all known secrets in the spec, in the case of oneOfs, some secret fields may not be present
# In that case, a KeyError is thrown. This is expected behavior.
pass
return result
__SECRETS_FROM_CONFIG: List[str] = []
def update_secrets(secrets: List[str]):
"""Update the list of secrets to be replaced"""
global __SECRETS_FROM_CONFIG
__SECRETS_FROM_CONFIG = secrets
def filter_secrets(string: str) -> str:
"""Filter secrets from a string by replacing them with ****"""
# TODO this should perform a maximal match for each secret. if "x" and "xk" are both secret values, and this method is called twice on
# the input "xk", then depending on call order it might only obfuscate "*k". This is a bug.
for secret in __SECRETS_FROM_CONFIG:
if secret:
string = string.replace(str(secret), "****")
return string