/test/message/connection-endpointSpec.js

https://gitlab.com/jasonparser/deepstream.io · JavaScript · 273 lines · 248 code · 25 blank · 0 comment · 0 complexity · 24c02a09f94da0ab0cea1d8b4ca148b6 MD5 · raw file

  1. var proxyquire = require( 'proxyquire' ).noCallThru(),
  2. engineIoMock = require( '../mocks/engine-io-mock' ),
  3. HttpMock = require( '../mocks/http-mock' ),
  4. httpMock = new HttpMock(),
  5. httpsMock = new HttpMock(),
  6. ConnectionEndpoint = proxyquire( '../../src/message/connection-endpoint', { 'engine.io': engineIoMock, 'http': httpMock, 'https': httpsMock } ),
  7. _msg = require( '../test-helper/test-helper' ).msg,
  8. permissionHandlerMock = require( '../mocks/permission-handler-mock' ),
  9. lastAuthenticatedMessage = null,
  10. lastLoggedMessage = null,
  11. socketMock,
  12. options,
  13. connectionEndpoint;
  14. options = {
  15. permissionHandler: require( '../mocks/permission-handler-mock' ),
  16. logger: { log: function( logLevel, event, msg ){ lastLoggedMessage = msg; } },
  17. maxAuthAttempts: 3,
  18. logInvalidAuthData: true
  19. };
  20. connectionEndpoint = new ConnectionEndpoint( options );
  21. connectionEndpoint.onMessage = function( socket, message ){
  22. lastAuthenticatedMessage = message;
  23. };
  24. describe( 'connection endpoint', function() {
  25. describe( 'validates HTTPS server conditions', function() {
  26. var options = null;
  27. var error = null;
  28. var connectionEndpointValidation = null;
  29. beforeEach(function() {
  30. sslOptions = {
  31. permissionHandler: require( '../mocks/permission-handler-mock' ),
  32. logger: { log: function( logLevel, event, msg ){} }
  33. };
  34. spyOn(httpMock, 'createServer').andCallThrough();
  35. spyOn(httpsMock, 'createServer').andCallThrough();
  36. });
  37. it( 'creates a http connection when sslKey and sslCert are not provided', function(){
  38. connectionEndpointValidation = new ConnectionEndpoint( sslOptions );
  39. expect(httpMock.createServer).toHaveBeenCalledWith();
  40. expect(httpsMock.createServer).not.toHaveBeenCalled();
  41. });
  42. it( 'creates a https connection when sslKey and sslCert are provided', function(){
  43. sslOptions.sslKey = 'sslPrivateKey';
  44. sslOptions.sslCert = 'sslCertificate';
  45. connectionEndpointValidation = new ConnectionEndpoint( sslOptions );
  46. expect(httpMock.createServer).not.toHaveBeenCalled();
  47. expect(httpsMock.createServer).toHaveBeenCalledWith( { "key": "sslPrivateKey", "cert": "sslCertificate"} );
  48. });
  49. it( 'creates a https connection when sslKey, sslCert and sslCa are provided', function(){
  50. sslOptions.sslKey = 'sslPrivateKey';
  51. sslOptions.sslCert = 'sslCertificate';
  52. sslOptions.sslCa = 'sslCertificateAuthority';
  53. connectionEndpointValidation = new ConnectionEndpoint( sslOptions );
  54. expect(httpMock.createServer).not.toHaveBeenCalled();
  55. expect(httpsMock.createServer).toHaveBeenCalledWith( { "key": "sslPrivateKey", "cert": "sslCertificate", "ca": "sslCertificateAuthority"} );
  56. });
  57. it( 'throws an exception when only sslCert is provided', function(){
  58. try {
  59. sslOptions.sslCert = 'sslCertificate';
  60. connectionEndpointValidation = new ConnectionEndpoint( sslOptions );
  61. } catch( e ) {
  62. error = e;
  63. } finally {
  64. expect( error.message ).toBe( 'Must also include sslKey in order to use HTTPS' );
  65. }
  66. });
  67. it( 'throws an exception when only sslKey is provided', function(){
  68. try {
  69. sslOptions.sslKey = "sslPrivateKey";
  70. connectionEndpointValidation = new ConnectionEndpoint( sslOptions );
  71. } catch( e ) {
  72. error = e;
  73. } finally {
  74. expect( error.message ).toBe( 'Must also include sslCert in order to use HTTPS' );
  75. }
  76. });
  77. it( 'throws an exception when sslCert and sslCa is provided', function(){
  78. try {
  79. sslOptions.sslCert = 'sslCertificate';
  80. sslOptions.sslCa = 'sslCertificateAuthority';
  81. connectionEndpointValidation = new ConnectionEndpoint( sslOptions );
  82. } catch( e ) {
  83. error = e;
  84. } finally {
  85. expect( error.message ).toBe( 'Must also include sslKey in order to use HTTPS' );
  86. }
  87. });
  88. it( 'throws an exception when sslKey and sslCa is provided', function(){
  89. try {
  90. sslOptions.sslKey = "sslPrivateKey";
  91. sslOptions.sslCa = 'sslCertificateAuthority';
  92. connectionEndpointValidation = new ConnectionEndpoint( sslOptions );
  93. } catch( e ) {
  94. error = e;
  95. } finally {
  96. expect( error.message ).toBe( 'Must also include sslCert in order to use HTTPS' );
  97. }
  98. });
  99. });
  100. describe( 'the connection endpoint handles invalid auth messages', function(){
  101. it( 'creates the connection endpoint', function(){
  102. socketMock = engineIoMock.simulateConnection();
  103. });
  104. it( 'handles invalid auth messages', function(){
  105. expect( socketMock.lastSendMessage ).toBe( null );
  106. expect( socketMock.isDisconnected ).toBe( false );
  107. socketMock.emit( 'message', 'gibberish' );
  108. expect( socketMock.lastSendMessage ).toBe( _msg( 'A|E|INVALID_AUTH_MSG|invalid authentication message+' ) );
  109. expect( socketMock.isDisconnected ).toBe( true );
  110. });
  111. it( 'has discarded the invalid socket', function(){
  112. socketMock.lastSendMessage = null;
  113. socketMock.emit( 'message', 'some more gibberish' );
  114. expect( socketMock.lastSendMessage ).toBe( null );
  115. });
  116. });
  117. describe( 'the connection endpoint handles invalid json', function(){
  118. it( 'creates the connection endpoint', function(){
  119. socketMock = engineIoMock.simulateConnection();
  120. });
  121. it( 'handles invalid json messages', function(){
  122. expect( socketMock.lastSendMessage ).toBe( null );
  123. expect( socketMock.isDisconnected ).toBe( false );
  124. socketMock.emit( 'message', _msg( 'A|REQ|{"a":"b}+' ) );
  125. expect( socketMock.lastSendMessage ).toBe( _msg( 'A|E|INVALID_AUTH_MSG|invalid authentication message+' ) );
  126. expect( socketMock.isDisconnected ).toBe( true );
  127. });
  128. });
  129. describe( 'the connection endpoint routes valid auth messages to the permissionHandler', function(){
  130. it( 'creates the connection endpoint', function(){
  131. socketMock = engineIoMock.simulateConnection();
  132. });
  133. it( 'handles valid auth messages', function(){
  134. expect( socketMock.lastSendMessage ).toBe( null );
  135. expect( socketMock.isDisconnected ).toBe( false );
  136. expect( permissionHandlerMock.lastUserValidationQueryArgs ).toBe( null );
  137. permissionHandlerMock.nextUserValidationResult = false;
  138. socketMock.emit( 'message', _msg( 'A|REQ|{"user":"wolfram"}+' ) );
  139. expect( permissionHandlerMock.lastUserValidationQueryArgs.length ).toBe( 3 );
  140. expect( permissionHandlerMock.lastUserValidationQueryArgs[ 1 ].user ).toBe( 'wolfram' );
  141. expect( lastLoggedMessage.indexOf( 'wolfram' ) ).not.toBe( -1 );
  142. expect( socketMock.lastSendMessage ).toBe( _msg('A|E|INVALID_AUTH_DATA|Invalid User+') );
  143. expect( socketMock.isDisconnected ).toBe( false );
  144. });
  145. });
  146. describe( 'disconnects if the number of invalid authentication attempts is exceeded', function(){
  147. it( 'creates the connection endpoint', function(){
  148. socketMock = engineIoMock.simulateConnection();
  149. });
  150. it( 'handles valid auth messages', function(){
  151. permissionHandlerMock.nextUserValidationResult = false;
  152. options.maxAuthAttempts = 3;
  153. socketMock.emit( 'message', _msg( 'A|REQ|{"user":"wolfram"}+' ) );
  154. expect( socketMock.lastSendMessage ).toBe( _msg( 'A|E|INVALID_AUTH_DATA|Invalid User+' ) );
  155. expect( socketMock.isDisconnected ).toBe( false );
  156. socketMock.emit( 'message', _msg( 'A|REQ|{"user":"wolfram"}+' ) );
  157. expect( socketMock.lastSendMessage ).toBe( _msg( 'A|E|INVALID_AUTH_DATA|Invalid User+' ) );
  158. expect( socketMock.isDisconnected ).toBe( false );
  159. socketMock.emit( 'message', _msg( 'A|REQ|{"user":"wolfram"}+' ) );
  160. expect( socketMock.lastSendMessage ).toBe( _msg( 'A|E|TOO_MANY_AUTH_ATTEMPTS|too many authentication attempts+' ) );
  161. expect( socketMock.isDisconnected ).toBe( true );
  162. });
  163. });
  164. describe( 'doesn\'t log credentials if logInvalidAuthData is set to false', function(){
  165. it( 'creates the connection endpoint', function(){
  166. options.logInvalidAuthData = false;
  167. socketMock = engineIoMock.simulateConnection();
  168. });
  169. it( 'handles valid auth messages', function(){
  170. permissionHandlerMock.nextUserValidationResult = false;
  171. socketMock.emit( 'message', _msg( 'A|REQ|{"user":"wolfram"}+' ) );
  172. expect( lastLoggedMessage.indexOf( 'wolfram' ) ).toBe( -1 );
  173. });
  174. });
  175. describe( 'the connection endpoint routes valid auth messages to the permissionHandler', function(){
  176. it( 'creates the connection endpoint', function(){
  177. socketMock = engineIoMock.simulateConnection();
  178. });
  179. it( 'authenticates valid sockets', function(){
  180. expect( socketMock.lastSendMessage ).toBe( null );
  181. expect( socketMock.isDisconnected ).toBe( false );
  182. permissionHandlerMock.nextUserValidationResult = true;
  183. socketMock.emit( 'message', _msg( 'A|REQ|{"user":"wolfram"}+' ) );
  184. expect( socketMock.lastSendMessage ).toBe( _msg( 'A|A+' ) );
  185. expect( socketMock.isDisconnected ).toBe( false );
  186. });
  187. it( 'forwards messages from authenticated sockets', function(){
  188. expect( lastAuthenticatedMessage ).toBe( null );
  189. socketMock.emit( 'message', 'testMsg' );
  190. expect( lastAuthenticatedMessage ).toBe( 'testMsg' );
  191. });
  192. it( 'notifies the permissionHandler when a client disconnects', function(){
  193. expect( permissionHandlerMock.onClientDisconnectCalledWith ).toBe( null );
  194. socketMock.close();
  195. expect( permissionHandlerMock.onClientDisconnectCalledWith ).toBe( 'test-user' );
  196. });
  197. });
  198. describe( 'closes all client connections on close', function(){
  199. it( 'calls close on connections', function( done ) {
  200. var closeSpy = jasmine.createSpy( 'close-event' );
  201. connectionEndpoint.on( 'close', closeSpy );
  202. connectionEndpoint.close();
  203. setTimeout( function() {
  204. expect( closeSpy ).toHaveBeenCalled();
  205. done();
  206. }, 0 );
  207. } );
  208. it( 'does not allow future connections', function() {
  209. socketMock = engineIoMock.simulateConnection();
  210. expect( socketMock.lastSendMessage ).toBe( null );
  211. expect( socketMock.isDisconnected ).toBe( false );
  212. socketMock.emit( 'message', 'gibberish' );
  213. expect( socketMock.lastSendMessage ).toBe( null );
  214. expect( socketMock.isDisconnected ).toBe( false );
  215. } );
  216. });
  217. });