Skip to content
This repository has been archived by the owner on Aug 18, 2024. It is now read-only.

Commit

Permalink
Add initializing/locked scene to fix race condition, and to allow for…
Browse files Browse the repository at this point in the history
… more

lockout options.  Add option to require password at launch. 

	* app/assistants/app-assistant.js (windowDeactivated): Choose action
	based on ring.prefs; use Keyring methods for lockout.
	(pushOpeningScene): Push "locked" scene instead of item list.  This avoids
	a race-condition that would often lead to showing the "new password"
	dialog on launch.
	(PasswordDialogAssistant): Add option to prevent canceling of dialog.
	Better formatting for error display.  Focus password input on error.
	(Keyring.doIfPasswordValid): Handle preventCancel arg.
	(Keyring.lockout,activateLockout,deactivateLockout): Factored out code
	from the scenes and collected it here for simplicity.  Supports
	different behaviors per preferences.

	* app/assistants/actions-assistant.js (setup): Properly handle labels on
	ListSelectors.
	(exportToUrl): Proper error handling.
	(importFileOrUrl): Fix a really dumb bug.  Proper error handling.
	(clearDatabase): Pop to appropriate scene.
    (activate, deactivate, timeoutOrDeactivate): Use new Keyring-based
	global system for lockout/timeout.
	(ImportExportDialogAssistant): Make OK button an activity button, and
	don't allow double-submit.
	
	* app/assistants/item-assistant.js (timeoutOrDeactivate): Use new Keyring
	based global system for lockout/timeout.
	
	* app/assistants/item-list-assistant.js (activate, deactivate,
	timeoutOrDeactivate): Use new Keyring-based global system for
	lockout/timeout.
	
	* app/assistants/help-assistant.js (activate, deactivate,
	timeoutOrDeactivate): Use new Keyring-based global system for
	lockout/timeout.
	
	* app/assistants/locked-assistant.js: New scene used at launch and
	(optionally) as an app-locked screen.  Home to the "create master
	password" dialog.  
	
	* app/assistants/preferences-assistant.js (setup): Add plumbing for new
	lockout prefs.  Properly handle labels on ListSelectors.
	(activate, deactivate, timeoutOrDeactivate): Use new Keyring-based
	global system for lockout/timeout.
	
	* app/models/ring.js (DEFAULT_PREFS): New prefs for lockout behavior.
	(onDeactivateOptions): What to do when minimized.
	(initDepotReader): Return early if data already loaded.
	(_loadDepotData): Note loading of data earlier in the cycle.
	(_loadDataHandler): Fill in any missing prefs.
	
	* app/views/help/help-scene.html: Added Change Log section.
	
	* app/views/locked/locked-scene.html: New initializing/locked scene.
	
	* app/views/locked/new-password-dialog.html: Hook for better error
	formatting.  Moved to new "locked" scene.
	
	* app/views/password-dialog.html: Hook for better error formatting.
	
	* app/views/preferences/preferences-scene.html: Added new prefs for lockout.
	Reorganized prefs into "paranoia" section.  Cleanup up L&F.
  • Loading branch information
Dirk Bergstrom committed Oct 5, 2009
1 parent 7d4d625 commit 556a5d0
Show file tree
Hide file tree
Showing 20 changed files with 511 additions and 210 deletions.
68 changes: 67 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
@@ -1,6 +1,72 @@
2009-10-04 Dirk Bergstrom <[email protected]>

Add initializing/locked scene to fix race condition, and to allow for more
lockout options. Add option to require password at launch.

* app/assistants/app-assistant.js (windowDeactivated): Choose action
based on ring.prefs; use Keyring methods for lockout.
(pushOpeningScene): Push "locked" scene instead of item list. This avoids
a race-condition that would often lead to showing the "new password"
dialog on launch.
(PasswordDialogAssistant): Add option to prevent canceling of dialog.
Better formatting for error display. Focus password input on error.
(Keyring.doIfPasswordValid): Handle preventCancel arg.
(Keyring.lockout,activateLockout,deactivateLockout): Factored out code
from the scenes and collected it here for simplicity. Supports
different behaviors per preferences.

