import { PermissionState, Room, RoomMember, RoomPermissionKey, RoomPermissions, RoomRole, RoomRoleAssignment, UserRole } from '../../../../shared-kernel'; import { SYSTEM_ROLE_IDS } from '../constants/access-control.constants'; import { getRolePermissionState, permissionStateToBoolean, resolveLegacyAllowState } from '../util/access-control.util'; import type { MemberIdentity } from '../models/access-control.model'; import { getAssignedRoleIds, normalizeRoomRoleAssignments, removeRoleFromAssignments } from './role-assignment.rules'; import { getRoomRoleById, normalizeRoomRoles, withUpdatedRole } from './role.rules'; import { normalizeChannelPermissionOverrides, resolveRoomPermission } from './permission.rules'; function applyRolePermissionChanges( roles: readonly RoomRole[], role: RoomRole | null | undefined, changes: Partial> ): RoomRole[] { if (!role) { return [...roles]; } return withUpdatedRole(roles, role.id, { permissions: { ...role.permissions, ...changes } }); } function getEveryoneLegacyPermissionChanges( role: RoomRole, permissions: Partial ): Partial> { return { joinVoice: resolveLegacyAllowState(permissions.allowVoice, role.permissions?.joinVoice, 'deny'), shareScreen: resolveLegacyAllowState(permissions.allowScreenShare, role.permissions?.shareScreen, 'deny'), uploadFiles: resolveLegacyAllowState(permissions.allowFileUploads, role.permissions?.uploadFiles, 'deny') }; } function getModeratorLegacyPermissionChanges( role: RoomRole, permissions: Partial ): Partial> { return { manageChannels: resolveLegacyAllowState(permissions.moderatorsManageRooms, role.permissions?.manageChannels, 'inherit'), manageIcon: resolveLegacyAllowState(permissions.moderatorsManageIcon, role.permissions?.manageIcon, 'inherit') }; } function getAdminLegacyPermissionChanges( role: RoomRole, permissions: Partial ): Partial> { return { manageChannels: resolveLegacyAllowState(permissions.adminsManageRooms, role.permissions?.manageChannels, 'inherit'), manageIcon: resolveLegacyAllowState(permissions.adminsManageIcon, role.permissions?.manageIcon, 'inherit') }; } export function resolveLegacyRole(room: Room, identity: MemberIdentity | null | undefined): UserRole { if (!identity) { return 'member'; } if (room.hostId === identity.id || room.hostId === identity.oderId) { return 'host'; } const assignedRoleIds = getAssignedRoleIds(room.roleAssignments, identity); if (assignedRoleIds.includes(SYSTEM_ROLE_IDS.admin)) { return 'admin'; } if (assignedRoleIds.includes(SYSTEM_ROLE_IDS.moderator)) { return 'moderator'; } if ( resolveRoomPermission(room, identity, 'manageRoles') || resolveRoomPermission(room, identity, 'banMembers') || resolveRoomPermission(room, identity, 'manageBans') || resolveRoomPermission(room, identity, 'manageServer') ) { return 'admin'; } if ( resolveRoomPermission(room, identity, 'kickMembers') || resolveRoomPermission(room, identity, 'deleteMessages') || resolveRoomPermission(room, identity, 'manageChannels') || resolveRoomPermission(room, identity, 'manageIcon') ) { return 'moderator'; } return 'member'; } export function deriveLegacyRoomPermissions(room: Pick): RoomPermissions { const roles = normalizeRoomRoles(room.roles, room.permissions); const everyoneRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.everyone); const moderatorRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.moderator); const adminRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.admin); return { allowVoice: permissionStateToBoolean(getRolePermissionState(everyoneRole, 'joinVoice'), true), allowScreenShare: permissionStateToBoolean(getRolePermissionState(everyoneRole, 'shareScreen'), true), allowFileUploads: permissionStateToBoolean(getRolePermissionState(everyoneRole, 'uploadFiles'), true), adminsManageRooms: getRolePermissionState(adminRole, 'manageChannels') === 'allow', moderatorsManageRooms: getRolePermissionState(moderatorRole, 'manageChannels') === 'allow', adminsManageIcon: getRolePermissionState(adminRole, 'manageIcon') === 'allow', moderatorsManageIcon: getRolePermissionState(moderatorRole, 'manageIcon') === 'allow', slowModeInterval: room.slowModeInterval ?? room.permissions?.slowModeInterval ?? 0 }; } export function withLegacyRoomPermissions(room: Room, permissions: Partial): Room { const roles = normalizeRoomRoles(room.roles, room.permissions); const everyoneRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.everyone); const moderatorRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.moderator); const adminRole = getRoomRoleById(roles, SYSTEM_ROLE_IDS.admin); let nextRoles = applyRolePermissionChanges(roles, everyoneRole, everyoneRole ? getEveryoneLegacyPermissionChanges(everyoneRole, permissions) : {}); nextRoles = applyRolePermissionChanges( nextRoles, moderatorRole, moderatorRole ? getModeratorLegacyPermissionChanges(moderatorRole, permissions) : {} ); nextRoles = applyRolePermissionChanges(nextRoles, adminRole, adminRole ? getAdminLegacyPermissionChanges(adminRole, permissions) : {}); return normalizeRoomAccessControl({ ...room, roles: nextRoles, slowModeInterval: permissions.slowModeInterval ?? room.slowModeInterval ?? room.permissions?.slowModeInterval ?? 0 }); } export function hydrateRoomMembers(room: Room): RoomMember[] { const roles = normalizeRoomRoles(room.roles, room.permissions); const roleAssignments = normalizeRoomRoleAssignments(room.roleAssignments, room.members, roles); return (room.members ?? []).map((member) => { const roleIds = getAssignedRoleIds(roleAssignments, member); const hydratedRoom: Room = { ...room, roles, roleAssignments }; return { ...member, roleIds, role: resolveLegacyRole(hydratedRoom, member) }; }); } export function normalizeRoomAccessControl(room: Room): Room { const roles = normalizeRoomRoles(room.roles, room.permissions); const roleAssignments = normalizeRoomRoleAssignments(room.roleAssignments, room.members, roles); const channelPermissions = normalizeChannelPermissionOverrides(room.channelPermissions, roles); const slowModeInterval = room.slowModeInterval ?? room.permissions?.slowModeInterval ?? 0; const nextRoom: Room = { ...room, roles, roleAssignments, channelPermissions, slowModeInterval }; nextRoom.permissions = deriveLegacyRoomPermissions(nextRoom); nextRoom.members = hydrateRoomMembers(nextRoom); return nextRoom; } export function removeRole( roles: readonly RoomRole[], assignments: readonly RoomRoleAssignment[] | undefined, roleId: string ): { roles: RoomRole[]; roleAssignments: RoomRoleAssignment[] } { const nextRoles = roles.filter((role) => role.id !== roleId || role.isSystem); return { roles: normalizeRoomRoles(nextRoles), roleAssignments: removeRoleFromAssignments(assignments, roleId) }; }