예제 #1
0
    def delete(self, environ, request, id, key=None):

        data = request.get_json()

        for field in set(data.keys()) - API.ACCEPT:
            data.pop(field)

        valid, reason = self.authenticate(data)
        if not valid:
            return BadRequest(reason)

        item = self.comments.get(id)
        if item is None:
            raise NotFound

        self.authorize(id, data, item, request.cookies.get(str(id), ''))

        self.cache.delete('hash', (item['email'] or item['remote_addr']).encode('utf-8'))

        with self.isso.lock:
            rv = self.comments.delete(id)

        if rv:
            for key in set(rv.keys()) - API.FIELDS:
                rv.pop(key)

        self.signal("comments.delete", id)

        resp = JSON(rv, 200)
        cookie = functools.partial(dump_cookie, expires=0, max_age=0)
        resp.headers.add("Set-Cookie", cookie(str(id)))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
        return resp
예제 #2
0
파일: comments.py 프로젝트: vonHabsi/isso
    def delete(self, environ, request, id, key=None):

        data = request.get_json()

        for field in set(data.keys()) - API.ACCEPT:
            data.pop(field)

        valid, reason = self.authenticate(data)
        if not valid:
            return BadRequest(reason)

        item = self.comments.get(id)
        if item is None:
            raise NotFound

        self.authorize(id, data, item, request.cookies.get(str(id), ''))

        self.cache.delete('hash', (item['email']
                                   or item['remote_addr']).encode('utf-8'))

        with self.isso.lock:
            rv = self.comments.delete(id)

        if rv:
            for key in set(rv.keys()) - API.FIELDS:
                rv.pop(key)

        self.signal("comments.delete", id)

        resp = JSON(rv, 200)
        cookie = functools.partial(dump_cookie, expires=0, max_age=0)
        resp.headers.add("Set-Cookie", cookie(str(id)))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
        return resp
예제 #3
0
    def delete(self, environ, request, id, key=None):

        try:
            rv = self.isso.unsign(request.cookies.get(str(id), ""))
        except (SignatureExpired, BadSignature):
            raise Forbidden
        else:
            if rv[0] != id:
                raise Forbidden

            # verify checksum, mallory might skip cookie deletion when he deletes a comment
            if rv[1] != sha1(self.comments.get(id)["text"]):
                raise Forbidden

        item = self.comments.get(id)

        if item is None:
            raise NotFound

        self.cache.delete('hash', (item['email'] or item['remote_addr']).encode('utf-8'))

        with self.isso.lock:
            rv = self.comments.delete(id)

        if rv:
            for key in set(rv.keys()) - API.FIELDS:
                rv.pop(key)

        self.signal("comments.delete", id)

        resp = JSON(rv, 200)
        cookie = functools.partial(dump_cookie, expires=0, max_age=0)
        resp.headers.add("Set-Cookie", cookie(str(id)))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
        return resp
예제 #4
0
파일: comments.py 프로젝트: jsmelquist/isso
    def delete(self, environ, request, id, key=None):

        try:
            rv = self.isso.unsign(request.cookies.get(str(id), ""))
        except (SignatureExpired, BadSignature):
            raise Forbidden
        else:
            if rv[0] != id:
                raise Forbidden

            # verify checksum, mallory might skip cookie deletion when he deletes a comment
            if rv[1] != sha1(self.comments.get(id)["text"]):
                raise Forbidden

        item = self.comments.get(id)

        if item is None:
            raise NotFound

        self.cache.delete('hash', (item['email'] or item['remote_addr']).encode('utf-8'))

        with self.isso.lock:
            rv = self.comments.delete(id)

        if rv:
            for key in set(rv.keys()) - API.FIELDS:
                rv.pop(key)

        self.signal("comments.delete", id)

        resp = JSON(rv, 200)
        cookie = functools.partial(dump_cookie, expires=0, max_age=0)
        resp.headers.add("Set-Cookie", cookie(str(id)))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
        return resp
예제 #5
0
파일: __init__.py 프로젝트: rhn/marginalia
    def show(self, environ, request):

        rv = {
            "version": dist.version,
            "host": str(local("host")),
            "origin": str(local("origin")),
            "moderation": self.moderation,
        }

        return Response(json.dumps(rv), 200, content_type="application/json")
