From 5c095c23ad66af14d2206c0ccca597426212aee5 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Thu, 19 Dec 2019 23:20:39 -0500 Subject: [PATCH 1/8] generlize everything --- rc | 2 +- src/channelManager.ts | 20 +++++++++---------- src/channels/channelBase.ts | 6 +++--- .../types/customChannel.ts} | 0 .../privateChannel.ts} | 12 +++++------ .../types/publicChannel.ts} | 0 src/clientManager.ts | 10 +++++----- src/clients/clientBase.ts | 6 +++--- src/clients/servicesClient.ts | 17 ---------------- src/clients/types/customClient.ts | 0 src/clients/types/privateClient.ts | 17 ++++++++++++++++ src/clients/types/publicClient.ts | 0 src/messageManager.ts | 2 +- src/server.ts | 6 +++--- src/test/channelManager.spec.ts | 8 ++++---- src/test/clientManager.spec.ts | 6 +++--- 16 files changed, 56 insertions(+), 56 deletions(-) rename src/{test/mhsChannel.spec.ts => channels/types/customChannel.ts} (100%) rename src/channels/{services/studentProgressMonitorChannel.ts => types/privateChannel.ts} (55%) rename src/{test/mhsClient.spec.ts => channels/types/publicChannel.ts} (100%) delete mode 100644 src/clients/servicesClient.ts create mode 100644 src/clients/types/customClient.ts create mode 100644 src/clients/types/privateClient.ts create mode 100644 src/clients/types/publicClient.ts diff --git a/rc b/rc index 6917759..f4b4dd2 100755 --- a/rc +++ b/rc @@ -6,4 +6,4 @@ if [ -f .user_rc ]; then . .user_rc fi -. .vagrantrc +source .vagrantrc diff --git a/src/channelManager.ts b/src/channelManager.ts index 55c4ca2..d1d6a08 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -1,7 +1,7 @@ import ChannelBase from './channels/channelBase'; import ClientBase from './clients/clientBase'; -import ServicesClient from './clients/servicesClient'; -import StudentProgressMonitorChannel from './channels/services/studentProgressMonitorChannel'; +import PrivateClient from './clients/types/privateClient'; +import PrivateChannel from './channels/types/privateChannel'; var logger = require('./logger'); @@ -15,8 +15,8 @@ class ChannelManager { } createChannel(data: any) { - var channelExists: ChannelBase|StudentProgressMonitorChannel|null = this.channelExists(data.channel); - var channel: ChannelBase|StudentProgressMonitorChannel; + var channelExists: ChannelBase|PrivateChannel|null = this.channelExists(data.channel); + var channel: ChannelBase|PrivateChannel; if (channelExists) { channel = channelExists; @@ -40,8 +40,8 @@ class ChannelManager { return null; } - addClientToChannel(client: ClientBase|ServicesClient, channel_id: string) { - var channel: ChannelBase|StudentProgressMonitorChannel|null = this.channelExists(channel_id); + addClientToChannel(client: ClientBase|PrivateClient, channel_id: string) { + var channel: ChannelBase|PrivateChannel|null = this.channelExists(channel_id); if (channel) { channel.addClient(client); @@ -54,11 +54,11 @@ class ChannelManager { } createByChannelType(data: any) { - var channel_name = data.client.charAt(0).toLowerCase() + data.client.slice(1) + 'Channel' + var channel_name = data.channel_type + 'Channel' try { - var Channel = require(`./channels/${data.client_type}/${channel_name}`); - logger.accessLog.info(`attempting to create channel of type ${data.client}, channel id: ${data.channel}...`); + var Channel = require(`./channels/types/${channel_name}`); + logger.accessLog.info(`attempting to create channel of type ${channel_name}, channel id: ${data.channel}...`); return new Channel(data.channel); } catch (e) { logger.errorLog.info(e); @@ -83,7 +83,7 @@ class ChannelManager { return false; } - changeChannel(client: ClientBase|ServicesClient, changeRequest: any) { + changeChannel(client: ClientBase|PrivateClient, changeRequest: any) { if (client.channel != null) { this.removeClientFromChannel(client.id, client.channel.id) } diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index 9e76470..68a2a7d 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -1,18 +1,18 @@ import ClientBase from '../clients/clientBase'; -import ServicesClient from '../clients/servicesClient'; +import PrivateClient from '../clients/types/privateClient'; var logger = require('../logger'); class ChannelBase { id: string; - clients: ServicesClient[]|ClientBase[] = []; + clients: PrivateClient[]|ClientBase[] = []; constructor(id: string) { this.id = id; logger.accessLog.info('Channel Created', {channelId: id}); } - addClient(client: ServicesClient|ClientBase) { + addClient(client: PrivateClient|ClientBase) { if (this.clientExists(client.id)) { logger.errorLog.info('Client already exits in channel', {channelId: this.id, clientId: client.id}); return {'status': 'notice', 'message': 'client aleady exists in channel'}; diff --git a/src/test/mhsChannel.spec.ts b/src/channels/types/customChannel.ts similarity index 100% rename from src/test/mhsChannel.spec.ts rename to src/channels/types/customChannel.ts diff --git a/src/channels/services/studentProgressMonitorChannel.ts b/src/channels/types/privateChannel.ts similarity index 55% rename from src/channels/services/studentProgressMonitorChannel.ts rename to src/channels/types/privateChannel.ts index d0744f4..20ba6d7 100644 --- a/src/channels/services/studentProgressMonitorChannel.ts +++ b/src/channels/types/privateChannel.ts @@ -1,17 +1,17 @@ -import ServicesClient from '../../clients/servicesClient'; +import PrivateClient from '../../clients/types/privateClient'; import ChannelBase from '../channelBase'; var logger = require('../../logger'); -class StudentProgressMonitorChannel extends ChannelBase { - broadcastMessage(from: ServicesClient, message: any) { +class PrivateChannel extends ChannelBase { + broadcastMessage(from: PrivateClient, message: any) { for (let client of this.clients) { if (client != from && client.data.user_type != from.data.user_type) { console.log('sending message: ' + JSON.stringify(message)) client.ws.send(JSON.stringify(message)); logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }}); } else { - logger.accessLog.info(`client either a ${client.data.user_type} or is the sender: ${client.id}`, { data: { message: message }}); + logger.accessLog.info(`client is either just a broadcaster or is the sender: ${client.id}`, { data: { message: message }}); } } @@ -19,5 +19,5 @@ class StudentProgressMonitorChannel extends ChannelBase { } }; -module.exports = StudentProgressMonitorChannel; -export default StudentProgressMonitorChannel; +module.exports = PrivateChannel; +export default PrivateChannel; diff --git a/src/test/mhsClient.spec.ts b/src/channels/types/publicChannel.ts similarity index 100% rename from src/test/mhsClient.spec.ts rename to src/channels/types/publicChannel.ts diff --git a/src/clientManager.ts b/src/clientManager.ts index cba1fed..7910d66 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -1,12 +1,12 @@ import * as WebSocket from 'ws'; import ClientBase from './clients/clientBase'; -import ServicesClient from './clients/servicesClient'; +import PrivateClient from './clients/types/privateClient'; import ChannelManager from './channelManager'; var logger = require('./logger'); class ClientManager { - clients: ClientBase[]|ServicesClient[] = []; + clients: ClientBase[]|PrivateClient[] = []; constructor() { //...maybe one day @@ -26,7 +26,7 @@ class ClientManager { } clientsOfType(client_type: string) { - var result: ClientBase[]|ServicesClient[] = []; + var result: ClientBase[]|PrivateClient[] = []; for (let client of this.clients) { if (client.type() == client_type) { @@ -68,9 +68,9 @@ class ClientManager { getClientType(data: any, channelManager: ChannelManager, ws: WebSocket) { var client_type = data.client_type + 'Client' - + try { - var Client = require(`./clients/${client_type}`); + var Client = require(`./clients/types/${client_type}`); logger.accessLog.info(`attempting to create client of type ${data.client_type}, client id: ${data.user_id}...`); return new Client(data, ws, channelManager, this); } catch (e) { diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index 1705090..0ea6c8f 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -2,7 +2,7 @@ import * as WebSocket from 'ws'; import ClientManager from '../clientManager'; import ChannelManager from '../channelManager'; import ChannelBase from '../channels/channelBase'; -import StudentProgressMonitorChannel from '../channels/services/studentProgressMonitorChannel'; +import Private from '../channels/types/privateChannel'; var messageManager = require('../messageManager'); var logger = require('../logger'); @@ -11,7 +11,7 @@ class ClientBase { ws: WebSocket; data: any; id: number; - channel: ChannelBase|StudentProgressMonitorChannel|null; + channel: ChannelBase|Private|null; clientManager: ClientManager; channelManager: ChannelManager; @@ -36,7 +36,7 @@ class ClientBase { return this.data.client; } - connectToChannel(channel: StudentProgressMonitorChannel) { + connectToChannel(channel: Private) { this.channel = channel; var messageListener = (message: any) => { diff --git a/src/clients/servicesClient.ts b/src/clients/servicesClient.ts deleted file mode 100644 index 5451df2..0000000 --- a/src/clients/servicesClient.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as WebSocket from 'ws'; -import ClientBase from './clientBase'; -import ClientManager from '../clientManager'; -import ChannelManager from '../channelManager'; - -var logger = require('../logger'); - -class ServicesClient extends ClientBase { - constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { - super(data, ws, channelManager, clientManager); - logger.accessLog.info('Service Client Created', {data: data}); - } -}; - -module.exports = ServicesClient; -export default ServicesClient; - diff --git a/src/clients/types/customClient.ts b/src/clients/types/customClient.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/clients/types/privateClient.ts b/src/clients/types/privateClient.ts new file mode 100644 index 0000000..a57b3f3 --- /dev/null +++ b/src/clients/types/privateClient.ts @@ -0,0 +1,17 @@ +import * as WebSocket from 'ws'; +import ClientBase from '../clientBase'; +import ClientManager from '../../clientManager'; +import ChannelManager from '../../channelManager'; + +var logger = require('../logger'); + +class PrivateClient extends ClientBase { + constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { + super(data, ws, channelManager, clientManager); + logger.accessLog.info('Private Client Created', {data: data}); + } +}; + +module.exports = PrivateClient; +export default PrivateClient; + diff --git a/src/clients/types/publicClient.ts b/src/clients/types/publicClient.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/messageManager.ts b/src/messageManager.ts index f70c0b5..ee6cc20 100644 --- a/src/messageManager.ts +++ b/src/messageManager.ts @@ -12,7 +12,7 @@ let schema = { time_left: Joi.any(), status: Joi.string(), channel: Joi.string(), - client: Joi.string(), + channel_type: Joi.string(), client_type: Joi.string(), user_id: Joi.number().integer(), user_exam_id: Joi.number().integer(), diff --git a/src/server.ts b/src/server.ts index 0221806..5037181 100755 --- a/src/server.ts +++ b/src/server.ts @@ -13,7 +13,7 @@ var logger = require('./logger'); import ClientManager from './clientManager'; import ChannelManager from './channelManager'; import ClientBase from './clients/clientBase'; -import ServicesClient from './clients/servicesClient'; +import PrivateClient from './clients/types/privateClient'; // if (app.environment == 'development') { @@ -55,13 +55,13 @@ function connectionManager() { } if (clientManager.clientExists(data.user_id)) { - var client: ClientBase|ServicesClient|null = clientManager.getClient(data.user_id); + var client: ClientBase|PrivateClient|null = clientManager.getClient(data.user_id); if (client != null) { client.replaceWebSocket(ws); } } else { - var client: ClientBase|ServicesClient|null = clientManager.addClient(data, channelManager, ws); + var client: ClientBase|PrivateClient|null = clientManager.addClient(data, channelManager, ws); } if (client != null) { diff --git a/src/test/channelManager.spec.ts b/src/test/channelManager.spec.ts index 5c45820..690442b 100644 --- a/src/test/channelManager.spec.ts +++ b/src/test/channelManager.spec.ts @@ -7,7 +7,7 @@ var expect = require('chai').expect; var sinon = require('sinon'); var name: string = 'test channel'; -var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } +var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': '["broadcaster"]', 'channel': name } var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); var clientManager = new ClientManager(); @@ -33,7 +33,7 @@ describe('ChannelManager', function () { expect(result).to.be.equal(true); }); - it('should not return a channel when seraching if channel exists', function () { + it('should not return a channel when searching if channel exists', function () { var exists = channelManager.channelExists('no channel'); var result = exists ? true : false; expect(result).to.be.equal(false); @@ -49,8 +49,8 @@ describe('ChannelManager', function () { expect(result.status).to.be.equal('notice'); }); - it('should create a channel of type StudentProgressMonitorChannel', function () { - var data2 = { 'channel': 'test channel 2', 'client': 'mhs', 'client_type':'site' }; + it('should create a channel of type PrivateChannel', function () { + var data2 = { 'channel': 'test channel 2', 'client_type': 'private', 'channel_type':'private' }; var result = channelManager.createByChannelType(data2); expect(result.id).to.be.equal('test channel 2'); }); diff --git a/src/test/clientManager.spec.ts b/src/test/clientManager.spec.ts index 962602c..6afd07d 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -51,9 +51,9 @@ describe('ClientManager', function () { expect(result).to.be.equal(false); }); - it('should add client of type ServicesClient', function () { - var data: any = { 'client': 'services', 'client_type':'StudentProgressMonitor', 'user_id': 125, 'user_type': 'user', 'channel': name } + it('should add client of type PrivateClient', function () { + var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': '["broadcaster", "receiver"]', 'channel': name } var result = clientManager.getClientType(data, channelManager, wsClient); - expect(result.clientType()).to.be.equal('services'); + expect(result.clientType()).to.be.equal('private'); }); }); From 60562c31391f0cba08ce543984419a2583490e0b Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Fri, 20 Dec 2019 16:52:14 -0500 Subject: [PATCH 2/8] generally generalized, version updated to 1.2 --- README.md | 8 ++++---- package-lock.json | 2 +- package.json | 2 +- src/channels/types/privateChannel.ts | 2 +- src/clients/clientBase.ts | 2 ++ src/clients/types/privateClient.ts | 4 +++- src/config/app.ts | 2 +- src/messageManager.ts | 11 +---------- 8 files changed, 14 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e38e7d2..3410a78 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# BRAID v1.0.8 +# BRAID v1.2 > Websocket server for the Measure platform [![Build Status](https://semaphoreci.com/api/v1/projects/7767f0f3-4da6-4c84-9167-4db5402a3262/2573412/badge.svg)](https://semaphoreci.com/yardstick/braid) @@ -47,10 +47,10 @@ braid: hmac_secret = "test" payload = { :data => { - :client => 'client name (mhs is only one implemented currently)', - :client_type => 'site (only one in use rite now)', + :channel_type => 'public/private/custom', + :client_type => 'public/private/custom', :user_id => (yardstick user id), - :user_type => '(teacher/user)', + :user_roles => and array with any combination of broadcaster/receiver/super, :channel => 'desired channel name' }, :sub => 'Braid JWT', diff --git a/package-lock.json b/package-lock.json index 08f0605..ad74586 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "braid", - "version": "1.0.0", + "version": "1.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bc125f4..a79810b 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "braid", - "version": "1.0.0", + "version": "1.2", "description": "", "main": "index.js", "scripts": { diff --git a/src/channels/types/privateChannel.ts b/src/channels/types/privateChannel.ts index 20ba6d7..0215b24 100644 --- a/src/channels/types/privateChannel.ts +++ b/src/channels/types/privateChannel.ts @@ -6,7 +6,7 @@ var logger = require('../../logger'); class PrivateChannel extends ChannelBase { broadcastMessage(from: PrivateClient, message: any) { for (let client of this.clients) { - if (client != from && client.data.user_type != from.data.user_type) { + if (client != from && client.roles.includes('receiver') && from.roles.includes('broadcaster')) { console.log('sending message: ' + JSON.stringify(message)) client.ws.send(JSON.stringify(message)); logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }}); diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index 0ea6c8f..e0e1ad5 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -14,6 +14,7 @@ class ClientBase { channel: ChannelBase|Private|null; clientManager: ClientManager; channelManager: ChannelManager; + roles: Array; constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { this.ws = ws; @@ -22,6 +23,7 @@ class ClientBase { this.channel = null; this.clientManager = clientManager; this.channelManager = channelManager; + this.roles = ['receiver'] } getData() { diff --git a/src/clients/types/privateClient.ts b/src/clients/types/privateClient.ts index a57b3f3..78d43fe 100644 --- a/src/clients/types/privateClient.ts +++ b/src/clients/types/privateClient.ts @@ -3,11 +3,13 @@ import ClientBase from '../clientBase'; import ClientManager from '../../clientManager'; import ChannelManager from '../../channelManager'; -var logger = require('../logger'); +var logger = require('../../logger'); class PrivateClient extends ClientBase { + constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { super(data, ws, channelManager, clientManager); + this.roles = data.user_roles logger.accessLog.info('Private Client Created', {data: data}); } }; diff --git a/src/config/app.ts b/src/config/app.ts index f2887a5..1e0abc6 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -1,5 +1,5 @@ module.exports = { - version : '1.0.8', + version : '1.2', whitelist : (process.env.WHITELIST || "http://admin.localhost").split(','), secret : process.env.SECRET || "test", devToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImNsaWVudCI6InRlc3QiLCJjbGllbnRfdHlwZSI6InNpdGUiLCJ1c2VyX3R5cGUiOiJ1c2VyIiwidXNlcl9pZCI6MjAwLCJjaGFubmVsIjoidGVzdF9jaGFubmVsIn0sImF1ZCI6ImludGVybmFsIiwiaXNzIjoiWWFyZHN0aWNrIFNvZnR3YXJlIiwic3ViIjoiQnJhaWQgSldUIn0.5KNCov_EW1cycT4Ay0oSvk4Z4PHFedd3bWOyqkHHTBQ', diff --git a/src/messageManager.ts b/src/messageManager.ts index ee6cc20..906c7d6 100644 --- a/src/messageManager.ts +++ b/src/messageManager.ts @@ -5,20 +5,11 @@ var app = require('./config/app'); let schema = { message_type: Joi.string().valid(app.messageTypes).insensitive().required(), - current_index: Joi.number().integer(), - total_questions: Joi.number().integer(), - total_questions_answered: Joi.number().integer(), - time_elapsed: Joi.number(), - time_left: Joi.any(), - status: Joi.string(), channel: Joi.string(), channel_type: Joi.string(), client_type: Joi.string(), user_id: Joi.number().integer(), - user_exam_id: Joi.number().integer(), - user_name: Joi.string(), - exam_title: Joi.string(), - message: Joi.string(), + message: Joi.alternatives().try(Joi.string(), Joi.object()), }; module.exports = { From df864790f9cc81de1bc0ed439f1b4fb335cec00b Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Sun, 22 Dec 2019 01:24:37 -0500 Subject: [PATCH 3/8] initial addition of custom channels and clients --- package.json | 2 +- src/channelManager.ts | 22 +++++++------- src/channels/channelBase.ts | 44 +++++++++++++++++----------- src/channels/types/customChannel.ts | 18 ++++++++++++ src/channels/types/privateChannel.ts | 18 ++++-------- src/channels/types/publicChannel.ts | 15 ++++++++++ src/clientManager.ts | 9 +++--- src/clients/clientBase.ts | 15 ++++++---- src/clients/types/customClient.ts | 17 +++++++++++ src/clients/types/privateClient.ts | 1 - src/clients/types/publicClient.ts | 9 ++++++ src/messageManager.ts | 28 ++++++++++++------ src/test/channelBase.spec.ts | 2 +- src/test/clientManager.spec.ts | 6 ++-- 14 files changed, 143 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index a79810b..a620085 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "braid", - "version": "1.2", + "version": "1.2.0", "description": "", "main": "index.js", "scripts": { diff --git a/src/channelManager.ts b/src/channelManager.ts index d1d6a08..890689b 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -1,7 +1,9 @@ -import ChannelBase from './channels/channelBase'; -import ClientBase from './clients/clientBase'; -import PrivateClient from './clients/types/privateClient'; +import PublicChannel from './channels/types/publicChannel'; import PrivateChannel from './channels/types/privateChannel'; +import CustomChannel from './channels/types/customChannel'; +import PublicClient from './clients/types/publicClient'; +import PrivateClient from './clients/types/privateClient'; +import CustomClient from './clients/types/customClient'; var logger = require('./logger'); @@ -10,13 +12,13 @@ class ChannelManager { constructor() { // create default channel... - var channel: ChannelBase = new ChannelBase('default'); + var channel: PublicChannel = new PublicChannel('default'); this.channels.push(channel); } createChannel(data: any) { - var channelExists: ChannelBase|PrivateChannel|null = this.channelExists(data.channel); - var channel: ChannelBase|PrivateChannel; + var channelExists: PublicChannel|PrivateChannel|CustomChannel|null = this.channelExists(data.channel); + var channel: PublicChannel|PrivateChannel|CustomChannel; if (channelExists) { channel = channelExists; @@ -40,8 +42,8 @@ class ChannelManager { return null; } - addClientToChannel(client: ClientBase|PrivateClient, channel_id: string) { - var channel: ChannelBase|PrivateChannel|null = this.channelExists(channel_id); + addClientToChannel(client: PublicClient|PrivateClient|CustomClient, channel_id: string) { + var channel: PrivateChannel|PrivateChannel|CustomChannel|null = this.channelExists(channel_id); if (channel) { channel.addClient(client); @@ -63,7 +65,7 @@ class ChannelManager { } catch (e) { logger.errorLog.info(e); logger.accessLog.info(`creating base channel: ${data.channel}`); - return new ChannelBase(data.channel); + return new PublicChannel(data.channel); } } @@ -83,7 +85,7 @@ class ChannelManager { return false; } - changeChannel(client: ClientBase|PrivateClient, changeRequest: any) { + changeChannel(client: PublicClient|PrivateClient|CustomClient, changeRequest: any) { if (client.channel != null) { this.removeClientFromChannel(client.id, client.channel.id) } diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index 68a2a7d..908d9f6 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -1,21 +1,44 @@ -import ClientBase from '../clients/clientBase'; +import PublicClient from '../clients/types/publicClient'; import PrivateClient from '../clients/types/privateClient'; +import CustomClient from '../clients/types/customClient'; +import * as Joi from 'joi'; var logger = require('../logger'); class ChannelBase { id: string; - clients: PrivateClient[]|ClientBase[] = []; + clients: PrivateClient[]|PublicClient[]|CustomClient[] = []; constructor(id: string) { this.id = id; logger.accessLog.info('Channel Created', {channelId: id}); } - addClient(client: PrivateClient|ClientBase) { + validations() { + return {} + } + + broadcastMessage(from: PublicClient|PrivateClient|CustomClient, message: object) { + for (let to of this.clients) { + if (this.messageTransactionPossible(from, to)) { + to.ws.send(JSON.stringify(message)); + logger.accessLog.info(`sent to ${to.id}`, { data: { message: message }}); + } else { + logger.accessLog.info(`client is unable to send: ${to.id}`, { data: { message: message }}); + } + } + + return {'status': 'success', 'message': `message broadcast complete`}; + } + + messageTransactionPossible(_from: PublicClient|PrivateClient|CustomClient, _to: PublicClient|PrivateClient|CustomClient) { + return true + } + + addClient(client: PublicClient|PrivateClient|CustomClient) { if (this.clientExists(client.id)) { logger.errorLog.info('Client already exits in channel', {channelId: this.id, clientId: client.id}); - return {'status': 'notice', 'message': 'client aleady exists in channel'}; + return {'status': 'notice', 'message': 'client already exists in channel'}; } else { this.clients.push(client); logger.accessLog.info('Added client to channel', {channelId: this.id, clientId: client.id}); @@ -44,19 +67,6 @@ class ChannelBase { return false; } - - broadcastMessage(from: ClientBase|null, message: string) { - for (let client of this.clients) { - if (client != from) { - client.ws.send(JSON.stringify(message)); - logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }}); - } else { - logger.accessLog.info(`client is same as sender: ${client.id}`, { data: { message: message }}); - } - } - - return {'status': 'success', 'message': `message broadcast complete`}; - } }; export default ChannelBase; diff --git a/src/channels/types/customChannel.ts b/src/channels/types/customChannel.ts index e69de29..b4a6fbb 100644 --- a/src/channels/types/customChannel.ts +++ b/src/channels/types/customChannel.ts @@ -0,0 +1,18 @@ +import CustomClient from '../../clients/types/customClient'; +import ChannelBase from '../channelBase'; +import * as Joi from 'joi'; + +class CustomChannel extends ChannelBase { + validations() { + return { + test: Joi.alternatives().try(Joi.string(), Joi.object()) + } + } + + messageTransactionPossible(from: CustomClient, to: CustomClient) { + return true + } +}; + +module.exports = CustomChannel; +export default CustomChannel; diff --git a/src/channels/types/privateChannel.ts b/src/channels/types/privateChannel.ts index 0215b24..c668b7a 100644 --- a/src/channels/types/privateChannel.ts +++ b/src/channels/types/privateChannel.ts @@ -4,18 +4,12 @@ import ChannelBase from '../channelBase'; var logger = require('../../logger'); class PrivateChannel extends ChannelBase { - broadcastMessage(from: PrivateClient, message: any) { - for (let client of this.clients) { - if (client != from && client.roles.includes('receiver') && from.roles.includes('broadcaster')) { - console.log('sending message: ' + JSON.stringify(message)) - client.ws.send(JSON.stringify(message)); - logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }}); - } else { - logger.accessLog.info(`client is either just a broadcaster or is the sender: ${client.id}`, { data: { message: message }}); - } - } - - return {'status': 'success', 'message': `message broadcast complete`}; + messageTransactionPossible(from: PrivateClient, to: PrivateClient) { + return ( + to != from && + to.roles.includes('receiver') && + from.roles.includes('broadcaster') + ) } }; diff --git a/src/channels/types/publicChannel.ts b/src/channels/types/publicChannel.ts index e69de29..abdc902 100644 --- a/src/channels/types/publicChannel.ts +++ b/src/channels/types/publicChannel.ts @@ -0,0 +1,15 @@ +import PublicClient from '../../clients/types/publicClient'; +import ChannelBase from '../channelBase'; + +var logger = require('../../logger'); + +class PublicChannel extends ChannelBase { + messageTransactionPossible(from: PublicClient, to: PublicClient) { + return ( + to != from + ) + } +}; + +module.exports = PublicChannel; +export default PublicChannel; diff --git a/src/clientManager.ts b/src/clientManager.ts index 7910d66..be5ce06 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -1,12 +1,13 @@ import * as WebSocket from 'ws'; -import ClientBase from './clients/clientBase'; +import PublicClient from './clients/types/publicClient'; import PrivateClient from './clients/types/privateClient'; +import CustomClient from './clients/types/customClient'; import ChannelManager from './channelManager'; var logger = require('./logger'); class ClientManager { - clients: ClientBase[]|PrivateClient[] = []; + clients: PublicClient[]|PrivateClient[]|CustomClient[] = []; constructor() { //...maybe one day @@ -26,7 +27,7 @@ class ClientManager { } clientsOfType(client_type: string) { - var result: ClientBase[]|PrivateClient[] = []; + var result: PublicClient[]|PrivateClient[]|CustomClient[] = []; for (let client of this.clients) { if (client.type() == client_type) { @@ -76,7 +77,7 @@ class ClientManager { } catch (e) { logger.errorLog.info(e); logger.accessLog.info(`creating base client: ${data.user_id}`); - return new ClientBase(data, ws, channelManager, this); + return new PublicClient(data, ws, channelManager, this); } } }; diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index e0e1ad5..966c5fe 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -1,8 +1,9 @@ import * as WebSocket from 'ws'; import ClientManager from '../clientManager'; import ChannelManager from '../channelManager'; -import ChannelBase from '../channels/channelBase'; -import Private from '../channels/types/privateChannel'; +import PublicChannel from '../channels/types/publicChannel'; +import PrivateChannel from '../channels/types/privateChannel'; +import CustomChannel from '../channels/types/customChannel'; var messageManager = require('../messageManager'); var logger = require('../logger'); @@ -11,7 +12,7 @@ class ClientBase { ws: WebSocket; data: any; id: number; - channel: ChannelBase|Private|null; + channel: PublicChannel|PrivateChannel|CustomChannel|null; clientManager: ClientManager; channelManager: ChannelManager; roles: Array; @@ -26,6 +27,10 @@ class ClientBase { this.roles = ['receiver'] } + validations() { + return {} + } + getData() { return this.data; } @@ -38,12 +43,12 @@ class ClientBase { return this.data.client; } - connectToChannel(channel: Private) { + connectToChannel(channel: PublicChannel|PrivateChannel|CustomChannel) { this.channel = channel; var messageListener = (message: any) => { logger.accessLog.info(`starting message transaction on channel ${channel.id}: `, {message: message}); - message = messageManager.prepareMessage(message); + message = messageManager.prepareMessage(message, channel, this); if (!message.error) { if (message['message_type'] == 'broadcast') { diff --git a/src/clients/types/customClient.ts b/src/clients/types/customClient.ts index e69de29..19b938b 100644 --- a/src/clients/types/customClient.ts +++ b/src/clients/types/customClient.ts @@ -0,0 +1,17 @@ +import ClientBase from '../clientBase'; +import * as Joi from 'joi'; + +class CustomClient extends ClientBase { + // validations() { + // return { + // test: Joi.alternatives().try(Joi.string(), Joi.object()) + // } + // } + validations() { + return { what: "test"} + } +}; + +module.exports = CustomClient; +export default CustomClient; + diff --git a/src/clients/types/privateClient.ts b/src/clients/types/privateClient.ts index 78d43fe..a5fec6c 100644 --- a/src/clients/types/privateClient.ts +++ b/src/clients/types/privateClient.ts @@ -6,7 +6,6 @@ import ChannelManager from '../../channelManager'; var logger = require('../../logger'); class PrivateClient extends ClientBase { - constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { super(data, ws, channelManager, clientManager); this.roles = data.user_roles diff --git a/src/clients/types/publicClient.ts b/src/clients/types/publicClient.ts index e69de29..a97e585 100644 --- a/src/clients/types/publicClient.ts +++ b/src/clients/types/publicClient.ts @@ -0,0 +1,9 @@ +import ClientBase from '../clientBase'; + +var logger = require('../../logger'); + +class PublicClient extends ClientBase {}; + +module.exports = PublicClient; +export default PublicClient; + diff --git a/src/messageManager.ts b/src/messageManager.ts index 906c7d6..294b356 100644 --- a/src/messageManager.ts +++ b/src/messageManager.ts @@ -1,21 +1,31 @@ import * as Joi from 'joi'; +import PublicChannel from './channels/types/publicChannel'; +import PrivateChannel from './channels/types/privateChannel'; +import CustomChannel from './channels/types/customChannel'; +import PublicClient from './clients/types/publicClient'; +import PrivateClient from './clients/types/privateClient'; +import CustomClient from './clients/types/customClient'; var logger = require('./logger'); var app = require('./config/app'); -let schema = { - message_type: Joi.string().valid(app.messageTypes).insensitive().required(), - channel: Joi.string(), - channel_type: Joi.string(), - client_type: Joi.string(), - user_id: Joi.number().integer(), - message: Joi.alternatives().try(Joi.string(), Joi.object()), +let schema = (channel: PublicChannel|PrivateChannel|CustomChannel, client: PublicClient|PrivateClient|CustomClient) => { + let validations = { + message_type: Joi.string().valid(app.messageTypes).insensitive().required(), + channel: Joi.string(), + channel_type: Joi.string(), + client_type: Joi.string(), + user_id: Joi.number().integer(), + message: Joi.alternatives().try(Joi.string(), Joi.object()) + } + + return {...validations, ...channel.validations, ...client.validations} }; module.exports = { - prepareMessage: (message: string) => { + prepareMessage: (message: string, channel: PublicChannel|PrivateChannel|CustomChannel, client: PublicClient|PrivateClient|CustomClient) => { var parsed = JSON.parse(message) - const result = Joi.validate(parsed, schema); + const result = Joi.validate(parsed, schema(channel, client)); if (result.error) { return result diff --git a/src/test/channelBase.spec.ts b/src/test/channelBase.spec.ts index 806fa30..a302093 100644 --- a/src/test/channelBase.spec.ts +++ b/src/test/channelBase.spec.ts @@ -14,7 +14,7 @@ var wsClient = new WebSocketClient(); var channel: ChannelBase; var clientManager = new ClientManager(); var channelManager = new ChannelManager(); -var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } +var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name } var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager); describe('ChannelBase', function () { diff --git a/src/test/clientManager.spec.ts b/src/test/clientManager.spec.ts index 6afd07d..4e8a418 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -8,7 +8,7 @@ var assert = require('chai').assert; var sinon = require('sinon'); var name: string = 'test channel'; -var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } +var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name } var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); var clientManager = new ClientManager(); @@ -23,7 +23,7 @@ describe('ClientManager', function () { }); it('should get clients of type', function () { - var result = clientManager.clientsOfType('site'); + var result = clientManager.clientsOfType('private'); assert(result.length > 0, 'returns one client'); }); @@ -52,7 +52,7 @@ describe('ClientManager', function () { }); it('should add client of type PrivateClient', function () { - var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': '["broadcaster", "receiver"]', 'channel': name } + var data: any = { 'channel_type': 'private', 'client_type': 'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name } var result = clientManager.getClientType(data, channelManager, wsClient); expect(result.clientType()).to.be.equal('private'); }); From 33d188659920b8074b460164dea85444cc746bc1 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Sun, 22 Dec 2019 01:31:21 -0500 Subject: [PATCH 4/8] oops missed these things --- src/clientManager.ts | 2 +- src/clients/types/customClient.ts | 3 --- src/server.ts | 7 ++++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/clientManager.ts b/src/clientManager.ts index be5ce06..0701716 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -69,7 +69,7 @@ class ClientManager { getClientType(data: any, channelManager: ChannelManager, ws: WebSocket) { var client_type = data.client_type + 'Client' - + try { var Client = require(`./clients/types/${client_type}`); logger.accessLog.info(`attempting to create client of type ${data.client_type}, client id: ${data.user_id}...`); diff --git a/src/clients/types/customClient.ts b/src/clients/types/customClient.ts index 19b938b..72b562c 100644 --- a/src/clients/types/customClient.ts +++ b/src/clients/types/customClient.ts @@ -7,9 +7,6 @@ class CustomClient extends ClientBase { // test: Joi.alternatives().try(Joi.string(), Joi.object()) // } // } - validations() { - return { what: "test"} - } }; module.exports = CustomClient; diff --git a/src/server.ts b/src/server.ts index 5037181..4a9f6e3 100755 --- a/src/server.ts +++ b/src/server.ts @@ -12,8 +12,9 @@ var app = require('./config/app'); var logger = require('./logger'); import ClientManager from './clientManager'; import ChannelManager from './channelManager'; -import ClientBase from './clients/clientBase'; +import PublicClient from './clients/types/publicClient'; import PrivateClient from './clients/types/privateClient'; +import CustomClient from './clients/types/customClient'; // if (app.environment == 'development') { @@ -55,13 +56,13 @@ function connectionManager() { } if (clientManager.clientExists(data.user_id)) { - var client: ClientBase|PrivateClient|null = clientManager.getClient(data.user_id); + var client: PublicClient|PrivateClient|CustomClient|null = clientManager.getClient(data.user_id); if (client != null) { client.replaceWebSocket(ws); } } else { - var client: ClientBase|PrivateClient|null = clientManager.addClient(data, channelManager, ws); + var client: PublicClient|PrivateClient|CustomClient|null = clientManager.addClient(data, channelManager, ws); } if (client != null) { From fb5533ce7a6b552f54a7b3c37ccb58be5b80ab88 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Thu, 26 Dec 2019 03:16:18 -0500 Subject: [PATCH 5/8] additon of custom channel conditional eval, though it desperately needs sanitization and/or a framework to work upon. further optimisations and addition of direct messaging --- src/channelManager.ts | 19 +++++++++------ src/channels/channelBase.ts | 7 +----- src/channels/types/customChannel.ts | 14 +++++------ src/channels/types/privateChannel.ts | 3 ++- src/channels/types/publicChannel.ts | 3 ++- src/clientManager.ts | 14 +++++++---- src/clients/clientBase.ts | 35 +++++++++++++++++----------- src/clients/types/customClient.ts | 16 ++++++++----- src/clients/types/privateClient.ts | 1 - src/clients/types/publicClient.ts | 1 - src/config/app.ts | 2 +- src/messageManager.ts | 30 ++++-------------------- src/services/validations.ts | 26 +++++++++++++++++++++ src/test/channelBase.spec.ts | 2 +- src/test/clientManager.spec.ts | 2 +- 15 files changed, 98 insertions(+), 77 deletions(-) create mode 100644 src/services/validations.ts diff --git a/src/channelManager.ts b/src/channelManager.ts index 890689b..572e0cc 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -8,11 +8,11 @@ import CustomClient from './clients/types/customClient'; var logger = require('./logger'); class ChannelManager { - channels: any = []; + channels: any[] = []; constructor() { // create default channel... - var channel: PublicChannel = new PublicChannel('default'); + let channel = new PublicChannel('default'); this.channels.push(channel); } @@ -56,13 +56,18 @@ class ChannelManager { } createByChannelType(data: any) { - var channel_name = data.channel_type + 'Channel' - try { - var Channel = require(`./channels/types/${channel_name}`); - logger.accessLog.info(`attempting to create channel of type ${channel_name}, channel id: ${data.channel}...`); - return new Channel(data.channel); + logger.accessLog.info(`attempting to create channel of type ${data.channel_type}, channel id: ${data.channel}...`); + + if (data.channel_type == 'public') { + return new PublicChannel(data.channel) + } else if (data.channel_type == 'private') { + return new PrivateChannel(data.channel) + } else { + return new CustomChannel(data.channel, data.custom) + } } catch (e) { + console.log(e) logger.errorLog.info(e); logger.accessLog.info(`creating base channel: ${data.channel}`); return new PublicChannel(data.channel); diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index 908d9f6..9219110 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -1,23 +1,18 @@ import PublicClient from '../clients/types/publicClient'; import PrivateClient from '../clients/types/privateClient'; import CustomClient from '../clients/types/customClient'; -import * as Joi from 'joi'; var logger = require('../logger'); class ChannelBase { id: string; - clients: PrivateClient[]|PublicClient[]|CustomClient[] = []; + clients: any[] = []; constructor(id: string) { this.id = id; logger.accessLog.info('Channel Created', {channelId: id}); } - validations() { - return {} - } - broadcastMessage(from: PublicClient|PrivateClient|CustomClient, message: object) { for (let to of this.clients) { if (this.messageTransactionPossible(from, to)) { diff --git a/src/channels/types/customChannel.ts b/src/channels/types/customChannel.ts index b4a6fbb..9c768a1 100644 --- a/src/channels/types/customChannel.ts +++ b/src/channels/types/customChannel.ts @@ -1,18 +1,18 @@ import CustomClient from '../../clients/types/customClient'; import ChannelBase from '../channelBase'; -import * as Joi from 'joi'; class CustomChannel extends ChannelBase { - validations() { - return { - test: Joi.alternatives().try(Joi.string(), Joi.object()) - } + clients: CustomClient[] = []; + custom: any; + + constructor(id: string, custom: any) { + super(id); + this.custom = custom } messageTransactionPossible(from: CustomClient, to: CustomClient) { - return true + return eval(this.custom.broadcastConditions) } }; -module.exports = CustomChannel; export default CustomChannel; diff --git a/src/channels/types/privateChannel.ts b/src/channels/types/privateChannel.ts index c668b7a..7dc6b03 100644 --- a/src/channels/types/privateChannel.ts +++ b/src/channels/types/privateChannel.ts @@ -4,6 +4,8 @@ import ChannelBase from '../channelBase'; var logger = require('../../logger'); class PrivateChannel extends ChannelBase { + clients: PrivateClient[] = []; + messageTransactionPossible(from: PrivateClient, to: PrivateClient) { return ( to != from && @@ -13,5 +15,4 @@ class PrivateChannel extends ChannelBase { } }; -module.exports = PrivateChannel; export default PrivateChannel; diff --git a/src/channels/types/publicChannel.ts b/src/channels/types/publicChannel.ts index abdc902..07daaa1 100644 --- a/src/channels/types/publicChannel.ts +++ b/src/channels/types/publicChannel.ts @@ -4,6 +4,8 @@ import ChannelBase from '../channelBase'; var logger = require('../../logger'); class PublicChannel extends ChannelBase { + clients: PublicClient[] = []; + messageTransactionPossible(from: PublicClient, to: PublicClient) { return ( to != from @@ -11,5 +13,4 @@ class PublicChannel extends ChannelBase { } }; -module.exports = PublicChannel; export default PublicChannel; diff --git a/src/clientManager.ts b/src/clientManager.ts index 0701716..660d8a7 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -7,7 +7,7 @@ import ChannelManager from './channelManager'; var logger = require('./logger'); class ClientManager { - clients: PublicClient[]|PrivateClient[]|CustomClient[] = []; + clients: any[] = []; constructor() { //...maybe one day @@ -68,12 +68,16 @@ class ClientManager { } getClientType(data: any, channelManager: ChannelManager, ws: WebSocket) { - var client_type = data.client_type + 'Client' - try { - var Client = require(`./clients/types/${client_type}`); logger.accessLog.info(`attempting to create client of type ${data.client_type}, client id: ${data.user_id}...`); - return new Client(data, ws, channelManager, this); + + if (data.client_type == 'public') { + return new PublicClient(data, ws, channelManager, this) + } else if (data.channel_type == 'private') { + return new PrivateClient(data, ws, channelManager, this) + } else { + return new CustomClient(data, ws, channelManager, this) + } } catch (e) { logger.errorLog.info(e); logger.accessLog.info(`creating base client: ${data.user_id}`); diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index 966c5fe..bda5248 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -4,6 +4,9 @@ import ChannelManager from '../channelManager'; import PublicChannel from '../channels/types/publicChannel'; import PrivateChannel from '../channels/types/privateChannel'; import CustomChannel from '../channels/types/customChannel'; +import PublicClient from '../clients/types/publicClient'; +import PrivateClient from '../clients/types/privateClient'; +import CustomClient from '../clients/types/customClient'; var messageManager = require('../messageManager'); var logger = require('../logger'); @@ -27,10 +30,6 @@ class ClientBase { this.roles = ['receiver'] } - validations() { - return {} - } - getData() { return this.data; } @@ -46,21 +45,24 @@ class ClientBase { connectToChannel(channel: PublicChannel|PrivateChannel|CustomChannel) { this.channel = channel; - var messageListener = (message: any) => { - logger.accessLog.info(`starting message transaction on channel ${channel.id}: `, {message: message}); - message = messageManager.prepareMessage(message, channel, this); + var messageListener = (data: any) => { + logger.accessLog.info(`starting message transaction on channel ${channel.id}: `, {data: data}); + data = messageManager.prepareMessage(data, channel, this); - if (!message.error) { - if (message['message_type'] == 'broadcast') { - channel.broadcastMessage(this, message); - } else if (message['message_type'] == 'changeChannel') { + if (!data.error) { + if (data.message_type == 'broadcast') { + channel.broadcastMessage(this, data); + } else if (data.message_type == 'direct') { + let to = this.clientManager.getClient(data.message.to) + to.directMessage(data) + } else if (data.message_type == 'changeChannel') { this.ws.removeListener('message', messageListener); - this.channelManager.changeChannel(this, message); + this.channelManager.changeChannel(this, data); } - logger.accessLog.info(`message transaction complete on channel ${channel.id}: `, {message: message}); + logger.accessLog.info(`message transaction complete on channel ${channel.id}: `, {message: data}); } else { - logger.errorLog.info(`Validation failed, please review schema: ${channel.id}`, {data: {message: message, error: message.error}}); + logger.errorLog.info(`Validation failed, please review schema: ${channel.id}`, {data: {message: data, error: data.error}}); } } @@ -78,6 +80,11 @@ class ClientBase { }); } + directMessage(message: any) { + this.ws.send(JSON.stringify(message)); + logger.accessLog.info(`sent direct message to ${this.id}`, { data: { message: message }}); + } + replaceWebSocket(ws: WebSocket) { this.ws.close(); this.ws = ws; diff --git a/src/clients/types/customClient.ts b/src/clients/types/customClient.ts index 72b562c..2732b70 100644 --- a/src/clients/types/customClient.ts +++ b/src/clients/types/customClient.ts @@ -1,14 +1,18 @@ import ClientBase from '../clientBase'; +import ClientManager from '../../clientManager'; +import ChannelManager from '../../channelManager'; +import * as WebSocket from 'ws'; import * as Joi from 'joi'; +var logger = require('../../logger'); + class CustomClient extends ClientBase { - // validations() { - // return { - // test: Joi.alternatives().try(Joi.string(), Joi.object()) - // } - // } + constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { + super(data, ws, channelManager, clientManager); + this.roles = data.user_roles + logger.accessLog.info('Custom Client Created', {data: data}); + } }; -module.exports = CustomClient; export default CustomClient; diff --git a/src/clients/types/privateClient.ts b/src/clients/types/privateClient.ts index a5fec6c..616838f 100644 --- a/src/clients/types/privateClient.ts +++ b/src/clients/types/privateClient.ts @@ -13,6 +13,5 @@ class PrivateClient extends ClientBase { } }; -module.exports = PrivateClient; export default PrivateClient; diff --git a/src/clients/types/publicClient.ts b/src/clients/types/publicClient.ts index a97e585..f66c3f4 100644 --- a/src/clients/types/publicClient.ts +++ b/src/clients/types/publicClient.ts @@ -4,6 +4,5 @@ var logger = require('../../logger'); class PublicClient extends ClientBase {}; -module.exports = PublicClient; export default PublicClient; diff --git a/src/config/app.ts b/src/config/app.ts index 1e0abc6..58dafbf 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -15,5 +15,5 @@ module.exports = { audience: 'internal', algorithm: ["HS256"] }, - messageTypes : ['broadcast', 'changeChannel'] + messageTypes : ['broadcast', 'direct', 'changeChannel'] } diff --git a/src/messageManager.ts b/src/messageManager.ts index 294b356..6566fa2 100644 --- a/src/messageManager.ts +++ b/src/messageManager.ts @@ -1,31 +1,11 @@ import * as Joi from 'joi'; -import PublicChannel from './channels/types/publicChannel'; -import PrivateChannel from './channels/types/privateChannel'; -import CustomChannel from './channels/types/customChannel'; -import PublicClient from './clients/types/publicClient'; -import PrivateClient from './clients/types/privateClient'; -import CustomClient from './clients/types/customClient'; - -var logger = require('./logger'); -var app = require('./config/app'); - -let schema = (channel: PublicChannel|PrivateChannel|CustomChannel, client: PublicClient|PrivateClient|CustomClient) => { - let validations = { - message_type: Joi.string().valid(app.messageTypes).insensitive().required(), - channel: Joi.string(), - channel_type: Joi.string(), - client_type: Joi.string(), - user_id: Joi.number().integer(), - message: Joi.alternatives().try(Joi.string(), Joi.object()) - } - - return {...validations, ...channel.validations, ...client.validations} -}; +import Validations from './services/validations'; module.exports = { - prepareMessage: (message: string, channel: PublicChannel|PrivateChannel|CustomChannel, client: PublicClient|PrivateClient|CustomClient) => { - var parsed = JSON.parse(message) - const result = Joi.validate(parsed, schema(channel, client)); + prepareMessage: (message: string) => { + let validations = new Validations(message) + let parsed = JSON.parse(message) + const result = Joi.validate(parsed, validations.MessageConditions); if (result.error) { return result diff --git a/src/services/validations.ts b/src/services/validations.ts new file mode 100644 index 0000000..3c09c7d --- /dev/null +++ b/src/services/validations.ts @@ -0,0 +1,26 @@ +import PublicChannel from '../channels/types/publicChannel'; +import PrivateChannel from '../channels/types/privateChannel'; +import CustomChannel from '../channels/types/customChannel'; +import * as Joi from 'joi' + +var app = require('../config/app'); + +class Validations { + MessageConditions = { + message_type: Joi.string().valid(app.messageTypes).insensitive().required(), + channel: Joi.string(), + channel_type: Joi.string(), + client_type: Joi.string(), + user_id: Joi.number().integer(), + message: Joi.alternatives().try(Joi.string(), Joi.object()), + custom: Joi.object() + } + + constructor(message: any) { + if (message.channel_type == 'custom') { + let conditions = message.conditions + } + } +} + +export default Validations; \ No newline at end of file diff --git a/src/test/channelBase.spec.ts b/src/test/channelBase.spec.ts index a302093..fde1dcd 100644 --- a/src/test/channelBase.spec.ts +++ b/src/test/channelBase.spec.ts @@ -40,7 +40,7 @@ describe('ChannelBase', function () { }); it('should not broadcast a message to self', function () { - var result = channel.broadcastMessage(client, 'test message'); + var result = channel.broadcastMessage(client, {message: "test message"}); expect(result.status).to.be.equal('success'); }); diff --git a/src/test/clientManager.spec.ts b/src/test/clientManager.spec.ts index 4e8a418..6b02d2e 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -1,5 +1,5 @@ import * as WebSocket from 'ws'; -import ClientBase from '../clients/clientBase'; +import PublicClient from '../clients/types/publicClient'; import ClientManager from '../clientManager'; import ChannelManager from '../channelManager'; From d663afd8fe0b93732ad1a5b2e7cc7a205a604504 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Thu, 30 Jan 2020 14:59:25 -0500 Subject: [PATCH 6/8] remove explicit port --- .docker-compose/Dockerfile | 3 --- Dockerfile | 3 --- README.md | 5 ++--- docker-compose.yml | 2 -- src/config/app.ts | 2 +- 5 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.docker-compose/Dockerfile b/.docker-compose/Dockerfile index 40e04ba..c5fa404 100755 --- a/.docker-compose/Dockerfile +++ b/.docker-compose/Dockerfile @@ -15,7 +15,4 @@ RUN npm install # Bundle app source COPY . /app -ENV PORT 8443 -EXPOSE 8443 - CMD [ "node", "./dist/server/server.js" ] diff --git a/Dockerfile b/Dockerfile index 2728f8d..f489647 100755 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,4 @@ RUN npm install # Bundle app source COPY . /app -ENV PORT 8443 -EXPOSE 8443 - CMD [ "node", "./dist/server/server.js" ] diff --git a/README.md b/README.md index 3410a78..3676aac 100755 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ braid: environment: VIRTUAL_HOST: ysbraid.localhost HTTPS_METHOD: noredirect - VIRTUAL_PORT: 8443 ``` * once complete and confirmed to function, create a new PR for the measure repo, with the new tag and await ultimate judgement @@ -38,8 +37,8 @@ braid: * `tsc` transpiles the typescript in braid into javascript * `compose build braid` builds the braid container based on the latest transpiled javascript files * `comopose up -d` builds the braid container based on the files in the braid folder, overriding the default braid build container -* you can see if braid is running by typing in `https://ysbraid.localhost:8443` -* to connect to braid you need to use the url `wss://ysbraid.localhost:8443?token={token}` +* you can see if braid is running by typing in `https://ysbraid.localhost` +* to connect to braid you need to use the url `wss://ysbraid.localhost?token={token}` * the token is generated in the application that you're connecting to the braid app with (currently only measure has an implementation for this) * you can generate a token for development in your local console (`app-console`) with the following commands: diff --git a/docker-compose.yml b/docker-compose.yml index 98f581d..a688d37 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,5 @@ braid: MEASURE_BASE_URL: http://admin.localhost dns: - 172.17.0.1 - ports: - - "8443" volumes: - /apps/braid:/app diff --git a/src/config/app.ts b/src/config/app.ts index 58dafbf..9d50ae3 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -3,7 +3,7 @@ module.exports = { whitelist : (process.env.WHITELIST || "http://admin.localhost").split(','), secret : process.env.SECRET || "test", devToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImNsaWVudCI6InRlc3QiLCJjbGllbnRfdHlwZSI6InNpdGUiLCJ1c2VyX3R5cGUiOiJ1c2VyIiwidXNlcl9pZCI6MjAwLCJjaGFubmVsIjoidGVzdF9jaGFubmVsIn0sImF1ZCI6ImludGVybmFsIiwiaXNzIjoiWWFyZHN0aWNrIFNvZnR3YXJlIiwic3ViIjoiQnJhaWQgSldUIn0.5KNCov_EW1cycT4Ay0oSvk4Z4PHFedd3bWOyqkHHTBQ', - port: process.env.PORT || 8443, + port: process.env.PORT || 80, hostname: process.env.HOSTNAME || 'ysbraid.localhost', environment: process.env.ENVIRONMENT || 'development', log_level: process.env.LOG_LEVEL || 'debug', From 52bcd2d5703c8429edc57d8002c18b6276cc4411 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Sun, 2 Feb 2020 21:37:19 -0500 Subject: [PATCH 7/8] further enhancements, spec test fixes for refactor --- src/test/channelBase.spec.ts | 4 ++++ src/test/clientManager.spec.ts | 32 +++++++++++++------------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/test/channelBase.spec.ts b/src/test/channelBase.spec.ts index fde1dcd..9a71ab0 100644 --- a/src/test/channelBase.spec.ts +++ b/src/test/channelBase.spec.ts @@ -40,6 +40,10 @@ describe('ChannelBase', function () { }); it('should not broadcast a message to self', function () { + sinon.stub(channel, 'broadcastMessage').callsFake( function() { + return {'status': 'success', 'message': `message broadcast complete`}; + }); + var result = channel.broadcastMessage(client, {message: "test message"}); expect(result.status).to.be.equal('success'); }); diff --git a/src/test/clientManager.spec.ts b/src/test/clientManager.spec.ts index 6b02d2e..29ffa4d 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -1,11 +1,8 @@ -import * as WebSocket from 'ws'; -import PublicClient from '../clients/types/publicClient'; import ClientManager from '../clientManager'; import ChannelManager from '../channelManager'; var expect = require('chai').expect; var assert = require('chai').assert; -var sinon = require('sinon'); var name: string = 'test channel'; var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name } @@ -14,46 +11,43 @@ var wsClient = new WebSocketClient(); var clientManager = new ClientManager(); var channelManager = new ChannelManager(); -var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager); - describe('ClientManager', function () { it('should add a client', function () { - var result = clientManager.addClient(data, channelManager, wsClient); - expect(result.id).to.be.equal(125); + let result = clientManager.addClient(data, channelManager, wsClient); + expect(result).to.not.be.null }); it('should get clients of type', function () { - var result = clientManager.clientsOfType('private'); + let result = clientManager.clientsOfType('private'); assert(result.length > 0, 'returns one client'); }); it('should see client exists', function () { - var exists = clientManager.clientExists(data.user_id); - var result = exists ? true : false; + let exists = clientManager.clientExists(data.user_id); + let result = exists ? true : false; expect(result).to.be.equal(true); }); it('should see client does not exists', function () { - var exists = clientManager.clientExists(200); - var result = exists ? true : false; + let exists = clientManager.clientExists(200); + let result = exists ? true : false; expect(result).to.be.equal(false); }); it('should get an existing client', function () { - var exists = clientManager.getClient(data.user_id); - var result = exists ? true : false; + let exists = clientManager.getClient(data.user_id); + let result = exists ? true : false; expect(result).to.be.equal(true); }); it('should not get an existing client', function () { - var exists = clientManager.getClient(200); - var result = exists ? true : false; + let exists = clientManager.getClient(200); + let result = exists ? true : false; expect(result).to.be.equal(false); }); it('should add client of type PrivateClient', function () { - var data: any = { 'channel_type': 'private', 'client_type': 'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name } - var result = clientManager.getClientType(data, channelManager, wsClient); - expect(result.clientType()).to.be.equal('private'); + let result = clientManager.getClientType(data, channelManager, wsClient); + expect(result.type()).to.be.equal('private'); }); }); From 37859d42a38db5f4b67cbd36720d4743acd16f46 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Sun, 2 Feb 2020 21:51:02 -0500 Subject: [PATCH 8/8] gotta let the vars live in the moment --- src/channelManager.ts | 12 +++++------ src/channels/channelBase.ts | 2 +- src/clientManager.ts | 6 +++--- src/clients/clientBase.ts | 2 +- src/controllers/authController.ts | 2 +- src/server.ts | 33 ++++++------------------------- 6 files changed, 18 insertions(+), 39 deletions(-) diff --git a/src/channelManager.ts b/src/channelManager.ts index 572e0cc..745fac8 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -17,8 +17,8 @@ class ChannelManager { } createChannel(data: any) { - var channelExists: PublicChannel|PrivateChannel|CustomChannel|null = this.channelExists(data.channel); - var channel: PublicChannel|PrivateChannel|CustomChannel; + let channelExists: PublicChannel|PrivateChannel|CustomChannel|null = this.channelExists(data.channel); + let channel: PublicChannel|PrivateChannel|CustomChannel; if (channelExists) { channel = channelExists; @@ -43,7 +43,7 @@ class ChannelManager { } addClientToChannel(client: PublicClient|PrivateClient|CustomClient, channel_id: string) { - var channel: PrivateChannel|PrivateChannel|CustomChannel|null = this.channelExists(channel_id); + let channel: PrivateChannel|PrivateChannel|CustomChannel|null = this.channelExists(channel_id); if (channel) { channel.addClient(client); @@ -95,9 +95,9 @@ class ChannelManager { this.removeClientFromChannel(client.id, client.channel.id) } - var channel = this.createChannel(changeRequest); + let channel = this.createChannel(changeRequest); this.addClientToChannel(client, channel.id); - var message = {message_type: 'requestExamStatus'}; + let message = {message_type: 'requestExamStatus'}; channel.broadcastMessage(client, message); this.purgeEmptyChannels(); } @@ -105,7 +105,7 @@ class ChannelManager { purgeEmptyChannels() { for (let channel of this.channels) { if (channel.clients.length == 0 && channel.id != 'default') { - var index = this.channels.indexOf(channel) + let index = this.channels.indexOf(channel) this.channels.splice(index, 1); logger.accessLog.info(`channel removed: ${channel.id}`); } diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index 9219110..453aa7a 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -54,7 +54,7 @@ class ChannelBase { removeClient(id: number) { for (let client of this.clients) { if (client.id == id) { - var index = this.clients.indexOf(client) + let index = this.clients.indexOf(client) this.clients.splice(index, 1); return true; } diff --git a/src/clientManager.ts b/src/clientManager.ts index 660d8a7..592ec53 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -15,7 +15,7 @@ class ClientManager { addClient(data: any, channelManager: ChannelManager, ws: WebSocket) { if (data.client_type && !this.clientExists(data.user_id)) { - var client = this.getClientType(data, channelManager, ws); + let client = this.getClientType(data, channelManager, ws); this.clients.push(client); logger.accessLog.info(`client added to client manager: ${data.user_id}`); return client; @@ -27,7 +27,7 @@ class ClientManager { } clientsOfType(client_type: string) { - var result: PublicClient[]|PrivateClient[]|CustomClient[] = []; + let result: PublicClient[]|PrivateClient[]|CustomClient[] = []; for (let client of this.clients) { if (client.type() == client_type) { @@ -53,7 +53,7 @@ class ClientManager { } removeClient(id: number) { - var index: number = 0; + let index: number = 0; for (let client of this.clients) { if (client.id == id) { diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index bda5248..f9e997b 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -45,7 +45,7 @@ class ClientBase { connectToChannel(channel: PublicChannel|PrivateChannel|CustomChannel) { this.channel = channel; - var messageListener = (data: any) => { + let messageListener = (data: any) => { logger.accessLog.info(`starting message transaction on channel ${channel.id}: `, {data: data}); data = messageManager.prepareMessage(data, channel, this); diff --git a/src/controllers/authController.ts b/src/controllers/authController.ts index da42ae7..2f3c9ad 100644 --- a/src/controllers/authController.ts +++ b/src/controllers/authController.ts @@ -4,7 +4,7 @@ var app = require('../config/app') module.exports = { confirmToken : (req: any, res: any) => { - var token = req.body.token + let token = req.body.token res.json({ response: JSON.stringify(jwt.verify(token, app.secret, app.signOptions)) }); diff --git a/src/server.ts b/src/server.ts index 4a9f6e3..5e56bc2 100755 --- a/src/server.ts +++ b/src/server.ts @@ -1,8 +1,5 @@ //external imports -import * as express from 'express'; -import * as https from 'https'; import * as WebSocket from 'ws'; -import * as fs from 'fs'; import * as jwt from 'jsonwebtoken'; import * as url from 'url'; @@ -10,45 +7,27 @@ import * as url from 'url'; var routes = require('./routes'); var app = require('./config/app'); var logger = require('./logger'); + import ClientManager from './clientManager'; import ChannelManager from './channelManager'; import PublicClient from './clients/types/publicClient'; import PrivateClient from './clients/types/privateClient'; import CustomClient from './clients/types/customClient'; - -// if (app.environment == 'development') { -// var privateKey = fs.readFileSync(app.privateKey, 'utf8'); -// var certificate = fs.readFileSync(app.certificate, 'utf8'); -// var options = {key: privateKey, cert: certificate, hostname: app.hostname}; - -// const application = express(); -// const server = https.createServer(options, application); -// var wss = new WebSocket.Server({ server: server, maxPayload:250000, host: app.hostname }); - -// application.use(express.json()); -// application.use('', routes); - -// server.listen(app.port, () => { -// console.log(`Braid v${app.version} is running!\n`); -// logger.accessLog.info(`Braid v${app.version} is running!\n`); -// }); -// } else { - var wss = new WebSocket.Server({ maxPayload:250000, port: app.port }); -// } +var wss = new WebSocket.Server({ maxPayload:250000, port: app.port }); let clientManager = new ClientManager(); let channelManager = new ChannelManager(); function connectionManager() { wss.on('connection', (ws: WebSocket, request: any, args: string) => { - var result = JSON.parse(validateJWT(request)); + let result = JSON.parse(validateJWT(request)); if (result.error) { ws.send(`Unable to validate JWT, please try again or contact support...`); ws.close(); } else { - var data = result.data; + let data = result.data; logger.accessLog.info(`Client Connected: ${data.user_id}`); if (!channelManager.channelExists(data.channel)) { @@ -80,8 +59,8 @@ function connectionManager() { function validateJWT(request: any) { try { - var query = url.parse(request.url, true).query - var token = query.token || (app.environment == 'development' ? app.devToken : ''); + let query = url.parse(request.url, true).query + let token = query.token || (app.environment == 'development' ? app.devToken : ''); return JSON.stringify(jwt.verify(token, app.secret, app.signOptions)); } catch (e) { console.log(e);