UNPKG

gpii-universal

Version:

Cross platform, core components of the GPII personalization infrastructure.

311 lines (289 loc) 14 kB
/* * 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); };