Chat Federation: using S2S if available.
* if both local and remote instance have external XMPP connections enabled, the user joins the remote room with his local account * some code refactoring (builtin.ts) Note: documentation and settings descriptions are to do. Related to #112
This commit is contained in:
@ -31,6 +31,17 @@ function anonymousConnectionInfos (livechatInfos: LiveChatJSONLDAttribute | fals
|
||||
return r
|
||||
}
|
||||
|
||||
export {
|
||||
anonymousConnectionInfos
|
||||
function remoteAuthenticatedConnectionEnabled (livechatInfos: LiveChatJSONLDAttribute | false): boolean {
|
||||
if (!livechatInfos) { return false }
|
||||
if (!livechatInfos.links) { return false }
|
||||
if (livechatInfos.type !== 'xmpp') { return false }
|
||||
for (const link of livechatInfos.links) {
|
||||
if (link.type === 'xmpp-s2s') { return true }
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export {
|
||||
anonymousConnectionInfos,
|
||||
remoteAuthenticatedConnectionEnabled
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ async function videoBuildJSONLD (
|
||||
'disable-websocket',
|
||||
'prosody-room-type',
|
||||
'federation-dont-publish-remotely',
|
||||
'chat-no-anonymous'
|
||||
'chat-no-anonymous',
|
||||
'prosody-room-allow-s2s'
|
||||
])
|
||||
|
||||
if (settings['federation-dont-publish-remotely']) {
|
||||
@ -70,6 +71,11 @@ async function videoBuildJSONLD (
|
||||
}
|
||||
|
||||
const links: LiveChatJSONLDLink[] = []
|
||||
if (settings['prosody-room-allow-s2s']) {
|
||||
links.push({
|
||||
type: 'xmpp-s2s'
|
||||
})
|
||||
}
|
||||
if (!settings['chat-no-anonymous']) {
|
||||
links.push({
|
||||
type: 'xmpp-bosh-anonymous',
|
||||
|
@ -36,6 +36,11 @@ function sanitizePeertubeLiveChatInfos (chatInfos: any): LiveChatJSONLDAttribute
|
||||
url: link.url
|
||||
})
|
||||
}
|
||||
if (link.type === 'xmpp-s2s') {
|
||||
r.links.push({
|
||||
type: link.type
|
||||
})
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
@ -4,6 +4,10 @@ interface VideoBuildResultContext {
|
||||
video: MVideoAP
|
||||
}
|
||||
|
||||
interface LiveChatJSONLDS2SLink {
|
||||
type: 'xmpp-s2s'
|
||||
}
|
||||
|
||||
interface LiveChatJSONLDAnonymousWebsocketLink {
|
||||
type: 'xmpp-websocket-anonymous'
|
||||
url: string
|
||||
@ -16,7 +20,7 @@ interface LiveChatJSONLDAnonymousBOSHLink {
|
||||
jid: string
|
||||
}
|
||||
|
||||
type LiveChatJSONLDLink = LiveChatJSONLDAnonymousBOSHLink | LiveChatJSONLDAnonymousWebsocketLink
|
||||
type LiveChatJSONLDLink = LiveChatJSONLDS2SLink | LiveChatJSONLDAnonymousBOSHLink | LiveChatJSONLDAnonymousWebsocketLink
|
||||
|
||||
interface LiveChatJSONLDInfos {
|
||||
type: 'xmpp'
|
||||
|
@ -156,6 +156,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
const prosodyDomain = await getProsodyDomain(options)
|
||||
const paths = await getProsodyFilePaths(options)
|
||||
const roomType = settings['prosody-room-type'] === 'channel' ? 'channel' : 'video'
|
||||
const enableUserS2S = enableRoomS2S && !(settings['federation-no-remote-chat'] as boolean)
|
||||
let certificates: ProsodyConfigCertificates = false
|
||||
|
||||
const apikey = await getAPIKey(options)
|
||||
@ -203,7 +204,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
config.useExternalComponents(componentsPort, components)
|
||||
}
|
||||
|
||||
if (enableRoomS2S) {
|
||||
if (enableRoomS2S || enableUserS2S) {
|
||||
certificates = 'generate-self-signed'
|
||||
if (config.paths.certsDirIsCustom) {
|
||||
certificates = 'use-from-dir'
|
||||
@ -223,7 +224,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
if (networkInterface.match(/^[a-f0-9:]+$/)) return
|
||||
throw new Error('Invalid s2s interfaces')
|
||||
})
|
||||
config.useRoomS2S(s2sPort, s2sInterfaces)
|
||||
config.useS2S(s2sPort, s2sInterfaces, !enableUserS2S)
|
||||
}
|
||||
|
||||
const logExpiration = readLogExpiration(options, logExpirationSetting)
|
||||
|
@ -258,13 +258,17 @@ class ProsodyConfigContent {
|
||||
this.global.set('c2s_ports', [c2sPort])
|
||||
}
|
||||
|
||||
useRoomS2S (s2sPort: string, s2sInterfaces: string[]): void {
|
||||
useS2S (s2sPort: string, s2sInterfaces: string[], mucOnly: boolean): void {
|
||||
this.global.set('s2s_ports', [s2sPort])
|
||||
this.global.set('s2s_interfaces', s2sInterfaces)
|
||||
this.global.set('s2s_secure_auth', false)
|
||||
this.global.add('modules_enabled', 'tls') // required for s2s and co
|
||||
this.muc.add('modules_enabled', 's2s')
|
||||
this.muc.add('modules_enabled', 'dialback') // This allows s2s connections without certicicates!
|
||||
if (!mucOnly && this.authenticated) {
|
||||
this.authenticated.add('modules_enabled', 's2s')
|
||||
this.authenticated.add('modules_enabled', 'dialback') // This allows s2s connections without certicicates!
|
||||
}
|
||||
}
|
||||
|
||||
useExternalComponents (componentsPort: string, components: ExternalComponent[]): void {
|
||||
|
@ -15,7 +15,7 @@ import { isAutoColorsAvailable, areAutoColorsValid, AutoColors } from '../../../
|
||||
import { getBoshUri, getWSUri } from '../uri/webchat'
|
||||
import { getVideoLiveChatInfos } from '../federation/storage'
|
||||
import { LiveChatJSONLDAttribute } from '../federation/types'
|
||||
import { anonymousConnectionInfos } from '../federation/connection-infos'
|
||||
import { anonymousConnectionInfos, remoteAuthenticatedConnectionEnabled } from '../federation/connection-infos'
|
||||
import * as path from 'path'
|
||||
const got = require('got')
|
||||
|
||||
@ -50,7 +50,8 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
||||
'prosody-room-type',
|
||||
'disable-websocket',
|
||||
'converse-theme', 'converse-autocolors',
|
||||
'federation-no-remote-chat'
|
||||
'federation-no-remote-chat',
|
||||
'prosody-room-allow-s2s'
|
||||
])
|
||||
|
||||
let autoViewerMode: boolean = false
|
||||
@ -110,26 +111,38 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
||||
const baseStaticUrl = getBaseStaticRoute(options)
|
||||
page = page.replace(/{{BASE_STATIC_URL}}/g, baseStaticUrl)
|
||||
|
||||
let connectionInfos: ConnectionInfos | null
|
||||
const prosodyDomain = await getProsodyDomain(options)
|
||||
const localAnonymousJID = 'anon.' + prosodyDomain
|
||||
const localBoshUri = getBoshUri(options)
|
||||
const localWsUri = settings['disable-websocket']
|
||||
? ''
|
||||
: (getWSUri(options) ?? '')
|
||||
|
||||
let remoteConnectionInfos: WCRemoteConnectionInfos | undefined
|
||||
let roomJID: string
|
||||
if (video?.remote) {
|
||||
connectionInfos = await _remoteConnectionInfos(remoteChatInfos ?? false)
|
||||
remoteConnectionInfos = await _remoteConnectionInfos(remoteChatInfos ?? false)
|
||||
if (!remoteConnectionInfos) {
|
||||
res.status(404)
|
||||
res.send('No compatible way to connect to remote chat')
|
||||
return
|
||||
}
|
||||
roomJID = remoteConnectionInfos.roomJID
|
||||
} else {
|
||||
connectionInfos = await _localConnectionInfos(
|
||||
roomJID = await _localRoomJID(
|
||||
options,
|
||||
settings,
|
||||
prosodyDomain,
|
||||
roomKey,
|
||||
video,
|
||||
channelId,
|
||||
req.query.forcetype === '1'
|
||||
)
|
||||
}
|
||||
if (!connectionInfos) {
|
||||
res.status(404)
|
||||
res.send('No compatible way to connect to remote chat')
|
||||
return
|
||||
}
|
||||
|
||||
page = page.replace(/{{JID}}/g, connectionInfos.userJID)
|
||||
page = page.replace(/{{IS_REMOTE_CHAT}}/g, video?.remote ? 'true' : 'false')
|
||||
page = page.replace(/{{LOCAL_ANONYMOUS_JID}}/g, localAnonymousJID)
|
||||
page = page.replace(/{{REMOTE_ANONYMOUS_JID}}/g, remoteConnectionInfos?.anonymous?.userJID ?? '')
|
||||
|
||||
let autocolorsStyles = ''
|
||||
if (
|
||||
@ -183,10 +196,20 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
||||
}
|
||||
|
||||
// ... then inject it in the page.
|
||||
page = page.replace(/{{ROOM}}/g, connectionInfos.roomJID)
|
||||
page = page.replace(/{{BOSH_SERVICE_URL}}/g, connectionInfos.boshUri)
|
||||
page = page.replace(/{{WS_SERVICE_URL}}/g, connectionInfos.wsUri ?? '')
|
||||
page = page.replace(/{{REMOTE_ANONYMOUS_XMPP_SERVER}}/g, connectionInfos.remoteXMPPServer ? 'true' : 'false')
|
||||
page = page.replace(/{{ROOM}}/g, roomJID)
|
||||
page = page.replace(/{{LOCAL_BOSH_SERVICE_URL}}/g, localBoshUri)
|
||||
page = page.replace(/{{LOCAL_WS_SERVICE_URL}}/g, localWsUri ?? '')
|
||||
page = page.replace(/{{REMOTE_BOSH_SERVICE_URL}}/g, remoteConnectionInfos?.anonymous?.boshUri ?? '')
|
||||
page = page.replace(/{{REMOTE_WS_SERVICE_URL}}/g, remoteConnectionInfos?.anonymous?.wsUri ?? '')
|
||||
page = page.replace(/{{REMOTE_ANONYMOUS_XMPP_SERVER}}/g, remoteConnectionInfos?.anonymous ? 'true' : 'false')
|
||||
// Note: to be able to connect to remote XMPP server, with a local account,
|
||||
// we must enable prosody-room-allow-s2s
|
||||
// (which is required, so we can use outgoing S2S from the authenticated virtualhost).
|
||||
// TODO: There should be another settings, rather than prosody-room-allow-s2s
|
||||
page = page.replace(
|
||||
/{{REMOTE_AUTHENTICATED_XMPP_SERVER}}/g,
|
||||
settings['prosody-room-allow-s2s'] && remoteConnectionInfos?.authenticated ? 'true' : 'false'
|
||||
)
|
||||
page = page.replace(/{{AUTHENTICATION_URL}}/g, authenticationUrl)
|
||||
page = page.replace(/{{AUTOVIEWERMODE}}/g, autoViewerMode ? 'true' : 'false')
|
||||
page = page.replace(/{{CONVERSEJS_THEME}}/g, converseJSTheme)
|
||||
@ -382,44 +405,45 @@ async function enableProxyRoute (
|
||||
})
|
||||
}
|
||||
|
||||
interface ConnectionInfos {
|
||||
userJID: string
|
||||
interface WCRemoteConnectionInfos {
|
||||
roomJID: string
|
||||
boshUri: string
|
||||
wsUri?: string
|
||||
remoteXMPPServer: boolean
|
||||
anonymous?: {
|
||||
userJID: string
|
||||
boshUri: string
|
||||
wsUri?: string
|
||||
}
|
||||
authenticated?: boolean
|
||||
}
|
||||
|
||||
async function _remoteConnectionInfos (remoteChatInfos: LiveChatJSONLDAttribute): Promise<ConnectionInfos | null> {
|
||||
async function _remoteConnectionInfos (remoteChatInfos: LiveChatJSONLDAttribute): Promise<WCRemoteConnectionInfos> {
|
||||
if (!remoteChatInfos) { throw new Error('Should have remote chat infos for remote videos') }
|
||||
const connectionInfos = anonymousConnectionInfos(remoteChatInfos ?? false)
|
||||
if (!connectionInfos || !connectionInfos.boshUri) {
|
||||
return null
|
||||
if (remoteChatInfos.type !== 'xmpp') { throw new Error('Should have remote xmpp chat infos for remote videos') }
|
||||
const connectionInfos: WCRemoteConnectionInfos = {
|
||||
roomJID: remoteChatInfos.jid
|
||||
}
|
||||
return {
|
||||
userJID: connectionInfos.userJID,
|
||||
roomJID: connectionInfos.roomJID,
|
||||
boshUri: connectionInfos.boshUri,
|
||||
wsUri: connectionInfos.wsUri,
|
||||
remoteXMPPServer: true
|
||||
if (remoteAuthenticatedConnectionEnabled(remoteChatInfos)) {
|
||||
connectionInfos.authenticated = true
|
||||
}
|
||||
const anonymousCI = anonymousConnectionInfos(remoteChatInfos ?? false)
|
||||
if (anonymousCI?.boshUri) {
|
||||
connectionInfos.anonymous = {
|
||||
userJID: anonymousCI.userJID,
|
||||
boshUri: anonymousCI.boshUri,
|
||||
wsUri: anonymousCI.wsUri
|
||||
}
|
||||
}
|
||||
return connectionInfos
|
||||
}
|
||||
|
||||
async function _localConnectionInfos (
|
||||
async function _localRoomJID (
|
||||
options: RegisterServerOptions,
|
||||
settings: SettingEntries,
|
||||
prosodyDomain: string,
|
||||
roomKey: string,
|
||||
video: MVideoThumbnail | undefined,
|
||||
channelId: number,
|
||||
forceType: boolean
|
||||
): Promise<ConnectionInfos> {
|
||||
const prosodyDomain = await getProsodyDomain(options)
|
||||
const jid = 'anon.' + prosodyDomain
|
||||
const boshUri = getBoshUri(options)
|
||||
const wsUri = settings['disable-websocket']
|
||||
? ''
|
||||
: (getWSUri(options) ?? '')
|
||||
|
||||
): Promise<string> {
|
||||
// Computing the room name...
|
||||
let room: string
|
||||
if (forceType) {
|
||||
@ -462,13 +486,7 @@ async function _localConnectionInfos (
|
||||
room = room.replace(/{{CHANNEL_NAME}}/g, channelName)
|
||||
}
|
||||
|
||||
return {
|
||||
userJID: jid,
|
||||
roomJID: room,
|
||||
boshUri,
|
||||
wsUri,
|
||||
remoteXMPPServer: false
|
||||
}
|
||||
return room
|
||||
}
|
||||
|
||||
export {
|
||||
|
Reference in New Issue
Block a user