* app/assistants/actions-assistant.js (setup): Properly handle labels on
ListSelectors.
(exportToUrl): Proper error handling.
(importFileOrUrl): Fix a really dumb bug. Proper error handling.
(clearDatabase): Pop to appropriate scene.
(activate, deactivate, timeoutOrDeactivate): Use new Keyring-based
global system for lockout/timeout.
(ImportExportDialogAssistant): Make OK button an activity button, and
don't allow double-submit.

* app/assistants/item-assistant.js (timeoutOrDeactivate): Use new Keyring
based global system for lockout/timeout.

* app/assistants/item-list-assistant.js (activate, deactivate,
timeoutOrDeactivate): Use new Keyring-based global system for
lockout/timeout.

* app/assistants/help-assistant.js (activate, deactivate,
timeoutOrDeactivate): Use new Keyring-based global system for
lockout/timeout.

* app/assistants/locked-assistant.js: New scene used at launch and
(optionally) as an app-locked screen. Home to the "create master
password" dialog.

* app/assistants/preferences-assistant.js (setup): Add plumbing for new
lockout prefs. Properly handle labels on ListSelectors.
(activate, deactivate, timeoutOrDeactivate): Use new Keyring-based
global system for lockout/timeout.

* app/models/ring.js (DEFAULT_PREFS): New prefs for lockout behavior.
(onDeactivateOptions): What to do when minimized.
(initDepotReader): Return early if data already loaded.
(_loadDepotData): Note loading of data earlier in the cycle.
(_loadDataHandler): Fill in any missing prefs.

* app/views/help/help-scene.html: Added Change Log section.

* app/views/locked/locked-scene.html: New initializing/locked scene.

* app/views/locked/new-password-dialog.html: Hook for better error
formatting. Moved to new "locked" scene.

* app/views/password-dialog.html: Hook for better error formatting.

* app/views/preferences/preferences-scene.html: Added new prefs for lockout.
Reorganized prefs into "paranoia" section. Cleanup up L&F.

2009-09-28 Dirk Bergstrom <[email protected]>

Fix a bug in V0 upgrader. Add user-visible display of ring.errors.
Fix a bug in V0 upgrader. Add user-visible display of ring.errors. Fix some
import bugs.