예제 #6
0
파일: __init__.py 프로젝트: Batur24/isso
    def show(self, environ, request):

        rv = {
            "version": dist.version,
            "host": str(local("host")),
            "origin": str(local("origin")),
            "moderation": self.moderation,
        }

        return Response(json.dumps(rv), 200, content_type="application/json")
예제 #7
0
def host(name):
    """
    Parse :param name: into `httplib`-compatible host:port.

    >>> host("http://example.tld/")
    ('example.tld', 80, False)
    >>> host("https://example.tld/")
    ('example.tld', 443, True)
    >>> host("example.tld")
    ('example.tld', 80, False)
    >>> host("example.tld:42")
    ('example.tld', 42, False)
    >>> host("https://example.tld:80/")
    ('example.tld', 80, True)
    """

    if not (isinstance(name, string_types)):
        name = str(name)

    if not name.startswith(("http://", "https://")):
        name = "http://" + name

    rv = urlparse(name)
    if rv.scheme == "https" and rv.port is None:
        return (rv.netloc, 443, True)
    return (rv.netloc.rsplit(":")[0], rv.port or 80, rv.scheme == "https")
예제 #8
0
파일: comments.py 프로젝트: alemic/isso
    def fetch(self, environ, request, uri):

        rv = list(self.comments.fetch(uri))
        if not rv:
            raise NotFound

        for item in rv:

            key = item['email'] or item['remote_addr']
            val = self.cache.get('hash', key.encode('utf-8'))

            if val is None:
                val = str(pbkdf2(key, self.isso.salt, 1000, 6))
                self.cache.set('hash', key.encode('utf-8'), val)

            item['hash'] = val

            for key in set(item.keys()) - API.FIELDS:
                item.pop(key)

        if request.args.get('plain', '0') == '0':
            for item in rv:
                item['text'] = markdown(item['text'])

        return JSON(json.dumps(rv), 200)
예제 #9
0
파일: comment.py 프로젝트: waytai/isso
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')
예제 #10
0
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')
예제 #11
0
파일: comments.py 프로젝트: vonHabsi/isso
    def edit(self, environ, request, id):

        data = request.get_json()

        for field in set(data.keys()) - API.ACCEPT:
            data.pop(field)

        valid, reason = self.authenticate(data)
        if not valid:
            return BadRequest(reason)

        valid, reason = self.verify(data)
        if not valid:
            return BadRequest(reason)

        comment = self.comments.get(id)

        if time.time() > comment.get('created') + self.conf.getint('max-age'):
            raise Forbidden

        self.authorize(id, data, comment, request.cookies.get(str(id), ''))

        data['modified'] = time.time()

        for key in set(data.keys()) - API.FIELDS:
            data.pop(key)

        with self.isso.lock:
            rv = self.comments.update(id, data)

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        self.signal("comments.edit", rv)

        cookie = functools.partial(dump_cookie,
                                   value=self.isso.sign(
                                       [rv["id"], sha1(rv["text"])]),
                                   max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])

        resp = JSON(rv, 200)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #12
0
    def edit(self, environ, request, id):

        data = request.get_json()

        for field in set(data.keys()) - API.ACCEPT:
            data.pop(field)

        valid, reason = self.authenticate(data)
        if not valid:
            return BadRequest(reason)

        valid, reason = self.verify(data)
        if not valid:
            return BadRequest(reason)

        comment = self.comments.get(id)

        if time.time() > comment.get('created') + self.conf.getint('max-age'):
            raise Forbidden

        self.authorize(id, data, comment, request.cookies.get(str(id), ''))

        data['modified'] = time.time()

        for key in set(data.keys()) - API.FIELDS:
            data.pop(key)

        with self.isso.lock:
            rv = self.comments.update(id, data)

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        self.signal("comments.edit", rv)

        cookie = functools.partial(dump_cookie,
                value=self.isso.sign([rv["id"], sha1(rv["text"])]),
                max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])

        resp = JSON(rv, 200)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #13
0
    def edit(self, environ, request, id):

        try:
            rv = self.isso.unsign(request.cookies.get(str(id), ''))
        except (SignatureExpired, BadSignature):
            raise Forbidden

        if rv[0] != id:
            raise Forbidden

        # verify checksum, mallory might skip cookie deletion when he deletes a comment
        if rv[1] != sha1(self.comments.get(id)["text"]):
            raise Forbidden

        data = request.get_json()

        if "text" not in data or data["text"] is None or len(data["text"]) < 3:
            raise BadRequest("no text given")

        for key in set(data.keys()) - set(["text", "author", "website"]):
            data.pop(key)

        data['modified'] = time.time()

        with self.isso.lock:
            rv = self.comments.update(id, data)

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        self.signal("comments.edit", rv)

        cookie = functools.partial(dump_cookie,
                                   value=self.isso.sign(
                                       [rv["id"], sha1(rv["text"])]),
                                   max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])

        resp = JSON(rv, 200)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #14
