def process(): responseBody = {'version': '1.1', 'error': None, 'result': None} requestBody = None try: # init/clear fossil cache clearCache() # read request try: requestBody = request.get_json() Logger.get('rpc').info( 'json rpc request. request: {0}'.format(requestBody)) except BadRequest: raise RequestError('ERR-R1', 'Invalid mime-type.') if not requestBody: raise RequestError('ERR-R2', 'Empty request.') if 'id' in requestBody: responseBody['id'] = requestBody['id'] # run request responseBody['result'] = ServiceRunner().invokeMethod( str(requestBody['method']), requestBody.get('params', [])) except CausedError as e: try: errorInfo = fossilize(e) except NonFossilizableException as e2: # catch Exceptions that are not registered as Fossils # and log them errorInfo = {'code': '', 'message': str(e2)} Logger.get('dev').exception('Exception not registered as fossil') # NoReport errors (i.e. not logged in) shouldn't be logged if not isinstance(e, NoReportError) and not getattr( e, '_disallow_report', False): Logger.get('rpc').exception( 'Service request failed. ' 'Request text:\r\n{0}\r\n\r\n'.format(requestBody)) if requestBody: params = requestBody.get('params', []) Sanitization._escapeHTML(params) errorInfo["requestInfo"] = { 'method': str(requestBody['method']), 'params': params, 'origin': str(requestBody.get('origin', 'unknown')) } Logger.get('rpc').debug('Arguments: {0}'.format( errorInfo['requestInfo'])) responseBody['error'] = errorInfo try: jsonResponse = dumps(responseBody, ensure_ascii=True) except UnicodeError: Logger.get('rpc').exception('Problem encoding JSON response') # This is to avoid exceptions due to old data encodings (based on iso-8859-1) responseBody['result'] = fix_broken_obj(responseBody['result']) jsonResponse = encode(responseBody) return app.response_class(jsonResponse, mimetype='application/json')
def process(): clearCache() payload = request.json Logger.get('rpc').info('json rpc request. request: %r', payload) rv = {} if 'id' in payload: rv['id'] = payload['id'] rv['result'] = invoke_method(str(payload['method']), payload.get('params', [])) return jsonify(rv)
def process(self): if request.method not in HTTP_VERBS: # Just to be sure that we don't get some crappy http verb we don't expect raise BadRequest res = '' g.rh = self sentry_set_tags({'rh': self.__class__.__name__}) if self.EVENT_FEATURE is not None: self._check_event_feature() logger.info('%s %s [IP=%s] [PID=%s] [UID=%r]', request.method, request.relative_url, request.remote_addr, os.getpid(), session.get('_user_id')) try: fossilize.clearCache() init_email_queue() self._check_csrf() res = self._do_process() signals.after_process.send() if self.commit: db.session.commit() flush_email_queue() else: db.session.rollback() except DatabaseError: db.session.rollback() handle_sqlalchemy_database_error( ) # this will re-raise an exception except Exception: # rollback to avoid errors as rendering the error page # within the indico layout may trigger an auto-flush db.session.rollback() raise logger.debug('Request successful') if res is None: return self._responseUtil.make_empty() response = self._responseUtil.make_response(res) if self.DENY_FRAMES: response.headers['X-Frame-Options'] = 'DENY' return response
def process(self): if request.method not in HTTP_VERBS: # Just to be sure that we don't get some crappy http verb we don't expect raise BadRequest res = '' g.rh = self sentry_set_tags({'rh': self.__class__.__name__}) if self.EVENT_FEATURE is not None: self._check_event_feature() logger.info('%s %s [IP=%s] [PID=%s] [UID=%r]', request.method, request.relative_url, request.remote_addr, os.getpid(), session.get('_user_id')) try: fossilize.clearCache() GenericMailer.flushQueue(False) self._check_csrf() res = self._do_process() signals.after_process.send() if self.commit: if GenericMailer.has_queue(): # ensure we fail early (before sending out e-mails) # in case there are DB constraint violations, etc... db.enforce_constraints() GenericMailer.flushQueue(True) db.session.commit() else: db.session.rollback() except DatabaseError: db.session.rollback() handle_sqlalchemy_database_error( ) # this will re-raise an exception logger.debug('Request successful') if res is None: return self._responseUtil.make_empty() response = self._responseUtil.make_response(res) if self.DENY_FRAMES: response.headers['X-Frame-Options'] = 'DENY' return response
def process(self): if request.method not in HTTP_VERBS: # Just to be sure that we don't get some crappy http verb we don't expect raise BadRequest res = '' g.rh = self sentry_set_tags({'rh': self.__class__.__name__}) if self.EVENT_FEATURE is not None: self._check_event_feature() logger.info('%s %s [IP=%s] [PID=%s] [UID=%r]', request.method, request.relative_url, request.remote_addr, os.getpid(), session.get('_user_id')) try: fossilize.clearCache() GenericMailer.flushQueue(False) self._check_csrf() res = self._do_process() signals.after_process.send() if self.commit: if GenericMailer.has_queue(): # ensure we fail early (before sending out e-mails) # in case there are DB constraint violations, etc... db.enforce_constraints() GenericMailer.flushQueue(True) db.session.commit() else: db.session.rollback() except DatabaseError: db.session.rollback() handle_sqlalchemy_database_error() # this will re-raise an exception logger.debug('Request successful') if res is None: return self._responseUtil.make_empty() response = self._responseUtil.make_response(res) if self.DENY_FRAMES: response.headers['X-Frame-Options'] = 'DENY' return response
def process(self): if request.method not in HTTP_VERBS: # Just to be sure that we don't get some crappy http verb we don't expect raise BadRequest res = '' g.rh = self sentry_set_tags({'rh': self.__class__.__name__}) if self.EVENT_FEATURE is not None: self._check_event_feature() logger.info('%s %s [IP=%s] [PID=%s] [UID=%r]', request.method, request.relative_url, request.remote_addr, os.getpid(), session.get('_user_id')) try: fossilize.clearCache() init_email_queue() self._check_csrf() res = self._do_process() signals.after_process.send() if self.commit: db.session.commit() flush_email_queue() else: db.session.rollback() except DatabaseError: db.session.rollback() handle_sqlalchemy_database_error() # this will re-raise an exception except Exception: # rollback to avoid errors as rendering the error page # within the indico layout may trigger an auto-flush db.session.rollback() raise logger.debug('Request successful') if res is None: return self._responseUtil.make_empty() response = self._responseUtil.make_response(res) if self.DENY_FRAMES: response.headers['X-Frame-Options'] = 'DENY' return response
def handler(prefix, path): path = posixpath.join('/', prefix, path) clearCache() # init fossil cache logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = [(key, [x.encode('utf-8') for x in values]) for key, values in request.form.iterlists()] query = urllib.urlencode(queryParams, doseq=1) # we only need/keep multiple values so we can properly validate the signature. # the legacy code below expects a dict with just the first value. # if you write a new api endpoint that needs multiple values get them from # ``request.values.getlist()`` directly queryParams = {key: values[0] for key, values in queryParams} else: # Parse the actual query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.args.iteritems()) query = request.query_string apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) cookieAuth = get_query_parameter(queryParams, ['ca', 'cookieauth'], 'no') == 'yes' signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) noCache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' onlyAuthed = get_query_parameter(queryParams, ['oa', 'onlyauthed'], 'no') == 'yes' scope = 'read:legacy_api' if request.method == 'GET' else 'write:legacy_api' try: oauth_valid, oauth_request = oauth.verify_request([scope]) if not oauth_valid and oauth_request and oauth_request.error_message != 'Bearer token not found.': raise BadRequest('OAuth error: {}'.format( oauth_request.error_message)) elif g.get( 'received_oauth_token' ) and oauth_request.error_message == 'Bearer token not found.': raise BadRequest('OAuth error: Invalid token') except ValueError: # XXX: Dirty hack to workaround a bug in flask-oauthlib that causes it # not to properly urlencode request query strings # Related issue (https://github.com/lepture/flask-oauthlib/issues/213) oauth_valid = False # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} responseUtil = ResponseUtil() is_response = False try: used_session = None if cookieAuth: used_session = session if not used_session.user: # ignore guest sessions used_session = None if apiKey or oauth_valid or not used_session: if not oauth_valid: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user aw = buildAW(ak, onlyPublic) else: # Access Token (OAuth) at = load_token(oauth_request.access_token.access_token) aw = buildAW(at, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cacheKey = normalizeQuery( path, query, remove=('_', 'ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) else: cacheKey = normalizeQuery(path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey else: # We authenticated using a session cookie. if Config.getInstance().getCSRFLevel() >= 2: token = request.headers.get( 'X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', 403) aw = AccessWrapper() if not onlyPublic: aw.setUser(used_session.avatar) userPrefix = 'user-{}_'.format(used_session.user.id) cacheKey = userPrefix + normalizeQuery( path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not aw.getUser(): raise HTTPAPIError('Not authenticated', 403) addToCache = not hook.NO_CACHE cache = GenericCache('HTTPAPI') cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: g.current_api_user = aw.user # Perform the actual exporting res = hook(aw) if isinstance(res, current_app.response_class): addToCache = False is_response = True result, extra, complete, typeMap = res, {}, True, {} elif isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get('cache_ttl') if ttl > 0: cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): responseUtil.status = e.getCode() if responseUtil.status == 405: responseUtil.headers[ 'Allow'] = 'GET' if request.method == 'POST' else 'POST'
def handler(prefix, path): path = posixpath.join('/', prefix, path) clearCache() # init fossil cache logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = [(key, [x.encode('utf-8') for x in values]) for key, values in request.form.iterlists()] query = urllib.urlencode(queryParams, doseq=1) # we only need/keep multiple values so we can properly validate the signature. # the legacy code below expects a dict with just the first value. # if you write a new api endpoint that needs multiple values get them from # ``request.values.getlist()`` directly queryParams = {key: values[0] for key, values in queryParams} else: # Parse the actual query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.args.iteritems()) query = request.query_string apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) cookieAuth = get_query_parameter(queryParams, ['ca', 'cookieauth'], 'no') == 'yes' signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) noCache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' onlyAuthed = get_query_parameter(queryParams, ['oa', 'onlyauthed'], 'no') == 'yes' scope = 'read:legacy_api' if request.method == 'GET' else 'write:legacy_api' try: oauth_valid, oauth_request = oauth.verify_request([scope]) if not oauth_valid and oauth_request and oauth_request.error_message != 'Bearer token not found.': raise BadRequest('OAuth error: {}'.format( oauth_request.error_message)) elif g.get( 'received_oauth_token' ) and oauth_request.error_message == 'Bearer token not found.': raise BadRequest('OAuth error: Invalid token') except ValueError: # XXX: Dirty hack to workaround a bug in flask-oauthlib that causes it # not to properly urlencode request query strings # Related issue (https://github.com/lepture/flask-oauthlib/issues/213) oauth_valid = False # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} status_code = None is_response = False try: used_session = None if cookieAuth: used_session = session if not used_session.user: # ignore guest sessions used_session = None if apiKey or oauth_valid or not used_session: if not oauth_valid: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user user = ak.user if ak and not onlyPublic else None else: # Access Token (OAuth) at = load_token(oauth_request.access_token.access_token) user = at.user if at and not onlyPublic else None # Get rid of API key in cache key if we did not impersonate a user if ak and user is None: cacheKey = normalizeQuery( path, query, remove=('_', 'ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) else: cacheKey = normalizeQuery(path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey else: # We authenticated using a session cookie. token = request.headers.get( 'X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', 403) user = used_session.user if not onlyPublic else None userPrefix = 'user-{}_'.format(used_session.user.id) cacheKey = userPrefix + normalizeQuery( path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not user: raise HTTPAPIError('Not authenticated', 403) addToCache = not hook.NO_CACHE cache = GenericCache('HTTPAPI') cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: g.current_api_user = user # Perform the actual exporting res = hook(user) if isinstance(res, current_app.response_class): addToCache = False is_response = True result, extra, complete, typeMap = res, {}, True, {} elif isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get('cache_ttl') if ttl > 0: cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError as e: error = e if e.getCode(): status_code = e.getCode() if result is None and error is None: # TODO: usage page raise NotFound else: if ak and error is None: # Commit only if there was an API key and no error norm_path, norm_query = normalizeQuery(path, query, remove=('signature', 'timestamp'), separate=True) uri = to_unicode('?'.join(filter(None, (norm_path, norm_query)))) ak.register_used(request.remote_addr, uri, not onlyPublic) db.session.commit() else: # No need to commit stuff if we didn't use an API key (nothing was written) # XXX do we even need this? db.session.rollback() # Log successful POST api requests if error is None and request.method == 'POST': logger.info('API request: %s?%s', path, query) if is_response: return result serializer = Serializer.create(dformat, query_params=queryParams, pretty=pretty, typeMap=typeMap, **hook.serializer_args) if error: if not serializer.schemaless: # if our serializer has a specific schema (HTML, ICAL, etc...) # use JSON, since it is universal serializer = Serializer.create('json') result = fossilize(error) else: if serializer.encapsulate: result = fossilize( HTTPAPIResult(result, path, query, ts, complete, extra), IHTTPAPIExportResultFossil) del result['_fossil'] try: data = serializer(result) response = current_app.make_response(data) content_type = serializer.get_response_content_type() if content_type: response.content_type = content_type if status_code: response.status_code = status_code return response except Exception: logger.exception('Serialization error in request %s?%s', path, query) raise
def process(self, params): if request.method not in HTTP_VERBS: # Just to be sure that we don't get some crappy http verb we don't expect raise BadRequest cfg = Config.getInstance() profile = cfg.getProfile() profile_name, res, textLog = '', '', [] self._startTime = datetime.now() g.rh = self if self.EVENT_FEATURE is not None: self._check_event_feature() textLog.append("%s : Database request started" % (datetime.now() - self._startTime)) logger.info(u'Request started: %s %s [IP=%s] [PID=%s]', request.method, request.relative_url, request.remote_addr, os.getpid()) is_error_response = False try: try: fossilize.clearCache() GenericMailer.flushQueue(False) self._check_auth(params) profile_name, res = self._do_process(profile) signals.after_process.send() if self.commit: if GenericMailer.has_queue(): # ensure we fail early (before sending out e-mails) # in case there are DB constraint violations, etc... db.enforce_constraints() GenericMailer.flushQueue(True) db.session.commit() else: db.session.rollback() except DatabaseError: handle_sqlalchemy_database_error() # this will re-raise an exception logger.info('Request successful') except Exception as e: db.session.rollback() res = self._getMethodByExceptionName(e)(e) if isinstance(e, HTTPException) and e.response is not None: res = e.response is_error_response = True totalTime = (datetime.now() - self._startTime) textLog.append('{} : Request ended'.format(totalTime)) # log request timing if profile and os.path.isfile(profile_name): rep = Config.getInstance().getTempDir() stats = pstats.Stats(profile_name) stats.sort_stats('cumulative', 'time', 'calls') stats.dump_stats(os.path.join(rep, 'IndicoRequestProfile.log')) output = StringIO.StringIO() sys.stdout = output stats.print_stats(100) sys.stdout = sys.__stdout__ s = output.getvalue() f = file(os.path.join(rep, 'IndicoRequest.log'), 'a+') f.write('--------------------------------\n') f.write('URL : {}\n'.format(request.url)) f.write('{} : start request\n'.format(self._startTime)) f.write('params:{}'.format(params)) f.write('\n'.join(textLog)) f.write(s) f.write('--------------------------------\n\n') f.close() if profile and profile_name and os.path.exists(profile_name): os.remove(profile_name) if self._responseUtil.call: return self._responseUtil.make_call() if is_error_response and isinstance(res, (current_app.response_class, Response)): # if we went through error handling code, responseUtil._status has been changed # so make_response() would fail return res # In case of no process needed, we should return empty string to avoid erroneous output # specially with getVars breaking the JS files. if not self._doProcess or res is None: return self._responseUtil.make_empty() return self._responseUtil.make_response(res)
def process(self): if request.method not in HTTP_VERBS: # Just to be sure that we don't get some crappy http verb we don't expect raise BadRequest profile = config.PROFILE profile_name, res, textLog = '', '', [] self._startTime = datetime.now() g.rh = self sentry_set_tags({'rh': self.__class__.__name__}) if self.EVENT_FEATURE is not None: self._check_event_feature() textLog.append("%s : Database request started" % (datetime.now() - self._startTime)) logger.info(u'Request started: %s %s [IP=%s] [PID=%s]', request.method, request.relative_url, request.remote_addr, os.getpid()) is_error_response = False try: try: fossilize.clearCache() GenericMailer.flushQueue(False) self._check_auth() profile_name, res = self._do_process(profile) signals.after_process.send() if self.commit: if GenericMailer.has_queue(): # ensure we fail early (before sending out e-mails) # in case there are DB constraint violations, etc... db.enforce_constraints() GenericMailer.flushQueue(True) db.session.commit() else: db.session.rollback() except DatabaseError: handle_sqlalchemy_database_error( ) # this will re-raise an exception logger.info('Request successful') except Exception as e: db.session.rollback() res = self._get_error_handler(e)(e) if isinstance(e, HTTPException) and e.response is not None: res = e.response is_error_response = True totalTime = (datetime.now() - self._startTime) textLog.append('{} : Request ended'.format(totalTime)) # log request timing if profile and os.path.isfile(profile_name): rep = config.TEMP_DIR stats = pstats.Stats(profile_name) stats.sort_stats('cumulative', 'time', 'calls') stats.dump_stats(os.path.join(rep, 'IndicoRequestProfile.log')) os.remove(profile_name) if is_error_response and isinstance( res, (current_app.response_class, Response)): # if we went through error handling code, responseUtil._status has been changed # so make_response() would fail return res # In case of no process needed, we should return empty string to avoid erroneous output # specially with getVars breaking the JS files. if not self._doProcess or res is None: return self._responseUtil.make_empty() response = self._responseUtil.make_response(res) if self.DENY_FRAMES: response.headers['X-Frame-Options'] = 'DENY' return response
def handler(prefix, path): path = posixpath.join('/', prefix, path) clearCache() # init fossil cache logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = [(key, [x.encode('utf-8') for x in values]) for key, values in request.form.iterlists()] query = urllib.urlencode(queryParams, doseq=1) # we only need/keep multiple values so we can properly validate the signature. # the legacy code below expects a dict with just the first value. # if you write a new api endpoint that needs multiple values get them from # ``request.values.getlist()`` directly queryParams = {key: values[0] for key, values in queryParams} else: # Parse the actual query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.args.iteritems()) query = request.query_string apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) cookieAuth = get_query_parameter(queryParams, ['ca', 'cookieauth'], 'no') == 'yes' signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) noCache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' onlyAuthed = get_query_parameter(queryParams, ['oa', 'onlyauthed'], 'no') == 'yes' scope = 'read:legacy_api' if request.method == 'GET' else 'write:legacy_api' try: oauth_valid, oauth_request = oauth.verify_request([scope]) if not oauth_valid and oauth_request and oauth_request.error_message != 'Bearer token not found.': raise BadRequest('OAuth error: {}'.format(oauth_request.error_message)) elif g.get('received_oauth_token') and oauth_request.error_message == 'Bearer token not found.': raise BadRequest('OAuth error: Invalid token') except ValueError: # XXX: Dirty hack to workaround a bug in flask-oauthlib that causes it # not to properly urlencode request query strings # Related issue (https://github.com/lepture/flask-oauthlib/issues/213) oauth_valid = False # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} responseUtil = ResponseUtil() is_response = False try: used_session = None if cookieAuth: used_session = session if not used_session.user: # ignore guest sessions used_session = None if apiKey or oauth_valid or not used_session: if not oauth_valid: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user user = ak.user if ak and not onlyPublic else None else: # Access Token (OAuth) at = load_token(oauth_request.access_token.access_token) user = at.user if at and not onlyPublic else None # Get rid of API key in cache key if we did not impersonate a user if ak and user is None: cacheKey = normalizeQuery(path, query, remove=('_', 'ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) else: cacheKey = normalizeQuery(path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey else: # We authenticated using a session cookie. token = request.headers.get('X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', 403) user = used_session.user if not onlyPublic else None userPrefix = 'user-{}_'.format(used_session.user.id) cacheKey = userPrefix + normalizeQuery(path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not user: raise HTTPAPIError('Not authenticated', 403) addToCache = not hook.NO_CACHE cache = GenericCache('HTTPAPI') cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: g.current_api_user = user # Perform the actual exporting res = hook(user) if isinstance(res, current_app.response_class): addToCache = False is_response = True result, extra, complete, typeMap = res, {}, True, {} elif isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get('cache_ttl') if ttl > 0: cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): responseUtil.status = e.getCode() if responseUtil.status == 405: responseUtil.headers['Allow'] = 'GET' if request.method == 'POST' else 'POST'