diff --git a/client/components/NetworkForm.vue b/client/components/NetworkForm.vue index 87aca0b04..cbc47dd35 100644 --- a/client/components/NetworkForm.vue +++ b/client/components/NetworkForm.vue @@ -178,31 +178,21 @@ diff --git a/defaults/config.js b/defaults/config.js index a943ca0f4..8be67f5c1 100644 --- a/defaults/config.js +++ b/defaults/config.js @@ -264,10 +264,7 @@ module.exports = { // ``` defaults: { name: "Libera.Chat", - host: "irc.libera.chat", - port: 6697, password: "", - tls: true, rejectUnauthorized: true, nick: "thelounge%%", username: "thelounge", @@ -285,6 +282,21 @@ module.exports = { // This value is set to `false` by default. lockNetwork: false, + networks: { + "Libera.Chat": { + host: "irc.libera.chat", + port: 6697, + tls: true, + rejectUnauthorized: true + }, + "OFTC": { + host: "irc.oftc.net", + port: 6697, + tls: true, + rejectUnauthorized: true + } + }, + // ## User management // ### `messageStorage` diff --git a/package.json b/package.json index 3deab8912..5b329ebb9 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "thelounge", "description": "The self-hosted Web IRC client", - "version": "4.4.3", + "version": "4.4.3+revspace6", "preferGlobal": true, "bin": { "thelounge": "index.js" }, "repository": { "type": "git", - "url": "https://github.com/thelounge/thelounge.git" + "url": "https://github.com/revspace/thelounge.git" }, "homepage": "https://thelounge.chat/", "scripts": { @@ -64,7 +64,7 @@ "file-type": "16.5.4", "filenamify": "4.3.0", "got": "11.8.5", - "irc-framework": "4.13.1", + "irc-framework": "https://github.com/revspace/nodejs-irc-framework", "is-utf8": "0.2.1", "ldapjs": "2.3.1", "linkify-it": "3.0.3", diff --git a/server/client.ts b/server/client.ts index f2f125941..876eada14 100644 --- a/server/client.ts +++ b/server/client.ts @@ -313,6 +313,7 @@ class Client { host: String(args.host || ""), port: parseInt(String(args.port), 10), tls: !!args.tls, + caCert: args.caCert, userDisconnected: !!args.userDisconnected, rejectUnauthorized: !!args.rejectUnauthorized, password: String(args.password || ""), diff --git a/server/command-line/start.ts b/server/command-line/start.ts index 6ad678541..bd132ee04 100644 --- a/server/command-line/start.ts +++ b/server/command-line/start.ts @@ -10,8 +10,13 @@ const program = new Command("start"); program .description("Start the server") .option("--dev", "Development mode with hot module reloading") + .option("--configPath ", "config file path") .on("--help", Utils.extraHelp) .action(function (options) { + if (options.configPath !== undefined) { + Config.configPath = options.configPath; + } + initalizeConfig(); const newLocal = "../server"; diff --git a/server/config.ts b/server/config.ts index 192333c85..82849cf34 100644 --- a/server/config.ts +++ b/server/config.ts @@ -32,11 +32,7 @@ type FileUpload = { export type Defaults = Pick< Network, | "name" - | "host" - | "port" | "password" - | "tls" - | "rejectUnauthorized" | "nick" | "username" | "realname" @@ -46,6 +42,7 @@ export type Defaults = Pick< | "saslPassword" > & { join: string; + network: string; }; type Identd = { @@ -83,6 +80,24 @@ type StoragePolicy = { deletionPolicy: "statusOnly" | "everything"; }; +type TemplateNetwork = { + name: string, + host: string, + port: number, + tls: boolean, + rejectUnauthorized: boolean, + caCert?: Buffer +}; + +type NetworkInConfig = { + name: string, + host: string, + port: number, + tls: boolean, + rejectUnauthorized?: boolean, + caCert?: string +}; + export type ConfigType = { public: boolean; host: string | undefined; @@ -103,6 +118,7 @@ export type ConfigType = { leaveMessage: string; defaults: Defaults; lockNetwork: boolean; + networks: {[name: string]: NetworkInConfig}; messageStorage: string[]; storagePolicy: StoragePolicy; useHexIp: boolean; @@ -119,14 +135,15 @@ class Config { path.join(__dirname, "..", "defaults", "config.js") )) as ConfigType; #homePath = ""; + configPath: string | undefined; + networks: {[name: string]: TemplateNetwork} = this.parseNetworks(); getHomePath() { return this.#homePath; } getConfigPath() { - // return path.join(this.#homePath, "config.js"); - return "/etc/thelounge/config.js"; + return this.configPath ?? path.join(this.#homePath, "config.js"); } getUserLogsPath() { @@ -171,8 +188,30 @@ class Config { ); } + getNetworks() { + return this.networks; + } + + getNetworkNames() { + return Object.keys(this.networks); + } + + parseNetworks() { + return Object.fromEntries(Object.entries(this.values.networks).map(([name, network]) => { + return [name, { + name, + host: network.host, + port: network.port, + tls: network.tls !== undefined ? network.tls : true, + rejectUnauthorized: network.rejectUnauthorized !== undefined ? network.rejectUnauthorized : true, + caCert: network.caCert ? fs.readFileSync(network.caCert) : undefined + }]; + })); + } + merge(newConfig: ConfigType) { this._merge_config_objects(this.values, newConfig); + this.networks = this.parseNetworks(); } _merge_config_objects(oldConfig: ConfigType, newConfig: ConfigType) { diff --git a/server/models/network.ts b/server/models/network.ts index 3860fa451..d698163e4 100644 --- a/server/models/network.ts +++ b/server/models/network.ts @@ -21,6 +21,7 @@ type NetworkIrcOptions = { username: string; gecos: string; tls: boolean; + ca_certificate?: Buffer; rejectUnauthorized: boolean; webirc: WebIRC | null; client_certificate: ClientCertificateType | null; @@ -94,6 +95,7 @@ class Network { host!: string; port!: number; tls!: boolean; + caCert!: Buffer; userDisconnected!: boolean; rejectUnauthorized!: boolean; password!: string; @@ -246,26 +248,25 @@ class Network { if (Config.values.lockNetwork) { // This check is needed to prevent invalid user configurations - if ( - !Config.values.public && - this.host && - this.host.length > 0 && - this.host !== Config.values.defaults.host - ) { + + const allowedNetwork = Object.values(Config.getNetworks()).find((network) => { + return (this.name === network.name || this.host === network.host); + }); + + if (allowedNetwork === undefined) { error(this, `The hostname you specified (${this.host}) is not allowed.`); return false; } - if (Config.values.public) { - this.name = Config.values.defaults.name; - // Sync lobby channel name - this.getLobby().name = Config.values.defaults.name; - } + this.name = allowedNetwork.name; + this.host = allowedNetwork.host; + this.port = allowedNetwork.port; + this.tls = allowedNetwork.tls; + this.rejectUnauthorized = allowedNetwork.rejectUnauthorized; - this.host = Config.values.defaults.host; - this.port = Config.values.defaults.port; - this.tls = Config.values.defaults.tls; - this.rejectUnauthorized = Config.values.defaults.rejectUnauthorized; + if (allowedNetwork.caCert !== undefined) { + this.caCert = allowedNetwork.caCert; + } } if (this.host.length === 0) { @@ -324,6 +325,7 @@ class Network { this.irc.options.gecos = this.realname; this.irc.options.tls = this.tls; this.irc.options.rejectUnauthorized = this.rejectUnauthorized; + this.irc.options.ca_certificate = this.caCert; this.irc.options.webirc = this.createWebIrc(client); this.irc.options.client_certificate = null; diff --git a/server/server.ts b/server/server.ts index 9809eaf31..364c5846a 100644 --- a/server/server.ts +++ b/server/server.ts @@ -873,6 +873,7 @@ function getClientConfiguration(data: AuthPerformData): SharedConfiguration | Lo useHexIp: Config.values.useHexIp, prefetch: Config.values.prefetch, fileUploadMaxFileSize: Uploader ? Uploader.getMaxFileSize() : undefined, // TODO can't be undefined? + networks: Config.getNetworkNames() }; const defaultsOverride = { @@ -889,8 +890,18 @@ function getClientConfiguration(data: AuthPerformData): SharedConfiguration | Lo }; if (!Config.values.lockNetwork) { + const defaultNetwork = Config.values.networks[Config.values.defaults.name]; + + if (defaultNetwork.rejectUnauthorized === undefined) { + defaultNetwork.rejectUnauthorized = true; + } + const defaults: ConfigNetDefaults = { ..._.clone(Config.values.defaults), + host: defaultNetwork.host, + port: defaultNetwork.port, + tls: defaultNetwork.tls, + rejectUnauthorized: defaultNetwork.rejectUnauthorized, ...defaultsOverride, }; const result: SharedConfiguration = { diff --git a/shared/types/config.ts b/shared/types/config.ts index 4b7919eee..a7486a498 100644 --- a/shared/types/config.ts +++ b/shared/types/config.ts @@ -3,6 +3,14 @@ export type ConfigTheme = { name: string; themeColor: string | null; }; + +type NetworkTemplate = { + host: string, + port: number, + tls: boolean, + rejectUnauthorized: boolean // if TLS certificates are validated +}; + type SharedConfigurationBase = { public: boolean; useHexIp: boolean; @@ -16,6 +24,7 @@ type SharedConfigurationBase = { themes: ConfigTheme[]; defaultTheme: string; fileUploadMaxFileSize?: number; + networks: string[]; }; export type ConfigNetDefaults = { @@ -34,6 +43,7 @@ export type ConfigNetDefaults = { saslAccount: string; saslPassword: string; }; + export type LockedConfigNetDefaults = Pick< ConfigNetDefaults, "name" | "nick" | "username" | "password" | "realname" | "join" diff --git a/yarn.lock b/yarn.lock index 33a05e5b9..6a1b8f94d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3151,7 +3151,7 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1: dependencies: browserslist "^4.23.3" -core-js@^3.27.2: +core-js@^3.38.1: version "3.38.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.38.1.tgz#aa375b79a286a670388a1a363363d53677c0383e" integrity sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw== @@ -4022,7 +4022,7 @@ eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -eventemitter3@^5.0.0: +eventemitter3@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== @@ -4943,21 +4943,20 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -irc-framework@4.13.1: - version "4.13.1" - resolved "https://registry.yarnpkg.com/irc-framework/-/irc-framework-4.13.1.tgz#9850ffd220c6ddded960f8b95d0612d646f9a1b7" - integrity sha512-oUdNyc5CLwYjsp5AP479EgdMMTepwYK9kury7sWzMV6IeMyKc6fExk6tnhN/jTWpiDKsYtbPAb01wE7yVtLcsQ== +"irc-framework@https://github.com/revspace/nodejs-irc-framework": + version "4.14.0" + resolved "https://github.com/revspace/nodejs-irc-framework#7444a1f3e7509342a26b909b2bfd5a9e955db6c8" dependencies: buffer "^6.0.3" - core-js "^3.27.2" - eventemitter3 "^5.0.0" + core-js "^3.38.1" + eventemitter3 "^5.0.1" grapheme-splitter "^1.0.4" iconv-lite "^0.6.3" isomorphic-textencoder "^1.0.1" lodash "^4.17.21" middleware-handler "^0.2.0" - regenerator-runtime "^0.13.11" - socks "^2.7.1" + regenerator-runtime "^0.14.1" + socks "^2.8.3" stream-browserify "^3.0.0" util "^0.12.5" @@ -7336,12 +7335,7 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - -regenerator-runtime@^0.14.0: +regenerator-runtime@^0.14.0, regenerator-runtime@^0.14.1: version "0.14.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== @@ -7877,7 +7871,7 @@ socks-proxy-agent@^6.0.0: debug "^4.3.3" socks "^2.6.2" -socks@^2.6.2, socks@^2.7.1: +socks@^2.6.2, socks@^2.8.3: version "2.8.3" resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==