gpii-universal
Version:
Cross platform, core components of the GPII personalization infrastructure.
311 lines (289 loc) • 14 kB
JavaScript
/*
* GPII User Logon State Change
*
* Copyright 2012 OCAD University
* Copyright 2015, 2017 Raising the Floor - International
* Copyright 2018 OCAD University
*
* Licensed under the New BSD license. You may not use this file except in
* compliance with this License.
*
* The research leading to these results has received funding from the European Union's
* Seventh Framework Programme (FP7/2007-2013)
* under grant agreement no. 289016.
*
* 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");
fluid.registerNamespace("gpii.lifecycleManager.userLogonHandling");
gpii.lifecycleManager.userLogonHandling.handleResetGpiiKey = function (that, lifecycleManager) {
var activeGpiiKey = lifecycleManager.getActiveSessionGpiiKey(); // find currently logged in user:
that.defaultSettingsDataPromise.then(function (defaultSettingsData) {
var resetActions = [];
if (activeGpiiKey) {
fluid.log("Reset: logging out the user with GPII key " + activeGpiiKey);
resetActions.push(that.logoutUserWithoutResponse(activeGpiiKey));
}
if (defaultSettingsData.defaultSnapshot) {
fluid.log("Reset: restoring the system to the default snapshot: ", defaultSettingsData.defaultSnapshot);
resetActions.push(lifecycleManager.restoreSnapshot(defaultSettingsData.defaultSnapshot));
}
var promise = fluid.promise.sequence(resetActions);
// A separate logout of "reset" is not necessary because of the special behavior of restoreSnapshot() that uses a
// special "restore" key and also detroys the restore session once the restoration completes. The final condition
// of using the "reset" key is to have the "noUser" log back in the system.
promise.then(function () {
that.events.onSuccess.fire("Reset successfully.");
}, function (error) {
lifecycleManager.errorResponse(that, "Reset: Error occurred during logout: " + error.message);
});
});
};
gpii.lifecycleManager.userLogonHandling.startLifecycle = function (that, lifecycleManager, lifecyclePayload, event) {
gpii.logFully("userLogonHandling.startLifecycle got final payload ", gpii.renderMegapayload(lifecyclePayload));
that.defaultSettingsDataPromise.then(function (defaultSettingsData) {
if (defaultSettingsData.defaultLifecycleInstructions && lifecyclePayload.gpiiKey !== "noUser") {
// Merge the default snapshot with current key's lifecycle instructions to combine the key in action
// with "reset all" functionality
fluid.set(lifecyclePayload, ["activeConfiguration", "lifecycleInstructions"], fluid.extend(
true, {}, defaultSettingsData.defaultLifecycleInstructions, fluid.get(lifecyclePayload, ["activeConfiguration", "lifecycleInstructions"])
));
}
var promise = lifecycleManager.start(lifecyclePayload);
promise.then(function () {
gpii.lifecycleManager.userLogonHandling.updateLogonChangeModel(lifecycleManager, lifecyclePayload.gpiiKey, "login", false);
event.fire("User with GPII key " + lifecyclePayload.gpiiKey + " was successfully logged in.");
}, function (error) {
fluid.log(fluid.logLevel.FAIL, "Error returned from lifecycle manager: ", error);
lifecycleManager.errorResponse(that, "Error occurred during login: " + error.message);
});
});
};
/**
* Updates the `currentUserLogon` portion of the lifecycle manager model.
*
* @param {Object} lifecycleManager - The lifecycleManager component.
* @param {String} gpiiKey - The gpiiKey of the user who is previously logging in or out.
* @param {String} logonType - "login" or "logout" depending on whether its a login or logout.
*/
gpii.lifecycleManager.userLogonHandling.updateCurrentUserLogon = function (lifecycleManager, gpiiKey, logonType) {
lifecycleManager.applier.change("currentUserLogon", {
type: logonType, // "login"/"logout"
gpiiKey: gpiiKey,
timeStamp: Date.now()
});
};
/**
* Updates the `logonChange` portion of the lifecycle manager model.
*
* @param {Object} lifecycleManager - The lifecycleManager component.
* @param {String} gpiiKey - The gpiiKey of the user who is logging in or out.
* @param {String} logonType - "login" or "logout" depending on whether its a login or logout.
* @param {Boolean} inProgress - If this call signifies a login that is (or is about to be) in progress.
*/
gpii.lifecycleManager.userLogonHandling.updateLogonChangeModel = function (lifecycleManager, gpiiKey, logonType, inProgress) {
lifecycleManager.applier.change("logonChange", {
type: logonType, // "login"/"logout"
inProgress: inProgress, // boolean
gpiiKey: gpiiKey, // string with GPII key
timeStamp: Date.now()
});
};
/*
* Resets the logon change model of the lifecycle manager. This means that the
* current GPII key is set to undefined, and inProgress set to false
*/
gpii.lifecycleManager.userLogonHandling.resetLogonChangeModel = function (lifecycleManager) {
fluid.log("Resetting logon change model of lifecycleManager");
gpii.lifecycleManager.userLogonHandling.updateLogonChangeModel(lifecycleManager, undefined, "logout", false);
};
/**
* Does a logout of the user with the given token. No response will be sent to the onError and onSuccess events
* but a promise will be returned for the logout action.
*
* @param {Component} that - An instance of stateChangeHandler component.
* @param {Component} lifecycleManager - An instance of lifecycleManager component.
* @param {String} gpiiKey - The user to logout.
* @return {Promise} A promise with successful or failed user logout action.
*/
gpii.lifecycleManager.userLogonHandling.logoutUserWithoutResponse = function (that, lifecycleManager, gpiiKey) {
gpii.lifecycleManager.userLogonHandling.updateLogonChangeModel(lifecycleManager, gpiiKey, "logout", true);
var promiseTogo = fluid.promise();
var stopPromise = lifecycleManager.stop({
gpiiKey: gpiiKey
});
stopPromise.then(function (response) {
fluid.log("userLogonStateChange.logoutUser: Lifecycle manager returned: ", response);
gpii.lifecycleManager.userLogonHandling.updateLogonChangeModel(lifecycleManager, gpiiKey, "logout", false);
promiseTogo.resolve(response);
}, function (error) {
fluid.log(fluid.logLevel.FAIL, "userLogonStateChange.logoutUser: An error occurred when attempting to log out user: ", error);
gpii.lifecycleManager.userLogonHandling.updateLogonChangeModel(lifecycleManager, gpiiKey, "logout", false);
promiseTogo.reject(error);
});
return promiseTogo;
};
// Logs out user with the logoutUserWithoutResponse function, but calls the relevant events on the logout request
gpii.lifecycleManager.userLogonHandling.logoutUser = function (that, lifecycleManager, gpiiKey) {
var promise = that.logoutUserWithoutResponse(gpiiKey);
promise.then(function () {
that.events.onSuccess.fire("User with GPII key " + gpiiKey + " was successfully logged out.");
}, function (error) {
lifecycleManager.errorResponse(that, "Error occurred during logout: " + error.message);
});
};
gpii.lifecycleManager.userLogonHandling.loginUser = function (that, lifecycleManager, gpiiKey) {
gpii.lifecycleManager.userLogonHandling.updateLogonChangeModel(lifecycleManager, gpiiKey, "login", true);
that.events.onGpiiKey.fire(gpiiKey);
};
gpii.lifecycleManager.getDeviceContext = function (deviceReporter, event, errorEvent) {
gpii.lifecycleManager.getDeviceContextPromise(deviceReporter).then(
event.fire,
errorEvent.fire
);
};
gpii.lifecycleManager.getDeviceContextPromise = function (deviceReporter) {
// GPII-4324 TODO: We have a factoring problem here since properly the LifecycleManager has no business
// reaching out into the environment to interact with the DeviceReporter, as shown by the
// awkward-looking reference {flowManager}.deviceReporter which is out of place with the others.
// However, this ended up being a natural place for the implementation since it is scoped to the
// matchmaking workflow which is tied to lifecycleManager's userLogonRequest via containment.
// When we improve this factoring we will better deliver on our goal of "containment without dependency"
var promiseTogo = fluid.promise();
var promise = deviceReporter.get();
promise.then(function (deviceData) {
gpii.logFully("getDeviceContext got deviceData ", deviceData);
if (!deviceData) {
promiseTogo.reject({message: "Device reporter returned `undefined`." });
} else if (deviceData.isError) {
promiseTogo.reject({message: "Error in device reporter data: " + deviceData.message});
} else {
promiseTogo.resolve(deviceData);
}
}, function (err) {
var error = fluid.extend(err, {
message: "Rejected deviceReporter promise: " + err.message
});
promiseTogo.reject(error);
});
return promiseTogo;
};
// A mixin grade for a matchMakingRequest request handler, supporting local user logon
fluid.defaults("gpii.lifecycleManager.userLogonHandling.stateChangeHandler", {
members: {
defaultSettingsDataPromise: null // A promise distributed from "gpii.flowManager.local". containing data for reset actions.
},
invokers: {
getDeviceContext: {
funcName: "gpii.lifecycleManager.getDeviceContext",
args: ["{flowManager}.deviceReporter", "{that}.events.onDeviceContext", "{that}.events.onError"]
},
startLifecycle: {
funcName: "gpii.lifecycleManager.userLogonHandling.startLifecycle",
args: [ "{that}", "{lifecycleManager}", "{arguments}.0", "{that}.events.onSuccess"]
// final payload from matchmaking process
},
logoutUserWithoutResponse: {
funcName: "gpii.lifecycleManager.userLogonHandling.logoutUserWithoutResponse",
args: ["{that}", "{lifecycleManager}", "{arguments}.0"]
},
logoutUser: {
funcName: "gpii.lifecycleManager.userLogonHandling.logoutUser",
args: ["{that}", "{lifecycleManager}", "{arguments}.0"]
},
loginUser: {
funcName: "gpii.lifecycleManager.userLogonHandling.loginUser",
args: ["{that}", "{lifecycleManager}", "{arguments}.0"]
},
handleResetGpiiKey: {
funcName: "gpii.lifecycleManager.userLogonHandling.handleResetGpiiKey",
args: ["{that}", "{lifecycleManager}"]
}
},
events: {
onMatchDone: null,
onError: null,
onSuccess: null
},
listeners: {
"onMatchDone": [{
priority: 100,
namespace: "updsteActivePrefsSetName",
listener: "{lifecycleManager}.updateActivePrefsSetName"
}, {
priority: 90,
namespace: "addLifecycleInstructionsToPayload",
listener: "{lifecycleManager}.addLifecycleInstructionsToPayload"
}, {
priority: 80,
namespace: "startLifecycle",
listener: "{that}.startLifecycle"
}],
"onError.handleErrors": {
listener: "gpii.lifecycleManager.userLogonHandling.resetLogonChangeModel",
args: [ "{lifecycleManager}" ]
}
}
});
// The grade used to handle login request for all-in-local GPII deployment
fluid.defaults("gpii.lifecycleManager.userLogonHandling.matchMakingStateChangeHandler", {
gradeNames: [
"gpii.lifecycleManager.userLogonHandling.stateChangeHandler",
"gpii.flowManager.matchMaking"
],
listeners: {
"onGpiiKey.getDeviceContext": "{that}.getDeviceContext"
}
});
// This grade communicate with GPII Cloud to read settings from the Cloud.
// This grade is distributed to "gpii.lifecycleManager.loginRequest" when GPII is deployed in untrusted settings.
fluid.defaults("gpii.lifecycleManager.untrusted.stateChangeHandler", {
gradeNames: ["fluid.component", "gpii.lifecycleManager.userLogonHandling.stateChangeHandler"],
invokers: {
getSettings: {
funcName: "gpii.lifecycleManager.untrusted.getSettings",
args: [
"{settingsDataSource}",
"{that}.gpiiKey",
"{arguments}.0", // device reporter data
"{that}.events.onMatchDone",
"{that}.events.onError"
]
}
},
events: {
onGpiiKey: null,
onDeviceContext: null
},
listeners: {
"onGpiiKey.setGpiiKey": {
listener: "gpii.flowManager.setGpiiKey",
args: ["{that}", "{arguments}.0"]
},
"onGpiiKey.getDeviceContext": {
func: "{that}.getDeviceContext",
priority: "after:setGpiiKey"
},
"onDeviceContext.getSettings": "{that}.getSettings"
}
});
gpii.lifecycleManager.untrusted.getSettings = function (settingsDataSource, gpiiKey, deviceReporterData, onMatchDone, onError) {
// Unnecessary to fetch settings for "noUser" from the cloud
var noUserSettings = {
gpiiKey: "noUser",
preferences: {},
matchMakerOutput: {
inferredConfiguration: {
"gpii-default": {
applications: {}
}
},
lifecycleInstructions: {}
}
};
var settings = gpiiKey === "noUser" ? fluid.promise().resolve(noUserSettings) : settingsDataSource.get(gpiiKey, deviceReporterData);
settings.then(onMatchDone.fire, onError.fire);
};