1
0
mirror of synced 2025-12-26 14:02:10 -05:00
Files
airbyte/airbyte-cdk/python/airbyte_cdk/utils/airbyte_secrets_utils.py
Cole Snodgrass 2e099acc52 update headers from 2022 -> 2023 (#22594)
* It's 2023!

* 2022 -> 2023

---------

Co-authored-by: evantahler <evan@airbyte.io>
2023-02-08 13:01:16 -08:00

73 lines
2.8 KiB
Python

#
# Copyright (c) 2023 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