115 lines
3.4 KiB
Python
115 lines
3.4 KiB
Python
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
|