def inner(request, *args, **kwargs): # Note: The cases involving OAUTH_ENABLED are here if OAUTH_ENABLED is switched from true to false # after a client has performed the handshake. (Not likely to happen, but could) auth_type = request['auth']['type'] # There is an http auth_type request and http auth is enabled if auth_type == 'http' and settings.HTTP_AUTH_ENABLED: http_auth_helper(request) # There is an http auth_type request and http auth is not enabled elif auth_type == 'http' and not settings.HTTP_AUTH_ENABLED: raise BadRequest( "HTTP authorization is not enabled. To enable, set the HTTP_AUTH_ENABLED flag to true in settings" ) # There is an oauth auth_type request and oauth is enabled elif auth_type == 'oauth' and settings.OAUTH_ENABLED: oauth_helper(request) # There is an oauth auth_type request and oauth is not enabled elif auth_type == 'oauth' and not settings.OAUTH_ENABLED: raise BadRequest( "OAuth is not enabled. To enable, set the OAUTH_ENABLED flag to true in settings" ) # There is no auth_type request and there is some sort of auth enabled elif auth_type == 'none' and (settings.HTTP_AUTH_ENABLED or settings.OAUTH_ENABLED): raise Unauthorized( "Auth is enabled but no authentication was sent with the request." ) # There is no auth_type request and no auth is enabled elif auth_type == 'none' and not (settings.HTTP_AUTH_ENABLED or settings.OAUTH_ENABLED): request['auth'] = None return func(request, *args, **kwargs)
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" ) headers = defaultdict(str) 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 server_validation(stmt_set, auth, payload_sha2s): auth_validated = False if type(stmt_set) is list: for stmt in stmt_set: server_validation(stmt, auth, payload_sha2s) else: if 'id' in stmt_set: statement_id = stmt_set['id'] if check_for_existing_statementId(statement_id): err_msg = "A statement with ID %s already exists" % statement_id raise ParamConflict(err_msg) server_validate_statement_object(stmt_set['object'], auth) if stmt_set['verb']['id'] == 'http://adlnet.gov/expapi/verbs/voided': validate_void_statement(stmt_set['object']['id']) if not 'objectType' in stmt_set['object'] or stmt_set['object']['objectType'] == 'Activity': get_act_def_data(stmt_set['object']) try: validator = StatementValidator.StatementValidator(None) validator.validate_activity(stmt_set['object']) except Exception, e: raise BadRequest(e.message) except ParamError, e: raise ParamError(e.message)
def statements_post(r_dict): payload_sha2s = r_dict.get('payload_sha2s', None) try: validator = StatementValidator.StatementValidator(r_dict['body']) msg = validator.validate() except Exception, e: raise BadRequest(e.message)
def statements_post(req_dict): if req_dict['params'].keys(): raise ParamError("The post statements request contained unexpected parameters: %s" % ", ".join(req_dict['params'].keys())) payload_sha2s = req_dict.get('payload_sha2s', None) if isinstance(req_dict['body'], basestring): req_dict['body'] = convert_to_dict(req_dict['body']) try: validator = StatementValidator.StatementValidator(req_dict['body']) msg = validator.validate() except Exception, e: raise BadRequest(e.message)
def convert_to_dict(incoming_data): data = {} # GET data will be non JSON string-have to try literal_eval if type(incoming_data) == dict: return incoming_data try: data = json.loads(incoming_data) except Exception, e: try: data = ast.literal_eval(incoming_data) except Exception, e: raise BadRequest( "Cannot evaluate data into dictionary to parse -- Error: %s in %s" % (e.message, incoming_data))
def statements_put(r_dict): # Must have statementId param-if not raise paramerror try: statement_id = r_dict['params']['statementId'] except KeyError: err_msg = "Error -- statements - method = %s, but statementId paramater is missing" % r_dict[ 'method'] raise ParamError(err_msg) # If statement with that ID already exists-raise conflict error if check_for_existing_statementId(statement_id): err_msg = "A statement with ID %s already exists" % statement_id raise ParamConflict(err_msg) # If there are no other params-raise param error since nothing else is supplied if not check_for_no_other_params_supplied(r_dict['body']): err_msg = "No other params are supplied with statementId." raise ParamError(err_msg) try: validator = StatementValidator.StatementValidator(r_dict['body']) msg = validator.validate() except Exception, e: raise BadRequest(e.message)
def parse_body(r, request): if request.method == 'POST' or request.method == 'PUT': # Parse out profiles/states if the POST dict is not empty if 'multipart/form-data' in request.META['CONTENT_TYPE']: if request.POST.dict().keys(): r['params'].update(request.POST.dict()) parser = MultiPartParser( request.META, StringIO.StringIO(request.raw_post_data), request.upload_handlers) post, files = parser.parse() r['files'] = files # If it is multipart/mixed, parse out all data elif 'multipart/mixed' in request.META['CONTENT_TYPE']: parse_attachment(r, request) # Normal POST/PUT data else: if request.body: # profile uses the request body r['raw_body'] = request.body # Body will be some type of string, not necessarily JSON r['body'] = convert_to_dict(request.body) else: raise BadRequest("No body in request") return r
raise ParamConflict("A statement with ID %s already exists" % statement_id) # Set id inside of statement with param id if not statement_body_id: req_dict['body']['id'] = statement_id # If there are no other params-raise param error since nothing else is supplied if not check_for_no_other_params_supplied(req_dict['body']): raise ParamError("No other params are supplied with statementId.") # Validate statement in body try: validator = StatementValidator.StatementValidator(req_dict['body']) msg = validator.validate() except Exception, e: raise BadRequest(e.message) except ParamError, e: raise ParamError(e.message) server_validation(req_dict['body'], req_dict.get('auth', None), req_dict.get('payload_sha2s', None)) return req_dict def validate_attachments(attachment_data, payload_sha2s): # For each attachment that is in the actual statement for attachment in attachment_data: # If the attachment data has a sha2 field, must validate it against the payload data if 'sha2' in attachment: sha2 = attachment['sha2'] # Check if the sha2 field is a key in the payload dict if not sha2 in payload_sha2s: err_msg = "Could not find attachment payload with sha: %s" % sha2 raise ParamError(err_msg)
def parse(request, more_id=None): r_dict = {} # Build headers from request in request dict r_dict['headers'] = get_headers(request.META) # Traditional authorization should be passed in headers r_dict['auth'] = {} if 'Authorization' in r_dict['headers']: # OAuth will always be dict, not http auth. Set required fields for oauth module and type for authentication # module set_authorization(r_dict, request) elif 'Authorization' in request.body or 'HTTP_AUTHORIZATION' in request.body: # Authorization could be passed into body if cross origin request r_dict['auth']['type'] = 'http' else: r_dict['auth']['type'] = 'none' r_dict['params'] = {} # lookin for weird IE CORS stuff.. it'll be a post with a 'method' url param if request.method == 'POST' and 'method' in request.GET: bdy = convert_post_body_to_dict(request.body) # 'content' is in body for the IE cors POST if 'content' in bdy: r_dict['body'] = urllib.unquote(bdy.pop('content')) # headers are in the body too for IE CORS, we removes them r_dict['headers'].update(get_headers(bdy)) for h in r_dict['headers']: bdy.pop(h, None) r_dict['params'].update(bdy) # all that should be left are params for the request, # we adds them to the params object for k in request.GET: if k == 'method': # make sure the method param goes in the special method spot r_dict[k] = request.GET[k] r_dict['params'][k] = request.GET[k] # Just parse body for all non IE CORS stuff else: r_dict = parse_body(r_dict, request) # Update dict with any GET data r_dict['params'].update(request.GET.dict()) # Method gets set for cors already if 'method' not in r_dict['params']: # Differentiate GET and POST if request.method == "POST" and (request.path[6:] == 'statements' or request.path[6:] == 'statements/'): # Can have empty body for POST (acts like GET) if 'body' in r_dict: # If body is a list, it's a post if not isinstance(r_dict['body'], list): # If actor verb and object not in body - means it's a GET or invalid POST if not ('actor' in r_dict['body'] and 'verb' in r_dict['body'] and 'object' in r_dict['body']): # If body keys are in get params - GET - else invalid request if set(r_dict['body'].keys()).issubset([ 'statementId', 'voidedStatementId', 'agent', 'verb', 'activity', 'registration', 'related_activities', 'related_agents', 'since', 'until', 'limit', 'format', 'attachments', 'ascending' ]): r_dict['method'] = 'GET' else: raise BadRequest( "Statement is missing actor, verb, or object") else: r_dict['method'] = 'POST' else: r_dict['method'] = 'POST' else: r_dict['method'] = 'GET' else: r_dict['method'] = request.method # Set if someone is hitting the statements/more endpoint if more_id: r_dict['more_id'] = more_id return r_dict