diff --git a/py_app/app/__pycache__/__init__.cpython-312.pyc b/py_app/app/__pycache__/__init__.cpython-312.pyc index 7f35629..e34e2d3 100644 Binary files a/py_app/app/__pycache__/__init__.cpython-312.pyc and b/py_app/app/__pycache__/__init__.cpython-312.pyc differ diff --git a/py_app/app/__pycache__/routes.cpython-312.pyc b/py_app/app/__pycache__/routes.cpython-312.pyc index c3f9a40..7e521c0 100644 Binary files a/py_app/app/__pycache__/routes.cpython-312.pyc and b/py_app/app/__pycache__/routes.cpython-312.pyc differ diff --git a/py_app/app/__pycache__/settings.cpython-312.pyc b/py_app/app/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000..e6e5e39 Binary files /dev/null and b/py_app/app/__pycache__/settings.cpython-312.pyc differ diff --git a/py_app/app/__pycache__/warehouse.cpython-312.pyc b/py_app/app/__pycache__/warehouse.cpython-312.pyc index 82cd5f0..a4f8703 100644 Binary files a/py_app/app/__pycache__/warehouse.cpython-312.pyc and b/py_app/app/__pycache__/warehouse.cpython-312.pyc differ diff --git a/py_app/app/db_create_scripts/create_roles_table.py b/py_app/app/db_create_scripts/create_roles_table.py new file mode 100644 index 0000000..693e846 --- /dev/null +++ b/py_app/app/db_create_scripts/create_roles_table.py @@ -0,0 +1,45 @@ +import sqlite3 +import os + +def create_roles_and_users_tables(db_path): + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + # Create users table if not exists + cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + role TEXT NOT NULL + ) + ''') + # Insert superadmin user if not exists (default password: 'admin', change after first login) + cursor.execute(''' + INSERT OR IGNORE INTO users (username, password, role) + VALUES (?, ?, ?) + ''', ('superadmin', 'superadmin123', 'superadmin')) + # Create roles table if not exists + cursor.execute(''' + CREATE TABLE IF NOT EXISTS roles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT UNIQUE NOT NULL, + access_level TEXT NOT NULL, + description TEXT + ) + ''') + # Insert superadmin role if not exists + cursor.execute(''' + INSERT OR IGNORE INTO roles (name, access_level, description) + VALUES (?, ?, ?) + ''', ('superadmin', 'full', 'Full access to all app areas and functions')) + conn.commit() + conn.close() + +if __name__ == "__main__": + # Default path to users.db in instance folder + instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance')) + if not os.path.exists(instance_folder): + os.makedirs(instance_folder) + db_path = os.path.join(instance_folder, 'users.db') + create_roles_and_users_tables(db_path) + print("Roles and users tables created. Superadmin user and role initialized.") diff --git a/py_app/app/db_create_scripts/drop_external_users_roles_tables.py b/py_app/app/db_create_scripts/drop_external_users_roles_tables.py new file mode 100644 index 0000000..2eb814e --- /dev/null +++ b/py_app/app/db_create_scripts/drop_external_users_roles_tables.py @@ -0,0 +1,26 @@ +import mariadb +import os + +def get_external_db_connection(): + settings_file = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance/external_server.conf')) + settings = {} + with open(settings_file, 'r') as f: + for line in f: + key, value = line.strip().split('=', 1) + settings[key] = value + return mariadb.connect( + user=settings['username'], + password=settings['password'], + host=settings['server_domain'], + port=int(settings['port']), + database=settings['database_name'] + ) + +if __name__ == "__main__": + conn = get_external_db_connection() + cursor = conn.cursor() + cursor.execute("DROP TABLE IF EXISTS users") + cursor.execute("DROP TABLE IF EXISTS roles") + conn.commit() + conn.close() + print("Dropped users and roles tables from external database.") diff --git a/py_app/app/db_create_scripts/print_internal_users.py b/py_app/app/db_create_scripts/print_internal_users.py new file mode 100644 index 0000000..5a953e0 --- /dev/null +++ b/py_app/app/db_create_scripts/print_internal_users.py @@ -0,0 +1,30 @@ +import sqlite3 +import os + +instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance')) +db_path = os.path.join(instance_folder, 'users.db') + +if not os.path.exists(db_path): + print("users.db not found at", db_path) + exit(1) + +conn = sqlite3.connect(db_path) +cursor = conn.cursor() + +# Check if users table exists +cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='users'") +if not cursor.fetchone(): + print("No users table found in users.db.") + conn.close() + exit(1) + +# Print all users +cursor.execute("SELECT id, username, password, role FROM users") +rows = cursor.fetchall() +if not rows: + print("No users found in users.db.") +else: + print("Users in users.db:") + for row in rows: + print(f"id={row[0]}, username={row[1]}, password={row[2]}, role={row[3]}") +conn.close() diff --git a/py_app/app/db_create_scripts/seed_internal_superadmin.py b/py_app/app/db_create_scripts/seed_internal_superadmin.py new file mode 100644 index 0000000..fd950c3 --- /dev/null +++ b/py_app/app/db_create_scripts/seed_internal_superadmin.py @@ -0,0 +1,34 @@ +import sqlite3 +import os +from flask import Flask + +app = Flask(__name__) +app.config['SECRET_KEY'] = 'your_secret_key' # Use the same key as in __init__.py + +instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance')) +if not os.path.exists(instance_folder): + os.makedirs(instance_folder) +db_path = os.path.join(instance_folder, 'users.db') + +conn = sqlite3.connect(db_path) +cursor = conn.cursor() + +# Create users table if not exists +cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + role TEXT NOT NULL + ) +''') + +# Insert superadmin user if not exists +cursor.execute(''' + INSERT OR IGNORE INTO users (username, password, role) + VALUES (?, ?, ?) +''', ('superadmin', 'superadmin123', 'superadmin')) + +conn.commit() +conn.close() +print("Internal users.db seeded with superadmin user.") diff --git a/py_app/app/routes.py b/py_app/app/routes.py index e166527..fc96280 100644 --- a/py_app/app/routes.py +++ b/py_app/app/routes.py @@ -9,9 +9,17 @@ from reportlab.pdfgen import canvas from flask import Blueprint, render_template, request, redirect, url_for, flash import csv from .warehouse import add_location +from .settings import settings_handler, edit_access_roles_handler bp = Blueprint('main', __name__) warehouse_bp = Blueprint('warehouse', __name__) +bp = Blueprint('main', __name__) +warehouse_bp = Blueprint('warehouse', __name__) + +@bp.route('/update_role_access/', methods=['POST']) +def update_role_access(role): + from .settings import update_role_access_handler + return update_role_access_handler(role) @bp.route('/store_articles') def store_articles(): @@ -48,40 +56,47 @@ def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] - user = User.query.filter_by(username=username, password=password).first() + user = None + print("Raw form input:", repr(username), repr(password)) + # Only check external MariaDB for user authentication + try: + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("SHOW TABLES LIKE 'users'") + if cursor.fetchone(): + cursor.execute("SELECT username, password, role FROM users WHERE username=%s AND password=%s", (username.strip(), password.strip())) + row = cursor.fetchone() + print("External DB query result:", row) + if row: + user = {'username': row[0], 'password': row[1], 'role': row[2]} + conn.close() + except Exception as e: + print("External DB error:", e) if user: - session['user'] = user.username - session['role'] = user.role + session['user'] = user['username'] + session['role'] = user['role'] + print("Logged in as:", session.get('user'), session.get('role')) return redirect(url_for('main.dashboard')) else: + print("Login failed for:", username, password) flash('Invalid credentials. Please try again.') return render_template('login.html') @bp.route('/dashboard') def dashboard(): + print("Session user:", session.get('user'), session.get('role')) if 'user' not in session: return redirect(url_for('main.login')) return render_template('dashboard.html') @bp.route('/settings') def settings(): - if 'role' not in session or session['role'] != 'superadmin': - flash('Access denied: Superadmin only.') - return redirect(url_for('main.dashboard')) - - # Fetch all users from the database - users = User.query.all() + return settings_handler() - # Load external database settings from the instance folder - external_settings = {} - settings_file = os.path.join(current_app.instance_path, 'external_server.conf') - if (os.path.exists(settings_file)): - with open(settings_file, 'r') as f: - for line in f: - key, value = line.strip().split('=', 1) - external_settings[key] = value - - return render_template('settings.html', users=users, external_settings=external_settings) +# Route for editing access roles (superadmin only) +@bp.route('/edit_access_roles') +def edit_access_roles(): + return edit_access_roles_handler() @bp.route('/quality') def quality(): diff --git a/py_app/app/settings.py b/py_app/app/settings.py new file mode 100644 index 0000000..0ea4544 --- /dev/null +++ b/py_app/app/settings.py @@ -0,0 +1,76 @@ +from flask import render_template, request, session, redirect, url_for, flash +from .models import User +from . import db + +# Settings module logic +import sqlite3 +import os +def ensure_roles_table(): + instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance')) + if not os.path.exists(instance_folder): + os.makedirs(instance_folder) + db_path = os.path.join(instance_folder, 'users.db') + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS roles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT UNIQUE NOT NULL, + access_level TEXT NOT NULL, + description TEXT + ) + """) + cursor.execute(""" + INSERT OR IGNORE INTO roles (name, access_level, description) + VALUES (?, ?, ?) + """, ('superadmin', 'full', 'Full access to all app areas and functions')) + conn.commit() + conn.close() +# List of roles (should match your app's roles) +ROLES = [ + 'superadmin', 'admin', 'manager', 'warehouse_manager', 'warehouse_worker', 'quality_manager', 'quality_worker' +] + +# Helper to check if current user is superadmin +def is_superadmin(): + return session.get('role') == 'superadmin' + +# Route handler for editing access roles +def edit_access_roles_handler(): + if not is_superadmin(): + flash('Access denied: Superadmin only.') + return redirect(url_for('main.dashboard')) + ensure_roles_table() + return render_template('edit_access_roles.html', roles=ROLES) + +# Handler for updating role access (stub, to be implemented) +def update_role_access_handler(role): + if not is_superadmin(): + flash('Access denied: Superadmin only.') + return redirect(url_for('main.dashboard')) + if role == 'superadmin': + flash('Superadmin access cannot be changed.') + return redirect(url_for('main.edit_access_roles')) + access_level = request.form.get('access_level') + # TODO: Save access_level for the role in the database or config + flash(f'Access for role {role} updated to {access_level}.') + return redirect(url_for('main.edit_access_roles')) + +def settings_handler(): + if 'role' not in session or session['role'] != 'superadmin': + flash('Access denied: Superadmin only.') + return redirect(url_for('main.dashboard')) + users = User.query.all() + # Load external database settings from the instance folder + external_settings = {} + import os + from flask import current_app + settings_file = os.path.join(current_app.instance_path, 'external_server.conf') + if os.path.exists(settings_file): + with open(settings_file, 'r') as f: + for line in f: + key, value = line.strip().split('=', 1) + external_settings[key] = value + return render_template('settings.html', users=users, external_settings=external_settings) + +# Add more settings-related functions here as needed diff --git a/py_app/app/templates/create_locations.html b/py_app/app/templates/create_locations.html index 05d5649..6a097eb 100644 --- a/py_app/app/templates/create_locations.html +++ b/py_app/app/templates/create_locations.html @@ -25,6 +25,22 @@ Go to Import Page + + {% if session['role'] in ['administrator', 'management'] %} +
+ +
To delete a location, enter the ID of the location and press delete.
To delete 2 or multiple locations, enter the IDs separated by "," and then press delete.
+
+ + +
+ +
+ {% endif %}
diff --git a/py_app/app/templates/edit_access_roles.html b/py_app/app/templates/edit_access_roles.html new file mode 100644 index 0000000..ae53cb1 --- /dev/null +++ b/py_app/app/templates/edit_access_roles.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} +{% block title %}Edit Access Roles{% endblock %} +{% block content %} +
+

Role Access Management

+

Configure which roles can view or execute functions on each app page and feature.

+ + + + + + + + + + + + + + + {% for role in roles %} + {% if role != 'superadmin' %} + + + + + + {% endif %} + {% endfor %} + +
RoleAccess LevelEditable
superadminFull access to all pages and functionsNot editable
{{ role }} +
+ + +
+
Editable
+

Only superadmin users can view and manage role access.

+
+{% endblock %} diff --git a/py_app/app/templates/import_locations_csv.html b/py_app/app/templates/import_locations_csv.html index 6bd6635..1d287d8 100644 --- a/py_app/app/templates/import_locations_csv.html +++ b/py_app/app/templates/import_locations_csv.html @@ -37,9 +37,9 @@
-