mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
129 lines
3.6 KiB
JavaScript
129 lines
3.6 KiB
JavaScript
// Video Auth Policy
|
|
// Purpose: Encapsulates mode, role, and stream-specific authorization decisions for MediaMTX auth checks.
|
|
// Scope: Evaluates viewer/publisher eligibility from normalized request context and socket/session state.
|
|
const {
|
|
shouldUseSnapshotsForNonTurnVideo,
|
|
shouldUseSnapshotsForExternalSpectatorVideo,
|
|
} = require('../../helpers/bandwidthSavings');
|
|
|
|
function createVideoAuthPolicy(deps) {
|
|
const {
|
|
getMode,
|
|
MODES,
|
|
isAdmin,
|
|
isLockdownAdmin,
|
|
getRole,
|
|
isVerified,
|
|
turnService,
|
|
roverManager,
|
|
ptzCameraService,
|
|
getSocketIp,
|
|
isLocalNetwork,
|
|
io,
|
|
} = deps;
|
|
|
|
function countControllableUsers() {
|
|
const ids = new Set();
|
|
io.sockets.sockets.forEach((candidate) => {
|
|
if (!candidate?.id || getRole(candidate) === 'spectator') return;
|
|
/*
|
|
MediaMTX can ask for authorization after a browser has already received
|
|
a token, so this count intentionally mirrors videoSocketService instead
|
|
of trusting the client-visible session policy snapshot.
|
|
*/
|
|
if (roverManager.getRoversForSocket(candidate.id).length > 0) {
|
|
ids.add(candidate.id);
|
|
}
|
|
});
|
|
if (typeof ptzCameraService.getParticipantSocketIds === 'function') {
|
|
ptzCameraService.getParticipantSocketIds().forEach((socketId) => {
|
|
const socket = io.sockets.sockets.get(socketId);
|
|
if (socket && getRole(socket) !== 'spectator') ids.add(socketId);
|
|
});
|
|
}
|
|
return ids.size;
|
|
}
|
|
|
|
function canView(socket) {
|
|
const mode = getMode();
|
|
if (!socket) return false;
|
|
if (mode === MODES.LOCKDOWN) {
|
|
return isLockdownAdmin(socket);
|
|
}
|
|
if (mode === MODES.ADMIN) {
|
|
const role = getRole(socket);
|
|
return role === 'spectator' || isAdmin(socket);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function canAccessStream({ socket, streamInfo, action, sourceType }) {
|
|
if (!canView(socket)) {
|
|
return false;
|
|
}
|
|
|
|
if (streamInfo.type === 'rover') {
|
|
const roverId = streamInfo.baseId || streamInfo.id;
|
|
if (!roverManager.canSeeRover(roverId, socket)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (streamInfo.type === 'ptz') {
|
|
return ptzCameraService.canRequestLiveVideo(socket);
|
|
}
|
|
|
|
if (sourceType === 'roverMic' && action === 'publish') {
|
|
const roverId = streamInfo.baseId || streamInfo.id;
|
|
if (!isVerified(socket)) {
|
|
return false;
|
|
}
|
|
if (!roverManager.isDriver(roverId, socket)) {
|
|
return false;
|
|
}
|
|
if (!turnService.canDrive(roverId, socket)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const role = getRole(socket);
|
|
const isAudio = streamInfo.id?.endsWith('-audio');
|
|
if (role === 'spectator' && !isAdmin(socket) && !isAudio) {
|
|
const socketIp = getSocketIp(socket);
|
|
if (!isLocalNetwork(socketIp) && shouldUseSnapshotsForExternalSpectatorVideo()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (streamInfo.type === 'rover' && role !== 'spectator' && !isAdmin(socket)) {
|
|
const roverId = streamInfo.baseId || streamInfo.id;
|
|
if (!roverManager.isDriver(roverId, socket)) {
|
|
return false;
|
|
}
|
|
if (
|
|
!isAudio &&
|
|
shouldUseSnapshotsForNonTurnVideo({ controllableUserCount: countControllableUsers() }) &&
|
|
!turnService.canRequestLiveVideo(roverId, socket)
|
|
) {
|
|
/*
|
|
This mirrors videoSocketService's token gate. MediaMTX can ask auth
|
|
after a token has been issued, so the same "must belong to this rover's
|
|
driver queue" rule has to be evaluated here too.
|
|
*/
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return {
|
|
canAccessStream,
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
createVideoAuthPolicy,
|
|
};
|