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">
<sys:String>ID</sys:String>
<sys:String>SESSKEY</sys:String>
<sys:String>USERID</sys:String>
<sys:String>CREATED</sys:String>
<sys:String>LIFE</sys:String>
</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 hashlib import sha256
from uuid import uuid4 as uuid
## Import db class from func.py and initialise it
from func import db
db=db()
db.startup()
app = Flask(__name__)
@ -12,7 +15,7 @@ app = Flask(__name__)
def timectime(s):
return datetime.utcfromtimestamp(s).strftime('%Y-%m-%d %H:%M')
@app.template_filter('spacer')
def timectime(s):
def convsize(s):
sizes=("B","KB","MB","GB","TB")
n=0
while s > 1000:
@ -20,12 +23,58 @@ def timectime(s):
s=s/1000
return str("%.2f" % s)+sizes[n]
## WEB FRONTEND
@app.route('/')
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()
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

View file

@ -1,6 +1,7 @@
## MAIN FUNCTIONS FILE FOR BACK-BACKEND OF FLASK
import mariadb as sql
from os import environ
import time
## params populated with environment variables, defaults can be changed for a permanent solution
conn_params={
@ -13,13 +14,13 @@ conn_params={
class db:
def __init__(self):
self.conn=sql.connect(**conn_params)
self.conn.autocommit=True
self.cur=self.conn.cursor()
## Creates all archives, if they don't exist already
## Called only on startup, hence the name
def startup(self):
self.cur.execute("""
CREATE TABLE IF NOT EXISTS Archs(
self.cur.execute("""CREATE TABLE IF NOT EXISTS Archs(
ID int PRIMARY KEY AUTO_INCREMENT,
NAME text NOT NULL,
HASH text NOT NULL,
@ -27,44 +28,77 @@ class db:
IMPORTED int,
CATEGORY int,
OWNER int
);
CREATE TABLE IF NOT EXISTS Users(
);""")
self.cur.execute("""CREATE TABLE IF NOT EXISTS Users(
ID int PRIMARY KEY AUTO_INCREMENT,
UNAME text NOT NULL,
DNAME text NOT NULL,
CREATED int NOT NULL,
STATE text,
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,
SESSKEY text NOT NULL,
USERID int NOT NULL,
CREATED int NOT NULL,
LIFE int
);
CREATE TABLE IF NOT EXISTS Cats(
);""")
self.cur.execute("""CREATE TABLE IF NOT EXISTS Cats(
ID int PRIMARY KEY AUTO_INCREMENT,
CATEGORY text NOT NULL,
PARENT int,
DESCRIPTION text
);
CREATE TABLE IF NOT EXISTS ArchLab(
);""")
self.cur.execute("""CREATE TABLE IF NOT EXISTS ArchLab(
ID int PRIMARY KEY AUTO_INCREMENT,
ARCHID 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,
LABEL text NOT NULL,
CATEGORY text,
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,
NAME text NOT NULL,
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
## 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
## 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):
global cur
match sorttype:
@ -94,7 +128,7 @@ class db:
sorttype="SIZE"
case _:
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()
return archives

View file

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

View file

@ -6,6 +6,13 @@ div.grid-item {
border: 2px black solid;
}
div.clickable:hover {
background: grey;
a.grid-item {
text-decoration: none;
display: contents;
height: 100%;
color: black;
}
div.clickable:hover {
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>
<span id="title">{{title}}</span>
<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>
</header>
<div id="content">

View file

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