136 lines
4.7 KiB
Python
136 lines
4.7 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2021 Google LLC. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Utils for handing transfer credentials."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import json
|
|
import os
|
|
|
|
import boto3
|
|
from googlecloudsdk.core.resource import resource_property
|
|
from googlecloudsdk.core.util import files
|
|
|
|
from six.moves import configparser
|
|
|
|
|
|
def _assign_with_error_on_duplicate(key, value, result_dict):
|
|
"""Assigns value to results_dict and raises error on duplicate key."""
|
|
if key in result_dict:
|
|
raise KeyError('Duplicate key in file: {}'.format(key))
|
|
result_dict[key] = value
|
|
|
|
|
|
def _extract_keys(keys, search_dict, result_dict):
|
|
"""Converts key to multiple cases and attempts to extract from search_dict."""
|
|
for original_key in keys:
|
|
if original_key in search_dict:
|
|
_assign_with_error_on_duplicate(original_key, search_dict[original_key],
|
|
result_dict)
|
|
else:
|
|
# Can error if both camel and snake case matches are present.
|
|
# Note: The below conversion utils don't work all the time.
|
|
# For example, they cannot handle kebab-case.
|
|
camel_case_key = resource_property.ConvertToCamelCase(original_key)
|
|
snake_case_key = resource_property.ConvertToSnakeCase(original_key)
|
|
if camel_case_key in search_dict:
|
|
_assign_with_error_on_duplicate(original_key,
|
|
search_dict[camel_case_key],
|
|
result_dict)
|
|
if snake_case_key in search_dict:
|
|
_assign_with_error_on_duplicate(original_key,
|
|
search_dict[snake_case_key],
|
|
result_dict)
|
|
|
|
|
|
def get_values_for_keys_from_file(file_path, keys):
|
|
"""Reads JSON or INI file and returns dict with values for requested keys.
|
|
|
|
JSON file keys should be top level.
|
|
INI file sections will be flattened.
|
|
|
|
Args:
|
|
file_path (str): Path of JSON or INI file to read.
|
|
keys (list[str]): Search for these keys to return from file.
|
|
|
|
Returns:
|
|
Dict[cred_key: cred_value].
|
|
|
|
Raises:
|
|
ValueError: The file was the incorrect format.
|
|
KeyError: Duplicate key found.
|
|
"""
|
|
result = {}
|
|
real_path = os.path.realpath(os.path.expanduser(file_path))
|
|
with files.FileReader(real_path) as file_reader:
|
|
try:
|
|
file_dict = json.loads(file_reader.read())
|
|
_extract_keys(keys, file_dict, result)
|
|
except json.JSONDecodeError:
|
|
# More file formats to try before raising error.
|
|
config = configparser.ConfigParser()
|
|
try:
|
|
config.read(real_path)
|
|
except configparser.ParsingError:
|
|
raise ValueError('Source creds file must be JSON or INI format.')
|
|
# Parse all sections of INI file into dict.
|
|
for section in config:
|
|
section_dict = dict(config[section])
|
|
_extract_keys(keys, section_dict, result)
|
|
|
|
return result
|
|
|
|
|
|
def get_aws_creds_from_file(file_path):
|
|
"""Scans file for AWS credentials keys.
|
|
|
|
Key fields prefixed with "aws" take precedence.
|
|
|
|
Args:
|
|
file_path (str): Path to creds file.
|
|
|
|
Returns:
|
|
Tuple of (access_key_id, secret_access_key).
|
|
Each tuple entry can be a string or None.
|
|
"""
|
|
creds_dict = get_values_for_keys_from_file(file_path, [
|
|
'aws_access_key_id', 'aws_secret_access_key', 'access_key_id',
|
|
'secret_access_key', 'role_arn'
|
|
])
|
|
access_key_id = creds_dict.get('aws_access_key_id',
|
|
creds_dict.get('access_key_id', None))
|
|
secret_access_key = creds_dict.get('aws_secret_access_key',
|
|
creds_dict.get('secret_access_key', None))
|
|
role_arn = creds_dict.get('role_arn', None)
|
|
return access_key_id, secret_access_key, role_arn
|
|
|
|
|
|
def get_default_aws_creds():
|
|
"""Returns creds from common AWS config file paths.
|
|
|
|
Currently does not return "role_arn" because there is no way to extract
|
|
this data from a boto3 Session object.
|
|
|
|
Returns:
|
|
Tuple of (access_key_id, secret_access_key, role_arn).
|
|
Each tuple entry can be a string or None.
|
|
"""
|
|
credentials = boto3.session.Session().get_credentials()
|
|
if credentials:
|
|
return credentials.access_key, credentials.secret_key
|
|
return None, None
|