| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177 |
- 'use strict'
- const { test } = require('tap')
- const eos = require('end-of-stream')
- const Faketimers = require('@sinonjs/fake-timers')
- const Client = require('../lib/client')
- const { setup, connect, noError, subscribe, subscribeMultiple } = require('./helper')
- const aedes = require('../')
- test('authenticate successfully a client with username and password', function (t) {
- t.plan(4)
- const s = noError(setup())
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- cb(null, true)
- }
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 0,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'successful connack')
- })
- })
- test('authenticate unsuccessfully a client with username and password', function (t) {
- t.plan(6)
- const s = setup()
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- cb(null, false)
- }
- s.broker.on('clientError', function (client, err) {
- t.equal(err.errorCode, 5)
- })
- s.broker.on('clientReady', function (client) {
- t.fail('client should not ready')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 5,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack, unauthorized')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('authenticate errors', function (t) {
- t.plan(7)
- const s = setup()
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- cb(new Error('this should happen!'))
- }
- s.broker.on('clientError', function (client, err) {
- t.equal(err.message, 'this should happen!')
- t.equal(err.errorCode, 5)
- })
- s.broker.on('clientReady', function (client) {
- t.fail('client should not ready')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 5,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack, unauthorized')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('authentication error when return code 1 (unacceptable protocol version) is passed', function (t) {
- t.plan(7)
- const s = setup()
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- const error = new Error('Auth error')
- error.returnCode = 1
- cb(error, null)
- }
- s.broker.on('clientError', function (client, err) {
- t.equal(err.message, 'Auth error')
- t.equal(err.errorCode, 5)
- })
- s.broker.on('clientReady', function (client) {
- t.fail('client should not ready')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 5,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack, unauthorized')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('authentication error when return code 2 (identifier rejected) is passed', function (t) {
- t.plan(7)
- const s = setup()
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- const error = new Error('Auth error')
- error.returnCode = 2
- cb(error, null)
- }
- s.broker.on('clientError', function (client, err) {
- t.equal(err.message, 'Auth error')
- t.equal(err.errorCode, 2)
- })
- s.broker.on('clientReady', function (client) {
- t.fail('client should not ready')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 2,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack, identifier rejected')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('authentication error when return code 3 (Server unavailable) is passed', function (t) {
- t.plan(7)
- const s = setup()
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- const error = new Error('Auth error')
- error.returnCode = 3
- cb(error, null)
- }
- s.broker.on('clientError', function (client, err) {
- t.equal(err.message, 'Auth error')
- t.equal(err.errorCode, 3)
- })
- s.broker.on('clientReady', function (client) {
- t.fail('client should not ready')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 3,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack, Server unavailable')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('authentication error when return code 4 (bad user or password) is passed', function (t) {
- t.plan(7)
- const s = setup()
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- const error = new Error('Auth error')
- error.returnCode = 4
- cb(error, null)
- }
- s.broker.on('clientError', function (client, err) {
- t.equal(err.message, 'Auth error')
- t.equal(err.errorCode, 4)
- })
- s.broker.on('clientReady', function (client) {
- t.fail('client should not ready')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 4,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack, bad username or password')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('authentication error when non numeric return code is passed', function (t) {
- t.plan(7)
- const s = setup()
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- const error = new Error('Non numeric error codes')
- error.returnCode = 'return Code'
- cb(error, null)
- }
- s.broker.on('clientError', function (client, err) {
- t.equal(err.message, 'Non numeric error codes')
- t.equal(err.errorCode, 5)
- })
- s.broker.on('clientReady', function (client) {
- t.fail('client should not ready')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 5,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack, unauthorized')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('authorize publish', function (t) {
- t.plan(4)
- const s = connect(setup(), { clientId: 'my-client-xyz' })
- t.teardown(s.broker.close.bind(s.broker))
- const expected = {
- cmd: 'publish',
- topic: 'hello',
- payload: Buffer.from('world'),
- qos: 0,
- retain: false,
- length: 12,
- dup: false
- }
- s.broker.authorizePublish = function (client, packet, cb) {
- t.ok(client, 'client exists')
- t.same(packet, expected, 'packet matches')
- cb()
- }
- s.broker.mq.on('hello', function (packet, cb) {
- t.notOk(Object.prototype.hasOwnProperty.call(packet, 'messageId'), 'should not contain messageId in QoS 0')
- expected.brokerId = s.broker.id
- expected.brokerCounter = s.broker.counter
- expected.clientId = 'my-client-xyz'
- delete expected.length
- t.same(packet, expected, 'packet matches')
- cb()
- })
- s.inStream.write({
- cmd: 'publish',
- topic: 'hello',
- payload: 'world'
- })
- })
- test('authorize waits for authenticate', function (t) {
- t.plan(6)
- const s = setup(aedes({ clientId: 'my-client-xyz-2' }))
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authenticate = function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- process.nextTick(function () {
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- client.authenticated = true
- cb(null, true)
- })
- }
- s.broker.authorizePublish = function (client, packet, cb) {
- t.ok(client.authenticated, 'client authenticated')
- cb()
- }
- const expected = {
- cmd: 'publish',
- topic: 'hello',
- payload: Buffer.from('world'),
- qos: 0,
- retain: false,
- length: 12,
- dup: false,
- clientId: 'my-client'
- }
- s.broker.mq.on('hello', function (packet, cb) {
- t.notOk(Object.prototype.hasOwnProperty.call(packet, 'messageId'), 'should not contain messageId in QoS 0')
- expected.brokerId = s.broker.id
- expected.brokerCounter = s.broker.counter
- delete expected.length
- t.same(packet, expected, 'packet matches')
- cb()
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- s.inStream.write({
- cmd: 'publish',
- topic: 'hello',
- payload: 'world'
- })
- })
- test('authorize publish from configOptions', function (t) {
- t.plan(4)
- const s = connect(setup(aedes({
- clientId: 'my-client-xyz-3',
- authorizePublish: function (client, packet, cb) {
- t.ok(client, 'client exists')
- t.same(packet, expected, 'packet matches')
- cb()
- }
- })), { clientId: 'my-client-xyz-3' })
- t.teardown(s.broker.close.bind(s.broker))
- const expected = {
- cmd: 'publish',
- topic: 'hello',
- payload: Buffer.from('world'),
- qos: 0,
- retain: false,
- length: 12,
- dup: false
- }
- s.broker.mq.on('hello', function (packet, cb) {
- t.notOk(Object.prototype.hasOwnProperty.call(packet, 'messageId'), 'should not contain messageId in QoS 0')
- expected.brokerId = s.broker.id
- expected.brokerCounter = s.broker.counter
- expected.clientId = 'my-client-xyz-3'
- delete expected.length
- t.same(packet, expected, 'packet matches')
- cb()
- })
- s.inStream.write({
- cmd: 'publish',
- topic: 'hello',
- payload: 'world'
- })
- })
- test('do not authorize publish', function (t) {
- t.plan(3)
- const s = connect(setup())
- t.teardown(s.broker.close.bind(s.broker))
- const expected = {
- cmd: 'publish',
- topic: 'hello',
- payload: Buffer.from('world'),
- qos: 0,
- retain: false,
- length: 12,
- dup: false
- }
- s.broker.authorizePublish = function (client, packet, cb) {
- t.ok(client, 'client exists')
- t.same(packet, expected, 'packet matches')
- cb(new Error('auth negated'))
- }
- eos(s.conn, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'publish',
- topic: 'hello',
- payload: 'world'
- })
- })
- test('modify qos out of range in authorize publish ', function (t) {
- t.plan(2)
- const s = connect(setup(), { clientId: 'my-client-xyz-4' })
- t.teardown(s.broker.close.bind(s.broker))
- const expected = {
- cmd: 'publish',
- topic: 'foo',
- payload: Buffer.from('bar'),
- qos: 0,
- retain: false,
- length: 12,
- dup: false,
- clientId: 'my-client-xyz-4'
- }
- s.broker.authorizePublish = function (client, packet, cb) {
- if (packet.topic === 'hello') { packet.qos = 10 }
- cb()
- }
- s.outStream.on('data', function (packet) {
- t.fail('should no data sent')
- })
- s.broker.mq.on('hello', function (packet, cb) {
- t.fail('should not publish')
- })
- s.broker.mq.on('foo', function (packet, cb) {
- t.notOk(Object.prototype.hasOwnProperty.call(packet, 'messageId'), 'should not contain messageId in QoS 0')
- expected.brokerId = s.broker.id
- expected.brokerCounter = s.broker.counter
- delete expected.length
- t.same(packet, expected, 'packet matches')
- cb()
- })
- s.inStream.write({
- cmd: 'publish',
- topic: 'hello',
- payload: 'world'
- })
- s.inStream.write({
- cmd: 'publish',
- topic: 'foo',
- payload: 'bar'
- })
- })
- test('authorize subscribe', function (t) {
- t.plan(5)
- const s = connect(setup())
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authorizeSubscribe = function (client, sub, cb) {
- t.ok(client, 'client exists')
- t.same(sub, {
- topic: 'hello',
- qos: 0
- }, 'topic matches')
- cb(null, sub)
- }
- subscribe(t, s, 'hello', 0)
- })
- test('authorize subscribe multiple same topics with same qos', function (t) {
- t.plan(4)
- const s = connect(setup())
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authorizeSubscribe = function (client, sub, cb) {
- t.same(sub, {
- topic: 'hello',
- qos: 0
- }, 'topic matches')
- cb(null, sub)
- }
- subscribeMultiple(t, s, [{ topic: 'hello', qos: 0 }, { topic: 'hello', qos: 0 }], [0])
- })
- test('authorize subscribe multiple same topics with different qos', function (t) {
- t.plan(4)
- const s = connect(setup())
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authorizeSubscribe = function (client, sub, cb) {
- t.same(sub, {
- topic: 'hello',
- qos: 1
- }, 'topic matches')
- cb(null, sub)
- }
- subscribeMultiple(t, s, [{ topic: 'hello', qos: 0 }, { topic: 'hello', qos: 1 }], [1])
- })
- test('authorize subscribe multiple different topics', function (t) {
- t.plan(7)
- const s = connect(setup())
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authorizeSubscribe = function (client, sub, cb) {
- t.ok(client, 'client exists')
- if (sub.topic === 'hello') {
- t.same(sub, {
- topic: 'hello',
- qos: 0
- }, 'topic matches')
- } else if (sub.topic === 'foo') {
- t.same(sub, {
- topic: 'foo',
- qos: 0
- }, 'topic matches')
- }
- cb(null, sub)
- }
- subscribeMultiple(t, s, [{ topic: 'hello', qos: 0 }, { topic: 'foo', qos: 0 }], [0, 0])
- })
- test('authorize subscribe from config options', function (t) {
- t.plan(5)
- const s = connect(setup(aedes({
- authorizeSubscribe: function (client, sub, cb) {
- t.ok(client, 'client exists')
- t.same(sub, {
- topic: 'hello',
- qos: 0
- }, 'topic matches')
- cb(null, sub)
- }
- })))
- t.teardown(s.broker.close.bind(s.broker))
- subscribe(t, s, 'hello', 0)
- })
- test('negate subscription', function (t) {
- t.plan(5)
- const s = connect(setup())
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authorizeSubscribe = function (client, sub, cb) {
- t.ok(client, 'client exists')
- t.same(sub, {
- topic: 'hello',
- qos: 0
- }, 'topic matches')
- cb(null, null)
- }
- s.inStream.write({
- cmd: 'subscribe',
- messageId: 24,
- subscriptions: [{
- topic: 'hello',
- qos: 0
- }]
- })
- s.outStream.once('data', function (packet) {
- t.equal(packet.cmd, 'suback')
- t.same(packet.granted, [128])
- t.equal(packet.messageId, 24)
- })
- })
- test('negate multiple subscriptions', function (t) {
- t.plan(6)
- const s = connect(setup())
- t.teardown(s.broker.close.bind(s.broker))
- s.broker.authorizeSubscribe = function (client, sub, cb) {
- t.ok(client, 'client exists')
- cb(null, null)
- }
- const expectedSubs = [{
- topic: 'hello',
- qos: 128
- }, {
- topic: 'world',
- qos: 128
- }]
- s.broker.once('subscribe', function (subs, client) {
- t.same(subs, expectedSubs)
- })
- s.inStream.write({
- cmd: 'subscribe',
- messageId: 24,
- subscriptions: [{
- topic: 'hello',
- qos: 0
- }, {
- topic: 'world',
- qos: 0
- }]
- })
- s.outStream.once('data', function (packet) {
- t.equal(packet.cmd, 'suback')
- t.same(packet.granted, [128, 128])
- t.equal(packet.messageId, 24)
- })
- })
- test('negate subscription with correct persistence', function (t) {
- t.plan(6)
- // rh, rap, nl are undefined because mqtt.parser is set to MQTT 3.1.1 and will thus erase these props from s.inStream.write
- const expected = [{
- topic: 'hello',
- qos: 0,
- rh: undefined,
- rap: undefined,
- nl: undefined
- }, {
- topic: 'world',
- qos: 0,
- rh: undefined,
- rap: undefined,
- nl: undefined
- }]
- const broker = aedes()
- t.teardown(broker.close.bind(broker))
- broker.authorizeSubscribe = function (client, sub, cb) {
- t.ok(client, 'client exists')
- if (sub.topic === 'hello') {
- sub = null
- }
- cb(null, sub)
- }
- const s = connect(setup(broker), { clean: false, clientId: 'abcde' })
- s.outStream.once('data', function (packet) {
- t.equal(packet.cmd, 'suback')
- t.same(packet.granted, [128, 0])
- broker.persistence.subscriptionsByClient(broker.clients.abcde, function (_, subs, client) {
- t.same(subs, expected)
- })
- t.equal(packet.messageId, 24)
- })
- s.inStream.write({
- cmd: 'subscribe',
- messageId: 24,
- subscriptions: [{
- topic: 'hello',
- qos: 0,
- rh: 0,
- rap: true,
- nl: false
- }, {
- topic: 'world',
- qos: 0,
- rh: 0,
- rap: true,
- nl: false
- }]
- })
- })
- test('negate multiple subscriptions random times', function (t) {
- t.plan(5)
- const clock = Faketimers.createClock()
- const s = connect(setup())
- t.teardown(function () {
- clock.reset()
- s.broker.close()
- })
- s.broker.authorizeSubscribe = function (client, sub, cb) {
- t.ok(client, 'client exists')
- if (sub.topic === 'hello') {
- clock.setTimeout(function () {
- cb(null, sub)
- }, 100)
- } else {
- cb(null, null)
- clock.tick(100)
- }
- }
- s.inStream.write({
- cmd: 'subscribe',
- messageId: 24,
- subscriptions: [{
- topic: 'hello',
- qos: 0
- }, {
- topic: 'world',
- qos: 0
- }]
- })
- s.outStream.once('data', function (packet) {
- t.equal(packet.cmd, 'suback')
- t.same(packet.granted, [0, 128])
- t.equal(packet.messageId, 24)
- })
- })
- test('failed authentication does not disconnect other client with same clientId', function (t) {
- t.plan(3)
- const broker = aedes()
- t.teardown(broker.close.bind(broker))
- const s = setup(broker)
- const s0 = setup(broker)
- broker.authenticate = function (client, username, password, cb) {
- cb(null, password.toString() === 'right')
- }
- s0.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'right',
- keepalive: 0
- })
- s0.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 0,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'successful connack')
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'wrong',
- keepalive: 0
- })
- })
- const removeEos = eos(s0.outStream, function () {
- t.fail('ended before time')
- })
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 5,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack')
- })
- eos(s.outStream, function () {
- t.pass('ended')
- removeEos()
- })
- })
- test('unauthorized connection should not unregister the correct one with same clientId', function (t) {
- t.plan(4)
- const broker = aedes({
- authenticate: function (client, username, password, callback) {
- if (username === 'correct') {
- callback(null, true)
- } else {
- const error = new Error()
- error.returnCode = 4
- callback(error, false)
- }
- }
- })
- t.teardown(broker.close.bind(broker))
- broker.on('clientError', function (client, err) {
- t.equal(err.message, 'bad user name or password')
- t.equal(err.errorCode, 4)
- t.equal(broker.connectedClients, 1, 'my-client still connected')
- })
- connect(setup(broker), {
- clientId: 'my-client',
- username: 'correct'
- }, function () {
- t.equal(broker.connectedClients, 1, 'my-client connected')
- connect(setup(broker), {
- clientId: 'my-client',
- username: 'unauthorized'
- }, function () {
- // other unauthorized connection with the same clientId should not unregister the correct one.
- t.fail('unauthorized should not connect')
- })
- })
- })
- test('set authentication method in config options', function (t) {
- t.plan(5)
- const s = setup(aedes({
- authenticate: function (client, username, password, cb) {
- t.type(client, Client, 'client is there')
- t.equal(username, 'my username', 'username is there')
- t.same(password, Buffer.from('my pass'), 'password is there')
- cb(null, false)
- }
- }))
- t.teardown(s.broker.close.bind(s.broker))
- s.outStream.on('data', function (packet) {
- t.same(packet, {
- cmd: 'connack',
- returnCode: 5,
- length: 2,
- qos: 0,
- retain: false,
- dup: false,
- topic: null,
- payload: null,
- sessionPresent: false
- }, 'unsuccessful connack')
- })
- eos(s.outStream, function () {
- t.equal(s.broker.connectedClients, 0, 'no connected clients')
- })
- s.inStream.write({
- cmd: 'connect',
- protocolId: 'MQTT',
- protocolVersion: 4,
- clean: true,
- clientId: 'my-client',
- username: 'my username',
- password: 'my pass',
- keepalive: 0
- })
- })
- test('change a topic name inside authorizeForward method in QoS 1 mode', function (t) {
- t.plan(3)
- const broker = aedes({
- authorizeForward: function (client, packet) {
- packet.payload = Buffer.from('another-world')
- packet.messageId = 2
- return packet
- }
- })
- t.teardown(broker.close.bind(broker))
- const expected = {
- cmd: 'publish',
- topic: 'hello',
- payload: Buffer.from('another-world'),
- dup: false,
- length: 22,
- qos: 1,
- retain: false,
- messageId: 2
- }
- broker.on('client', function (client) {
- client.subscribe({
- topic: 'hello',
- qos: 1
- }, function (err) {
- t.error(err, 'no error')
- broker.publish({
- topic: 'hello',
- payload: Buffer.from('world'),
- qos: 1
- }, function (err) {
- t.error(err, 'no error')
- })
- })
- })
- const s = connect(setup(broker))
- s.outStream.once('data', function (packet) {
- t.same(packet, expected, 'packet matches')
- })
- })
- ;[true, false].forEach(function (cleanSession) {
- test(`unauthorized forward publish in QoS 1 mode [clean=${cleanSession}]`, function (t) {
- t.plan(2)
- const broker = aedes({
- authorizeForward: function (client, packet) {
- return null
- }
- })
- t.teardown(broker.close.bind(broker))
- broker.on('client', function (client) {
- client.subscribe({
- topic: 'hello',
- qos: 1
- }, function (err) {
- t.error(err, 'no error')
- broker.publish({
- topic: 'hello',
- payload: Buffer.from('world'),
- qos: 1
- }, function (err) {
- t.error(err, 'no error')
- })
- })
- })
- const s = connect(setup(broker), { clean: cleanSession })
- s.outStream.once('data', function (packet) {
- t.fail('Should have not recieved this packet')
- })
- })
- })
- test('prevent publish in QoS 0 mode', function (t) {
- t.plan(2)
- const broker = aedes({
- authorizeForward: function (client, packet) {
- return null
- }
- })
- t.teardown(broker.close.bind(broker))
- broker.on('client', function (client) {
- client.subscribe({
- topic: 'hello',
- qos: 0
- }, function (err) {
- t.error(err, 'no error')
- broker.publish({
- topic: 'hello',
- payload: Buffer.from('world'),
- qos: 0
- }, function (err) {
- t.error(err, 'no error')
- })
- })
- })
- const s = connect(setup(broker))
- s.outStream.once('data', function (packet) {
- t.fail('Should have not recieved this packet')
- })
- })
|