def normalizeQuery(path, query, remove=('signature',), separate=False): """Normalize request path and query so it can be used for caching and signing Returns a string consisting of path and sorted query string. Dynamic arguments like signature and timestamp are removed from the query string. """ queryParams = remove_lists(parse_qs(query)) if remove: for key in remove: queryParams.pop(key, None) sortedQuery = sorted(queryParams.items(), key=lambda x: x[0].lower()) if separate: return path, sortedQuery and urllib.urlencode(sortedQuery) elif sortedQuery: return '%s?%s' % (path, urllib.urlencode(sortedQuery)) else: return path
def handler(req, **params): ContextManager.destroy() logger = Logger.get('httpapi') path, query = req.URLFields['PATH_INFO'], req.URLFields['QUERY_STRING'] if req.method == 'POST': # Convert POST data to a query string queryParams = dict(req.form) for key, value in queryParams.iteritems(): queryParams[key] = [str(value)] query = urllib.urlencode(remove_lists(queryParams)) else: # Parse the actual query string queryParams = parse_qs(query) dbi = DBMgr.getInstance() dbi.startRequest() minfo = HelperMaKaCInfo.getMaKaCInfoInstance() if minfo.getRoomBookingModuleActive(): Factory.getDALManager().connect() 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' # 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 apache.SERVER_RETURN, apache.HTTP_NOT_FOUND # Disable caching if we are not just retrieving data (or the hook requires it) if req.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} try: session = None if cookieAuth: session = getSessionForReq(req) if not session.getUser(): # ignore guest sessions session = None if apiKey or not session: # 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, req, 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 = req.headers_in.get('X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', apache.HTTP_FORBIDDEN) aw = AccessWrapper() if not onlyPublic: aw.setUser(session.getUser()) userPrefix = 'user-' + session.getUser().getId() + '_' 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', apache.HTTP_FORBIDDEN) obj = None 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: # Perform the actual exporting res = hook(aw, req) if 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 = HelperMaKaCInfo.getMaKaCInfoInstance().getAPICacheTTL() cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): req.status = e.getCode() if req.status == apache.HTTP_METHOD_NOT_ALLOWED: req.headers_out['Allow'] = 'GET' if req.method == 'POST' else 'POST'
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)
def handler(req, **params): ContextManager.destroy() logger = Logger.get('httpapi') path, query = req.URLFields['PATH_INFO'], req.URLFields['QUERY_STRING'] if req.method == 'POST': # Convert POST data to a query string queryParams = dict(req.form) for key, value in queryParams.iteritems(): queryParams[key] = [str(value)] query = urllib.urlencode(remove_lists(queryParams)) else: # Parse the actual query string queryParams = parse_qs(query) dbi = DBMgr.getInstance() dbi.startRequest() minfo = HelperMaKaCInfo.getMaKaCInfoInstance() if minfo.getRoomBookingModuleActive(): Factory.getDALManager().connect() mode = path.split('/')[1] apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) no_cache = 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' # Disable caching if we are not exporting if mode != 'export': no_cache = True # Get our handler function and its argument and response type func, dformat = HTTPAPIHook.parseRequest(path, queryParams) if func is None or dformat is None: raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND ak = error = result = None ts = int(time.time()) typeMap = {} try: # 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, req, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cache_key = normalizeQuery(path, query, remove=('ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache')) else: cache_key = normalizeQuery(path, query, remove=('signature', 'timestamp', 'nc', 'nocache')) if signature: # in case the request was signed, store the result under a different key cache_key = 'signed_' + cache_key obj = None addToCache = True cache = GenericCache('HTTPAPI') cache_key = RE_REMOVE_EXTENSION.sub('', cache_key) if not no_cache: obj = cache.get(cache_key) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: # Perform the actual exporting res = func(aw, req) if 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 = HelperMaKaCInfo.getMaKaCInfoInstance().getAPICacheTTL() cache.set(cache_key, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): req.status = e.getCode() if req.status == apache.HTTP_METHOD_NOT_ALLOWED: req.headers_out[ 'Allow'] = 'GET' if req.method == 'POST' else 'POST'
def handler(req, **params): ContextManager.destroy() logger = Logger.get("httpapi") path, query = req.URLFields["PATH_INFO"], req.URLFields["QUERY_STRING"] if req.method == "POST": # Convert POST data to a query string queryParams = dict(req.form) for key, value in queryParams.iteritems(): queryParams[key] = [str(value)] query = urllib.urlencode(remove_lists(queryParams)) else: # Parse the actual query string queryParams = parse_qs(query) dbi = DBMgr.getInstance() dbi.startRequest() minfo = HelperMaKaCInfo.getMaKaCInfoInstance() if minfo.getRoomBookingModuleActive(): Factory.getDALManager().connect() 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" # 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 apache.SERVER_RETURN, apache.HTTP_NOT_FOUND # Disable caching if we are not just retrieving data (or the hook requires it) if req.method == "POST" or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} try: sessionUser = getSessionForReq(req).getUser() if cookieAuth else None if apiKey or not sessionUser: # 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, req, 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. # Reject POST for security reasons (CSRF) if req.method == "POST": raise HTTPAPIError("Cannot POST when using cookie authentication", apache.HTTP_FORBIDDEN) aw = AccessWrapper() if not onlyPublic: aw.setUser(sessionUser) userPrefix = "user-" + sessionUser.getId() + "_" cacheKey = userPrefix + normalizeQuery( path, query, remove=("nc", "nocache", "ca", "cookieauth", "oa", "onlyauthed") ) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not aw.getUser(): raise HTTPAPIError("Not authenticated", apache.HTTP_FORBIDDEN) obj = None 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: # Perform the actual exporting res = hook(aw, req) if 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 = HelperMaKaCInfo.getMaKaCInfoInstance().getAPICacheTTL() cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): req.status = e.getCode() if req.status == apache.HTTP_METHOD_NOT_ALLOWED: req.headers_out["Allow"] = "GET" if req.method == "POST" else "POST"
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)
def handler(req, **params): ContextManager.destroy() logger = Logger.get('httpapi') path, query = req.URLFields['PATH_INFO'], req.URLFields['QUERY_STRING'] if req.method == 'POST': # Convert POST data to a query string queryParams = dict(req.form) for key, value in queryParams.iteritems(): queryParams[key] = [str(value)] query = urllib.urlencode(remove_lists(queryParams)) else: # Parse the actual query string queryParams = parse_qs(query) dbi = DBMgr.getInstance() dbi.startRequest() minfo = HelperMaKaCInfo.getMaKaCInfoInstance() if minfo.getRoomBookingModuleActive(): Factory.getDALManager().connect() mode = path.split('/')[1] apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) no_cache = 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' # Disable caching if we are not exporting if mode != 'export': no_cache = True # Get our handler function and its argument and response type func, dformat = HTTPAPIHook.parseRequest(path, queryParams) if func is None or dformat is None: raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND ak = error = result = None ts = int(time.time()) typeMap = {} try: # 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, req, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cache_key = normalizeQuery(path, query, remove=('ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache')) else: cache_key = normalizeQuery(path, query, remove=('signature', 'timestamp', 'nc', 'nocache')) if signature: # in case the request was signed, store the result under a different key cache_key = 'signed_' + cache_key obj = None addToCache = True cache = GenericCache('HTTPAPI') cache_key = RE_REMOVE_EXTENSION.sub('', cache_key) if not no_cache: obj = cache.get(cache_key) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: # Perform the actual exporting res = func(aw, req) if 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 = HelperMaKaCInfo.getMaKaCInfoInstance().getAPICacheTTL() cache.set(cache_key, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): req.status = e.getCode() if req.status == apache.HTTP_METHOD_NOT_ALLOWED: req.headers_out['Allow'] = 'GET' if req.method == 'POST' else 'POST'