Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPII-3496: Auto-login with user folder #725

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions gpii/node_modules/userListeners/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
115 changes: 115 additions & 0 deletions gpii/node_modules/userListeners/src/autologin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* 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"]
},
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().
*
* 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;
};
101 changes: 95 additions & 6 deletions gpii/node_modules/userListeners/src/listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand All @@ -43,16 +45,21 @@ 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: "{kettle.server}.events.onListen"
}
},
// The listeners are stopping.
"onListenersStop": null
},
listeners: {
"onCreate.startListeners": "{that}.events.onListenersStart"
}
});

Expand Down Expand Up @@ -86,6 +93,14 @@ fluid.defaults("gpii.userListener", {
"{that}",
"{arguments}.0" // The error.
]
},
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: {
Expand All @@ -100,7 +115,6 @@ fluid.defaults("gpii.userListener", {
// Start/stop this listener
"{userListeners}.events.onListenersStart": "{that}.startListener",
"{userListeners}.events.onListenersStop": "{that}.stopListener",

"onTokenArrive.callFlowManager": {
funcName: "gpii.userListeners.tokenArrived",
args: [
Expand Down Expand Up @@ -176,3 +190,78 @@ 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;
};

/**
* 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;
};
24 changes: 5 additions & 19 deletions gpii/node_modules/userListeners/src/usb.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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);
});
}

Expand Down
1 change: 1 addition & 0 deletions gpii/node_modules/userListeners/test/all-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@

"use strict";

require("./autologinTests.js");
require("./pcscTests.js");
require("./usbTests.js");
Loading