def _process(self): filename = "%s-Categ.atom" % self._target.getName().replace("/", "") hook = CategoryEventHook({'from': 'today'}, 'categ', {'idlist': self._target.getId(), 'dformat': 'atom'}) res = hook(self.getAW()) resultFossil = {'results': res[0], 'url': str(self._uh.getURL(self._target))} serializer = Serializer.create('atom') return send_file(filename, StringIO(serializer(resultFossil).encode('utf-8')), 'ATOM')
def _process(self): filename = "%s-Categ.atom" % self._target.getName().replace("/", "") hook = CategoryEventHook({'from': 'today'}, 'categ', {'idlist': self._target.getId(), 'dformat': 'atom'}) res = hook(self.getAW()) resultFossil = {'results': res[0], 'url': str(self._uh.getURL(self._target))} serializer = Serializer.create('atom') return send_file(filename, StringIO(serializer(resultFossil)), 'ATOM')
def _process(self): if not self._target.isScheduled(): raise NoReportError( _("You cannot export the contribution with id %s because it is not scheduled" ) % self._target.getId()) filename = "%s-Contribution.ics" % self._target.getTitle() hook = ContributionHook({}, 'contribution', { 'event': self._conf.getId(), 'idlist': self._contrib.getId(), 'dformat': 'ics' }) res = hook(self.getAW(), self._req) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') data = serializer(resultFossil) self._req.headers_out["Content-Length"] = "%s" % len(data) cfg = Config.getInstance() mimetype = cfg.getFileTypeMimeType("ICAL") self._req.content_type = """%s""" % (mimetype) self._req.headers_out[ "Content-Disposition"] = """inline; filename="%s\"""" % cleanHTMLHeaderFilename( filename) return data
def _process(self): filename = "%s-Categ.ics" % self._target.getName().replace("/", "") hook = CategoryEventHook({}, 'categ', {'idlist': self._target.getId(), 'dformat': 'ics'}) res = hook(self.getAW()) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') return send_file(filename, StringIO(serializer(resultFossil)), 'ICAL')
def get_session_ical_file(sess): from indico.web.http_api.metadata.serializer import Serializer data = { 'results': serialize_session_for_ical(sess) if sess.start_dt and sess.end_dt else [] } serializer = Serializer.create('ics') return BytesIO(serializer(data))
def _process( self ): filename = "%s-Session.ics"%self._session.getTitle() hook = SessionHook({}, 'session', {'event': self._conf.getId(), 'idlist':self._session.getId(), 'dformat': 'ics'}) res = hook(self.getAW()) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') return send_file(filename, StringIO(serializer(resultFossil)), 'ICAL')
def _process(self): filename = "%s-Event.ics" % self._target.getTitle() hook = CategoryEventHook({'detail': self._detailLevel}, 'event', {'idlist': self._conf.getId(), 'dformat': 'ics'}) res = hook(self.getAW()) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') return send_file(filename, StringIO(serializer(resultFossil)), 'ICAL')
def _process(self): filename = "%s-Session.ics" % self._session.getTitle() hook = SessionHook({}, 'session', { 'event': self._conf.getId(), 'idlist': self._session.getId(), 'dformat': 'ics' }) res = hook(self.getAW()) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') return send_file(filename, StringIO(serializer(resultFossil)), 'ICAL')
def _process( self ): if not self._target.isScheduled(): raise NoReportError(_("You cannot export the contribution with id %s because it is not scheduled")%self._target.getId()) filename = "%s-Contribution.ics"%self._target.getTitle() hook = ContributionHook({}, 'contribution', {'event': self._conf.getId(), 'idlist':self._contrib.getId(), 'dformat': 'ics'}) res = hook(self.getAW()) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') return send_file(filename, StringIO(serializer(resultFossil)), 'ICAL')
def _process(self): hook = CategoryEventHook({"from": ["today"]}, "categ", {"idlist": self._target.getId(), "dformat": "atom"}) res = hook(self.getAW(), self._req) resultFossil = {"results": res[0], "url": str(self._uh.getURL(self._target))} serializer = Serializer.create("atom") data = serializer(resultFossil) cfg = Config.getInstance() mimetype = cfg.getFileTypeMimeType("ATOM") self._req.content_type = """%s""" % (mimetype) return data
def _process( self ): hook = CategoryEventHook({'from': ['today']}, 'categ', {'idlist':self._target.getId(), 'dformat': 'atom'}) res = hook(self.getAW(), self._req) resultFossil = {'results': res[0], 'url': str(self._uh.getURL(self._target))} serializer = Serializer.create('atom') data = serializer(resultFossil) cfg = Config.getInstance() mimetype = cfg.getFileTypeMimeType( "ATOM" ) self._req.content_type = """%s"""%(mimetype) return data
def _process( self ): filename = "%s-Session.ics"%self._session.getTitle() hook = SessionHook({}, 'session', {'event': self._conf.getId(), 'idlist':self._session.getId(), 'dformat': 'ics'}) res = hook(self.getAW(), self._req) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') data = serializer(resultFossil) self._req.headers_out["Content-Length"] = "%s"%len(data) cfg = Config.getInstance() mimetype = cfg.getFileTypeMimeType( "ICAL" ) self._req.content_type = """%s"""%(mimetype) self._req.headers_out["Content-Disposition"] = """inline; filename="%s\""""%cleanHTMLHeaderFilename(filename) return data
def _process( self ): filename = "%s-Categ.ics"%self._target.getName().replace("/","") hook = CategoryEventHook({}, 'categ', {'idlist':self._target.getId(), 'dformat': 'ics'}) res = hook(self.getAW(), self._req) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') data = serializer(resultFossil) self._req.headers_out["Content-Length"] = "%s"%len(data) cfg = Config.getInstance() mimetype = cfg.getFileTypeMimeType( "ICAL" ) self._req.content_type = """%s"""%(mimetype) self._req.headers_out["Content-Disposition"] = """inline; filename="%s\""""%cleanHTMLHeaderFilename(filename) return data
def _process(self): if not self._target.isScheduled(): raise NoReportError( _("You cannot export the contribution with id {0} because it is not scheduled" ).format(self._target.getId())) filename = "{0}-Contribution.ics".format(self._target.getTitle()) hook = ContributionHook({}, 'contribution', { 'event': self._conf.getId(), 'idlist': self._contrib.getId(), 'dformat': 'ics' }) res = hook(self.getAW()) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') return send_file(filename, StringIO(serializer(resultFossil)), 'ICAL')
def _process(self): if not self._target.isScheduled(): raise NoReportError( _("You cannot export the contribution with id {0} because it is not scheduled").format( self._target.getId() ) ) filename = "{0}-Contribution.ics".format(self._target.getTitle()) hook = ContributionHook( {}, "contribution", {"event": self._conf.getId(), "idlist": self._contrib.getId(), "dformat": "ics"} ) res = hook(self.getAW()) resultFossil = {"results": res[0]} serializer = Serializer.create("ics") return send_file(filename, StringIO(serializer(resultFossil)), "ICAL")
def _process( self ): if not self._target.isScheduled(): raise NoReportError(_("You cannot export the contribution with id %s because it is not scheduled")%self._target.getId()) filename = "%s-Contribution.ics"%self._target.getTitle() hook = ContributionHook({}, 'contribution', {'event': self._conf.getId(), 'idlist':self._contrib.getId(), 'dformat': 'ics'}) res = hook(self.getAW(), self._req) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') data = serializer(resultFossil) self._req.headers_out["Content-Length"] = "%s"%len(data) cfg = Config.getInstance() mimetype = cfg.getFileTypeMimeType( "ICAL" ) self._req.content_type = """%s"""%(mimetype) self._req.headers_out["Content-Disposition"] = """inline; filename="%s\""""%cleanHTMLHeaderFilename(filename) return data
def _process(self): filename = "%s-Session.ics" % self._session.getTitle() hook = SessionHook({}, 'session', { 'event': self._conf.getId(), 'idlist': self._session.getId(), 'dformat': 'ics' }) res = hook(self.getAW(), self._req) resultFossil = {'results': res[0]} serializer = Serializer.create('ics') data = serializer(resultFossil) self._req.headers_out["Content-Length"] = "%s" % len(data) cfg = Config.getInstance() mimetype = cfg.getFileTypeMimeType("ICAL") self._req.content_type = """%s""" % (mimetype) self._req.headers_out[ "Content-Disposition"] = """inline; filename="%s\"""" % cleanHTMLHeaderFilename( filename) return data
def get_session_ical_file(sess): from indico.web.http_api.metadata.serializer import Serializer data = {'results': serialize_session_for_ical(sess) if sess.start_dt and sess.end_dt else []} serializer = Serializer.create('ics') return BytesIO(serializer(data))
self._attachment = Attachment.get(int(self._pathParams['res'])) if not self._attachment: raise HTTPAPIError("File not found", 404) def export_file(self, user): if self._attachment.type != AttachmentType.file: raise HTTPAPIError("Resource is not a file", 404) return self._attachment.file.send() def _has_access(self, user): return self._attachment.can_access(user) class FileSerializer(Serializer): encapsulate = False schemaless = False def _execute(self, fdata): return fdata def get_response_content_type(self): # we already have a response with the correct headers return None Serializer.register('bin', FileSerializer)
pass # retry else: break else: # No need to commit stuff if we didn't use an API key # (nothing was written) if minfo.getRoomBookingModuleActive(): Factory.getDALManager().rollback() Factory.getDALManager().disconnect() dbi.endRequest(False) # Log successful POST api requests if error is None and req.method == 'POST': logger.info('API request: %s?%s' % (path, query)) serializer = Serializer.create(dformat, pretty=pretty, typeMap=typeMap, **remove_lists(queryParams)) 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) serializer.set_headers(req)
pass # retry else: break else: # No need to commit stuff if we didn't use an API key # (nothing was written) if minfo.getRoomBookingModuleActive(): Factory.getDALManager().rollback() Factory.getDALManager().disconnect() dbi.endRequest(False) # Log successful POST api requests if error is None and request.method == 'POST': logger.info('API request: %s?%s' % (path, query)) serializer = Serializer.create(dformat, pretty=pretty, typeMap=typeMap, **queryParams) 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) serializer.set_headers(responseUtil)
## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Indico;if not, see <http://www.gnu.org/licenses/>. """ json-based fossil serializer """ # indico imports from indico.util import json # module imports from indico.web.http_api.metadata.serializer import Serializer class JSONSerializer(Serializer): """ Does basically direct translation from the fossi """ _mime = "application/json" def _execute(self, fossil): return json.dumps(fossil, pretty=self.pretty) Serializer.register("json", JSONSerializer)
# WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Indico; if not, see <http://www.gnu.org/licenses/>. """ json-based fossil serializer """ # indico imports from indico.util import json # module imports from indico.web.http_api.metadata.serializer import Serializer class JSONSerializer(Serializer): """ Does basically direct translation from the fossi """ _mime = 'application/json' def _execute(self, fossil): return json.dumps(fossil, pretty=self.pretty) Serializer.register('json', JSONSerializer)
uri = to_unicode('?'.join(filter(None, (norm_path, norm_query)))) ak.register_used(request.remote_addr, uri, not onlyPublic) transaction.commit() else: # No need to commit stuff if we didn't use an API key (nothing was written) # XXX do we even need this? transaction.abort() # 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']
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) transaction.commit() else: # No need to commit stuff if we didn't use an API key (nothing was written) # XXX do we even need this? transaction.abort() # Log successful POST api requests if error is None and request.method == 'POST': logger.info('API request: %s?%s' % (path, query)) 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) serializer.set_headers(responseUtil)
elif type(v) == dict: elem.append(self._xmlForFossil(v)) else: txt = self._convert(v) try: elem.text = txt except Exception, e: Logger.get('xmlSerializer').error( 'Setting XML text value failed: %s (id: %s)' % (e, id)) return felement def _execute(self, fossil, xml_declaration=True): if type(fossil) == list: # collection of fossils doc = etree.ElementTree(etree.Element("collection")) for elem in fossil: self._xmlForFossil(elem, doc) result = doc else: result = self._xmlForFossil(fossil) return etree.tostring(result, pretty_print=self.pretty, xml_declaration=xml_declaration, encoding='utf-8') Serializer.register('xml', XMLSerializer)
else: subelem = etree.SubElement(elem, "item") subelem.text = self._convert(subv) elif isinstance(v, dict): elem.append(self._xmlForFossil(v)) else: txt = self._convert(v) try: elem.text = txt except Exception: Logger.get("xmlSerializer").exception( "Setting XML text value failed (id: {}, value {!r})".format(id, txt) ) return felement def _execute(self, fossil, xml_declaration=True): if type(fossil) == list: # collection of fossils doc = etree.ElementTree(etree.Element("collection")) for elem in fossil: self._xmlForFossil(elem, doc) result = doc else: result = self._xmlForFossil(fossil) return etree.tostring(result, pretty_print=self.pretty, xml_declaration=xml_declaration, encoding="utf-8") Serializer.register("xml", XMLSerializer)
elem.append(self._xmlForFossil(subv)) else: subelem = etree.SubElement(elem, 'item') subelem.text = self._convert(subv) elif isinstance(v, dict): elem.append(self._xmlForFossil(v)) else: txt = self._convert(v) try: elem.text = txt except Exception: Logger.get('xmlSerializer').exception('Setting XML text value failed (id: %s, value %r)', id, txt) return felement def _execute(self, fossil, xml_declaration=True): if type(fossil) == list: # collection of fossils doc = etree.ElementTree(etree.Element("collection")) for elem in fossil: self._xmlForFossil(elem, doc) result = doc else: result = self._xmlForFossil(fossil) return etree.tostring(result, pretty_print=self.pretty, xml_declaration=xml_declaration, encoding='utf-8') Serializer.register('xml', XMLSerializer)
# # Indico is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Indico; if not, see <http://www.gnu.org/licenses/>. """ json-based fossil serializer """ # indico imports from indico.util import json # module imports from indico.web.http_api.metadata.serializer import Serializer class JSONSerializer(Serializer): """ Does basically direct translation from the fossi """ _mime = 'application/json' def _execute(self, fossil): return json.dumps(fossil, pretty=self.pretty) Serializer.register('json', JSONSerializer)
return self._file.canAccess(aw) @classmethod def _matchPath(cls, path): if not hasattr(cls, '_RE'): cls._RE = re.compile(r'/' + cls.PREFIX + '/event/' + cls.RE + r'\.(\w+)$') return cls._RE.match(path) class FileSerializer(Serializer): encapsulate = False schemaless = False def _execute(self, fdata): cfg = Config.getInstance() self._mime = cfg.getFileTypeMimeType(fdata['ftype']) if cfg.getUseXSendFile(): return "" else: return fdata['data'] def set_headers(self, req): super(FileSerializer, self).set_headers(req) set_file_headers(req, **self._obj) Serializer.register('bin', FileSerializer)
def handler(prefix, path): path = posixpath.join('/', prefix, path) logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = list(request.form.lists()) query = 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 = {key: value for key, value in request.args.items()} query = request.query_string.decode() 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' oauth_token = None if request.headers.get('Authorization', '').lower().startswith('bearer '): try: oauth_token = require_oauth.acquire_token([scope]) except OAuth2Error as exc: raise BadRequest(f'OAuth error: {exc}') # 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_token or not used_session: auth_token = None if not oauth_token: # 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) user = oauth_token.user if 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', 'access_token')) else: cacheKey = normalizeQuery( path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed', 'access_token')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey if auth_token: # if oauth was used, we also make the cache key unique cacheKey = f'oauth-{auth_token.id}_{cacheKey}' else: # We authenticated using a session cookie. # XXX: This is not used anymore within indico and should be removed whenever we rewrite # the code here. 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 cacheKey = normalizeQuery(path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) if user is not None: # We *always* prefix the cache key with the user ID so we never get an overlap between # authenticated and unauthenticated requests cacheKey = f'user-{user.id}_{cacheKey}' sentry_sdk.set_user({ 'id': user.id, 'email': user.email, 'name': user.full_name, 'source': 'http_api' }) else: cacheKey = f'public_{cacheKey}' # 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 cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = API_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: API_CACHE.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError as e: error = e if e.code: status_code = e.code if result is None and error is None: 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 = '?'.join(_f for _f in (norm_path, norm_query) if _f) 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 = {'message': error.message} elif serializer.encapsulate: result = HTTPAPIResultSchema().dump( HTTPAPIResult(result, path, query, ts, extra)) 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 get_contribution_ical_file(contrib): data = {'results': serialize_contribution_for_ical(contrib)} serializer = Serializer.create('ics') return BytesIO(serializer(data))
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' if not request.headers.get('Authorization', '').lower().startswith('basic '): 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 else: 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
except ConflictError: pass # retry else: break else: # No need to commit stuff if we didn't use an API key # (nothing was written) dbi.endRequest(False) LDAPConnector.destroy() # Log successful POST api requests if error is None and request.method == 'POST': logger.info('API request: %s?%s' % (path, query)) serializer = Serializer.create(dformat, 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) serializer.set_headers(responseUtil)