From 43736d68bb1a7441cc66443d406df9b1d7384cf6 Mon Sep 17 00:00:00 2001 From: Hugo H Date: Tue, 23 Dec 2025 22:06:29 +0000 Subject: [PATCH] Added new pages, and added user approval and login to external services (Still need to add service tokens) --- config.json | 7 + db.py | 189 +++++++++ initdb.py | 25 +- logout.html | 0 main.py | 76 +++- templates/approveservice.html | 518 ++++++++++++++++++++++ templates/home.html | 11 + templates/logbackintoservice.html | 459 ++++++++++++++++++++ templates/login.html | 55 +++ templates/logout.html | 187 ++++++++ templates/signup.html | 683 ++++++++++++++++++++++++++++++ 11 files changed, 2202 insertions(+), 8 deletions(-) create mode 100644 config.json create mode 100644 logout.html create mode 100644 templates/approveservice.html create mode 100644 templates/home.html create mode 100644 templates/logbackintoservice.html create mode 100644 templates/logout.html create mode 100644 templates/signup.html diff --git a/config.json b/config.json new file mode 100644 index 0000000..c7e51f7 --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "permissionDetails":{ + "readAccountData":["Access your profile information","Name, email, and account details"], + "storeAppData":["Stora data in your account","Store data affiliated with you"], + "getAppData":["Read app data","Read data set by this app affiliated with you"] + } +} \ No newline at end of file diff --git a/db.py b/db.py index 4ceb993..b464417 100644 --- a/db.py +++ b/db.py @@ -95,6 +95,36 @@ def getGroupByName(name, dbuser, dbpass, dbhost, dbname): print(f"Error retrieving group: {e}") raise +def getServiceById(id, dbuser, dbpass, dbhost, dbname): + try: + conn = psycopg2.connect( + host=dbhost, + user=dbuser, + password=dbpass, + database=dbname + ) + cur = conn.cursor() + print(id) + cur.execute("SELECT name, permissions, creation_date FROM services WHERE id = %s", (id,)) + group_record = cur.fetchone() + + cur.close() + conn.close() + + if group_record: + return { + "name": group_record[0], + "permissions": group_record[1], + "creation_date": group_record[2] + } + else: + return None + + except Exception as e: + print(f"Error retrieving group: {e}") + raise + + def createUser(name, username, email, password, group, dbuser, dbpass, dbhost, dbname): try: conn = psycopg2.connect( @@ -124,6 +154,165 @@ def createUser(name, username, email, password, group, dbuser, dbpass, dbhost, d print(f"Error creating user: {e}") raise +def createRequestToken(user_id, dbuser, dbpass, dbhost, dbname): + try: + conn = psycopg2.connect( + host=dbhost, + user=dbuser, + password=dbpass, + database=dbname + ) + cur = conn.cursor() + + token_id = secrets.token_urlsafe(32) + + cur.execute(""" + INSERT INTO requestTokens (id, owner_id) + VALUES (%s, %s) RETURNING id, creation_date, expiration_date + """, (token_id, user_id)) + + token_data = cur.fetchone() + conn.commit() + + cur.close() + conn.close() + + return token_data[0] + + except Exception as e: + print(f"Error creating token: {e}") + raise + +def checkRequestToken(token_id, dbuser, dbpass, dbhost, dbname): + try: + conn = psycopg2.connect( + host=dbhost, + user=dbuser, + password=dbpass, + database=dbname + ) + cur = conn.cursor() + + cur.execute(""" + SELECT owner_id FROM requestTokens + WHERE id = %s AND expiration_date > CURRENT_TIMESTAMP + """, (token_id,)) + + token_record = cur.fetchone() + + cur.close() + conn.close() + + if token_record: + cur.execute("DELETE FROM requestTokens WHERE id = %s", (token_id,)) + conn.commit() + return token_record[0] + else: + return None + + except Exception as e: + print(f"Error verifying token: {e}") + return None + +def getUserServiceData(user_id, service_id, record_key, dbuser, dbpass, dbhost, dbname): + try: + conn = psycopg2.connect( + host=dbhost, + user=dbuser, + password=dbpass, + database=dbname + ) + cur = conn.cursor() + + cur.execute(""" + SELECT permissions FROM services + WHERE id = %s + """, (service_id,)) + + service_perms = cur.fetchone() + + print(service_perms) + + cur.execute(""" + SELECT value FROM userData + WHERE property = %s AND user_id = %s AND service_id = %s + """, (record_key, user_id, service_id)) + + service_record = cur.fetchone() + + cur.close() + conn.close() + + if service_record: + return service_record[0] + else: + return None + + except Exception as e: + print(f"Error retrieving user service data: {e}") + raise + +def createUserServiceData(key, value, service_id, user_id, dbuser, dbpass, dbhost, dbname): + try: + conn = psycopg2.connect( + host=dbhost, + user=dbuser, + password=dbpass, + database=dbname + ) + cur = conn.cursor() + + cur.execute(""" + INSERT INTO userData (user_id, service_id, property, value) + VALUES (%s, %s) RETURNING id + """, (user_id, service_id, key, value)) + + row = cur.fetchone() + + conn.commit() + + cur.close() + conn.close() + + return "Success" + + except Exception as e: + print(f"Error creating service: {e}") + raise + + +def createService(name, permissions, dbuser, dbpass, dbhost, dbname): + try: + conn = psycopg2.connect( + host=dbhost, + user=dbuser, + password=dbpass, + database=dbname + ) + cur = conn.cursor() + + cur.execute(""" + INSERT INTO services (name, permissions) + VALUES (%s, %s) RETURNING id, key + """, (name, permissions)) + + row = cur.fetchone() + + serviceData = { + "id": row[0], + "key": row[1] + } + conn.commit() + + cur.close() + conn.close() + + return serviceData + + except Exception as e: + print(f"Error creating service: {e}") + raise + def createToken(user_id, dbuser, dbpass, dbhost, dbname): try: conn = psycopg2.connect( diff --git a/initdb.py b/initdb.py index 169c643..fef9af3 100644 --- a/initdb.py +++ b/initdb.py @@ -49,6 +49,27 @@ def createTables(dbuser, dbpass, dbhost, dbname): """) print("Table 'groups' created or already exists") + cur.execute(""" + CREATE TABLE IF NOT EXISTS services ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + key uuid UNIQUE DEFAULT gen_random_uuid(), + name VARCHAR(255) UNIQUE NOT NULL, + permissions JSON, + creation_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + print("Table 'services' created or already exists") + + cur.execute(""" + CREATE TABLE IF NOT EXISTS requestTokens ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + owner_id uuid REFERENCES users(id) + creation_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + expiration_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP + INTERVAL '1 hour' + ) + """) + print("Table 'requestTokens' created or already exists") + cur.execute(""" CREATE TABLE IF NOT EXISTS users ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), @@ -66,7 +87,7 @@ def createTables(dbuser, dbpass, dbhost, dbname): CREATE TABLE IF NOT EXISTS userData ( id SERIAL PRIMARY KEY, user_id uuid REFERENCES users(id), - service_id VARCHAR(255) NOT NULL, + service_id uuid NOT NULL, creation_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, property VARCHAR(255) NOT NULL, value VARCHAR(255) NOT NULL @@ -78,7 +99,7 @@ def createTables(dbuser, dbpass, dbhost, dbname): CREATE TABLE IF NOT EXISTS groupData ( id SERIAL PRIMARY KEY, group_id uuid REFERENCES groups(id), - service_id VARCHAR(255) NOT NULL, + service_id uuid NOT NULL, creation_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, property VARCHAR(255) NOT NULL, value VARCHAR(255) NOT NULL diff --git a/logout.html b/logout.html new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py index 578a4ab..acb48e9 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,15 @@ import flask -from flask import render_template, jsonify, request, redirect, Response +from flask import render_template, jsonify, request, redirect, Response, json import initdb import db from dotenv import load_dotenv from os import getenv +with open("config.json", "r") as f: + config = json.load(f) + +print(config) + load_dotenv() DB_HOST = getenv("DB_HOST") @@ -54,17 +59,76 @@ def logout(): db.removeToken(token, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) return render_template('logout.html', appName=appName) +@app.route('/login/service/', methods = ['GET']) +def logIntoServiceWebsite(serviceid): + token = request.cookies.get('auth_token', 'none') + redirectUrl = request.args.get("redirect") + userId = db.verifyToken(token, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + if (userId == None): + return render_template('login.html', appName=appName) + else: + serviceData = db.getServiceById(serviceid, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + if not serviceData: + return jsonify("Service not found") + else: + serviceName = serviceData["name"] + requestToken = db.createRequestToken(userId, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + if db.getUserServiceData(userId, serviceid, "approved", DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) == "True": + return render_template('logbackintoservice.html', appName=appName, serviceName=serviceName, requestToken=requestToken, serviceId=serviceid, redirectUrl=redirectUrl) + else: + permissions = [] + for permission in serviceData["permissions"]: + permissions.append(config["permissionDetails"][permission]) + return render_template('approveservice.html', appName=appName, serviceName=serviceName, permissions=permissions, requestToken=requestToken, serviceId=serviceid, redirectUrl = redirectUrl) + +@app.route('/api/login/service', methods = ['POST']) +def logIntoService(): + requestId = request.json["request_id"] + serviceId = request.json["service_id"] + redirectURL = request.json["redirect_url"] + token = request.cookies.get('auth_token', 'none') + userId = db.verifyToken(token, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + requestUserId = db.checkRequestToken(requestId, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + if userId == requestUserId: + if db.getUserServiceData(userId, serviceId, "approved", DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) != "True": + db.createUserServiceData("approved", "True", serviceId, userId, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + serviceToken = "TEST FOR NOW, FIX IT FIRST THING" + # TODO: Generate token for service to access data + redirectURL = redirectURL + "?token=" + serviceToken + return redirect(redirectURL) + else: + +@app.route('/api/createservice', methods = ['POST']) +def createService(): + try: + name = request.json['name'] + permissions = json.dumps(request.json['permissions']) + print(permissions) + serviceData =db.createService(name, permissions, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + print(serviceData) + return jsonify({"success": True, "service": serviceData}) + except Exception as e: + print(f"Signup error: {e}") + return jsonify({"success": False, "error": str(e)}) + @app.route('/api/signup', methods = ['POST']) def handleSignup(): try: username = request.json['username'].lower() email = request.json['email'].lower() password = request.json['password'] - displayName = request.json['displayname'] - db.createUser(displayName, username, email, password, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) - except: - return jsonify("An error occured") - + name = request.json['name'] + root_group = db.getGroupByName("root", DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + group_id = root_group['id'] if root_group else None + db.createUser(name, username, email, password, group_id, DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) + return jsonify({"success": True}) + except Exception as e: + print(f"Signup error: {e}") + return jsonify({"success": False, "error": str(e)}) + +@app.route('/testpage/', methods = ['GET']) +def testPage(pageStr): + return render_template(pageStr, appName=appName, serviceName="Test Service") if __name__ == '__main__': initdb.createDatabase(DB_USER, DB_PASSWORD, DB_HOST, DB_NAME) diff --git a/templates/approveservice.html b/templates/approveservice.html new file mode 100644 index 0000000..196fc74 --- /dev/null +++ b/templates/approveservice.html @@ -0,0 +1,518 @@ + + + + + + Approve Service | {{ appName }} + + + + + + + + + + \ No newline at end of file diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..d01f779 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,11 @@ + + + + + + Document + + + + + \ No newline at end of file diff --git a/templates/logbackintoservice.html b/templates/logbackintoservice.html new file mode 100644 index 0000000..684e301 --- /dev/null +++ b/templates/logbackintoservice.html @@ -0,0 +1,459 @@ + + + + + + Approve Service | {{ appName }} + + + + + + + + + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html index 0189575..5271856 100644 --- a/templates/login.html +++ b/templates/login.html @@ -280,6 +280,43 @@ cursor: pointer; padding: 0.5rem; } + + /* Mobile and popup responsiveness */ + @media (max-width: 768px) { + body { + padding: 0; + align-items: stretch; + justify-content: stretch; + } + + .login-card { + max-width: none; + width: 100%; + height: 100vh; + border-radius: 0; + border: none; + box-shadow: none; + } + + .card-content { + padding: 1.5rem; + height: 100%; + justify-content: flex-start; + padding-top: 2rem; + } + + .brand-header { + margin-bottom: 1.5rem; + } + + h2 { + font-size: 1.5rem; + } + + .description { + font-size: 0.75rem; + } + } @@ -313,6 +350,10 @@ + +
+

Don't have an account? Sign up

+
@@ -396,6 +437,20 @@ + +
+
+
+ +
+ +

You have been logged out

+

Your session has ended. You have been successfully logged out of your account.

+ + + + Back to Login + +
+
+ + \ No newline at end of file diff --git a/templates/signup.html b/templates/signup.html new file mode 100644 index 0000000..d3b39f3 --- /dev/null +++ b/templates/signup.html @@ -0,0 +1,683 @@ + + + + + + Sign Up | {{ appName }} + + + + + + + + + + \ No newline at end of file