Added login functionality. It's a bit clunky, but it deas what it needs to. Testing needed.
The header now has a search bar, but the page itself needs to be created.
This commit is contained in:
Michael Rodin 2023-10-18 12:55:20 +02:00
parent d4016ce80c
commit dda864a92e
10 changed files with 153 additions and 29 deletions

View file

@ -235,6 +235,7 @@
<x:Array Type="sys:Object"> <x:Array Type="sys:Object">
<sys:String>ID</sys:String> <sys:String>ID</sys:String>
<sys:String>SESSKEY</sys:String> <sys:String>SESSKEY</sys:String>
<sys:String>USERID</sys:String>
<sys:String>CREATED</sys:String> <sys:String>CREATED</sys:String>
<sys:String>LIFE</sys:String> <sys:String>LIFE</sys:String>
</x:Array> </x:Array>

View file

@ -1,9 +1,12 @@
from flask import Flask,redirect,url_for,request,render_template from flask import Flask,redirect,url_for,request,render_template,make_response
from datetime import datetime from datetime import datetime
from hashlib import sha256
from uuid import uuid4 as uuid
## Import db class from func.py and initialise it ## Import db class from func.py and initialise it
from func import db from func import db
db=db() db=db()
db.startup()
app = Flask(__name__) app = Flask(__name__)
@ -12,7 +15,7 @@ app = Flask(__name__)
def timectime(s): def timectime(s):
return datetime.utcfromtimestamp(s).strftime('%Y-%m-%d %H:%M') return datetime.utcfromtimestamp(s).strftime('%Y-%m-%d %H:%M')
@app.template_filter('spacer') @app.template_filter('spacer')
def timectime(s): def convsize(s):
sizes=("B","KB","MB","GB","TB") sizes=("B","KB","MB","GB","TB")
n=0 n=0
while s > 1000: while s > 1000:
@ -20,12 +23,58 @@ def timectime(s):
s=s/1000 s=s/1000
return str("%.2f" % s)+sizes[n] return str("%.2f" % s)+sizes[n]
## WEB FRONTEND ## WEB FRONTEND
@app.route('/') @app.route('/')
def homepage(): def homepage():
# try to get sesskey, else logout state
try:
# get sesskey and get info about user
sesskey=request.cookies.get('session')
res,userid=db.check_sesskey(sesskey)
if not res:
return 500
userdata=db.get_user_info(userid)
logged_in=True
except Exception as e:
logged_in=False
userdata=()
archives=db.get_n_archives() archives=db.get_n_archives()
return render_template("home.html", title="Homepage",archives=archives) return render_template("home.html", title="Homepage",userdata=userdata,login=logged_in,archives=archives)
@app.route('/user')
@app.route('/user/<userid>')
def userpage(userid:int=0):
if userid==0:
return make_response(redirect('/'))
@app.route('/login', methods=["GET","POST"])
def loginpage():
# POST: Process login request
if request.method == 'POST':
username=request.form['username']
password=sha256(request.form['password'].encode()).hexdigest()
code,userid,passhash=db.get_passhash(username)
if code != 200:
return passhash
# if passwords match, create session and return cookie
if password.upper() == passhash.upper():
lifetime=3000000 # lifetime of the sesskey in seconds
sesskey=str(uuid())
db.set_sesskey(sesskey,userid,lifetime)
resp=setcookie("session",sesskey,lifetime)
return resp
else:
return "<h2>You've entered the wrong password. This incident will be reported.</h2><br> Go back and try again.<br>" + password.upper() + "<br>" + passhash.upper() # TODO: DELETE
# GET: Login form
else:
return render_template("login.html", title="Login")
## FUNCTIONS
def setcookie(name:str,value:str,lifetime:int=10000):
resp = make_response(redirect('/'))
resp.set_cookie(name, value, max_age=lifetime)
return resp
## API CALLS ## API CALLS

View file

