210 lines
7.2 KiB
TypeScript
210 lines
7.2 KiB
TypeScript
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<Record<RoomPermissionKey, PermissionState | undefined>>
|
|
): RoomRole[] {
|
|
if (!role) {
|
|
return [...roles];
|
|
}
|
|
|
|
return withUpdatedRole(roles, role.id, {
|
|
permissions: {
|
|
...role.permissions,
|
|
...changes
|
|
}
|
|
});
|
|
}
|
|
|
|
function getEveryoneLegacyPermissionChanges(
|
|
role: RoomRole,
|
|
permissions: Partial<RoomPermissions>
|
|
): Partial<Record<RoomPermissionKey, PermissionState | undefined>> {
|
|
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<RoomPermissions>
|
|
): Partial<Record<RoomPermissionKey, PermissionState | undefined>> {
|
|
return {
|
|
manageChannels: resolveLegacyAllowState(permissions.moderatorsManageRooms, role.permissions?.manageChannels, 'inherit'),
|
|
manageIcon: resolveLegacyAllowState(permissions.moderatorsManageIcon, role.permissions?.manageIcon, 'inherit')
|
|
};
|
|
}
|
|
|
|
function getAdminLegacyPermissionChanges(
|
|
role: RoomRole,
|
|
permissions: Partial<RoomPermissions>
|
|
): Partial<Record<RoomPermissionKey, PermissionState | undefined>> {
|
|
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<Room, 'roles' | 'permissions' | 'slowModeInterval'>): 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<RoomPermissions>): 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)
|
|
};
|
|
}
|