From d227e191077f6087ca1ff1808ad5c2bc2878e7b5 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 10:52:20 -0500 Subject: [PATCH 01/15] Add support for logging in to SciServer directly. Closes #1999 --- src/common/compute/backends/ComputeClient.js | 3 +- .../backends/sciserver-compute/Client.js | 9 +-- .../backends/sciserver-compute/metadata.json | 22 +++--- src/common/sciserver-auth.js | 74 +++++-------------- src/common/storage/backends/StorageClient.js | 3 +- .../backends/sciserver-files/Client.js | 5 +- .../backends/sciserver-files/metadata.json | 24 +++--- src/plugins/ExecuteJob/ExecuteJob.js | 3 + src/plugins/ImportArtifact/ImportArtifact.js | 1 + src/plugins/ImportArtifact/metadata.json | 2 +- src/routers/SciServerAuth/SciServerAuth.js | 48 ++++++++++-- src/routers/SciServerAuth/Tokens.js | 48 ++++++++++++ 12 files changed, 141 insertions(+), 101 deletions(-) create mode 100644 src/routers/SciServerAuth/Tokens.js diff --git a/src/common/compute/backends/ComputeClient.js b/src/common/compute/backends/ComputeClient.js index 507fbd8c2..ab57fe7b0 100644 --- a/src/common/compute/backends/ComputeClient.js +++ b/src/common/compute/backends/ComputeClient.js @@ -2,8 +2,9 @@ define([], function() { class ComputeClient { - constructor (logger, blobClient) { + constructor (logger, blobClient, config) { this.logger = logger.fork('compute'); + this.userId = config.userId; this.blobClient = blobClient; this._events = {}; } diff --git a/src/common/compute/backends/sciserver-compute/Client.js b/src/common/compute/backends/sciserver-compute/Client.js index 9d8c6a037..43c80b5a3 100644 --- a/src/common/compute/backends/sciserver-compute/Client.js +++ b/src/common/compute/backends/sciserver-compute/Client.js @@ -17,7 +17,7 @@ define([ module, path, fetch, - login, + getToken, PREPARE_AND_RUN, ) { const Headers = fetch.Headers; @@ -26,7 +26,6 @@ define([ constructor(logger, blobClient, config) { super(logger, blobClient, config); this.username = config.username; - this.password = config.password; this.computeDomain = config.computeDomain; this.previousJobState = {}; this.consoleOutputLen = {}; @@ -66,7 +65,6 @@ define([ const metadata = await this.blobClient.getMetadata(hash); const config = { username: this.username, - password: this.password, volume: `${this.username}/scratch`, volumePool: 'Temporary' }; @@ -117,7 +115,7 @@ define([ } async token () { - return login(this.username, this.password); + return getToken(this.username, this.userId); } async getJobState (jobInfo) { @@ -234,11 +232,10 @@ define([ _getStorageConfigAndDataInfo (filepath) { const dirs = filepath.split('/').slice(4); let [volumePool, owner, volume] = dirs.slice(0, 3); - const password = this.password; volume = owner + '/' + volume; const filename = dirs.slice(3).join('/'); return { - config: {username: this.username, volumePool, password, volume}, + config: {username: this.username, volumePool, volume}, dataInfo: { data: {filename, volume, volumePool} } diff --git a/src/common/compute/backends/sciserver-compute/metadata.json b/src/common/compute/backends/sciserver-compute/metadata.json index 7f02b5f7c..2c2c2afa2 100644 --- a/src/common/compute/backends/sciserver-compute/metadata.json +++ b/src/common/compute/backends/sciserver-compute/metadata.json @@ -7,19 +7,17 @@ "displayName": "Username", "description": "SciServer username", "value": "", - "valueType": "string", - "readOnly": false - }, - { - "name": "password", - "displayName": "Password", - "description": "SciServer password", - "value": "", - "valueType": "string", + "valueType": "stringX", + "valueItemsURL": "/routers/SciServerAuth", + "extraValueItems": [ + { + "name": "Link account...", + "type": "URL", + "value": "https://apps.sciserver.org/login-portal/login?callbackUrl=<%= window.location.origin %>/routers/SciServerAuth/register" + } + ], "readOnly": false, - "options": { - "isPassword": true - } + "isAuth": true }, { "name": "computeDomain", diff --git a/src/common/sciserver-auth.js b/src/common/sciserver-auth.js index edbfd126e..9487b5fb4 100644 --- a/src/common/sciserver-auth.js +++ b/src/common/sciserver-auth.js @@ -10,70 +10,30 @@ root.CONSTANTS = factory(); } }(this, function() { - const LOGIN_URL = 'https://apps.sciserver.org/login-portal/keystone/v3/tokens'; const isBrowser = typeof window !== 'undefined'; - const fetch = isBrowser ? window.fetch : require('node-fetch'); - const Headers = isBrowser ? window.Headers : fetch.Headers; + const TokenStorage = isBrowser ? null : require('../routers/SciServerAuth/Tokens'); - async function loginViaProxy(username, password) { - const url = '/routers/SciServerAuth/token'; - const opts = { - method: 'POST', - headers: new Headers({ - 'Content-Type': 'application/json' - }), - body: JSON.stringify({username, password}) - }; - const response = await fetch(url, opts); - return await response.text(); - } - - async function fetchNewToken(username, password) { - if (isBrowser) { - return loginViaProxy(username, password); - } - - const url = `${LOGIN_URL}?TaskName=DeepForge.Authentication.Login`; - const opts = { - method: 'POST', - headers: new Headers({ - 'Content-Type': 'application/json' - }), - body: getLoginBody(username, password) - }; - const response = await fetch(url, opts); - return response.headers.get('X-Subject-Token'); - } - - const tokens = {}; - const hours = 1000*60*60; - function login(username, password) { - tokens[username] = tokens[username] || {}; - if (!tokens[username][password]) { - tokens[username][password] = fetchNewToken(username, password); - setTimeout(clearToken.bind(null, username, password), 23*hours); + async function getTokenBrowser(ssUser) { + const url = `/routers/SciServerAuth/${ssUser}/token`; + const response = await fetch(url); + if (response.status < 400) { + return await response.text(); + } else { + throw new Error(await response.text()); } - return tokens[username][password]; } - function clearToken(username, password) { - delete tokens[username][password]; + async function getTokenNodeJS(ssUser, dfUser) { + return await TokenStorage.getToken(dfUser, ssUser); } - function getLoginBody(username, password) { - return JSON.stringify({ - auth: { - identity: { - password: { - user: { - name: username, - password: password - } - } - } - } - }); + async function getToken(ssUser, dfUser) { + if (isBrowser) { + return getTokenBrowser(ssUser); + } else { + return getTokenNodeJS(ssUser, dfUser); + } } - return login; + return getToken; })); diff --git a/src/common/storage/backends/StorageClient.js b/src/common/storage/backends/StorageClient.js index f1178d5d1..ab8c7e7db 100644 --- a/src/common/storage/backends/StorageClient.js +++ b/src/common/storage/backends/StorageClient.js @@ -10,9 +10,10 @@ define([ require.nodeRequire('node-fetch'); const Headers = require.isBrowser ? window.Headers : fetch.Headers; const stream = require.isBrowser ? null : require.nodeRequire('stream'); - const StorageClient = function(id, name, logger) { + const StorageClient = function(id, name, logger, config) { this.id = id; this.name = name; + this.userId = config.userId; if (!logger) { logger = Logger.create(`gme:storage:${id}`, gmeConfig.client.log); } diff --git a/src/common/storage/backends/sciserver-files/Client.js b/src/common/storage/backends/sciserver-files/Client.js index a36c0760e..9a1b71388 100644 --- a/src/common/storage/backends/sciserver-files/Client.js +++ b/src/common/storage/backends/sciserver-files/Client.js @@ -4,13 +4,12 @@ define([ 'deepforge/sciserver-auth', ], function ( StorageClient, - login, + getToken, ) { const BASE_URL = 'https://apps.sciserver.org/fileservice/api/'; const SciServerFiles = function (id, name, logger, config = {}) { StorageClient.apply(this, arguments); this.username = config.username; - this.password = config.password; this.volumePool = config.volumePool || 'Storage'; this.volume = (config.volume || '').replace(/^Storage\//, ''); }; @@ -84,7 +83,7 @@ define([ }; SciServerFiles.prototype.fetch = async function (action, url, opts = {}) { - const token = await login(this.username, this.password); + const token = await getToken(this.username, this.userId); opts.headers = opts.headers || {}; opts.headers['X-Auth-Token'] = token; try { diff --git a/src/common/storage/backends/sciserver-files/metadata.json b/src/common/storage/backends/sciserver-files/metadata.json index e62794587..2099f7ad8 100644 --- a/src/common/storage/backends/sciserver-files/metadata.json +++ b/src/common/storage/backends/sciserver-files/metadata.json @@ -4,24 +4,20 @@ { "name": "username", "displayName": "Username", - "description": "SciServer username", + "description": "SciServer account to use", "value": "", - "valueType": "string", + "valueType": "stringX", + "valueItemsURL": "/routers/SciServerAuth", + "extraValueItems": [ + { + "name": "Link account...", + "type": "URL", + "value": "https://apps.sciserver.org/login-portal/login?callbackUrl=<%= window.location.origin %>/routers/SciServerAuth/register" + } + ], "readOnly": false, "isAuth": true }, - { - "name": "password", - "displayName": "Password", - "description": "SciServer password", - "value": "", - "valueType": "string", - "readOnly": false, - "isAuth": true, - "options": { - "isPassword": true - } - }, { "name": "volume", "displayName": "Volume", diff --git a/src/plugins/ExecuteJob/ExecuteJob.js b/src/plugins/ExecuteJob/ExecuteJob.js index 46557d334..6c4f39038 100644 --- a/src/plugins/ExecuteJob/ExecuteJob.js +++ b/src/plugins/ExecuteJob/ExecuteJob.js @@ -164,6 +164,7 @@ define([ ExecuteJob.prototype.createComputeClient = function () { const config = this.getCurrentConfig(); const backend = Compute.getBackend(config.compute.id); + config.compute.config.userId = this.getUserId(); if (config.compute.id === 'gme') { config.compute.config.webgmeToken = this.blobClient.webgmeToken; // HACK } @@ -218,6 +219,7 @@ define([ ExecuteJob.prototype.getStorageClient = async function () { const {storage} = this.getCurrentConfig(); const backend = Storage.getBackend(storage.id); + storage.config.userId = this.getUserId(); return await backend.getClient(this.logger, storage.config); }; @@ -235,6 +237,7 @@ define([ ExecuteJob.prototype.getStorageClientForInputData = async function (dataInfo) { const configDict = await this.getInputStorageConfigs(); const config = configDict[JSON.stringify(dataInfo)]; + config.userId = this.getUserId(); const client = await Storage.getClient(dataInfo.backend, null, config); return client; }; diff --git a/src/plugins/ImportArtifact/ImportArtifact.js b/src/plugins/ImportArtifact/ImportArtifact.js index 620638e18..1e97c45b5 100644 --- a/src/plugins/ImportArtifact/ImportArtifact.js +++ b/src/plugins/ImportArtifact/ImportArtifact.js @@ -52,6 +52,7 @@ define([ ImportArtifact.prototype.symLink = async function(path, storage) { const {id, config} = storage; + config.userId = this.getUserId(); const srcStorage = await Storage.getBackend(id).getClient(this.logger, config); return await srcStorage.stat(path); }; diff --git a/src/plugins/ImportArtifact/metadata.json b/src/plugins/ImportArtifact/metadata.json index f137f8c53..45a8a1e67 100644 --- a/src/plugins/ImportArtifact/metadata.json +++ b/src/plugins/ImportArtifact/metadata.json @@ -7,7 +7,7 @@ "class": "glyphicon glyphicon-transfer", "src": "" }, - "disableServerSideExecution": false, + "disableServerSideExecution": true, "disableBrowserSideExecution": false, "writeAccessRequired": true, "configStructure": [ diff --git a/src/routers/SciServerAuth/SciServerAuth.js b/src/routers/SciServerAuth/SciServerAuth.js index 88363b592..08d415c65 100644 --- a/src/routers/SciServerAuth/SciServerAuth.js +++ b/src/routers/SciServerAuth/SciServerAuth.js @@ -2,7 +2,10 @@ const express = require('express'); const router = express.Router(); -const login = require('../../common/sciserver-auth'); +const fetch = require('node-fetch'); +const TokenStorage = require('./tokens'); +let gmeConfig; +let storage; /** * Called when the server is created but before it starts to listening to incoming requests. @@ -19,28 +22,61 @@ const login = require('../../common/sciserver-auth'); * @param {object} middlewareOpts.workerManager - Spawns and keeps track of "worker" sub-processes. */ function initialize(middlewareOpts) { + gmeConfig = middlewareOpts.gmeConfig; + const getUserId = req => { + return middlewareOpts.getUserId(req) || gmeConfig.authentication.guestAccount; + }; const logger = middlewareOpts.logger.fork('SciServerAuth'); + storage = require('../storage')(logger, gmeConfig); logger.debug('initializing ...'); - router.post('/token', async function (req, res/*, next*/) { - const {username, password} = req.body; + router.get('/register', async function (req, res) { + const {token} = req.query; + const userId = getUserId(req); try { - const token = await login(username, password); - res.status(200).send(token); + const name = await getUsername(token); + await TokenStorage.register(userId, name, token); + res.status(200).send(`SciServer account "${name}" is now accessible from DeepForge for the next 24 hours.`); } catch (err) { res.status(500).send(err.message); } }); + router.get('/', async function (req, res) { + const userId = getUserId(req); + const names = await TokenStorage.getUsernames(userId); + return res.json(names); + }); + + router.get('/:name/token', async function (req, res) { + const userId = getUserId(req); + const {name} = req.params; + try { + return res.send(await TokenStorage.getToken(userId, name)); + } catch (err) { + const statusCode = err instanceof TokenStorage.NotFoundError ? 404 : 500; + return res.status(statusCode).send(err.message); + } + }); + logger.debug('ready'); } +async function getUsername(token) { + const url = `https://apps.sciserver.org/login-portal/keystone/v3/tokens/${token}`; + const response = await fetch(url); + const data = await response.json(); + return data.token.user.name; +} + /** * Called before the server starts listening. * @param {function} callback */ -function start(callback) { +async function start(callback) { + const db = await storage; + TokenStorage.init(gmeConfig, db); callback(); } diff --git a/src/routers/SciServerAuth/Tokens.js b/src/routers/SciServerAuth/Tokens.js new file mode 100644 index 000000000..910e09f76 --- /dev/null +++ b/src/routers/SciServerAuth/Tokens.js @@ -0,0 +1,48 @@ +/* + * This is a wrapper for storing SciServer tokens for individual DeepForge users. To + * keep it simpler, DeepForge usernames are referred to as userId/user (following the + * convention in webgme routers) and SciServer usernames are referred to as username/name. + */ +const MONGO_COLLECTION = 'SciServerTokens'; + +class TokenStorage { + constructor() { + this.collection = null; + this.guestAccount = undefined; + } + + init(gmeConfig, db) { + const one_day = 60*60*24; + this.collection = db.collection(MONGO_COLLECTION); + this.collection.createIndex({createdAt: 1}, {expireAfterSeconds: one_day}); + this.guestAccount = gmeConfig.authentication.guestAccount; + } + + async register(user=this.guestAccount, name, token) { + const createdAt = new Date(); + await this.collection.update({user, name}, {$set: {token, createdAt}}, {upsert: true}); + } + + async getUsernames(userId=this.guestAccount) { + const docs = await this.collection.find({user: userId}, {name: 1}).toArray(); + return docs.map(doc => doc.name); + } + + async getToken(userId=this.guestAccount, username) { + const doc = await this.collection.findOne({user: userId, name: username}, {token: 1}); + if (!doc) { + throw new NotFoundError(); + } + return doc.token; + } +} + +class NotFoundError extends Error { + constructor() { + super('Token not found.'); + } +} + +TokenStorage.NotFoundError = NotFoundError; + +module.exports = new TokenStorage(); From 8bfdb44c4ad8e4357f75f9ecb12ae95f49e38567 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 14:12:51 -0500 Subject: [PATCH 02/15] Update tests --- src/common/sciserver-auth.js | 13 +++++---- src/routers/SciServerAuth/SciServerAuth.js | 2 +- test/assets/configs/compute.js | 11 ++++--- test/assets/configs/sciserver.js | 34 +++++++++++++++++++++- test/assets/configs/storage.js | 11 ++++--- 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/common/sciserver-auth.js b/src/common/sciserver-auth.js index 9487b5fb4..7aea33b7c 100644 --- a/src/common/sciserver-auth.js +++ b/src/common/sciserver-auth.js @@ -1,17 +1,18 @@ /* globals define */ + (function(root, factory){ if(typeof define === 'function' && define.amd) { - define([], function(){ - return factory(); + define(['path', 'module'], function(path, module){ + const __dirname = path.dirname(module.uri); + return factory(require(__dirname + '/../routers/SciServerAuth/Tokens')); }); } else if(typeof module === 'object' && module.exports) { - module.exports = factory(); + module.exports = factory(require('../routers/SciServerAuth/Tokens')); } else { - root.CONSTANTS = factory(); + root.SciServerAuth = factory(); } -}(this, function() { +}(this, function(TokenStorage) { const isBrowser = typeof window !== 'undefined'; - const TokenStorage = isBrowser ? null : require('../routers/SciServerAuth/Tokens'); async function getTokenBrowser(ssUser) { const url = `/routers/SciServerAuth/${ssUser}/token`; diff --git a/src/routers/SciServerAuth/SciServerAuth.js b/src/routers/SciServerAuth/SciServerAuth.js index 08d415c65..789c72246 100644 --- a/src/routers/SciServerAuth/SciServerAuth.js +++ b/src/routers/SciServerAuth/SciServerAuth.js @@ -3,7 +3,7 @@ const express = require('express'); const router = express.Router(); const fetch = require('node-fetch'); -const TokenStorage = require('./tokens'); +const TokenStorage = require('./Tokens'); let gmeConfig; let storage; diff --git a/test/assets/configs/compute.js b/test/assets/configs/compute.js index 20541c348..bf9482c34 100644 --- a/test/assets/configs/compute.js +++ b/test/assets/configs/compute.js @@ -1,10 +1,13 @@ -const {getSciServerPassword, getSciServerUsername} = require('./sciserver'); +const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); +const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); -function getSciServerJobsConfig() { +async function getSciServerJobsConfig() { const username = getSciServerUsername(); + const password = getSciServerPassword(); + const token = await login(username, password); + await TokenStorage.register(undefined, username, token); return { username: username, - password: getSciServerPassword(), volume: `${username}/deepforge_test`, computeDomain: 'Small Jobs Domain', }; @@ -13,7 +16,7 @@ function getSciServerJobsConfig() { module.exports = async function() { const configs = {}; configs['gme'] = {}; - configs['sciserver-compute'] = getSciServerJobsConfig(); + configs['sciserver-compute'] = await getSciServerJobsConfig(); return configs; }; diff --git a/test/assets/configs/sciserver.js b/test/assets/configs/sciserver.js index aae35e792..cc4f093d9 100644 --- a/test/assets/configs/sciserver.js +++ b/test/assets/configs/sciserver.js @@ -1,3 +1,6 @@ +const fetch = require('node-fetch'); +const Headers = fetch.Headers; + function getSciServerUsername() { return process.env.SCISERVER_USERNAME || 'deepforge'; } @@ -6,4 +9,33 @@ function getSciServerPassword() { return process.env.SCISERVER_PASSWORD; } -module.exports = {getSciServerPassword, getSciServerUsername}; +async function login(username, password) { + const LOGIN_URL = 'https://apps.sciserver.org/login-portal/keystone/v3/tokens'; + const url = `${LOGIN_URL}?TaskName=DeepForge.Authentication.Login`; + const opts = { + method: 'POST', + headers: new Headers({ + 'Content-Type': 'application/json' + }), + body: getLoginBody(username, password) + }; + const response = await fetch(url, opts); + return response.headers.get('X-Subject-Token'); +} + +function getLoginBody(username, password) { + return JSON.stringify({ + auth: { + identity: { + password: { + user: { + name: username, + password: password + } + } + } + } + }); +} + +module.exports = {getSciServerPassword, getSciServerUsername, login}; diff --git a/test/assets/configs/storage.js b/test/assets/configs/storage.js index 7c54ec97d..ff5b83584 100644 --- a/test/assets/configs/storage.js +++ b/test/assets/configs/storage.js @@ -1,12 +1,15 @@ -const {getSciServerPassword, getSciServerUsername} = require('./sciserver'); +const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); +const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); -function getSciServerFilesConfig() { +async function getSciServerFilesConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); + const token = await login(username, password); + await TokenStorage.register(undefined, username, token); const volume = `${username}/deepforge_test`; const volumePool = 'Temporary'; - return {username, password, volume, volumePool}; + return {username, volume, volumePool}; } function getS3Config() { @@ -22,7 +25,7 @@ module.exports = async function() { const configs = {}; configs['gme'] = {}; - configs['sciserver-files'] = getSciServerFilesConfig(); + configs['sciserver-files'] = await getSciServerFilesConfig(); configs['s3'] = getS3Config(); return configs; From 486c25d646792711dabfd1ffd2730662953d62a0 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 15:20:00 -0500 Subject: [PATCH 03/15] Set default value for compute config --- src/common/compute/backends/ComputeClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/compute/backends/ComputeClient.js b/src/common/compute/backends/ComputeClient.js index ab57fe7b0..56a2b3c44 100644 --- a/src/common/compute/backends/ComputeClient.js +++ b/src/common/compute/backends/ComputeClient.js @@ -2,7 +2,7 @@ define([], function() { class ComputeClient { - constructor (logger, blobClient, config) { + constructor (logger, blobClient, config={}) { this.logger = logger.fork('compute'); this.userId = config.userId; this.blobClient = blobClient; From f7a9eae7d3bcb914b10ccb52e81cd7165ffb69b4 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 15:44:11 -0500 Subject: [PATCH 04/15] Refactor configs for tests --- test/assets/configs/compute.js | 4 +--- test/assets/configs/sciserver.js | 8 +++++++- test/assets/configs/storage.js | 4 +--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/assets/configs/compute.js b/test/assets/configs/compute.js index bf9482c34..6f89313c1 100644 --- a/test/assets/configs/compute.js +++ b/test/assets/configs/compute.js @@ -1,11 +1,9 @@ const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); -const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); async function getSciServerJobsConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); - const token = await login(username, password); - await TokenStorage.register(undefined, username, token); + await login(username, password); return { username: username, volume: `${username}/deepforge_test`, diff --git a/test/assets/configs/sciserver.js b/test/assets/configs/sciserver.js index cc4f093d9..645d6efd5 100644 --- a/test/assets/configs/sciserver.js +++ b/test/assets/configs/sciserver.js @@ -1,3 +1,4 @@ +const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); const fetch = require('node-fetch'); const Headers = fetch.Headers; @@ -9,7 +10,7 @@ function getSciServerPassword() { return process.env.SCISERVER_PASSWORD; } -async function login(username, password) { +async function getToken(username, password) { const LOGIN_URL = 'https://apps.sciserver.org/login-portal/keystone/v3/tokens'; const url = `${LOGIN_URL}?TaskName=DeepForge.Authentication.Login`; const opts = { @@ -23,6 +24,11 @@ async function login(username, password) { return response.headers.get('X-Subject-Token'); } +async function login(username, password) { + const token = await getToken(username, password); + await TokenStorage.register(undefined, username, token); +} + function getLoginBody(username, password) { return JSON.stringify({ auth: { diff --git a/test/assets/configs/storage.js b/test/assets/configs/storage.js index ff5b83584..357f302bb 100644 --- a/test/assets/configs/storage.js +++ b/test/assets/configs/storage.js @@ -1,11 +1,9 @@ const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); -const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); async function getSciServerFilesConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); - const token = await login(username, password); - await TokenStorage.register(undefined, username, token); + await login(username, password); const volume = `${username}/deepforge_test`; const volumePool = 'Temporary'; From 09d96b0e3534f136cee17939db7e01da9a086e69 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 10:52:20 -0500 Subject: [PATCH 05/15] Add support for logging in to SciServer directly. Closes #1999 --- src/common/compute/backends/ComputeClient.js | 3 +- .../backends/sciserver-compute/Client.js | 9 +-- .../backends/sciserver-compute/metadata.json | 22 +++--- src/common/sciserver-auth.js | 74 +++++-------------- src/common/storage/backends/StorageClient.js | 3 +- .../backends/sciserver-files/Client.js | 5 +- .../backends/sciserver-files/metadata.json | 24 +++--- src/plugins/ExecuteJob/ExecuteJob.js | 3 + src/plugins/ImportArtifact/ImportArtifact.js | 1 + src/plugins/ImportArtifact/metadata.json | 2 +- src/routers/SciServerAuth/SciServerAuth.js | 48 ++++++++++-- src/routers/SciServerAuth/Tokens.js | 48 ++++++++++++ 12 files changed, 141 insertions(+), 101 deletions(-) create mode 100644 src/routers/SciServerAuth/Tokens.js diff --git a/src/common/compute/backends/ComputeClient.js b/src/common/compute/backends/ComputeClient.js index 507fbd8c2..ab57fe7b0 100644 --- a/src/common/compute/backends/ComputeClient.js +++ b/src/common/compute/backends/ComputeClient.js @@ -2,8 +2,9 @@ define([], function() { class ComputeClient { - constructor (logger, blobClient) { + constructor (logger, blobClient, config) { this.logger = logger.fork('compute'); + this.userId = config.userId; this.blobClient = blobClient; this._events = {}; } diff --git a/src/common/compute/backends/sciserver-compute/Client.js b/src/common/compute/backends/sciserver-compute/Client.js index 9d8c6a037..43c80b5a3 100644 --- a/src/common/compute/backends/sciserver-compute/Client.js +++ b/src/common/compute/backends/sciserver-compute/Client.js @@ -17,7 +17,7 @@ define([ module, path, fetch, - login, + getToken, PREPARE_AND_RUN, ) { const Headers = fetch.Headers; @@ -26,7 +26,6 @@ define([ constructor(logger, blobClient, config) { super(logger, blobClient, config); this.username = config.username; - this.password = config.password; this.computeDomain = config.computeDomain; this.previousJobState = {}; this.consoleOutputLen = {}; @@ -66,7 +65,6 @@ define([ const metadata = await this.blobClient.getMetadata(hash); const config = { username: this.username, - password: this.password, volume: `${this.username}/scratch`, volumePool: 'Temporary' }; @@ -117,7 +115,7 @@ define([ } async token () { - return login(this.username, this.password); + return getToken(this.username, this.userId); } async getJobState (jobInfo) { @@ -234,11 +232,10 @@ define([ _getStorageConfigAndDataInfo (filepath) { const dirs = filepath.split('/').slice(4); let [volumePool, owner, volume] = dirs.slice(0, 3); - const password = this.password; volume = owner + '/' + volume; const filename = dirs.slice(3).join('/'); return { - config: {username: this.username, volumePool, password, volume}, + config: {username: this.username, volumePool, volume}, dataInfo: { data: {filename, volume, volumePool} } diff --git a/src/common/compute/backends/sciserver-compute/metadata.json b/src/common/compute/backends/sciserver-compute/metadata.json index 7f02b5f7c..2c2c2afa2 100644 --- a/src/common/compute/backends/sciserver-compute/metadata.json +++ b/src/common/compute/backends/sciserver-compute/metadata.json @@ -7,19 +7,17 @@ "displayName": "Username", "description": "SciServer username", "value": "", - "valueType": "string", - "readOnly": false - }, - { - "name": "password", - "displayName": "Password", - "description": "SciServer password", - "value": "", - "valueType": "string", + "valueType": "stringX", + "valueItemsURL": "/routers/SciServerAuth", + "extraValueItems": [ + { + "name": "Link account...", + "type": "URL", + "value": "https://apps.sciserver.org/login-portal/login?callbackUrl=<%= window.location.origin %>/routers/SciServerAuth/register" + } + ], "readOnly": false, - "options": { - "isPassword": true - } + "isAuth": true }, { "name": "computeDomain", diff --git a/src/common/sciserver-auth.js b/src/common/sciserver-auth.js index edbfd126e..9487b5fb4 100644 --- a/src/common/sciserver-auth.js +++ b/src/common/sciserver-auth.js @@ -10,70 +10,30 @@ root.CONSTANTS = factory(); } }(this, function() { - const LOGIN_URL = 'https://apps.sciserver.org/login-portal/keystone/v3/tokens'; const isBrowser = typeof window !== 'undefined'; - const fetch = isBrowser ? window.fetch : require('node-fetch'); - const Headers = isBrowser ? window.Headers : fetch.Headers; + const TokenStorage = isBrowser ? null : require('../routers/SciServerAuth/Tokens'); - async function loginViaProxy(username, password) { - const url = '/routers/SciServerAuth/token'; - const opts = { - method: 'POST', - headers: new Headers({ - 'Content-Type': 'application/json' - }), - body: JSON.stringify({username, password}) - }; - const response = await fetch(url, opts); - return await response.text(); - } - - async function fetchNewToken(username, password) { - if (isBrowser) { - return loginViaProxy(username, password); - } - - const url = `${LOGIN_URL}?TaskName=DeepForge.Authentication.Login`; - const opts = { - method: 'POST', - headers: new Headers({ - 'Content-Type': 'application/json' - }), - body: getLoginBody(username, password) - }; - const response = await fetch(url, opts); - return response.headers.get('X-Subject-Token'); - } - - const tokens = {}; - const hours = 1000*60*60; - function login(username, password) { - tokens[username] = tokens[username] || {}; - if (!tokens[username][password]) { - tokens[username][password] = fetchNewToken(username, password); - setTimeout(clearToken.bind(null, username, password), 23*hours); + async function getTokenBrowser(ssUser) { + const url = `/routers/SciServerAuth/${ssUser}/token`; + const response = await fetch(url); + if (response.status < 400) { + return await response.text(); + } else { + throw new Error(await response.text()); } - return tokens[username][password]; } - function clearToken(username, password) { - delete tokens[username][password]; + async function getTokenNodeJS(ssUser, dfUser) { + return await TokenStorage.getToken(dfUser, ssUser); } - function getLoginBody(username, password) { - return JSON.stringify({ - auth: { - identity: { - password: { - user: { - name: username, - password: password - } - } - } - } - }); + async function getToken(ssUser, dfUser) { + if (isBrowser) { + return getTokenBrowser(ssUser); + } else { + return getTokenNodeJS(ssUser, dfUser); + } } - return login; + return getToken; })); diff --git a/src/common/storage/backends/StorageClient.js b/src/common/storage/backends/StorageClient.js index f1178d5d1..ab8c7e7db 100644 --- a/src/common/storage/backends/StorageClient.js +++ b/src/common/storage/backends/StorageClient.js @@ -10,9 +10,10 @@ define([ require.nodeRequire('node-fetch'); const Headers = require.isBrowser ? window.Headers : fetch.Headers; const stream = require.isBrowser ? null : require.nodeRequire('stream'); - const StorageClient = function(id, name, logger) { + const StorageClient = function(id, name, logger, config) { this.id = id; this.name = name; + this.userId = config.userId; if (!logger) { logger = Logger.create(`gme:storage:${id}`, gmeConfig.client.log); } diff --git a/src/common/storage/backends/sciserver-files/Client.js b/src/common/storage/backends/sciserver-files/Client.js index a36c0760e..9a1b71388 100644 --- a/src/common/storage/backends/sciserver-files/Client.js +++ b/src/common/storage/backends/sciserver-files/Client.js @@ -4,13 +4,12 @@ define([ 'deepforge/sciserver-auth', ], function ( StorageClient, - login, + getToken, ) { const BASE_URL = 'https://apps.sciserver.org/fileservice/api/'; const SciServerFiles = function (id, name, logger, config = {}) { StorageClient.apply(this, arguments); this.username = config.username; - this.password = config.password; this.volumePool = config.volumePool || 'Storage'; this.volume = (config.volume || '').replace(/^Storage\//, ''); }; @@ -84,7 +83,7 @@ define([ }; SciServerFiles.prototype.fetch = async function (action, url, opts = {}) { - const token = await login(this.username, this.password); + const token = await getToken(this.username, this.userId); opts.headers = opts.headers || {}; opts.headers['X-Auth-Token'] = token; try { diff --git a/src/common/storage/backends/sciserver-files/metadata.json b/src/common/storage/backends/sciserver-files/metadata.json index e62794587..2099f7ad8 100644 --- a/src/common/storage/backends/sciserver-files/metadata.json +++ b/src/common/storage/backends/sciserver-files/metadata.json @@ -4,24 +4,20 @@ { "name": "username", "displayName": "Username", - "description": "SciServer username", + "description": "SciServer account to use", "value": "", - "valueType": "string", + "valueType": "stringX", + "valueItemsURL": "/routers/SciServerAuth", + "extraValueItems": [ + { + "name": "Link account...", + "type": "URL", + "value": "https://apps.sciserver.org/login-portal/login?callbackUrl=<%= window.location.origin %>/routers/SciServerAuth/register" + } + ], "readOnly": false, "isAuth": true }, - { - "name": "password", - "displayName": "Password", - "description": "SciServer password", - "value": "", - "valueType": "string", - "readOnly": false, - "isAuth": true, - "options": { - "isPassword": true - } - }, { "name": "volume", "displayName": "Volume", diff --git a/src/plugins/ExecuteJob/ExecuteJob.js b/src/plugins/ExecuteJob/ExecuteJob.js index 46557d334..6c4f39038 100644 --- a/src/plugins/ExecuteJob/ExecuteJob.js +++ b/src/plugins/ExecuteJob/ExecuteJob.js @@ -164,6 +164,7 @@ define([ ExecuteJob.prototype.createComputeClient = function () { const config = this.getCurrentConfig(); const backend = Compute.getBackend(config.compute.id); + config.compute.config.userId = this.getUserId(); if (config.compute.id === 'gme') { config.compute.config.webgmeToken = this.blobClient.webgmeToken; // HACK } @@ -218,6 +219,7 @@ define([ ExecuteJob.prototype.getStorageClient = async function () { const {storage} = this.getCurrentConfig(); const backend = Storage.getBackend(storage.id); + storage.config.userId = this.getUserId(); return await backend.getClient(this.logger, storage.config); }; @@ -235,6 +237,7 @@ define([ ExecuteJob.prototype.getStorageClientForInputData = async function (dataInfo) { const configDict = await this.getInputStorageConfigs(); const config = configDict[JSON.stringify(dataInfo)]; + config.userId = this.getUserId(); const client = await Storage.getClient(dataInfo.backend, null, config); return client; }; diff --git a/src/plugins/ImportArtifact/ImportArtifact.js b/src/plugins/ImportArtifact/ImportArtifact.js index b023ae624..1dea0c0c4 100644 --- a/src/plugins/ImportArtifact/ImportArtifact.js +++ b/src/plugins/ImportArtifact/ImportArtifact.js @@ -51,6 +51,7 @@ define([ ImportArtifact.prototype.symLink = async function(path, storage) { const {id, config} = storage; + config.userId = this.getUserId(); const srcStorage = await Storage.getBackend(id).getClient(this.logger, config); return await srcStorage.stat(path); }; diff --git a/src/plugins/ImportArtifact/metadata.json b/src/plugins/ImportArtifact/metadata.json index f137f8c53..45a8a1e67 100644 --- a/src/plugins/ImportArtifact/metadata.json +++ b/src/plugins/ImportArtifact/metadata.json @@ -7,7 +7,7 @@ "class": "glyphicon glyphicon-transfer", "src": "" }, - "disableServerSideExecution": false, + "disableServerSideExecution": true, "disableBrowserSideExecution": false, "writeAccessRequired": true, "configStructure": [ diff --git a/src/routers/SciServerAuth/SciServerAuth.js b/src/routers/SciServerAuth/SciServerAuth.js index 88363b592..08d415c65 100644 --- a/src/routers/SciServerAuth/SciServerAuth.js +++ b/src/routers/SciServerAuth/SciServerAuth.js @@ -2,7 +2,10 @@ const express = require('express'); const router = express.Router(); -const login = require('../../common/sciserver-auth'); +const fetch = require('node-fetch'); +const TokenStorage = require('./tokens'); +let gmeConfig; +let storage; /** * Called when the server is created but before it starts to listening to incoming requests. @@ -19,28 +22,61 @@ const login = require('../../common/sciserver-auth'); * @param {object} middlewareOpts.workerManager - Spawns and keeps track of "worker" sub-processes. */ function initialize(middlewareOpts) { + gmeConfig = middlewareOpts.gmeConfig; + const getUserId = req => { + return middlewareOpts.getUserId(req) || gmeConfig.authentication.guestAccount; + }; const logger = middlewareOpts.logger.fork('SciServerAuth'); + storage = require('../storage')(logger, gmeConfig); logger.debug('initializing ...'); - router.post('/token', async function (req, res/*, next*/) { - const {username, password} = req.body; + router.get('/register', async function (req, res) { + const {token} = req.query; + const userId = getUserId(req); try { - const token = await login(username, password); - res.status(200).send(token); + const name = await getUsername(token); + await TokenStorage.register(userId, name, token); + res.status(200).send(`SciServer account "${name}" is now accessible from DeepForge for the next 24 hours.`); } catch (err) { res.status(500).send(err.message); } }); + router.get('/', async function (req, res) { + const userId = getUserId(req); + const names = await TokenStorage.getUsernames(userId); + return res.json(names); + }); + + router.get('/:name/token', async function (req, res) { + const userId = getUserId(req); + const {name} = req.params; + try { + return res.send(await TokenStorage.getToken(userId, name)); + } catch (err) { + const statusCode = err instanceof TokenStorage.NotFoundError ? 404 : 500; + return res.status(statusCode).send(err.message); + } + }); + logger.debug('ready'); } +async function getUsername(token) { + const url = `https://apps.sciserver.org/login-portal/keystone/v3/tokens/${token}`; + const response = await fetch(url); + const data = await response.json(); + return data.token.user.name; +} + /** * Called before the server starts listening. * @param {function} callback */ -function start(callback) { +async function start(callback) { + const db = await storage; + TokenStorage.init(gmeConfig, db); callback(); } diff --git a/src/routers/SciServerAuth/Tokens.js b/src/routers/SciServerAuth/Tokens.js new file mode 100644 index 000000000..910e09f76 --- /dev/null +++ b/src/routers/SciServerAuth/Tokens.js @@ -0,0 +1,48 @@ +/* + * This is a wrapper for storing SciServer tokens for individual DeepForge users. To + * keep it simpler, DeepForge usernames are referred to as userId/user (following the + * convention in webgme routers) and SciServer usernames are referred to as username/name. + */ +const MONGO_COLLECTION = 'SciServerTokens'; + +class TokenStorage { + constructor() { + this.collection = null; + this.guestAccount = undefined; + } + + init(gmeConfig, db) { + const one_day = 60*60*24; + this.collection = db.collection(MONGO_COLLECTION); + this.collection.createIndex({createdAt: 1}, {expireAfterSeconds: one_day}); + this.guestAccount = gmeConfig.authentication.guestAccount; + } + + async register(user=this.guestAccount, name, token) { + const createdAt = new Date(); + await this.collection.update({user, name}, {$set: {token, createdAt}}, {upsert: true}); + } + + async getUsernames(userId=this.guestAccount) { + const docs = await this.collection.find({user: userId}, {name: 1}).toArray(); + return docs.map(doc => doc.name); + } + + async getToken(userId=this.guestAccount, username) { + const doc = await this.collection.findOne({user: userId, name: username}, {token: 1}); + if (!doc) { + throw new NotFoundError(); + } + return doc.token; + } +} + +class NotFoundError extends Error { + constructor() { + super('Token not found.'); + } +} + +TokenStorage.NotFoundError = NotFoundError; + +module.exports = new TokenStorage(); From 5635f1c99a0d6a43540a6f36537565585ea0b01f Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 14:12:51 -0500 Subject: [PATCH 06/15] Update tests --- src/common/sciserver-auth.js | 13 +++++---- src/routers/SciServerAuth/SciServerAuth.js | 2 +- test/assets/configs/compute.js | 11 ++++--- test/assets/configs/sciserver.js | 34 +++++++++++++++++++++- test/assets/configs/storage.js | 11 ++++--- 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/common/sciserver-auth.js b/src/common/sciserver-auth.js index 9487b5fb4..7aea33b7c 100644 --- a/src/common/sciserver-auth.js +++ b/src/common/sciserver-auth.js @@ -1,17 +1,18 @@ /* globals define */ + (function(root, factory){ if(typeof define === 'function' && define.amd) { - define([], function(){ - return factory(); + define(['path', 'module'], function(path, module){ + const __dirname = path.dirname(module.uri); + return factory(require(__dirname + '/../routers/SciServerAuth/Tokens')); }); } else if(typeof module === 'object' && module.exports) { - module.exports = factory(); + module.exports = factory(require('../routers/SciServerAuth/Tokens')); } else { - root.CONSTANTS = factory(); + root.SciServerAuth = factory(); } -}(this, function() { +}(this, function(TokenStorage) { const isBrowser = typeof window !== 'undefined'; - const TokenStorage = isBrowser ? null : require('../routers/SciServerAuth/Tokens'); async function getTokenBrowser(ssUser) { const url = `/routers/SciServerAuth/${ssUser}/token`; diff --git a/src/routers/SciServerAuth/SciServerAuth.js b/src/routers/SciServerAuth/SciServerAuth.js index 08d415c65..789c72246 100644 --- a/src/routers/SciServerAuth/SciServerAuth.js +++ b/src/routers/SciServerAuth/SciServerAuth.js @@ -3,7 +3,7 @@ const express = require('express'); const router = express.Router(); const fetch = require('node-fetch'); -const TokenStorage = require('./tokens'); +const TokenStorage = require('./Tokens'); let gmeConfig; let storage; diff --git a/test/assets/configs/compute.js b/test/assets/configs/compute.js index 20541c348..bf9482c34 100644 --- a/test/assets/configs/compute.js +++ b/test/assets/configs/compute.js @@ -1,10 +1,13 @@ -const {getSciServerPassword, getSciServerUsername} = require('./sciserver'); +const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); +const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); -function getSciServerJobsConfig() { +async function getSciServerJobsConfig() { const username = getSciServerUsername(); + const password = getSciServerPassword(); + const token = await login(username, password); + await TokenStorage.register(undefined, username, token); return { username: username, - password: getSciServerPassword(), volume: `${username}/deepforge_test`, computeDomain: 'Small Jobs Domain', }; @@ -13,7 +16,7 @@ function getSciServerJobsConfig() { module.exports = async function() { const configs = {}; configs['gme'] = {}; - configs['sciserver-compute'] = getSciServerJobsConfig(); + configs['sciserver-compute'] = await getSciServerJobsConfig(); return configs; }; diff --git a/test/assets/configs/sciserver.js b/test/assets/configs/sciserver.js index aae35e792..cc4f093d9 100644 --- a/test/assets/configs/sciserver.js +++ b/test/assets/configs/sciserver.js @@ -1,3 +1,6 @@ +const fetch = require('node-fetch'); +const Headers = fetch.Headers; + function getSciServerUsername() { return process.env.SCISERVER_USERNAME || 'deepforge'; } @@ -6,4 +9,33 @@ function getSciServerPassword() { return process.env.SCISERVER_PASSWORD; } -module.exports = {getSciServerPassword, getSciServerUsername}; +async function login(username, password) { + const LOGIN_URL = 'https://apps.sciserver.org/login-portal/keystone/v3/tokens'; + const url = `${LOGIN_URL}?TaskName=DeepForge.Authentication.Login`; + const opts = { + method: 'POST', + headers: new Headers({ + 'Content-Type': 'application/json' + }), + body: getLoginBody(username, password) + }; + const response = await fetch(url, opts); + return response.headers.get('X-Subject-Token'); +} + +function getLoginBody(username, password) { + return JSON.stringify({ + auth: { + identity: { + password: { + user: { + name: username, + password: password + } + } + } + } + }); +} + +module.exports = {getSciServerPassword, getSciServerUsername, login}; diff --git a/test/assets/configs/storage.js b/test/assets/configs/storage.js index 7c54ec97d..ff5b83584 100644 --- a/test/assets/configs/storage.js +++ b/test/assets/configs/storage.js @@ -1,12 +1,15 @@ -const {getSciServerPassword, getSciServerUsername} = require('./sciserver'); +const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); +const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); -function getSciServerFilesConfig() { +async function getSciServerFilesConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); + const token = await login(username, password); + await TokenStorage.register(undefined, username, token); const volume = `${username}/deepforge_test`; const volumePool = 'Temporary'; - return {username, password, volume, volumePool}; + return {username, volume, volumePool}; } function getS3Config() { @@ -22,7 +25,7 @@ module.exports = async function() { const configs = {}; configs['gme'] = {}; - configs['sciserver-files'] = getSciServerFilesConfig(); + configs['sciserver-files'] = await getSciServerFilesConfig(); configs['s3'] = getS3Config(); return configs; From 5883915ee1afc8e18cd8b95bb4c8f0300f6cab86 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 15:20:00 -0500 Subject: [PATCH 07/15] Set default value for compute config --- src/common/compute/backends/ComputeClient.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/compute/backends/ComputeClient.js b/src/common/compute/backends/ComputeClient.js index ab57fe7b0..56a2b3c44 100644 --- a/src/common/compute/backends/ComputeClient.js +++ b/src/common/compute/backends/ComputeClient.js @@ -2,7 +2,7 @@ define([], function() { class ComputeClient { - constructor (logger, blobClient, config) { + constructor (logger, blobClient, config={}) { this.logger = logger.fork('compute'); this.userId = config.userId; this.blobClient = blobClient; From 088b61b881781b400aa20ff882e80b206c6a0488 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 18 Mar 2021 15:44:11 -0500 Subject: [PATCH 08/15] Refactor configs for tests --- test/assets/configs/compute.js | 4 +--- test/assets/configs/sciserver.js | 8 +++++++- test/assets/configs/storage.js | 4 +--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/assets/configs/compute.js b/test/assets/configs/compute.js index bf9482c34..6f89313c1 100644 --- a/test/assets/configs/compute.js +++ b/test/assets/configs/compute.js @@ -1,11 +1,9 @@ const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); -const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); async function getSciServerJobsConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); - const token = await login(username, password); - await TokenStorage.register(undefined, username, token); + await login(username, password); return { username: username, volume: `${username}/deepforge_test`, diff --git a/test/assets/configs/sciserver.js b/test/assets/configs/sciserver.js index cc4f093d9..645d6efd5 100644 --- a/test/assets/configs/sciserver.js +++ b/test/assets/configs/sciserver.js @@ -1,3 +1,4 @@ +const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); const fetch = require('node-fetch'); const Headers = fetch.Headers; @@ -9,7 +10,7 @@ function getSciServerPassword() { return process.env.SCISERVER_PASSWORD; } -async function login(username, password) { +async function getToken(username, password) { const LOGIN_URL = 'https://apps.sciserver.org/login-portal/keystone/v3/tokens'; const url = `${LOGIN_URL}?TaskName=DeepForge.Authentication.Login`; const opts = { @@ -23,6 +24,11 @@ async function login(username, password) { return response.headers.get('X-Subject-Token'); } +async function login(username, password) { + const token = await getToken(username, password); + await TokenStorage.register(undefined, username, token); +} + function getLoginBody(username, password) { return JSON.stringify({ auth: { diff --git a/test/assets/configs/storage.js b/test/assets/configs/storage.js index ff5b83584..357f302bb 100644 --- a/test/assets/configs/storage.js +++ b/test/assets/configs/storage.js @@ -1,11 +1,9 @@ const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver'); -const TokenStorage = require('../../../src/routers/SciServerAuth/Tokens'); async function getSciServerFilesConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); - const token = await login(username, password); - await TokenStorage.register(undefined, username, token); + await login(username, password); const volume = `${username}/deepforge_test`; const volumePool = 'Temporary'; From a1e2d45eb8d2978440a8dae0367942dd8e1770e0 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 23 Mar 2021 11:09:28 -0500 Subject: [PATCH 09/15] Add prepare fn to sciserver compute/storage --- .../backends/sciserver-compute/metadata.js | 7 + .../backends/sciserver-compute/metadata.json | 33 --- src/common/sciserver-auth.js | 17 +- .../backends/sciserver-files/Client.js | 237 +++++++++--------- .../backends/sciserver-files/metadata.json | 40 --- src/common/storage/index.js | 4 +- 6 files changed, 134 insertions(+), 204 deletions(-) delete mode 100644 src/common/compute/backends/sciserver-compute/metadata.json delete mode 100644 src/common/storage/backends/sciserver-files/metadata.json diff --git a/src/common/compute/backends/sciserver-compute/metadata.js b/src/common/compute/backends/sciserver-compute/metadata.js index 4e9fd10cb..67560a8c5 100644 --- a/src/common/compute/backends/sciserver-compute/metadata.js +++ b/src/common/compute/backends/sciserver-compute/metadata.js @@ -1,6 +1,8 @@ /*globals define*/ define([ + 'deepforge/sciserver-auth', ], function( + fetchToken, ) { return { name: 'SciServer Compute', @@ -34,5 +36,10 @@ define([ ] } ], + prepare: async config => { + const token = await fetchToken(config.username); + config.token = token; + return config; + } }; }); diff --git a/src/common/compute/backends/sciserver-compute/metadata.json b/src/common/compute/backends/sciserver-compute/metadata.json deleted file mode 100644 index 2c2c2afa2..000000000 --- a/src/common/compute/backends/sciserver-compute/metadata.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "SciServer Compute", - "dashboard": "./dashboard/index", - "configStructure": [ - { - "name": "username", - "displayName": "Username", - "description": "SciServer username", - "value": "", - "valueType": "stringX", - "valueItemsURL": "/routers/SciServerAuth", - "extraValueItems": [ - { - "name": "Link account...", - "type": "URL", - "value": "https://apps.sciserver.org/login-portal/login?callbackUrl=<%= window.location.origin %>/routers/SciServerAuth/register" - } - ], - "readOnly": false, - "isAuth": true - }, - { - "name": "computeDomain", - "displayName": "Compute Domain", - "description": "A small job shares resources with up to 4 other jobs and has a max quota for RAM of approx 32GB. A large job runs exclusively and has all CPU cores and RAM available (approx 240GB), however since only one large job will run at a time, there may be a longer wait for the job to start.", - "value": "Small Jobs Domain", - "valueItems": [ - "Small Jobs Domain", - "Large Jobs Domain" - ] - } - ] -} diff --git a/src/common/sciserver-auth.js b/src/common/sciserver-auth.js index 7aea33b7c..b0772bcab 100644 --- a/src/common/sciserver-auth.js +++ b/src/common/sciserver-auth.js @@ -2,16 +2,15 @@ (function(root, factory){ if(typeof define === 'function' && define.amd) { - define(['path', 'module'], function(path, module){ - const __dirname = path.dirname(module.uri); - return factory(require(__dirname + '/../routers/SciServerAuth/Tokens')); + define([], function(){ + return factory(); }); } else if(typeof module === 'object' && module.exports) { - module.exports = factory(require('../routers/SciServerAuth/Tokens')); + module.exports = factory(); } else { root.SciServerAuth = factory(); } -}(this, function(TokenStorage) { +}(this, function() { const isBrowser = typeof window !== 'undefined'; async function getTokenBrowser(ssUser) { @@ -24,15 +23,11 @@ } } - async function getTokenNodeJS(ssUser, dfUser) { - return await TokenStorage.getToken(dfUser, ssUser); - } - - async function getToken(ssUser, dfUser) { + async function getToken(ssUser) { if (isBrowser) { return getTokenBrowser(ssUser); } else { - return getTokenNodeJS(ssUser, dfUser); + throw new Error('Cannot retrieve SciServer token outside of browser.'); } } diff --git a/src/common/storage/backends/sciserver-files/Client.js b/src/common/storage/backends/sciserver-files/Client.js index 9a1b71388..155875a3a 100644 --- a/src/common/storage/backends/sciserver-files/Client.js +++ b/src/common/storage/backends/sciserver-files/Client.js @@ -7,134 +7,135 @@ define([ getToken, ) { const BASE_URL = 'https://apps.sciserver.org/fileservice/api/'; - const SciServerFiles = function (id, name, logger, config = {}) { - StorageClient.apply(this, arguments); - this.username = config.username; - this.volumePool = config.volumePool || 'Storage'; - this.volume = (config.volume || '').replace(/^Storage\//, ''); - }; - - SciServerFiles.prototype = Object.create(StorageClient.prototype); - - SciServerFiles.prototype.getFile = async function (dataInfo) { - const response = await this.getDownloadResponse(dataInfo); - if (require.isBrowser) { - return await response.arrayBuffer(); - } else { - return Buffer.from(await response.arrayBuffer()); + class SciServerFiles extends StorageClient { + constructor (id, name, logger, config = {}) { + super(id, name, logger, config); + this.username = config.username; + this.token = config.token; + this.volumePool = config.volumePool || 'Storage'; + this.volume = (config.volume || '').replace(/^Storage\//, ''); } - }; - SciServerFiles.prototype.getFileStream = async function(dataInfo) { - const response = await this.getDownloadResponse(dataInfo); - return response.body; - }; + async getFile (dataInfo) { + const response = await this.getDownloadResponse(dataInfo); + if (require.isBrowser) { + return await response.arrayBuffer(); + } else { + return Buffer.from(await response.arrayBuffer()); + } + } - SciServerFiles.prototype.putFile = async function (filename, content) { - if (!this.volume) { - throw new Error('Cannot upload file to SciServer. No volume specified.'); + async getFileStream (dataInfo) { + const response = await this.getDownloadResponse(dataInfo); + return response.body; } - const opts = { - method: 'PUT', - body: content, - }; - - const url = `file/${this.volumePool}/${this.volume}/${filename}`; - await this.fetch('upload', url, opts, 'upload'); - const metadata = { - filename: filename, - volume: this.volume, - size: content.byteLength || content.size, - volumePool: this.volumePool - }; - return this.createDataInfo(metadata); - }; - - SciServerFiles.prototype.putFileStream = async function(filename, stream) { - this.ensureStreamSupport(); - this.ensureReadableStream(stream); - await this.putFile(filename, stream); - // stat necessary because of byteLength - return await this.stat(filename); - }; - - SciServerFiles.prototype.deleteDir = async function (dirname) { - const url = `data/${this.volumePool}/${this.volume}/${dirname}`; - const opts = {method: 'DELETE'}; - return await this.fetch('delete directory', url, opts); - }; - - SciServerFiles.prototype.deleteFile = async function (dataInfo) { - const {volume, filename, volumePool} = dataInfo.data; - const url = `data/${volumePool}/${volume}/${filename}`; - const opts = {method: 'DELETE'}; - return await this.fetch('delete', url, opts); - }; - - SciServerFiles.prototype.getMetadata = async function (dataInfo) { - const metadata = {size: dataInfo.data.size}; - return metadata; - }; - - SciServerFiles.prototype.getCachePath = async function (dataInfo) { - const {volume, filename} = dataInfo.data; - return `${this.id}/${volume}/${filename}`; - }; - - SciServerFiles.prototype.fetch = async function (action, url, opts = {}) { - const token = await getToken(this.username, this.userId); - opts.headers = opts.headers || {}; - opts.headers['X-Auth-Token'] = token; - try { - const response = await StorageClient.prototype.fetch.call(this, url, opts); - return response; - } catch (errRes) { - const err = errRes instanceof Error ? errRes : - await this.getErrorMsg(errRes); - throw new Error(`SciServerFiles ${action} failed: ${err}`); + async putFile (filename, content) { + if (!this.volume) { + throw new Error('Cannot upload file to SciServer. No volume specified.'); + } + + const opts = { + method: 'PUT', + body: content, + }; + + const url = `file/${this.volumePool}/${this.volume}/${filename}`; + await this.fetch('upload', url, opts, 'upload'); + const metadata = { + filename: filename, + volume: this.volume, + size: content.byteLength || content.size, + volumePool: this.volumePool + }; + return this.createDataInfo(metadata); } - }; - - SciServerFiles.prototype.getErrorMsg = async function (response) { - try { - const contents = await response.json(); - return JSON.stringify(contents); - } catch (err) { - return await response.text(); + + async putFileStream (filename, stream) { + this.ensureStreamSupport(); + this.ensureReadableStream(stream); + await this.putFile(filename, stream); + // stat necessary because of byteLength + return await this.stat(filename); } - }; - SciServerFiles.prototype.getURL = function (url) { - if (url.startsWith('http')) { - return url; + async deleteDir (dirname) { + const url = `data/${this.volumePool}/${this.volume}/${dirname}`; + const opts = {method: 'DELETE'}; + return await this.fetch('delete directory', url, opts); } - return BASE_URL + url; - }; - - SciServerFiles.prototype.stat = async function(path) { - const splitPath = path.split('/'); - const filename = splitPath.pop(); - const parentDir = splitPath.join('/'); - const url = `jsontree/${this.volumePool}/${this.volume}/${parentDir}?level=2`; - const response = await this.fetch('stat', url); - const files = (await response.json()).root.files || []; - const metadata = files.find(file => file.name === filename); - if(metadata) { - metadata.volume = this.volume; - metadata.volumePool = this.volumePool; - metadata.filename = path; - } else { - throw new Error(`The file at ${path} doesn't exist in ${this.volume}`); + + async deleteFile (dataInfo) { + const {volume, filename, volumePool} = dataInfo.data; + const url = `data/${volumePool}/${volume}/${filename}`; + const opts = {method: 'DELETE'}; + return await this.fetch('delete', url, opts); + } + + async getMetadata (dataInfo) { + const metadata = {size: dataInfo.data.size}; + return metadata; + } + + async getCachePath (dataInfo) { + const {volume, filename} = dataInfo.data; + return `${this.id}/${volume}/${filename}`; + } + + async fetch (action, url, opts = {}) { + const token = this.token || await getToken(this.username); + opts.headers = opts.headers || {}; + opts.headers['X-Auth-Token'] = token; + try { + const response = await StorageClient.prototype.fetch.call(this, url, opts); + return response; + } catch (errRes) { + const err = errRes instanceof Error ? errRes : + await this.getErrorMsg(errRes); + throw new Error(`SciServerFiles ${action} failed: ${err}`); + } + } + + async getErrorMsg (response) { + try { + const contents = await response.json(); + return JSON.stringify(contents); + } catch (err) { + return await response.text(); + } + } + + getURL (url) { + if (url.startsWith('http')) { + return url; + } + return BASE_URL + url; + } + + async stat (path) { + const splitPath = path.split('/'); + const filename = splitPath.pop(); + const parentDir = splitPath.join('/'); + const url = `jsontree/${this.volumePool}/${this.volume}/${parentDir}?level=2`; + const response = await this.fetch('stat', url); + const files = (await response.json()).root.files || []; + const metadata = files.find(file => file.name === filename); + if(metadata) { + metadata.volume = this.volume; + metadata.volumePool = this.volumePool; + metadata.filename = path; + } else { + throw new Error(`The file at ${path} doesn't exist in ${this.volume}`); + } + return this.createDataInfo(metadata); + } + + async getDownloadResponse (dataInfo) { + let {volume, filename, volumePool='Storage'} = dataInfo.data; + const url = `file/${volumePool}/${volume}/${filename}`; + return await this.fetch('download', url); } - return this.createDataInfo(metadata); - }; - - SciServerFiles.prototype.getDownloadResponse = async function (dataInfo) { - let {volume, filename, volumePool='Storage'} = dataInfo.data; - const url = `file/${volumePool}/${volume}/${filename}`; - return await this.fetch('download', url); - }; + } return SciServerFiles; }); diff --git a/src/common/storage/backends/sciserver-files/metadata.json b/src/common/storage/backends/sciserver-files/metadata.json deleted file mode 100644 index 2099f7ad8..000000000 --- a/src/common/storage/backends/sciserver-files/metadata.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "SciServer Files Service", - "configStructure": [ - { - "name": "username", - "displayName": "Username", - "description": "SciServer account to use", - "value": "", - "valueType": "stringX", - "valueItemsURL": "/routers/SciServerAuth", - "extraValueItems": [ - { - "name": "Link account...", - "type": "URL", - "value": "https://apps.sciserver.org/login-portal/login?callbackUrl=<%= window.location.origin %>/routers/SciServerAuth/register" - } - ], - "readOnly": false, - "isAuth": true - }, - { - "name": "volume", - "displayName": "Volume", - "description": "Volume to use for upload.", - "value": "USERNAME/deepforge_data", - "valueType": "string", - "readOnly": false - }, - { - "name": "volumePool", - "displayName": "Volume Pool", - "description": "Folders and files in User Volumes under “Storage” will be backed up and permanent, but there is a quota limit of 10GB. Folders and files in User Volumes under “Temporary” are not backed up, and will be deleted after a particular time period.", - "value": "Storage", - "valueItems": [ - "Storage", - "Temporary" - ] - } - ] -} diff --git a/src/common/storage/index.js b/src/common/storage/index.js index e97caae71..0275bf9fa 100644 --- a/src/common/storage/index.js +++ b/src/common/storage/index.js @@ -2,7 +2,7 @@ define([ 'module', './backends/StorageBackend', - 'text!deepforge/storage/backends/sciserver-files/metadata.json', + 'deepforge/storage/backends/sciserver-files/metadata', 'deepforge/storage/backends/gme/metadata', 'text!deepforge/storage/backends/s3/metadata.json' ],function( @@ -14,7 +14,7 @@ define([ ) { const Storage = {}; const StorageMetadata = {}; - StorageMetadata['sciserver-files'] = JSON.parse(sciserverFiles); + StorageMetadata['sciserver-files'] = sciserverFiles; StorageMetadata['gme'] = gme; StorageMetadata['s3'] = JSON.parse(s3); const STORAGE_BACKENDS = Object.keys(StorageMetadata); From b26b7bd8544686837c9de732c567990e2052d45b Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 23 Mar 2021 11:43:04 -0500 Subject: [PATCH 10/15] pass token to storage adapter --- .../compute/backends/sciserver-compute/Client.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/common/compute/backends/sciserver-compute/Client.js b/src/common/compute/backends/sciserver-compute/Client.js index 43c80b5a3..19dd7022d 100644 --- a/src/common/compute/backends/sciserver-compute/Client.js +++ b/src/common/compute/backends/sciserver-compute/Client.js @@ -7,7 +7,6 @@ define([ 'module', 'path', 'node-fetch', - 'deepforge/sciserver-auth', 'text!./files/prepare-and-run.sh', ], function( ComputeClient, @@ -17,7 +16,6 @@ define([ module, path, fetch, - getToken, PREPARE_AND_RUN, ) { const Headers = fetch.Headers; @@ -26,6 +24,8 @@ define([ constructor(logger, blobClient, config) { super(logger, blobClient, config); this.username = config.username; + this.token = config.token; + assert(this.token, 'Token not provided. Perhaps configuration skipped prepare step?'); this.computeDomain = config.computeDomain; this.previousJobState = {}; this.consoleOutputLen = {}; @@ -65,6 +65,7 @@ define([ const metadata = await this.blobClient.getMetadata(hash); const config = { username: this.username, + token: this.token, volume: `${this.username}/scratch`, volumePool: 'Temporary' }; @@ -108,23 +109,18 @@ define([ } async fetch (url, opts={}) { - const token = await this.token(); opts.headers = opts.headers || new Headers(); - opts.headers.append('X-Auth-Token', token); + opts.headers.append('X-Auth-Token', this.token); return fetch(url, opts); } - async token () { - return getToken(this.username, this.userId); - } - async getJobState (jobInfo) { const url = 'https://apps.sciserver.org/racm//jobm/rest/dockerjobs'; const opts = { headers: new Headers(), }; - opts.headers.append('X-Auth-Token', await this.token()); + opts.headers.append('X-Auth-Token', this.token); const response = await fetch(url, opts); const {status} = response; From 1d544439ecffd577312cade2d370c6eaac6cfa3a Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 23 Mar 2021 11:50:19 -0500 Subject: [PATCH 11/15] pass token to storage adapter --- src/common/compute/backends/sciserver-compute/Client.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/common/compute/backends/sciserver-compute/Client.js b/src/common/compute/backends/sciserver-compute/Client.js index 19dd7022d..7f2b3319d 100644 --- a/src/common/compute/backends/sciserver-compute/Client.js +++ b/src/common/compute/backends/sciserver-compute/Client.js @@ -231,7 +231,12 @@ define([ volume = owner + '/' + volume; const filename = dirs.slice(3).join('/'); return { - config: {username: this.username, volumePool, volume}, + config: { + username: this.username, + token: this.token, + volumePool, + volume + }, dataInfo: { data: {filename, volume, volumePool} } From cda2bcbf2c193d9f5ee669d6a1f8fedd05127480 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 23 Mar 2021 11:50:41 -0500 Subject: [PATCH 12/15] Remove old imports --- src/common/sciserver-auth.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/common/sciserver-auth.js b/src/common/sciserver-auth.js index c88be9897..b0772bcab 100644 --- a/src/common/sciserver-auth.js +++ b/src/common/sciserver-auth.js @@ -2,12 +2,11 @@ (function(root, factory){ if(typeof define === 'function' && define.amd) { - define(['path', 'module'], function(path, module){ - const __dirname = path.dirname(module.uri); - return factory(require(__dirname + '/../routers/SciServerAuth/Tokens')); + define([], function(){ + return factory(); }); } else if(typeof module === 'object' && module.exports) { - module.exports = factory(require('../routers/SciServerAuth/Tokens')); + module.exports = factory(); } else { root.SciServerAuth = factory(); } From d5047449a4b5480bd7280834cdcc18e8960ad06f Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 23 Mar 2021 11:52:58 -0500 Subject: [PATCH 13/15] Add sciserver metadata file --- .../backends/sciserver-files/metadata.js | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/common/storage/backends/sciserver-files/metadata.js diff --git a/src/common/storage/backends/sciserver-files/metadata.js b/src/common/storage/backends/sciserver-files/metadata.js new file mode 100644 index 000000000..2483f3e76 --- /dev/null +++ b/src/common/storage/backends/sciserver-files/metadata.js @@ -0,0 +1,54 @@ +/*global define*/ +define([ + 'deepforge/sciserver-auth', +], function( + fetchToken, +) { + const metadata = { + name: 'SciServer Files Service', + configStructure: [ + { + name: 'username', + displayName: 'Username', + description: 'SciServer account to use', + value: '', + valueType: 'stringX', + valueItemsURL: '/routers/SciServerAuth', + extraValueItems: [ + { + name: 'Link account...', + type: 'URL', + value: 'https://apps.sciserver.org/login-portal/login?callbackUrl=<%= window.location.origin %>/routers/SciServerAuth/register' + } + ], + readOnly: false, + isAuth: true + }, + { + name: 'volume', + displayName: 'Volume', + description: 'Volume to use for upload.', + value: 'USERNAME/deepforge_data', + valueType: 'string', + readOnly: false + }, + { + name: 'volumePool', + displayName: 'Volume Pool', + description: 'Folders and files in User Volumes under “Storage” will be backed up and permanent, but there is a quota limit of 10GB. Folders and files in User Volumes under “Temporary” are not backed up, and will be deleted after a particular time period.', + value: 'Storage', + valueItems: [ + 'Storage', + 'Temporary' + ] + } + ], + prepare: async config => { + const token = await fetchToken(config.username); + config.token = token; + return config; + } + }; + + return metadata; +}); From 815bc1ec5d08c7dc2dece19ae4c125c3e6ddb23b Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 23 Mar 2021 13:30:22 -0500 Subject: [PATCH 14/15] Remove unused user ID --- src/common/compute/backends/ComputeClient.js | 3 +-- src/common/storage/backends/StorageClient.js | 3 +-- src/plugins/ExecuteJob/ExecuteJob.js | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/common/compute/backends/ComputeClient.js b/src/common/compute/backends/ComputeClient.js index 56a2b3c44..507fbd8c2 100644 --- a/src/common/compute/backends/ComputeClient.js +++ b/src/common/compute/backends/ComputeClient.js @@ -2,9 +2,8 @@ define([], function() { class ComputeClient { - constructor (logger, blobClient, config={}) { + constructor (logger, blobClient) { this.logger = logger.fork('compute'); - this.userId = config.userId; this.blobClient = blobClient; this._events = {}; } diff --git a/src/common/storage/backends/StorageClient.js b/src/common/storage/backends/StorageClient.js index ab8c7e7db..f1178d5d1 100644 --- a/src/common/storage/backends/StorageClient.js +++ b/src/common/storage/backends/StorageClient.js @@ -10,10 +10,9 @@ define([ require.nodeRequire('node-fetch'); const Headers = require.isBrowser ? window.Headers : fetch.Headers; const stream = require.isBrowser ? null : require.nodeRequire('stream'); - const StorageClient = function(id, name, logger, config) { + const StorageClient = function(id, name, logger) { this.id = id; this.name = name; - this.userId = config.userId; if (!logger) { logger = Logger.create(`gme:storage:${id}`, gmeConfig.client.log); } diff --git a/src/plugins/ExecuteJob/ExecuteJob.js b/src/plugins/ExecuteJob/ExecuteJob.js index 6c4f39038..a05c58e34 100644 --- a/src/plugins/ExecuteJob/ExecuteJob.js +++ b/src/plugins/ExecuteJob/ExecuteJob.js @@ -164,7 +164,6 @@ define([ ExecuteJob.prototype.createComputeClient = function () { const config = this.getCurrentConfig(); const backend = Compute.getBackend(config.compute.id); - config.compute.config.userId = this.getUserId(); if (config.compute.id === 'gme') { config.compute.config.webgmeToken = this.blobClient.webgmeToken; // HACK } From 9943194430266ccb74b3ad67d61ab958a54c22c9 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 23 Mar 2021 13:49:44 -0500 Subject: [PATCH 15/15] Add token to test configs --- test/assets/configs/compute.js | 3 ++- test/assets/configs/sciserver.js | 1 + test/assets/configs/storage.js | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/assets/configs/compute.js b/test/assets/configs/compute.js index 6f89313c1..8bf23c9bb 100644 --- a/test/assets/configs/compute.js +++ b/test/assets/configs/compute.js @@ -3,9 +3,10 @@ const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver async function getSciServerJobsConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); - await login(username, password); + const token = await login(username, password); return { username: username, + token: token, volume: `${username}/deepforge_test`, computeDomain: 'Small Jobs Domain', }; diff --git a/test/assets/configs/sciserver.js b/test/assets/configs/sciserver.js index 645d6efd5..9c9eaa87d 100644 --- a/test/assets/configs/sciserver.js +++ b/test/assets/configs/sciserver.js @@ -27,6 +27,7 @@ async function getToken(username, password) { async function login(username, password) { const token = await getToken(username, password); await TokenStorage.register(undefined, username, token); + return token; } function getLoginBody(username, password) { diff --git a/test/assets/configs/storage.js b/test/assets/configs/storage.js index 357f302bb..a471e5e0e 100644 --- a/test/assets/configs/storage.js +++ b/test/assets/configs/storage.js @@ -3,11 +3,11 @@ const {getSciServerPassword, getSciServerUsername, login} = require('./sciserver async function getSciServerFilesConfig() { const username = getSciServerUsername(); const password = getSciServerPassword(); - await login(username, password); + const token = await login(username, password); const volume = `${username}/deepforge_test`; const volumePool = 'Temporary'; - return {username, volume, volumePool}; + return {username, token, volume, volumePool}; } function getS3Config() {