refactor(rbac): Migrate legacy is_admin checks to role-based has_role()/set_role()
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Replace ~20 remaining is_admin references across backend, templates and scripts with proper SystemRole checks. Column is_admin stays as deprecated (synced by set_role()) until DB migration removes it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
85e14bb4bf
commit
c0d60481f0
5
app.py
5
app.py
@ -162,7 +162,8 @@ from database import (
|
|||||||
HourlyActivity,
|
HourlyActivity,
|
||||||
AuditLog,
|
AuditLog,
|
||||||
SecurityAlert,
|
SecurityAlert,
|
||||||
ZOPKNews
|
ZOPKNews,
|
||||||
|
SystemRole
|
||||||
)
|
)
|
||||||
|
|
||||||
# Import services
|
# Import services
|
||||||
@ -291,7 +292,7 @@ def is_admin_exempt():
|
|||||||
"""Exempt logged-in admins from rate limiting."""
|
"""Exempt logged-in admins from rate limiting."""
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
try:
|
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:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@ -158,7 +158,7 @@ def admin_users():
|
|||||||
companies = db.query(Company).order_by(Company.name).all()
|
companies = db.query(Company).order_by(Company.name).all()
|
||||||
|
|
||||||
total_users = len(users)
|
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)
|
verified_count = sum(1 for u in users if u.is_verified)
|
||||||
unverified_count = total_users - verified_count
|
unverified_count = total_users - verified_count
|
||||||
|
|
||||||
@ -203,12 +203,13 @@ def admin_user_add():
|
|||||||
password_hash=password_hash,
|
password_hash=password_hash,
|
||||||
name=data.get('name', '').strip() or None,
|
name=data.get('name', '').strip() or None,
|
||||||
company_id=data.get('company_id') 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_verified=data.get('is_verified', True),
|
||||||
is_active=True
|
is_active=True
|
||||||
)
|
)
|
||||||
|
|
||||||
db.add(new_user)
|
db.add(new_user)
|
||||||
|
if data.get('is_admin', False):
|
||||||
|
new_user.set_role(SystemRole.ADMIN)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(new_user)
|
db.refresh(new_user)
|
||||||
|
|
||||||
@ -243,15 +244,20 @@ def admin_user_toggle_admin(user_id):
|
|||||||
if not user:
|
if not user:
|
||||||
return jsonify({'success': False, 'error': 'Użytkownik nie znaleziony'}), 404
|
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()
|
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({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'is_admin': user.is_admin,
|
'is_admin': is_now_admin,
|
||||||
'message': f"{'Nadano' if user.is_admin else 'Odebrano'} uprawnienia admina"
|
'role': user.role,
|
||||||
|
'message': f"{'Nadano' if is_now_admin else 'Odebrano'} uprawnienia admina"
|
||||||
})
|
})
|
||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|||||||
@ -504,7 +504,7 @@ def admin_company_users(company_id):
|
|||||||
'id': u.id,
|
'id': u.id,
|
||||||
'name': u.name,
|
'name': u.name,
|
||||||
'email': u.email,
|
'email': u.email,
|
||||||
'is_admin': u.is_admin,
|
'role': u.role,
|
||||||
'is_verified': u.is_verified
|
'is_verified': u.is_verified
|
||||||
} for u in users]
|
} for u in users]
|
||||||
|
|
||||||
|
|||||||
@ -67,7 +67,7 @@ def admin_security():
|
|||||||
User.totp_enabled == True
|
User.totp_enabled == True
|
||||||
).count()
|
).count()
|
||||||
total_admins = db.query(User).filter(
|
total_admins = db.query(User).filter(
|
||||||
User.is_admin == True
|
User.role == 'ADMIN'
|
||||||
).count()
|
).count()
|
||||||
|
|
||||||
# Alert type breakdown
|
# Alert type breakdown
|
||||||
|
|||||||
@ -307,7 +307,7 @@ def admin_status():
|
|||||||
|
|
||||||
# Users statistics
|
# Users statistics
|
||||||
try:
|
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()
|
app_metrics['users_with_2fa'] = db.query(User).filter(User.totp_enabled == True).count()
|
||||||
except Exception:
|
except Exception:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
|
|||||||
@ -48,7 +48,7 @@ INSTRUKCJE:
|
|||||||
- email (WYMAGANY - jeśli brak prawidłowego emaila, pomiń użytkownika)
|
- email (WYMAGANY - jeśli brak prawidłowego emaila, pomiń użytkownika)
|
||||||
- imię i nazwisko (jeśli dostępne)
|
- imię i nazwisko (jeśli dostępne)
|
||||||
- firma (dopasuj do listy dostępnych firm po nazwie, nawet częściowej)
|
- 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
|
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
|
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",
|
"name": "Imię Nazwisko lub null",
|
||||||
"company_id": 123,
|
"company_id": 123,
|
||||||
"company_name": "Nazwa dopasowanej firmy lub null",
|
"company_name": "Nazwa dopasowanej firmy lub null",
|
||||||
"is_admin": false,
|
"role": "MEMBER",
|
||||||
"warnings": []
|
"warnings": []
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
@ -95,7 +95,7 @@ ZWRÓĆ TYLKO CZYSTY JSON w dokładnie takim formacie (bez żadnego tekstu przed
|
|||||||
"name": "Imię Nazwisko lub null",
|
"name": "Imię Nazwisko lub null",
|
||||||
"company_id": 123,
|
"company_id": 123,
|
||||||
"company_name": "Nazwa dopasowanej firmy lub null",
|
"company_name": "Nazwa dopasowanej firmy lub null",
|
||||||
"is_admin": false,
|
"role": "MEMBER",
|
||||||
"warnings": []
|
"warnings": []
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
@ -267,11 +267,14 @@ def admin_users_bulk_create():
|
|||||||
password_hash=password_hash,
|
password_hash=password_hash,
|
||||||
name=user_data.get('name', '').strip() or None,
|
name=user_data.get('name', '').strip() or None,
|
||||||
company_id=company_id,
|
company_id=company_id,
|
||||||
is_admin=user_data.get('is_admin', False),
|
|
||||||
is_verified=True,
|
is_verified=True,
|
||||||
is_active=True
|
is_active=True
|
||||||
)
|
)
|
||||||
db.add(new_user)
|
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
|
db.flush() # Get the ID
|
||||||
|
|
||||||
created.append({
|
created.append({
|
||||||
|
|||||||
@ -512,7 +512,7 @@ def api_enrich_company_ai(company_id):
|
|||||||
}), 404
|
}), 404
|
||||||
|
|
||||||
# Check permissions: user with company edit rights
|
# 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):
|
if not current_user.can_edit_company(company.id):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
|
|||||||
@ -1066,7 +1066,7 @@ def report_content():
|
|||||||
|
|
||||||
# Notify admins about the report
|
# Notify admins about the report
|
||||||
try:
|
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]
|
admin_ids = [u.id for u in admin_users]
|
||||||
reporter_name = current_user.name or current_user.email.split('@')[0]
|
reporter_name = current_user.name or current_user.email.split('@')[0]
|
||||||
create_forum_report_notification(
|
create_forum_report_notification(
|
||||||
|
|||||||
@ -400,7 +400,7 @@ def accept_changes(app_id):
|
|||||||
application.proposed_changes_comment = None
|
application.proposed_changes_comment = None
|
||||||
|
|
||||||
# Create notification for admins
|
# 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:
|
for admin in admins:
|
||||||
notification = UserNotification(
|
notification = UserNotification(
|
||||||
user_id=admin.id,
|
user_id=admin.id,
|
||||||
@ -500,7 +500,7 @@ def reject_changes(app_id):
|
|||||||
application.updated_at = datetime.now()
|
application.updated_at = datetime.now()
|
||||||
|
|
||||||
# Create notification for admins
|
# 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:
|
for admin in admins:
|
||||||
notification = UserNotification(
|
notification = UserNotification(
|
||||||
user_id=admin.id,
|
user_id=admin.id,
|
||||||
|
|||||||
12
database.py
12
database.py
@ -1923,8 +1923,8 @@ class NordaEvent(Base):
|
|||||||
if not user or not user.is_authenticated:
|
if not user or not user.is_authenticated:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Admins can see everything
|
# Admins and office managers can see everything
|
||||||
if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER):
|
if user.has_role(SystemRole.OFFICE_MANAGER):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
access = self.access_level or 'members_only'
|
access = self.access_level or 'members_only'
|
||||||
@ -1948,8 +1948,8 @@ class NordaEvent(Base):
|
|||||||
if not user or not user.is_authenticated:
|
if not user or not user.is_authenticated:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Admins can attend everything
|
# Admins and office managers can attend everything
|
||||||
if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER):
|
if user.has_role(SystemRole.OFFICE_MANAGER):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
access = self.access_level or 'members_only'
|
access = self.access_level or 'members_only'
|
||||||
@ -1972,8 +1972,8 @@ class NordaEvent(Base):
|
|||||||
if not user or not user.is_authenticated:
|
if not user or not user.is_authenticated:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Admins can see attendees
|
# Admins and office managers can see attendees
|
||||||
if user.is_admin or user.has_role(SystemRole.OFFICE_MANAGER):
|
if user.has_role(SystemRole.OFFICE_MANAGER):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
access = self.access_level or 'members_only'
|
access = self.access_level or 'members_only'
|
||||||
|
|||||||
@ -52,7 +52,6 @@ def main():
|
|||||||
name=name,
|
name=name,
|
||||||
password_hash=generate_password_hash(temp_password),
|
password_hash=generate_password_hash(temp_password),
|
||||||
is_active=True,
|
is_active=True,
|
||||||
is_admin=False,
|
|
||||||
company_id=company.id if company else None,
|
company_id=company.id if company else None,
|
||||||
created_at=datetime.now()
|
created_at=datetime.now()
|
||||||
)
|
)
|
||||||
|
|||||||
@ -27,7 +27,7 @@ def main():
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
company = db.query(Company).filter_by(id=12).first()
|
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:
|
if not company:
|
||||||
print('Firma nie znaleziona')
|
print('Firma nie znaleziona')
|
||||||
|
|||||||
@ -555,7 +555,7 @@
|
|||||||
<path d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
|
<path d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
{% if company.status == 'archived' and current_user.is_admin %}
|
{% if company.status == 'archived' and current_user.can_manage_users() %}
|
||||||
<button class="btn-icon danger" onclick="hardDeleteCompany({{ company.id }}, '{{ company.name|e }}')" title="Trwale usuń">
|
<button class="btn-icon danger" onclick="hardDeleteCompany({{ company.id }}, '{{ company.name|e }}')" title="Trwale usuń">
|
||||||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||||
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||||
|
|||||||
@ -1122,7 +1122,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<tr data-user-id="{{ user.id }}"
|
<tr data-user-id="{{ user.id }}"
|
||||||
data-is-admin="{{ 'true' if user.is_admin else 'false' }}"
|
data-role="{{ user.role }}"
|
||||||
data-is-verified="{{ 'true' if user.is_verified else 'false' }}">
|
data-is-verified="{{ 'true' if user.is_verified else 'false' }}">
|
||||||
<td>{{ user.id }}</td>
|
<td>{{ user.id }}</td>
|
||||||
<td>
|
<td>
|
||||||
@ -1158,7 +1158,7 @@
|
|||||||
{{ user.created_at.strftime('%d.%m.%Y %H:%M') }}
|
{{ user.created_at.strftime('%d.%m.%Y %H:%M') }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if user.is_admin %}
|
{% if user.can_manage_users() %}
|
||||||
<span class="badge badge-admin">Admin</span>
|
<span class="badge badge-admin">Admin</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_rada_member %}
|
{% if user.is_rada_member %}
|
||||||
@ -1182,9 +1182,9 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Toggle Admin -->
|
<!-- Toggle Admin -->
|
||||||
<button class="btn-icon admin-toggle {{ 'active' if user.is_admin else '' }}"
|
<button class="btn-icon admin-toggle {{ 'active' if user.can_manage_users() else '' }}"
|
||||||
onclick="toggleAdmin({{ user.id }})"
|
onclick="toggleAdmin({{ user.id }})"
|
||||||
title="{{ 'Odbierz uprawnienia admina' if user.is_admin else 'Nadaj uprawnienia admina' }}"
|
title="{{ 'Odbierz uprawnienia admina' if user.can_manage_users() else 'Nadaj uprawnienia admina' }}"
|
||||||
{% if user.id == current_user.id %}disabled{% endif %}>
|
{% if user.id == current_user.id %}disabled{% endif %}>
|
||||||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||||
<path d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
|
<path d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
|
||||||
@ -1744,7 +1744,7 @@ Lub format CSV, Excel, lista emaili..."></textarea>
|
|||||||
|
|
||||||
const filter = this.dataset.filter;
|
const filter = this.dataset.filter;
|
||||||
document.querySelectorAll('[data-user-id]').forEach(row => {
|
document.querySelectorAll('[data-user-id]').forEach(row => {
|
||||||
const isAdmin = row.dataset.isAdmin === 'true';
|
const isAdmin = row.dataset.role === 'ADMIN';
|
||||||
const isVerified = row.dataset.isVerified === 'true';
|
const isVerified = row.dataset.isVerified === 'true';
|
||||||
|
|
||||||
let show = false;
|
let show = false;
|
||||||
@ -2434,7 +2434,7 @@ Lub format CSV, Excel, lista emaili..."></textarea>
|
|||||||
</td>
|
</td>
|
||||||
<td>${user.name || '-'}</td>
|
<td>${user.name || '-'}</td>
|
||||||
<td>${user.company_name || '-'}</td>
|
<td>${user.company_name || '-'}</td>
|
||||||
<td>${user.is_admin ? 'Tak' : 'Nie'}</td>
|
<td>${user.role === 'ADMIN' ? 'Tak' : 'Nie'}</td>
|
||||||
<td>
|
<td>
|
||||||
${hasWarnings ? `
|
${hasWarnings ? `
|
||||||
<div class="ai-user-warning">
|
<div class="ai-user-warning">
|
||||||
|
|||||||
@ -684,7 +684,7 @@
|
|||||||
<div class="info-box">
|
<div class="info-box">
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ reviewer.name if reviewer else 'Biuro Izby NORDA' }}</strong>
|
<strong>{{ reviewer.name if reviewer else 'Biuro Izby NORDA' }}</strong>
|
||||||
{% if reviewer and reviewer.is_admin %}<span class="reviewer-role">Biuro Izby NORDA</span>{% endif %}
|
{% if reviewer and reviewer.can_access_admin_panel() %}<span class="reviewer-role">Biuro Izby NORDA</span>{% endif %}
|
||||||
zaproponował(a) aktualizację danych na podstawie oficjalnych danych z
|
zaproponował(a) aktualizację danych na podstawie oficjalnych danych z
|
||||||
{% if application.registry_source == 'KRS' %}
|
{% if application.registry_source == 'KRS' %}
|
||||||
Krajowego Rejestru Sądowego (KRS).
|
Krajowego Rejestru Sądowego (KRS).
|
||||||
@ -730,7 +730,7 @@
|
|||||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
|
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
|
||||||
</svg>
|
</svg>
|
||||||
Komentarz od: <strong>{{ reviewer.name if reviewer else 'Biuro Izby NORDA' }}</strong>
|
Komentarz od: <strong>{{ reviewer.name if reviewer else 'Biuro Izby NORDA' }}</strong>
|
||||||
{% if reviewer and reviewer.is_admin %}<span class="reviewer-role">Biuro Izby NORDA</span>{% endif %}
|
{% if reviewer and reviewer.can_access_admin_panel() %}<span class="reviewer-role">Biuro Izby NORDA</span>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<p>{{ application.proposed_changes_comment }}</p>
|
<p>{{ application.proposed_changes_comment }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -287,9 +287,9 @@ def admin_required(f):
|
|||||||
if not current_user.is_authenticated:
|
if not current_user.is_authenticated:
|
||||||
return redirect(url_for('auth.login'))
|
return redirect(url_for('auth.login'))
|
||||||
|
|
||||||
# Use new role system, fallback to is_admin for backward compatibility
|
# Use role system (is_admin fallback removed — role is source of truth)
|
||||||
SystemRole = _get_system_role()
|
SystemRole = _get_system_role()
|
||||||
if not (current_user.has_role(SystemRole.ADMIN) or current_user.is_admin):
|
if not current_user.has_role(SystemRole.ADMIN):
|
||||||
flash('Brak uprawnień administratora.', 'error')
|
flash('Brak uprawnień administratora.', 'error')
|
||||||
return redirect(url_for('public.index'))
|
return redirect(url_for('public.index'))
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user