IT Assets: add role-based auth system and portal user sync
Auth: - Fix local login (was redirecting to portal; now authenticates AdminUser directly) - Portal SSO still takes priority in production via nginx headers Role system (admin | editor | readonly): - New app/utils/decorators.py with editor_required and admin_required decorators - All write routes protected with editor_required (create/edit/delete/import/mask) - Settings user management protected with admin_required - Sidebar hides write-only links for readonly users - Dashboard quick actions and list page buttons hidden for readonly Settings page: - Role colour badges (admin=red, editor=blue, readonly=grey) - Inline role changer per user (dropdown auto-submit) - Reset password modal per user - Delete user button with confirmation - Add user form includes role selector with legend Portal user sync: - New /internal/sync-user endpoint receives user pre-creation from portal - INTERNAL_SYNC_SECRET added to config - portal/config.py: added internal_url for itassets app so _sync_user_to_app works
This commit is contained in:
@@ -4,6 +4,7 @@ from flask import (Blueprint, render_template, redirect, url_for,
|
||||
flash, request, current_app, jsonify)
|
||||
from flask_login import login_required, current_user
|
||||
from app.extensions import db
|
||||
from app.utils.decorators import editor_required
|
||||
from app.models.user import User
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.services.csv_service import parse_users_csv
|
||||
@@ -63,6 +64,7 @@ def index():
|
||||
# ------------------------------------------------------------------
|
||||
@bp.route('/new', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@editor_required
|
||||
def create():
|
||||
if request.method == 'POST':
|
||||
windows_id = request.form.get('windows_id', '').strip()
|
||||
@@ -113,6 +115,7 @@ def detail(user_id):
|
||||
# ------------------------------------------------------------------
|
||||
@bp.route('/<int:user_id>/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@editor_required
|
||||
def edit(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
|
||||
@@ -148,6 +151,7 @@ def edit(user_id):
|
||||
# ------------------------------------------------------------------
|
||||
@bp.route('/<int:user_id>/mask', methods=['POST'])
|
||||
@login_required
|
||||
@editor_required
|
||||
def mask(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
|
||||
@@ -191,6 +195,7 @@ def import_page():
|
||||
# ------------------------------------------------------------------
|
||||
@bp.route('/import/csv', methods=['POST'])
|
||||
@login_required
|
||||
@editor_required
|
||||
def import_csv():
|
||||
file = request.files.get('csv_file')
|
||||
if not file or not file.filename.endswith('.csv'):
|
||||
@@ -249,6 +254,7 @@ def import_csv():
|
||||
# ------------------------------------------------------------------
|
||||
@bp.route('/import/ldap', methods=['POST'])
|
||||
@login_required
|
||||
@editor_required
|
||||
def import_ldap():
|
||||
if not current_app.config.get('LDAP_SERVER'):
|
||||
flash('LDAP server is not configured. Update Settings first.', 'danger')
|
||||
|
||||
Reference in New Issue
Block a user