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

This commit is contained in:
Josh Burman 2019-12-26 03:16:18 -05:00
parent 33d1886599
commit fb5533ce7a
15 changed files with 98 additions and 77 deletions

View File

@ -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);

View File

@ -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)) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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}`);

View File

@ -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;

View File

@ -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;

View File

@ -13,6 +13,5 @@ class PrivateClient extends ClientBase {
}
};
module.exports = PrivateClient;
export default PrivateClient;

View File

@ -4,6 +4,5 @@ var logger = require('../../logger');
class PublicClient extends ClientBase {};
module.exports = PublicClient;
export default PublicClient;

View File

@ -15,5 +15,5 @@ module.exports = {
audience: 'internal',
algorithm: ["HS256"]
},
messageTypes : ['broadcast', 'changeChannel']
messageTypes : ['broadcast', 'direct', 'changeChannel']
}

View File

@ -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

View File

@ -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;

View File

@ -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');
});

View File

@ -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';