blob: 08a80d2900b02d8a6fbcb9de526df0d23a447187 [file] [log] [blame]
#!/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.core import Ec2stackError
from ec2stack import errors
def get(item, data=None):
if data is None:
data = request.form
if item in data:
return data[item]
else:
return None
def authentication_required(f):
@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):
return dict((key.lower(), value) for key, value in dct.iteritems())
def require_parameters(required_parameters):
for parameter in required_parameters:
if not contains_parameter(parameter):
errors.missing_paramater(parameter)
def require_atleast_one_parameter(parameters):
parameter = None
for parameter in parameters:
if contains_parameter(parameter):
return
errors.missing_paramater(parameter)
def error_to_aws(response, error_map):
for errortext, error_function in error_map.iteritems():
if errortext in response['errortext']:
error_function()
def contains_parameter(parameter, data=None):
if data is None:
data = request.form
return (get(parameter, data)) is not None
def contains_parameter_with_keyword(key):
return len(get_request_parameter_keys(key)) >= 1
def get_request_parameter_keys(prefix, data=None):
if data is None:
data = request.form
return [item for item in data if prefix in item]
def get_secretkey(data=None):
if data is None:
data = request.form
apikey = get('AWSAccessKeyId', data)
user = USERS.get(apikey)
if user is None:
raise Ec2stackError(
'401',
'AuthFailure',
'Unable to find a secret key for %s, please insure you registered'
% apikey
)
return user.secretkey.encode('utf-8')
def _valid_signature_method():
signature_method = get('SignatureMethod')
if signature_method not in ['HmacSHA1', 'HmacSHA256']:
raise Ec2stackError(
'400',
'InvalidParameterValue',
'Value (%s) for parameter SignatureMethod is invalid. '
'Unknown signature method.' % signature_method
)
def _valid_signature_version():
signature_version = get('SignatureVersion')
if signature_version != '2':
raise Ec2stackError(
'400',
'InvalidParameterValue',
'Value (%s) for parameter SignatureVersion is invalid.'
'Valid Signature versions are 2.'
% signature_version
)
def _valid_signature():
signature = get('Signature')
generated_signature = generate_signature()
if signature != generated_signature:
raise Ec2stackError(
'401',
'AuthFailure',
'AWS was not able to validate the provided access credentials.'
)
def generate_signature(data=None, method=None, host=None):
if data is None:
data = request.form
secretkey = get_secretkey(data)
request_string = _get_request_string(data, method, host)
signature = hmac.new(
key=secretkey,
msg=bytes(request_string),
digestmod=hashlib.sha256
).digest()
signature = b64encode(signature)
return signature
def _get_request_string(data, method=None, host=None):
if method is None:
method = request.method
if host is None:
host = request.host
query_string = _get_query_string(data)
request_string = '\n'.join(
[method, host, '/', query_string]
)
return request_string.encode('utf-8')
def _get_query_string(data):
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):
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):
content = render_template(request_id=uuid(), **kwargs)
response = make_response(content)
return _create_response(response, '200')
def _create_response(response, code):
response.headers['Content-Type'] = 'application/xml'
response.status_code = int(code)
return response
def read_file(name):
filepath = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'../',
name
)
data = open(filepath)
return data.read()