diff --git a/client/common/lib/contexts/peertube.ts b/client/common/lib/contexts/peertube.ts index 0dac9914..61c4d34d 100644 --- a/client/common/lib/contexts/peertube.ts +++ b/client/common/lib/contexts/peertube.ts @@ -18,6 +18,7 @@ export type LiveChatSettings = SettingEntries & { 'prosody-room-allow-s2s': boolean 'converse-theme': ConverseJSTheme 'prosody-room-type': string + 'livechat-token-disabled': boolean } export class PtContext { diff --git a/client/common/videowatch/elements/share-chat.ts b/client/common/videowatch/elements/share-chat.ts index 88c8aa88..cb9a3ff1 100644 --- a/client/common/videowatch/elements/share-chat.ts +++ b/client/common/videowatch/elements/share-chat.ts @@ -111,7 +111,12 @@ export class ShareChatElement extends LivechatElement { // Note: for dockEnabled, we check: // * that the user is logged in // * that the video is local (for remote video, tests case are too complicated, and it's not the main use case, so…) - this.dockEnabled = !isAnonymousUser(this.ptContext.ptOptions) && this._video.isLocal + // * settings is not disabled + this.dockEnabled = ( + !isAnonymousUser(this.ptContext.ptOptions) && + this._video.isLocal && + !settings['livechat-token-disabled'] + ) this.autocolorsAvailable = isAutoColorsAvailable(settings['converse-theme']) this._restorePreviousState() diff --git a/languages/en.yml b/languages/en.yml index 9f99fdbb..36b41eea 100644 --- a/languages/en.yml +++ b/languages/en.yml @@ -536,3 +536,11 @@ token_action_create: Create a new token token_action_revoke: Revoke the token token_default_label: Token generated from the web interface token_action_revoke_confirm: Are you sure you want to revoke this token? +auth_description: | +

Authentication

+livechat_token_disabled_label: 'Disable livechat tokens' +livechat_token_disabled_description: | + Users can generate long term tokens to connect to the chat. + These tokens can for example be used to include the chat in OBS web docks. + Check the documentation for more information. + You can disable this feature by checking this setting. diff --git a/server/lib/prosody/auth.ts b/server/lib/prosody/auth.ts index 674d281a..98636228 100644 --- a/server/lib/prosody/auth.ts +++ b/server/lib/prosody/auth.ts @@ -63,6 +63,7 @@ let singleton: LivechatProsodyAuth | undefined export class LivechatProsodyAuth { private readonly _options: RegisterServerOptions private readonly _prosodyDomain: string + private _userTokensEnabled: boolean private readonly _tokensPath: string private readonly _passwords: Map = new Map() private readonly _tokensInfoByJID: Map = new Map() @@ -80,9 +81,10 @@ export class LivechatProsodyAuth { outputEncoding: 'hex' as Encoding } - constructor (options: RegisterServerOptions, prosodyDomain: string, secretKey: string) { + constructor (options: RegisterServerOptions, prosodyDomain: string, userTokensEnabled: boolean, secretKey: string) { this._options = options this._prosodyDomain = prosodyDomain + this._userTokensEnabled = userTokensEnabled this._secretKey = secretKey this._tokensPath = path.join( options.peertubeHelpers.plugin.getDataDirectoryPath(), @@ -131,18 +133,20 @@ export class LivechatProsodyAuth { if (entry) { return true } - try { - const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain) - if (!tokensInfo || !tokensInfo.tokens.length) { + if (this._userTokensEnabled) { + try { + const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain) + if (!tokensInfo || !tokensInfo.tokens.length) { + return false + } + // Checking that the user is valid: + if (await this._userIdValid(tokensInfo.userId)) { + return true + } + } catch (err) { + this._logger.error(err as string) return false } - // Checking that the user is valid: - if (await this._userIdValid(tokensInfo.userId)) { - return true - } - } catch (err) { - this._logger.error(err as string) - return false } return false } @@ -152,23 +156,25 @@ export class LivechatProsodyAuth { if (entry && entry.password === password) { return true } - try { - const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain) - if (!tokensInfo || !tokensInfo.tokens.length) { - return false - } - // Checking that the user is valid: - if (!await this._userIdValid(tokensInfo.userId)) { - return false - } + if (this._userTokensEnabled) { + try { + const tokensInfo = await this._getTokensInfoForJID(normalizedUsername + '@' + this._prosodyDomain) + if (!tokensInfo || !tokensInfo.tokens.length) { + return false + } + // Checking that the user is valid: + if (!await this._userIdValid(tokensInfo.userId)) { + return false + } - // Is the password in tokens? - if (tokensInfo.tokens.find((t) => t.password === password)) { - return true + // Is the password in tokens? + if (tokensInfo.tokens.find((t) => t.password === password)) { + return true + } + } catch (err) { + this._logger.error(err as string) + return false } - } catch (err) { - this._logger.error(err as string) - return false } return false } @@ -179,6 +185,9 @@ export class LivechatProsodyAuth { * @param user the user */ public async getUserTokens (user: MUserDefault): Promise { + if (!this._userTokensEnabled) { + return undefined + } if (!user || !user.id) { return undefined } @@ -210,7 +219,22 @@ export class LivechatProsodyAuth { return tokens } + /** + * Enable or disable user tokens. Must be called when the settings change. + * @param enabled + */ + public setUserTokensEnabled (enabled: boolean): void { + this._userTokensEnabled = !!enabled + if (!this.userRegistered) { + // Empty the cache: + this._tokensInfoByJID.clear() + } + } + public async createUserToken (user: MUserDefault, label: string): Promise { + if (!this._userTokensEnabled) { + return undefined + } if (!user || !user.id) { return undefined } @@ -230,6 +254,9 @@ export class LivechatProsodyAuth { } public async revokeUserToken (user: MUserDefault, id: number): Promise { + if (!this._userTokensEnabled) { + return false + } if (!user || !user.id) { return false } @@ -448,12 +475,13 @@ export class LivechatProsodyAuth { await options.storageManager.storeData('livechat-prosody-auth-secretkey', secretKey) } - singleton = new LivechatProsodyAuth(options, prosodyDomain, secretKey) + const userTokenDisabled = await options.settingsManager.getSetting('livechat-token-disabled') + + singleton = new LivechatProsodyAuth(options, prosodyDomain, !userTokenDisabled, secretKey) return singleton } public static async destroySingleton (): Promise { - // TODO: sync to disk singleton = undefined } } diff --git a/server/lib/settings.ts b/server/lib/settings.ts index eda78e1d..c51a3a62 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -9,6 +9,7 @@ import { RoomChannel } from './room-channel' import { BotsCtl } from './bots/ctl' import { ExternalAuthOIDC, ExternalAuthOIDCType } from './external-auth/oidc' import { Emojis } from './emojis' +import { LivechatProsodyAuth } from './prosody/auth' import { loc } from './loc' const escapeHTML = require('escape-html') @@ -21,6 +22,7 @@ async function initSettings (options: RegisterServerOptions): Promise { initImportantNotesSettings(options) initChatSettings(options) initFederationSettings(options) + initAuth(options) initExternalAuth(options) initAdvancedChannelCustomizationSettings(options) initChatBehaviourSettings(options) @@ -73,6 +75,8 @@ async function initSettings (options: RegisterServerOptions): Promise { await Emojis.destroySingleton() await Emojis.initSingleton(options) + LivechatProsodyAuth.singleton().setUserTokensEnabled(!settings['livechat-token-disabled']) + peertubeHelpers.logger.info('Saving settings, ensuring prosody is running') await ensureProsodyRunning(options) @@ -181,6 +185,35 @@ function initFederationSettings ({ registerSetting }: RegisterServerOptions): vo }) } +/** + * Initialize settings related to authentication. + * @param options peertube server options + */ +function initAuth (options: RegisterServerOptions): void { + const registerSetting = options.registerSetting + + registerSetting({ + type: 'html', + private: true, + descriptionHTML: loc('auth_description') + }) + + registerSetting({ + type: 'html', + private: true, + descriptionHTML: loc('experimental_warning') + }) + + registerSetting({ + name: 'livechat-token-disabled', + label: loc('livechat_token_disabled_label'), + descriptionHTML: loc('livechat_token_disabled_description'), + type: 'input-checkbox', + default: false, + private: false + }) +} + /** * Registers settings related to the "External Authentication" section. * @param param0 server options diff --git a/server/main.ts b/server/main.ts index e3854f26..61ae90ac 100644 --- a/server/main.ts +++ b/server/main.ts @@ -52,7 +52,7 @@ async function register (options: RegisterServerOptions): Promise { await initSettings(options) await Emojis.initSingleton(options) // after settings, before routes - await LivechatProsodyAuth.initSingleton(options) + await LivechatProsodyAuth.initSingleton(options) // after settings, before routes await initCustomFields(options) await initRouters(options) diff --git a/support/documentation/content/en/documentation/admin/settings.md b/support/documentation/content/en/documentation/admin/settings.md index 39d97e57..31b58387 100644 --- a/support/documentation/content/en/documentation/admin/settings.md +++ b/support/documentation/content/en/documentation/admin/settings.md @@ -24,6 +24,12 @@ Following settings concern the federation with other Peertube instances, and oth {{% livechat_label federation_dont_publish_remotely_description %}} +## Authentication + +### {{% livechat_label livechat_token_disabled_label %}} + +In case you have any trouble with the long term authentication tokens, you can disable the feature here. + ## External Authentication See the detailed documentation page: