Commit d185a448 authored by Moritz Sokoll's avatar Moritz Sokoll 🦀
Browse files

added token.py for authentication | added login page

parent f5bc8642
......@@ -2,3 +2,5 @@ venv
sessions
dbconfig.json
dbconfig
server.conf
__pycache__
......@@ -11,3 +11,14 @@ stylesheets:
sassc src/sass/navbar.scss src/static/css/navbar.css
sassc src/sass/main.scss src/static/css/main.css
sassc src/sass/postcard.scss src/static/css/postcard.css
sassc src/sass/login.scss src/static/css/login.css
start:
docker-compose up -d
stop:
docker-compose down
restart:
docker-compose down
docker-compose up -d
# Disclaimer
This Project is a project for me to learn included technologies/langauges. Its not ment for production environments.
# PyBlog
## a blog written in python with flask
PyBlog is a blogging system written in python. Its one of my first Docker using projects
......
# imports
from flask import Flask, jsonify, request, Response, make_response, session
from flask_session import Session
import mysql.connector as sql
from libapi.conf import ConfigFile
from libapi.token import TokenStore
# create the Flask application instance
api = Flask(__name__)
api.config["SESSION_PERMANENT"] = False
api.config["SESSION_TYPE"] = "filesystem"
Session(api)
#set the directory containing the query files
querydir = "sql-src"
#loading server configuration
servconf = ConfigFile("server.conf").parseConfig()
#create the Tokenstore for access tokens
tokenstore = TokenStore()
#configuring the mysql/mariadb connection
dbconf = ConfigFile("dbconfig").parseConfig()
def createDbconn():
......@@ -64,27 +75,32 @@ def registerUser():
@api.route("/login", methods=["POST"])
def loginUser():
if not "pwd" in request.args or "uname" not in request.args:
return jsonify({"error": "400 bad request", "reason": "missing parameter"}), 400
cursor.execute(open(f"{querydir}/auth.sql", "r").read(), request.args["pwd"])
if not "pwd" in request.form or "uname" not in request.form:
return jsonify({"error": "400 bad request"}), 400
conn = createDbconn()
cursor = conn.cursor()
cursor.execute(open(f"{querydir}/auth.sql", "r").read(), (request.form["pwd"], request.form["uname"]))
response = cursor.fetchone()
if len(response) < 1:
return jsonify({"error": "500 failed to authenticate"}), 500
id = response[0]
session[str(id)] = request.form["username"]
return jsonify({"username": request.form["username"], "id": id})
token = tokenstore.generate(str(id), 64)
response = make_response(jsonify({"token": token, "uname": request.form["uname"]}))
response.headers["Access-Control-Allow-Origin"] = '*'
return response
@api.route("/auth", methods=["POST"])
def authenticateUser():
if not "pwd" in request.args or "uname" not in request.args:
return jsonify({"error": "400 bad request", "reason": "missing parameter"}), 400
if not request.form["username"] in session:
if not request.form["uname"] in session:
return jsonify({"authenticated": False}), 200
return jsonify({"username": request.form["username"], "id": id})
return jsonify({"username": request.form["uname"], "id": id})
#/search gives metadata about all posts containig a query string. e.g. /search?q=tech will return post metadata about all posts where the title contains 'tech'
#/search gives metadata about all posts containig a query string. e.g. /search?q=tech will return post metadata about all posts where the title or the author contains 'tech'
@api.route("/search", methods=["GET"])
def searchPosts():
if not "q" in request.args:
......@@ -134,4 +150,4 @@ def userPosts():
if __name__ == "__main__":
api.run("0.0.0.0", port=5001, debug=True)
api.run(servconf["host"], port=int(servconf["port"]), debug=bool(servconf["debug"]))
import random, string
class TokenStore:
def __init__(self):
self.tokens = []
self.tokenmap = {}
def generate(self, key:str, length:int=10):
if key in self.tokenmap:
raise Exception("replacing existing keys not allowed")
token = ''.join(random.choice(string.ascii_letters) for _ in range(length))
while token in self.tokenmap:
token = ''.join(random.choice(string.ascii_letters) for _ in range(length))
self.tokens.append(token)
self.tokenmap[key] = token
return token
def remove(self, token:str):
for i in self.tokenmap:
if self.tokenmap[i] = token:
del self.tokenmap[i]
break
else:
raise ValueError("token not used")
flask
mysql-connector
Flask-Session
SELECT uuid FROM pyblog_users WHERE password=SHA(?, 256);
SELECT uuid FROM pyblog_users WHERE password=SHA2(%s, 256) AND name=%s;
......@@ -5,7 +5,7 @@ WORKDIR /app
RUN python3 -m venv venv
RUN bash venv/bin/activate
RUN pip3 install -U pip
RUN pip3 install flask
RUN pip3 install -r requirements.txt
EXPOSE 5000
CMD [ "python3", "app.py" ]
from flask import Flask, request
from flask import Flask, request, redirect, session, jsonify, make_response, Response
from flask_session import Session
import requests
app = Flask(__name__)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
@app.route("/")
def index():
......@@ -29,6 +34,36 @@ def search():
@app.route("/user")
def usershow():
return open("/app/pages/user.html", "r").read()
@app.route("/login")
def login():
return open("/app/pages/login.html", "r").read()
@app.route("/auth", methods=["POST"])
def authorize():
if not "uname" in request.form or not "pwd" in request.form:
return "Failed to Authorize", 400
authresp = requests.post("http://blogapi:5001/login", data={"uname": request.form["uname"], "pwd": request.form["pwd"]})
if not authresp.ok:
return "Failed to Authorize", 500
resjson = authresp.json()
if not "token" in resjson:
return "Failed to Authorize", 500
session["token"] = [resjson["token"], resjson["uname"]]
return redirect("/")
@app.route("/token", methods=["GET"])
def retToken():
try:
response = make_response(jsonify({"token": session["token"][0], "uname": session["token"][1]}))
response.headers["Access-Control-Allow-Origin"] = '*'
return response
except:
response = make_response(jsonify({"error": "404 not found"}))
response.headers["Access-Control-Allow-Origin"] = '*'
return response, 404
if __name__ == "__main__":
app.run("0.0.0.0", 5000, debug=True)
......@@ -22,6 +22,7 @@
</form>
</li>
<li><a class="navbar" href="/about">about</a></li>
<li><a class="navbar" href="/login">login</a></li>
</ul>
</nav>
<h1>About</h1>
......
......@@ -23,6 +23,7 @@
</form>
</li>
<li><a class="navbar" href="/about">about</a></li>
<li><a class="navbar" href="/login">login</a></li>
</ul>
</nav>
<h1 id="title">Title</h1>
......
......@@ -6,8 +6,9 @@
<link rel="icon" href="/static/book.png">
<link rel="stylesheet" href="/static/css/navbar.css">
<link rel="stylesheet" href="/static/css/main.css">
<script src="/static/auth.js"></script>
</head>
<body>
<body onload="loggedIn();">
<nav class="nav">
<ul>
<li onclick="document.location.href='/';" class="nav-titlediv">
......@@ -22,6 +23,7 @@
</form>
</li>
<li><a class="navbar" href="/about">about</a></li>
<li><a class="navbar" href="/login">login</a></li>
</ul>
</nav>
<h1>ULUdev's Blog</h1>
......@@ -34,4 +36,15 @@
<p>Later here will be some links to blog articles (retrieved by the API)</p>
</div>
</body>
<script>
async function loggedIn() {
let token = await getAuthToken();
if (token === null) {
alert("you are not logged in");
}
else {
alert(`Your login token is: ${token}`);
}
}
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Login | PyBlog</title>
<link rel="stylesheet" href="/static/css/main.css">
<link rel="stylesheet" href="/static/css/login.css">
</head>
<body>
<form class="login" method="POST" action="/auth">
<h2>Login</h2>
<input type="text" placeholder="Username" name="uname" class="textedit">
<input type="password" placeholder="Password" name="pwd" class="textedit">
<input type="submit" value="Submit" class="submit">
</form>
</body>
</html>
......@@ -22,6 +22,7 @@
</form>
</li>
<li><a class="navbar" href="/about">about</a></li>
<li><a class="navbar" href="/login">login</a></li>
</ul>
</nav>
<h1>New Article</h1>
......
......@@ -23,6 +23,7 @@
</form>
</li>
<li><a class="navbar" href="/about">about</a></li>
<li><a class="navbar" href="/login">login</a></li>
</ul>
</nav>
<h1>Results</h1>
......
......@@ -12,7 +12,7 @@
</head>
<body onload="loadUser(); loadUserPosts();">
<nav class="nav">
<ul>
<ul id="navelements">
<li onclick="document.location.href='/';" class="nav-titlediv">
<img src="/static/book.png" style="width: 5rem; height: 5rem;"></img>
<h2 class="nav-title">PyBlog</h2>
......@@ -25,6 +25,7 @@
</form>
</li>
<li><a class="navbar" href="/about">about</a></li>
<li><a class="navbar" href="/login">login</a></li>
</ul>
</nav>
<main>
......
flask
requests
Flask-Session
form {
background-color: #bbb;
margin: auto;
padding: 40px;
width: 200px;
height: 200px;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 0 10px black;
border-radius: 5px;
}
form {
* {
margin: auto;
}
}
.textedit {
background: transparent;
border: none;
border-bottom: 1.5px solid #49d;
padding: 5px;
}
.submit {
background-color: #49d;
color: white;
border: none;
border-radius: 5px;
padding: 5px 20px;
box-shadow: 0 0 2px black;
transition: .2s;
&:hover {
cursor: pointer;
background-color: white;
color: #49d;
}
}
function authorize(){
const urlquery = new URLSearchParams(window.location.href);
const uuid = urlquery.get("uuid");
const opts = {
method: "POST",
body: JSON.stringify(whatever)
/*
auth.js
function to authenticate users
Author: Moritz Sokoll
License: GPLv3
This file is part of the PyBlog project <https://gitlab.sokoll.com/moritz/blog>
*/
async function getAuthToken(){
let request = await fetch("http://localhost:5000/token");
let responsejson = await request.json();
if ("token" in responsejson === true) {
return responsejson;
}
else {
return null;
}
}
form {
background-color: #bbb;
margin: auto;
padding: 40px;
width: 200px;
height: 200px;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 0 10px black;
border-radius: 5px; }
form * {
margin: auto; }
.textedit {
background: transparent;
border: none;
border-bottom: 1.5px solid #49d;
padding: 5px; }
.submit {
background-color: #49d;
color: white;
border: none;
border-radius: 5px;
padding: 5px 20px;
box-shadow: 0 0 2px black;
transition: .2s; }
.submit:hover {
cursor: pointer;
background-color: white;
color: #49d; }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment