Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/functional/s3/test_presign_command.py
1567 views
1
# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
import datetime
14
15
from botocore.compat import urlsplit
16
from awscli.testutils import BaseAWSCommandParamsTest, mock, temporary_file
17
from awscli.testutils import create_clidriver
18
19
20
# Values used to fix time.time() and datetime.datetime.now()
21
# so we know the exact values of the signatures generated.
22
FROZEN_TIMESTAMP = 1471305652
23
DEFAULT_EXPIRES = 3600
24
FROZEN_TIME = mock.Mock(return_value=FROZEN_TIMESTAMP)
25
FROZEN_DATETIME = mock.Mock(
26
return_value=datetime.datetime(2016, 8, 18, 14, 33, 3, 0))
27
28
29
class TestPresignCommand(BaseAWSCommandParamsTest):
30
31
prefix = 's3 presign '
32
33
def enable_addressing_mode_in_config(self, fileobj, mode):
34
fileobj.write(
35
"[default]\n"
36
"s3 =\n"
37
" addressing_style = %s\n" % mode
38
)
39
fileobj.flush()
40
self.environ['AWS_CONFIG_FILE'] = fileobj.name
41
self.driver = create_clidriver()
42
43
def enable_sigv4_from_config_file(self, fileobj):
44
fileobj.write(
45
"[default]\n"
46
"s3 =\n"
47
" signature_version = s3v4\n"
48
)
49
fileobj.flush()
50
self.environ['AWS_CONFIG_FILE'] = fileobj.name
51
self.driver = create_clidriver()
52
53
def assert_presigned_url_matches(self, actual_url, expected_match):
54
"""Verify generated presigned URL matches expected dict.
55
56
This method compares an actual URL against a dict of expected
57
values. The reason that the "expected_match" is a dict instead
58
of the expected presigned URL is because the query params
59
are unordered so we can't guarantee an expected query param
60
ordering.
61
62
"""
63
parts = urlsplit(actual_url)
64
self.assertEqual(parts.netloc, expected_match['hostname'])
65
self.assertEqual(parts.path, expected_match['path'])
66
query_params = self.parse_query_string(parts.query)
67
self.assertEqual(query_params, expected_match['query_params'])
68
69
def parse_query_string(self, query_string):
70
pairs = []
71
for part in query_string.split('&'):
72
pairs.append(part.split('=', 1))
73
return dict(pairs)
74
75
def get_presigned_url_for_cmd(self, cmdline):
76
with mock.patch('time.time', FROZEN_TIME):
77
with mock.patch('datetime.datetime') as d:
78
d.now = FROZEN_DATETIME
79
stdout = self.assert_params_for_cmd(cmdline, None)[0].strip()
80
return stdout
81
82
def test_generates_a_url(self):
83
stdout = self.get_presigned_url_for_cmd(
84
self.prefix + 's3://bucket/key')
85
86
self.assert_presigned_url_matches(
87
stdout, {
88
'hostname': 'bucket.s3.amazonaws.com',
89
'path': '/key',
90
'query_params': {
91
'AWSAccessKeyId': 'access_key',
92
'Expires': str(FROZEN_TIMESTAMP + DEFAULT_EXPIRES),
93
'Signature': '2m9M0eLB%2BqI0nUpkyTskKmHd0Ig%3D',
94
}
95
}
96
)
97
98
def test_handles_non_dns_compatible_buckets(self):
99
stdout = self.get_presigned_url_for_cmd(
100
self.prefix + 's3://bucket.dots/key')
101
102
self.assert_presigned_url_matches(
103
stdout, {
104
'hostname': 's3.amazonaws.com',
105
'path': '/bucket.dots/key',
106
'query_params': {
107
'AWSAccessKeyId': 'access_key',
108
'Expires': str(FROZEN_TIMESTAMP + DEFAULT_EXPIRES),
109
'Signature': '0IiC2vxub438EVcKfEFEMHuoHRw%3D',
110
}
111
}
112
)
113
114
def test_handles_expires_in(self):
115
expires_in = 1000
116
stdout = self.get_presigned_url_for_cmd(
117
self.prefix + 's3://bucket/key --expires-in %s' % expires_in)
118
119
self.assert_presigned_url_matches(
120
stdout, {
121
'hostname': 'bucket.s3.amazonaws.com',
122
'path': '/key',
123
'query_params': {
124
'AWSAccessKeyId': 'access_key',
125
'Expires': str(FROZEN_TIMESTAMP + expires_in),
126
'Signature': 'WZEMcfBNlzfTZBq3bOvYef1cfoU%3D',
127
}
128
}
129
)
130
131
def test_handles_sigv4(self):
132
with temporary_file('w') as f:
133
self.enable_sigv4_from_config_file(f)
134
stdout = self.get_presigned_url_for_cmd(
135
self.prefix + 's3://bucket/key')
136
137
expected = {
138
'hostname': 'bucket.s3.amazonaws.com',
139
'path': '/key',
140
'query_params': {
141
'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
142
'X-Amz-Credential': (
143
'access_key%2F20160818%2Fus-east-1'
144
'%2Fs3%2Faws4_request'),
145
'X-Amz-Date': '20160818T143303Z',
146
'X-Amz-Expires': '3600',
147
'X-Amz-Signature': (
148
'd28b6c4a54f31196a6d49335556736a3fc29f036018c8e'
149
'50775887299092d1a0'),
150
'X-Amz-SignedHeaders': 'host'
151
}
152
}
153
self.assert_presigned_url_matches(stdout, expected)
154
155
def test_s3_prefix_not_needed(self):
156
# Consistent with the 'ls' command.
157
stdout = self.get_presigned_url_for_cmd(
158
self.prefix + 'bucket/key')
159
160
self.assert_presigned_url_matches(
161
stdout, {
162
'hostname': 'bucket.s3.amazonaws.com',
163
'path': '/key',
164
'query_params': {
165
'AWSAccessKeyId': 'access_key',
166
'Expires': str(FROZEN_TIMESTAMP + DEFAULT_EXPIRES),
167
'Signature': '2m9M0eLB%2BqI0nUpkyTskKmHd0Ig%3D',
168
}
169
}
170
)
171
172
def test_can_support_addressing_mode_config(self):
173
with temporary_file('w') as f:
174
self.enable_addressing_mode_in_config(f, 'path')
175
stdout = self.get_presigned_url_for_cmd(
176
self.prefix + 's3://bucket/key')
177
self.assert_presigned_url_matches(
178
stdout, {
179
'hostname': 's3.amazonaws.com',
180
'path': '/bucket/key',
181
'query_params': {
182
'AWSAccessKeyId': 'access_key',
183
'Expires': str(FROZEN_TIMESTAMP + DEFAULT_EXPIRES),
184
'Signature': '2m9M0eLB%2BqI0nUpkyTskKmHd0Ig%3D',
185
}
186
}
187
)
188
189