fluid-client
Version:
Client for accessing www.fluid.us
159 lines (114 loc) • 4.18 kB
JavaScript
'use strict';
/**
* Class for validating response parameters
**/
const constants = require('./constants');
const errors = require('./errors');
const utilities = require('./utilities');
const hash_utils = require('./hash-utils');
const client = require('./base-client');
const parser = require('./parser');
class ResponseValidator {
get isValid() { return this._valid; }
get isSigned() { return this._signed; }
get isExpired() { return this._expired; }
get version() { return this._version; }
get resource() { return this._resource; }
get amount() { return this.checkProperty('amount'); }
get time() { return this.checkProperty('time'); }
get state() { return this.checkProperty('state'); }
constructor(pubId = client.config.pubId,
secretKey = client.config.secretKey) {
this.pubId = pubId;
this.secretKey = secretKey;
}
validateUrl(url) {
this.reset();
const data = parser.parseUrl(url);
if (data)
this.validateDataHash(data, data.resource);
return this;
}
validateParam(param, resource=undefined) {
this.reset();
const data = parser.parseParam(param);
if (data)
this.validateDataHash(data, resource);
return this;
}
////////////////////////////////////////////////////////////////
// internal
validateDataHash(data, resource=undefined) {
utilities.debug('Validating data-hash %j with resource:%s', data, resource);
if (!data)
return;
if (data.pub_id !== this.pubId)
throw new errors.SignatureError('Pub ID mismatch');
const packet = utilities.decode64( data.packet);
let hash = hash_utils.stringToHash(packet);
utilities.debug('Encoded hash is', hash);
hash.resource = this.checkResource(hash.resource, resource);
this._version = data.version;
this._resource = hash.resource;
this.performValidation(hash, data.signature);
}
performValidation(hash, signature) {
this.reset();
hash_utils.checkHashKeys(hash, constants.RESPONSE_KEYS);
this.checkSignature(hash, signature);
this.checkValid(hash.valid);
this._resource = hash.resource;
this._amount = hash.amount && parseInt(hash.amount);
this._state = hash.state;
this._time = parseInt(hash.time);
this._hash = hash;
this._signed = true;
if (this._time)
this.checkExpiry(this._time);
this._valid = true;
}
checkSignature(hash, signature) {
if (utilities.isEmpty(signature))
throw new errors.SignatureError('Signature Required');
const calculated = hash_utils.signHash(hash, this.secretKey);
utilities.debug('Checking signature of %j, given:%s, calculated:%s', hash, signature, calculated);
if (signature !== calculated)
throw new errors.SignatureError('Invalid signature');
}
checkValid(valid) {
if (!valid || valid.toLowerCase()[0] !== 'y')
throw new errors.SignatureError('Signature Invalid');
}
checkExpiry(time) {
const now = Date.now() / 1000;
this._expired = Math.abs(now - time) > client.config.tolerance;
utilities.debug('Checking expiration given:%s now:%s [%s], expired=%s', time, now, new Date(), this._expired);
if (this._expired)
throw new errors.SignatureExpiredError('Token has expired');
}
checkResource(resource1, resource2) {
resource1 = utilities.nullable(resource1);
resource2 = utilities.nullable(resource2);
if (!resource1 && !resource2)
throw new errors.SignatureError('No resource set');
else if (!resource1)
return resource2;
else if (!resource2)
return resource1;
else if (resource1 !== resource2)
throw new errors.SignatureError('Resource does not match');
else
return resource1;
}
checkProperty(prop) {
if (prop === 'time' && this._expired)
throw new errors.SignatureExpiredError('Expired');
if (!this._valid)
throw new errors.SignatureError('Not validated');
return this[`_${prop}`];
}
reset() {
this._valid = this.signed = this.expired = undefined;
}
}
module.exports = ResponseValidator;