Skip to content

Commit

Permalink
Add password and channel checks to TCP endpoint.
Browse files Browse the repository at this point in the history
Fixes #93. Thanks to @pdbjjens for the suggestion.
  • Loading branch information
danielwippermann committed Oct 11, 2023
1 parent 333eb6f commit f9901cd
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 6 deletions.
31 changes: 25 additions & 6 deletions src/tcp-connection-endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class TcpConnectionEndpoint extends EventEmitter {
* @augments EventEmitter
* @param {object} options The initialization values for this instance.
* @param {number} options.port See {@link TcpConnectionEndpoint#port}
* @param {number} options.channels See {@link TcpConnectionEndpoint#channels}
* @param {string} options.password See {@link TcpConnectionEndpoint#password}
* @param {string[]} options.channels See {@link TcpConnectionEndpoint#channels}
*
* @classdesc
* The TcpConnectionEndpoint can act as the remote side for a TcpConnection.
Expand All @@ -38,6 +39,12 @@ class TcpConnectionEndpoint extends EventEmitter {
*/
port: 7053,

/**
* The password to check against if the PASS command is received.
* @type {string}
*/
password: null,

/**
* The list of channels to return if the CHANNELLIST command is received.
* @type {string[]}
Expand Down Expand Up @@ -147,8 +154,13 @@ class TcpConnectionEndpoint extends EventEmitter {
connectionInfo.viaTag = md [1];
callback(null, '+OK');
} else if ((md = /^PASS (.*)$/.exec(line))) {
connectionInfo.password = md [1];
callback(null, '+OK');
const passwordString = md [1];
if (!_this.password || (passwordString === _this.password)) {
connectionInfo.password = passwordString;
callback(null, '+OK');
} else {
callback(new Error('Password mismatch'));
}
} else if ((md = /^CHANNELLIST$/.exec(line))) {
const response = _this.channels.reduce((memo, channel, index) => {
if (channel) {
Expand All @@ -158,9 +170,16 @@ class TcpConnectionEndpoint extends EventEmitter {
}, []).join('\r\n');

callback(null, response + '\r\n+OK');
} else if ((md = /^CHANNEL (.*)$/.exec(line))) {
connectionInfo.channel = md [1];
callback(null, '+OK');
} else if ((md = /^CHANNEL (\d+)$/.exec(line))) {
const channelString = md [1];
const index = +channelString;
const channel = _this.channels [index];
if (channel) {
connectionInfo.channel = channelString;
callback(null, '+OK');
} else {
callback(new Error('Channel not available'));
}
} else if ((md = /^QUIT$/.exec(line))) {
callback(null, '+OK', false);
} else if ((md = /^DATA$/.exec(line))) {
Expand Down
116 changes: 116 additions & 0 deletions test/specs/tcp-connection-endpoint.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('TcpConnectionEndpoint', () => {

expectOwnPropertyNamesToEqual(ep, [
'port',
'password',
'channels',

// base class related
Expand All @@ -49,19 +50,22 @@ describe('TcpConnectionEndpoint', () => {
]);

expect(ep.port).toBe(7053);
expect(ep.password).toBe(null);
expect(ep.channels).toEqual([ 'VBus' ]);
});

it('should copy selected options', () => {
const options = {
port: 12345,
password: 'vbus',
channels: [ 'VBus 1', 'VBus 2' ],
junk: 'DO NOT COPY ME',
};

const ep = new TcpConnectionEndpoint(options);

expect(ep.port).toBe(options.port);
expect(ep.password).toBe(options.password);
expect(ep.channels).toBe(options.channels);
expect(ep.junk).toBe(undefined);
});
Expand Down Expand Up @@ -287,6 +291,118 @@ describe('TcpConnectionEndpoint', () => {
});
});

it('should work correctly for matching passwords', async () => {
let ep, conn;
try {
ep = new TcpConnectionEndpoint({
port: 0,
password: 's3cr3t',
});

await ep.start();

conn = new TcpConnection({
host: '127.0.0.1',
port: ep.port,
password: 's3cr3t',
});

await conn.connect();
} finally {
if (conn) {
conn.disconnect();
}

if (ep) {
ep.stop();
}
}
});

it('should work correctly for mismatching passwords', async () => {
let ep, conn;
try {
ep = new TcpConnectionEndpoint({
port: 0,
password: 's3cr3t',
});

await ep.start();

conn = new TcpConnection({
host: '127.0.0.1',
port: ep.port,
password: 'h8xx0r',
});

await expect(() => conn.connect()).rejects.toThrow('Remote side responded with "-ERROR: \\"Error: Password mismatch\\""');
} finally {
if (conn) {
conn.disconnect();
}

if (ep) {
ep.stop();
}
}
});

it('should work correctly for matching channels', async () => {
let ep, conn;
try {
ep = new TcpConnectionEndpoint({
port: 0,
channels: [ null, 'VBus 1', 'VBus 2' ],
});

await ep.start();

conn = new TcpConnection({
host: '127.0.0.1',
port: ep.port,
channel: 2,
});

await conn.connect();
} finally {
if (conn) {
conn.disconnect();
}

if (ep) {
ep.stop();
}
}
});

it('should work correctly for mismatching channels', async () => {
let ep, conn;
try {
ep = new TcpConnectionEndpoint({
port: 0,
channels: [ null, 'VBus 1', 'VBus 2' ],
});

await ep.start();

conn = new TcpConnection({
host: '127.0.0.1',
port: ep.port,
channel: 3,
});

await expect(() => conn.connect()).rejects.toThrow('Remote side responded with "-ERROR: \\"Error: Channel not available\\""');
} finally {
if (conn) {
conn.disconnect();
}

if (ep) {
ep.stop();
}
}
});

});

});

0 comments on commit f9901cd

Please sign in to comment.