Add /json endpoint and fix pasting via curl to /paste

pull/12/head
Cesura 4 years ago
parent 45192964ea
commit 7e68faf27f

@ -6,7 +6,7 @@ from os import environ
from distutils.util import strtobool from distutils.util import strtobool
from threading import Thread from threading import Thread
pastey_version = "0.4" pastey_version = "0.4.1"
loaded_config = {} loaded_config = {}
loaded_themes = [] loaded_themes = []
@ -25,7 +25,6 @@ config.listen_address = environ["PASTEY_LISTEN_ADDRESS"] if "PASTEY_LISTEN_ADDRE
config.listen_port = environ["PASTEY_LISTEN_PORT"] if "PASTEY_LISTEN_PORT" in environ else config.listen_port config.listen_port = environ["PASTEY_LISTEN_PORT"] if "PASTEY_LISTEN_PORT" in environ else config.listen_port
config.use_whitelist = bool(strtobool(environ["PASTEY_USE_WHITELIST"])) if "PASTEY_USE_WHITELIST" in environ else config.use_whitelist config.use_whitelist = bool(strtobool(environ["PASTEY_USE_WHITELIST"])) if "PASTEY_USE_WHITELIST" in environ else config.use_whitelist
config.restrict_pasting = bool(strtobool(environ["PASTEY_RESTRICT_PASTING"])) if "PASTEY_RESTRICT_PASTING" in environ else config.restrict_pasting config.restrict_pasting = bool(strtobool(environ["PASTEY_RESTRICT_PASTING"])) if "PASTEY_RESTRICT_PASTING" in environ else config.restrict_pasting
config.restrict_raw_pasting = bool(strtobool(environ["PASTEY_RESTRICT_RAW_PASTING"])) if "PASTEY_RESTRICT_RAW_PASTING" in environ else config.restrict_raw_pasting
config.guess_threshold = float(environ["PASTEY_GUESS_THRESHOLD"]) if "PASTEY_GUESS_THRESHOLD" in environ else config.guess_threshold config.guess_threshold = float(environ["PASTEY_GUESS_THRESHOLD"]) if "PASTEY_GUESS_THRESHOLD" in environ else config.guess_threshold
config.recent_pastes = int(environ["PASTEY_RECENT_PASTES"]) if "PASTEY_RECENT_PASTES" in environ else config.recent_pastes config.recent_pastes = int(environ["PASTEY_RECENT_PASTES"]) if "PASTEY_RECENT_PASTES" in environ else config.recent_pastes
config.whitelist_cidr = environ["PASTEY_WHITELIST_CIDR"].split(",") if "PASTEY_WHITELIST_CIDR" in environ else config.whitelist_cidr config.whitelist_cidr = environ["PASTEY_WHITELIST_CIDR"].split(",") if "PASTEY_WHITELIST_CIDR" in environ else config.whitelist_cidr

@ -88,7 +88,14 @@ def is_expired(paste):
# Build a URL, accounting for config options # Build a URL, accounting for config options
def build_url(request, path="/"): def build_url(request, path="/"):
protocol = request.url.split('//')[0] if not config.force_https_links else "https:"
domain = request.url.split('//')[1].split("/")[0] if config.override_domain == "" else config.override_domain domain = request.url.split('//')[1].split("/")[0] if config.override_domain == "" else config.override_domain
# Check for HTTPS headers
if 'X-Forwarded-Proto' in request.headers and request.headers['X-Forwarded-Proto'] == "https":
protocol = "https:"
elif 'X-Forwarded-Port' in request.headers and request.headers['X-Forwarded-Port'] == "443":
protocol = "https:"
else:
protocol = request.url.split('//')[0] if not config.force_https_links else "https:"
return protocol + "//" + domain + path return protocol + "//" + domain + path

@ -21,9 +21,6 @@ blacklist_cidr = []
# Restrict pasting functionality to whitelisted IPs # Restrict pasting functionality to whitelisted IPs
restrict_pasting = False restrict_pasting = False
# Restrict raw pasting to whitelisted IPs
restrict_raw_pasting = True
# Rate limit for pasting (ignored for whitelisted users) # Rate limit for pasting (ignored for whitelisted users)
rate_limit = "5/hour" rate_limit = "5/hour"
@ -54,6 +51,8 @@ show_cli_button = True
# Include https in the generated links instead of http # Include https in the generated links instead of http
# This assumes you are behind something else doing the SSL # This assumes you are behind something else doing the SSL
# termination, but want the users to see https links # termination, but want the users to see https links
#
# This is normally handled by the HTTP headers now
force_https_links = False force_https_links = False
# This can be used to specify a different domain for generated links # This can be used to specify a different domain for generated links

