Skip to content

Commit eca5f98

Browse files
authored
Merge pull request boto#3833 from houglum/add_anon_s3_conn_config_option
Allows anon auth'n to S3 via a config option.
2 parents 8fac187 + aa0f1a5 commit eca5f98

File tree

5 files changed

+81
-19
lines changed

5 files changed

+81
-19
lines changed

boto/auth.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,21 +98,26 @@
9898
class HmacKeys(object):
9999
"""Key based Auth handler helper."""
100100

101-
def __init__(self, host, config, provider):
101+
def __init__(self, host, config, provider, anon=False):
102102
if provider.access_key is None or provider.secret_key is None:
103-
raise boto.auth_handler.NotReadyToAuthenticate()
103+
if not anon:
104+
raise boto.auth_handler.NotReadyToAuthenticate()
105+
else:
106+
self._hmac = None
107+
self._hmac_256 = None
104108
self.host = host
105109
self.update_provider(provider)
106110

107111
def update_provider(self, provider):
108112
self._provider = provider
109-
self._hmac = hmac.new(self._provider.secret_key.encode('utf-8'),
110-
digestmod=sha)
111-
if sha256:
112-
self._hmac_256 = hmac.new(self._provider.secret_key.encode('utf-8'),
113-
digestmod=sha256)
114-
else:
115-
self._hmac_256 = None
113+
if self._provider.secret_key: # Anonymous handler has no key.
114+
self._hmac = hmac.new(self._provider.secret_key.encode('utf-8'),
115+
digestmod=sha)
116+
if sha256:
117+
self._hmac_256 = hmac.new(
118+
self._provider.secret_key.encode('utf-8'), digestmod=sha256)
119+
else:
120+
self._hmac_256 = None
116121

117122
def algorithm(self):
118123
if self._hmac_256:
@@ -152,7 +157,8 @@ class AnonAuthHandler(AuthHandler, HmacKeys):
152157
capability = ['anon']
153158

154159
def __init__(self, host, config, provider):
155-
super(AnonAuthHandler, self).__init__(host, config, provider)
160+
AuthHandler.__init__(self, host, config, provider)
161+
HmacKeys.__init__(self, host, config, provider, anon=True)
156162

157163
def add_auth(self, http_request, **kwargs):
158164
pass

boto/connection.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@ def __init__(self, host, aws_access_key_id=None,
543543
self.http_connection_kwargs['timeout'] = config.getint(
544544
'Boto', 'http_socket_timeout', 70)
545545

546+
is_anonymous_connection = getattr(self, 'anon', False)
547+
546548
if isinstance(provider, Provider):
547549
# Allow overriding Provider
548550
self.provider = provider
@@ -552,7 +554,8 @@ def __init__(self, host, aws_access_key_id=None,
552554
aws_access_key_id,
553555
aws_secret_access_key,
554556
security_token,
555-
profile_name)
557+
profile_name,
558+
anon=is_anonymous_connection)
556559

557560
# Allow config file to override default host, port, and host header.
558561
if self.provider.host:

boto/provider.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,15 @@ class Provider(object):
179179
}
180180

181181
def __init__(self, name, access_key=None, secret_key=None,
182-
security_token=None, profile_name=None):
182+
security_token=None, profile_name=None, anon=False):
183183
self.host = None
184184
self.port = None
185185
self.host_header = None
186186
self.access_key = access_key
187187
self.secret_key = secret_key
188188
self.security_token = security_token
189189
self.profile_name = profile_name
190+
self.anon = anon
190191
self.name = name
191192
self.acl_class = self.AclClassMap[self.name]
192193
self.canned_acls = self.CannedAclsMap[self.name]
@@ -244,6 +245,9 @@ def set_security_token(self, value):
244245
security_token = property(get_security_token, set_security_token)
245246

246247
def _credentials_need_refresh(self):
248+
if self.anon:
249+
return False
250+
247251
if self._credential_expiry_time is None:
248252
return False
249253
else:
@@ -264,6 +268,9 @@ def _credentials_need_refresh(self):
264268

265269
def get_credentials(self, access_key=None, secret_key=None,
266270
security_token=None, profile_name=None):
271+
if self.anon:
272+
return
273+
267274
access_key_name, secret_key_name, security_token_name, \
268275
profile_name_name = self.CredentialMap[self.name]
269276

boto/s3/connection.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,20 +171,31 @@ def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
171171
host=NoHostProvided, debug=0, https_connection_factory=None,
172172
calling_format=DefaultCallingFormat, path='/',
173173
provider='aws', bucket_class=Bucket, security_token=None,
174-
suppress_consec_slashes=True, anon=False,
174+
suppress_consec_slashes=True, anon=None,
175175
validate_certs=None, profile_name=None):
176+
self.bucket_class = bucket_class
177+
178+
if isinstance(calling_format, six.string_types):
179+
calling_format=boto.utils.find_class(calling_format)()
180+
self.calling_format = calling_format
181+
182+
# Fetching config options at init time, instead of using a class-level
183+
# default (set at class declaration time) as the default arg value,
184+
# allows our tests to ensure that the config file options are
185+
# respected.
186+
if anon is None:
187+
# Only fetch from the config option if a non-default arg value was
188+
# provided.
189+
anon = boto.config.getbool('s3', 'no_sign_request', False)
190+
self.anon = anon
191+
176192
no_host_provided = False
177-
# Try falling back to the boto config file's value, if present.
178193
if host is NoHostProvided:
179194
host = boto.config.get('s3', 'host')
180195
if host is None:
181196
host = self.DefaultHost
182197
no_host_provided = True
183-
if isinstance(calling_format, six.string_types):
184-
calling_format=boto.utils.find_class(calling_format)()
185-
self.calling_format = calling_format
186-
self.bucket_class = bucket_class
187-
self.anon = anon
198+
188199
super(S3Connection, self).__init__(host,
189200
aws_access_key_id, aws_secret_access_key,
190201
is_secure, port, proxy, proxy_port, proxy_user, proxy_pass,

tests/unit/s3/test_connection.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,41 @@ def test_generate_url(self):
5959
url = conn.generate_url(0, 'GET', bucket='examplebucket', key='test.txt')
6060
self.assertNotIn('Signature=', url)
6161

62+
def test_anon_default_taken_from_config_opt(self):
63+
self.config = {
64+
's3': {
65+
# Value must be a string for `config.getbool` to not crash.
66+
'no_sign_request': 'True',
67+
}
68+
}
69+
70+
conn = self.connection_class(
71+
aws_access_key_id='less',
72+
aws_secret_access_key='more',
73+
host='s3.amazonaws.com',
74+
)
75+
url = conn.generate_url(
76+
0, 'GET', bucket='examplebucket', key='test.txt')
77+
self.assertNotIn('Signature=', url)
78+
79+
def test_explicit_anon_arg_overrides_config_value(self):
80+
self.config = {
81+
's3': {
82+
# Value must be a string for `config.getbool` to not crash.
83+
'no_sign_request': 'True',
84+
}
85+
}
86+
87+
conn = self.connection_class(
88+
aws_access_key_id='less',
89+
aws_secret_access_key='more',
90+
host='s3.amazonaws.com',
91+
anon=False
92+
)
93+
url = conn.generate_url(
94+
0, 'GET', bucket='examplebucket', key='test.txt')
95+
self.assertIn('Signature=', url)
96+
6297

6398
class TestPresigned(MockServiceWithConfigTestCase):
6499
connection_class = S3Connection

0 commit comments

Comments
 (0)