0
파일: comments.py 프로젝트: posativ/isso
    def edit(self, environ, request, id):

        try:
            rv = self.isso.unsign(request.cookies.get(str(id), ''))
        except (SignatureExpired, BadSignature):
            raise Forbidden

        if rv[0] != id:
            raise Forbidden

        # verify checksum, mallory might skip cookie deletion when he deletes a comment
        if rv[1] != sha1(self.comments.get(id)["text"]):
            raise Forbidden

        data = request.get_json()

        if "text" not in data or data["text"] is None or len(data["text"]) < 3:
            raise BadRequest("no text given")

        for key in set(data.keys()) - set(["text", "author", "website"]):
            data.pop(key)

        data['modified'] = time.time()

        with self.isso.lock:
            rv = self.comments.update(id, data)

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        self.signal("comments.edit", rv)

        cookie = functools.partial(dump_cookie,
                                   value=self.isso.sign(
                                       [rv["id"], sha1(rv["text"])]),
                                   max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])

        resp = JSON(rv, 200)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #15
0
파일: comments.py 프로젝트: Laul0/isso
    def _remote_addr(self, request):
        """Return the anonymized IP address of the requester.

        Takes into consideration a potential X-Forwarded-For HTTP header
        if a necessary server.trusted-proxies configuration entry is set.

        Recipe source: https://stackoverflow.com/a/22936947/636849
        """
        remote_addr = request.remote_addr
        if self.trusted_proxies:
            route = request.access_route + [remote_addr]
            remote_addr = next((addr for addr in reversed(route)
                                if addr not in self.trusted_proxies),
                               remote_addr)
        return utils.anonymize(str(remote_addr))
예제 #16
0
    def dislike(self, environ, request, id):

        nv = self.comments.vote(
            False, id, utils.anonymize(str(request.remote_addr)))
        return JSON(nv, 200)
예제 #17
0
    def new(self, environ, request, uri):

        data = request.get_json()

        for field in set(data.keys()) - API.ACCEPT:
            data.pop(field)

        for key in ("author", "email", "website", "parent"):
            data.setdefault(key, None)

        valid, reason = API.verify(data)
        if not valid:
            return BadRequest(reason)

        for field in ("author", "email", "website"):
            if data.get(field) is not None:
                data[field] = cgi.escape(data[field])

        if data.get("website"):
            data["website"] = normalize(data["website"])

        data['mode'] = 2 if self.moderated else 1
        data['remote_addr'] = utils.anonymize(str(request.remote_addr))

        with self.isso.lock:
            if uri not in self.threads:
                if 'title' not in data:
                    with http.curl('GET', local("origin"), uri) as resp:
                        if resp and resp.status == 200:
                            uri, title = parse.thread(resp.read(), id=uri)
                        else:
                            return NotFound('URI does not exist %s')
                else:
                    title = data['title']

                thread = self.threads.new(uri, title)
                self.signal("comments.new:new-thread", thread)
            else:
                thread = self.threads[uri]

        # notify extensions that the new comment is about to save
        self.signal("comments.new:before-save", thread, data)

        valid, reason = self.guard.validate(uri, data)
        if not valid:
            self.signal("comments.new:guard", reason)
            raise Forbidden(reason)

        with self.isso.lock:
            rv = self.comments.add(uri, data)

        # notify extension, that the new comment has been successfully saved
        self.signal("comments.new:after-save", thread, rv)

        cookie = functools.partial(dump_cookie,
                                   value=self.isso.sign(
                                       [rv["id"], sha1(rv["text"])]),
                                   max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])
        rv["hash"] = self.hash(rv['email'] or rv['remote_addr'])

        self.cache.set(
            'hash', (rv['email'] or rv['remote_addr']).encode('utf-8'), rv['hash'])

        rv = self._add_gravatar_image(rv)

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        # success!
        self.signal("comments.new:finish", thread, rv)

        resp = JSON(rv, 202 if rv["mode"] == 2 else 201)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #18