@ -1,6 +1,7 @@
## MAIN FUNCTIONS FILE FOR BACK-BACKEND OF FLASK ## MAIN FUNCTIONS FILE FOR BACK-BACKEND OF FLASK
import mariadb as sql import mariadb as sql
from os import environ from os import environ
import time
## params populated with environment variables, defaults can be changed for a permanent solution ## params populated with environment variables, defaults can be changed for a permanent solution
conn_params={ conn_params={
@ -13,13 +14,13 @@ conn_params={
class db: class db:
def __init__(self): def __init__(self):
self.conn=sql.connect(**conn_params) self.conn=sql.connect(**conn_params)
self.conn.autocommit=True
self.cur=self.conn.cursor() self.cur=self.conn.cursor()
## Creates all archives, if they don't exist already ## Creates all archives, if they don't exist already
## Called only on startup, hence the name ## Called only on startup, hence the name
def startup(self): def startup(self):
self.cur.execute(""" self.cur.execute("""CREATE TABLE IF NOT EXISTS Archs(
CREATE TABLE IF NOT EXISTS Archs(
ID int PRIMARY KEY AUTO_INCREMENT, ID int PRIMARY KEY AUTO_INCREMENT,
NAME text NOT NULL, NAME text NOT NULL,
HASH text NOT NULL, HASH text NOT NULL,
@ -27,44 +28,77 @@ class db:
IMPORTED int, IMPORTED int,
CATEGORY int, CATEGORY int,
OWNER int OWNER int
); );""")
CREATE TABLE IF NOT EXISTS Users( self.cur.execute("""CREATE TABLE IF NOT EXISTS Users(
ID int PRIMARY KEY AUTO_INCREMENT, ID int PRIMARY KEY AUTO_INCREMENT,
UNAME text NOT NULL, UNAME text NOT NULL,
DNAME text NOT NULL, DNAME text NOT NULL,
CREATED int NOT NULL, CREATED int NOT NULL,
STATE text, STATE text,
PASSHASH text NOT NULL PASSHASH text NOT NULL
); );""")
CREATE TABLE IF NOT EXISTS Sessions( self.cur.execute("""CREATE TABLE IF NOT EXISTS Sessions(
ID int PRIMARY KEY AUTO_INCREMENT, ID int PRIMARY KEY AUTO_INCREMENT,
SESSKEY text NOT NULL, SESSKEY text NOT NULL,
USERID int NOT NULL,
CREATED int NOT NULL, CREATED int NOT NULL,
LIFE int LIFE int
); );""")
CREATE TABLE IF NOT EXISTS Cats( self.cur.execute("""CREATE TABLE IF NOT EXISTS Cats(
ID int PRIMARY KEY AUTO_INCREMENT, ID int PRIMARY KEY AUTO_INCREMENT,
CATEGORY text NOT NULL, CATEGORY text NOT NULL,
PARENT int, PARENT int,
DESCRIPTION text DESCRIPTION text
); );""")
CREATE TABLE IF NOT EXISTS ArchLab( self.cur.execute("""CREATE TABLE IF NOT EXISTS ArchLab(
ID int PRIMARY KEY AUTO_INCREMENT, ID int PRIMARY KEY AUTO_INCREMENT,
ARCHID int NOT NULL, ARCHID int NOT NULL,
LABID int NOT NULL LABID int NOT NULL
); );""")
CREATE TABLE IF NOT EXISTS Labs( self.cur.execute("""CREATE TABLE IF NOT EXISTS Labs(
ID int PRIMARY KEY AUTO_INCREMENT, ID int PRIMARY KEY AUTO_INCREMENT,
LABEL text NOT NULL, LABEL text NOT NULL,
CATEGORY text, CATEGORY text,
TYPE int NOT NULL TYPE int NOT NULL
); );""")
CREATE TABLE IF NOT EXISTS LabType( self.cur.execute("""CREATE TABLE IF NOT EXISTS LabType(
ID int PRIMARY KEY AUTO_INCREMENT, ID int PRIMARY KEY AUTO_INCREMENT,
NAME text NOT NULL, NAME text NOT NULL,
DESCRIPTION text DESCRIPTION text
); );""")
""")
## Gets the passhash from a specific user
## OUTPUT: (If user exists) int=200, passhash:str
## (If user does not exist) int=400, Exception:str
def get_passhash(self, username:str):
self.cur.execute(f"SELECT ID,PASSHASH FROM Users WHERE UNAME='{username}'")
try:
resp=self.cur.fetchone()
except Exception as e:
return 400, e, NULL
return 200, resp[0], resp[1]
## Checks if sesskey exists and is not expired
## OUTPUT: (if valiid) bool=True, USERID:str
## (in invalid) bool=False, str=""
def check_sesskey(self, sesskey:str):
self.cur.execute(f"SELECT SESSKEY,USERID FROM Sessions WHERE SESSKEY='{sesskey}'")
entry=self.cur.fetchone()
if sesskey in entry:
return True, entry[1]
else:
return False, ""
## Sets a session key. That's it.
def set_sesskey(self, sesskey:str, userid:int, lifetime:int):
self.cur.execute(f"INSERT INTO Sessions(SESSKEY,USERID,CREATED,LIFE) VALUES('{sesskey}',{userid},{time.time()},{lifetime})")
## Gets and returns all user info about one (1) user
## OUTPUT: tuple=(ID:int,UNAME:str,DNAME:str,CREATED:int,STATE:text,PASSHASH:text)
def get_user_info(self, userid:int):
print("USERID:::::::::::" + userid)
self.cur.execute(f"SELECT * FROM Users WHERE ID='{userid}'")
return self.cur.fetchone()
## Returns all relevant information about one (1) archive ## Returns all relevant information about one (1) archive
## OUTPUT: archive:tuple=(NAME:str,HASH:str,IMPORTED[UNIX]:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,UNAME:str,DNAME:str) ## OUTPUT: archive:tuple=(NAME:str,HASH:str,IMPORTED[UNIX]:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,UNAME:str,DNAME:str)
@ -86,7 +120,7 @@ class db:
return archive, labels return archive, labels
## Returns n archives, sorted by (imported )time or size ## Returns n archives, sorted by (imported )time or size
## OUTPUT: archives:array=[…,(NAME:str,SIZE:str,IMPORTED[UNIX]:int),…] ## OUTPUT: archives:array=[…,(ID:int,NAME:str,SIZE:str,IMPORTED[UNIX]:int),…]
def get_n_archives(self, sorttype:str="time",category:int=0, count:int=20): def get_n_archives(self, sorttype:str="time",category:int=0, count:int=20):
global cur global cur
match sorttype: match sorttype:
@ -94,7 +128,7 @@ class db:
sorttype="SIZE" sorttype="SIZE"
case _: case _:
sorttype="IMPORTED" sorttype="IMPORTED"
self.cur.execute(f"""SELECT NAME,SIZE,IMPORTED FROM Archs{"" if category==0 else " WHERE CATEGORY=" + category} ORDER BY {sorttype} DESC LIMIT {count};""") self.cur.execute(f"""SELECT ID,NAME,SIZE,IMPORTED FROM Archs{"" if category==0 else " WHERE CATEGORY=" + category} ORDER BY {sorttype} DESC LIMIT {count};""")
archives=self.cur.fetchall() archives=self.cur.fetchall()
return archives return archives

