/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.plugin.securityconfig.masterpassword; import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.table.*; import net.java.sip.communicator.plugin.securityconfig.*; import net.java.sip.communicator.service.credentialsstorage.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.*; import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; // disambiguation /** * The dialog that displays all saved account passwords. * * @author Dmitri Melnikov */ public class SavedPasswordsDialog extends SIPCommDialog { /** * The logger. */ private static Logger logger = Logger.getLogger(SavedPasswordsDialog.class); /** * Serial version UID. */ private static final long serialVersionUID = 0L; /** * UI components. */ private JPanel mainPanel; private JButton closeButton; /** * The {@link CredentialsStorageService}. */ private static final CredentialsStorageService credentialsStorageService = SecurityConfigActivator.getCredentialsStorageService(); /** * The <tt>ResourceManagementService</tt> used by this instance to access * the localized and internationalized resources of the application. */ private static final ResourceManagementService resources = SecurityConfigActivator.getResources(); /** * Instance of this dialog. */ private static SavedPasswordsDialog dialog; /** * Builds the dialog. */ private SavedPasswordsDialog() { super(false); initComponents(); this.setTitle( resources.getI18NString( "plugin.securityconfig.masterpassword.SAVED_PASSWORDS")); this.setMinimumSize(new Dimension(550, 300)); this.setPreferredSize(new Dimension(550, 300)); this.setResizable(false); this.getContentPane().add(mainPanel); this.pack(); Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension screenSize = toolkit.getScreenSize(); int x = (screenSize.width - this.getWidth()) / 2; int y = (screenSize.height - this.getHeight()) / 2; this.setLocation(x,y); } /** * Initialises the UI components. */ private void initComponents() { this.setLayout(new GridBagLayout()); mainPanel = new TransparentPanel(new BorderLayout(10, 10)); GridBagConstraints c = new GridBagConstraints(); c.gridy = 0; c.fill = GridBagConstraints.BOTH; c.weightx = 1.0; c.weighty = 1.0; c.insets = new Insets(5, 5, 5, 5); c.anchor = GridBagConstraints.PAGE_START; PasswordsPanel accPassPanel = new PasswordsPanel(); this.add(accPassPanel, c); c.gridy = 1; c.weighty = 0.0; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.LAST_LINE_END; closeButton = new JButton(resources.getI18NString("service.gui.CLOSE")); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { dialog = null; dispose(); } }); this.add(closeButton, c); } @Override protected void close(boolean isEscaped) { closeButton.doClick(); } /** * @return the {@link SavedPasswordsDialog} instance */ public static SavedPasswordsDialog getInstance() { if (dialog == null) { dialog = new SavedPasswordsDialog(); } return dialog; } /** * Panel containing the accounts table and buttons. */ private static class PasswordsPanel extends TransparentPanel { /** * Serial version UID. */ private static final long serialVersionUID = 0L; /** * The table model for the accounts table. */ private class PasswordsTableModel extends AbstractTableModel { /** * Serial version UID. */ private static final long serialVersionUID = 0L; /** * Index of the first column. */ public static final int TYPE_INDEX = 0; /** * Index of the second column. */ public static final int USER_NAME_INDEX = 1; /** * Index of the third column. */ public static final int PASSWORD_INDEX = 2; /** * The row objects for this model. */ public final List<PasswordsTableRow> savedPasswords = new ArrayList<PasswordsTableRow>(); /** * Returns the name for the given column. * * @param column the column index * @return the column name for the given index */ @Override public String getColumnName(int column) { String key; switch (column) { case TYPE_INDEX: key = "plugin.securityconfig.masterpassword.COL_TYPE"; break; case USER_NAME_INDEX: key = "plugin.securityconfig.masterpassword.COL_NAME"; break; case PASSWORD_INDEX: key = "plugin.securityconfig.masterpassword.COL_PASSWORD"; break; default: return null; } return resources.getI18NString(key); } /** * Returns the value for the given row and column. * * @param row table's row * @param column table's column * @return object inside the table at the given row and column */ public Object getValueAt(int row, int column) { if (row < 0) return null; PasswordsTableRow savedPass = savedPasswords.get(row); switch (column) { case TYPE_INDEX: return savedPass.type; case USER_NAME_INDEX: return savedPass.name; case PASSWORD_INDEX: String pass = credentialsStorageService .loadPassword(savedPass.property); return (pass == null) ? resources .getI18NString( "plugin.securityconfig.masterpassword.CANNOT_DECRYPT") : pass; default: return null; } } /** * Number of rows in the table. * * @return number of rows */ public int getRowCount() { return savedPasswords.size(); } /** * Number of columns depends on whether we are showing passwords or * not. * * @return number of columns */ public int getColumnCount() { return showPasswords ? 3 : 2; } } /** * Are we showing the passwords column or not. */ private boolean showPasswords = false; /** * The button to remove the saved password for the selected account. */ private JButton removeButton; /** * The button to remove saved passwords for all accounts. */ private JButton removeAllButton; /** * The button to show the saved passwords for all accounts in plain text. */ private JButton showPasswordsButton; /** * The table itself. */ private JTable accountsTable; /** * Builds the panel. */ public PasswordsPanel() { this.initComponents(); this.initContent(); } /** * Returns the {@link AccountID} object for the selected row. * @return the selected account */ private PasswordsTableRow getSelectedAccountID() { PasswordsTableModel model = (PasswordsTableModel) accountsTable.getModel(); int index = accountsTable.getSelectedRow(); if (index < 0 || index > model.savedPasswords.size()) return null; return model.savedPasswords.get(index); } /** * Initializes the table's components. */ private void initComponents() { setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), resources.getI18NString( "plugin.securityconfig.masterpassword.STORED_ACCOUNT_PASSWORDS"))); this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); accountsTable = new JTable(); accountsTable.setModel(new PasswordsTableModel()); accountsTable.setSelectionMode( javax.swing.ListSelectionModel.SINGLE_SELECTION); accountsTable.setCellSelectionEnabled(false); accountsTable.setColumnSelectionAllowed(false); accountsTable.setRowSelectionAllowed(true); accountsTable.getColumnModel().getColumn( PasswordsTableModel.USER_NAME_INDEX).setPreferredWidth(270); accountsTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; // activate remove button on select removeButton.setEnabled(true); } }); JScrollPane pnlAccounts = new JScrollPane(accountsTable); this.add(pnlAccounts); JPanel pnlButtons = new TransparentPanel(); pnlButtons.setLayout(new BorderLayout()); this.add(pnlButtons); JPanel leftButtons = new TransparentPanel(); pnlButtons.add(leftButtons, BorderLayout.WEST); removeButton = new JButton( resources.getI18NString( "plugin.securityconfig.masterpassword.REMOVE_PASSWORD_BUTTON")); // enabled on row selection removeButton.setEnabled(false); removeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { PasswordsTableRow selectedRow = getSelectedAccountID(); if (selectedRow != null) { PasswordsTableModel model = (PasswordsTableModel) accountsTable.getModel(); removeSavedPassword(selectedRow.property); model.savedPasswords.remove(selectedRow); int selectedRowIx = accountsTable.getSelectedRow(); model.fireTableRowsDeleted(selectedRowIx, selectedRowIx); } } }); leftButtons.add(removeButton); removeAllButton = new JButton( resources.getI18NString( "plugin.securityconfig.masterpassword.REMOVE_ALL_PASSWORDS_BUTTON")); removeAllButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { PasswordsTableModel model = (PasswordsTableModel) accountsTable.getModel(); if (model.savedPasswords.isEmpty()) { return; } int answer = SecurityConfigActivator .getUIService() .getPopupDialog() .showConfirmPopupDialog( resources.getI18NString( "plugin.securityconfig.masterpassword.REMOVE_ALL_CONFIRMATION"), resources.getI18NString( "plugin.securityconfig.masterpassword.REMOVE_ALL_TITLE"), PopupDialog.YES_NO_OPTION); if (answer == PopupDialog.YES_OPTION) { for (PasswordsTableRow row : model.savedPasswords) { removeSavedPassword(row.property); } model.savedPasswords.clear(); model.fireTableDataChanged(); } } }); leftButtons.add(removeAllButton); JPanel rightButtons = new TransparentPanel(); pnlButtons.add(rightButtons, BorderLayout.EAST); showPasswordsButton = new JButton( resources.getI18NString( "plugin.securityconfig.masterpassword.SHOW_PASSWORDS_BUTTON")); showPasswordsButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { // show the master password input only when it's set and the // passwords column is hidden if (credentialsStorageService.isUsingMasterPassword() && !showPasswords) { showOrHidePasswordsProtected(); } else { showOrHidePasswords(); } } }); rightButtons.add(showPasswordsButton); } /** * Loads data that support password saving. */ private void initContent() { // init content with accounts passwords PasswordsTableModel model = (PasswordsTableModel) accountsTable.getModel(); for(Map.Entry<AccountID, String> entry : SecurityConfigActivator.getAccountIDsWithSavedPasswords() .entrySet()) { AccountID accID = entry.getKey(); String protocol = accID.getAccountPropertyString( ProtocolProviderFactory.PROTOCOL); if(protocol == null) protocol = resources.getI18NString( "plugin.securityconfig.masterpassword.PROTOCOL_UNKNOWN"); model.savedPasswords.add( new PasswordsTableRow( entry.getValue(), protocol, accID.getUserID())); } for(Map.Entry<String, String> entry : SecurityConfigActivator.getChatRoomsWithSavedPasswords() .entrySet()) { String description = entry.getKey(); model.savedPasswords.add( new PasswordsTableRow( entry.getValue(), resources.getI18NString("service.gui.CHAT_ROOM"), description)); } // load provisioning passwords String PROVISIONING_PROPERTIES_PREFIX = "net.java.sip.communicator.plugin.provisioning.auth"; ConfigurationService configurationService = SecurityConfigActivator.getConfigurationService(); String uname = configurationService .getString(PROVISIONING_PROPERTIES_PREFIX + ".USERNAME"); if(uname != null && uname.length() > 0) model.savedPasswords.add( new PasswordsTableRow( PROVISIONING_PROPERTIES_PREFIX, resources.getI18NString( "plugin.provisioning.PROVISIONING"), uname)); // load http passwords String HTTP_PROPERTIES_PREFIX = "net.java.sip.communicator.util.http.credential"; // This will contain double entries for a password // one for username and one for the password List<String> httpPasses = configurationService.getPropertyNamesByPrefix( HTTP_PROPERTIES_PREFIX, false); int prefLen = HTTP_PROPERTIES_PREFIX.length() + 1; for(String prop: httpPasses) { // we skip the entry containing the encoded password if(prop.contains("PASSWORD") || prefLen > prop.length()) continue; model.savedPasswords.add( new PasswordsTableRow( prop, "http://" + prop.substring(prefLen), configurationService.getString(prop))); } } /** * Removes the password from the storage. * * @param property for the saved password */ private void removeSavedPassword(String property) { credentialsStorageService.removePassword(property); } /** * Toggles the passwords column. */ private void showOrHidePasswords() { showPasswords = !showPasswords; showPasswordsButton.setText( resources.getI18NString( showPasswords ? "plugin.securityconfig.masterpassword.HIDE_PASSWORDS_BUTTON" : "plugin.securityconfig.masterpassword.SHOW_PASSWORDS_BUTTON")); PasswordsTableModel model = (PasswordsTableModel) accountsTable.getModel(); model.fireTableStructureChanged(); } /** * Displays a master password prompt to the user, verifies the entered * password and then executes <tt>showOrHidePasswords</tt> method. */ private void showOrHidePasswordsProtected() { String master; boolean correct = true; MasterPasswordInputService masterPasswordInputService = SecurityConfigActivator.getMasterPasswordInputService(); if(masterPasswordInputService == null) { logger.error("Missing MasterPasswordInputService to show input dialog"); return; } do { master = masterPasswordInputService.showInputDialog(correct); if (master == null) return; correct = (master.length() != 0) && credentialsStorageService .verifyMasterPassword(master); } while (!correct); showOrHidePasswords(); } /** * Object representing passwords table row. */ class PasswordsTableRow { /** * The property we can use to extract password from * credential service. */ private String property; /** * String representing type that is displayed to user. */ private String type; /** * String representing name that is displayed to user. */ private String name; /** * Constructing. * @param property the property * @param type the type of the record. * @param name the name of the record. */ PasswordsTableRow(String property, String type, String name) { this.property = property; this.type = type; this.name = name; } } } }