@ -5,6 +5,7 @@ from flask import Flask, render_template, request, redirect, abort
from urllib.parse import quote from urllib.parse import quote
from datetime import datetime from datetime import datetime
from os import environ from os import environ
import json
# Load themes # Load themes
loaded_themes = common.get_themes() loaded_themes = common.get_themes()
@ -116,33 +117,49 @@ def paste():
if config.restrict_pasting and not common.verify_whitelist(source_ip): if config.restrict_pasting and not common.verify_whitelist(source_ip):
abort(401) abort(401)
# Content field is necessary
if 'content' not in request.form:
abort(400)
content = request.form['content'] content = request.form['content']
# Check if content is empty # Check if content is empty
if request.form['content'].strip() == "": if content.strip() == "":
return redirect("/new") if 'cli' in request.form:
abort(400)
else:
return redirect("/new")
else: else:
# Verify form options # Verify form options
title = request.form['title'] if request.form['title'].strip() != "" else "Untitled" title = request.form['title'] if ('title' in request.form and request.form['title'].strip() != "") else "Untitled"
single = True if 'single' in request.form else False single = True if 'single' in request.form else False
encrypt = True if 'encrypt' in request.form else False encrypt = True if 'encrypt' in request.form else False
expiration = int(request.form['expiration']) if 'expiration' in request.form else -1
# Create paste # Create paste
unique_id, key = functions.new_paste(title, content, source_ip, expires=int(request.form['expiration']), single=single, encrypt=encrypt) unique_id, key = functions.new_paste(title, content, source_ip, expires=expiration, single=single, encrypt=encrypt)
if encrypt: if encrypt:
return redirect("/view/" + unique_id + "/" + quote(key))
# Return link if cli form option was set
if 'cli' in request.form:
return common.build_url(request, "/view/" + unique_id + "/" + quote(key)), 200
else:
return redirect("/view/" + unique_id + "/" + quote(key))
else: else:
return redirect("/view/" + unique_id) if 'cli' in request.form:
return common.build_url(request, "/view/" + unique_id), 200
else:
return redirect("/view/" + unique_id)
# POST new raw paste # POST new raw paste
@app.route('/raw', methods = ['POST']) @app.route('/raw', methods = ['POST'])
@limiter.limit(config.rate_limit, exempt_when=lambda: common.verify_whitelist(common.get_source_ip(request))) @limiter.limit(config.rate_limit, exempt_when=lambda: common.verify_whitelist(common.get_source_ip(request)))
def raw(): def paste_raw():
source_ip = common.get_source_ip(request) source_ip = common.get_source_ip(request)
# Check if restrict pasting to whitelist CIDRs is enabled # Check if restrict pasting to whitelist CIDRs is enabled
if config.restrict_raw_pasting and not common.verify_whitelist(source_ip): if config.restrict_pasting and not common.verify_whitelist(source_ip):
abort(401) abort(401)
# Create paste # Create paste
@ -151,6 +168,44 @@ def raw():
return link, 200 return link, 200
# POST new json paste
@app.route('/json', methods = ['POST'])
@limiter.limit(config.rate_limit, exempt_when=lambda: common.verify_whitelist(common.get_source_ip(request)))
def paste_json():
source_ip = common.get_source_ip(request)
# Check if restrict pasting to whitelist CIDRs is enabled
if config.restrict_pasting and not common.verify_whitelist(source_ip):
abort(401)
# Check json integrity
try:
paste = json.loads(request.data)
except json.JSONDecodeError:
abort(400)
# Content field is mandatory
if 'content' not in paste or paste['content'].strip() == "":
abort(400)
content = paste['content']
# Optional fields
title = paste['title'] if ('title' in paste and paste['title'].strip() != "") else "Untitled"
single = paste['single'] if ('single' in paste and type(paste['single']) == bool) else False
encrypt = paste['encrypt'] if ('encrypt' in paste and type(paste['encrypt']) == bool) else False
expiration = paste['expiration'] if ('expiration' in paste and type(paste['expiration']) == int) else -1
# Create paste
unique_id, key = functions.new_paste(title, content, source_ip, expires=expiration, single=single, encrypt=encrypt)
if encrypt:
return {
"link": common.build_url(request, "/view/" + unique_id + "/" + quote(key))
}, 200
else:
return {
"link": common.build_url(request, "/view/" + unique_id)
}, 200
# Custom 404 handler # Custom 404 handler
@app.errorhandler(404) @app.errorhandler(404)
def page_not_found(e): def page_not_found(e):

@ -129,15 +129,40 @@
<div class="row justify-content-md-center"> <div class="row justify-content-md-center">
<div class="col-md-4"> <div class="col-md-4">
<input class="form-control pastey-input" id="paste-url" type="text" value="{{ script_url }}" readonly /> <input class="form-control pastey-input pastey-view-url" id="paste-url" type="text" value="{{ script_url }}" readonly />
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<a href="{{ script_url }}"> <a href="{{ script_url }}">
<button type="button" class="btn btn-success text-nowrap"><i class="fas fa-download"></i>&nbsp;&nbsp;&nbsp;Download</button> <button type="button" class="btn btn-success text-nowrap"><i class="fas fa-download"></i>&nbsp;&nbsp;&nbsp;Download</button>
</a> </a>
</div> </div>
</div> </div><br />
<div class="row text-center">
<div class="col-md-12">
<h1 class="mb-3 h4">JSON</h1>
<p>You can also send a properly-formatted json POST request to <span style="font-weight:bold;">/json</span>:</p>
</div>
</div>
<div class="row justify-content-md-center">
<div class="col-md-3">
<pre>{
"content": "This is a paste",
"title": "Interesting title",
"expiration": -1,
"encrypt": true,
"single": false
}</pre>
</div>
</div>
<div class="row text-center">
<div class="col-md-12">
<p>Note that <span style="font-weight:bold;">expiration</span> is some value in hours, and that -1 = no expiration date.
Only the <span style="font-weight:bold;">content</span> field is mandatory.</p>
</div>
</div>
</div> </div>
</main> </main>
<!--Main layout--> <!--Main layout-->

Loading…
Cancel
Save