diff --git a/app.py b/app.py index da37378..7a0d3b6 100644 --- a/app.py +++ b/app.py @@ -162,7 +162,8 @@ from database import ( HourlyActivity, AuditLog, SecurityAlert, - ZOPKNews + ZOPKNews, + SystemRole ) # Import services @@ -291,7 +292,7 @@ def is_admin_exempt(): """Exempt logged-in admins from rate limiting.""" from flask_login import current_user try: - return current_user.is_authenticated and current_user.is_admin + return current_user.is_authenticated and current_user.has_role(SystemRole.ADMIN) except Exception: return False diff --git a/blueprints/admin/routes.py b/blueprints/admin/routes.py index b155311..9418e12 100644 --- a/blueprints/admin/routes.py +++ b/blueprints/admin/routes.py @@ -158,7 +158,7 @@ def admin_users(): companies = db.query(Company).order_by(Company.name).all() total_users = len(users) - admin_count = sum(1 for u in users if u.is_admin) + admin_count = sum(1 for u in users if u.has_role(SystemRole.ADMIN)) verified_count = sum(1 for u in users if u.is_verified) unverified_count = total_users - verified_count @@ -203,12 +203,13 @@ def admin_user_add(): password_hash=password_hash, name=data.get('name', '').strip() or None, company_id=data.get('company_id') or None, - is_admin=data.get('is_admin', False), is_verified=data.get('is_verified', True), is_active=True ) db.add(new_user) + if data.get('is_admin', False): + new_user.set_role(SystemRole.ADMIN) db.commit() db.refresh(new_user) @@ -243,15 +244,20 @@ def admin_user_toggle_admin(user_id): if not user: return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404 - user.is_admin = not user.is_admin + if user.has_role(SystemRole.ADMIN): + user.set_role(SystemRole.MEMBER) + else: + user.set_role(SystemRole.ADMIN) db.commit() - logger.info(f"Admin {current_user.email} {'granted' if user.is_admin else 'revoked'} admin for user {user.email}") + is_now_admin = user.has_role(SystemRole.ADMIN) + logger.info(f"Admin {current_user.email} {'granted' if is_now_admin else 'revoked'} admin for user {user.email}") return jsonify({ 'success': True, - 'is_admin': user.is_admin, - 'message': f"{'Nadano' if user.is_admin else 'Odebrano'} uprawnienia admina" + 'is_admin': is_now_admin, + 'role': user.role, + 'message': f"{'Nadano' if is_now_admin else 'Odebrano'} uprawnienia admina" }) finally: db.close() diff --git a/blueprints/admin/routes_companies.py b/blueprints/admin/routes_companies.py index 8699be9..a6cd3ab 100644 --- a/blueprints/admin/routes_companies.py +++ b/blueprints/admin/routes_companies.py @@ -504,7 +504,7 @@ def admin_company_users(company_id): 'id': u.id, 'name': u.name, 'email': u.email, - 'is_admin': u.is_admin, + 'role': u.role, 'is_verified': u.is_verified } for u in users] diff --git a/blueprints/admin/routes_security.py b/blueprints/admin/routes_security.py index 28e2c92..bd056c5 100644 --- a/blueprints/admin/routes_security.py +++ b/blueprints/admin/routes_security.py @@ -67,7 +67,7 @@ def admin_security(): User.totp_enabled == True ).count() total_admins = db.query(User).filter( - User.is_admin == True + User.role == 'ADMIN' ).count() # Alert type breakdown diff --git a/blueprints/admin/routes_status.py b/blueprints/admin/routes_status.py index b134f18..fd45887 100644 --- a/blueprints/admin/routes_status.py +++ b/blueprints/admin/routes_status.py @@ -307,7 +307,7 @@ def admin_status(): # Users statistics try: - app_metrics['admins'] = db.query(User).filter(User.is_admin == True).count() + app_metrics['admins'] = db.query(User).filter(User.role == 'ADMIN').count() app_metrics['users_with_2fa'] = db.query(User).filter(User.totp_enabled == True).count() except Exception: db.rollback() diff --git a/blueprints/admin/routes_users_api.py b/blueprints/admin/routes_users_api.py index 56ccaaa..074ab7b 100644 --- a/blueprints/admin/routes_users_api.py +++ b/blueprints/admin/routes_users_api.py @@ -48,7 +48,7 @@ INSTRUKCJE: - email (WYMAGANY - jeśli brak prawidłowego emaila, pomiń użytkownika) - imię i nazwisko (jeśli dostępne) - firma (dopasuj do listy dostępnych firm po nazwie, nawet częściowej) - - rola: jeśli tekst zawiera słowa "admin", "administrator", "zarząd" przy danej osobie - ustaw is_admin na true + - rola: jeśli tekst zawiera słowa "admin", "administrator", "zarząd" przy danej osobie - ustaw role na "ADMIN", w przeciwnym razie "MEMBER" 3. Jeśli email jest niepoprawny (brak @), dodaj ostrzeżenie 4. Jeśli firma nie pasuje do żadnej z listy, ustaw company_id na null @@ -61,7 +61,7 @@ ZWRÓĆ TYLKO CZYSTY JSON w dokładnie takim formacie (bez żadnego tekstu przed "name": "Imię Nazwisko lub null", "company_id": 123, "company_name": "Nazwa dopasowanej firmy lub null", - "is_admin": false, + "role": "MEMBER", "warnings": [] }} ] @@ -95,7 +95,7 @@ ZWRÓĆ TYLKO CZYSTY JSON w dokładnie takim formacie (bez żadnego tekstu przed "name": "Imię Nazwisko lub null", "company_id": 123, "company_name": "Nazwa dopasowanej firmy lub null", - "is_admin": false, + "role": "MEMBER", "warnings": [] }} ] @@ -267,11 +267,14 @@ def admin_users_bulk_create(): password_hash=password_hash, name=user_data.get('name', '').strip() or None, company_id=company_id, - is_admin=user_data.get('is_admin', False), is_verified=True, is_active=True ) db.add(new_user) + # Set role based on AI parse result (supports both old is_admin and new role field) + ai_role = user_data.get('role', 'MEMBER') + if ai_role == 'ADMIN' or user_data.get('is_admin', False): + new_user.set_role(SystemRole.ADMIN) db.flush() # Get the ID created.append({ diff --git a/blueprints/api/routes_company.py b/blueprints/api/routes_company.py index aac7858..b9d913a 100644 --- a/blueprints/api/routes_company.py +++ b/blueprints/api/routes_company.py @@ -512,7 +512,7 @@ def api_enrich_company_ai(company_id): }), 404 # Check permissions: user with company edit rights - logger.info(f"Permission check: user={current_user.email}, is_admin={current_user.is_admin}, user_company_id={current_user.company_id}, target_company_id={company.id}") + logger.info(f"Permission check: user={current_user.email}, role={current_user.role}, user_company_id={current_user.company_id}, target_company_id={company.id}") if not current_user.can_edit_company(company.id): return jsonify({ 'success': False, diff --git a/blueprints/forum/routes.py b/blueprints/forum/routes.py index 1e71aba..6123f13 100644 --- a/blueprints/forum/routes.py +++ b/blueprints/forum/routes.py @@ -1066,7 +1066,7 @@ def report_content(): # Notify admins about the report try: - admin_users = db.query(User).filter(User.is_admin == True, User.is_active == True).all() + admin_users = db.query(User).filter(User.role == 'ADMIN', User.is_active == True).all() admin_ids = [u.id for u in admin_users] reporter_name = current_user.name or current_user.email.split('@')[0] create_forum_report_notification( diff --git a/blueprints/membership/routes.py b/blueprints/membership/routes.py index de8950c..73204f6 100644 --- a/blueprints/membership/routes.py +++ b/blueprints/membership/routes.py @@ -400,7 +400,7 @@ def accept_changes(app_id): application.proposed_changes_comment = None # Create notification for admins - admins = db.query(User).filter(User.is_admin == True).all() + admins = db.query(User).filter(User.role == 'ADMIN').all() for admin in admins: notification = UserNotification( user_id=admin.id, @@ -500,7 +500,7 @@ def reject_changes(app_id): application.updated_at = datetime.now() # Create notification for admins - admins = db.query(User).filter(User.is_admin == True).all() + admins = db.query(User).filter(User.role == 'ADMIN').all() for admin in admins: notification = UserNotification( user_id=admin.id, diff --git a/database.py b/database.py index daceb79..f7f4ccd 100644 --- a/database.py +++ b/database.py @@ -1923,8 +1923,8 @@ class NordaEvent(Base): if not user or not user.is_authenticated: return False - # Admins can see everything - if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER): + # Admins and office managers can see everything + if user.has_role(SystemRole.OFFICE_MANAGER): return True access = self.access_level or 'members_only' @@ -1948,8 +1948,8 @@ class NordaEvent(Base): if not user or not user.is_authenticated: return False - # Admins can attend everything - if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER): + # Admins and office managers can attend everything + if user.has_role(SystemRole.OFFICE_MANAGER): return True access = self.access_level or 'members_only' @@ -1972,8 +1972,8 @@ class NordaEvent(Base): if not user or not user.is_authenticated: return False - # Admins can see attendees - if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER): + # Admins and office managers can see attendees + if user.has_role(SystemRole.OFFICE_MANAGER): return True access = self.access_level or 'members_only' diff --git a/scripts/register_whatsapp_attendees.py b/scripts/register_whatsapp_attendees.py index bfee49d..1979553 100644 --- a/scripts/register_whatsapp_attendees.py +++ b/scripts/register_whatsapp_attendees.py @@ -52,7 +52,6 @@ def main(): name=name, password_hash=generate_password_hash(temp_password), is_active=True, - is_admin=False, company_id=company.id if company else None, created_at=datetime.now() ) diff --git a/scripts/test_ai_proposal.py b/scripts/test_ai_proposal.py index 7bd78f4..866e7b9 100644 --- a/scripts/test_ai_proposal.py +++ b/scripts/test_ai_proposal.py @@ -27,7 +27,7 @@ def main(): db = SessionLocal() try: company = db.query(Company).filter_by(id=12).first() - admin_user = db.query(User).filter_by(is_admin=True).first() + admin_user = db.query(User).filter_by(role='ADMIN').first() if not company: print('Firma nie znaleziona') diff --git a/templates/admin/companies.html b/templates/admin/companies.html index dc68422..03dc803 100644 --- a/templates/admin/companies.html +++ b/templates/admin/companies.html @@ -555,7 +555,7 @@ - {% if company.status == 'archived' and current_user.is_admin %} + {% if company.status == 'archived' and current_user.can_manage_users() %} -