1
0
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:
ksamuel 2020-08-12 15:21:49 +02:00
parent 500ca82b69
commit 3b4c57e850
6 changed files with 107 additions and 77 deletions

View File

@ -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,
},
)

View File

@ -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

View File

@ -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)

View File

@ -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
View 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)

View File

@ -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 }}