Possibility to configure an OpenID Connect provider on the instance level WIP (#128).

This commit is contained in:
John Livingston
2024-04-17 16:35:26 +02:00
parent 8574ab581d
commit 3a5f27e751
7 changed files with 230 additions and 1 deletions

View File

@ -0,0 +1,66 @@
import type { RegisterServerOptions } from '@peertube/peertube-types'
import type { ExternalAccountInfos } from '../../external-auth/types'
import { getCurrentProsody } from './host'
import { getProsodyDomain } from '../config/domain'
import { getAPIKey } from '../../apikey'
const got = require('got')
/**
* Created or updates a user.
* Can be used to manage external accounts for example (create the user the first time, update infos next time).
* Uses an API provided by mod_http_peertubelivechat_manage_users.
*
* @param options Peertube server options
* @param data up-to-date user infos.
* @returns true if success
*/
async function ensureUser (options: RegisterServerOptions, infos: ExternalAccountInfos): Promise<boolean> {
const logger = options.peertubeHelpers.logger
const currentProsody = getCurrentProsody()
if (!currentProsody) {
throw new Error('It seems that prosody is not binded... Cant call API.')
}
const prosodyDomain = await getProsodyDomain(options)
logger.info('Calling ensureUser for ' + infos.jid)
// Requesting on localhost, because currentProsody.host does not always resolves correctly (docker use case, ...)
const apiUrl = `http://localhost:${currentProsody.port}/` +
'peertubelivechat_manage_users/' +
`external.${prosodyDomain}/` + // the virtual host name
'ensure-user'
const apiData = {
jid: infos.jid,
nickname: infos.nickname,
password: infos.password
}
try {
logger.debug('Calling ensure-user API on url: ' + apiUrl)
const result = await got(apiUrl, {
method: 'POST',
headers: {
authorization: 'Bearer ' + await getAPIKey(options),
host: currentProsody.host
},
json: apiData,
responseType: 'json',
resolveBodyOnly: true
})
logger.debug('ensure-user API response: ' + JSON.stringify(result))
if (result.result !== 'ok') {
logger.error('ensure-user API has failed: ' + JSON.stringify(result))
return false
}
} catch (err) {
logger.error(`ensure-user failed: ' ${err as string}`)
return false
}
return true
}
export {
ensureUser
}

View File

@ -13,6 +13,7 @@ import { parseExternalComponents } from './config/components'
import { getRemoteServerInfosDir } from '../federation/storage'
import { BotConfiguration } from '../configuration/bot'
import { debugMucAdmins } from '../debug'
import { ExternalAuthOIDC } from '../external-auth/oidc'
async function getWorkingDir (options: RegisterServerOptions): Promise<string> {
const peertubeHelpers = options.peertubeHelpers
@ -194,6 +195,17 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
const useBots = !settings['disable-channel-configuration']
const bots: ProsodyConfig['bots'] = {}
let useExternal: boolean = false
try {
const oidc = ExternalAuthOIDC.singleton()
if (await oidc.isOk()) {
useExternal = true
}
} catch (err) {
logger.error(err)
useExternal = false
}
// Note: for the bots to connect, we must allow multiplexing.
// This will be done on the http (BOSH/Websocket) port, as it only listen on localhost.
// TODO: to improve performance, try to avoid multiplexing, and find a better way for bots to connect.
@ -243,6 +255,11 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
if (!disableAnon) {
config.useAnonymous(autoBanIP)
}
if (useExternal) {
config.useExternal(apikey)
}
config.useHttpAuthentication(authApiUrl)
const useWS = !!options.registerWebSocketRoute // this comes with Peertube >=5.0.0, and is a prerequisite to websocket
config.usePeertubeBoshAndWebsocket(prosodyDomain, port, publicServerUrl, useWS, useMultiplexing)

View File

@ -140,6 +140,7 @@ class ProsodyConfigContent {
global: ProsodyConfigGlobal
authenticated?: ProsodyConfigVirtualHost
anon?: ProsodyConfigVirtualHost
external?: ProsodyConfigVirtualHost
muc: ProsodyConfigComponent
bot?: ProsodyConfigVirtualHost
externalComponents: ProsodyConfigComponent[] = []
@ -222,6 +223,19 @@ class ProsodyConfigContent {
}
}
/**
* Activates the virtual host for external account authentication (OpenID Connect, ...)
*/
useExternal (apikey: string): void {
this.external = new ProsodyConfigVirtualHost('external.' + this.prosodyDomain)
this.external.set('modules_enabled', [
'ping',
'http',
'http_peertubelivechat_manage_users'
])
this.external.set('peertubelivechat_manage_users_apikey', apikey)
}
useHttpAuthentication (url: string): void {
this.authenticated = new ProsodyConfigVirtualHost(this.prosodyDomain)
@ -304,6 +318,17 @@ class ProsodyConfigContent {
this.authenticated.set('http_host', prosodyDomain)
this.authenticated.set('http_external_url', 'http://' + prosodyDomain)
}
if (this.external) {
this.external.set('allow_anonymous_s2s', false)
this.external.add('modules_enabled', 'http')
this.external.add('modules_enabled', 'bosh')
if (useWS) {
this.external.add('modules_enabled', 'websocket')
}
this.external.set('http_host', prosodyDomain)
this.external.set('http_external_url', 'http://' + prosodyDomain)
}
}
useC2S (c2sPort: string): void {
@ -501,6 +526,10 @@ class ProsodyConfigContent {
content += this.bot.write()
content += '\n\n'
}
if (this.external) {
content += this.external.write()
content += '\n\n'
}
content += this.muc.write()
content += '\n\n'
for (const externalComponent of this.externalComponents) {