Socket
Socket
Sign inDemoInstall

rpcapi

Package Overview
Dependencies
156
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.2 to 1.2.0

src/accessMethods/WebAPIAccessMethod/WebAPIAccessMethod.itest.d.ts

1

index.d.ts

@@ -5,1 +5,2 @@ export { API } from './src/API';

export { WebSocketAccessMethod } from './src/accessMethods/WebSocketAccessMethod';
export { AccessDeniedError } from './src/errorTypes';

@@ -14,2 +14,4 @@ "use strict";

exports.WebSocketAccessMethod = WebSocketAccessMethod_1.WebSocketAccessMethod;
var errorTypes_1 = require("./src/errorTypes");
exports.AccessDeniedError = errorTypes_1.AccessDeniedError;
//# sourceMappingURL=index.js.map

2

package.json
{
"name": "rpcapi",
"version": "1.1.2",
"version": "1.2.0",
"description": "Provides a struture for hosting RPC style APIs, supports both http and websocket access out of the box",

@@ -5,0 +5,0 @@ "repository": {

@@ -36,2 +36,6 @@ [![Build status](https://travis-ci.org/jyelewis/RPCAPI.svg?branch=master)](https://travis-ci.org/jyelewis/RPCAPI) [![Coverage Status](https://coveralls.io/repos/github/jyelewis/RPCAPI/badge.svg?branch=master)](https://coveralls.io/github/jyelewis/RPCAPI?branch=master)

- [Client code](#pushing-to-the-client---client-code)
- [Authentication](#authentication)
- [Checking access keys](#checking-access-keys)
- [Providing an access key via webapi](#providing-an-access-key-via-webapi)
- [Providing an access key via websocket client](#providing-an-access-key-via-websocket-client)
- [Advanced topics](#advanced-topics)

@@ -398,2 +402,35 @@ - [Mocking](#mocking)

## Authentication
When connecting to an endpoint, you can optionally provide an accessKey, this is available to the endpoint class via `this.accessKey`
### Checking access keys
From the api endpoint on the class, throw AccessDeniedError to reject a request.
Generally this will be done after doing a lookup on the accessKey (`this.accessKey`) to determine whether the user has access to the requested resource.
AccessDeniedError can be thrown from connect() to prevent the connection being completed.
It can also be thrown from a specific action.
### Providing an access key via webapi
Access keys are simply passed as url parameters eg.
```
http://localhost:8081/api/calculator/add?accessKey=qwer2134&a=1&b=2
```
### Providing an access key via websocket client
When connecting to an endpoint, pass the access key as the second parameter to .connectToEndpoint()
```javascript
const ep = await apiClient.connectToEndpoint('test', 'myAccessKey');
```
It is also possible to set a default access key at a connection level.
This is useful when using access keys to identify a user, the default access key can be set after they login
and from then on all requests will be authenticated. (NOTE: Existing connections will remain unchanged)
```javascript
apiClient.accessKey = 'myAccessKey';
const ep = await apiClient.connectToEndpoint('test');
```
## Advanced topics

@@ -400,0 +437,0 @@

@@ -40,2 +40,3 @@ "use strict";

var convertParamType_1 = require("./convertParamType");
var errorTypes_1 = require("../../errorTypes");
var defaultConfig = {

@@ -53,3 +54,3 @@ prefix: '/api'

var _this = this;
app.get(this.prefix + "/:endpoint/:action", function (req, res) {
app.get(this.prefix + "/:endpoint(*)/:action", function (req, res) {
var endpointName = req.params.endpoint;

@@ -73,2 +74,8 @@ var actionName = req.params.action;

}
if (e instanceof errorTypes_1.AccessDeniedError) {
res
.status(401)
.end(_this.formatResult(e.message));
return;
}
res

@@ -104,2 +111,3 @@ .status(500)

}
endpoint.accessKey = strParams.accessKey;
return [4 /*yield*/, endpoint.callConnect()];

@@ -106,0 +114,0 @@ case 1:

@@ -54,2 +54,3 @@ "use strict";

var delay_1 = require("../../util/delay");
var errorTypes_1 = require("../../errorTypes");
ava_1.default('Calls endpoint', function (t) { return __awaiter(_this, void 0, void 0, function () {

@@ -244,2 +245,104 @@ var hasCalled, TestEndpoint, testApi, accessMethod, retVal;

}); });
ava_1.default('Passes accessKey to endpoint before calling connect', function (t) { return __awaiter(_this, void 0, void 0, function () {
var TestEndpoint, testApi, accessMethod;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
TestEndpoint = /** @class */ (function (_super) {
__extends(TestEndpoint, _super);
function TestEndpoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
TestEndpoint.prototype.connect = function () {
t.is(this.accessKey, 'testAccessKey');
};
TestEndpoint.prototype.$testFunc = function () {
t.is(this.accessKey, 'testAccessKey');
return { done: true };
};
return TestEndpoint;
}(APIEndpoint_1.APIEndpoint));
testApi = new API_1.API();
testApi.registerEndpoint('test', TestEndpoint);
accessMethod = new index_1.WebAPIAccessMethod(testApi);
return [4 /*yield*/, accessMethod.processRequest('test', 'testFunc', { accessKey: 'testAccessKey' })];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}); });
ava_1.default('Calling access denied in connect causes processRequest to throw', function (t) { return __awaiter(_this, void 0, void 0, function () {
var TestEndpoint, testApi, accessMethod, e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
TestEndpoint = /** @class */ (function (_super) {
__extends(TestEndpoint, _super);
function TestEndpoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
TestEndpoint.prototype.connect = function () {
throw new errorTypes_1.AccessDeniedError('Test access denied');
};
TestEndpoint.prototype.$testFunc = function () {
return { done: true };
};
return TestEndpoint;
}(APIEndpoint_1.APIEndpoint));
testApi = new API_1.API();
testApi.registerEndpoint('test', TestEndpoint);
accessMethod = new index_1.WebAPIAccessMethod(testApi);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, accessMethod.processRequest('test', 'testFunc', {})];
case 2:
_a.sent();
t.fail();
return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
t.is(e_1.message, 'Test access denied');
t.pass();
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); });
ava_1.default('Calling access denied in action causes processRequest to throw', function (t) { return __awaiter(_this, void 0, void 0, function () {
var TestEndpoint, testApi, accessMethod, e_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
TestEndpoint = /** @class */ (function (_super) {
__extends(TestEndpoint, _super);
function TestEndpoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
TestEndpoint.prototype.$testFunc = function () {
throw new errorTypes_1.AccessDeniedError('Test access denied');
};
return TestEndpoint;
}(APIEndpoint_1.APIEndpoint));
testApi = new API_1.API();
testApi.registerEndpoint('test', TestEndpoint);
accessMethod = new index_1.WebAPIAccessMethod(testApi);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, accessMethod.processRequest('test', 'testFunc', {})];
case 2:
_a.sent();
t.fail();
return [3 /*break*/, 4];
case 3:
e_2 = _a.sent();
t.is(e_2.message, 'Test access denied');
t.pass();
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); });
//# sourceMappingURL=WebAPIAccessMethod.test.js.map

@@ -12,6 +12,6 @@ /// <reference types="socket.io" />

handleNewConnection(socket: SocketIO.Socket): void;
createNewSocketEndpoint(socketId: string, endpointName: string): Promise<IEndpointConnection>;
createNewSocketEndpoint(socketId: string, endpointName: string, accessKey?: string): Promise<IEndpointConnection>;
disconnectAllSocketEndpoints(socketId: string): void;
disconnectEndpointConnection(socketId: string, endpointConnectionId: string): void;
callEndpointAction(socketId: string, endpointConnectionId: string, actionName: string, params: any): Promise<any>;
callEndpointAction(socketId: string, endpointConnectionId: string, actionName: string, params?: any): Promise<any>;
}

@@ -52,2 +52,3 @@ "use strict";

var guid_1 = require("../../util/guid");
var errorTypes_1 = require("../../errorTypes");
var WebSocketAccessMethod = /** @class */ (function (_super) {

@@ -71,4 +72,4 @@ __extends(WebSocketAccessMethod, _super);

var _this = this;
socket.on('connectToEndpoint', function (endpointName, cb) {
_this.createNewSocketEndpoint(socket.id, endpointName).then(function (endpointConnection) {
socket.on('connectToEndpoint', function (endpointName, accessKey, cb) {
_this.createNewSocketEndpoint(socket.id, endpointName, accessKey).then(function (endpointConnection) {
if (endpointConnection === null) {

@@ -84,2 +85,6 @@ cb("Unable to create endpoint connection, '" + endpointName + "' does not exist", null);

}).catch(function (e) {
if (e instanceof errorTypes_1.AccessDeniedError) {
cb("Access denied: " + e.message, null);
return;
}
console.error(e);

@@ -99,2 +104,5 @@ cb("Unable to create endpoint connection, '" + endpointName + "' threw an error while setting up", null);

}
else if (e instanceof errorTypes_1.AccessDeniedError) {
cb("Access denied: " + e.message, null);
}
else {

@@ -117,3 +125,3 @@ if (_this.outputActionErrors) {

};
WebSocketAccessMethod.prototype.createNewSocketEndpoint = function (socketId, endpointName) {
WebSocketAccessMethod.prototype.createNewSocketEndpoint = function (socketId, endpointName, accessKey) {
return __awaiter(this, void 0, void 0, function () {

@@ -134,4 +142,7 @@ var endpoint, newEndpointConnection;

this.addEndpointConnection(newEndpointConnection);
endpoint.accessKey = accessKey;
//TODO: Clean up automatically if this fails? (we will currently leave a dead connection registered)
return [4 /*yield*/, newEndpointConnection.endpoint.callConnect()];
case 1:
//TODO: Clean up automatically if this fails? (we will currently leave a dead connection registered)
_a.sent();

@@ -164,2 +175,3 @@ return [2 /*return*/, newEndpointConnection];

WebSocketAccessMethod.prototype.callEndpointAction = function (socketId, endpointConnectionId, actionName, params) {
if (params === void 0) { params = {}; }
return __awaiter(this, void 0, void 0, function () {

@@ -166,0 +178,0 @@ var endpointConnection, actionParams, validatedParams, paramName, actionParamType, paramValue;

@@ -57,2 +57,3 @@ "use strict";

var API_1 = require("../../API");
var errorTypes_1 = require("../../errorTypes");
var socketAM;

@@ -77,2 +78,5 @@ var server;

};
TestEndpoint.prototype.$accessDenied = function () {
throw new errorTypes_1.AccessDeniedError('Test access denied');
};
TestEndpoint.prototype.$add = function (_a) {

@@ -99,5 +103,16 @@ var a = _a.a, b = _a.b;

}(APIEndpoint_1.APIEndpoint));
var TestThrowingEndpoint = /** @class */ (function (_super) {
__extends(TestThrowingEndpoint, _super);
function TestThrowingEndpoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
TestThrowingEndpoint.prototype.connect = function () {
throw new errorTypes_1.AccessDeniedError();
};
return TestThrowingEndpoint;
}(APIEndpoint_1.APIEndpoint));
var testAPI = new API_1.API();
testAPI.registerEndpoint('test', TestEndpoint);
testAPI.registerEndpoint('test2', TestEndpoint2);
testAPI.registerEndpoint('throws', TestThrowingEndpoint);
var app = express();

@@ -143,3 +158,3 @@ server = new http.Server(app);

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'doesntExist', function (error) {
connection.emit('connectToEndpoint', 'doesntExist', undefined, function (error) {
t.is(error, 'Unable to create endpoint connection, \'doesntExist\' does not exist');

@@ -157,3 +172,3 @@ resolve();

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test', function (error, epcid) {
connection.emit('connectToEndpoint', 'test', undefined, function (error, epcid) {
t.falsy(error);

@@ -172,3 +187,3 @@ t.truthy(epcid);

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test', function (error, epcid) {
connection.emit('connectToEndpoint', 'test', undefined, function (error, epcid) {
t.falsy(error);

@@ -191,3 +206,3 @@ t.truthy(epcid);

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test', function (error, epcid) {
connection.emit('connectToEndpoint', 'test', undefined, function (error, epcid) {
t.falsy(error);

@@ -210,3 +225,3 @@ t.truthy(epcid);

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test', function (error, epcid) {
connection.emit('connectToEndpoint', 'test', undefined, function (error, epcid) {
t.falsy(error);

@@ -229,3 +244,3 @@ t.truthy(epcid);

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test', function (error, epcid) {
connection.emit('connectToEndpoint', 'test', undefined, function (error, epcid) {
t.falsy(error);

@@ -243,2 +258,34 @@ t.truthy(epcid);

}); });
ava_1.default('Sends AccessDeniedError if action throws AccessDeniedError', function (t) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve) {
var connection = ioClient('http://localhost:8056/');
connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test', undefined, function (error, epcid) {
t.falsy(error);
t.truthy(epcid);
connection.emit('callEndpointFunction', epcid, 'accessDenied', {}, function (error, res) {
t.is(error, 'Access denied: Test access denied');
t.falsy(res);
resolve();
});
});
});
})];
});
}); });
ava_1.default('Sends AccessDeniedError if endpoint connect throws AccessDeniedError', function (t) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve) {
var connection = ioClient('http://localhost:8056/');
connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'throws', undefined, function (error, epcid) {
t.is(error, 'Access denied: Access denied');
t.falsy(epcid);
resolve();
});
});
})];
});
}); });
ava_1.default('Calls disconenct() on the endpoint when client asks to disconnect endpoint', function (t) { return __awaiter(_this, void 0, void 0, function () {

@@ -249,3 +296,3 @@ return __generator(this, function (_a) {

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test', function (error, epcid) {
connection.emit('connectToEndpoint', 'test', undefined, function (error, epcid) {
t.falsy(error);

@@ -269,3 +316,3 @@ t.truthy(epcid);

connection.on('serverReady', function () {
connection.emit('connectToEndpoint', 'test2', function (error, epcid) {
connection.emit('connectToEndpoint', 'test2', undefined, function (error, epcid) {
t.falsy(error);

@@ -272,0 +319,0 @@ t.truthy(epcid);

@@ -54,2 +54,3 @@ "use strict";

var delay_1 = require("../../util/delay");
var errorTypes_1 = require("../../errorTypes");
ava_1.default('Calls connect() when new endpoint is created', function (t) { return __awaiter(_this, void 0, void 0, function () {

@@ -623,2 +624,100 @@ var hasCalled, TestEndpoint, testApi, accessMethod;

}); });
ava_1.default('Pasing accessKey when creating endpoint assigns it on the endpoint before calling connect()', function (t) { return __awaiter(_this, void 0, void 0, function () {
var TestEndpoint, testApi, accessMethod;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
TestEndpoint = /** @class */ (function (_super) {
__extends(TestEndpoint, _super);
function TestEndpoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
TestEndpoint.prototype.connect = function () {
t.is(this.accessKey, 'myAccessKey');
};
return TestEndpoint;
}(APIEndpoint_1.APIEndpoint));
testApi = new API_1.API();
testApi.registerEndpoint('test', TestEndpoint);
accessMethod = new index_1.WebSocketAccessMethod(testApi);
return [4 /*yield*/, accessMethod.createNewSocketEndpoint('mySocketID', 'test', 'myAccessKey')];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}); });
ava_1.default('Throwing AccessDeniedError returns error when connecting to endpoint', function (t) { return __awaiter(_this, void 0, void 0, function () {
var TestEndpoint, testApi, accessMethod, e_6;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
TestEndpoint = /** @class */ (function (_super) {
__extends(TestEndpoint, _super);
function TestEndpoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
TestEndpoint.prototype.connect = function () {
throw new errorTypes_1.AccessDeniedError('Test access denied');
};
return TestEndpoint;
}(APIEndpoint_1.APIEndpoint));
testApi = new API_1.API();
testApi.registerEndpoint('test', TestEndpoint);
accessMethod = new index_1.WebSocketAccessMethod(testApi);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, accessMethod.createNewSocketEndpoint('mySocketID', 'test')];
case 2:
_a.sent();
t.fail();
return [3 /*break*/, 4];
case 3:
e_6 = _a.sent();
t.is(e_6.message, 'Test access denied');
t.pass();
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); });
ava_1.default('Throwing AccessDeniedError returns error when calling action', function (t) { return __awaiter(_this, void 0, void 0, function () {
var TestEndpoint, testApi, accessMethod, ep, e_7;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
TestEndpoint = /** @class */ (function (_super) {
__extends(TestEndpoint, _super);
function TestEndpoint() {
return _super !== null && _super.apply(this, arguments) || this;
}
TestEndpoint.prototype.$test = function () {
throw new errorTypes_1.AccessDeniedError('Test access denied');
};
return TestEndpoint;
}(APIEndpoint_1.APIEndpoint));
testApi = new API_1.API();
testApi.registerEndpoint('test', TestEndpoint);
accessMethod = new index_1.WebSocketAccessMethod(testApi);
return [4 /*yield*/, accessMethod.createNewSocketEndpoint('mySocketID', 'test')];
case 1:
ep = _a.sent();
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, accessMethod.callEndpointAction('mySocketID', ep.endpointConnectionId, 'test')];
case 3:
_a.sent();
t.fail();
return [3 /*break*/, 5];
case 4:
e_7 = _a.sent();
t.is(e_7.message, 'Test access denied');
t.pass();
return [3 /*break*/, 5];
case 5: return [2 /*return*/];
}
});
}); });
//# sourceMappingURL=WebSocketAccessMethod.test.js.map

@@ -11,2 +11,3 @@ export declare enum paramType {

private emitHandler;
accessKey: string;
actionExists(actionName: string): boolean;

@@ -13,0 +14,0 @@ actionParams(actionName: string): {

@@ -49,2 +49,3 @@ "use strict";

this.connected = false;
this.accessKey = null;
}

@@ -88,3 +89,2 @@ APIEndpoint.prototype.actionExists = function (actionName) {

};
//TODO: Test
APIEndpoint.prototype.callConnect = function () {

@@ -91,0 +91,0 @@ return __awaiter(this, void 0, void 0, function () {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var endpointRegex = /^[-_.a-z0-9]+$/;
var endpointRegex = /^[-_\.\/a-z0-9]+$/;
function isValidEndpointName(endpointName) {

@@ -5,0 +5,0 @@ endpointName = endpointName.toLowerCase();

@@ -16,6 +16,6 @@ "use strict";

t.true(isValidEndpointName_1.isValidEndpointName('__test__'));
t.true(isValidEndpointName_1.isValidEndpointName('test/users'));
//Invalid names
t.false(isValidEndpointName_1.isValidEndpointName('test users'));
t.false(isValidEndpointName_1.isValidEndpointName('test users'));
t.false(isValidEndpointName_1.isValidEndpointName('test/users'));
t.false(isValidEndpointName_1.isValidEndpointName('test=users'));

@@ -22,0 +22,0 @@ t.false(isValidEndpointName_1.isValidEndpointName('Hellø'));

@@ -41,2 +41,3 @@ "use strict";

var CalculatorEndpoint_1 = require("./CalculatorEndpoint");
var delay_1 = require("../util/delay");
ava_1.default('Add basic numbers', function (t) { return __awaiter(_this, void 0, void 0, function () {

@@ -201,2 +202,56 @@ var calc, value;

}); });
ava_1.default('Watches adds', function (t) { return __awaiter(_this, void 0, void 0, function () {
var calc, value;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
calc = new CalculatorEndpoint_1.CalculatorEndpoint();
return [4 /*yield*/, calc.callConnect()];
case 1:
_a.sent();
calc.registerEmitHandler(function (eventName, args) {
t.is(eventName, 'addCalculationPerformed');
t.is(args[0], 3);
t.pass();
});
return [4 /*yield*/, calc.callAction('watchAdds')];
case 2:
value = _a.sent();
t.deepEqual(value, { done: true });
return [4 /*yield*/, calc.callAction('add', { a: 1, b: 2 })];
case 3:
_a.sent();
return [4 /*yield*/, delay_1.delay(10)];
case 4:
_a.sent();
return [2 /*return*/];
}
});
}); });
ava_1.default('Fails to watch adds if the connection doenst allow emitting', function (t) { return __awaiter(_this, void 0, void 0, function () {
var calc, e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
calc = new CalculatorEndpoint_1.CalculatorEndpoint();
return [4 /*yield*/, calc.callConnect()];
case 1:
_a.sent();
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, calc.callAction('watchAdds')];
case 3:
_a.sent();
t.fail();
return [3 /*break*/, 5];
case 4:
e_1 = _a.sent();
t.is(e_1.message, 'Cannot watch adds from and endpoint that cannot receive events');
t.pass();
return [3 /*break*/, 5];
case 5: return [2 /*return*/];
}
});
}); });
//# sourceMappingURL=CalculatorEndpoint.test.js.map

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc