Implements 6-tier role hierarchy: - ADMIN (100): Full system access - OFFICE_MANAGER (50): Admin panel without user management - MANAGER (40): Full company control + user management - EMPLOYEE (30): Edit company data (with delegated permissions) - MEMBER (20): Full content access (forum, contacts, chat) - UNAFFILIATED (10): Public profiles only Features: - SystemRole and CompanyRole enums in database.py - UserCompanyPermissions model for delegation - New decorators: @role_required(), @company_permission() - Auto-detection of MANAGER role from KRS data - Backward compatible with is_admin flag Migration: 035_add_role_system.sql Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
355 lines
9.3 KiB
Python
355 lines
9.3 KiB
Python
"""
|
|
Permission Helpers
|
|
==================
|
|
|
|
Helper functions for complex permission checks beyond simple role checks.
|
|
|
|
This module provides:
|
|
- Content access checks (company profiles, contact info)
|
|
- Feature access checks (forum, chat, classifieds)
|
|
- Data visibility filters (what user can see)
|
|
|
|
Usage:
|
|
from utils.permissions import can_view_contact_info, filter_visible_companies
|
|
|
|
if can_view_contact_info(current_user, company):
|
|
show_contact_details()
|
|
|
|
visible_companies = filter_visible_companies(current_user, all_companies)
|
|
"""
|
|
|
|
from typing import List, Optional, TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from database import User, Company, SystemRole
|
|
|
|
|
|
def can_view_contact_info(user: 'User', target_company: 'Company' = None) -> bool:
|
|
"""
|
|
Check if user can view contact information (email, phone, address).
|
|
|
|
Non-members (UNAFFILIATED) cannot see contact details.
|
|
Members and above can see all contact info (respecting privacy settings).
|
|
|
|
Args:
|
|
user: The user requesting access
|
|
target_company: The company whose info is being accessed (optional)
|
|
|
|
Returns:
|
|
True if user can view contact information
|
|
"""
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
|
|
return user.can_view_contacts()
|
|
|
|
|
|
def can_view_company_dashboard(user: 'User', company_id: int) -> bool:
|
|
"""
|
|
Check if user can view a company's internal dashboard.
|
|
|
|
Args:
|
|
user: The user requesting access
|
|
company_id: ID of the company dashboard
|
|
|
|
Returns:
|
|
True if user can view the dashboard
|
|
"""
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
|
|
# Admins and Office Managers can view any dashboard
|
|
if user.can_access_admin_panel():
|
|
return True
|
|
|
|
# User can view their own company's dashboard
|
|
return user.company_id == company_id
|
|
|
|
|
|
def can_invite_user_to_company(user: 'User', company_id: int) -> bool:
|
|
"""
|
|
Check if user can invite new users to a company.
|
|
|
|
Only MANAGER role (within company) and ADMIN can invite users.
|
|
|
|
Args:
|
|
user: The user attempting to invite
|
|
company_id: ID of the company to invite to
|
|
|
|
Returns:
|
|
True if user can invite users to this company
|
|
"""
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
|
|
return user.can_manage_company(company_id)
|
|
|
|
|
|
def get_editable_company_fields(user: 'User', company_id: int) -> List[str]:
|
|
"""
|
|
Get list of company profile fields that user can edit.
|
|
|
|
Access is determined by:
|
|
1. System role (OFFICE_MANAGER/ADMIN can edit everything)
|
|
2. Company role (MANAGER has full access to their company)
|
|
3. Delegated permissions (EMPLOYEE gets specific permissions from MANAGER)
|
|
|
|
Args:
|
|
user: The user requesting edit access
|
|
company_id: ID of the company to edit
|
|
|
|
Returns:
|
|
List of field names user can edit
|
|
"""
|
|
from database import SystemRole, CompanyRole
|
|
|
|
if not user or not user.is_authenticated:
|
|
return []
|
|
|
|
# Not allowed to edit this company at all
|
|
if not user.can_edit_company(company_id):
|
|
return []
|
|
|
|
# Field categories
|
|
description_fields = [
|
|
'description_short',
|
|
'description_full',
|
|
'core_values',
|
|
'founding_history',
|
|
]
|
|
|
|
services_fields = [
|
|
'services_offered',
|
|
'technologies_used',
|
|
'operational_area',
|
|
'languages_offered',
|
|
]
|
|
|
|
contact_fields = [
|
|
'email',
|
|
'phone',
|
|
'address_street',
|
|
'address_city',
|
|
'address_postal',
|
|
]
|
|
|
|
social_fields = [
|
|
'website',
|
|
# Social media handled via related tables
|
|
]
|
|
|
|
admin_only_fields = [
|
|
'name',
|
|
'legal_name',
|
|
'nip',
|
|
'regon',
|
|
'krs',
|
|
'year_established',
|
|
'employees_count',
|
|
'capital_amount',
|
|
'status',
|
|
'category_id',
|
|
]
|
|
|
|
# Admins and Office Managers can edit everything
|
|
if user.has_role(SystemRole.OFFICE_MANAGER):
|
|
return description_fields + services_fields + contact_fields + social_fields + admin_only_fields
|
|
|
|
# Check user's own company
|
|
if user.company_id != company_id:
|
|
return []
|
|
|
|
# Managers can edit everything except admin-only fields
|
|
if user.company_role_enum >= CompanyRole.MANAGER:
|
|
return description_fields + services_fields + contact_fields + social_fields
|
|
|
|
# Employees get permissions based on delegation
|
|
if user.company_role_enum >= CompanyRole.EMPLOYEE:
|
|
fields = []
|
|
|
|
if user.can_edit_company_field('description'):
|
|
fields.extend(description_fields)
|
|
|
|
if user.can_edit_company_field('services'):
|
|
fields.extend(services_fields)
|
|
|
|
if user.can_edit_company_field('contacts'):
|
|
fields.extend(contact_fields)
|
|
|
|
if user.can_edit_company_field('social'):
|
|
fields.extend(social_fields)
|
|
|
|
return fields
|
|
|
|
return []
|
|
|
|
|
|
def filter_visible_companies(user: 'User', companies: List['Company']) -> List['Company']:
|
|
"""
|
|
Filter companies list based on user's access level.
|
|
|
|
All users can see basic company info (name, category, description).
|
|
Only members can see contact details.
|
|
|
|
This function doesn't filter the list - all companies are visible.
|
|
Use can_view_contact_info() to determine what details to show.
|
|
|
|
Args:
|
|
user: The user viewing companies
|
|
companies: List of companies to filter
|
|
|
|
Returns:
|
|
Same list (filtering happens at template level)
|
|
"""
|
|
# All companies are visible to everyone
|
|
# Contact info visibility is handled in templates using can_view_contact_info
|
|
return companies
|
|
|
|
|
|
def get_chat_access_level(user: 'User') -> str:
|
|
"""
|
|
Get user's NordaGPT chat access level.
|
|
|
|
Args:
|
|
user: The user accessing chat
|
|
|
|
Returns:
|
|
'full' - Full access to all features
|
|
'limited' - Basic Q&A only, no company recommendations
|
|
'none' - No access
|
|
"""
|
|
from database import SystemRole
|
|
|
|
if not user or not user.is_authenticated:
|
|
return 'none'
|
|
|
|
if user.has_role(SystemRole.MEMBER):
|
|
return 'full'
|
|
|
|
# UNAFFILIATED users get limited access
|
|
return 'limited'
|
|
|
|
|
|
def can_access_b2b_classifieds(user: 'User') -> bool:
|
|
"""
|
|
Check if user can access B2B classifieds (tablica ogłoszeń).
|
|
|
|
Requires at least MEMBER role.
|
|
|
|
Args:
|
|
user: The user requesting access
|
|
|
|
Returns:
|
|
True if user can access B2B classifieds
|
|
"""
|
|
from database import SystemRole
|
|
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
|
|
return user.has_role(SystemRole.MEMBER)
|
|
|
|
|
|
def can_create_classified(user: 'User') -> bool:
|
|
"""
|
|
Check if user can create B2B classified ads.
|
|
|
|
Requires at least EMPLOYEE role (must be associated with a company).
|
|
|
|
Args:
|
|
user: The user attempting to create
|
|
|
|
Returns:
|
|
True if user can create classifieds
|
|
"""
|
|
from database import SystemRole
|
|
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
|
|
# Must have a company association and be at least EMPLOYEE
|
|
return user.company_id is not None and user.has_role(SystemRole.EMPLOYEE)
|
|
|
|
|
|
def can_access_calendar(user: 'User') -> bool:
|
|
"""
|
|
Check if user can access the events calendar.
|
|
|
|
Requires at least MEMBER role.
|
|
|
|
Args:
|
|
user: The user requesting access
|
|
|
|
Returns:
|
|
True if user can access calendar
|
|
"""
|
|
from database import SystemRole
|
|
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
|
|
return user.has_role(SystemRole.MEMBER)
|
|
|
|
|
|
def can_create_event(user: 'User') -> bool:
|
|
"""
|
|
Check if user can create calendar events.
|
|
|
|
Requires OFFICE_MANAGER or ADMIN role.
|
|
|
|
Args:
|
|
user: The user attempting to create
|
|
|
|
Returns:
|
|
True if user can create events
|
|
"""
|
|
from database import SystemRole
|
|
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
|
|
return user.has_role(SystemRole.OFFICE_MANAGER)
|
|
|
|
|
|
def get_user_permissions_summary(user: 'User') -> dict:
|
|
"""
|
|
Get a summary of all permissions for a user.
|
|
|
|
Useful for debugging and displaying in user profile.
|
|
|
|
Args:
|
|
user: The user to summarize
|
|
|
|
Returns:
|
|
Dictionary with permission flags
|
|
"""
|
|
from database import SystemRole
|
|
|
|
if not user or not user.is_authenticated:
|
|
return {
|
|
'role': None,
|
|
'company_role': None,
|
|
'can_view_contacts': False,
|
|
'can_access_forum': False,
|
|
'can_access_chat': 'none',
|
|
'can_access_admin_panel': False,
|
|
'can_manage_users': False,
|
|
'can_moderate_forum': False,
|
|
'can_edit_own_company': False,
|
|
'can_manage_own_company': False,
|
|
}
|
|
|
|
return {
|
|
'role': user.role,
|
|
'role_label': dict(SystemRole.choices()).get(user.system_role.value, 'Nieznany'),
|
|
'company_role': user.company_role,
|
|
'can_view_contacts': user.can_view_contacts(),
|
|
'can_access_forum': user.can_access_forum(),
|
|
'can_access_chat': get_chat_access_level(user),
|
|
'can_access_admin_panel': user.can_access_admin_panel(),
|
|
'can_manage_users': user.can_manage_users(),
|
|
'can_moderate_forum': user.can_moderate_forum(),
|
|
'can_edit_own_company': user.can_edit_company() if user.company_id else False,
|
|
'can_manage_own_company': user.can_manage_company() if user.company_id else False,
|
|
}
|