0
 def checkip(self, env, req):
     return Response(utils.anonymize(str(req.remote_addr)), 200)
예제 #19
0
파일: comments.py 프로젝트: rhn/marginalia
    def new(self, environ, request, uri, key):
        data = request.get_json()

        # check access keys
        rv = self.db.execute(['SELECT uri FROM access WHERE key = ? ;'],
                             (key, )).fetchall()
        if not rv or rv[0][0] != uri:
            raise Forbidden

        for field in set(data.keys()) - API.ACCEPT:
            data.pop(field)

        for key in ("author", "parent"):
            data.setdefault(key, None)

        if data['parent'] is not None:
            data.setdefault('place', None)

        valid, reason = API.verify(data)
        if not valid:
            return BadRequest(reason)

        escaped = dict((key, cgi.escape(value) if value is not None else None)
                       for key, value in data.items())

        added = {}
        if escaped.get("website") is not None:
            added["website"] = normalize(escaped["website"])

        added['mode'] = 2 if self.moderated else 1

        prepared = dict(escaped)
        prepared.update(added)

        with self.isso.lock:
            if uri in self._threads:
                thread = self._threads[uri]
            else:
                if 'title' in prepared:
                    title = prepared['title']
                else:
                    with http.curl('GET', local("origin"), uri) as resp:
                        if resp and resp.status == 200:
                            uri, title = parse.thread(resp.read(), id=uri)
                        else:
                            return NotFound('URI does not exist %s')

                thread = self._threads.new(uri, title)
                self.signal("comments.new:new-thread", thread)
        # notify extensions that the new comment is about to save
        self.signal("comments.new:before-save", thread, prepared)

        valid, reason = self.guard.validate(uri, prepared)
        if not valid:
            self.signal("comments.new:guard", reason)
            raise Forbidden(reason)

        with self.isso.lock:
            rv = self.comments.add(uri, prepared)

        # notify extension, that the new comment has been successfully saved
        self.signal("comments.new:after-save", thread, rv)

        cookie = functools.partial(dump_cookie,
                                   value=self.isso.sign(
                                       [rv["id"], sha1(rv["text"])]),
                                   max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])
        rv["hash"] = self.hash(rv['remote_addr'])

        self.cache.set('hash', (rv['remote_addr']).encode('utf-8'), rv['hash'])

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        # success!
        self.signal("comments.new:finish", thread, rv)

        resp = JSON(rv, 202 if rv["mode"] == 2 else 201)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #20
0
    def like(self, environ, request, id):

        nv = self.comments.vote(True, id, str(request.remote_addr))
        return JSON(nv, 200)
예제 #21
0
파일: comment.py 프로젝트: waytai/isso
def dislike(app, environ, request, id):

    nv = app.db.comments.vote(False, id, utils.anonymize(str(request.remote_addr)))
    return Response(json.dumps(nv), 200)
예제 #22
0
파일: comments.py 프로젝트: h4ck3rm1k3/isso
    def new(self, environ, request, uri):

        logger.debug("got uri :%s" % uri)
        logger.debug("got eviron :%s" % str(environ))
        logger.debug("got request :%s" % pprint.pformat(request.__dict__))
                
        data = request.get_json()

        logger.debug("got data :%s" % data)

        
        for field in set(data.keys()) - API.ACCEPT:
            f = data.pop(field)
            logger.debug("skip data %s:%s" % (field, f ))

        for key in ("author", "email", "website", "parent"):
            data.setdefault(key, None)

        valid, reason = API.verify(data)
        if not valid:
            return BadRequest(reason)

        for field in ("author", "email", "website"):
            if data.get(field) is not None:
                data[field] = cgi.escape(data[field])

        if data.get("website"):
            data["website"] = normalize(data["website"])

        data['mode'] = 2 if self.moderated else 1

        logger.debug("request.remote_addr %s" % (request.remote_addr ))
                    
        data['remote_addr'] = utils.anonymize(str(request.remote_addr))

        with self.isso.lock:
            if uri not in self.threads:
                org = str(local("origin"))
                if org == '<LocalProxy unbound>':
                    org = environ['HTTP_ORIGIN']

                logger.debug("got origin %s" % (org))
                
                with http.curl('GET', org, uri) as resp:
                    if resp and resp.status == 200:
                        uri, title = parse.thread(resp.read(), id=uri)
                    else:
                        return NotFound('URI does not exist')

                thread = self.threads.new(uri, title)
                self.signal("comments.new:new-thread", thread)
            else:
                thread = self.threads[uri]

        # notify extensions that the new comment is about to save
        self.signal("comments.new:before-save", thread, data)

        valid, reason = self.guard.validate(uri, data)
        if not valid:
            self.signal("comments.new:guard", reason)
            raise Forbidden(reason)

        with self.isso.lock:
            rv = self.comments.add(uri, data)

        # notify extension, that the new comment has been successfully saved
        self.signal("comments.new:after-save", thread, rv)

        cookie = functools.partial(dump_cookie,
            value=self.isso.sign([rv["id"], sha1(rv["text"])]),
            max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])
        rv["hash"] = self.hash(rv['email'] or rv['remote_addr'])

        self.cache.set('hash', (rv['email'] or rv['remote_addr']).encode('utf-8'), rv['hash'])

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        # success!
        self.signal("comments.new:finish", thread, rv)

        resp = JSON(rv, 202 if rv["mode"] == 2 else 201)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #23
