mirror of
https://github.com/Tygs/0bin.git
synced 2023-08-10 21:13:00 +03:00
Add empty admin page with login
This commit is contained in:
parent
500ca82b69
commit
3b4c57e850
@ -7,12 +7,21 @@ import sys
|
|||||||
|
|
||||||
import _thread as thread
|
import _thread as thread
|
||||||
|
|
||||||
import urllib.parse as urlparse
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import bottle
|
import bottle
|
||||||
from bottle import Bottle, static_file, view, request, HTTPResponse, redirect, abort
|
from bottle import (
|
||||||
|
Bottle,
|
||||||
|
debug,
|
||||||
|
static_file,
|
||||||
|
view,
|
||||||
|
request,
|
||||||
|
HTTPResponse,
|
||||||
|
redirect,
|
||||||
|
abort,
|
||||||
|
)
|
||||||
|
|
||||||
from beaker.middleware import SessionMiddleware
|
from beaker.middleware import SessionMiddleware
|
||||||
|
|
||||||
@ -37,15 +46,9 @@ GLOBAL_CONTEXT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app = SessionMiddleware(
|
app = Bottle()
|
||||||
Bottle(),
|
|
||||||
{
|
ADMIN_LOGIN_URL = settings.ADMIN_URL + "login/"
|
||||||
"session.type": "file",
|
|
||||||
"session.cookie_expires": 300,
|
|
||||||
"session.data_dir": settings.SESSIONS_DIR,
|
|
||||||
"session.auto": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
@ -54,61 +57,68 @@ def index():
|
|||||||
return GLOBAL_CONTEXT
|
return GLOBAL_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
@app.route("/faq/")
|
@app.get("/faq/")
|
||||||
@view("faq")
|
@view("faq")
|
||||||
def faq():
|
def faq():
|
||||||
return GLOBAL_CONTEXT
|
return GLOBAL_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
@app.route(settings.ADMIN_URL, method="GET")
|
@app.get(settings.ADMIN_URL)
|
||||||
|
@app.post(settings.ADMIN_URL)
|
||||||
@view("admin")
|
@view("admin")
|
||||||
def admin():
|
def admin():
|
||||||
session = request.environ.get("beaker.session")
|
session = request.environ.get("beaker.session")
|
||||||
if not session or not session.get("is_authenticated"):
|
if not session or not session.get("is_authenticated"):
|
||||||
redirect(settings.ADMIN_URL + "/login")
|
redirect(ADMIN_LOGIN_URL)
|
||||||
|
|
||||||
return {}
|
paste_id = request.forms.get("paste", "")
|
||||||
|
if paste_id:
|
||||||
|
try:
|
||||||
|
if "/paste/" in paste_id:
|
||||||
|
paste_id = urlparse(paste_id).path.split("/path/")[-1]
|
||||||
|
paste = Paste.load(paste_id)
|
||||||
|
paste.delete()
|
||||||
|
except (TypeError, ValueError, FileNotFoundError):
|
||||||
|
return {"status": "error", "message": f"Cannot find paste '{paste_id}'"}
|
||||||
|
|
||||||
|
return {"status": "ok", "message": "Paste deleted"}
|
||||||
|
|
||||||
|
return {"status": "ok", **GLOBAL_CONTEXT}
|
||||||
|
|
||||||
|
|
||||||
@app.route(settings.ADMIN_URL + "/:paste_id", method="DELETE")
|
@app.get(ADMIN_LOGIN_URL)
|
||||||
@view("admin")
|
@app.post(ADMIN_LOGIN_URL)
|
||||||
def delete_paste_from_admin(paste_id):
|
@view("login")
|
||||||
session = request.environ.get("beaker.session")
|
|
||||||
if not session or not session.get("is_authenticated"):
|
|
||||||
abort(403, "Sorry, access denied.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
paste = Paste.load(paste_id)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
return error404(ValueError)
|
|
||||||
|
|
||||||
paste.delete()
|
|
||||||
|
|
||||||
return {"status": "ok", "message": "Paste deleted"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.route(settings.ADMIN_URL + "/login")
|
|
||||||
def login():
|
def login():
|
||||||
|
|
||||||
password = request.forms.get("password")
|
password = request.forms.get("password")
|
||||||
if password:
|
if password:
|
||||||
if not check_password(password):
|
if not check_password(password):
|
||||||
return {"error": "Wrong password"}
|
return {"status": "error", "message": "Wrong password", **GLOBAL_CONTEXT}
|
||||||
|
|
||||||
session = request.environ.get("beaker.session")
|
session = request.environ.get("beaker.session")
|
||||||
session["is_authenticated"] = True
|
session["is_authenticated"] = True
|
||||||
session.save()
|
session.save()
|
||||||
|
|
||||||
redirect(settings.ADMIN_URL)
|
redirect(settings.ADMIN_URL)
|
||||||
else:
|
|
||||||
return {}
|
return {"status": "ok", **GLOBAL_CONTEXT}
|
||||||
|
|
||||||
|
|
||||||
@app.route("/paste/create", method="POST")
|
@app.post(settings.ADMIN_URL + "logout/")
|
||||||
|
@view("logout")
|
||||||
|
def logout():
|
||||||
|
session = request.environ.get("beaker.session")
|
||||||
|
session["is_authenticated"] = False
|
||||||
|
session.save()
|
||||||
|
redirect("/")
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/paste/create")
|
||||||
def create_paste():
|
def create_paste():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
body = urlparse.parse_qs(request.body.read(int(settings.MAX_SIZE * 1.1)))
|
body = parse_qs(request.body.read(int(settings.MAX_SIZE * 1.1)))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return {"status": "error", "message": "Wrong data payload."}
|
return {"status": "error", "message": "Wrong data payload."}
|
||||||
|
|
||||||
@ -158,7 +168,7 @@ def create_paste():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route("/paste/:paste_id", method="GET")
|
@app.get("/paste/:paste_id")
|
||||||
@view("paste")
|
@view("paste")
|
||||||
def display_paste(paste_id):
|
def display_paste(paste_id):
|
||||||
|
|
||||||
@ -191,7 +201,7 @@ def display_paste(paste_id):
|
|||||||
return {"paste": paste, "keep_alive": keep_alive, **GLOBAL_CONTEXT}
|
return {"paste": paste, "keep_alive": keep_alive, **GLOBAL_CONTEXT}
|
||||||
|
|
||||||
|
|
||||||
@app.route("/paste/:paste_id", method="DELETE")
|
@app.delete("/paste/:paste_id")
|
||||||
def delete_paste(paste_id):
|
def delete_paste(paste_id):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -216,7 +226,7 @@ def error404(code):
|
|||||||
return GLOBAL_CONTEXT
|
return GLOBAL_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
@app.route("/static/<filename:path>")
|
@app.get("/static/<filename:path>")
|
||||||
def server_static(filename):
|
def server_static(filename):
|
||||||
return static_file(filename, root=settings.STATIC_FILES_ROOT)
|
return static_file(filename, root=settings.STATIC_FILES_ROOT)
|
||||||
|
|
||||||
@ -249,3 +259,14 @@ def get_app(debug=None, settings_file="", compressed_static=None, settings=setti
|
|||||||
bottle.debug(True)
|
bottle.debug(True)
|
||||||
|
|
||||||
return settings, app
|
return settings, app
|
||||||
|
|
||||||
|
|
||||||
|
app = SessionMiddleware(
|
||||||
|
app,
|
||||||
|
{
|
||||||
|
"session.type": "file",
|
||||||
|
"session.cookie_expires": 300,
|
||||||
|
"session.data_dir": settings.SESSIONS_DIR,
|
||||||
|
"session.auto": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@ -132,9 +132,9 @@ def ensure_var_env():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
settings.VAR_DIR.mkdir(exist_ok=True, parents=True)
|
settings.VAR_DIR.mkdir(exist_ok=True, parents=True)
|
||||||
settings.PASTE_FILES_ROOT = VAR_DIR / "content"
|
settings.PASTE_FILES_ROOT = settings.VAR_DIR / "content"
|
||||||
settings.PASTE_FILES_ROOT.mkdir(exist_ok=True)
|
settings.PASTE_FILES_ROOT.mkdir(exist_ok=True)
|
||||||
settings.SESSIONS_DIR = VAR_DIR / "sessions"
|
settings.SESSIONS_DIR = settings.VAR_DIR / "sessions"
|
||||||
settings.SESSIONS_DIR.mkdir(exist_ok=True)
|
settings.SESSIONS_DIR.mkdir(exist_ok=True)
|
||||||
|
|
||||||
secret_key_file = settings.VAR_DIR / "secret_key"
|
secret_key_file = settings.VAR_DIR / "secret_key"
|
||||||
@ -150,7 +150,7 @@ def ensure_var_env():
|
|||||||
settings.ADMIN_PASSWORD_FILE = admin_password_file
|
settings.ADMIN_PASSWORD_FILE = admin_password_file
|
||||||
|
|
||||||
payload = ("admin" + settings.SECRET_KEY).encode("ascii")
|
payload = ("admin" + settings.SECRET_KEY).encode("ascii")
|
||||||
settings.ADMIN_URL = "/admin/" + hashlib.sha256(payload).hexdigest()
|
settings.ADMIN_URL = "/admin/" + hashlib.sha256(payload).hexdigest() + "/"
|
||||||
|
|
||||||
|
|
||||||
def hash_password(password):
|
def hash_password(password):
|
||||||
@ -166,7 +166,7 @@ def hash_password(password):
|
|||||||
|
|
||||||
def check_password(password):
|
def check_password(password):
|
||||||
try:
|
try:
|
||||||
return settings.ADMIN_PASSWORD_FILE.read_bytes() != hash_password(password)
|
return settings.ADMIN_PASSWORD_FILE.read_bytes() == hash_password(password)
|
||||||
except (FileNotFoundError, AttributeError):
|
except (FileNotFoundError, AttributeError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -1,32 +1,25 @@
|
|||||||
%if is_authenticated:
|
<form action="./delete/" method="post">
|
||||||
|
%if status == "error":
|
||||||
<form action="" method="delete">
|
<div class="alert alert-danger" role="alert alert-danger">
|
||||||
|
{{message}}
|
||||||
|
</div>
|
||||||
|
%end
|
||||||
<div>
|
<div>
|
||||||
<form>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label>Paste to delete</label>
|
||||||
<label>Paste to delete</label>
|
<input name="paste" type="text" class="form-control" placeholder="Paste URL or ID">
|
||||||
<input type="text" class="form-control" placeholder="Paste URL or ID">
|
</div>
|
||||||
</div>
|
<button type="submit" class="btn btn-black">Delete</button>
|
||||||
<button type="submit" class="btn btn-black">Delete</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
%else:
|
<form action="./logout/" method="post">
|
||||||
<form action="/login" method="post">
|
<div>
|
||||||
|
<button type="submit" class="btn btn-black">Logout</button>
|
||||||
<div class="login-form">
|
</div>
|
||||||
<form>
|
</form>
|
||||||
|
|
||||||
<label>Password</label>
|
|
||||||
<input type="password" class="form-control" placeholder="Password">
|
|
||||||
<button type="submit" class="btn btn-black">Login</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
%end
|
|
||||||
|
|
||||||
|
|
||||||
% rebase('base', settings=settings, pastes_count=pastes_count)
|
% rebase('base', settings=settings, pastes_count=pastes_count)
|
||||||
|
@ -35,14 +35,14 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li class="submenu"><a href="#" @click.prevent="openPreviousPastesMenu = !openPreviousPastesMenu">Previous
|
<li class="submenu"><a href="#" @click.prevent="openPreviousPastesMenu = !openPreviousPastesMenu">Previous
|
||||||
pastes +</a>
|
pastes v</a>
|
||||||
<ul class="previous-pastes" id="topmenu" v-if="openPreviousPastesMenu"
|
<ul class="previous-pastes" id="topmenu" v-if="openPreviousPastesMenu"
|
||||||
@mouseleave="openPreviousPastesMenu =false">
|
@mouseleave="openPreviousPastesMenu =false">
|
||||||
<li class="item active" v-if="previousPastes.length === 0">
|
<li class="item active" v-if="previousPastes.length === 0">
|
||||||
<a href="#">No paste yet...</a>
|
<a href="#">No paste yet</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="item active" v-for="paste in previousPastes">
|
<li class="item active" v-for="paste in previousPastes">
|
||||||
<a :href="paste.link" @click="forceLoad(paste.link)">{% paste.displayDate %}.</a>
|
<a :href="paste.link" @click="forceLoad(paste.link)">{% paste.displayDate %}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
18
zerobin/views/login.tpl
Normal file
18
zerobin/views/login.tpl
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<form action="." method="post">
|
||||||
|
<div class="login-form">
|
||||||
|
<form>
|
||||||
|
<label>Password</label>
|
||||||
|
%if status == "error":
|
||||||
|
<div class="alert alert-danger" role="alert alert-danger">
|
||||||
|
{{message}}
|
||||||
|
</div>
|
||||||
|
%end
|
||||||
|
<input type="password" class="form-control" placeholder="Password" name="password">
|
||||||
|
<button type="submit" class="btn btn-black">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
% rebase('base', settings=settings, pastes_count=pastes_count)
|
@ -19,8 +19,6 @@
|
|||||||
%end
|
%end
|
||||||
%end
|
%end
|
||||||
|
|
||||||
<h1>{% currentPaste.type %}</h1>
|
|
||||||
|
|
||||||
<div class="well paste-form">
|
<div class="well paste-form">
|
||||||
<form action="/" method="get" accept-charset="utf-8">
|
<form action="/" method="get" accept-charset="utf-8">
|
||||||
|
|
||||||
@ -57,7 +55,7 @@
|
|||||||
%if expiration:
|
%if expiration:
|
||||||
<span id="expiration-tag">Expire {{ expiration }}</span>
|
<span id="expiration-tag">Expire {{ expiration }}</span>
|
||||||
%end
|
%end
|
||||||
|
|
||||||
<pre id="paste-content" class="prettyprint">
|
<pre id="paste-content" class="prettyprint">
|
||||||
<code>
|
<code>
|
||||||
{{ paste.content }}
|
{{ paste.content }}
|
||||||
|
Loading…
Reference in New Issue
Block a user