backtrace-service
Advanced tools
Comparing version 1.1.10 to 1.1.12
/** | ||
* getBackupConfig fetches a config file provided by the service itself. | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
export declare function getBackupConfig(serviceName: string): object; | ||
/** | ||
* getProperConfig fetches from the expected place on the machine (outside the service). | ||
* @param service | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
export declare function getBackupConfig(serviceName: string): object; | ||
export declare function getProperConfig(serviceName: string): object | undefined; | ||
/** | ||
* getConfig fetches from the expected place on the machine (outside the service). | ||
* If configuration doesn't exists then getConfig method will try to fetch configuration from internal service path | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
export declare function getConfig(serviceName: string): object; |
@@ -14,4 +14,3 @@ "use strict"; | ||
* getBackupConfig fetches a config file provided by the service itself. | ||
* getProperConfig fetches from the expected place on the machine (outside the service). | ||
* @param service | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
@@ -31,2 +30,6 @@ function getBackupConfig(serviceName) { | ||
exports.getBackupConfig = getBackupConfig; | ||
/** | ||
* getProperConfig fetches from the expected place on the machine (outside the service). | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
function getProperConfig(serviceName) { | ||
@@ -43,2 +46,7 @@ try { | ||
exports.getProperConfig = getProperConfig; | ||
/** | ||
* getConfig fetches from the expected place on the machine (outside the service). | ||
* If configuration doesn't exists then getConfig method will try to fetch configuration from internal service path | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
function getConfig(serviceName) { | ||
@@ -45,0 +53,0 @@ var config = getProperConfig(serviceName); |
import { NextFunction, Request, Response } from 'express'; | ||
import { IAuthRequestOptions, ICoronerRequestOption } from './model/authRequestOptions'; | ||
import { ICoronerRequestOption } from './model/authRequestOptions'; | ||
import { IServerConfiguration } from './model/serverConfiguration'; | ||
@@ -8,2 +8,12 @@ /** | ||
export declare class IdentityManager { | ||
name: string; | ||
secret: string; | ||
logger: { | ||
log: (level: string, log: string) => void; | ||
} | undefined; | ||
constructor(name: string, secret: string); | ||
setLogger(logger: { | ||
log: (level: string, msg: string) => void; | ||
}): void; | ||
private log; | ||
serviceRequest(opts: ICoronerRequestOption): ((request: Request, response: Response, next: NextFunction) => void)[]; | ||
@@ -15,5 +25,4 @@ /** | ||
* Upon success, the configuration data will be in req.coronerAuth.cfg. | ||
* @param opts AuthRequestOptions - service name and service request | ||
*/ | ||
authenticateRequest(opts: IAuthRequestOptions): (request: Request, response: Response, next: NextFunction) => void; | ||
authenticateRequest(): (request: Request, response: Response, next: NextFunction) => void; | ||
/** | ||
@@ -26,2 +35,7 @@ * Check if authtoken is a valid token generated by coronerd | ||
/** | ||
* Login to a coronerd. | ||
* @param Coronerd base URL to login to | ||
*/ | ||
loginCoronerd(coronerdUrl: string): Promise<IServerConfiguration | undefined>; | ||
/** | ||
* Get Backtrace configuration | ||
@@ -28,0 +42,0 @@ * @param universeUrl url to coronerd universe |
@@ -49,13 +49,24 @@ "use strict"; | ||
var IdentityManager = /** @class */ (function () { | ||
function IdentityManager() { | ||
function IdentityManager(name, secret) { | ||
this.name = name; | ||
this.secret = secret; | ||
this.logger = undefined; | ||
} | ||
IdentityManager.prototype.setLogger = function (logger) { | ||
if (!logger || !logger.log) | ||
throw new TypeError('unusable logger passed in'); | ||
this.logger = logger; | ||
}; | ||
IdentityManager.prototype.log = function (level, msg) { | ||
if (this.logger) | ||
this.logger.log(level, msg); | ||
}; | ||
/* | ||
* Generate a middleware handler for Express.js, using options to hook the | ||
* service up. | ||
* | ||
* @param opts ICoronerRequestOption - coronerd url callback if desired | ||
*/ | ||
IdentityManager.prototype.serviceRequest = function (opts) { | ||
var _this = this; | ||
if (!opts || !opts.name || !opts.secret) { | ||
throw new TypeError('Missing required parameters in method options'); | ||
} | ||
/* | ||
@@ -83,7 +94,7 @@ * Handle <prefix>/service requests. These require action QS. Other QS | ||
} | ||
if (!_this.checkHmac(opts.secret, nonce, hmac)) { | ||
if (!_this.checkHmac(_this.secret, nonce, hmac)) { | ||
responseResult_1.ResponseResult.unauthorized(response); | ||
return; | ||
} | ||
if (opts.coronerdCallback) { | ||
if (opts && opts.coronerdCallback) { | ||
opts.coronerdCallback(url); | ||
@@ -108,15 +119,5 @@ } | ||
* Upon success, the configuration data will be in req.coronerAuth.cfg. | ||
* @param opts AuthRequestOptions - service name and service request | ||
*/ | ||
IdentityManager.prototype.authenticateRequest = function (opts) { | ||
IdentityManager.prototype.authenticateRequest = function () { | ||
var _this = this; | ||
if (!opts || !opts.name || !opts.secret) { | ||
throw new TypeError('Missing required parameters in method options'); | ||
} | ||
var logger = function (level, msg) { | ||
if (!opts.logger || !opts.logger.log) { | ||
return; | ||
} | ||
opts.logger.log(level, msg); | ||
}; | ||
return function (request, response, next) { | ||
@@ -133,3 +134,3 @@ var data = request.coronerAuth; | ||
if (!token || !url) { | ||
logger('error', request.ip + ": missing internal params token: " + token + " || url: " + url); | ||
_this.log('error', request.ip + ": missing internal params token: " + token + " || url: " + url); | ||
responseResult_1.ResponseResult.badRequest(response, 'missing parameters'); | ||
@@ -143,5 +144,5 @@ return; | ||
headers: { | ||
'X-Service-Name': opts.name, | ||
'X-Service-Name': _this.name, | ||
'X-Coroner-Token': token, | ||
'X-Service-HMAC': _this.generateHmac(opts.secret, token), | ||
'X-Service-HMAC': _this.generateHmac(_this.secret, token), | ||
}, | ||
@@ -158,3 +159,3 @@ }) | ||
if (!!coronerd_nonce && !!coronerd_hmac) { | ||
if (!_this.checkHmac(opts.secret, coronerd_nonce, coronerd_hmac)) { | ||
if (!_this.checkHmac(_this.secret, coronerd_nonce, coronerd_hmac)) { | ||
responseResult_1.ResponseResult.badRequest(response, 'missing parameters'); | ||
@@ -201,2 +202,29 @@ next(new Error('Invalid server generated HMAC')); | ||
/** | ||
* Login to a coronerd. | ||
* @param Coronerd base URL to login to | ||
*/ | ||
IdentityManager.prototype.loginCoronerd = function (coronerdUrl) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var requestUrl, nonce, hmac, result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
requestUrl = coronerdUrl + "/api/login"; | ||
nonce = crypto_1.randomBytes(32).toString('hex'); | ||
hmac = this.generateHmac(this.secret, nonce); | ||
return [4 /*yield*/, axios_1.default.post(requestUrl, null, { | ||
headers: { | ||
'X-Service-Name': this.name, | ||
'X-Service-Nonce': nonce, | ||
'X-Service-HMAC': hmac, | ||
}, | ||
})]; | ||
case 1: | ||
result = _a.sent(); | ||
return [2 /*return*/, result.status === 200 ? result.data : undefined]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Get Backtrace configuration | ||
@@ -236,9 +264,9 @@ * @param universeUrl url to coronerd universe | ||
}; | ||
IdentityManager.prototype.generateHmac = function (secret, authToken) { | ||
IdentityManager.prototype.generateHmac = function (secret, nonce) { | ||
return crypto_1.createHmac('sha256', secret) | ||
.update(authToken) | ||
.update(nonce) | ||
.digest('hex'); | ||
}; | ||
IdentityManager.prototype.checkHmac = function (secret, authToken, proposedHMac) { | ||
return this.generateHmac(secret, authToken) === proposedHMac; | ||
IdentityManager.prototype.checkHmac = function (secret, nonce, proposedHMac) { | ||
return this.generateHmac(secret, nonce) === proposedHMac; | ||
}; | ||
@@ -245,0 +273,0 @@ return IdentityManager; |
@@ -17,7 +17,7 @@ export interface IAuthRequestOptions { | ||
} | ||
export interface ICoronerRequestOption extends IAuthRequestOptions { | ||
export interface ICoronerRequestOption { | ||
/** | ||
* Coronerd callback | ||
*/ | ||
coronerdCallback: (url: string) => void; | ||
coronerdCallback: (url: string) => void | undefined; | ||
} | ||
@@ -24,0 +24,0 @@ export interface ICoronerAuth { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=authRequestOptions.js.map |
@@ -0,0 +0,0 @@ export interface IConfig { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=serverConfiguration.js.map |
@@ -13,3 +13,3 @@ /** | ||
*/ | ||
export { UnvierseHelper } from './universe/backtraceUniverseHelper'; | ||
export { UniverseHelper } from './universe/backtraceUniverseHelper'; | ||
export { getBackupConfig, getConfig, getProperConfig } from './config/config'; |
@@ -19,3 +19,3 @@ "use strict"; | ||
var backtraceUniverseHelper_1 = require("./universe/backtraceUniverseHelper"); | ||
exports.UnvierseHelper = backtraceUniverseHelper_1.UnvierseHelper; | ||
exports.UniverseHelper = backtraceUniverseHelper_1.UniverseHelper; | ||
var config_1 = require("./config/config"); | ||
@@ -22,0 +22,0 @@ exports.getBackupConfig = config_1.getBackupConfig; |
export declare function info(log: string): void; | ||
export declare function warning(log: string): void; | ||
export declare function error(log: string): void; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { Response } from 'express'; |
@@ -0,0 +0,0 @@ "use strict"; |
/** | ||
* Backtrae universe utils | ||
*/ | ||
export declare class UnvierseHelper { | ||
export declare class UniverseHelper { | ||
/** | ||
@@ -20,3 +20,3 @@ * return Backtrace host url | ||
*/ | ||
static getBacktaceUnivereName(urlString: string): string | undefined; | ||
static getBacktaceUniverseName(urlString: string): string | undefined; | ||
} |
@@ -14,4 +14,4 @@ "use strict"; | ||
*/ | ||
var UnvierseHelper = /** @class */ (function () { | ||
function UnvierseHelper() { | ||
var UniverseHelper = /** @class */ (function () { | ||
function UniverseHelper() { | ||
} | ||
@@ -25,3 +25,3 @@ /** | ||
*/ | ||
UnvierseHelper.getBacktraceHostName = function (urlString) { | ||
UniverseHelper.getBacktraceHostName = function (urlString) { | ||
if (!urlString) { | ||
@@ -40,3 +40,3 @@ return undefined; | ||
*/ | ||
UnvierseHelper.getBacktaceUnivereName = function (urlString) { | ||
UniverseHelper.getBacktaceUniverseName = function (urlString) { | ||
if (!urlString) { | ||
@@ -53,5 +53,5 @@ return undefined; | ||
}; | ||
return UnvierseHelper; | ||
return UniverseHelper; | ||
}()); | ||
exports.UnvierseHelper = UnvierseHelper; | ||
exports.UniverseHelper = UniverseHelper; | ||
//# sourceMappingURL=backtraceUniverseHelper.js.map |
{ | ||
"name": "backtrace-service", | ||
"version": "1.1.10", | ||
"version": "1.1.12", | ||
"description": "Common tools for Backtrace Node services", | ||
@@ -5,0 +5,0 @@ "author": "Backtrace", |
294
README.md
# Backtrace Service Layer nodejs library | ||
This library consists of a single express.js middleware function. It may | ||
contain other common Service Layer related capabilities later. | ||
Library of common functions needed for Node.js Backtrace services. | ||
## Quick Example | ||
## Getting started | ||
``` | ||
$ npm install | ||
$ npm run build | ||
$ npm run test | ||
``` | ||
Then in your code you can include by using | ||
```typescript | ||
import * as backtrace-service from 'backtrace-service' | ||
``` | ||
or use require instead | ||
```typescript | ||
const backtraceService = require('backtrace-service'); | ||
``` | ||
## Service layer integration | ||
To start using Service Layer Integration you have to create new object of a `IdentityManager` class. | ||
After that you can use `serviceRequest` and `authenticateRequest` methods. | ||
### Service flow | ||
Services are expected to configure one or more authentication tokens, which will be distributed to | ||
coronerd instances using the service. While a service is running, it may receive registration | ||
requests at `${url_prefix}/service`, which enable the service to integrate new coronerd instances on | ||
the fly. | ||
Coronerd URLs provided as the `url` parameter to `coronerdCallback` are the base URLs, for example, | ||
`https://backtrace.sp.backtrace.io/`. Services may expect to append the appropriate resource for | ||
their needs, e.g. `/api/config`, to reach that resource on the coronerd instance. | ||
What data coronerd will send to service to register? Coronerd via HTTP POST will send in a request | ||
body: | ||
- action - action name (for example register), | ||
- url - coroner url (for example `https://yolo.sp.backtrace.io/`) | ||
- nonce - auth token | ||
- hmac - Hash Message Authentication Code | ||
#### Quick Example | ||
`TypeScript:` | ||
```typescript | ||
@@ -12,6 +57,6 @@ import { ICoronerRequestOption, IdentityManager } from 'backtrace-service'; | ||
const identityManager = new IdentityManager(); | ||
const idm = new IdentityManager(serviceName, serviceSecret); | ||
app.post( | ||
'/api/{serviceName}/service', | ||
identityManager.serviceRequest({ | ||
idm.serviceRequest({ | ||
name: serviceName, | ||
@@ -28,11 +73,16 @@ secret: serviceSecret, | ||
`JavaScript:` | ||
```javascript | ||
const identityManager = require('backtrace-service').IdentityManager; | ||
app.post("${url_prefix}/service", btservice.serviceRequest({ | ||
name: 'simple', | ||
secret: 'asdfghjk', | ||
coronerdCallback: (url) => { | ||
console.log('heard from coronerd at: ' + url); | ||
}, | ||
})); | ||
const btservice = require('backtrace-service'); | ||
const idm = new btservice.IdentityManager(serviceName, serviceSecret); | ||
app.post( | ||
'${url_prefix}/service', | ||
idm.serviceRequest({ | ||
name: 'simple', | ||
secret: 'asdfghjk', | ||
coronerdCallback: (url) => { | ||
console.log('heard from coronerd at: ' + url); | ||
}, | ||
}), | ||
); | ||
``` | ||
@@ -43,37 +93,26 @@ | ||
The following options are accepted as the sole argument for the call: | ||
* name: Name of the service (usually its type). | ||
* secret: The shared secret that the service will use to authenticate | ||
incoming requests and config replies from a coronerd. | ||
* coronerdCallback: A callback that takes an URL parameter, and performs any | ||
service specific setup associated with integrating a new coronerd instance. | ||
* logger (optional): An object that has a `log` function which can be logged | ||
to. For example, winston logger instances. | ||
## Service flow | ||
- name: Name of the service (usually its type). | ||
- secret: The shared secret that the service will use to authenticate incoming requests and config | ||
replies from a coronerd. | ||
- coronerdCallback: A callback that takes a URL parameter, and performs any service specific setup | ||
associated with integrating a new coronerd instance. | ||
- logger (optional): An object that has a `log` function which can be logged to. For example, | ||
winston logger instances. | ||
Services are expected to configure one or more authentication tokens, which | ||
will be distributed to coronerd instances using the service. While a | ||
service is running, it may receive registration requests at | ||
`${url_prefix}/service`, which enable the service to integrate new coronerd | ||
instances on the fly. | ||
### authenticateRequest usage | ||
Coronerd URLs provided as the `url` parameter to `coronerdCallback` are the | ||
base URLs, for example, `https://backtrace.sp.backtrace.io/`. Services may | ||
expect to append the appropriate resource for their needs, e.g. | ||
`/api/config`, to reach that resource on the coronerd instance. | ||
This function is intended as an additional middleware which may be used in application routes to | ||
validate requests that involve a session token issued by a remote coronerd. The actual call can | ||
reuse the same options argument used for `serviceRequest`, although it does not use | ||
`coronerdCallback`. | ||
## authenticateRequest usage | ||
In the route middleware list, prior to `authenticateRequest`, the application must attach a | ||
`req.coronerAuth` object which contains: | ||
This function is intended as an additional middleware which may be used in | ||
application routes to validate requests that involve a session token issued | ||
by a remote coronerd. The actual call can reuse the same options argument | ||
used for `serviceRequest`, although it does not use `coronerdCallback`. | ||
- url: The full URL to the remote coronerd instance. | ||
- token: The user's session token to be validated. | ||
In the route middleware list, prior to `authenticateRequest`, the | ||
application must attach a `req.coronerAuth` object which contains: | ||
For example: | ||
* url: The full URL to the remote coronerd instance. | ||
* token: The user's session token to be validated. | ||
For example: | ||
``` | ||
@@ -86,6 +125,171 @@ req.coronerAuth = { | ||
This normalized form is used due to the fact that different services take | ||
these parameters from clients in different ways. | ||
If `req.coronerAuth` object is undefined, `authenticateRequest` method will try to retrieve token | ||
and location information from headers. If you prefer to use headers instead of extending request | ||
object please set `X-Coroner-Token` and `X-Coroner-Location` | ||
Middlewares that come after `authenticateRequest` will have access to the | ||
validated coronerd `/api/config` response in `req.coronerAuth.cfg`. | ||
This normalized form is used due to the fact that different services take these parameters from | ||
clients in different ways. | ||
Middlewares that come after `authenticateRequest` will have access to the validated coronerd | ||
`/api/config` response in `req.coronerAuth.cfg`. | ||
#### Quick Example | ||
Example of sample middleware | ||
`TypeScript:` | ||
```typescript | ||
import { ICoronerRequestOption, IdentityManager } from 'backtrace-service'; | ||
... | ||
@Middleware({ type: 'before' }) | ||
export class AuthMiddleware implements ExpressMiddlewareInterface { | ||
private readonly _identityManager = new IdentityManager(serviceName, serviceSecret); | ||
public use(req: express.Request, res: express.Response, next: express.NextFunction): any { | ||
return this._identityManager.authenticateRequest()(req, res, next); | ||
} | ||
} | ||
``` | ||
In example above middleware will try to retrieve `X-Coroner-Token` and `X-Coroner-Location` from | ||
request headers. | ||
`JavaScript:` | ||
```javascript | ||
const idm = new btservice.IdentityManager(); | ||
// create express app... | ||
// prepare utility method | ||
function prepAuth(req, res, next) { | ||
const auth = { | ||
token: req.get('X-Coroner-Token'), | ||
url: req.get('X-Coroner-Location'), | ||
}; | ||
req.coronerAuth = auth; | ||
next(); | ||
} | ||
// create application options | ||
const svcOpts = { | ||
name: 'simple', | ||
secret: 'asdfghjk', | ||
coronerdCallback: (url) => { | ||
log('info', 'heard: ' + url); | ||
}, | ||
logger: logger, | ||
}; | ||
app.post('/api/{name}/{action}', prepAuth, idm.authenticateRequest(svcOpts), (req, res) => { | ||
res.json(req.coronerAuth); | ||
}); | ||
``` | ||
## Utils | ||
Backtrace-Service tools offers few utility methods. By using them we want to standarize how we read | ||
application configurations/log application messages/create custom auth methods. | ||
### Identity Manager | ||
Service Layer Integration using `IdentityManager` class to provide integrations methods with | ||
coronerd. If you want to write auth method to coronerd or check if token is valid you can use | ||
methods available in a`IdentityManager` instance. | ||
#### Quick Example: | ||
```typescript | ||
import { IdentityManager } from 'backtrace-service'; | ||
const token = //auth token | ||
const universeUrl = //universe url: https://yolo.sp.backtrace.io | ||
const identityManager = new IdentityManager(serviceName, serviceSecret); | ||
// get configuration from coronerd | ||
const configuration = await identityManager.getConfiguration(universeUrl, token); | ||
// validate token | ||
const validToken = await identityManager.isValidToken(universeUrl, token) | ||
``` | ||
#### GetConfiguration | ||
`IdentityManager` method that allows you to prepare a request to coronerd to retrieve configuration. | ||
Please keep in mind, if you're using SL integration, configuration should be available in | ||
`request.coronerAuth.cfg`. `GetConfiguration` method using `Promise` API to get data from a server. | ||
#### IsValidToken | ||
`IdentityManager` method that allows you to validate token with coronerd. In this case | ||
`IdentityManager` will return boolean information - `true` if coronerd returns valid configuration, | ||
otherwise false. `IsValidToken` method using `Promise` API. Please keep in mind to use `await` or | ||
`then/catch` syntax to get a result from method. | ||
### Configuration | ||
`Backtrace-Service` package allows you to read service configuration file. To standarize paths used | ||
in your service to configuration files we suggest to use these methods to easily integrate service | ||
with other services. | ||
#### GetBackupConfig | ||
`getBackupConfig` fetches a config file provided by the service itself. Path used to read | ||
configuration: `${process.cwd()}/${serviceName}.conf` | ||
#### GetProperConfig | ||
`getProperConfig` fetches from the expected place on the machine (outside the service). Path used to | ||
read configuration: `/etc/backtrace/${serviceName}/${serviceName}.conf` | ||
### GetConfig | ||
`getConfig` fetches configuration from the expected place on the machine (outside the service). If | ||
configuration doesn't exists then getConfig method will try to fetch configuration from internal | ||
service path Algorithm: | ||
1. Try to fetch configuration from: `/etc/backtrace/${serviceName}/${serviceName}.conf` | ||
2. If configuration from #1 doesn't exists try to read configuration frile from path: | ||
`${process.cwd()}/${serviceName}.conf` | ||
3. Return config or undefined | ||
example: | ||
```typescript | ||
import { getBackupConfig, getProperConfig, getConfig } from 'backtrace-service'; | ||
//read backup configuration - service internal configuration | ||
const serviceConf = getBackupConfig('service-name'); | ||
//read production configuration - outside service | ||
const prodConf = getProperConfig('service-name'); | ||
//get configuration - depends on preferences | ||
const conf = getConfig('service-name'); | ||
``` | ||
### UniverseHelper | ||
Utility methods that allows you to get more information about Backtrace universe from a url. | ||
#### getBacktraceHostName | ||
`GetBacktraceHostName` method allows you to get Backtrace host name from provided `urlString`. For | ||
example - method will return `https://yolo.sp.backtrace.io` from url | ||
`https://yolo.sp.backtrace.io/blah/poop/doop?query=123&anotherQuery=nbgaubakgb` | ||
### getBacktaceUniverseName | ||
`GetBacktaceUniverseName` method allows you to retrieve a Backtrace universe name. For example: | ||
method will return `yolo` from url `https://yolo.sp.backtrace.io` or | ||
`https://submit.backtrace.io/yolo/....` | ||
example: | ||
```typescript | ||
import { UniverseHelper } from 'backtrace-service'; | ||
const url = 'https://yolo.sp.backtrace.io/blah?a=1&b=bgj'; | ||
const url2 = 'https://submit.backtrace.io/bolo/token/json'; | ||
// method below will print a string 'https://yolo.sp.backtrace.io | ||
console.log(UniverseHelper.getBacktraceHostName(url)); | ||
//method below will print a string 'bolo | ||
console.log(UnvierseHelper.getBacktraceUniverseName(url2)); | ||
``` |
@@ -6,4 +6,3 @@ import * as fs from 'fs'; | ||
* getBackupConfig fetches a config file provided by the service itself. | ||
* getProperConfig fetches from the expected place on the machine (outside the service). | ||
* @param service | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
@@ -22,2 +21,6 @@ export function getBackupConfig(serviceName: string): object { | ||
/** | ||
* getProperConfig fetches from the expected place on the machine (outside the service). | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
export function getProperConfig(serviceName: string): object | undefined{ | ||
@@ -36,2 +39,9 @@ try { | ||
/** | ||
* getConfig fetches from the expected place on the machine (outside the service). | ||
* If configuration doesn't exists then getConfig method will try to fetch configuration from internal service path | ||
* @param serviceName - service name - for example: share/saml/comments | ||
*/ | ||
export function getConfig(serviceName: string): object { | ||
@@ -38,0 +48,0 @@ let config = getProperConfig(serviceName); |
import axios from 'axios'; | ||
import bodyParser from 'body-parser'; | ||
import { createHmac } from 'crypto'; | ||
import { createHmac, randomBytes } from 'crypto'; | ||
import { NextFunction, Request, Response } from 'express'; | ||
@@ -17,11 +17,30 @@ import { ResponseResult } from '../model/responseResult'; | ||
export class IdentityManager { | ||
name: string; | ||
secret: string; | ||
logger: { log: (level: string, log: string) => void } | undefined; | ||
constructor(name: string, secret: string) { | ||
this.name = name; | ||
this.secret = secret; | ||
this.logger = undefined; | ||
} | ||
public setLogger(logger: { log: (level: string, msg: string) => void }) { | ||
if (!logger || !logger.log) | ||
throw new TypeError('unusable logger passed in'); | ||
this.logger = logger; | ||
} | ||
private log(level: string, msg: string) { | ||
if (this.logger) | ||
this.logger.log(level, msg); | ||
} | ||
/* | ||
* Generate a middleware handler for Express.js, using options to hook the | ||
* service up. | ||
* | ||
* @param opts ICoronerRequestOption - coronerd url callback if desired | ||
*/ | ||
public serviceRequest(opts: ICoronerRequestOption) { | ||
if (!opts || !opts.name || !opts.secret) { | ||
throw new TypeError('Missing required parameters in method options'); | ||
} | ||
/* | ||
@@ -58,3 +77,3 @@ * Handle <prefix>/service requests. These require action QS. Other QS | ||
if (!this.checkHmac(opts.secret, nonce, hmac)) { | ||
if (!this.checkHmac(this.secret, nonce, hmac)) { | ||
ResponseResult.unauthorized(response); | ||
@@ -64,3 +83,3 @@ return; | ||
if (opts.coronerdCallback) { | ||
if (opts && opts.coronerdCallback) { | ||
opts.coronerdCallback(url); | ||
@@ -88,15 +107,4 @@ } | ||
* Upon success, the configuration data will be in req.coronerAuth.cfg. | ||
* @param opts AuthRequestOptions - service name and service request | ||
*/ | ||
public authenticateRequest(opts: IAuthRequestOptions) { | ||
if (!opts || !opts.name || !opts.secret) { | ||
throw new TypeError('Missing required parameters in method options'); | ||
} | ||
const logger = (level: string, msg: string) => { | ||
if (!opts.logger || !opts.logger.log) { | ||
return; | ||
} | ||
opts.logger.log(level, msg); | ||
}; | ||
public authenticateRequest() { | ||
return (request: Request, response: Response, next: NextFunction) => { | ||
@@ -113,3 +121,3 @@ let data = (request as any).coronerAuth as ICoronerAuth; | ||
if (!token || !url) { | ||
logger('error', `${request.ip}: missing internal params token: ${token} || url: ${url}`); | ||
this.log('error', `${request.ip}: missing internal params token: ${token} || url: ${url}`); | ||
ResponseResult.badRequest(response, 'missing parameters'); | ||
@@ -124,5 +132,5 @@ return; | ||
headers: { | ||
'X-Service-Name': opts.name, | ||
'X-Service-Name': this.name, | ||
'X-Coroner-Token': token, | ||
'X-Service-HMAC': this.generateHmac(opts.secret, token), | ||
'X-Service-HMAC': this.generateHmac(this.secret, token), | ||
}, | ||
@@ -140,3 +148,3 @@ }) | ||
if (!!coronerd_nonce && !!coronerd_hmac) { | ||
if (!this.checkHmac(opts.secret, coronerd_nonce, coronerd_hmac)) { | ||
if (!this.checkHmac(this.secret, coronerd_nonce, coronerd_hmac)) { | ||
ResponseResult.badRequest(response, 'missing parameters'); | ||
@@ -176,2 +184,22 @@ next(new Error('Invalid server generated HMAC')); | ||
/** | ||
* Login to a coronerd. | ||
* @param Coronerd base URL to login to | ||
*/ | ||
public async loginCoronerd( | ||
coronerdUrl: string | ||
): Promise<IServerConfiguration | undefined> { | ||
const requestUrl = `${coronerdUrl}/api/login`; | ||
const nonce = randomBytes(32).toString('hex'); | ||
const hmac = this.generateHmac(this.secret, nonce); | ||
const result = await axios.post<IServerConfiguration>(requestUrl, null, { | ||
headers: { | ||
'X-Service-Name': this.name, | ||
'X-Service-Nonce': nonce, | ||
'X-Service-HMAC': hmac, | ||
}, | ||
}); | ||
return result.status === 200 ? result.data : undefined; | ||
} | ||
/** | ||
* Get Backtrace configuration | ||
@@ -202,11 +230,11 @@ * @param universeUrl url to coronerd universe | ||
private generateHmac(secret: string, authToken: string) { | ||
private generateHmac(secret: string, nonce: string) { | ||
return createHmac('sha256', secret) | ||
.update(authToken) | ||
.update(nonce) | ||
.digest('hex'); | ||
} | ||
private checkHmac(secret: string, authToken: string, proposedHMac: string) { | ||
return this.generateHmac(secret, authToken) === proposedHMac; | ||
private checkHmac(secret: string, nonce: string, proposedHMac: string) { | ||
return this.generateHmac(secret, nonce) === proposedHMac; | ||
} | ||
} |
@@ -18,7 +18,7 @@ export interface IAuthRequestOptions { | ||
export interface ICoronerRequestOption extends IAuthRequestOptions { | ||
export interface ICoronerRequestOption { | ||
/** | ||
* Coronerd callback | ||
*/ | ||
coronerdCallback: (url: string) => void; | ||
coronerdCallback: (url: string) => void | undefined; | ||
} | ||
@@ -25,0 +25,0 @@ |
@@ -15,3 +15,3 @@ /** | ||
*/ | ||
export { UnvierseHelper } from './universe/backtraceUniverseHelper'; | ||
export { UniverseHelper } from './universe/backtraceUniverseHelper'; | ||
export { getBackupConfig, getConfig, getProperConfig } from './config/config'; |
import chalk from 'chalk'; | ||
export function info(log: string): void { | ||
if (process.env.NODE_ENV !== 'test') { | ||
return console.log(chalk.green(`[ INFO ] ${log}`)); | ||
} else { | ||
return; | ||
} | ||
if (process.env.NODE_ENV !== 'test') { | ||
return console.log(chalk.green(`[ INFO ] ${log}`)); | ||
} else { | ||
return; | ||
} | ||
} | ||
export function warning(log: string): void { | ||
if (process.env.NODE_ENV !== 'test') { | ||
return console.warn(chalk.yellow(`[ WARNING ] ${log}`)); | ||
} else { | ||
return; | ||
} | ||
if (process.env.NODE_ENV !== 'test') { | ||
return console.warn(chalk.yellow(`[ WARNING ] ${log}`)); | ||
} else { | ||
return; | ||
} | ||
} | ||
export function error(log: string): void { | ||
if (process.env.NODE_ENV !== 'test') { | ||
return console.error(chalk.red(`[ ERROR ] ${log}`)); | ||
} else { | ||
return; | ||
} | ||
} | ||
if (process.env.NODE_ENV !== 'test') { | ||
return console.error(chalk.red(`[ ERROR ] ${log}`)); | ||
} else { | ||
return; | ||
} | ||
} |
@@ -0,0 +0,0 @@ import { Response } from 'express'; |
@@ -6,3 +6,3 @@ import * as url from 'url'; | ||
*/ | ||
export class UnvierseHelper { | ||
export class UniverseHelper { | ||
/** | ||
@@ -30,3 +30,3 @@ * return Backtrace host url | ||
*/ | ||
public static getBacktaceUnivereName(urlString: string): string | undefined { | ||
public static getBacktaceUniverseName(urlString: string): string | undefined { | ||
if (!urlString) { | ||
@@ -33,0 +33,0 @@ return undefined; |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
71717
1170
292