0
파일: comment.py 프로젝트: waytai/isso
def single(app, environ, request, id):

    if request.method == 'GET':
        rv = app.db.comments.get(id)
        if rv is None:
            raise NotFound

        for key in set(rv.keys()) - FIELDS:
            rv.pop(key)

        if request.args.get('plain', '0') == '0':
            rv['text'] = app.markdown(rv['text'])

        return Response(json.dumps(rv), 200, content_type='application/json')

    try:
        rv = app.unsign(request.cookies.get(str(id), ''))
    except (SignatureExpired, BadSignature):
        try:
            rv = app.unsign(request.cookies.get('admin', ''))
        except (SignatureExpired, BadSignature):
            raise Forbidden

    if rv[0] != id:
        raise Forbidden

    # verify checksum, mallory might skip cookie deletion when he deletes a comment
    if rv[1] != hashlib.md5(app.db.comments.get(id)["text"].encode('utf-8')).hexdigest():
        raise Forbidden

    if request.method == 'PUT':
        data = request.get_json()

        if "text" not in data or data["text"] is None or len(data["text"]) < 3:
            raise BadRequest("no text given")

        for key in set(data.keys()) - set(["text", "author", "website"]):
            data.pop(key)

        data['modified'] = time.time()

        with app.lock:
            rv = app.db.comments.update(id, data)

        for key in set(rv.keys()) - FIELDS:
            rv.pop(key)

        logger.info('comment %i edited: %s', id, json.dumps(rv))

        checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest()
        rv["text"] = app.markdown(rv["text"])

        cookie = functools.partial(dump_cookie,
                value=app.sign([rv["id"], checksum]),
                max_age=app.conf.getint('general', 'max-age'))

        resp = Response(json.dumps(rv), 200, 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

    if request.method == 'DELETE':

        item = app.db.comments.get(id)
        app.cache.delete('hash', (item['email'] or item['remote_addr']).encode('utf-8'))

        rv = app.db.comments.delete(id)
        if rv:
            for key in set(rv.keys()) - FIELDS:
                rv.pop(key)

        logger.info('comment %i deleted', id)

        cookie = functools.partial(dump_cookie, expires=0, max_age=0)

        resp = Response(json.dumps(rv), 200, content_type='application/json')
        resp.headers.add("Set-Cookie", cookie(str(id)))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % id))
        return resp
예제 #24
0
파일: comments.py 프로젝트: alemic/isso
    def like(self, environ, request, id):

        nv = self.comments.vote(True, id, utils.anonymize(str(request.remote_addr)))
        return Response(json.dumps(nv), 200)
예제 #25
0
파일: comments.py 프로젝트: jsmelquist/isso
    def dislike(self, environ, request, id):

        nv = self.comments.vote(False, id, utils.anonymize(str(request.remote_addr)))
        return JSON(nv, 200)
