Skip to content
This repository has been archived by the owner on Apr 21, 2021. It is now read-only.

Commit

Permalink
Merge pull request #19 from thorrak/master
Browse files Browse the repository at this point in the history
Fix validation issues
  • Loading branch information
Kyle authored Oct 4, 2016
2 parents b1c468a + 73585db commit b67ff2a
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 15 deletions.
10 changes: 5 additions & 5 deletions django_alexa/alexa.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


@intent
def LaunchRequest(session):
def LaunchRequest(**kwargs):
"""
Default Start Session Intent
---
Expand All @@ -21,7 +21,7 @@ def LaunchRequest(session):


@intent
def CancelIntent(session):
def CancelIntent(**kwargs):
"""
Default Cancel Intent
---
Expand All @@ -33,7 +33,7 @@ def CancelIntent(session):


@intent
def StopIntent(session):
def StopIntent(**kwargs):
"""
Default Stop Intent
---
Expand All @@ -45,7 +45,7 @@ def StopIntent(session):


@intent
def HelpIntent(session):
def HelpIntent(**kwargs):
"""
Default Help Intent
---
Expand All @@ -57,7 +57,7 @@ def HelpIntent(session):


@intent
def SessionEndedRequest(session):
def SessionEndedRequest(**kwargs):
"""
Default End Session Intent
---
Expand Down
2 changes: 1 addition & 1 deletion django_alexa/internal/intents_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 17 additions & 6 deletions django_alexa/internal/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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


Expand All @@ -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:
Expand All @@ -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


Expand All @@ -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})
21 changes: 19 additions & 2 deletions django_alexa/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand Down Expand Up @@ -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
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from setuptools import setup
from os import path


def open_file(fname):
return open(path.join(path.dirname(__file__), fname))

setup(
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(),
)

0 comments on commit b67ff2a

Please sign in to comment.