Some checks failed
Build Android APK / build-android-apk (push) Has been cancelled
Deploy Web Apps / deploy (push) Has been cancelled
Queue Release Build / prepare (push) Has been cancelled
Queue Release Build / build-linux (push) Has been cancelled
Queue Release Build / build-windows (push) Has been cancelled
Queue Release Build / finalize (push) Has been cancelled
215 lines
6.6 KiB
TypeScript
215 lines
6.6 KiB
TypeScript
import {
|
|
Room,
|
|
RoomMember,
|
|
RoomRole,
|
|
RoomRoleAssignment
|
|
} from '../../../../shared-kernel';
|
|
import { SYSTEM_ROLE_IDS } from '../constants/access-control.constants';
|
|
import { localizeSystemRoleDisplayName } from './role-display.rules';
|
|
import type { MemberIdentity } from '../models/access-control.model';
|
|
import {
|
|
buildRoleLookup,
|
|
compareText,
|
|
memberIdentityKey,
|
|
matchesIdentity,
|
|
roleSortDescending,
|
|
uniqueStrings
|
|
} from '../util/access-control.util';
|
|
import { getRoomRoleById, normalizeRoomRoles } from './role.rules';
|
|
|
|
function sortAssignments(assignments: readonly RoomRoleAssignment[]): RoomRoleAssignment[] {
|
|
return [...assignments].sort((firstAssignment, secondAssignment) =>
|
|
compareText(firstAssignment.oderId || firstAssignment.userId, secondAssignment.oderId || secondAssignment.userId)
|
|
);
|
|
}
|
|
|
|
function resolveLegacyMemberRoleIds(member: RoomMember, validRoleIds: Set<string>): string[] {
|
|
if (Array.isArray(member.roleIds) && member.roleIds.length > 0) {
|
|
return uniqueStrings(member.roleIds).filter((roleId) => validRoleIds.has(roleId));
|
|
}
|
|
|
|
if (member.role === 'admin') {
|
|
return [SYSTEM_ROLE_IDS.admin];
|
|
}
|
|
|
|
if (member.role === 'moderator') {
|
|
return [SYSTEM_ROLE_IDS.moderator];
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
export function normalizeRoomRoleAssignments(
|
|
assignments: readonly RoomRoleAssignment[] | undefined,
|
|
members: readonly RoomMember[] | undefined,
|
|
roles: readonly RoomRole[]
|
|
): RoomRoleAssignment[] {
|
|
const validRoleIds = new Set(roles.map((role) => role.id).filter((roleId) => roleId !== SYSTEM_ROLE_IDS.everyone));
|
|
const normalizedByUserKey = new Map<string, RoomRoleAssignment>();
|
|
const hasExplicitAssignments = Array.isArray(assignments);
|
|
|
|
for (const assignment of assignments ?? []) {
|
|
if (!assignment || typeof assignment !== 'object') {
|
|
continue;
|
|
}
|
|
|
|
const userId = typeof assignment.userId === 'string' ? assignment.userId.trim() : '';
|
|
const oderId = typeof assignment.oderId === 'string' ? assignment.oderId.trim() : undefined;
|
|
const key = oderId || userId;
|
|
|
|
if (!key) {
|
|
continue;
|
|
}
|
|
|
|
const roleIds = uniqueStrings(assignment.roleIds ?? []).filter((roleId) => validRoleIds.has(roleId));
|
|
|
|
if (roleIds.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
normalizedByUserKey.set(key, {
|
|
userId: userId || key,
|
|
oderId,
|
|
roleIds
|
|
});
|
|
}
|
|
|
|
if (hasExplicitAssignments) {
|
|
return sortAssignments(Array.from(normalizedByUserKey.values()));
|
|
}
|
|
|
|
for (const member of members ?? []) {
|
|
const key = memberIdentityKey(member);
|
|
|
|
if (!key) {
|
|
continue;
|
|
}
|
|
|
|
const roleIds = resolveLegacyMemberRoleIds(member, validRoleIds);
|
|
|
|
if (roleIds.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
normalizedByUserKey.set(key, {
|
|
userId: member.id || key,
|
|
oderId: member.oderId || undefined,
|
|
roleIds
|
|
});
|
|
}
|
|
|
|
return sortAssignments(Array.from(normalizedByUserKey.values()));
|
|
}
|
|
|
|
export function getAssignedRoleIds(assignments: readonly RoomRoleAssignment[] | undefined, identity: MemberIdentity | null | undefined): string[] {
|
|
const assignment = (assignments ?? []).find((candidateAssignment) => matchesIdentity(identity, candidateAssignment));
|
|
|
|
return uniqueStrings(assignment?.roleIds ?? []);
|
|
}
|
|
|
|
export function getDisplayRoleName(
|
|
room: Room,
|
|
member: MemberIdentity | null | undefined,
|
|
translate?: (key: string) => string
|
|
): string {
|
|
const translateOr = (key: string, fallback: string) => translate?.(key) ?? fallback;
|
|
|
|
if (!member) {
|
|
return translateOr('shared.leaveServer.roles.member', 'Member');
|
|
}
|
|
|
|
if (room.hostId === member.id || room.hostId === member.oderId) {
|
|
return translateOr('shared.leaveServer.roles.owner', 'Owner');
|
|
}
|
|
|
|
const roles = normalizeRoomRoles(room.roles, room.permissions);
|
|
const roleLookup = buildRoleLookup(roles);
|
|
const assignedRoles = getAssignedRoleIds(room.roleAssignments, member)
|
|
.map((roleId) => roleLookup.get(roleId))
|
|
.filter((role): role is RoomRole => !!role)
|
|
.sort(roleSortDescending);
|
|
const roleName = assignedRoles[0]?.name;
|
|
|
|
if (!roleName) {
|
|
return translateOr('shared.accessControl.roles.everyone', '@everyone');
|
|
}
|
|
|
|
return translate ? localizeSystemRoleDisplayName(roleName, translate) : roleName;
|
|
}
|
|
|
|
export function getAssignedRoles(room: Room, identity: MemberIdentity | null | undefined): RoomRole[] {
|
|
const roles = normalizeRoomRoles(room.roles, room.permissions);
|
|
const roleLookup = buildRoleLookup(roles);
|
|
|
|
return getAssignedRoleIds(room.roleAssignments, identity)
|
|
.map((roleId) => roleLookup.get(roleId))
|
|
.filter((role): role is RoomRole => !!role)
|
|
.sort(roleSortDescending);
|
|
}
|
|
|
|
export function getHighestAssignedRole(room: Room, identity: MemberIdentity | null | undefined): RoomRole | null {
|
|
if (!identity) {
|
|
return null;
|
|
}
|
|
|
|
if (room.hostId === identity.id || room.hostId === identity.oderId) {
|
|
return null;
|
|
}
|
|
|
|
return getAssignedRoles(room, identity)[0] ?? getRoomRoleById(normalizeRoomRoles(room.roles, room.permissions), SYSTEM_ROLE_IDS.everyone) ?? null;
|
|
}
|
|
|
|
export function setRoleAssignmentsForMember(
|
|
assignments: readonly RoomRoleAssignment[] | undefined,
|
|
member: MemberIdentity,
|
|
roleIds: readonly string[]
|
|
): RoomRoleAssignment[] {
|
|
const nextAssignments = new Map<string, RoomRoleAssignment>();
|
|
const memberKey = memberIdentityKey(member);
|
|
|
|
for (const assignment of assignments ?? []) {
|
|
const key = memberIdentityKey({
|
|
id: assignment.userId,
|
|
oderId: assignment.oderId
|
|
});
|
|
|
|
if (!key || key === memberKey) {
|
|
continue;
|
|
}
|
|
|
|
nextAssignments.set(key, {
|
|
userId: assignment.userId,
|
|
oderId: assignment.oderId,
|
|
roleIds: uniqueStrings(assignment.roleIds)
|
|
});
|
|
}
|
|
|
|
const normalizedRoleIds = uniqueStrings(roleIds);
|
|
|
|
if (memberKey && normalizedRoleIds.length > 0) {
|
|
nextAssignments.set(memberKey, {
|
|
userId: member.id || member.oderId || memberKey,
|
|
oderId: member.oderId || undefined,
|
|
roleIds: normalizedRoleIds
|
|
});
|
|
}
|
|
|
|
return sortAssignments(Array.from(nextAssignments.values()));
|
|
}
|
|
|
|
export function removeRoleFromAssignments(assignments: readonly RoomRoleAssignment[] | undefined, roleId: string): RoomRoleAssignment[] {
|
|
return (assignments ?? [])
|
|
.map((assignment) => ({
|
|
...assignment,
|
|
roleIds: assignment.roleIds.filter((candidateRoleId) => candidateRoleId !== roleId)
|
|
}))
|
|
.filter((assignment) => assignment.roleIds.length > 0);
|
|
}
|
|
|
|
export function getRoleIdsForMember(room: Room, member: MemberIdentity | null | undefined): string[] {
|
|
return getAssignedRoleIds(
|
|
normalizeRoomRoleAssignments(room.roleAssignments, room.members, normalizeRoomRoles(room.roles, room.permissions)),
|
|
member
|
|
);
|
|
}
|