예제 #26
0
파일: comments.py 프로젝트: jsmelquist/isso
    def new(self, environ, request, uri):

        data = request.get_json()

        for field in set(data.keys()) - API.ACCEPT:
            data.pop(field)

        for key in ("author", "email", "website", "parent"):
            data.setdefault(key, None)

        valid, reason = API.verify(data)
        if not valid:
            return BadRequest(reason)

        for field in ("author", "email"):
            if data.get(field) is not None:
                data[field] = cgi.escape(data[field])

        data['mode'] = 2 if self.moderated else 1
        data['remote_addr'] = utils.anonymize(str(request.remote_addr))

        with self.isso.lock:
            if uri not in self.threads:
                with http.curl('GET', local("origin"), uri) as resp:
                    if resp and resp.status == 200:
                        uri, title = parse.thread(resp.read(), id=uri)
                    else:
                        return NotFound('URI does not exist')

                thread = self.threads.new(uri, title)
                self.signal("comments.new:new-thread", thread)
            else:
                thread = self.threads[uri]

        # notify extensions that the new comment is about to save
        self.signal("comments.new:before-save", thread, data)

        valid, reason = self.guard.validate(uri, data)
        if not valid:
            self.signal("comments.new:guard", reason)
            raise Forbidden(reason)

        with self.isso.lock:
            rv = self.comments.add(uri, data)

        # notify extension, that the new comment has been successfully saved
        self.signal("comments.new:after-save", thread, rv)

        cookie = functools.partial(dump_cookie,
            value=self.isso.sign([rv["id"], sha1(rv["text"])]),
            max_age=self.conf.getint('max-age'))

        rv["text"] = self.isso.render(rv["text"])
        rv["hash"] = pbkdf2(rv['email'] or rv['remote_addr'], self.isso.salt, 1000, 6).decode("utf-8")

        self.cache.set('hash', (rv['email'] or rv['remote_addr']).encode('utf-8'), rv['hash'])

        for key in set(rv.keys()) - API.FIELDS:
            rv.pop(key)

        # success!
        self.signal("comments.new:finish", thread, rv)

        resp = JSON(rv, 202 if rv["mode"] == 2 else 201)
        resp.headers.add("Set-Cookie", cookie(str(rv["id"])))
        resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
        return resp
예제 #27
0
파일: comment.py 프로젝트: waytai/isso
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
예제 #28
0
def dislike(app, environ, request, id):

    nv = app.db.comments.vote(False, id,
                              utils.anonymize(str(request.remote_addr)))
    return Response(json.dumps(nv), 200)
예제 #29
0
def single(app, environ, request, id):

    if request.method == 'GET':
        rv = app.db.comments.get(id)
        if rv is None:
            raise NotFound

        for key in set(rv.keys()) - FIELDS:
            rv.pop(key)

        if request.args.get('plain', '0') == '0':
            rv['text'] = app.markdown(rv['text'])

        return Response(json.dumps(rv), 200, content_type='application/json')

    try:
        rv = app.unsign(request.cookies.get(str(id), ''))
    except (SignatureExpired, BadSignature):
        try:
            rv = app.unsign(request.cookies.get('admin', ''))
        except (SignatureExpired, BadSignature):
            raise Forbidden

    if rv[0] != id:
        raise Forbidden

    # verify checksum, mallory might skip cookie deletion when he deletes a comment
    if rv[1] != hashlib.md5(
            app.db.comments.get(id)["text"].encode('utf-8')).hexdigest():
        raise Forbidden

    if request.method == 'PUT':
        data = request.get_json()

        if data.get("text") is not None and len(data['text']) < 3:
            raise BadRequest("no text given")

        for key in set(data.keys()) - set(["text", "author", "website"]):
            data.pop(key)

        data['modified'] = time.time()

        with app.lock:
            rv = app.db.comments.update(id, data)

        for key in set(rv.keys()) - FIELDS:
            rv.pop(key)

        logger.info('comment %i edited: %s', id, json.dumps(rv))

        checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest()
        rv["text"] = app.markdown(rv["text"])

        resp = Response(json.dumps(rv), 200, 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

    if request.method == 'DELETE':

        item = app.db.comments.get(id)
        app.cache.delete('hash', item['email'] or item['remote_addr'])

        rv = app.db.comments.delete(id)
        if rv:
            for key in set(rv.keys()) - FIELDS:
                rv.pop(key)

        logger.info('comment %i deleted', id)

        resp = Response(json.dumps(rv), 200, content_type='application/json')
        resp.delete_cookie(str(id), path='/')
        return resp
예제 #30
0
파일: comments.py 프로젝트: jsmelquist/isso
 def checkip(self, env, req):
     return Response(utils.anonymize(str(req.remote_addr)), 200)
예제 #31
0
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