diff --git a/django_alexa/alexa.py b/django_alexa/alexa.py index 5130687..8472cb0 100644 --- a/django_alexa/alexa.py +++ b/django_alexa/alexa.py @@ -3,7 +3,7 @@ @intent -def LaunchRequest(session): +def LaunchRequest(**kwargs): """ Default Start Session Intent --- @@ -21,7 +21,7 @@ def LaunchRequest(session): @intent -def CancelIntent(session): +def CancelIntent(**kwargs): """ Default Cancel Intent --- @@ -33,7 +33,7 @@ def CancelIntent(session): @intent -def StopIntent(session): +def StopIntent(**kwargs): """ Default Stop Intent --- @@ -45,7 +45,7 @@ def StopIntent(session): @intent -def HelpIntent(session): +def HelpIntent(**kwargs): """ Default Help Intent --- @@ -57,7 +57,7 @@ def HelpIntent(session): @intent -def SessionEndedRequest(session): +def SessionEndedRequest(**kwargs): """ Default End Session Intent --- diff --git a/django_alexa/internal/intents_schema.py b/django_alexa/internal/intents_schema.py index b16c921..1afab47 100644 --- a/django_alexa/internal/intents_schema.py +++ b/django_alexa/internal/intents_schema.py @@ -37,7 +37,7 @@ def route(cls, session, app, intent, intent_kwargs): if slot and bool(intent_kwargs) is False: msg = "Intent '{0}.{1}' requires slots data and none was provided".format(app, intent) raise InternalError(msg) - intent_kwargs['session'] = session.get('attributes', {}) + intent_kwargs['session'] = session msg = "Routing: '{0}.{1}' with args {2} to '{3}.{4}'".format(app, intent, intent_kwargs, diff --git a/django_alexa/internal/validation.py b/django_alexa/internal/validation.py index 756b9a8..683462f 100644 --- a/django_alexa/internal/validation.py +++ b/django_alexa/internal/validation.py @@ -48,7 +48,10 @@ def validate_current_timestamp(value): log.debug("Alexa: {0}".format(utc_timestamp)) log.debug("Server: {0}".format(utc_timestamp_now)) log.debug("Delta: {0}".format(delta)) - return False if delta > timedelta(minutes=2, seconds=30) else True + if abs(delta) > timedelta(minutes=2, seconds=30): + return False + else: + return True def validate_char_limit(value): @@ -71,7 +74,10 @@ def verify_cert_url(cert_url): if parsed_url.scheme == 'https': if parsed_url.hostname == "s3.amazonaws.com": if os.path.normpath(parsed_url.path).startswith("/echo.api/"): - return True + if parsed_url.port is None: + return True + elif parsed_url.port == 443: + return True return False @@ -81,6 +87,8 @@ def verify_signature(request_body, signature, cert_url): """ if signature is None or cert_url is None: return False + if len(signature) == 0: + return False cert_str = requests.get(cert_url) certificate = crypto.load_certificate(crypto.FILETYPE_PEM, str(cert_str.text)) if certificate.has_expired() is True: @@ -92,7 +100,7 @@ def verify_signature(request_body, signature, cert_url): if crypto.verify(certificate, decoded_signature, request_body, 'sha1') is None: return True except: - raise InternalError("Error occured during signature validation") + raise InternalError("Error occured during signature validation", {"error": 400}) return False @@ -103,9 +111,12 @@ def validate_alexa_request(request_headers, request_body): """ if ALEXA_REQUEST_VERIFICATON is True: timestamp = json.loads(request_body)['request']['timestamp'] + # For each of the following errors, the alexa service expects an HTTP error code. This isn't well documented. + # I'm going to return 403 forbidden just to be safe (but need to pass a message to the custom error handler, + # hence why I'm adding an argument when raising the error) if validate_current_timestamp(timestamp) is False: - raise InternalError("Invalid Request Timestamp") + raise InternalError("Invalid Request Timestamp", {"error": 400}) if verify_cert_url(request_headers.get('HTTP_SIGNATURECERTCHAINURL')) is False: - raise InternalError("Invalid Certificate Chain URL") + raise InternalError("Invalid Certificate Chain URL", {"error": 400}) if verify_signature(request_body, request_headers.get('HTTP_SIGNATURE'), request_headers.get('HTTP_SIGNATURECERTCHAINURL')) is False: - raise InternalError("Invalid Request Signature") + raise InternalError("Invalid Request Signature", {"error": 400}) diff --git a/django_alexa/views.py b/django_alexa/views.py index 069b0c4..e873e81 100644 --- a/django_alexa/views.py +++ b/django_alexa/views.py @@ -4,6 +4,7 @@ from django.conf import settings from rest_framework.response import Response from rest_framework.status import HTTP_200_OK +from django.http import HttpResponseBadRequest, HttpResponseForbidden from rest_framework.views import APIView from .serializers import ASKInputSerializer from .internal import ALEXA_APP_IDS, ResponseBuilder, IntentsSchema, validate_alexa_request, validate_reponse_limit @@ -27,7 +28,22 @@ def handle_exception(self, exc): msg = "An internal error occured in the skill." log.exception(msg) data = ResponseBuilder.create_response(message=msg) - return Response(data=data, status=HTTP_200_OK) + # If we need to return an error code, do so. + # There is probably a better way of doing this, but this works. If anyone knows of a better way, please - + # submit a correction + try: + error = exc.args[1] + if error['error'] == 403: + log.debug(data) + return HttpResponseForbidden() + elif error['error'] == 400: + log.debug(data) + return HttpResponseBadRequest() + else: + # If we are passed an error code we should probably do something more here, but for now - this works. + return Response(data=data, status=HTTP_200_OK) + except: + return Response(data=data, status=HTTP_200_OK) def handle_request(self, validated_data): log.info("Alexa Request Body: {0}".format(validated_data)) @@ -68,6 +84,7 @@ def post(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs): log.debug("#" * 10 + "Start Alexa Request" + "#" * 10) response = super(ASKView, self).dispatch(request, *args, **kwargs) - validate_reponse_limit(response.render().content) + if response.status_code == 200: + validate_reponse_limit(response.render().content) log.debug("#" * 10 + "End Alexa Request" + "#" * 10) return response diff --git a/setup.py b/setup.py index 5cd6e24..e5dd61b 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ from setuptools import setup from os import path + def open_file(fname): return open(path.join(path.dirname(__file__), fname)) @@ -8,5 +9,5 @@ def open_file(fname): setup_requires=['pbr', 'pyversion'], pbr=True, auto_version="PBR", - install_requires=open_file('requirements.txt').readlines(), + install_requires=open(path.join(path.dirname(__file__), 'requirements.txt')).readlines(), )