def test_slug_context() -> None: count = 512 with utility.SlugContext(True) as slug_context: L = [next(slug_context) for _ in range(count)] assert len(slug_context._slugs) == count assert len(set(L)) == count # Make sure none of the generated slugs are already in the database # in a very slow way with database.session() as session: for slug in L: assert (not session.query( database.Paste).filter_by(slug=slug).one_or_none()) assert (not session.query( database.File).filter_by(slug=slug).one_or_none())
async def post(self) -> None: if defensive.ratelimit(self.request, area="create"): raise error.RatelimitError() try: data = tornado.escape.json_decode(self.request.body) except json.decoder.JSONDecodeError: raise tornado.web.HTTPError(400, "could not parse json body") expiry = data.get("expiry") if expiry not in configuration.expiries: log.info( "Paste.post: a paste was submitted with an invalid expiry") raise tornado.web.HTTPError(400, "invalid expiry") auto_scale = data.get("long", None) is None files = data.get("files", []) if not files: raise tornado.web.HTTPError(400, "no files provided") with database.session() as session, utility.SlugContext( auto_scale) as slug_context: paste = database.Paste( next(slug_context), configuration.expiries[expiry], "v1-api", ) for file in files: lexer = file.get("lexer", "") content = file.get("content") filename = file.get("name") if lexer not in utility.list_languages(): raise tornado.web.HTTPError(400, "invalid lexer") if not content: raise tornado.web.HTTPError(400, "invalid content (empty)") try: paste.files.append( database.File( next(slug_context), content, lexer, filename, )) except error.ValidationError: raise tornado.web.HTTPError( 400, "invalid content (exceeds size limit)") if sum(len(f.fmt) for f in paste.files) > configuration.paste_size: raise tornado.web.HTTPError( 400, "invalid content (exceeds size limit)") paste.files[0].slug = paste.slug session.add(paste) try: session.commit() except Exception: # XXX be more precise log.warning("%r", slug_context._slugs) raise # Send the client to the paste url_request = self.request.full_url() url_paste = urljoin(url_request, f"/{paste.slug}") url_removal = urljoin(url_request, f"/remove/{paste.removal}") self.write({"link": url_paste, "removal": url_removal})
def post(self) -> None: # type: ignore """POST handler for the 'web' side of things.""" expiry = self.get_body_argument("expiry") if expiry not in configuration.expiries: log.info( "CreateAction.post: a paste was submitted with an invalid expiry" ) raise error.ValidationError() auto_scale = self.get_body_argument("long", None) is None lexers = self.get_body_arguments("lexer") raws = self.get_body_arguments("raw", strip=False) filenames = self.get_body_arguments("filename") if not all([lexers, raws, filenames]): # Prevent empty argument lists from making it through raise error.ValidationError() if not all(raw.strip() for raw in raws): # Prevent empty raws from making it through raise error.ValidationError() if any(len(L) != len(lexers) for L in [lexers, raws, filenames]): log.info("CreateAction.post: mismatching argument lists") raise error.ValidationError() if any(lexer not in utility.list_languages() for lexer in lexers): log.info("CreateAction.post: a file had an invalid lexer") raise error.ValidationError() with database.session() as session, utility.SlugContext( auto_scale ) as slug_context: paste = database.Paste( next(slug_context), configuration.expiries[expiry], "web" ) for (lexer, raw, filename) in zip(lexers, raws, filenames): paste.files.append( database.File( next(slug_context), raw, lexer, filename if filename else None, ) ) if sum(len(f.fmt) for f in paste.files) > configuration.paste_size: log.info("CreateAction.post: sum of files was too large") raise error.ValidationError() # For the first file we will always use the same slug as the paste, # since slugs are generated to be unique over both pastes and files # this can be done safely. paste.files[0].slug = paste.slug session.add(paste) session.commit() # The removal cookie is set for the specific path of the paste it is # related to self.set_cookie( "removal", str(paste.removal), path=f"/{paste.slug}" ) # Send the client to the paste self.redirect(f"/{paste.slug}")