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:
ske087
2026-07-08 21:35:16 +03:00
parent 6034a62b08
commit 7d24e7f527
17 changed files with 320 additions and 29 deletions
+24 -6
View File
@@ -1,18 +1,36 @@
from flask import Blueprint, redirect, url_for, current_app
from flask_login import logout_user, login_required
from datetime import datetime
from flask import Blueprint, redirect, url_for, render_template, request, flash
from flask_login import login_user, logout_user, login_required, current_user
from app.extensions import db
from app.models.admin_user import AdminUser
bp = Blueprint('auth', __name__)
@bp.route('/login', methods=['GET', 'POST'])
def login():
# Authentication is handled by the EDP portal via nginx SSO.
# Redirect any direct login attempts to the portal.
return redirect(current_app.config.get('PORTAL_LOGIN_URL', 'http://localhost:8080/login'))
# Portal SSO (nginx headers) handles production login automatically.
# This route handles direct / standalone access.
if current_user.is_authenticated:
return redirect(url_for('dashboard.index'))
if request.method == 'POST':
username = request.form.get('username', '').strip()
password = request.form.get('password', '')
user = AdminUser.query.filter_by(username=username, is_active=True).first()
if user and user.check_password(password):
login_user(user, remember=False)
user.last_login = datetime.utcnow()
db.session.commit()
next_page = request.args.get('next') or url_for('dashboard.index')
return redirect(next_page)
flash('Invalid username or password.', 'danger')
return render_template('auth/login.html')
@bp.route('/logout')
@login_required
def logout():
logout_user()
return redirect(current_app.config.get('PORTAL_LOGOUT_URL', 'http://localhost:8080/logout'))
return redirect(url_for('auth.login'))