Bug 700527 - Master password support for native fennec. r=mfinkle

This commit is contained in:
Wes Johnston 2011-12-21 13:08:01 -08:00
Родитель 0b3d8de319
Коммит 37e18a6598
7 изменённых файлов: 250 добавлений и 3 удалений

Просмотреть файл

@ -41,16 +41,24 @@ package org.mozilla.gecko;
import java.lang.CharSequence;
import java.util.ArrayList;
import android.app.Dialog;
import android.text.Editable;
import android.app.AlertDialog;
import android.os.Build;
import android.os.Bundle;
import android.content.res.Resources;
import android.content.Context;
import android.preference.*;
import android.preference.Preference.*;
import android.text.InputType;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.text.TextWatcher;
import android.content.DialogInterface;
import org.json.JSONArray;
import org.json.JSONException;
@ -128,9 +136,17 @@ public class GeckoPreferences
return super.onOptionsItemSelected(item);
}
final private int DIALOG_CREATE_MASTER_PASSWORD = 0;
final private int DIALOG_REMOVE_MASTER_PASSWORD = 1;
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String prefName = preference.getKey();
if (prefName.equals("privacy.masterpassword.enabled")) {
showDialog((Boolean)newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD);
return false;
}
setPreference(prefName, newValue);
if (preference instanceof ListPreference) {
// We need to find the entry for the new value
@ -143,6 +159,123 @@ public class GeckoPreferences
return true;
}
private EditText getTextBox(int aHintText) {
EditText input = new EditText(GeckoApp.mAppContext);
int inputtype = InputType.TYPE_CLASS_TEXT;
inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
input.setInputType(inputtype);
String hint = getResources().getString(aHintText);
input.setHint(aHintText);
return input;
}
private AlertDialog mDialog = null;
private class PasswordTextWatcher implements TextWatcher {
EditText input1 = null;
EditText input2 = null;
PasswordTextWatcher(EditText aInput1, EditText aInput2) {
input1 = aInput1;
input2 = aInput2;
}
public void afterTextChanged(Editable s) {
if (mDialog == null)
return;
String text1 = input1.getText().toString();
String text2 = input2.getText().toString();
mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(text1.equals(text2));
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
public void onTextChanged(CharSequence s, int start, int before, int count) { }
}
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);
switch(id) {
case DIALOG_CREATE_MASTER_PASSWORD:
final EditText input1 = getTextBox(R.string.masterpassword_password);
final EditText input2 = getTextBox(R.string.masterpassword_confirm);
PasswordTextWatcher watcher = new PasswordTextWatcher(input1, input2);
input1.addTextChangedListener((TextWatcher)watcher);
input2.addTextChangedListener((TextWatcher)watcher);
linearLayout.addView(input1);
linearLayout.addView(input2);
builder.setTitle(R.string.masterpassword_create_title)
.setView((View)linearLayout)
.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
JSONObject jsonPref = new JSONObject();
try {
jsonPref.put("name", "privacy.masterpassword.enabled");
jsonPref.put("type", "string");
jsonPref.put("value", input1.getText().toString());
GeckoEvent event = new GeckoEvent("Preferences:Set", jsonPref.toString());
GeckoAppShell.sendEventToGecko(event);
} catch(Exception ex) {
Log.e(LOGTAG, "Error setting masterpassword", ex);
}
mDialog = null;
input1.setText("");
input2.setText("");
return;
}
})
.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mDialog = null;
input1.setText("");
input2.setText("");
return;
}
});
break;
case DIALOG_REMOVE_MASTER_PASSWORD:
final EditText input = getTextBox(R.string.masterpassword_password);
linearLayout.addView(input);
builder.setTitle(R.string.masterpassword_remove_title)
.setView((View)linearLayout)
.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try {
JSONObject jsonPref = new JSONObject();
jsonPref.put("name", "privacy.masterpassword.enabled");
jsonPref.put("type", "string");
jsonPref.put("value", input.getText().toString());
GeckoEvent event = new GeckoEvent("Preferences:Set", jsonPref.toString());
GeckoAppShell.sendEventToGecko(event);
} catch(Exception ex) {
Log.e(LOGTAG, "Error setting masterpassword", ex);
}
input.setText("");
mDialog = null;
return;
}
})
.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mDialog = null;
input.setText("");
return;
}
});
break;
default:
return null;
}
return mDialog = builder.create();
}
private void refresh(JSONArray jsonPrefs) {
// enable all preferences once we have them from gecko
GeckoAppShell.getMainHandler().post(new Runnable() {

Просмотреть файл

@ -62,15 +62,16 @@
<!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
<!ENTITY pref_clear_private_data "Clear private data">
<!ENTITY pref_clear_private_data_confirm "Browsing settings, including passwords and cookies, will be deleted">
<!ENTITY pref_enable_plugins "Enable Plugins">
<!ENTITY pref_enable_plugins "Enable plugins">
<!ENTITY pref_enable_plugins_yes "Yes">
<!ENTITY pref_enable_plugins_tap_to_play "Tap To Play">
<!ENTITY pref_enable_plugins_tap_to_play "Tap to play">
<!ENTITY pref_enable_plugins_no "No">
<!ENTITY pref_font_size "Font size">
<!ENTITY pref_font_size_small "Small">
<!ENTITY pref_font_size_medium "Medium">
<!ENTITY pref_font_size_large "Large">
<!ENTITY pref_font_size_xlarge "Extra Large">
<!ENTITY pref_use_master_password "Use master password">
<!ENTITY quit "Quit">
@ -89,3 +90,11 @@
<!ENTITY site_settings_cancel "Cancel">
<!ENTITY site_settings_clear "Clear">
<!ENTITY site_settings_no_settings "There are no settings to clear.">
<!ENTITY masterpassword_create_title "Create Master Password">
<!ENTITY masterpassword_remove_title "Remove Master Password">
<!ENTITY masterpassword_password "Password">
<!ENTITY masterpassword_confirm "Confirm password">
<!ENTITY button_ok "OK">
<!ENTITY button_cancel "Cancel">

Просмотреть файл

@ -65,6 +65,11 @@
android:title="@string/pref_telemetry"
android:persistent="false" />
<CheckBoxPreference android:key="privacy.masterpassword.enabled"
android:title="@string/pref_use_master_password"
android:defaultValue="false"
android:persistent="false" />
</PreferenceCategory>
</PreferenceScreen>

Просмотреть файл

@ -93,4 +93,13 @@
<string name="contextmenu_open_new_tab">&contextmenu_open_new_tab;</string>
<string name="contextmenu_add_to_launcher">&contextmenu_add_to_launcher;</string>
<string name="contextmenu_share">&contextmenu_share;</string>
<string name="pref_use_master_password">&pref_use_master_password;</string>
<string name="masterpassword_create_title">&masterpassword_create_title;</string>
<string name="masterpassword_remove_title">&masterpassword_remove_title;</string>
<string name="masterpassword_password">&masterpassword_password;</string>
<string name="masterpassword_confirm">&masterpassword_confirm;</string>
<string name="button_ok">&button_ok;</string>
<string name="button_cancel">&button_cancel;</string>
</resources>

Просмотреть файл

@ -490,6 +490,12 @@ var BrowserApp = {
pref.value = PluginHelper.getPluginPreference();
prefs.push(pref);
continue;
} else if (prefName == MasterPassword.pref) {
// Master password is not a "real" pref
pref.type = "bool";
pref.value = MasterPassword.enabled;
prefs.push(pref);
continue;
}
try {
@ -552,6 +558,11 @@ var BrowserApp = {
if (json.name == "plugin.enable") {
PluginHelper.setPluginPreference(json.value);
return;
} else if(json.name == MasterPassword.pref) {
if (MasterPassword.enabled)
MasterPassword.removePassword(json.value);
else
MasterPassword.setPassword(json.value);
}
// when sending to java, we normalized special preferences that use
@ -3483,3 +3494,81 @@ var PermissionsHelper = {
}
}
}
var MasterPassword = {
pref: "privacy.masterpassword.enabled",
get _secModuleDB() {
delete this._secModuleDB;
return this._secModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(Ci.nsIPKCS11ModuleDB);
},
get _pk11DB() {
delete this._pk11DB;
return this._pk11DB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(Ci.nsIPK11TokenDB);
},
get enabled() {
let slot = this._secModuleDB.findSlotByName(this._tokenName);
if (slot) {
let status = slot.status;
return status != Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED && status != Ci.nsIPKCS11Slot.SLOT_READY;
}
return false;
},
setPassword: function setPassword(aPassword) {
try {
let status;
let slot = this._secModuleDB.findSlotByName(this._tokenName);
if (slot)
status = slot.status;
else
return false;
let token = this._pk11DB.findTokenByName(this._tokenName);
if (status == Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED)
token.initPassword(aPassword);
else if (status == Ci.nsIPKCS11Slot.SLOT_READY)
token.changePassword("", aPassword);
this.updatePref();
return true;
} catch(e) {
dump("MasterPassword.setPassword: " + e);
}
return false;
},
removePassword: function removePassword(aOldPassword) {
try {
let token = this._pk11DB.getInternalKeyToken();
if (token.checkPassword(aOldPassword)) {
token.changePassword(aOldPassword, "");
this.updatePref();
return true;
}
} catch(e) {
dump("MasterPassword.removePassword: " + e + "\n");
}
NativeWindow.toast.show(Strings.browser.GetStringFromName("masterPassword.incorrect"), "short");
return false;
},
updatePref: function() {
var prefs = [];
let pref = {
name: this.pref,
type: "bool",
value: this.enabled
};
prefs.push(pref);
sendMessageToJava({
gecko: {
type: "Preferences:Data",
preferences: prefs
}
});
}
}

Просмотреть файл

@ -328,7 +328,7 @@ Prompt.prototype = {
nsIPrompt_promptPassword: function nsIPrompt_promptPassword(
aTitle, aText, aPassword, aCheckMsg, aCheckState) {
let inputs = [{ type: "password", hint: "Password", value: aPassword.value }];
let inputs = [{ type: "password", hint: "Password", value: aPassword.value || "" }];
let data = this.commonPrompt(aTitle, aText, null, aCheckMsg, aCheckState, inputs);
if (aCheckMsg)

Просмотреть файл

@ -214,3 +214,5 @@ clickToPlayPlugins.no=No
# dislay a list of current permissions settings for a site.
# Example: "Store Offline Data: Allow"
siteSettings.labelToValue=%S: %S
masterPassword.incorrect=Incorrect password