def fetch(app, environ, request, uri): rv = list(app.db.comments.fetch(uri)) if not rv: raise NotFound for item in rv: key = item['email'] or item['remote_addr'] val = app.cache.get('hash', key.encode('utf-8')) if val is None: val = str(pbkdf2(key, app.salt, 1000, 6)) app.cache.set('hash', key.encode('utf-8'), val) item['hash'] = val for key in set(item.keys()) - FIELDS: item.pop(key) if request.args.get('plain', '0') == '0': for item in rv: item['text'] = app.markdown(item['text']) return Response(json.dumps(rv), 200, content_type='application/json')
def fetch(app, environ, request, uri): rv = list(app.db.comments.fetch(uri)) if not rv: raise NotFound for item in rv: key = item['email'] or item['remote_addr'] val = app.cache.get('hash', key) if val is None: val = str(pbkdf2(key, app.salt, 1000, 6)) app.cache.set('hash', key, val) item['hash'] = val for key in set(item.keys()) - FIELDS: item.pop(key) if request.args.get('plain', '0') == '0': for item in rv: item['text'] = app.markdown(item['text']) return Response(json.dumps(rv), 200, content_type='application/json')
def new(app, environ, request, uri): data = request.get_json() for field in set(data.keys()) - set(['text', 'author', 'website', 'email', 'parent']): data.pop(field) if "text" not in data or data["text"] is None or len(data["text"]) < 3: raise BadRequest("no text given") if "id" in data and not isinstance(data["id"], int): raise BadRequest("parent id must be an integer") if len(data.get("email") or "") > 254: raise BadRequest("http://tools.ietf.org/html/rfc5321#section-4.5.3") for field in ("author", "email"): if data.get(field): data[field] = cgi.escape(data[field]) data['mode'] = (app.conf.getboolean('moderation', 'enabled') and 2) or 1 data['remote_addr'] = utils.anonymize(str(request.remote_addr)) with app.lock: if uri not in app.db.threads: for host in app.conf.getiter('general', 'host'): with http.curl('GET', host, uri) as resp: if resp and resp.status == 200: title = parse.title(resp.read()) break else: return Response('URI does not exist', 404) app.db.threads.new(uri, title) logger.info('new thread: %s -> %s', uri, title) else: title = app.db.threads[uri].title try: with app.lock: rv = app.db.comments.add(uri, data) except db.IssoDBException: raise Forbidden host = list(app.conf.getiter('general', 'host'))[0].rstrip("/") href = host + uri + "#isso-%i" % rv["id"] deletion = host + environ["SCRIPT_NAME"] + "/delete/" + app.sign(str(rv["id"])) activation = None if app.conf.getboolean('moderation', 'enabled'): activation = host + environ["SCRIPT_NAME"] + "/activate/" + app.sign(str(rv["id"])) app.notify(title, notify.format(rv, href, utils.anonymize(str(request.remote_addr)), activation_key=activation, deletion_key=deletion)) # save checksum of text into cookie, so mallory can't modify/delete a comment, if # he add a comment, then removed it but not the signed cookie. checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest() rv["text"] = app.markdown(rv["text"]) rv["hash"] = str(pbkdf2(rv['email'] or rv['remote_addr'], app.salt, 1000, 6)) app.cache.set('hash', (rv['email'] or rv['remote_addr']).encode('utf-8'), rv['hash']) for key in set(rv.keys()) - FIELDS: rv.pop(key) # success! logger.info('comment created: %s', json.dumps(rv)) cookie = functools.partial(dump_cookie, value=app.sign([rv["id"], checksum]), max_age=app.conf.getint('general', 'max-age')) resp = Response(json.dumps(rv), 202 if rv["mode"] == 2 else 201, content_type='application/json') resp.headers.add("Set-Cookie", cookie(str(rv["id"]))) resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"])) return resp
def new(app, environ, request, uri): data = request.get_json() for field in set(data.keys()) - set( ['text', 'author', 'website', 'email', 'parent']): data.pop(field) if not data.get("text"): raise BadRequest("no text given") if "id" in data and not isinstance(data["id"], int): raise BadRequest("parent id must be an integer") for field in ("author", "email"): if data.get(field): data[field] = cgi.escape(data[field]) data['mode'] = (app.conf.getboolean('moderation', 'enabled') and 2) or 1 data['remote_addr'] = utils.anonymize(str(request.remote_addr)) with app.lock: if uri not in app.db.threads: for host in app.conf.getiter('general', 'host'): with http.curl('GET', host, uri) as resp: if resp and resp.status == 200: title = parse.title(resp.read()) break else: return Response('URI does not exist', 404) app.db.threads.new(uri, title) logger.info('new thread: %s -> %s', uri, title) else: title = app.db.threads[uri].title try: with app.lock: rv = app.db.comments.add(uri, data) except db.IssoDBException: raise Forbidden host = list(app.conf.getiter('general', 'host'))[0].rstrip("/") href = host + uri + "#isso-%i" % rv["id"] deletion = host + environ["SCRIPT_NAME"] + "/delete/" + app.sign( str(rv["id"])) activation = None if app.conf.getboolean('moderation', 'enabled'): activation = host + environ["SCRIPT_NAME"] + "/activate/" + app.sign( str(rv["id"])) app.notify( title, notify.format(rv, href, utils.anonymize(str(request.remote_addr)), activation_key=activation, deletion_key=deletion)) # save checksum of text into cookie, so mallory can't modify/delete a comment, if # he add a comment, then removed it but not the signed cookie. checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest() rv["text"] = app.markdown(rv["text"]) rv["hash"] = str( pbkdf2(rv.get('email') or rv['remote_addr'], app.salt, 1000, 6)) for key in set(rv.keys()) - FIELDS: rv.pop(key) # success! logger.info('comment created: %s', json.dumps(rv)) resp = Response(json.dumps(rv), 202 if rv["mode"] == 2 else 201, content_type='application/json') resp.set_cookie(str(rv["id"]), app.sign([rv["id"], checksum]), max_age=app.conf.getint('general', 'max-age')) return resp