This repository has been archived by the owner on Apr 24, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
/
helpers.py
339 lines (255 loc) · 7.87 KB
/
helpers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#!/usr/bin/env python
# encoding: utf-8
import os
import hmac
import hashlib
from uuid import uuid1 as uuid
from base64 import b64encode
from urllib import urlencode
from functools import wraps
from flask import request, make_response, render_template
from ec2stack.services import USERS
from ec2stack import errors
def get(item, data=None):
"""
Gets the specified item in the given data.
@param item: Key of the item.
@param data: Data the item is in.
@return: Item if found, otherwise None.
"""
if data is None:
data = request.form
if item in data:
return data[item]
else:
return None
def authentication_required(f):
"""
Checked that the given signature is valid.
@param f: Function to wrap around.
@return: Result of signature check.
"""
@wraps(f)
def decorated(*args, **kwargs):
required_params = {'Action', 'AWSAccessKeyId', 'Signature',
'SignatureMethod', 'SignatureVersion', 'Timestamp',
'Version'}
require_parameters(required_params)
_valid_signature_method()
_valid_signature_version()
_valid_signature()
return f(*args, **kwargs)
return decorated
def normalize_dict_keys(dct):
"""
Normalizes all keys in the given dictionary.
@param dct: Dictionary to normalize.
@return: Dictionary normalized.
"""
return dict((key.lower(), value) for key, value in dct.iteritems())
def require_parameters(required_parameters):
"""
Checks that the given array of parameters are present.
@param required_parameters: Array of required parameters.
"""
for parameter in required_parameters:
if not contains_parameter(parameter):
errors.missing_parameter(parameter)
def require_atleast_one_parameter(parameters):
"""
Require atleast one parameter.
@param parameters: Array of possible parameters.
@return: void.
"""
parameter = None
for parameter in parameters:
if contains_parameter(parameter):
return
errors.missing_parameter(parameter)
def contains_parameter(parameter, data=None):
"""
Checks if the parameter is contained within the given data.
@param parameter: Parameter to check.
@param data: Data to check in.
@return: Boolean.
"""
if data is None:
data = request.form
return (get(parameter, data)) is not None
def contains_parameter_with_keyword(prefix):
"""
Checks if the request contains parameters with the given prefix.
@param prefix: Prefix of parameters.
@return: Boolean.
"""
return len(get_request_parameter_keys(prefix)) >= 1
def get_request_parameter_keys(prefix, data=None):
"""
Gets all parameters containing prefix.
@param prefix: Prefix of parameters.
@param data: Data to search.
@return: List of matching parameters.
"""
if data is None:
data = request.form
return [item for item in data if prefix in item]
def get_secretkey(data=None):
"""
Get the secret key from the database.
@param data: Data to get the API KEY from.
@return: The users secret key.
@raise Ec2stackError: if the secretkey is not found.
"""
if data is None:
data = request.form
apikey = get('AWSAccessKeyId', data)
user = USERS.get(apikey)
if user is None:
errors.apikey_not_found(apikey)
return user.secretkey.encode('utf-8')
def _valid_signature_method():
"""
Check that the given signature method is correct.
@raise Ec2stackError: if the signature method is invalid.
"""
signature_method = get('SignatureMethod')
if signature_method not in ['HmacSHA1', 'HmacSHA256']:
errors.invalid_parameter_value(
'Value (%s) for parameter SignatureMethod is invalid. '
'Unknown signature method.' % signature_method
)
def _valid_signature_version():
"""
Checks that the given signature version is correct.
@raise Ec2stackError: if the signature version is invalid.
"""
signature_version = get('SignatureVersion')
if signature_version != '2':
errors.invalid_parameter_value(
'Value (%s) for parameter SignatureVersion is invalid.'
'Valid Signature versions are 2.'
% signature_version
)
def _valid_signature():
"""
Checks that the given signature matches the signature generated.
@raise Ec2stackError: if the signature does not match the generated signature.
"""
signature = get('Signature')
generated_signature = generate_signature()
if signature != generated_signature:
errors.authentication_failure()
def generate_signature(data=None, method=None, host=None, path=None):
"""
Generates a signature.
@param data: Data of the request.
@param method: HTTP method used.
@param host: HTTP post.
@param path: HTTP hort.
@return: A signatured.
"""
if data is None:
data = request.form
signature_type = get('SignatureMethod', data)
secretkey = get_secretkey(data)
request_string = _get_request_string(data, method, host, path)
if signature_type == 'HmacSHA1':
digestmod = hashlib.sha1
else:
digestmod = hashlib.sha256
signature = hmac.new(
key=secretkey,
msg=bytes(request_string),
digestmod=digestmod
).digest()
signature = b64encode(signature)
return signature
def _get_request_string(data, method=None, host=None, path=None):
"""
Creates the request string
@param data: Data of the request.
@param method: HTTP method used.
@param host: HTTP host.
@param path: HTTP path.
@return: Request string.
"""
if method is None:
method = request.method
if host is None:
host = request.host
if path is None:
path = request.path
query_string = _get_query_string(data)
request_string = '\n'.join(
[method, host, path, query_string]
)
return request_string.encode('utf-8')
def _get_query_string(data):
"""
Creates the query string.
@param data: Data of the request.
@return: Query String.
"""
params = {}
for param in data:
if param != 'Signature':
params[param] = data[param]
keys = sorted(params.keys())
values = map(params.get, keys)
query_string = urlencode(
list(
zip(keys, values)
)
)
query_string = query_string.replace('+', '%20')
return query_string
def error_response(code, error, message):
"""
Returns a error response.
@param code: Status code.
@param error: Error type.
@param message: Error message.
@return: Response.
"""
response = make_response(
render_template(
'generic_error.xml',
response_type='Response',
error=error,
message=message,
request_id=uuid()
)
)
return _create_response(response, int(code))
def successful_response(**kwargs):
"""
Returns a successful response.
@param kwargs: Parameters to render the template with.
@return: Response.
"""
content = render_template(request_id=uuid(), **kwargs)
response = make_response(content)
return _create_response(response, '200')
def _create_response(response, code):
"""
Creates a response.
@param response: Response to use.
@param code: Status code of the response.
@return: Response.
"""
response.headers['Content-Type'] = 'application/xml'
response.status_code = int(code)
return response
def read_file(name):
"""
Reads the given file.
@param name: Filename of the file to read.
@return: Contents of the file.
"""
filepath = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'../',
name
)
data = open(filepath)
return data.read()