aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl
diff options
context:
space:
mode:
authorLyubomir Marinov <lyubomir.marinov@jitsi.org>2010-07-21 08:55:46 +0000
committerLyubomir Marinov <lyubomir.marinov@jitsi.org>2010-07-21 08:55:46 +0000
commita7d9ca3c38f3de835255c04f5a9cdbaaadafc3dc (patch)
treefa7b53f75a887e6896b13e3c563a5df36cf22753 /src/net/java/sip/communicator/impl
parentc4f6c118f0adfae2fd68e11644df789cebeeb8e6 (diff)
downloadjitsi-a7d9ca3c38f3de835255c04f5a9cdbaaadafc3dc.zip
jitsi-a7d9ca3c38f3de835255c04f5a9cdbaaadafc3dc.tar.gz
jitsi-a7d9ca3c38f3de835255c04f5a9cdbaaadafc3dc.tar.bz2
Commits migration.patch (migrates old passwords to the credential storage on startup) and passwordInput.patch (the OK button gets clicked when Enter is hit inside the master password entry field) provided by Dmitri Melnikov as part of Google Summer of Code 2010 on the dev mailing list in the thread "Password Storage in trunk".
Diffstat (limited to 'src/net/java/sip/communicator/impl')
-rw-r--r--src/net/java/sip/communicator/impl/credentialsstorage/CredentialsStorageServiceImpl.java107
-rw-r--r--src/net/java/sip/communicator/impl/credentialsstorage/credentialsstorage.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/impl/gui/UIServiceImpl.java50
-rw-r--r--src/net/java/sip/communicator/impl/gui/utils/MasterPasswordInputDialog.java217
4 files changed, 279 insertions, 98 deletions
diff --git a/src/net/java/sip/communicator/impl/credentialsstorage/CredentialsStorageServiceImpl.java b/src/net/java/sip/communicator/impl/credentialsstorage/CredentialsStorageServiceImpl.java
index 7ed868d..217f4b3 100644
--- a/src/net/java/sip/communicator/impl/credentialsstorage/CredentialsStorageServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/credentialsstorage/CredentialsStorageServiceImpl.java
@@ -25,7 +25,7 @@ public class CredentialsStorageServiceImpl
implements CredentialsStorageService
{
/**
- * The logger for this class.
+ * The <tt>Logger</tt> for this class.
*/
private final Logger logger =
Logger.getLogger(CredentialsStorageServiceImpl.class);
@@ -66,14 +66,16 @@ public class CredentialsStorageServiceImpl
/**
* Initializes the credentials service by fetching the configuration service
- * reference from the bundle context.
- *
+ * reference from the bundle context. Encrypts and moves all passwords to
+ * new properties.
+ *
* @param bc bundle context
*/
void start(BundleContext bc)
{
- this.configurationService
+ configurationService
= ServiceUtils.getService(bc, ConfigurationService.class);
+ moveAllPasswordProperties();
}
/**
@@ -125,37 +127,22 @@ public class CredentialsStorageServiceImpl
}
/**
- * Loads the password for the specified account.
- * First check if the password is stored in the configuration unencrypted
- * and if so, encrypt it and store in the new property. Otherwise, if the
- * password is stored encrypted, decrypt it with the master password.
- *
- * Many threads can call this method at the same time, and the
- * first thread may present the user with the master password prompt and
- * create a <tt>Crypto</tt> instance based on the input
- * (<tt>createCrypto</tt> method). This instance will be used later by all
- * other threads.
- *
- * @param accountPrefix account prefix
+ * Loads the password for the specified account. If the password is stored
+ * encrypted, decrypts it with the master password.
+ *
+ * Many threads can call this method at the same time, and the first thread
+ * may present the user with the master password prompt and create a
+ * <tt>Crypto</tt> instance based on the input (<tt>createCrypto</tt>
+ * method). This instance will be used later by all other threads.
+ *
+ * @param accountPrefix account prefix
* @return the loaded password for the <tt>accountPrefix</tt>
* @see CredentialsStorageServiceImpl#createCrypto()
*/
public synchronized String loadPassword(String accountPrefix)
{
- String password;
-
- if (isStoredUnencrypted(accountPrefix))
- {
- password = new String(Base64.decode(getUnencrypted(accountPrefix)));
- if (movePasswordProperty(accountPrefix, password))
- password = null;
- }
- else
- password = null;
-
- if ((password == null)
- && isStoredEncrypted(accountPrefix)
- && createCrypto())
+ String password = null;
+ if (isStoredEncrypted(accountPrefix) && createCrypto())
{
try
{
@@ -204,7 +191,8 @@ public class CredentialsStorageServiceImpl
* the MP is considered correct.
*
* @param master master password
- * @return true if the password is correct, false otherwise
+ * @return <tt>true</tt> if the password is correct; <tt>false</tt>,
+ * otherwise
*/
public boolean verifyMasterPassword(String master)
{
@@ -244,7 +232,8 @@ public class CredentialsStorageServiceImpl
*
* @param oldPassword old master password
* @param newPassword new master password
- * @return true if master password was changed successfully, false otherwise
+ * @return <tt>true</tt> if master password was changed successfully;
+ * <tt>false</tt>, otherwise
*/
public boolean changeMasterPassword(String oldPassword, String newPassword)
{
@@ -298,6 +287,38 @@ public class CredentialsStorageServiceImpl
{
crypto = new AESCrypto(master);
}
+
+ /**
+ * Moves all password properties from unencrypted
+ * {@link #ACCOUNT_UNENCRYPTED_PASSWORD} to the corresponding encrypted
+ * {@link #ACCOUNT_ENCRYPTED_PASSWORD}.
+ */
+ private void moveAllPasswordProperties()
+ {
+ // if the MP is set we cannot move properties
+ // since retrieving UIService would cause an exception
+ if (isUsingMasterPassword())
+ return;
+
+ List<String> unencryptedProperties
+ = configurationService.getPropertyNamesBySuffix(
+ ACCOUNT_UNENCRYPTED_PASSWORD);
+
+ for (String prop : unencryptedProperties)
+ {
+ int idx = prop.lastIndexOf('.');
+
+ if (idx != -1)
+ {
+ String prefix = prop.substring(0, idx);
+ String password
+ = new String(Base64.decode(getUnencrypted(prefix)));
+
+ if (!movePasswordProperty(prefix, password))
+ logger.warn("Failed to move password for prefix " + prefix);
+ }
+ }
+ }
/**
* Asks for master password if needed, encrypts the password, saves it to
@@ -361,7 +382,8 @@ public class CredentialsStorageServiceImpl
* master password or with null. If the user decided not to input anything,
* the instance is not created.
*
- * @return true if Crypto instance was created, false otherwise
+ * @return <tt>true</tt> if the Crypto instance was created; <tt>false</tt>,
+ * otherwise
*/
private boolean createCrypto()
{
@@ -401,7 +423,7 @@ public class CredentialsStorageServiceImpl
* Displays a password prompt to the user in a loop until it is correct or
* the user presses the cancel button.
*
- * @return the entered password or null if none was provided.
+ * @return the entered password or <tt>null</tt> if none was provided.
*/
private String showPasswordPrompt()
{
@@ -464,7 +486,7 @@ public class CredentialsStorageServiceImpl
* Check if encrypted account password is saved in the configuration.
*
* @param accountPrefix account prefix
- * @return true if saved, false if not
+ * @return <tt>true</tt> if saved, <tt>false</tt> if not
*/
public boolean isStoredEncrypted(String accountPrefix)
{
@@ -498,19 +520,4 @@ public class CredentialsStorageServiceImpl
configurationService.setProperty(
accountPrefix + "." + ACCOUNT_UNENCRYPTED_PASSWORD, value);
}
-
- /**
- * Check if unencrypted account password is saved in the configuration.
- *
- * @param accountPrefix account prefix
- * @return true if saved, false if not
- */
- public boolean isStoredUnencrypted(String accountPrefix)
- {
- configurationService.getPropertyNamesByPrefix("", false);
- return
- configurationService.getString(
- accountPrefix + "." + ACCOUNT_UNENCRYPTED_PASSWORD)
- != null;
- }
}
diff --git a/src/net/java/sip/communicator/impl/credentialsstorage/credentialsstorage.manifest.mf b/src/net/java/sip/communicator/impl/credentialsstorage/credentialsstorage.manifest.mf
index cf2c80b..1a4dc22 100644
--- a/src/net/java/sip/communicator/impl/credentialsstorage/credentialsstorage.manifest.mf
+++ b/src/net/java/sip/communicator/impl/credentialsstorage/credentialsstorage.manifest.mf
@@ -9,6 +9,5 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.util,
javax.crypto,
- javax.crypto.spec,
- javax.swing
+ javax.crypto.spec
Export-Package: net.java.sip.communicator.service.credentialsstorage
diff --git a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
index 606ddae..c66c623 100644
--- a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
@@ -8,9 +8,9 @@ package net.java.sip.communicator.impl.gui;
import java.awt.*;
import java.awt.event.*;
+import java.beans.*;
import java.util.*;
import java.util.List;
-import java.beans.*;
import javax.swing.*;
@@ -1220,53 +1220,11 @@ public class UIServiceImpl
* @param prevSuccess <tt>true</tt> if any previous call returned a correct
* master password and there is no need to show an extra "verification
* failed" message
- * @return the master password obtained from the user
+ * @return the master password obtained from the user or <tt>null</tt> if
+ * none was provided
*/
public String getMasterPassword(boolean prevSuccess)
{
- ResourceManagementService resources = GuiActivator.getResources();
- Object[] msg;
- String inputMsg
- = resources.getI18NString(
- "plugin.securityconfig.masterpassword.MP_INPUT");
- JPasswordField passwordField = new JPasswordField();
-
- if (!prevSuccess)
- {
- String errorMsg
- = "<html><font color=\"red\">"
- + resources.getI18NString(
- "plugin.securityconfig.masterpassword"
- + ".MP_VERIFICATION_FAILURE_MSG")
- + "</font></html>";
-
- msg = new Object[] { errorMsg, inputMsg, passwordField };
- }
- else
- msg = new Object[] { inputMsg, passwordField };
-
- // clear the password field
- passwordField.setText("");
-
- int chosenOption
- = JOptionPane.showOptionDialog(
- null,
- msg,
- resources.getI18NString(
- "plugin.securityconfig.masterpassword.MP_TITLE"),
- JOptionPane.YES_NO_OPTION,
- JOptionPane.QUESTION_MESSAGE,
- null,
- new String[]
- {
- resources.getI18NString("service.gui.OK"),
- resources.getI18NString("service.gui.CANCEL")
- },
- null);
-
- return
- (chosenOption == JOptionPane.YES_OPTION)
- ? new String(passwordField.getPassword())
- : null;
+ return MasterPasswordInputDialog.showInput(prevSuccess);
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/utils/MasterPasswordInputDialog.java b/src/net/java/sip/communicator/impl/gui/utils/MasterPasswordInputDialog.java
new file mode 100644
index 0000000..94177eb
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/utils/MasterPasswordInputDialog.java
@@ -0,0 +1,217 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license. See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.utils;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * The master password input dialog.
+ *
+ * @author Dmitri Melnikov
+ */
+public class MasterPasswordInputDialog
+ extends SIPCommDialog
+ implements ActionListener
+{
+ /**
+ * Instance of this class.
+ */
+ private static MasterPasswordInputDialog dialog;
+
+ /**
+ * The <tt>ResourceManagementService</tt> used by this instance to access
+ * the localized and internationalized resources of the application.
+ */
+ private final ResourceManagementService resources
+ = GuiActivator.getResources();
+
+ /**
+ * Password obtained from the user.
+ */
+ private String password;
+
+ /**
+ * UI components.
+ */
+ private JPasswordField currentPasswdField;
+ private JButton okButton;
+ private JButton cancelButton;
+ private JTextArea infoTextArea;
+ private JTextArea errorTextArea;
+ private JPanel textFieldsPanel;
+ private JPanel buttonsPanel;
+ private JPanel mainPanel;
+
+ /**
+ * Builds the dialog.
+ */
+ private MasterPasswordInputDialog()
+ {
+ super(false);
+
+ initComponents();
+
+ this.setTitle(resources
+ .getI18NString("plugin.securityconfig.masterpassword.MP_TITLE"));
+ this.setModalityType(ModalityType.APPLICATION_MODAL);
+ this.setResizable(false);
+
+ this.getContentPane().add(mainPanel);
+
+ this.pack();
+
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ int x = (screenSize.width - this.getWidth()) / 2;
+ int y = (screenSize.height - this.getHeight()) / 2;
+
+ this.setLocation(x, y);
+ }
+
+ /**
+ * Initializes the UI components.
+ */
+ private void initComponents()
+ {
+ mainPanel = new TransparentPanel();
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+
+ // info text
+ infoTextArea = new JTextArea();
+ infoTextArea.setEditable(false);
+ infoTextArea.setOpaque(false);
+ infoTextArea.setFont(infoTextArea.getFont().deriveFont(Font.BOLD));
+ infoTextArea.setText(resources
+ .getI18NString("plugin.securityconfig.masterpassword.MP_INPUT"));
+
+ // error text
+ errorTextArea = new JTextArea();
+ errorTextArea.setEditable(false);
+ errorTextArea.setOpaque(false);
+ errorTextArea.setForeground(Color.red);
+ errorTextArea.setFont(errorTextArea.getFont().deriveFont(Font.BOLD));
+ errorTextArea.setText(resources
+ .getI18NString("plugin.securityconfig.masterpassword"
+ + ".MP_VERIFICATION_FAILURE_MSG"));
+
+ // password fields
+ currentPasswdField = new JPasswordField(15);
+ currentPasswdField.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent arg0)
+ {
+ okButton.doClick();
+ }
+ });
+ textFieldsPanel = new TransparentPanel(new GridLayout(0, 1, 8, 8));
+ textFieldsPanel.add(currentPasswdField);
+
+ // OK and cancel buttons
+ okButton = new JButton(resources.getI18NString("service.gui.OK"));
+ okButton.setMnemonic(GuiActivator.getResources().getI18nMnemonic(
+ "service.gui.OK"));
+ okButton.addActionListener(this);
+
+ cancelButton =
+ new JButton(resources.getI18NString("service.gui.CANCEL"));
+ cancelButton.setMnemonic(GuiActivator.getResources().getI18nMnemonic(
+ "service.gui.CANCEL"));
+ cancelButton.addActionListener(this);
+
+ buttonsPanel = new TransparentPanel(new FlowLayout(FlowLayout.CENTER));
+ buttonsPanel.add(okButton);
+ buttonsPanel.add(cancelButton);
+
+ rebuildMainPanel(false);
+ }
+
+ /**
+ * Removes and adds again all the components to the main panel.
+ *
+ * @param includeErrorMsg when true also includes an error text component
+ */
+ private void rebuildMainPanel(boolean includeErrorMsg)
+ {
+ mainPanel.removeAll();
+
+ if (includeErrorMsg)
+ mainPanel.add(errorTextArea);
+ mainPanel.add(infoTextArea);
+ mainPanel.add(textFieldsPanel);
+ mainPanel.add(buttonsPanel);
+ }
+
+ /**
+ * Shows an input dialog to the user to obtain the master password.
+ *
+ * @param prevSuccess <tt>true</tt> if any previous call returned a correct
+ * master password and there is no need to show an extra "verification
+ * failed" message
+ * @return the master password obtained from the user or <tt>null</tt> if
+ * none was provided
+ */
+ public static String showInput(boolean prevSuccess)
+ {
+ if (dialog == null)
+ dialog = new MasterPasswordInputDialog();
+
+ dialog.rebuildMainPanel(!prevSuccess);
+ dialog.resetPassword();
+
+ // blocks until user performs an action
+ dialog.setVisible(true);
+
+ return dialog.password;
+ }
+
+ /**
+ * OK button click event handler. Retrieves the password and hides the
+ * dialog.
+ *
+ * @param e action event
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JButton sourceButton = (JButton) e.getSource();
+ if (sourceButton.equals(okButton))
+ {
+ password
+ = new String(
+ ((JPasswordField) currentPasswdField).getPassword());
+ }
+ // hide dialog and unblock application
+ dialog.setVisible(false);
+ }
+
+ /**
+ * Closes the dialog.
+ *
+ * @param escaped <tt>true</tt> if this dialog has been closed by pressing
+ * the Esc key; otherwise, <tt>false</tt>
+ */
+ protected void close(boolean escaped)
+ {
+ cancelButton.doClick();
+ }
+
+ /**
+ * Resets the password by clearing the input field and setting
+ * <tt>password</tt> to <tt>null</tt>.
+ */
+ private void resetPassword()
+ {
+ password = null;
+ currentPasswdField.setText("");
+ currentPasswdField.requestFocusInWindow();
+ }
+}