From ed3ec6a560e318f384a085e064deef7f8c00fdd3 Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Sun, 13 Feb 2022 13:24:06 +0100 Subject: [PATCH 1/6] test/models/network.js: Fix test groupping --- test/models/network.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/models/network.js b/test/models/network.js index fe8699d9..b99b5b89 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -61,8 +61,10 @@ describe("Network", function () { ignoreList: [], }); }); + }); - it("validate should set correct defaults", function () { + describe("#validate()", function () { + it("should set correct defaults", function () { Helper.config.defaults.nick = ""; const network = new Network({ @@ -83,7 +85,7 @@ describe("Network", function () { expect(network2.username).to.equal("InvalidNick"); }); - it("lockNetwork should be enforced when validating", function () { + it("should enforce lockNetwork", function () { Helper.config.lockNetwork = true; // Make sure we lock in private mode @@ -112,8 +114,10 @@ describe("Network", function () { Helper.config.lockNetwork = false; }); + }); - it("editing a network should enforce correct types", function () { + describe("#edit(client, args)", function () { + it("should enforce correct types", function () { let saveCalled = false; let nameEmitCalled = false; @@ -177,7 +181,9 @@ describe("Network", function () { "/whois test", ]); }); + }); + describe("Network(attr)", function () { it("should generate uuid (v4) for each network", function () { const network1 = new Network(); const network2 = new Network(); From bcd4a060ecff8110d09088320ce49dba15f199a5 Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Sun, 13 Feb 2022 13:24:38 +0100 Subject: [PATCH 2/6] test/models/network.js: Reorder tests --- test/models/network.js | 118 ++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/test/models/network.js b/test/models/network.js index b99b5b89..12d9b01e 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -8,6 +8,65 @@ const Network = require("../../src/models/network"); const Helper = require("../../src/helper"); describe("Network", function () { + describe("Network(attr)", function () { + it("should generate uuid (v4) for each network", function () { + const network1 = new Network(); + const network2 = new Network(); + + expect(network1.uuid).to.have.lengthOf(36); + expect(network2.uuid).to.have.lengthOf(36); + expect(network1.uuid).to.not.equal(network2.uuid); + }); + + it("lobby should be at the top", function () { + const network = new Network({ + name: "Super Nice Network", + channels: [ + new Chan({name: "AAAA!", type: Chan.Type.QUERY}), + new Chan({name: "#thelounge"}), + new Chan({name: "&foobar"}), + ], + }); + network.channels.push(new Chan({name: "#swag"})); + + expect(network.channels[0].name).to.equal("Super Nice Network"); + expect(network.channels[0].type).to.equal(Chan.Type.LOBBY); + }); + + it("should maintain channel reference", function () { + const chan = new Chan({ + name: "#506-bug-fix", + messages: [ + new Msg({ + text: "message in constructor", + }), + ], + }); + + const network = new Network({ + name: "networkName", + channels: [chan], + }); + + chan.messages.push( + new Msg({ + text: "message in original instance", + }) + ); + + network.channels[1].messages.push( + new Msg({ + text: "message after network creation", + }) + ); + + expect(network.channels[1].messages).to.have.lengthOf(3); + expect(network.channels[1].messages[0].text).to.equal("message in constructor"); + expect(network.channels[1].messages[1].text).to.equal("message in original instance"); + expect(network.channels[1].messages[2].text).to.equal("message after network creation"); + }); + }); + describe("#export()", function () { it("should produce an valid object", function () { const network = new Network({ @@ -183,65 +242,6 @@ describe("Network", function () { }); }); - describe("Network(attr)", function () { - it("should generate uuid (v4) for each network", function () { - const network1 = new Network(); - const network2 = new Network(); - - expect(network1.uuid).to.have.lengthOf(36); - expect(network2.uuid).to.have.lengthOf(36); - expect(network1.uuid).to.not.equal(network2.uuid); - }); - - it("lobby should be at the top", function () { - const network = new Network({ - name: "Super Nice Network", - channels: [ - new Chan({name: "AAAA!", type: Chan.Type.QUERY}), - new Chan({name: "#thelounge"}), - new Chan({name: "&foobar"}), - ], - }); - network.channels.push(new Chan({name: "#swag"})); - - expect(network.channels[0].name).to.equal("Super Nice Network"); - expect(network.channels[0].type).to.equal(Chan.Type.LOBBY); - }); - - it("should maintain channel reference", function () { - const chan = new Chan({ - name: "#506-bug-fix", - messages: [ - new Msg({ - text: "message in constructor", - }), - ], - }); - - const network = new Network({ - name: "networkName", - channels: [chan], - }); - - chan.messages.push( - new Msg({ - text: "message in original instance", - }) - ); - - network.channels[1].messages.push( - new Msg({ - text: "message after network creation", - }) - ); - - expect(network.channels[1].messages).to.have.lengthOf(3); - expect(network.channels[1].messages[0].text).to.equal("message in constructor"); - expect(network.channels[1].messages[1].text).to.equal("message in original instance"); - expect(network.channels[1].messages[2].text).to.equal("message after network creation"); - }); - }); - describe("#getFilteredClone(lastActiveChannel, lastMessage)", function () { it("should filter channels", function () { const chan = new Chan(); From bd2a6cc5bec8702f6f54c2ae6fbf0c1018b65526 Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Sun, 13 Feb 2022 14:21:40 +0100 Subject: [PATCH 3/6] test/models/network.js: Add a simple test for STS policies --- test/fixtures/env.js | 9 +++++++++ test/models/network.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/test/fixtures/env.js b/test/fixtures/env.js index 67f22edb..57adf7ad 100644 --- a/test/fixtures/env.js +++ b/test/fixtures/env.js @@ -1,4 +1,13 @@ "use strict"; +const fs = require("fs"); + const home = require("path").join(__dirname, ".thelounge"); require("../../src/helper").setHome(home); + +const STSPolicies = require("../../src/plugins/sts"); // Must be imported *after* setHome + +exports.mochaGlobalTeardown = async function () { + STSPolicies.refresh.cancel(); // Cancel debounced function, so it does not write later + fs.unlinkSync(STSPolicies.stsFile); +}; diff --git a/test/models/network.js b/test/models/network.js index 12d9b01e..5b2b7eef 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -6,6 +6,7 @@ const Msg = require("../../src/models/msg"); const User = require("../../src/models/user"); const Network = require("../../src/models/network"); const Helper = require("../../src/helper"); +const STSPolicies = require("../../src/plugins/sts"); describe("Network", function () { describe("Network(attr)", function () { @@ -173,6 +174,33 @@ describe("Network", function () { Helper.config.lockNetwork = false; }); + + it("should apply STS policies iff they match", function () { + const client = {idMsg: 1, emit() {}}; + STSPolicies.update("irc.example.com", 7000, 3600); + + let network = new Network({ + host: "irc.example.com", + port: 1337, + tls: false, + }); + + expect(network.validate(client)).to.be.true; + expect(network.port).to.equal(7000); + expect(network.tls).to.be.true; + + network = new Network({ + host: "irc2.example.com", + port: 1337, + tls: false, + }); + + expect(network.validate(client)).to.be.true; + expect(network.port).to.equal(1337); + expect(network.tls).to.be.false; + + STSPolicies.update("irc.example.com", 7000, 0); // Cleanup + }); }); describe("#edit(client, args)", function () { From ba210e853b0f8c9d0e9d33d3fce0bb4486cba21e Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Sun, 13 Feb 2022 14:21:59 +0100 Subject: [PATCH 4/6] test/models/network.js: Add tests for automatic client certificate creation/deletion --- test/models/network.js | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/models/network.js b/test/models/network.js index 5b2b7eef..315581a0 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -7,6 +7,7 @@ const User = require("../../src/models/user"); const Network = require("../../src/models/network"); const Helper = require("../../src/helper"); const STSPolicies = require("../../src/plugins/sts"); +const ClientCertificate = require("../../src/plugins/clientCertificate"); describe("Network", function () { describe("Network(attr)", function () { @@ -201,6 +202,49 @@ describe("Network", function () { STSPolicies.update("irc.example.com", 7000, 0); // Cleanup }); + + it("should remove client certs if TLS is disabled", function () { + Helper.config.public = false; + + const client = {idMsg: 1, emit() {}, messageStorage: []}; + + const network = new Network({host: "irc.example.com", sasl: "external"}); + network.createIrcFramework(client); + expect(network.irc).to.not.be.null; + + const client_cert = network.irc.options.client_certificate; + expect(client_cert).to.not.be.null; + expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); + + expect(network.validate(client)).to.be.true; // Deletes the cert + + expect(ClientCertificate.get(network.uuid)).to.not.deep.equal(client_cert); // Because ClientCertificate.get regenerates it + + ClientCertificate.remove(network.uuid); + Helper.config.public = true; + }); + }); + + describe("#createIrcFramework(client)", function () { + it("should generate and use a client certificate when using SASL external", function () { + Helper.config.public = false; + + const client = {idMsg: 1, emit() {}}; + STSPolicies.update("irc.example.com", 7000, 3600); + + let network = new Network({host: "irc.example.com"}); + network.createIrcFramework(client); + expect(network.irc).to.not.be.null; + expect(network.irc.options.client_certificate).to.be.null; + + network = new Network({host: "irc.example.com", sasl: "external"}); + network.createIrcFramework(client); + expect(network.irc).to.not.be.null; + expect(network.irc.options.client_certificate).to.not.be.null; + + ClientCertificate.remove(network.uuid); + Helper.config.public = true; + }); }); describe("#edit(client, args)", function () { From 53b4d00732879f1ca8e99bb810ba04293cdc82f7 Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Sun, 13 Feb 2022 14:26:45 +0100 Subject: [PATCH 5/6] Preserve client certificate when TLS is indirectly enabled by a STS policy Closes GH-4152. --- src/models/network.js | 8 ++++---- test/models/network.js | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/models/network.js b/src/models/network.js index 17aaff60..822afdf2 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -122,10 +122,6 @@ Network.prototype.validate = function (client) { this.sasl = ""; } - if (!this.tls) { - ClientCertificate.remove(this.uuid); - } - if (Helper.config.lockNetwork) { // This check is needed to prevent invalid user configurations if ( @@ -188,6 +184,10 @@ Network.prototype.validate = function (client) { this.rejectUnauthorized = true; } + if (!this.tls) { + ClientCertificate.remove(this.uuid); + } + return true; }; diff --git a/test/models/network.js b/test/models/network.js index 315581a0..6abdb3e4 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -223,6 +223,28 @@ describe("Network", function () { ClientCertificate.remove(network.uuid); Helper.config.public = true; }); + + it("should remove client certs if there is a STS policy", function () { + Helper.config.public = false; + + const client = {idMsg: 1, emit() {}, messageStorage: []}; + STSPolicies.update("irc.example.com", 7000, 3600); + + const network = new Network({host: "irc.example.com", sasl: "external"}); + network.createIrcFramework(client); + expect(network.irc).to.not.be.null; + + const client_cert = network.irc.options.client_certificate; + expect(client_cert).to.not.be.null; + expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); + + expect(network.validate(client)).to.be.true; + + expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged + + ClientCertificate.remove(network.uuid); + Helper.config.public = true; + }); }); describe("#createIrcFramework(client)", function () { From ae7020f569367090eb18a6f8976759d31aed3165 Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Sat, 5 Mar 2022 11:20:57 +0100 Subject: [PATCH 6/6] Do not remove client certificate, even when TLS is disabled It does not really make sense to remove it, as it can lock someone out of their account, just by temporarily disabling TLS. --- src/models/network.js | 4 ---- test/models/network.js | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/models/network.js b/src/models/network.js index 822afdf2..bfd14c06 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -184,10 +184,6 @@ Network.prototype.validate = function (client) { this.rejectUnauthorized = true; } - if (!this.tls) { - ClientCertificate.remove(this.uuid); - } - return true; }; diff --git a/test/models/network.js b/test/models/network.js index 6abdb3e4..1b5a1ed1 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -203,7 +203,7 @@ describe("Network", function () { STSPolicies.update("irc.example.com", 7000, 0); // Cleanup }); - it("should remove client certs if TLS is disabled", function () { + it("should not remove client certs if TLS is disabled", function () { Helper.config.public = false; const client = {idMsg: 1, emit() {}, messageStorage: []}; @@ -216,15 +216,15 @@ describe("Network", function () { expect(client_cert).to.not.be.null; expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); - expect(network.validate(client)).to.be.true; // Deletes the cert + expect(network.validate(client)).to.be.true; - expect(ClientCertificate.get(network.uuid)).to.not.deep.equal(client_cert); // Because ClientCertificate.get regenerates it + expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged ClientCertificate.remove(network.uuid); Helper.config.public = true; }); - it("should remove client certs if there is a STS policy", function () { + it("should not remove client certs if there is a STS policy", function () { Helper.config.public = false; const client = {idMsg: 1, emit() {}, messageStorage: []};