View file

@ -23,7 +23,6 @@ header > div#container {
} }
.big-button { .big-button {
width: 2em;
height: 2em; height: 2em;
margin: auto 0.1em; margin: auto 0.1em;
border: none; border: none;

View file

@ -6,6 +6,13 @@ div.grid-item {
border: 2px black solid; border: 2px black solid;
} }
a.grid-item {
text-decoration: none;
display: contents;
height: 100%;
color: black;
}
div.clickable:hover { div.clickable:hover {
background: grey; background: lightgrey;
} }

9
flask/static/login.css Normal file
View file

@ -0,0 +1,9 @@
div.grid-container {
margin: 2em 0 0 1em;
display: inline-grid;
grid-template-columns: auto auto;
}
div.grid-container > * {
margin: 1em auto;
}

View file

@ -11,7 +11,15 @@
<header> <header>
<span id="title">{{title}}</span> <span id="title">{{title}}</span>
<div id="container"> <div id="container">
<button class="big-button">&#x1F50E;</div> <form action="/search" method="get">
<input type="text" name="q" placeholder="search">
<input type="submit" value="&#x1F50E;" class="big-button" id="search-button">
</form>
{% if login %}
<a href="/user/{{userdata[0]}}"><button class="big-button">{{userdata[2]}}</button></a>
{% else %}
<a href="/login"><button class="big-button">Login</button></a>
{% endif %}
</div> </div>
</header> </header>
<div id="content"> <div id="content">

View file

@ -8,9 +8,9 @@
{% if archives|length > 0 %} {% if archives|length > 0 %}
<div class="grid-container"> <div class="grid-container">
{% for arch in archives %} {% for arch in archives %}
<div class="grid-item clickable"><b>{{arch[0]}}</b></div> <a class="grid-item" href="/view/{{arch[0]}}"><div class="grid-item clickable"><b>{{arch[1]}}</b></div></a>
<div class="grid-item"><p>{{arch[1]|spacer}}</p></div> <div class="grid-item"><p>{{arch[2]|spacer}}</p></div>
<div class="grid-item"><p>{{arch[2]|ctime}}</p></div> <div class="grid-item"><p>{{arch[3]|ctime}}</p></div>
{% endfor %} {% endfor %}
</div> </div>

View file

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block meta %}
<link rel="stylesheet" href="/static/login.css" />
{% endblock %}
{% block content %}
<form action="/login" method="post">
<div class="grid-container">
<span>Username: </span><input type="text" name="username" placeholder="Username">
<span>Password: </span><input type="password" name="password" placeholder="******">
<input type = 'submit' value = 'Login'/>
</div>
</form>
{% endblock %}

View file

@ -1 +1,3 @@
flask flask
mariadb
hashlib