mirror of
https://github.com/Fluffy-Bean/GameExpo23.git
synced 2025-05-14 14:22:16 +00:00
Remove Token login and switch to Sessions
This commit is contained in:
parent
966d377adb
commit
dcfd7871ed
6 changed files with 121 additions and 113 deletions
|
@ -3,86 +3,77 @@ import shortuuid
|
|||
from flask import Blueprint, request, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from server.models import Tokens, Scores, Users
|
||||
from server.models import Scores, Sessions, Users
|
||||
from server.extensions import db
|
||||
from server.config import GAME_VERSION, GAME_VERSIONS, GAME_DIFFICULTIES, USER_MAX_TOKENS, MAX_SEARCH_RESULTS
|
||||
from server.config import GAME_VERSION, GAME_VERSIONS, GAME_DIFFICULTIES, USER_MAX_TOKENS, MAX_SEARCH_RESULTS, USER_REGEX
|
||||
|
||||
|
||||
blueprint = Blueprint("api", __name__, url_prefix="/api")
|
||||
|
||||
|
||||
@blueprint.route("/tokens", methods=["DELETE", "POST"])
|
||||
@blueprint.route("/tokens", methods=["POST"])
|
||||
@login_required
|
||||
def tokens():
|
||||
if request.method == "DELETE":
|
||||
token_id = request.form["token_id"]
|
||||
if not token_id:
|
||||
return jsonify({"error": "No token ID provided!"}), 400
|
||||
session_id = request.form["session_id"]
|
||||
|
||||
token = Tokens.query.filter_by(id=token_id).first()
|
||||
if not token:
|
||||
return jsonify({"error": "Token not found!"}), 404
|
||||
if token.user_id != current_user.id:
|
||||
return jsonify({"error": "You do not own this token!"}), 403
|
||||
if not session_id:
|
||||
return jsonify({"error": "No Session provided!"}), 400
|
||||
|
||||
db.session.delete(token)
|
||||
db.session.commit()
|
||||
session = Sessions.query.filter_by(id=session_id).first()
|
||||
|
||||
return jsonify({"success": "Token deleted!"}), 200
|
||||
elif request.method == "POST":
|
||||
if len(Tokens.query.filter_by(user_id=current_user.id).all()) >= USER_MAX_TOKENS:
|
||||
return jsonify({"error": f"You already have {USER_MAX_TOKENS} tokens!"}), 403
|
||||
if not session:
|
||||
return jsonify({"error": "Session not found!"}), 404
|
||||
if session.user_id != current_user.id:
|
||||
return jsonify({"error": "You do not own this session!"}), 403
|
||||
|
||||
new_string = str(shortuuid.ShortUUID().random(length=20))
|
||||
token = Tokens(token=new_string, user_id=current_user.id)
|
||||
db.session.add(token)
|
||||
db.session.commit()
|
||||
db.session.delete(session)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"success": "Token added!"}), 200
|
||||
return jsonify({"success": "Session deleted!"})
|
||||
|
||||
|
||||
@blueprint.route("/post", methods=["POST"])
|
||||
def post():
|
||||
form = request.form
|
||||
errors = []
|
||||
error = []
|
||||
|
||||
if not form:
|
||||
errors += "No form data provided!"
|
||||
if not form["token"]:
|
||||
errors += "No token provided!"
|
||||
error.append("No form data provided!")
|
||||
if not form["session"]:
|
||||
error.append("No session key provided!")
|
||||
if not form["version"]:
|
||||
errors += "No version provided!"
|
||||
error.append("No version provided!")
|
||||
|
||||
if errors:
|
||||
return jsonify(errors), 400
|
||||
if error:
|
||||
return jsonify(error), 400
|
||||
|
||||
try:
|
||||
int(form["score"])
|
||||
int(form["difficulty"])
|
||||
except TypeError:
|
||||
errors += "Invalid score and difficulty must be valid numbers!"
|
||||
error.append("Invalid score and difficulty must be valid numbers!")
|
||||
|
||||
if int(form["difficulty"]) not in GAME_DIFFICULTIES:
|
||||
errors += "Invalid difficulty!"
|
||||
error.append("Invalid difficulty!")
|
||||
|
||||
token_data = Tokens.query.filter_by(token=form["token"]).first()
|
||||
if not token_data:
|
||||
errors += "Authentication failed!"
|
||||
session_data = Sessions.query.filter_by(auth_key=form["session"]).first()
|
||||
if not session_data:
|
||||
error.append("Authentication failed!")
|
||||
|
||||
if errors:
|
||||
return jsonify(errors), 400
|
||||
if error:
|
||||
return jsonify(error), 400
|
||||
|
||||
score = Scores(
|
||||
score=int(form["score"]),
|
||||
difficulty=int(form["difficulty"]),
|
||||
version=form["version"],
|
||||
user_id=token_data.user_id,
|
||||
user_id=session_data.user_id,
|
||||
)
|
||||
|
||||
db.session.add(score)
|
||||
db.session.commit()
|
||||
|
||||
return "Success!", 200
|
||||
return "Success!"
|
||||
|
||||
|
||||
@blueprint.route("/search", methods=["GET"])
|
||||
|
@ -92,26 +83,52 @@ def search():
|
|||
if not search_arg:
|
||||
return "No search query provided!", 400
|
||||
|
||||
users = Users.query.filter(Users.username.contains(search)).limit(MAX_SEARCH_RESULTS).all()
|
||||
users = Users.query.filter(Users.username.icontains(search_arg)).limit(MAX_SEARCH_RESULTS).all()
|
||||
|
||||
return jsonify([user.username for user in users]), 200
|
||||
return jsonify([user.username for user in users])
|
||||
|
||||
|
||||
# @blueprint.route("/login", methods=["POST"])
|
||||
# def login():
|
||||
# username = request.form["username"]
|
||||
# password = request.form["password"]
|
||||
# errors = []
|
||||
#
|
||||
# if not username:
|
||||
# errors += "Empty Username"
|
||||
# if not password:
|
||||
# errors += "Empty Password"
|
||||
#
|
||||
# if errors:
|
||||
# return jsonify(errors), 400
|
||||
#
|
||||
# user = Users.query.filter(username=username).first()
|
||||
# if not user:
|
||||
# errors += "No user found"
|
||||
#
|
||||
@blueprint.route("/login", methods=["POST"])
|
||||
def login():
|
||||
username = request.form["username"].strip()
|
||||
password = request.form["password"].strip()
|
||||
device = request.form["device"].strip()
|
||||
username_regex = re.compile(USER_REGEX)
|
||||
|
||||
error = []
|
||||
|
||||
if not username or not username_regex.match(username) or not password:
|
||||
error.append("Username or Password is incorrect!")
|
||||
|
||||
user = Users.query.filter_by(username=username).first()
|
||||
|
||||
if not user or not check_password_hash(user.password, password):
|
||||
error.append("Username or Password is incorrect!")
|
||||
|
||||
if error:
|
||||
return jsonify(error), 400
|
||||
|
||||
session = Sessions(
|
||||
user_id=user.id,
|
||||
auth_key=str(shortuuid.ShortUUID().random(length=32)),
|
||||
id_address=request.remote_addr,
|
||||
device_type=device
|
||||
)
|
||||
db.session.add(session)
|
||||
db.session.commit()
|
||||
|
||||
return str(session.auth_key)
|
||||
|
||||
|
||||
@blueprint.route("/authenticate", methods=["POST"])
|
||||
def authenticate():
|
||||
auth_key = request.form["auth_key"].strip()
|
||||
|
||||
session = Sessions.query.filter_by(auth_key=auth_key).first()
|
||||
|
||||
if not session:
|
||||
return "Invalid session", 400
|
||||
|
||||
user_data = Users.query.filter_by(id=session.user_id).first()
|
||||
|
||||
return jsonify({'username':user_data.username})
|
||||
|
|
|
@ -6,7 +6,8 @@ from flask_login import login_required, login_user, logout_user, current_user
|
|||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
from server.extensions import db
|
||||
from server.models import Users, Tokens
|
||||
from server.models import Users, Sessions
|
||||
from server.config import USER_REGEX
|
||||
|
||||
|
||||
blueprint = Blueprint("auth", __name__)
|
||||
|
@ -31,8 +32,8 @@ def account():
|
|||
if action == "password":
|
||||
flash("Insert password change function", "error")
|
||||
|
||||
token_list = Tokens.query.filter_by(user_id=current_user.id).all()
|
||||
return render_template("account.html", token_list=token_list)
|
||||
sessions = Sessions.query.filter_by(user_id=current_user.id).all()
|
||||
return render_template("account.html", sessions=sessions)
|
||||
|
||||
|
||||
@blueprint.route("/register", methods=["POST"])
|
||||
|
@ -40,7 +41,7 @@ def register():
|
|||
# Get the form data
|
||||
username = request.form["username"].strip()
|
||||
password = request.form["password"].strip()
|
||||
username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
|
||||
username_regex = re.compile(USER_REGEX)
|
||||
|
||||
error = []
|
||||
|
||||
|
@ -79,7 +80,7 @@ def login():
|
|||
# Get the form data
|
||||
username = request.form["username"].strip()
|
||||
password = request.form["password"].strip()
|
||||
username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
|
||||
username_regex = re.compile(USER_REGEX)
|
||||
|
||||
error = []
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ GAME_VERSIONS = ["alpha"]
|
|||
GAME_DIFFICULTIES = [0, 1, 2, 3, 4]
|
||||
|
||||
USER_MAX_TOKENS = 3
|
||||
USER_REGEX = r"\b[A-Za-z0-9._-]+\b"
|
||||
|
||||
MAX_TOP_SCORES = 15
|
||||
MAX_SEARCH_RESULTS = 5
|
||||
|
|
|
@ -33,18 +33,25 @@ class Scores(db.Model):
|
|||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
|
||||
|
||||
class Tokens(db.Model):
|
||||
class Sessions(db.Model):
|
||||
"""
|
||||
Token table
|
||||
Sessions table
|
||||
"""
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
token = db.Column(db.String, nullable=False, unique=True)
|
||||
auth_key = db.Column(db.String, nullable=False, unique=True)
|
||||
ip_address = db.Column(db.String)
|
||||
device_type = db.Column(db.String)
|
||||
created_at = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
server_default=db.func.now(),
|
||||
)
|
||||
last_used = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
server_default=db.func.now(),
|
||||
)
|
||||
|
||||
|
||||
class Users(db.Model, UserMixin):
|
||||
|
@ -64,7 +71,7 @@ class Users(db.Model, UserMixin):
|
|||
|
||||
|
||||
scores = db.relationship("Scores", backref=db.backref('users', lazy=True))
|
||||
tokens = db.relationship("Tokens", backref=db.backref('users', lazy=True))
|
||||
tokens = db.relationship("Sessions", backref=db.backref('users', lazy=True))
|
||||
|
||||
def get_id(self):
|
||||
return str(self.alt_id)
|
||||
|
|
|
@ -11,44 +11,22 @@ function addFlashMessage(message, type='success') {
|
|||
document.querySelector('.flash').appendChild(flask);
|
||||
}
|
||||
|
||||
function ajax(url, form, callback, method='POST') {
|
||||
console.log(form)
|
||||
fetch(url, {
|
||||
method: method,
|
||||
function yeetSession(id) {
|
||||
let form = new FormData();
|
||||
form.append('session_id', id);
|
||||
|
||||
fetch('/api/tokens', {
|
||||
method: 'POST',
|
||||
body: form,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => callback(data))
|
||||
.catch(error => addFlashMessage(error.error, 'error'));
|
||||
}
|
||||
|
||||
function deleteToken(id) {
|
||||
let form = new FormData();
|
||||
form.append('token_id', id);
|
||||
|
||||
ajax('/api/tokens', form, (data) => {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
addFlashMessage(data.success, 'success');
|
||||
document.querySelector(`#token-${id}`).remove();
|
||||
document.querySelector(`#sess-${id}`).remove();
|
||||
} else {
|
||||
addFlashMessage(data.error, 'error');
|
||||
}
|
||||
}, 'DELETE');
|
||||
}
|
||||
|
||||
function addToken() {
|
||||
ajax('/api/tokens', null, (data) => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
addFlashMessage(data.error, 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function viewToken(id) {
|
||||
let token = document.querySelector(`#token-${id}`);
|
||||
let hidden = token.children[2];
|
||||
|
||||
hidden.classList.toggle('hidden');
|
||||
})
|
||||
.catch(error => addFlashMessage(error.error, 'error'));
|
||||
}
|
||||
|
|
|
@ -9,18 +9,22 @@
|
|||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="block">
|
||||
<h2>Tokens</h2>
|
||||
<p>These are your API tokens. Used to link the uploaded scores with your account.</p>
|
||||
<table>
|
||||
{% for token in token_list %}
|
||||
<tr id="token-{{ token.id }}">
|
||||
<td><button onclick="deleteToken({{ token.id }})" class="button secondary"><i class="ph ph-trash"></i></button></td>
|
||||
<td><button onclick="viewToken({{ token.id }})" class="button primary"><i class="ph ph-eye"></i></button></td>
|
||||
<td class="hidden">{{ token.token }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<button onclick="addToken()" class="button primary">Create New Token</button>
|
||||
<h2>Sessions</h2>
|
||||
<p>Devices and games that you logged into, yeet them if it wasn't you who logged in!</p>
|
||||
{% if sessions %}
|
||||
<table>
|
||||
{% for session in sessions %}
|
||||
<tr id="sess-{{ session.id }}">
|
||||
<td><button onclick="yeetSession({{ session.id }})" class="button secondary"><i class="ph ph-trash"></i></button></td>
|
||||
<td>{{ session.device_type }}</td>
|
||||
<td>{{ session.created_at.strftime('%Y-%m-%d') }}</td>
|
||||
<td>{{ session.last_used.strftime('%Y-%m-%d') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No sessions active. If you're looking to logout all website users, reset your password.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="block secondary">
|
||||
|
|
Loading…
Add table
Reference in a new issue