Check user token in love service (#9464)

Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com>
This commit is contained in:
Anton Alexeyev
2025-07-04 19:17:03 +05:00
committed by GitHub
parent 582bfdf26e
commit f92bcb37f0
2 changed files with 70 additions and 37 deletions
+3 -1
View File
@@ -113,9 +113,11 @@ export async function getToken (
if (endpoint === undefined) {
throw new Error('Love service endpoint not found')
}
const token = getPlatformToken()
const res = await fetch(concatLink(endpoint, '/getToken'), {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ roomName: getTokenRoomName(roomName, roomId), _id: userId, participantName })
@@ -553,7 +555,7 @@ async function withRetries (fn: () => Promise<void>, retries: number, delay: num
async function connect (name: string, room: Room, _id: string): Promise<void> {
const wsURL = getMetadata(love.metadata.WebSocketURL)
if (wsURL === undefined) {
if (wsURL === undefined || getCurrentAccount().role === AccountRole.ReadOnlyGuest) {
return
}
+67 -36
View File
@@ -17,7 +17,7 @@ import { setMetadata } from '@hcengineering/platform'
import serverClient from '@hcengineering/server-client'
import { initStatisticsContext, StorageConfig, StorageConfiguration } from '@hcengineering/server-core'
import { storageConfigFromEnv } from '@hcengineering/server-storage'
import serverToken, { decodeToken } from '@hcengineering/server-token'
import serverToken, { decodeToken, Token } from '@hcengineering/server-token'
import {
getClient as getAccountClientRaw,
isWorkspaceLoginInfo,
@@ -25,7 +25,7 @@ import {
} from '@hcengineering/account-client'
import { RoomMetadata, TranscriptionStatus, MeetingMinutes } from '@hcengineering/love'
import cors from 'cors'
import express from 'express'
import express, { type Request } from 'express'
import { IncomingHttpHeaders } from 'http'
import {
AccessToken,
@@ -151,6 +151,15 @@ export const main = async (): Promise<void> => {
const roomName = req.body.roomName
const _id = req.body._id
const participantName = req.body.participantName
if (typeof roomName !== 'string') {
res.status(400).send()
return
}
if (!hasWorkspaceAccess(roomName, req)) {
res.status(401).send()
return
}
res.send(await createToken(roomName, _id, participantName))
})
@@ -161,18 +170,21 @@ export const main = async (): Promise<void> => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.post('/startRecord', async (req, res) => {
const token = extractToken(req.headers)
if (token === undefined) {
res.status(401).send()
return
}
const roomName = req.body.roomName
const room = req.body.room
const meetingMinutes = req.body.meetingMinutes
if (typeof roomName !== 'string') {
res.status(400).send()
return
}
if (!hasWorkspaceAccess(roomName, req)) {
res.status(401).send()
return
}
try {
const token = extractToken(req.headers)
const wsLoginInfo = await getAccountClient(token).getLoginInfoByToken()
if (!isWorkspaceLoginInfo(wsLoginInfo)) {
console.error('No workspace found for the token')
@@ -194,39 +206,37 @@ export const main = async (): Promise<void> => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.post('/stopRecord', async (req, res) => {
const token = extractToken(req.headers)
if (token === undefined) {
const roomName = req.body.roomName
if (typeof roomName !== 'string') {
res.status(400).send()
return
}
if (!hasWorkspaceAccess(roomName, req)) {
res.status(401).send()
return
}
// just check token
decodeToken(token)
await updateMetadata(roomClient, req.body.roomName, { recording: false })
void stopEgress(egressClient, req.body.roomName)
await updateMetadata(roomClient, roomName, { recording: false })
void stopEgress(egressClient, roomName)
res.send()
})
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.post('/transcription', async (req, res) => {
const token = extractToken(req.headers)
if (token === undefined) {
res.status(401).send()
return
}
// just check token
decodeToken(token)
const roomName = req.body.roomName
const language = req.body.language
const transcription = req.body.transcription as TranscriptionStatus
if (roomName == null) {
if (typeof roomName !== 'string') {
res.status(400).send()
return
}
if (!hasWorkspaceAccess(roomName, req)) {
res.status(401).send()
return
}
const metadata = language != null ? { transcription, language } : { transcription }
try {
await updateMetadata(roomClient, roomName, metadata)
@@ -239,21 +249,19 @@ export const main = async (): Promise<void> => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.post('/language', async (req, res) => {
const token = extractToken(req.headers)
if (token === undefined) {
res.status(401).send()
return
}
// just check token
decodeToken(token)
const roomName = req.body.roomName
const language = req.body.language
if (roomName == null || language == null) {
if (typeof roomName !== 'string' || language == null) {
res.status(400).send()
return
}
if (!hasWorkspaceAccess(roomName, req)) {
res.status(401).send()
return
}
try {
await updateMetadata(roomClient, roomName, { language })
res.send()
@@ -360,6 +368,29 @@ const startRecord = async (
return filepath
}
function hasWorkspaceAccess (roomName: string, req: Request): boolean {
const workspace = roomName.split('_')[0]
const token = extractToken(req.headers)
if (token === undefined) {
return false
}
let decodedToken: Token | undefined
try {
decodedToken = decodeToken(token)
} catch (e) {}
if (
decodedToken === undefined ||
decodedToken.workspace !== workspace ||
decodedToken.extra?.readonly === 'true' ||
decodedToken.extra?.guest === 'true'
) {
return false
}
return true
}
function parseMetadata (metadata?: string | null): RoomMetadata {
if (metadata === '' || metadata == null) return {}