loko/streetup/contracts/permissions.py
2026-07-22 14:48:40 +02:00

419 lines
No EOL
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from django.core.exceptions import PermissionDenied
from django.utils.translation import gettext as _
from functools import wraps
from common.models import UserContractAccess, UserContractAccess, UserConfig, UserThematics
from contracts.models import Contract, ContractOrder, ContractOrderDocument, ClaimDeclarationAttachment
ALLOWED_ROLES_FOR_CONTRACT_ACCESS = {"admin", "manager", "external_manager", "controller", "top_manager"}
def get_allowed_contracts_for_user(user):
"""
Retourne une queryset des contrats accessibles à un utilisateur
s'il a le droit de voir les interventions ET un rôle autorisé.
"""
try:
config = user.config
except AttributeError:
return Contract.objects.none()
# Vérifie les rôles
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
return Contract.objects.none()
if "admin" in user_roles or "top_manager" in user_roles:
return Contract.objects.all()
# Filtre les UserContractAccess valides
allowed_contract_ids = UserContractAccess.objects.filter(
user_config=config,
can_view_interventions=True
).values_list("contract_id", flat=True)
return Contract.objects.filter(id__in=allowed_contract_ids)
def user_has_access_to_contract(user, contract_id):
"""
Vérifie si un utilisateur peut accéder à un contrat donné :
- A un rôle autorisé
- A un accès UserContractAccess avec can_view_interventions=True
"""
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
return False
if "admin" in user_roles or "top_manager" in user_roles:
return True
return UserContractAccess.objects.filter(
user_config=config,
contract_id=contract_id,
can_view_interventions=True
).exists()
def user_can_view_contract_prices(user, contract_id):
"""Vrai si l'utilisateur est admin ou dispose du droit can_view_prices sur le contrat."""
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
return False
if "admin" in user_roles or "top_manager" in user_roles:
return True
return UserContractAccess.objects.filter(
user_config=config,
contract_id=contract_id,
can_view_prices=True
).exists()
def contract_view_required(view_func):
"""
Décorateur basé sur user_has_access_to_contract.
La vue doit avoir un paramètre `contract_id` (en keyword arg).
"""
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
contract_id = kwargs.get("contract_id")
if contract_id is None:
raise PermissionDenied(_("Paramètre 'contract_id' manquant."))
from .permissions import user_has_access_to_contract # éviter import circulaire
if not user_has_access_to_contract(request.user, contract_id):
raise PermissionDenied(_("Accès au contrat refusé."))
return view_func(request, *args, **kwargs)
return _wrapped_view
def user_has_access_to_contract_order(user, order_id):
"""
Vérifie que l'utilisateur a accès au contrat lié à une ContractOrder.
"""
try:
order = ContractOrder.objects.select_related('contract').get(id=order_id)
except ContractOrder.DoesNotExist:
return False
return user_has_access_to_contract(user, order.contract.id)
def order_view_required(view_func):
"""
Décorateur pour restreindre l'accès à une vue de ContractOrder.
Nécessite un paramètre keyword `order_id`.
"""
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
order_id = kwargs.get("order_id")
if order_id is None:
raise PermissionDenied(_("Paramètre 'order_id' manquant."))
from .permissions import user_has_access_to_contract_order
if not user_has_access_to_contract_order(request.user, order_id):
raise PermissionDenied(_("Accès à la commande refusé."))
return view_func(request, *args, **kwargs)
return _wrapped_view
ALLOWED_ROLES_FOR_ORDER_CREATION = {"admin", "manager"}
def user_can_add_order(user):
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if user_roles.intersection(ALLOWED_ROLES_FOR_ORDER_CREATION):
return True
return False
def user_can_add_order_for_contract(user, contract_id):
"""
Vérifie que l'utilisateur a le droit d'ajouter une commande à un contrat donné :
- Il doit avoir un rôle 'admin' ou 'manager'
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
"""
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection(ALLOWED_ROLES_FOR_ORDER_CREATION):
return False
return UserContractAccess.objects.filter(
user_config=config,
contract_id=contract_id,
can_view_interventions=True
).exists()
def user_has_access_to_contract_posts(user, contract_id):
"""
Vérifie que l'utilisateur a accès aux postes du contrat.
"""
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
return False
if "admin" in user_roles or "top_manager" in user_roles:
return True
return UserContractAccess.objects.filter(
user_config=config,
contract_id=contract_id,
can_view_posts=True
).exists()
ALLOWED_ROLES_FOR_QUOTE_MANAGEMENT = {"admin", "external_manager"}
def user_can_manage_quote_for_contract(user, contract_id):
"""
Vérifie que l'utilisateur a le droit de gérer le devis d'une commande :
- Il doit avoir un rôle 'admin' ou 'external_manager'
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
"""
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection(ALLOWED_ROLES_FOR_QUOTE_MANAGEMENT):
return False
return UserContractAccess.objects.filter(
user_config=config,
contract_id=contract_id,
can_view_interventions=True
).exists()
def user_can_manage_direct_order_posts(user, contract_id):
"""
Vérifie que l'utilisateur peut créer/modifier/supprimer des commandes directes :
- Il doit avoir le rôle 'external_manager'
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
"""
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
if not config.roles.filter(name='external_manager').exists():
return False
return UserContractAccess.objects.filter(
user_config=config,
contract_id=contract_id,
can_view_interventions=True
).exists()
ALLOWED_ROLES_FOR_DIRECT_POST_VALIDATION = {"admin", "manager", "controller"}
def user_can_validate_direct_order_posts(user, contract_id):
"""
Vérifie que l'utilisateur peut valider/refuser des commandes directes :
- Il doit avoir un rôle 'admin', 'manager' ou 'controller'
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
"""
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection(ALLOWED_ROLES_FOR_DIRECT_POST_VALIDATION):
return False
return UserContractAccess.objects.filter(
user_config=config,
contract_id=contract_id,
can_view_interventions=True
).exists()
def user_can_generate_order_documents(user, order):
"""
Vérifie que l'utilisateur a le droit de générer des documents pour une commande donnée :
- Il doit avoir un rôle 'admin' ou 'external_manager'
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
"""
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if not user_roles.intersection({"admin", "external_manager"}):
return False
return UserContractAccess.objects.filter(
user_config=config,
contract_id=order.contract.id,
can_view_interventions=True
).exists()
# ---------------------------------------------------------------------------
# Permissions équipes (CompanyTeam / CompanyMember)
# ---------------------------------------------------------------------------
def get_viewable_teams_for_user(user):
"""
Retourne le queryset des équipes que l'utilisateur peut consulter.
Les admins voient toutes les équipes ; les autres ont besoin d'un
UserCompanyTeamAccess avec can_view=True.
"""
from contracts.models import CompanyTeam, UserCompanyTeamAccess
if not user.is_authenticated:
return CompanyTeam.objects.none()
try:
config = user.config
except AttributeError:
return CompanyTeam.objects.none()
user_roles = set(config.roles.values_list("name", flat=True))
if "admin" in user_roles:
return CompanyTeam.objects.all()
return CompanyTeam.objects.filter(
user_accesses__user_config=config,
user_accesses__can_view=True,
)
def user_can_view_teams(user):
"""Vérifie si l'utilisateur peut accéder à au moins une équipe."""
from contracts.models import UserCompanyTeamAccess
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if "admin" in user_roles:
return True
return UserCompanyTeamAccess.objects.filter(
user_config=config,
can_view=True,
).exists()
def user_can_edit_teams(user):
"""
Vérifie si l'utilisateur a le droit d'éditer des équipes.
Les admins ont toujours le droit ; les autres ont besoin d'un
UserCompanyTeamAccess avec can_edit=True.
"""
from contracts.models import UserCompanyTeamAccess
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if "admin" in user_roles:
return True
return UserCompanyTeamAccess.objects.filter(
user_config=config,
can_edit=True,
).exists()
def user_can_edit_specific_team(user, team_id):
"""
Vérifie si l'utilisateur peut éditer une équipe précise (créer/supprimer
des membres, déplacer des membres vers/depuis cette équipe).
"""
from contracts.models import UserCompanyTeamAccess
if not user.is_authenticated:
return False
try:
config = user.config
except AttributeError:
return False
user_roles = set(config.roles.values_list("name", flat=True))
if "admin" in user_roles:
return True
return UserCompanyTeamAccess.objects.filter(
user_config=config,
team_id=team_id,
can_edit=True,
).exists()
def guard_contract_order_file(obj: ContractOrder, user) -> bool:
# Accès si l’utilisateur a accès à la commande
return user_has_access_to_contract_order(user, obj.id)
def guard_contract_file(obj: Contract, user) -> bool:
# Si un jour tu ajoutes un FileField sur Contract
return user_has_access_to_contract(user, obj.id)
def guard_order_document_file(obj: ContractOrderDocument, user) -> bool:
# Document rattaché à une commande → on réutilise la même permission
return user_has_access_to_contract_order(user, obj.order_id)
def guard_claim_attachment_file(obj: ClaimDeclarationAttachment, user) -> bool:
# Pièce jointe de déclaration de créance → accès via la commande du document parent
return user_has_access_to_contract_order(user, obj.claim_document.order_id)
def guard_order_attachment_file(obj, user) -> bool:
# Pièce jointe générique d'une commande (photos, PDF, …)
return user_has_access_to_contract_order(user, obj.order_id)