From 1277344c80de892d9246e1aa2d045a27c7a659f7 Mon Sep 17 00:00:00 2001 From: ste Date: Thu, 3 Jan 2019 19:08:01 +0000 Subject: [PATCH 1/2] GPII-3496: Auto-login implementation. --- .../gpii.pouchManager.config.base.json5 | 6 + gpii/node_modules/userListeners/index.js | 1 + .../userListeners/src/autologin.js | 86 ++++++++ .../userListeners/src/listeners.js | 87 +++++++- gpii/node_modules/userListeners/src/usb.js | 24 +-- .../userListeners/test/all-tests.js | 1 + .../userListeners/test/autologinTests.js | 200 ++++++++++++++++++ .../userListeners/test/pcscTests.js | 3 + .../userListeners/test/usbTests.js | 3 + 9 files changed, 383 insertions(+), 28 deletions(-) create mode 100644 gpii/node_modules/userListeners/src/autologin.js create mode 100644 gpii/node_modules/userListeners/test/autologinTests.js diff --git a/gpii/node_modules/pouchManager/configs/gpii.pouchManager.config.base.json5 b/gpii/node_modules/pouchManager/configs/gpii.pouchManager.config.base.json5 index 19484578c..92eb34e03 100644 --- a/gpii/node_modules/pouchManager/configs/gpii.pouchManager.config.base.json5 +++ b/gpii/node_modules/pouchManager/configs/gpii.pouchManager.config.base.json5 @@ -24,6 +24,12 @@ ] } } + }, + "distributeOptions": { + "delayListeners": { + "target": "{that userListeners}.options.events.onListenersStart.events.pouchManager", + "record": "{pouchManager}.events.onReady" + } } }, "require": ["gpii-pouchdb", "pouchManager", "journal"] diff --git a/gpii/node_modules/userListeners/index.js b/gpii/node_modules/userListeners/index.js index b43dee974..7a69dc57a 100644 --- a/gpii/node_modules/userListeners/index.js +++ b/gpii/node_modules/userListeners/index.js @@ -25,3 +25,4 @@ fluid.module.register("userListeners", __dirname, require); require("./src/listeners.js"); require("./src/pcsc.js"); require("./src/usb.js"); +require("./src/autologin.js"); diff --git a/gpii/node_modules/userListeners/src/autologin.js b/gpii/node_modules/userListeners/src/autologin.js new file mode 100644 index 000000000..c896c6c19 --- /dev/null +++ b/gpii/node_modules/userListeners/src/autologin.js @@ -0,0 +1,86 @@ +/* AutoLogin user listener. + * + * Copyright 2017 Raising the Floor - International + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * The R&D leading to these results received funding from the + * Department of Education - Grant H421A150005 (GPII-APCP). However, + * these results do not necessarily represent the policy of the + * Department of Education, and you should not assume endorsement by the + * Federal Government. + * + * You may obtain a copy of the License at + * https://github.com/GPII/universal/blob/master/LICENSE.txt + */ + +"use strict"; + +var fluid = require("infusion"); + +var gpii = fluid.registerNamespace("gpii"); + +// The AutoLogin user listener. +fluid.defaults("gpii.userListeners.autoLogin", { + gradeNames: ["fluid.component", "fluid.contextAware", "gpii.userListener"], + contextAwareness: { + platform: { + checks: { + windows: { + contextValue: "{gpii.contexts.windows}", + gradeNames: "gpii.userListeners.autoLogin.windows" + } + } + } + }, + members: { + tokenDirectory: "@expand:{settingsDir}.getGpiiSettingsDir()", + tokenFile: ".gpii-user-token.txt", + done: false, + listenerName: "auto-login" + }, + components: { + settingsDir: { + type: "gpii.settingsDir" + } + }, + invokers: { + startListener: "{that}.attemptLogin", + stopListener: "fluid.identity", + attemptLogin: { + funcName: "gpii.userListeners.autoLogin.attemptLogin", + args: ["{that}"] + }, + getToken: { + func: "{that}.readTokenFile", + args: ["{that}.tokenDirectory", "{that}.tokenFile"] + } + } +}); + +/** + * Performs the auto-login, by raising the onTokenArrive event with the result of getToken(). + * + * This should only be called once, during startup. Subsequent calls will have no effect. + * + * @param {Component} that - The gpii.userListeners.autoLogin instance. + * @return {Promise} A promise resolving when the token has been read. + */ +gpii.userListeners.autoLogin.attemptLogin = function (that) { + var promise; + + if (that.done) { + promise = fluid.promise().reject({ + isError: true, + message: "autoLogin.attemptLogin had already been called." + }); + } else { + that.done = true; + promise = that.getToken().then(function (token) { + that.events.onTokenArrive.fire(that, token); + }); + } + + return promise; +}; diff --git a/gpii/node_modules/userListeners/src/listeners.js b/gpii/node_modules/userListeners/src/listeners.js index 14fe11d56..de0a436a8 100644 --- a/gpii/node_modules/userListeners/src/listeners.js +++ b/gpii/node_modules/userListeners/src/listeners.js @@ -18,7 +18,9 @@ "use strict"; var fluid = require("infusion"), - request = require("request"); + request = require("request"), + fs = require("fs"), + path = require("path"); var gpii = fluid.registerNamespace("gpii"); @@ -43,22 +45,38 @@ fluid.defaults("gpii.userListeners", { }, usb: { type: "gpii.userListeners.usb" + }, + autoLogin: { + type: "gpii.userListeners.autoLogin" } }, events: { // The listeners are starting. - "onListenersStart": null, + "onListenersStart": { + // If pouch is being used, its onReady event is distributed here. + events: { + kettle: "{that kettle.server}.events.onListen" + } + }, // The listeners are stopping. "onListenersStop": null - }, - listeners: { - "onCreate.startListeners": "{that}.events.onListenersStart" } }); // A user listener. fluid.defaults("gpii.userListener", { gradeNames: ["fluid.component"], + contextAwareness: { + platform: { + checks: { + test: { + contextValue: "{gpii.contexts.test}", + gradeNames: ["gpii.userListener.test"] + } + }, + defaultGradeNames: "gpii.userListener.active" + } + }, events: { // A token has arrived. "onTokenArrive": null, @@ -86,6 +104,10 @@ fluid.defaults("gpii.userListener", { "{that}", "{arguments}.0" // The error. ] + }, + readTokenFile: { + funcName: "gpii.userListeners.readTokenFile", + args: [ "{arguments}.0", "{arguments}.1" ] // directory, file } }, members: { @@ -97,10 +119,6 @@ fluid.defaults("gpii.userListener", { failCount: 0 }, listeners: { - // Start/stop this listener - "{userListeners}.events.onListenersStart": "{that}.startListener", - "{userListeners}.events.onListenersStop": "{that}.stopListener", - "onTokenArrive.callFlowManager": { funcName: "gpii.userListeners.tokenArrived", args: [ @@ -121,6 +139,14 @@ fluid.defaults("gpii.userListener", { failDelay: 10 }); +fluid.defaults("gpii.userListener.active", { + listeners: { + // Start/stop this listener + "{userListeners}.events.onListenersStart": "{that}.startListener", + "{userListeners}.events.onListenersStop": "{that}.stopListener" + } +}); + /** * Handles the onTokenArrive event. * @@ -176,3 +202,46 @@ gpii.userListeners.failed = function (that, err) { }); }); }; + +/** + * Reads a token from the file. + * + * @param {String} tokenDirectory - The directory containing the token file. + * @param {String} tokenFile [optional] The file, in tokenDirectory, containing the token. Can be omitted if the first + * argument is the full path to the file. + * @return {Promise} resolves when the token is read, rejects if there's no file (or there was an error reading it). + */ +gpii.userListeners.readTokenFile = function (tokenDirectory, tokenFile) { + var promise = fluid.promise(); + + // Try to read the token file. + var tokenPath = path.join(tokenDirectory, tokenFile || ""); + fluid.log("Reading token file: ", tokenPath); + fs.readFile(tokenPath, "utf8", function (err, data) { + if (err) { + if (err.code === "ENOENT") { + fluid.log("Token file not found"); + promise.reject(); + } else { + var message = "Error reading token file " + tokenPath + ": " + err.message; + fluid.log(message); + promise.reject({ + isError: true, + message: message, + error: err + }); + } + } else { + var token = data.trim(); + if (token.length > 0) { + fluid.log("Got token: ", token); + promise.resolve(token); + } else { + fluid.log("Empty token file"); + promise.reject(); + } + } + }); + + return promise; +}; diff --git a/gpii/node_modules/userListeners/src/usb.js b/gpii/node_modules/userListeners/src/usb.js index 0731b1c42..e182a8cc8 100644 --- a/gpii/node_modules/userListeners/src/usb.js +++ b/gpii/node_modules/userListeners/src/usb.js @@ -17,9 +17,7 @@ "use strict"; -var fluid = require("infusion"), - fs = require("fs"), - path = require("path"); +var fluid = require("infusion"); var gpii = fluid.registerNamespace("gpii"); @@ -92,22 +90,10 @@ gpii.userListeners.usb.readUSBToken = function (that, usbPath) { }); } else { // Try to read the token file. - var tokenFile = path.join(usbPath, that.tokenFile); - fs.readFile(tokenFile, "utf8", function (err, data) { - if (err) { - fluid.log("Error reading token from device (" + usbPath + "):"); - promise.reject({ - isError: true, - message: "Error reading token from device (" + usbPath + ")", - error: err - }); - } else { - that.currentDevice.token = data.trim(); - that.currentDevice.path = usbPath; - fluid.log("Got token from USB device (" + that.currentDevice.path + "):", that.currentDevice.token); - that.events.onTokenArrive.fire(that, that.currentDevice.token); - promise.resolve(that.currentDevice.token); - } + promise = that.readTokenFile(usbPath, that.tokenFile).then(function (token) { + that.currentDevice.token = token; + that.currentDevice.path = usbPath; + that.events.onTokenArrive.fire(that, that.currentDevice.token); }); } diff --git a/gpii/node_modules/userListeners/test/all-tests.js b/gpii/node_modules/userListeners/test/all-tests.js index 1b2f7faba..712813a33 100644 --- a/gpii/node_modules/userListeners/test/all-tests.js +++ b/gpii/node_modules/userListeners/test/all-tests.js @@ -18,5 +18,6 @@ "use strict"; +require("./autologinTests.js"); require("./pcscTests.js"); require("./usbTests.js"); diff --git a/gpii/node_modules/userListeners/test/autologinTests.js b/gpii/node_modules/userListeners/test/autologinTests.js new file mode 100644 index 000000000..3c5818cd9 --- /dev/null +++ b/gpii/node_modules/userListeners/test/autologinTests.js @@ -0,0 +1,200 @@ +/* + * Auto-login User listener tests + * + * Copyright 2017 Raising the Floor - International + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * The R&D leading to these results received funding from the + * Department of Education - Grant H421A150005 (GPII-APCP). However, + * these results do not necessarily represent the policy of the + * Department of Education, and you should not assume endorsement by the + * Federal Government. + * + * You may obtain a copy of the License at + * https://github.com/GPII/universal/blob/master/LICENSE.txt + */ + +"use strict"; +var fluid = require("infusion"), + os = require("os"), + path = require("path"), + fs = require("fs"); + +var jqUnit = fluid.require("node-jqunit"); +var gpii = fluid.registerNamespace("gpii"); +fluid.registerNamespace("gpii.tests.userListener"); + +require("../index.js"); + +fluid.defaults("gpii.tests.userListener.autoLoginListener", { + gradeNames: ["fluid.component"], + listeners: { + "onTokenArrive.callFlowManager": "fluid.identity", + "onTokenRemove.callFlowManager": "fluid.identity" + }, + invokers: { + startListener: "fluid.identity", + stopListener: "fluid.identity" + } +}); + +var teardowns = []; +jqUnit.module("gpii.tests.userListener.autoLogin", { + teardown: function () { + while (teardowns.length) { + teardowns.pop()(); + } + } +}); + +gpii.tests.userListener.tokenFileTests = fluid.freezeRecursive([ + { + filename: "test1", + content: "test-key1", + expect: "test-key1" + }, + { + filename: "test2", + content: " \n \t test-key2 \n \t \r", + expect: "test-key2" + }, + { + filename: "test3", + content: "", + expect: "reject" + }, + { + filename: "test4", + content: " \r\n\t", + expect: "reject" + }, + { + directory: "/does/not/exist", + filename: "a", + expect: "reject" + }, + { + directory: "/", + filename: "", + expect: "reject" + }, + { + directory: "", + filename: "", + expect: "reject" + } +]); + +/** + * Get an instance of the gpii.userListeners.autoLogin component. + * @return {Component} The gpii.userListeners.autoLogin component. + */ +gpii.tests.userListener.getAutoLoginListener = function () { + var userListeners = gpii.userListeners({ + listeners: { + "onCreate.startListeners": "fluid.identity" + }, + events: { + "onListenersStart": null + }, + distributeOptions: { + record: "gpii.tests.userListener.autoLoginListener", + target: "{that autoLogin}.options.gradeNames" + } + }); + + return userListeners.autoLogin; +}; + +jqUnit.asyncTest("autoLogin readTokenFile", function () { + + var tests = gpii.tests.userListener.tokenFileTests; + + jqUnit.expect(tests.length * 2); + var tempDir = path.join(os.tmpdir(), "gpii-listener-tests" + Math.random()); + fs.mkdirSync(tempDir); + var tempFiles = []; + + teardowns.push(function () { + while (tempFiles.length) { + fs.unlinkSync(tempFiles.pop()); + } + fs.rmdirSync(tempDir); + }); + + var doTest = function (index) { + if (index >= tests.length) { + jqUnit.start(); + } else { + var currentTest = tests[index]; + var messagePrefix = " - test index " + index; + + if (currentTest.hasOwnProperty("content")) { + var tempFile = path.join(tempDir, currentTest.filename); + fs.writeFileSync(tempFile, currentTest.content, "utf8"); + tempFiles.push(tempFile); + } + + var directory = currentTest.hasOwnProperty("directory") ? currentTest.directory : tempDir; + var file = currentTest.filename; + + var p = gpii.userListeners.readTokenFile(directory, file); + + jqUnit.assertTrue("readTokenFile must return a promise" + messagePrefix, fluid.isPromise(p)); + + p.then(function (token) { + jqUnit.assertEquals("readTokenFile should resolve with the expected value" + messagePrefix, + currentTest.expect, token); + + doTest(index + 1); + }, function () { + jqUnit.assertEquals("readTokenFile should reject if expected" + messagePrefix, + currentTest.expect, "reject"); + + doTest(index + 1); + }); + } + }; + + doTest(0); +}); + + +jqUnit.asyncTest("Test attemptLogin", function () { + + jqUnit.expect(2); + + var testToken = "test-token" + Math.random(); + + // Write the test token file. + var tempDir = os.tmpdir(); + var tokenFile = "test-token-file" + Math.random(); + var tokenPath = path.join(tempDir, tokenFile); + fs.writeFileSync(tokenPath, testToken, "utf8"); + + teardowns.push(function () { + fs.unlinkSync(tokenPath); + }); + + var autoLogin = gpii.tests.userListener.getAutoLoginListener(); + + autoLogin.tokenDirectory = tempDir; + autoLogin.tokenFile = tokenFile; + + var eventRaised = false; + + autoLogin.events.onTokenArrive.addListener(function (that, token) { + jqUnit.assertFalse("Token event should only fire once", eventRaised); + eventRaised = true; + jqUnit.assertEquals("Token in event should match the test token", testToken, token); + }); + + gpii.userListeners.autoLogin.attemptLogin(autoLogin).then(function () { + // try it again - it should reject. + gpii.userListeners.autoLogin.attemptLogin(autoLogin).then(function () { + jqUnit.fail("Second call to attemptLogin should reject"); + }, jqUnit.start); + }); +}); diff --git a/gpii/node_modules/userListeners/test/pcscTests.js b/gpii/node_modules/userListeners/test/pcscTests.js index cf3b61973..45e524e13 100644 --- a/gpii/node_modules/userListeners/test/pcscTests.js +++ b/gpii/node_modules/userListeners/test/pcscTests.js @@ -138,6 +138,9 @@ gpii.tests.userListener.getPcscListener = function () { listeners: { "onCreate.startListeners": "fluid.identity" }, + events: { + "onListenersStart": null + }, distributeOptions: { record: "gpii.test.userListeners.pcsc", target: "{that pcsc}.options.gradeNames" diff --git a/gpii/node_modules/userListeners/test/usbTests.js b/gpii/node_modules/userListeners/test/usbTests.js index 97c35eec3..95b596a52 100644 --- a/gpii/node_modules/userListeners/test/usbTests.js +++ b/gpii/node_modules/userListeners/test/usbTests.js @@ -57,6 +57,9 @@ gpii.tests.userListener.getUSBListener = function () { listeners: { "onCreate.startListeners": "fluid.identity" }, + events: { + "onListenersStart": null + }, distributeOptions: { record: "gpii.tests.userListener.usbListener", target: "{that usb}.options.gradeNames" From 6256052879fba828ed685412fd8afabfdef4952f Mon Sep 17 00:00:00 2001 From: ste Date: Thu, 10 Jan 2019 00:01:42 +0000 Subject: [PATCH 2/2] GPII-3496: Added storing of the user token on session start. --- .../userListeners/src/autologin.js | 31 ++++++- .../userListeners/src/listeners.js | 60 ++++++++----- .../userListeners/test/autologinTests.js | 89 ++++++++++++++++++- .../userListeners/test/pcscTests.js | 10 ++- .../userListeners/test/usbTests.js | 10 ++- 5 files changed, 173 insertions(+), 27 deletions(-) diff --git a/gpii/node_modules/userListeners/src/autologin.js b/gpii/node_modules/userListeners/src/autologin.js index c896c6c19..5bd370c57 100644 --- a/gpii/node_modules/userListeners/src/autologin.js +++ b/gpii/node_modules/userListeners/src/autologin.js @@ -55,10 +55,39 @@ fluid.defaults("gpii.userListeners.autoLogin", { getToken: { func: "{that}.readTokenFile", args: ["{that}.tokenDirectory", "{that}.tokenFile"] + }, + storeToken: { + func: "{that}.writeTokenFile", + args: [ "{that}.tokenDirectory", "{that}.tokenFile", "{arguments}.0" ] } - } + }, + listeners: { + "onCreate.loginListener": { + funcName: "gpii.userListeners.autoLogin.addLoginListener", + args: [ "{that}", "{lifecycleManager}.events.onSessionStart"] + } + }, + // Disabling by default, as it is the safest and will prevent people's keys from being accidentally stored when it + // is not desired (eg, on shared or public access computers). + saveLastLogin: false }); +/** + * Adds a listener to the onSessionStart event of the lifecycle manager, which stores the token file. + * + * @param {Component} that The gpii.userListeners.autoLogin instance. + * @param {EventFirer} firer The event firer for the onSessionStart event. + */ +gpii.userListeners.autoLogin.addLoginListener = function (that, firer) { + if (that.options.saveLastLogin) { + firer.addListener(function (gradeName, gpiiKey) { + if (gpiiKey !== "noUser") { + that.storeToken(gpiiKey); + } + }); + } +}; + /** * Performs the auto-login, by raising the onTokenArrive event with the result of getToken(). * diff --git a/gpii/node_modules/userListeners/src/listeners.js b/gpii/node_modules/userListeners/src/listeners.js index de0a436a8..c716844df 100644 --- a/gpii/node_modules/userListeners/src/listeners.js +++ b/gpii/node_modules/userListeners/src/listeners.js @@ -55,7 +55,7 @@ fluid.defaults("gpii.userListeners", { "onListenersStart": { // If pouch is being used, its onReady event is distributed here. events: { - kettle: "{that kettle.server}.events.onListen" + kettle: "{kettle.server}.events.onListen" } }, // The listeners are stopping. @@ -66,17 +66,6 @@ fluid.defaults("gpii.userListeners", { // A user listener. fluid.defaults("gpii.userListener", { gradeNames: ["fluid.component"], - contextAwareness: { - platform: { - checks: { - test: { - contextValue: "{gpii.contexts.test}", - gradeNames: ["gpii.userListener.test"] - } - }, - defaultGradeNames: "gpii.userListener.active" - } - }, events: { // A token has arrived. "onTokenArrive": null, @@ -108,6 +97,10 @@ fluid.defaults("gpii.userListener", { readTokenFile: { funcName: "gpii.userListeners.readTokenFile", args: [ "{arguments}.0", "{arguments}.1" ] // directory, file + }, + writeTokenFile: { + funcName: "gpii.userListeners.writeTokenFile", + args: [ "{arguments}.0", "{arguments}.1", "{arguments}.2" ] // directory, file, token } }, members: { @@ -119,6 +112,9 @@ fluid.defaults("gpii.userListener", { failCount: 0 }, listeners: { + // Start/stop this listener + "{userListeners}.events.onListenersStart": "{that}.startListener", + "{userListeners}.events.onListenersStop": "{that}.stopListener", "onTokenArrive.callFlowManager": { funcName: "gpii.userListeners.tokenArrived", args: [ @@ -139,14 +135,6 @@ fluid.defaults("gpii.userListener", { failDelay: 10 }); -fluid.defaults("gpii.userListener.active", { - listeners: { - // Start/stop this listener - "{userListeners}.events.onListenersStart": "{that}.startListener", - "{userListeners}.events.onListenersStop": "{that}.stopListener" - } -}); - /** * Handles the onTokenArrive event. * @@ -245,3 +233,35 @@ gpii.userListeners.readTokenFile = function (tokenDirectory, tokenFile) { return promise; }; + +/** + * Writes a token to a file. + * + * @param {String} tokenDirectory - The directory containing the token file. + * @param {String} tokenFile [optional] The file, in tokenDirectory, containing the token. Can be omitted if the first + * argument is the full path to the file. + * @param {String} tokenValue - The token's value. + * @return {Promise} resolves when complete, with a value of true if it was written. + */ +gpii.userListeners.writeTokenFile = function (tokenDirectory, tokenFile, tokenValue) { + var promise = fluid.promise(); + + var tokenPath = path.join(tokenDirectory, tokenFile || ""); + fluid.log("Writing token '" + tokenValue + "' to ", tokenPath); + + fs.writeFile(tokenPath, tokenValue, "utf8", function (err) { + if (err) { + var message = "Error writing token file " + tokenPath + ": " + err.message; + fluid.log(message); + promise.reject({ + isError: true, + message: message, + error: err + }); + } else { + promise.resolve(true); + } + }); + + return promise; +}; diff --git a/gpii/node_modules/userListeners/test/autologinTests.js b/gpii/node_modules/userListeners/test/autologinTests.js index 3c5818cd9..4e66e6e9d 100644 --- a/gpii/node_modules/userListeners/test/autologinTests.js +++ b/gpii/node_modules/userListeners/test/autologinTests.js @@ -32,7 +32,8 @@ fluid.defaults("gpii.tests.userListener.autoLoginListener", { gradeNames: ["fluid.component"], listeners: { "onTokenArrive.callFlowManager": "fluid.identity", - "onTokenRemove.callFlowManager": "fluid.identity" + "onTokenRemove.callFlowManager": "fluid.identity", + "onCreate.loginListener": "fluid.identity" }, invokers: { startListener: "fluid.identity", @@ -161,7 +162,54 @@ jqUnit.asyncTest("autoLogin readTokenFile", function () { doTest(0); }); +jqUnit.asyncTest("autoLogin writeTokenFile", function () { + var tempDir = os.tmpdir(); + var tokenFile = "test-token-file" + Math.random(); + var tokenPath = path.join(tempDir, tokenFile); + + teardowns.push(function () { + try { + fs.unlinkSync(tokenPath); + } catch (e) { + // ignore + } + }); + + fluid.promise.sequence([ + function () { + var p = gpii.userListeners.writeTokenFile(tempDir, tokenFile, "token1", true); + jqUnit.assertTrue("1st writeTokenFile should return a promise", fluid.isPromise(p)); + + return p.then(function () { + var content = fs.readFileSync(tokenPath, "utf8"); + jqUnit.assertEquals("Token in file should match what was written", "token1", content); + }); + }, + function () { + // Using the same path as before also tests over-writing. + var p = gpii.userListeners.writeTokenFile(tokenPath, null, "token2", true); + jqUnit.assertTrue("2nd writeTokenFile should return a promise", fluid.isPromise(p)); + + return p.then(function () { + var content = fs.readFileSync(tokenPath, "utf8"); + jqUnit.assertEquals("Token in file should match what was written", "token2", content); + }); + }, + function () { + var p = gpii.userListeners.writeTokenFile("does/not/exist", tokenFile, "token3", true); + jqUnit.assertTrue("3rd writeTokenFile should return a promise", fluid.isPromise(p)); + + var promiseTogo = fluid.promise(); + p.then(function () { + jqUnit.fail("writeTokenFile with a bad path should reject"); + promiseTogo.reject(); + }, promiseTogo.resolve); + } + ]).then(jqUnit.start, jqUnit.fail); +}); + +// Test if the login on startup gets called. jqUnit.asyncTest("Test attemptLogin", function () { jqUnit.expect(2); @@ -175,7 +223,11 @@ jqUnit.asyncTest("Test attemptLogin", function () { fs.writeFileSync(tokenPath, testToken, "utf8"); teardowns.push(function () { - fs.unlinkSync(tokenPath); + try { + fs.unlinkSync(tokenPath); + } catch (e) { + // ignore + } }); var autoLogin = gpii.tests.userListener.getAutoLoginListener(); @@ -198,3 +250,36 @@ jqUnit.asyncTest("Test attemptLogin", function () { }, jqUnit.start); }); }); + +// Tests that the onSessionStart event would be listened upon if configured to. +jqUnit.test("test addLoginListener", function () { + + var tokenValue = "test-token"; + var tokenWrote = false; + + var autoLogin = gpii.tests.userListener.getAutoLoginListener(); + + // Fake the storeToken routine. + autoLogin.storeToken = function (token) { + tokenWrote = true; + jqUnit.assertEquals("Correct token should be written", tokenValue, token); + }; + + var firer = fluid.makeEventFirer(); + + // Test when saving is disabled + autoLogin.options.saveLastLogin = false; + gpii.userListeners.autoLogin.addLoginListener(autoLogin, firer); + firer.fire("dummy", tokenValue); + + jqUnit.assertFalse("Token should not have been written", tokenWrote); + + // Test when saving is enabled + autoLogin.options.saveLastLogin = true; + gpii.userListeners.autoLogin.addLoginListener(autoLogin, firer); + firer.fire("dummy", tokenValue); + + jqUnit.assertTrue("Token should have been written", tokenWrote); + + autoLogin.destroy(); +}); diff --git a/gpii/node_modules/userListeners/test/pcscTests.js b/gpii/node_modules/userListeners/test/pcscTests.js index 45e524e13..ded8250dc 100644 --- a/gpii/node_modules/userListeners/test/pcscTests.js +++ b/gpii/node_modules/userListeners/test/pcscTests.js @@ -142,8 +142,14 @@ gpii.tests.userListener.getPcscListener = function () { "onListenersStart": null }, distributeOptions: { - record: "gpii.test.userListeners.pcsc", - target: "{that pcsc}.options.gradeNames" + pcsc: { + record: "gpii.test.userListeners.pcsc", + target: "{that pcsc}.options.gradeNames" + }, + autoLogin: { + record: "gpii.tests.userListener.autoLoginListener", + target: "{that autoLogin}.options.gradeNames" + } } }); diff --git a/gpii/node_modules/userListeners/test/usbTests.js b/gpii/node_modules/userListeners/test/usbTests.js index 95b596a52..14cea51a1 100644 --- a/gpii/node_modules/userListeners/test/usbTests.js +++ b/gpii/node_modules/userListeners/test/usbTests.js @@ -61,8 +61,14 @@ gpii.tests.userListener.getUSBListener = function () { "onListenersStart": null }, distributeOptions: { - record: "gpii.tests.userListener.usbListener", - target: "{that usb}.options.gradeNames" + usb: { + record: "gpii.tests.userListener.usbListener", + target: "{that usb}.options.gradeNames" + }, + autoLogin: { + record: "gpii.tests.userListener.autoLoginListener", + target: "{that autoLogin}.options.gradeNames" + } } });