@oat-sa/tao-core-sdk
Version:
Core libraries of TAO
159 lines (149 loc) • 6.09 kB
JavaScript
define(['lodash', 'module', 'core/tokenStore', 'core/promiseQueue'], function (_, module, tokenStoreFactory, promiseQueue) { 'use strict';
_ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
module = module && Object.prototype.hasOwnProperty.call(module, 'default') ? module['default'] : module;
tokenStoreFactory = tokenStoreFactory && Object.prototype.hasOwnProperty.call(tokenStoreFactory, 'default') ? tokenStoreFactory['default'] : tokenStoreFactory;
promiseQueue = promiseQueue && Object.prototype.hasOwnProperty.call(promiseQueue, 'default') ? promiseQueue['default'] : promiseQueue;
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2016-2019 (original work) Open Assessment Technologies SA ;
*/
let validateTokensOpt = true;
let clientConfigFetched = false;
const defaults = {
maxSize: 6,
tokenTimeLimit: 1000 * 60 * 24
};
/**
* Stores the security token queue
* @param {Object} [options]
* @param {String} [options.maxSize]
* @param {String} [options.tokenTimeLimit]
* @param {String} [options.initialToken]
* @returns {tokenHandler}
*/
function tokenHandlerFactory(options) {
// Convert legacy parameter:
if (_.isString(options)) {
options = {
initialToken: options
};
}
options = _.defaults({}, options, _.omit(module.config(), 'tokens'), defaults);
// Initialise storage for tokens:
const tokenStore = tokenStoreFactory(options);
/**
* @typedef {Object} tokenHandler
*/
return {
/**
* Gets the next security token from the token queue
* If none are available, it can check the ClientConfig (once only per page)
* Once the token is got, it is erased from the store (because they are single-use by design)
*
* @returns {Promise<String>} the token value
*/
getToken() {
const initialToken = options.initialToken;
const getFirstTokenValue = () => tokenStore.dequeue().then(currentToken => {
if (currentToken) {
return currentToken.value;
}
return null;
});
// If set, initialToken will be provided directly, without using store:
if (initialToken) {
options.initialToken = null;
return Promise.resolve(initialToken);
}
// Some async checks before we go for the token:
return tokenStore.expireOldTokens().then(() => {
if (!clientConfigFetched) {
// Client Config allowed! (first and only time)
return this.getClientConfigTokens().then(getFirstTokenValue);
} else {
return tokenStore.getSize().then(queueSize => {
if (queueSize > 0) {
// Token available, use it
return getFirstTokenValue();
} else if (!validateTokensOpt) {
return this.getClientConfigTokens().then(getFirstTokenValue);
} else {
// No more token options, refresh needed
return Promise.reject(new Error('No tokens available. Please refresh the page.'));
}
});
}
});
},
/**
* Adds a new security token to the token queue
* Internally, old tokens are deleted to keep queue within maximum pool size
* @param {String} newToken
* @returns {Promise<Boolean>} - resolves true if successful
*/
setToken(newToken) {
return tokenStore.enqueue(newToken);
},
/**
* Extracts tokens from the Client Config which should be received on every page load
* @returns {Promise<Boolean>} - resolves true when completed
*/
getClientConfigTokens() {
const {
tokens,
validateTokens
} = module.config();
const clientTokens = (tokens || []).map(serverToken => ({
value: serverToken,
receivedAt: Date.now()
}));
// set validateToken options from the config
validateTokensOpt = validateTokens;
// Record that this function ran:
clientConfigFetched = true;
return Promise.resolve(clientTokens).then(newTokens => {
// Add the fetched tokens to the store
// Uses a promiseQueue to ensure synchronous adding
const setTokenQueue = promiseQueue();
newTokens.forEach(token => setTokenQueue.serie(() => this.setToken(token)));
return setTokenQueue.serie(() => true);
});
},
/**
* Clears the token store
* @returns {Promise<Boolean>} - resolves to true when cleared
*/
clearStore() {
return tokenStore.clear();
},
/**
* Getter for the current queue length
* @returns {Promise<Number>}
*/
getQueueLength() {
return tokenStore.getSize();
},
/**
* Setter for maximum pool size
* @param {Number} size
*/
setMaxSize(size) {
tokenStore.setMaxSize(size);
}
};
}
return tokenHandlerFactory;
});