From 7ba74b5167955eeac3f1eb4056d7eebddb329445 Mon Sep 17 00:00:00 2001 From: Jerry Ma Date: Tue, 7 Aug 2018 16:33:15 -0700 Subject: [PATCH] Enable AWS cred file to be read as long as .s3cfg auth fields are nonexistent or empty. This commit allows the credentials to be read from ~/.aws/credentials or user-specified (via envvar) file if .s3cfg exists but does not have authentication options. The previous behavior is that s3cmd will *not* read credentials from the above path as long as .s3cfg exists (even if it contains no authentication options). --- S3/Config.py | 29 ++++++++++++++++++++++------- S3/S3.py | 5 +++-- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/S3/Config.py b/S3/Config.py index 046c1f444..c6eb9af50 100644 --- a/S3/Config.py +++ b/S3/Config.py @@ -24,7 +24,7 @@ import http.client as httplib import locale -try: +try: from configparser import NoOptionError, NoSectionError, MissingSectionHeaderError, ConfigParser as PyConfigParser except ImportError: # Python2 fallback code @@ -217,8 +217,7 @@ def __init__(self, configfile = None, access_key=None, secret_key=None, access_t try: self.read_config_file(configfile) except IOError: - if 'AWS_CREDENTIAL_FILE' in os.environ or 'AWS_PROFILE' in os.environ: - self.aws_credential_file() + pass # override these if passed on the command-line if access_key and secret_key: @@ -229,7 +228,10 @@ def __init__(self, configfile = None, access_key=None, secret_key=None, access_t # Do not refresh the IAM role when an access token is provided. self._access_token_refresh = False - if len(self.access_key)==0: + # The next few clauses are in descending order of priority. + # That is, we will prefer key envvars, then AWS cred file, then IAM auth. + + if not self.is_option_nonempty('access_key'): env_access_key = os.getenv('AWS_ACCESS_KEY') or os.getenv('AWS_ACCESS_KEY_ID') env_secret_key = os.getenv('AWS_SECRET_KEY') or os.getenv('AWS_SECRET_ACCESS_KEY') env_access_token = os.getenv('AWS_SESSION_TOKEN') or os.getenv('AWS_SECURITY_TOKEN') @@ -241,8 +243,15 @@ def __init__(self, configfile = None, access_key=None, secret_key=None, access_t # Do not refresh the IAM role when an access token is provided. self._access_token_refresh = False self.access_token = config_unicodise(env_access_token) - else: - self.role_config() + + if not self.is_option_nonempty('access_key'): + self.aws_credential_file() + + if not self.is_option_nonempty('access_key'): + self.role_config() + + if not self.is_option_nonempty('access_key'): + raise Exception('There is no access key available!') #TODO check KMS key is valid if self.kms_key and self.server_side_encryption == True: @@ -250,6 +259,9 @@ def __init__(self, configfile = None, access_key=None, secret_key=None, access_t if self.kms_key and self.signature_v2 == True: raise Exception('KMS encryption requires signature v4. Please set signature_v2 to False') + def is_option_nonempty(self, option_name): + return hasattr(self, option_name) and bool(str(getattr(self, option_name))) + def role_config(self): """ Get credentials from IAM authentication @@ -273,6 +285,7 @@ def role_config(self): else: raise IOError except: + warning('IAM role config failed') raise def role_refresh(self): @@ -284,9 +297,11 @@ def role_refresh(self): def aws_credential_file(self): try: - aws_credential_file = os.path.expanduser('~/.aws/credentials') + aws_credential_file = os.path.expanduser('~/.aws/credentials') if 'AWS_CREDENTIAL_FILE' in os.environ and os.path.isfile(os.environ['AWS_CREDENTIAL_FILE']): aws_credential_file = config_unicodise(os.environ['AWS_CREDENTIAL_FILE']) + elif not os.path.isfile(aws_credential_file): + return config = PyConfigParser() diff --git a/S3/S3.py b/S3/S3.py index 8e454e4c9..7878557fe 100644 --- a/S3/S3.py +++ b/S3/S3.py @@ -1662,9 +1662,10 @@ def recv_file(self, request, stream, labels, start_position = 0, retries = _max_ debug("Response:\n" + pprint.pformat(response)) except ParameterError as e: raise - except OSError as e: - raise except (IOError, Exception) as e: + if isinstance(e, OSError) and not isinstance(e, ConnectionResetError): + raise + if self.config.progress_meter: progress.done("failed") if ((hasattr(e, 'errno') and e.errno and