from django.contrib.auth import REDIRECT_FIELD_NAME from django.core.exceptions import PermissionDenied from django.shortcuts import redirect from django.conf import settings from urllib.parse import urlencode from functools import wraps def _user_has_restricted_access(user, map_config): """ Return True if an authenticated *user* satisfies at least one of the restricted-access conditions configured on *map_config*. Admins always bypass the restriction. """ from common.models import UserConfig, UserThematics try: user_config = UserConfig.objects.get(user=user) except UserConfig.DoesNotExist: return False # Admin role: unconditional bypass if user_config.roles.filter(name='admin').exists(): return True # Thematic-based access if map_config.thematics.exists(): user_thematic_ids = UserThematics.objects.filter( user_config=user_config ).values_list('thematic_id', flat=True) if map_config.thematics.filter(pk__in=user_thematic_ids).exists(): return True # Team-based access if map_config.teams.exists(): from contracts.models import UserCompanyTeamAccess user_team_ids = UserCompanyTeamAccess.objects.filter( user_config=user_config ).values_list('team_id', flat=True) if map_config.teams.filter(pk__in=user_team_ids).exists(): return True # Direct user access if map_config.users.filter(pk=user.pk).exists(): return True return False def check_map_access(request, map_code): """ Verify that *request* is authorised to access the map identified by *map_code*. Returns ``True`` on success. Returns a redirect ``HttpResponse`` when the user needs to authenticate. Raises ``PermissionDenied`` when the user is authenticated but not authorised. When no ``MapConfig`` record exists for *map_code* the default policy is ``authenticated`` (any logged-in user may access). """ from .models import MapConfig try: map_config = MapConfig.objects.get(code=map_code) except MapConfig.DoesNotExist: # Fallback: require authentication if not request.user.is_authenticated: return _login_redirect(request) return True if map_config.access_level == MapConfig.ACCESS_PUBLIC: return True if not request.user.is_authenticated: return _login_redirect(request) if map_config.access_level == MapConfig.ACCESS_AUTHENTICATED: return True # ACCESS_RESTRICTED if _user_has_restricted_access(request.user, map_config): return True raise PermissionDenied def _login_redirect(request): login_url = settings.LOGIN_URL path = request.get_full_path() return redirect(f"{login_url}?{urlencode({REDIRECT_FIELD_NAME: path})}") def map_access_required(map_code): """ View decorator that enforces map-level access control. Usage:: @map_access_required('concrete_blocks') def my_view(request): ... """ def decorator(view_func): @wraps(view_func) def _wrapped_view(request, *args, **kwargs): result = check_map_access(request, map_code) if result is not True: return result # redirect response return view_func(request, *args, **kwargs) return _wrapped_view return decorator