* app/assistants/help-assistant.js (setup): Display any ring.errors at the
bottom of the scene, so we can get users to tell us what's wrong.
Expand Down
77 changes: 42 additions & 35 deletions app/assistants/actions-assistant.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ ActionsAssistant.prototype.setup = function() {
// Export
this.controller.setupWidget("destination",
{modelProperty: "destination",
label: "Destination",
labelPlacement: Mojo.Widget.labelPlacementLeft,
choices: [{label: $L("Clipboard"), value: "clipboard"},
{label: $L("URL"), value: "url"}
]},
Expand All @@ -44,6 +46,8 @@ ActionsAssistant.prototype.setup = function() {
// Data source
this.controller.setupWidget("source",
{modelProperty: "source",
label: "Source",
labelPlacement: Mojo.Widget.labelPlacementLeft,
choices: [{label: $L("Clipboard"), value: "clipboard"},
{label: $L("File"), value: "file"},
{label: $L("URL"), value: "url"}
Expand All @@ -56,8 +60,11 @@ ActionsAssistant.prototype.setup = function() {
}, this.ring.resolutions);
choices.sort();
this.controller.setupWidget("resolution",
{modelProperty: "resolution", choices: choices},
this.ring.prefs.import_);
{modelProperty: "resolution",
label: "Resolution",
labelPlacement: Mojo.Widget.labelPlacementLeft,
choices: choices},
this.ring.prefs.import_);
// Preferences handling
this.controller.setupWidget("prefs", {modelProperty: "prefs"},
this.ring.prefs.import_);
Expand Down Expand Up @@ -134,14 +141,20 @@ ActionsAssistant.prototype.exportToUrl = function(url) {
evalJSON: false,
evalJS: false,
onSuccess: function(transport) {
this.ring.prefs.export_.url= url;
this.controller.showAlertDialog({
onChoose: function(value) {},
title: $L("Database Exported"),
message: $L("An encrypted copy of the Keyring database was POSTed to " +
url + "."),
choices:[{label:$L("OK"), value:""}]
});
if (transport.status < 200 || transport.status > 299 ||
! transport.responseText.match(/^\s*ok\s*$/i)) {
Mojo.Controller.errorDialog($L("Error exporting to ") + url,
this.controller.window);
} else {
this.ring.prefs.export_.url= url;
this.controller.showAlertDialog({
onChoose: function(value) {},
title: $L("Database Exported"),
message: $L("An encrypted copy of the Keyring database was POSTed to ") +
url,
choices:[{label:$L("OK"), value:""}]
});
}
}.bind(this),
onFailure: function(transport) {
Mojo.Controller.errorDialog(e.name + " error exporting: " +
Expand Down Expand Up @@ -200,7 +213,7 @@ ActionsAssistant.prototype.importFileOrUrl = function(path, password) {
this.ring.prefs.import_.url = fullPath;
} else if (! path.charAt(0) != '/') {
path = '/' + path;
fullPath = '/media/internal' + filename;
fullPath = '/media/internal' + path;
this.ring.prefs.import_.filename = path;
}
// Save the prefs
Expand All @@ -212,12 +225,17 @@ ActionsAssistant.prototype.importFileOrUrl = function(path, password) {
evalJSON: false,
evalJS: false,
onSuccess: function(transport) {
var importData = transport.responseText;
this.ring.importData(importData,
this.ring.prefs.import_.resolution,
this.ring.prefs.import_.prefs,
password,
this.importResults.bind(this));
if (transport.status < 200 || transport.status > 299) {
this.importResults(false, 'Error reading data from "' +
path + '"');
} else {
var importData = transport.responseText;
this.ring.importData(importData,
this.ring.prefs.import_.resolution,
this.ring.prefs.import_.prefs,
password,
this.importResults.bind(this));
}
}.bind(this),
onFailure: function(transport) {
this.importResults(false, 'Unable to read data from "' +
Expand Down Expand Up @@ -245,7 +263,8 @@ ActionsAssistant.prototype.clearDatabase = function() {
if (value.search("yes") > -1) {
this.ring.clearDatabase(value == "yes-factory");
this.ring.itemsReSorted = true;
this.controller.stageController.popScenesTo("item-list");
var popTo = (value == "yes-factory") ? "locked" : "item-list";
this.controller.stageController.popScenesTo(popTo);
}
}.bind(this),
title: $L("Clear Database"),
Expand All @@ -258,13 +277,6 @@ ActionsAssistant.prototype.clearDatabase = function() {
});
};

/* Don't leave an item visible when we minimize. */
ActionsAssistant.prototype.timeoutOrDeactivate = function() {
Mojo.Log.info("Actions scene timeoutOrDeactivate");
this.ring.clearPassword();
this.controller.stageController.popScenesTo("item-list");
};

ActionsAssistant.prototype.activate = function(event) {
Mojo.Event.listen(this.controller.get("clearDatabaseButton"), Mojo.Event.tap,
this.clearDbAction);
Expand All @@ -273,12 +285,7 @@ ActionsAssistant.prototype.activate = function(event) {
Mojo.Event.listen(this.controller.get("importButton"), Mojo.Event.tap,
this.importAction);

Mojo.Event.listen(this.controller.stageController.document,
Mojo.Event.stageDeactivate, this.timeoutOrDeactivate.bind(this));

// Pop the scene if the user is idle too long
this.cancelIdleTimeout = this.controller.setUserIdleTimeout(this.controller.sceneElement,
this.timeoutOrDeactivate.bind(this), this.ring.prefs.timeout);
Keyring.activateLockout(this);
};


Expand All @@ -290,9 +297,7 @@ ActionsAssistant.prototype.deactivate = function(event) {
Mojo.Event.stopListening(this.controller.get("importButton"), Mojo.Event.tap,
this.importAction);

Mojo.Event.stopListening(this.controller.stageController.document,
Mojo.Event.stageDeactivate, this.timeoutOrDeactivate.bind(this));
this.cancelIdleTimeout();
Keyring.deactivateLockout(this);
};

ActionsAssistant.prototype.cleanup = function(event) {
Expand Down Expand Up @@ -348,7 +353,7 @@ ImportExportDialogAssistant = Class.create ({
this.controller.hideWidgetContainer("password-group");
}

this.controller.setupWidget("okButton", {type: Mojo.Widget.defaultButton},
this.controller.setupWidget("okButton", {type: Mojo.Widget.activityButton},
{label: $L("Ok"), disabled: false});
this.okHandler = this.ok.bindAsEventListener(this);
this.controller.listen("okButton", Mojo.Event.tap, this.okHandler);
Expand All @@ -367,6 +372,8 @@ ImportExportDialogAssistant = Class.create ({

ok: function() {
Mojo.Log.info("ok");
this.controller.stopListening("okButton", Mojo.Event.tap,
this.okHandler);
this.callbackOnSuccess(this.dataModel.value, this.passwordModel.value);
},

Expand Down
86 changes: 64 additions & 22 deletions app/assistants/app-assistant.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,17 @@ AppAssistant.prototype.setup = function() {
};

AppAssistant.prototype.windowDeactivated = function() {
// TODO consider making this optional via prefs.
this.ring.clearPassword();
Mojo.Log.info("windowDeactivated in scene", this.stageController.topScene().sceneName);
switch (this.ring.prefs.onDeactivate) {
case 'lock':
Keyring.lockout(this, this.ring);
break;
case 'lockSoon':
Keyring.lockout.delay(this.ring.lockSoonDelay, this, this.ring);
break;
default:
// Do nothing
}
};

AppAssistant.prototype.handleLaunch = function() {
Expand All @@ -61,22 +70,24 @@ AppAssistant.prototype.handleLaunch = function() {
AppAssistant.prototype.openChildWindow = function() {
this.stageController = this.appController.getStageController('lightWeight');
if (this.stageController){
// app window is open, give it focus
/* app window is open, give it focus. We presume that ring data has
* already been loaded. */
Mojo.Log.info("give open window focus");
this.stageController.activate();
} else{
// otherwise create the app window
Mojo.Log.info("create app window");
this.appController.createStageWithCallback({name: 'lightWeight', lightweight: true},
this.pushOpeningScene.bind(this));
this.appController.createStageWithCallback(
{name: 'lightWeight', lightweight: true},
this.pushOpeningScene.bind(this));
}

};

AppAssistant.prototype.pushOpeningScene = function(stageController) {
this.stageController = stageController;
Mojo.Event.listen(stageController.document,
Mojo.Event.stageDeactivate, this.windowDeactivated.bind(this));
stageController.pushScene('item-list', this.ring);
stageController.pushScene('locked', this.ring);
};

//-----------------------------------------
Expand Down Expand Up @@ -124,16 +135,17 @@ AppAssistant.prototype.handleCommand = function(event) {
* The "Enter your password" dialog, used throughout the application.
*/
PasswordDialogAssistant = Class.create ({
initialize: function(controller, ring, callback) {
initialize: function(controller, ring, callback, noCancel) {
this.controller = controller;
this.ring = ring;
this.callbackOnSuccess = callback;
this.noCancel = noCancel ? true : false;
},

setup: function(widget) {
this.widget = widget;

this.controller.get("password-title").update($L("Unlock"));
this.controller.get("password-title").update($L("Enter Password to Unlock"));

this.controller.setupWidget(
"password",
Expand All @@ -158,12 +170,14 @@ PasswordDialogAssistant = Class.create ({
this.unlockHandler = this.unlock.bindAsEventListener(this);
this.controller.listen("unlockButton", Mojo.Event.tap,
this.unlockHandler);

this.cancelButtonModel = {label: $L("Cancel"), disabled: false};
this.controller.setupWidget("cancelButton", {type: Mojo.Widget.defaultButton},
this.cancelButtonModel);
this.controller.listen("cancelButton", Mojo.Event.tap,
this.widget.mojo.close);

if (! this.noCancel) {
this.cancelButtonModel = {label: $L("Cancel"), disabled: false};
this.controller.setupWidget("cancelButton", {type: Mojo.Widget.defaultButton},
this.cancelButtonModel);
this.controller.listen("cancelButton", Mojo.Event.tap,
this.widget.mojo.close);
}
},

keyPressHandler: function(event) {
Expand All @@ -182,35 +196,63 @@ PasswordDialogAssistant = Class.create ({
Mojo.Log.info("Bad Password");
// TODO select random insult from the sudo list
// FIXME apply some decent styling to the error message
// FIXME set focus on password input
this.controller.get("errmsg").update($L("==> Invalid Password <=="));
this.controller.get("password").focus();
this.controller.get("errmsg").update($L("Invalid Password"));
this.controller.get("password").mojo.focus();
}
},

//cleanup - remove listeners
cleanup: function() {
this.controller.stopListening("unlockButton", Mojo.Event.tap,
this.unlockHandler);
this.controller.stopListening("cancelButton", Mojo.Event.tap,
this.widget.mojo.close);
if (! this.noCancel) {
this.controller.stopListening("cancelButton", Mojo.Event.tap,
this.widget.mojo.close);
}
this.controller.stopListening("password", Mojo.Event.propertyChange,
this.keyPressHandler.bind(this));
}
});

/* If the user has entered a valid password within the timeout window, or they
* enter it into the dialog, return true. */
Keyring.doIfPasswordValid = function(sceneController, ring, callback) {
Keyring.doIfPasswordValid = function(sceneController, ring, callback, preventCancel) {
if (ring.passwordValid()) {
callback();
} else {
sceneController.showDialog({
template: "password-dialog",
assistant: new PasswordDialogAssistant(sceneController, ring, callback)
preventCancel: preventCancel ? true : false,
assistant: new PasswordDialogAssistant(sceneController, ring,
callback, preventCancel)
});
}
};

/* Called by scenes on timeout or app deactivation/minimization. */
Keyring.lockout = function(controller, ring) {
var sceneName = controller.stageController.topScene().sceneName;
Mojo.Log.info("Timeout or Deactivate in scene", sceneName);
ring.clearPassword();
// Don't pop scene if we're already on the lockoutTo page.
if (sceneName != ring.prefs.lockoutTo) {
controller.stageController.popScenesTo(ring.prefs.lockoutTo);
}
};

Keyring.activateLockout = function(sceneAssistant) {
Mojo.Log.info("activateLockout for scene",
sceneAssistant.controller.stageController.topScene().sceneName);
// Clear password after idle timeout
sceneAssistant.cancelIdleTimeout = sceneAssistant.controller.setUserIdleTimeout(
sceneAssistant.controller.sceneElement,
Keyring.lockout.bind(Keyring, sceneAssistant.controller, sceneAssistant.ring),
sceneAssistant.ring.prefs.timeout);
};


Keyring.deactivateLockout = function(sceneAssistant) {
Mojo.Log.info("deactivateLockout for scene",
sceneAssistant.controller.stageController.topScene().sceneName);
sceneAssistant.cancelIdleTimeout();
};
Loading

0 comments on commit 556a5d0

Please sign in to comment.