User management and module improvements

- Added daily_mirror module to permissions system
- Fixed user module management - updates now work correctly
- Implemented dashboard module filtering based on user permissions
- Fixed warehouse create_locations page (config parser and delete)
- Implemented POST-Redirect-GET pattern to prevent duplicate entries
- Added application license system with validation middleware
- Cleaned up debug logging code
- Improved user module selection with fetch API instead of form submit
This commit is contained in:
ske087
2025-11-29 14:16:36 +02:00
parent 3e314332a7
commit 7912885046
9 changed files with 355 additions and 69 deletions

View File

@@ -29,6 +29,28 @@ from .access_control import (
bp = Blueprint('main', __name__)
warehouse_bp = Blueprint('warehouse', __name__)
def check_app_license():
"""Check if the application license is valid."""
license_path = os.path.join(current_app.instance_path, 'app_license.json')
# If no license file exists, return invalid
if not os.path.exists(license_path):
return False, '⚠️ Application License Expired\n\nThis application requires a valid license to operate.\nPlease contact your superadmin to renew the license.'
try:
with open(license_path, 'r') as f:
license_data = json.load(f)
valid_until = datetime.strptime(license_data['valid_until'], '%Y-%m-%d')
# Check if license is still valid
if datetime.utcnow().date() > valid_until.date():
return False, f'⚠️ Application License Expired\n\nThe license expired on {license_data["valid_until"]}.\nPlease contact your superadmin to renew the license.'
return True, 'License valid'
except Exception as e:
return False, f'⚠️ License Validation Error\n\nCould not validate application license.\nPlease contact your superadmin.'
@bp.route('/main_scan')
@requires_quality_module
def main_scan():
@@ -97,6 +119,15 @@ def login():
user_modules = ['quality', 'warehouse', 'labels', 'daily_mirror']
session['modules'] = user_modules
# Check app license for non-superadmin users
if user['role'] != 'superadmin':
license_valid, license_message = check_app_license()
if not license_valid:
session.clear()
flash(license_message, 'danger')
return render_template('login.html')
print("Logged in as:", session.get('user'), session.get('role'), "modules:", user_modules)
return redirect(url_for('main.dashboard'))
else:
@@ -191,7 +222,16 @@ 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')
# Get user's modules and role
user_role = session.get('role')
user_modules = session.get('modules', [])
# Superadmin and admin see all modules
if user_role in ['superadmin', 'admin']:
user_modules = ['quality', 'warehouse', 'labels', 'daily_mirror']
return render_template('dashboard.html', user_modules=user_modules, user_role=user_role)
@bp.route('/settings')
@admin_plus
@@ -402,7 +442,7 @@ def quick_update_modules():
# Get current user to validate role
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT username, role FROM users WHERE id=%s", (user_id,))
cursor.execute("SELECT username, role, modules FROM users WHERE id=%s", (user_id,))
user_row = cursor.fetchone()
if not user_row:
@@ -410,11 +450,12 @@ def quick_update_modules():
conn.close()
return redirect(url_for('main.user_management_simple'))
username, role = user_row
username, role, current_modules = user_row
# Validate modules for the role
from app.permissions_simple import validate_user_modules
is_valid, error_msg = validate_user_modules(role, modules)
if not is_valid:
flash(f'Invalid module assignment: {error_msg}')
conn.close()
@@ -425,18 +466,25 @@ def quick_update_modules():
if modules and role in ['manager', 'worker']:
import json
modules_json = json.dumps(modules)
elif not modules and role in ['manager', 'worker']:
# Empty modules list for manager/worker
import json
modules_json = json.dumps([])
# Update modules only
cursor.execute("UPDATE users SET modules=%s WHERE id=%s", (modules_json, user_id))
conn.commit()
conn.close()
flash(f'Modules updated successfully for user "{username}".')
flash(f'Modules updated successfully for user "{username}". New modules: {", ".join(modules) if modules else "None"}', 'success')
return redirect(url_for('main.user_management_simple'))
except Exception as e:
print(f"Error updating modules: {e}")
flash('Error updating modules.')
print(f"ERROR updating modules: {e}")
import traceback
traceback.print_exc()
flash(f'Error updating modules: {str(e)}', 'danger')
return redirect(url_for('main.user_management_simple'))
@bp.route('/reports')
@@ -2398,7 +2446,64 @@ def download_extension():
keys = []
except Exception as e:
keys = []
return render_template('download_extension.html', pairing_keys=keys)
# Load app license key
license_path = os.path.join(current_app.instance_path, 'app_license.json')
license_data = None
try:
if os.path.exists(license_path):
with open(license_path, 'r') as f:
license_data = json.load(f)
# Calculate days remaining
valid_until = datetime.strptime(license_data['valid_until'], '%Y-%m-%d')
days_remaining = (valid_until.date() - datetime.utcnow().date()).days
license_data['days_remaining'] = days_remaining
except Exception as e:
license_data = None
return render_template('download_extension.html', pairing_keys=keys, license_data=license_data)
@bp.route('/generate_app_license', methods=['POST'])
@superadmin_only
def generate_app_license():
"""Generate a license key for the application."""
validity_days = int(request.form.get('validity_days', 365)) # Default to 365 days
# Generate a secure license key
license_key = secrets.token_urlsafe(48)
valid_until = (datetime.utcnow() + timedelta(days=validity_days)).strftime('%Y-%m-%d')
created_at = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
# Create license data
license_data = {
'license_key': license_key,
'valid_until': valid_until,
'created_at': created_at,
'validity_days': validity_days
}
# Save license key
license_path = os.path.join(current_app.instance_path, 'app_license.json')
with open(license_path, 'w') as f:
json.dump(license_data, f, indent=2)
flash(f'App license generated successfully (valid for {validity_days} days until {valid_until}).', 'success')
return redirect(url_for('main.download_extension'))
@bp.route('/revoke_app_license', methods=['POST'])
@superadmin_only
def revoke_app_license():
"""Revoke the application license."""
license_path = os.path.join(current_app.instance_path, 'app_license.json')
if os.path.exists(license_path):
os.remove(license_path)
flash('App license revoked successfully.', 'success')
else:
flash('No license key found to revoke.', 'warning')
return redirect(url_for('main.download_extension'))
@bp.route('/extension_files/<path:filename>')
def extension_files(filename):