def parse_attachment(r, request): message = request.body # i need boundary to be in the message for email to parse it right if 'boundary' not in message[:message.index("--")]: if 'boundary' in request.META['CONTENT_TYPE']: message = request.META['CONTENT_TYPE'] + message else: raise BadRequest( "Could not find the boundary for the multipart content") msg = email.message_from_string(message) if msg.is_multipart(): parts = [] for part in msg.walk(): parts.append(part) if len(parts) < 1: raise ParamError( "The content of the multipart request didn't contain a statement" ) # ignore parts[0], it's the whole thing # parts[1] better be the statement r['body'] = convert_to_dict(parts[1].get_payload()) if len(parts) > 2: r['payload_sha2s'] = [] for a in parts[2:]: # attachments thehash = a.get("X-Experience-API-Hash") if not thehash: raise BadRequest( "X-Experience-API-Hash header was missing from attachment" ) r['payload_sha2s'].append(thehash) # Save msg object to cache att_cache.set(thehash, a) else: raise ParamError( "This content was not multipart for the multipart request.") # see if the posted statements have attachments att_stmts = [] if isinstance(r['body'], list): for s in r['body']: if 'attachments' in s: att_stmts.append(s) elif 'attachments' in r['body']: att_stmts.append(r['body']) if att_stmts: # find if any of those statements with attachments have a signed statement signed_stmts = [(s, a) for s in att_stmts for a in s.get('attachments', None) if a['usageType'] == "http://adlnet.gov/expapi/attachments/signature"] for ss in signed_stmts: attmnt = att_cache.get(ss[1]['sha2']).get_payload(decode=True) jws = JWS(jws=attmnt) try: if not jws.verify() or not jws.validate(ss[0]): raise BadRequest("The JSON Web Signature is not valid") except JWSException as jwsx: raise BadRequest(jwsx)
def parse_attachment(r, request): message = request.body # i need boundary to be in the message for email to parse it right if 'boundary' not in message[:message.index("--")]: if 'boundary' in request.META['CONTENT_TYPE']: message = request.META['CONTENT_TYPE'] + message else: raise BadRequest("Could not find the boundary for the multipart content") msg = email.message_from_string(message) if msg.is_multipart(): parts = [] for part in msg.walk(): parts.append(part) if len(parts) < 1: raise ParamError("The content of the multipart request didn't contain a statement") # ignore parts[0], it's the whole thing # parts[1] better be the statement r['body'] = convert_to_dict(parts[1].get_payload()) if len(parts) > 2: r['payload_sha2s'] = [] for a in parts[2:]: # attachments thehash = a.get("X-Experience-API-Hash") if not thehash: raise BadRequest("X-Experience-API-Hash header was missing from attachment") r['payload_sha2s'].append(thehash) # Save msg object to cache att_cache.set(thehash, a) else: raise ParamError("This content was not multipart for the multipart request.") # see if the posted statements have attachments att_stmts = [] if isinstance(r['body'], list): for s in r['body']: if 'attachments' in s: att_stmts.append(s) elif 'attachments' in r['body']: att_stmts.append(r['body']) if att_stmts: # find if any of those statements with attachments have a signed statement signed_stmts = [(s,a) for s in att_stmts for a in s.get('attachments', None) if a['usageType'] == "http://adlnet.gov/expapi/attachments/signature"] for ss in signed_stmts: attmnt = att_cache.get(ss[1]['sha2']).get_payload(decode=True) jws = JWS(jws=attmnt) try: if not jws.verify() or not jws.validate(ss[0]): raise BadRequest("The JSON Web Signature is not valid") except JWSException as jwsx: raise BadRequest(jwsx)
def parse_attachment(r, request): # Email library insists on having the multipart header in the body - workaround message = request.body if 'boundary' not in message[:message.index("--")]: if 'boundary' in request.META['CONTENT_TYPE']: message = "Content-Type:" + request.META['CONTENT_TYPE'] + "\r\n" + message else: raise BadRequest("Could not find the boundary for the multipart content") msg = email.message_from_string(message) if msg.is_multipart(): parts = msg.get_payload() stmt_part = parts.pop(0) if stmt_part['Content-Type'] != "application/json": raise ParamError("Content-Type of statement was not application/json") try: r['body'] = json.loads(stmt_part.get_payload()) except Exception: raise ParamError("Statement was not valid JSON") # Find the signature sha2 from the list attachment values in the statements (there should only be one) if isinstance(r['body'], list): signature_att = list(itertools.chain(*[[a.get('sha2', None) for a in s['attachments'] if a.get('usageType', None) == "http://adlnet.gov/expapi/attachments/signature"] for s in r['body'] if 'attachments' in s])) else: signature_att = [a.get('sha2', None) for a in r['body']['attachments'] if a.get('usageType', None) == "http://adlnet.gov/expapi/attachments/signature" and 'attachments' in r['body']] # Get all sha2s from the request payload_sha2s = [p.get('X-Experience-API-Hash', None) for p in msg.get_payload()] # Check each sha2 in payload, if even one of them is None then there is a missing hash for sha2 in payload_sha2s: if not sha2: raise BadRequest("X-Experience-API-Hash header was missing from attachment") # Check the sig sha2 in statements if it not in the payload sha2s then the sig sha2 is missing for sig in signature_att: if sig: if sig not in payload_sha2s: raise BadRequest("Signature attachment is missing from request") else: raise BadRequest("Signature attachment is missing from request") # We know all sha2s are there so set it and loop through each payload r['payload_sha2s'] = payload_sha2s for part in msg.get_payload(): xhash = part.get('X-Experience-API-Hash') c_type = part['Content-Type'] # Payloads are base64 encoded implictly from email lib (except for plaintext) if "text/plain" in c_type: payload = b64encode(part.get_payload()) else: payload = part.get_payload() att_cache.set(xhash, payload) else: raise ParamError("This content was not multipart for the multipart request.") # See if the posted statements have attachments att_stmts = [] if isinstance(r['body'], list): for s in r['body']: if 'attachments' in s: att_stmts.append(s) elif 'attachments' in r['body']: att_stmts.append(r['body']) if att_stmts: # find if any of those statements with attachments have a signed statement signed_stmts = [(s,a) for s in att_stmts for a in s.get('attachments', None) if a['usageType'] == "http://adlnet.gov/expapi/attachments/signature"] for ss in signed_stmts: attmnt = b64decode(att_cache.get(ss[1]['sha2'])) jws = JWS(jws=attmnt) try: if not jws.verify() or not jws.validate(ss[0]): raise BadRequest("The JSON Web Signature is not valid") except JWSException as jwsx: raise BadRequest(jwsx)
def parse_attachment(r, request): # Email library insists on having the multipart header in the body - workaround message = request.body if 'boundary' not in message[:message.index("--")]: if 'boundary' in request.META['CONTENT_TYPE']: message = "Content-Type:" + request.META[ 'CONTENT_TYPE'] + "\r\n" + message else: raise BadRequest( "Could not find the boundary for the multipart content") msg = email.message_from_string(message) if msg.is_multipart(): parts = msg.get_payload() stmt_part = parts.pop(0) if stmt_part['Content-Type'] != "application/json": raise ParamError( "Content-Type of statement was not application/json") try: r['body'] = json.loads(stmt_part.get_payload()) except Exception: raise ParamError("Statement was not valid JSON") # Find the signature sha2 from the list attachment values in the statements (there should only be one) if isinstance(r['body'], list): signature_att = list( itertools.chain(*[[ a.get('sha2', None) for a in s['attachments'] if a.get('usageType', None) == "http://adlnet.gov/expapi/attachments/signature" ] for s in r['body'] if 'attachments' in s])) else: signature_att = [ a.get('sha2', None) for a in r['body']['attachments'] if a.get('usageType', None) == "http://adlnet.gov/expapi/attachments/signature" and 'attachments' in r['body'] ] # Get all sha2s from the request payload_sha2s = [ p.get('X-Experience-API-Hash', None) for p in msg.get_payload() ] # Check each sha2 in payload, if even one of them is None then there is a missing hash for sha2 in payload_sha2s: if not sha2: raise BadRequest( "X-Experience-API-Hash header was missing from attachment") # Check the sig sha2 in statements if it not in the payload sha2s then the sig sha2 is missing for sig in signature_att: if sig: if sig not in payload_sha2s: raise BadRequest( "Signature attachment is missing from request") else: raise BadRequest( "Signature attachment is missing from request") # We know all sha2s are there so set it and loop through each payload r['payload_sha2s'] = payload_sha2s for part in msg.get_payload(): xhash = part.get('X-Experience-API-Hash') c_type = part['Content-Type'] # Payloads are base64 encoded implictly from email lib (except for plaintext) if "text/plain" in c_type: payload = b64encode(part.get_payload()) else: payload = part.get_payload() att_cache.set(xhash, payload) else: raise ParamError( "This content was not multipart for the multipart request.") # See if the posted statements have attachments att_stmts = [] if isinstance(r['body'], list): for s in r['body']: if 'attachments' in s: att_stmts.append(s) elif 'attachments' in r['body']: att_stmts.append(r['body']) if att_stmts: # find if any of those statements with attachments have a signed statement signed_stmts = [(s, a) for s in att_stmts for a in s.get('attachments', None) if a['usageType'] == "http://adlnet.gov/expapi/attachments/signature"] for ss in signed_stmts: attmnt = b64decode(att_cache.get(ss[1]['sha2'])) jws = JWS(jws=attmnt) try: if not jws.verify() or not jws.validate(ss[0]): raise BadRequest("The JSON Web Signature is not valid") except JWSException as jwsx: raise BadRequest(jwsx)