summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Houglum <houglum@google.com>2018-09-16 18:10:07 -0700
committerMatt Houglum <houglum@google.com>2018-09-16 19:20:53 -0700
commitaa0f1a5137e0062afc34e347d4a9c88e4f94a507 (patch)
tree0601b09dcef17fa8d542d9f2de85bfad63f93b73
parent8fac1878734c5ac085b781f619c70ea4b6e913c3 (diff)
downloadboto-aa0f1a5137e0062afc34e347d4a9c88e4f94a507.tar.gz
Allows anon auth'n to S3 via a config option.
See https://stackoverflow.com/questions/52308440/fetch-content-in-aws-s3-public-bucket-from-gcp-data-storage/52322861#52322861 for a valid use case for this option. This option was named after the "--no-sign-request" option in the AWS CLI. The additional changes to provider.py and auth.py are to prevent Boto from checking and failing to find credentials on the metadata server (and thus spewing error-level logs to the console), and to allow pickling to work for the AnonAuthHandler and HmacKeys classes.
-rw-r--r--boto/auth.py26
-rw-r--r--boto/connection.py5
-rw-r--r--boto/provider.py9
-rw-r--r--boto/s3/connection.py25
-rw-r--r--tests/unit/s3/test_connection.py35
5 files changed, 81 insertions, 19 deletions
diff --git a/boto/auth.py b/boto/auth.py
index b479d126..4374f73b 100644
--- a/boto/auth.py
+++ b/boto/auth.py
@@ -98,21 +98,26 @@ SIGV4_DETECT = [
class HmacKeys(object):
"""Key based Auth handler helper."""
- def __init__(self, host, config, provider):
+ def __init__(self, host, config, provider, anon=False):
if provider.access_key is None or provider.secret_key is None:
- raise boto.auth_handler.NotReadyToAuthenticate()
+ if not anon:
+ raise boto.auth_handler.NotReadyToAuthenticate()
+ else:
+ self._hmac = None
+ self._hmac_256 = None
self.host = host
self.update_provider(provider)
def update_provider(self, provider):
self._provider = provider
- self._hmac = hmac.new(self._provider.secret_key.encode('utf-8'),
- digestmod=sha)
- if sha256:
- self._hmac_256 = hmac.new(self._provider.secret_key.encode('utf-8'),
- digestmod=sha256)
- else:
- self._hmac_256 = None
+ if self._provider.secret_key: # Anonymous handler has no key.
+ self._hmac = hmac.new(self._provider.secret_key.encode('utf-8'),
+ digestmod=sha)
+ if sha256:
+ self._hmac_256 = hmac.new(
+ self._provider.secret_key.encode('utf-8'), digestmod=sha256)
+ else:
+ self._hmac_256 = None
def algorithm(self):
if self._hmac_256:
@@ -152,7 +157,8 @@ class AnonAuthHandler(AuthHandler, HmacKeys):
capability = ['anon']
def __init__(self, host, config, provider):
- super(AnonAuthHandler, self).__init__(host, config, provider)
+ AuthHandler.__init__(self, host, config, provider)
+ HmacKeys.__init__(self, host, config, provider, anon=True)
def add_auth(self, http_request, **kwargs):
pass
diff --git a/boto/connection.py b/boto/connection.py
index 2fef4487..34b428f1 100644
--- a/boto/connection.py
+++ b/boto/connection.py
@@ -543,6 +543,8 @@ class AWSAuthConnection(object):
self.http_connection_kwargs['timeout'] = config.getint(
'Boto', 'http_socket_timeout', 70)
+ is_anonymous_connection = getattr(self, 'anon', False)
+
if isinstance(provider, Provider):
# Allow overriding Provider
self.provider = provider
@@ -552,7 +554,8 @@ class AWSAuthConnection(object):
aws_access_key_id,
aws_secret_access_key,
security_token,
- profile_name)
+ profile_name,
+ anon=is_anonymous_connection)
# Allow config file to override default host, port, and host header.
if self.provider.host:
diff --git a/boto/provider.py b/boto/provider.py
index e08afa30..494f3f7d 100644
--- a/boto/provider.py
+++ b/boto/provider.py
@@ -179,7 +179,7 @@ class Provider(object):
}
def __init__(self, name, access_key=None, secret_key=None,
- security_token=None, profile_name=None):
+ security_token=None, profile_name=None, anon=False):
self.host = None
self.port = None
self.host_header = None
@@ -187,6 +187,7 @@ class Provider(object):
self.secret_key = secret_key
self.security_token = security_token
self.profile_name = profile_name
+ self.anon = anon
self.name = name
self.acl_class = self.AclClassMap[self.name]
self.canned_acls = self.CannedAclsMap[self.name]
@@ -244,6 +245,9 @@ class Provider(object):
security_token = property(get_security_token, set_security_token)
def _credentials_need_refresh(self):
+ if self.anon:
+ return False
+
if self._credential_expiry_time is None:
return False
else:
@@ -264,6 +268,9 @@ class Provider(object):
def get_credentials(self, access_key=None, secret_key=None,
security_token=None, profile_name=None):
+ if self.anon:
+ return
+
access_key_name, secret_key_name, security_token_name, \
profile_name_name = self.CredentialMap[self.name]
diff --git a/boto/s3/connection.py b/boto/s3/connection.py
index fa3fbd72..f364a6b0 100644
--- a/boto/s3/connection.py
+++ b/boto/s3/connection.py
@@ -171,20 +171,31 @@ class S3Connection(AWSAuthConnection):
host=NoHostProvided, debug=0, https_connection_factory=None,
calling_format=DefaultCallingFormat, path='/',
provider='aws', bucket_class=Bucket, security_token=None,
- suppress_consec_slashes=True, anon=False,
+ suppress_consec_slashes=True, anon=None,
validate_certs=None, profile_name=None):
+ self.bucket_class = bucket_class
+
+ if isinstance(calling_format, six.string_types):
+ calling_format=boto.utils.find_class(calling_format)()
+ self.calling_format = calling_format
+
+ # Fetching config options at init time, instead of using a class-level
+ # default (set at class declaration time) as the default arg value,
+ # allows our tests to ensure that the config file options are
+ # respected.
+ if anon is None:
+ # Only fetch from the config option if a non-default arg value was
+ # provided.
+ anon = boto.config.getbool('s3', 'no_sign_request', False)
+ self.anon = anon
+
no_host_provided = False
- # Try falling back to the boto config file's value, if present.
if host is NoHostProvided:
host = boto.config.get('s3', 'host')
if host is None:
host = self.DefaultHost
no_host_provided = True
- if isinstance(calling_format, six.string_types):
- calling_format=boto.utils.find_class(calling_format)()
- self.calling_format = calling_format
- self.bucket_class = bucket_class
- self.anon = anon
+
super(S3Connection, self).__init__(host,
aws_access_key_id, aws_secret_access_key,
is_secure, port, proxy, proxy_port, proxy_user, proxy_pass,
diff --git a/tests/unit/s3/test_connection.py b/tests/unit/s3/test_connection.py
index 05c561b4..8fd33a0a 100644
--- a/tests/unit/s3/test_connection.py
+++ b/tests/unit/s3/test_connection.py
@@ -59,6 +59,41 @@ class TestAnon(MockServiceWithConfigTestCase):
url = conn.generate_url(0, 'GET', bucket='examplebucket', key='test.txt')
self.assertNotIn('Signature=', url)
+ def test_anon_default_taken_from_config_opt(self):
+ self.config = {
+ 's3': {
+ # Value must be a string for `config.getbool` to not crash.
+ 'no_sign_request': 'True',
+ }
+ }
+
+ conn = self.connection_class(
+ aws_access_key_id='less',
+ aws_secret_access_key='more',
+ host='s3.amazonaws.com',
+ )
+ url = conn.generate_url(
+ 0, 'GET', bucket='examplebucket', key='test.txt')
+ self.assertNotIn('Signature=', url)
+
+ def test_explicit_anon_arg_overrides_config_value(self):
+ self.config = {
+ 's3': {
+ # Value must be a string for `config.getbool` to not crash.
+ 'no_sign_request': 'True',
+ }
+ }
+
+ conn = self.connection_class(
+ aws_access_key_id='less',
+ aws_secret_access_key='more',
+ host='s3.amazonaws.com',
+ anon=False
+ )
+ url = conn.generate_url(
+ 0, 'GET', bucket='examplebucket', key='test.txt')
+ self.assertIn('Signature=', url)
+
class TestPresigned(MockServiceWithConfigTestCase):
connection_class = S3Connection