예제 #1
0
 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)
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
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))
예제 #7
0
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)
예제 #8
0
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
예제 #9
0
        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)
예제 #10
0
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