Decrypt KMS objects in configuration.

    Args:
        cfg (dict): configuration dictionary
        aws_config (dict): aws credentials
            dict of arguments passed into boto3 session
            example:
                aws_creds = {'aws_access_key_id': aws_access_key_id,
                             'aws_secret_access_key': aws_secret_access_key,
                             'region_name': 'us-east-1'}

    Returns:
        dict: decrypted configuration dictionary

    AWS credentials follow the standard boto flow. Provided values first,
    followed by environment, and then configuration files on the machine.
    Ideally, one would set up an IAM role for this machine to authenticate.

    The aim is to find in the dictionary items which have been encrypted
    with KMS, then decrypt them if possible.

    A user can create a key "_kms" in which to store the data. All data
    at this level will be replaced with the decrypted contents. For example:

        {'component': {'key': {'_kms': 'encrypted cipher text', 'nothing': 'should go here'}}}

    will transform to:

        {'component': {'key': 'decrypted value'}}

    To get the value to be stored as a KMS encrypted string:

        from figgypy.utils import kms_encrypt
        encrypted = kms_encrypt('your secret', 'your key or alias', optional_aws_config)

def kms_decrypt(cfg, aws_config=None):
    """Decrypt KMS objects in configuration.

    Args:
        cfg (dict): configuration dictionary
        aws_config (dict): aws credentials
            dict of arguments passed into boto3 session
            example:
                aws_creds = {'aws_access_key_id': aws_access_key_id,
                             'aws_secret_access_key': aws_secret_access_key,
                             'region_name': 'us-east-1'}

    Returns:
        dict: decrypted configuration dictionary

    AWS credentials follow the standard boto flow. Provided values first,
    followed by environment, and then configuration files on the machine.
    Ideally, one would set up an IAM role for this machine to authenticate.

    The aim is to find in the dictionary items which have been encrypted
    with KMS, then decrypt them if possible.

    A user can create a key "_kms" in which to store the data. All data
    at this level will be replaced with the decrypted contents. For example:

        {'component': {'key': {'_kms': 'encrypted cipher text', 'nothing': 'should go here'}}}

    will transform to:

        {'component': {'key': 'decrypted value'}}

    To get the value to be stored as a KMS encrypted string:

        from figgypy.utils import kms_encrypt
        encrypted = kms_encrypt('your secret', 'your key or alias', optional_aws_config)
    """
    def decrypt(obj):
        """Decrypt the object.

        It is an inner function because we must first configure our KMS
        client. Then we call this recursively on the object.
        """
        if isinstance(obj, list):
            res_v = []
            for item in obj:
                res_v.append(decrypt(item))
            return res_v
        elif isinstance(obj, dict):
            if '_kms' in obj:
                try:
                    res = client.decrypt(CiphertextBlob=b64decode(obj['_kms']))
                    obj = n(res['Plaintext'])
                except ClientError as err:
                    if 'AccessDeniedException' in err.args[0]:
                        log.warning('Unable to decrypt %s. Key does not exist or no access', obj['_kms'])
                    else:
                        raise
            else:
                for k, v in obj.items():
                    obj[k] = decrypt(v)
        else:
            pass
        return obj
    try:
        aws = boto3.session.Session(**aws_config)
        client = aws.client('kms')
    except NoRegionError:
        log.info('Missing or invalid aws configuration. Will not be able to unpack KMS secrets.')
        return cfg
    return decrypt(cfg)