Skip to content

Commit c25d08b

Browse files
authored
Merge pull request #95 from michalc/feat/allow-buckets-without-auth
feat: allow public buckets, i.e. without auth
2 parents 6a027ea + 5066889 commit c25d08b

File tree

3 files changed

+89
-8
lines changed

3 files changed

+89
-8
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,26 @@ with \
175175
print(row)
176176
```
177177

178+
179+
### Public Buckets
180+
181+
For public buckets where credentials should not be passed, pass `None` as the `get_credentials` parameter.
182+
183+
```python
184+
query_my_db = partial(sqlite_s3_query,
185+
url='https://my-public-bucket.s3.eu-west-2.amazonaws.com/my-db.sqlite',
186+
get_credentials=None,
187+
)
188+
189+
with \
190+
query_my_db() as query, \
191+
query('SELECT * FROM my_table_2 WHERE my_col = ?', params=('my-value',)) as (columns, rows):
192+
193+
for row in rows:
194+
print(row)
195+
```
196+
197+
178198
### HTTP Client
179199

180200
The HTTP client can be changed by overriding the the default `get_http_client` parameter, which is shown below.

sqlite_s3_query.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,23 @@ def sqlite_s3_query_multi(url, get_credentials=lambda now: (
7474
local = threading.local()
7575
local.pending_exception = None
7676

77+
def get_request_headers_for_private_buckets(method, params, headers, now):
78+
region, access_key_id, secret_access_key, session_token = get_credentials(now)
79+
to_auth_headers = headers + (
80+
(('x-amz-security-token', session_token),) if session_token is not None else \
81+
()
82+
)
83+
return aws_sigv4_headers(
84+
now, access_key_id, secret_access_key, region, method, to_auth_headers, params,
85+
)
86+
87+
def get_request_headers_for_public_buckets(_, __, headers, ___):
88+
return headers
89+
90+
get_request_headers = \
91+
get_request_headers_for_private_buckets if get_credentials is not None else \
92+
get_request_headers_for_public_buckets
93+
7794
def set_pending_exception(exception):
7895
local.pending_exception = exception
7996

@@ -98,14 +115,7 @@ def run_with_db(db, func, *args):
98115
@contextmanager
99116
def make_auth_request(http_client, method, params, headers):
100117
now = datetime.utcnow()
101-
region, access_key_id, secret_access_key, session_token = get_credentials(now)
102-
to_auth_headers = headers + (
103-
(('x-amz-security-token', session_token),) if session_token is not None else \
104-
()
105-
)
106-
request_headers = aws_sigv4_headers(
107-
now, access_key_id, secret_access_key, region, method, to_auth_headers, params,
108-
)
118+
request_headers = get_request_headers(method, params, headers, now)
109119
url = f'{scheme}://{netloc}{path}'
110120
with http_client.stream(method, url, params=params, headers=request_headers) as response:
111121
response.raise_for_status()

test.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,27 @@ def test_select_with_named_params(self):
148148

149149
self.assertEqual(rows, [(500,)])
150150

151+
def test_select_with_named_params_public_bucket(self):
152+
create_bucket('my-public-bucket')
153+
disable_auth('my-public-bucket')
154+
with get_db([
155+
("CREATE TABLE my_table (my_col_a text, my_col_b text);", ())
156+
] + [
157+
("INSERT INTO my_table VALUES " + ','.join(["('some-text-a', 'some-text-b')"] * 500), ()),
158+
("INSERT INTO my_table VALUES " + ','.join(["('some-text-c', 'some-text-d')"] * 100), ()),
159+
]) as db:
160+
put_object_with_versioning('my-public-bucket', 'my.db', db)
161+
162+
with sqlite_s3_query(
163+
'http://localhost:9000/my-public-bucket/my.db',
164+
get_credentials=None,
165+
get_libsqlite3=get_libsqlite3
166+
) as query:
167+
with query('SELECT COUNT(*) FROM my_table WHERE my_col_a = :first', named_params=((':first', 'some-text-a'),)) as (columns, rows):
168+
rows = list(rows)
169+
170+
self.assertEqual(rows, [(500,)])
171+
151172
def test_select_large(self):
152173
empty = (bytes(4050),)
153174

@@ -840,6 +861,36 @@ def enable_versioning(bucket):
840861
response = httpx.put(url, content=content, headers=headers)
841862
response.raise_for_status()
842863

864+
def disable_auth(bucket):
865+
content = f'''
866+
{{
867+
"Version": "2012-10-17",
868+
"Statement": [
869+
{{
870+
"Sid": "Stmt1405592139000",
871+
"Effect": "Allow",
872+
"Principal": "*",
873+
"Action": [
874+
"s3:GetObject",
875+
"s3:GetObjectVersion"
876+
],
877+
"Resource": [
878+
"arn:aws:s3:::{bucket}/*"
879+
]
880+
}}
881+
]
882+
}}
883+
'''.encode()
884+
url = f'http://127.0.0.1:9000/{bucket}/?policy'
885+
body_hash = hashlib.sha256(content).hexdigest()
886+
parsed_url = urllib.parse.urlsplit(url)
887+
888+
headers = aws_sigv4_headers(
889+
'AKIAIOSFODNN7EXAMPLE', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
890+
(), 's3', 'us-east-1', parsed_url.netloc, 'PUT', parsed_url.path, (('policy', ''),), body_hash,
891+
)
892+
response = httpx.put(url, content=content, headers=headers)
893+
response.raise_for_status()
843894

844895
def aws_sigv4_headers(access_key_id, secret_access_key, pre_auth_headers,
845896
service, region, host, method, path, params, body_hash):

0 commit comments

Comments
 (0)