55from collections import OrderedDict , defaultdict
66from datetime import datetime , time , timedelta
77from operator import attrgetter
8- from typing import List
98from urllib .parse import urljoin , urlparse
109from zoneinfo import ZoneInfo
1110
5049from eventyay .common .text .phrases import phrases
5150from eventyay .common .urls import EventUrls
5251from eventyay .consts import TIMEZONE_CHOICES
53- from eventyay .core .permissions import MAX_PERMISSIONS_IF_SILENCED , SYSTEM_ROLES , Permission
52+ from eventyay .core .permissions import (
53+ MAX_PERMISSIONS_IF_SILENCED ,
54+ ORGANIZER_ROLES ,
55+ SYSTEM_ROLES ,
56+ Permission ,
57+ normalize_permission_value ,
58+ traits_match_required ,
59+ )
5460from eventyay .core .utils .json import CustomJSONEncoder
5561from eventyay .helpers .database import GroupConcat
5662from eventyay .helpers .daterange import daterange
6268 has_any_permission ,
6369 is_event_visible ,
6470)
65-
71+ from eventyay . eventyay_common . video . permissions import VIDEO_PERMISSION_BY_FIELD , VIDEO_TRAIT_ROLE_MAP
6672from ..settings import settings_hierarkey
6773from .auth import User
6874from .mixins import OrderedModel , PretalxModel
@@ -85,7 +91,6 @@ def default_roles():
8591 attendee = [
8692 Permission .EVENT_VIEW ,
8793 Permission .EVENT_EXHIBITION_CONTACT ,
88- Permission .EVENT_CHAT_DIRECT ,
8994 ]
9095 viewer = attendee + [Permission .ROOM_VIEW , Permission .ROOM_CHAT_READ ]
9196 participant = viewer + [
@@ -1352,38 +1357,73 @@ def decode_token(self, token, allow_raise=False):
13521357 if exc and allow_raise :
13531358 raise exc
13541359
1360+ def _get_trait_grants_with_defaults (self ):
1361+ base_trait_grants = self .trait_grants if self .trait_grants is not None else default_grants ()
1362+ slug = getattr (self , "slug" , None ) or getattr (self , "id" , None )
1363+ if not slug :
1364+ return base_trait_grants
1365+ augmented = dict (base_trait_grants )
1366+ for role , trait_name in VIDEO_TRAIT_ROLE_MAP .items ():
1367+ augmented .setdefault (role , [f"eventyay-video-event-{ slug } -{ trait_name .replace ('_' , '-' )} " ])
1368+ return augmented
1369+
1370+ def _remove_direct_messaging_if_unauthorized (self , result , user_traits ):
1371+ """Remove EVENT_CHAT_DIRECT permission if user doesn't have the direct messaging trait.
1372+
1373+ Args:
1374+ result: Permission result dictionary to modify
1375+ user_traits: List of user traits
1376+ """
1377+ direct_messaging_def = VIDEO_PERMISSION_BY_FIELD .get ('can_video_direct_message' )
1378+ if not direct_messaging_def :
1379+ return
1380+
1381+ direct_messaging_trait = direct_messaging_def .trait_value (self .slug )
1382+ has_direct_messaging_trait = direct_messaging_trait in user_traits
1383+
1384+ if not has_direct_messaging_trait :
1385+ direct_message_value = Permission .EVENT_CHAT_DIRECT .value
1386+ result [self ] = {
1387+ p for p in result [self ]
1388+ if normalize_permission_value (p ) != direct_message_value
1389+ }
1390+
13551391 def has_permission_implicit (
13561392 self ,
13571393 * ,
13581394 traits ,
1359- permissions : List [Permission ],
1395+ permissions : list [Permission ],
13601396 room = None ,
13611397 allow_empty_traits = True ,
13621398 ):
13631399 # Ensure trait_grants and roles are not None - use defaults if missing
1364- event_trait_grants = self .trait_grants if self . trait_grants is not None else default_grants ()
1400+ event_trait_grants = self ._get_trait_grants_with_defaults ()
13651401 event_roles = self .roles if self .roles is not None else default_roles ()
13661402
13671403 for role , required_traits in event_trait_grants .items ():
13681404 if (
1369- isinstance (required_traits , list )
1370- and all (any (x in traits for x in (r if isinstance (r , list ) else [r ])) for r in required_traits )
1405+ traits_match_required (traits , required_traits )
13711406 and (required_traits or allow_empty_traits )
13721407 ):
13731408 role_permissions = event_roles .get (role , SYSTEM_ROLES .get (role , []))
1374- if any (p in role_permissions or p .value in role_permissions for p in permissions ):
1409+ if any (
1410+ normalize_permission_value (p ) in role_permissions
1411+ for p in permissions
1412+ ):
13751413 return True
13761414
13771415 if room :
13781416 room_trait_grants = room .trait_grants if room .trait_grants is not None else {}
13791417 for role , required_traits in room_trait_grants .items ():
13801418 if (
1381- isinstance (required_traits , list )
1382- and all (any (x in traits for x in (r if isinstance (r , list ) else [r ])) for r in required_traits )
1419+ traits_match_required (traits , required_traits )
13831420 and (required_traits or allow_empty_traits )
13841421 ):
13851422 role_permissions = event_roles .get (role , SYSTEM_ROLES .get (role , []))
1386- if any (p in role_permissions or p .value in role_permissions for p in permissions ):
1423+ if any (
1424+ normalize_permission_value (p ) in role_permissions
1425+ for p in permissions
1426+ ):
13871427 return True
13881428
13891429 # Return False if no permission was granted
@@ -1405,7 +1445,7 @@ def has_permission(self, *, user, permission: Permission, room=None):
14051445 return False
14061446
14071447 if self .has_permission_implicit (
1408- traits = user .traits ,
1448+ traits = user .traits or [] ,
14091449 permissions = permission ,
14101450 room = room ,
14111451 allow_empty_traits = user .type == User .UserType .PERSON ,
@@ -1415,7 +1455,8 @@ def has_permission(self, *, user, permission: Permission, room=None):
14151455 roles = user .get_role_grants (room )
14161456 event_roles = self .roles if self .roles is not None else default_roles ()
14171457 for r in roles :
1418- if any (p .value in event_roles .get (r , SYSTEM_ROLES .get (r , [])) for p in permission ):
1458+ role_perms = event_roles .get (r , SYSTEM_ROLES .get (r , []))
1459+ if any (normalize_permission_value (p ) in role_perms for p in permission ):
14191460 return True
14201461
14211462 async def has_permission_async (self , * , user , permission : Permission , room = None ):
@@ -1434,7 +1475,7 @@ async def has_permission_async(self, *, user, permission: Permission, room=None)
14341475 return False
14351476
14361477 if self .has_permission_implicit (
1437- traits = user .traits ,
1478+ traits = user .traits or [] ,
14381479 permissions = permission ,
14391480 room = room ,
14401481 allow_empty_traits = user .type == User .UserType .PERSON ,
@@ -1444,7 +1485,8 @@ async def has_permission_async(self, *, user, permission: Permission, room=None)
14441485 roles = await user .get_role_grants_async (room )
14451486 event_roles = self .roles if self .roles is not None else default_roles ()
14461487 for r in roles :
1447- if any (p .value in event_roles .get (r , SYSTEM_ROLES .get (r , [])) for p in permission ):
1488+ role_perms = event_roles .get (r , SYSTEM_ROLES .get (r , []))
1489+ if any (normalize_permission_value (p ) in role_perms for p in permission ):
14481490 return True
14491491
14501492 def get_all_permissions (self , user ):
@@ -1455,25 +1497,38 @@ def get_all_permissions(self, user):
14551497 allow_empty_traits = user .type == User .UserType .PERSON
14561498
14571499 # Ensure trait_grants and roles are not None
1458- event_trait_grants = self .trait_grants if self . trait_grants is not None else default_grants ()
1500+ event_trait_grants = self ._get_trait_grants_with_defaults ()
14591501 event_roles = self .roles if self .roles is not None else default_roles ()
14601502
1503+ user_traits = user .traits or []
1504+
14611505 for role , required_traits in event_trait_grants .items ():
14621506 if (
1463- isinstance (required_traits , list )
1464- and all (any (x in user .traits for x in (r if isinstance (r , list ) else [r ])) for r in required_traits )
1507+ traits_match_required (user_traits , required_traits )
14651508 and (required_traits or allow_empty_traits )
14661509 ):
1467- result [self ].update (event_roles .get (role , SYSTEM_ROLES .get (role , [])))
1468-
1469- # Removed user.world_grants loop (attribute not present on unified User model)
1510+ role_perms = event_roles .get (role , SYSTEM_ROLES .get (role , []))
1511+ result [self ].update (role_perms )
1512+
1513+ # Admin mode in the ticket/talk system is represented by the ``admin`` trait on the video side.
1514+ # When admin mode is ON, the user has the ``admin`` trait and should retain full access.
1515+ admin_mode_active = "admin" in user_traits
1516+
1517+ if admin_mode_active :
1518+ # Grant all video manager permissions when admin mode is active
1519+ for role_name in ORGANIZER_ROLES :
1520+ role_perms = event_roles .get (role_name , SYSTEM_ROLES .get (role_name , []))
1521+ result [self ].update (role_perms )
1522+ else :
1523+ # Remove EVENT_CHAT_DIRECT from ALL users unless they have the direct messaging trait.
1524+ # Only users with can_video_direct_message team permission get the video_direct_messaging trait.
1525+ self ._remove_direct_messaging_if_unauthorized (result , user_traits )
14701526
14711527 for room in self .rooms .all ():
14721528 room_trait_grants = room .trait_grants if room .trait_grants is not None else {}
14731529 for role , required_traits in room_trait_grants .items ():
14741530 if (
1475- isinstance (required_traits , list )
1476- and all (any (x in user .traits for x in (r if isinstance (r , list ) else [r ])) for r in required_traits )
1531+ traits_match_required (user_traits , required_traits )
14771532 and (required_traits or allow_empty_traits )
14781533 ):
14791534 result [room ].update (event_roles .get (role , SYSTEM_ROLES .get (role , [])))
0 commit comments