/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.tools; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Window; import java.net.PasswordAuthentication; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; import com.rapidminer.RapidMiner; import com.rapidminer.gui.ApplicationFrame; import com.rapidminer.gui.RapidMinerGUI; import com.rapidminer.gui.security.UserCredential; import com.rapidminer.gui.security.Wallet; import com.rapidminer.gui.tools.SwingTools.ResultRunnable; import com.rapidminer.gui.tools.dialogs.ButtonDialog; import com.rapidminer.tools.LogService; import com.rapidminer.tools.PasswordInputCanceledException; /** * Dialog asking for username and passwords. Answers may be cached (if chosen by user). * * @author Simon Fischer * */ public class PasswordDialog extends ButtonDialog { /** * Determines if the PasswordDialog should be suppressed */ private static AtomicInteger suppressionCount = new AtomicInteger(0); /** * Checks if a suppression exists * * @return true if one or more suppressions exist */ private static boolean isSuppressed() { return suppressionCount.get() > 0; } /** * Prevents the dialog from being shown to the user * <p> * Warning: You must remove the suppression from the PasswordDialog after you are done! * </p> */ public static void addSuppression() { suppressionCount.incrementAndGet(); } /** * Removes the suppression from the PasswordDialog, must be called after addSuppression */ public static void removeSuppression() { suppressionCount.decrementAndGet(); } private static final long serialVersionUID = 1L; private JTextField usernameField = new JTextField(20); private JPasswordField passwordField = new JPasswordField(20); private JCheckBox rememberBox = new JCheckBox(new ResourceActionAdapter("authentication.remember")); private PasswordDialog(Window owner, String i18nKey, UserCredential preset, Object... args) { super(owner, i18nKey, ModalityType.APPLICATION_MODAL, args); setModal(true); if (preset != null && preset.getUsername() != null) { usernameField.setText(preset.getUsername()); } if (preset != null && preset.getPassword() != null) { passwordField.setText(new String(preset.getPassword())); rememberBox.setSelected(true); } String url = preset != null ? preset.getURL() : null; JPanel main = new JPanel(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.FIRST_LINE_START; c.insets = new Insets(4, 4, 4, 4); JLabel label = new ResourceLabel("authentication.username", url); label.setLabelFor(usernameField); c.gridwidth = GridBagConstraints.RELATIVE; main.add(label, c); c.gridwidth = GridBagConstraints.REMAINDER; main.add(usernameField, c); label = new ResourceLabel("authentication.password", url); label.setLabelFor(passwordField); c.gridwidth = GridBagConstraints.RELATIVE; main.add(label, c); c.gridwidth = GridBagConstraints.REMAINDER; main.add(passwordField, c); main.add(rememberBox, c); layoutDefault(main, makeOkButton(), makeCancelButton()); } public PasswordAuthentication makeAuthentication() { return new PasswordAuthentication(usernameField.getText(), passwordField.getPassword()); } public static PasswordAuthentication getPasswordAuthentication(String forUrl, boolean forceRefresh) throws PasswordInputCanceledException { return getPasswordAuthentication(forUrl, forceRefresh, false); } /** * @param id * the ID to accompany the given url. Used to differentiate multiple entries for the * same url. * @param i18nKey * The i18nKey has to look like every dialog i18n: gui.dialog.KEY.title etc. * @param args * Will be used when i18nKey is set. If i18nKey is <code>null</code> it will be not * be used **/ @SuppressWarnings("deprecation") public static PasswordAuthentication getPasswordAuthentication(String id, String forUrl, boolean forceRefresh, boolean hideDialogIfPasswordKnown, String i18nKey, Object... args) throws PasswordInputCanceledException { // First check whether the credentials are stored within the Wallet UserCredential authentication = Wallet.getInstance().getEntry(id, forUrl); // return immediately if known and desired if (hideDialogIfPasswordKnown && !forceRefresh && authentication != null && authentication.getPassword() != null) { if (authentication.getPassword().length == 0) { return null; } LogService.getRoot().log(Level.CONFIG, "com.rapidminer.gui.tools.PassworDialog.reusing_cached_password", forUrl); return authentication.makePasswordAuthentication(); } // If password was not stored within the wallet or a refresh is forced, check whether we are // in headless mode if (RapidMiner.getExecutionMode().isHeadless()) { LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.tools.PassworDialog.no_query_password_in_batch_mode", forUrl); return null; } // Throw away all requests before the UI is ready if (!RapidMinerGUI.isInitialized() || !ApplicationFrame.getApplicationFrame().isVisible()) { return null; } // If the PasswordDialog is suppressed for reasons don't show it if (PasswordDialog.isSuppressed()) { return null; } char[] oldPassword = null; // clear cache if refresh forced if (forceRefresh && authentication != null) { oldPassword = authentication.getPassword(); authentication.setPassword(null); Wallet.getInstance().registerCredentials(id, authentication); } if (authentication == null) { authentication = new UserCredential(forUrl, null, null); } if (i18nKey == null || i18nKey.contains("authentication")) { i18nKey = "authentication"; args = new Object[] { authentication.getURL() }; } final Object[] pdArgs = args; final UserCredential passwordDialogCredentials = authentication; final String passwordI18N = i18nKey; // create PasswordDialog on EDT final PasswordDialog pd = SwingTools.invokeAndWaitWithResult(new ResultRunnable<PasswordDialog>() { @Override public PasswordDialog run() { PasswordDialog pd = new PasswordDialog(ApplicationFrame.getApplicationFrame(), passwordI18N, passwordDialogCredentials, pdArgs); // Onboarding is already a APPLICATION_MODAL pd.setModalityType(ModalityType.TOOLKIT_MODAL); pd.setVisible(true); return pd; } }); if (pd.wasConfirmed()) { PasswordAuthentication result = pd.makeAuthentication(); if (pd.rememberBox.isSelected()) { UserCredential userCredential = new UserCredential(forUrl, result.getUserName(), result.getPassword()); if (id != null) { Wallet.getInstance().registerCredentials(id, userCredential); } else { // compatibility with old API Wallet.getInstance().registerCredentials(userCredential); } Wallet.getInstance().saveCache(); } else { if (id != null) { Wallet.getInstance().removeEntry(id, forUrl); } else { // compatibility with old API Wallet.getInstance().removeEntry(forUrl); } Wallet.getInstance().saveCache(); } return result; } else { // If the user canceled the Password Dialog on forceRefresh the password is at this // point null. Restore old value to prevent NPEs. if (forceRefresh) { authentication.setPassword(oldPassword); } throw new PasswordInputCanceledException(); } } public static PasswordAuthentication getPasswordAuthentication(String forUrl, boolean forceRefresh, boolean hideDialogIfPasswordKnown) throws PasswordInputCanceledException { return getPasswordAuthentication(forUrl, forceRefresh, hideDialogIfPasswordKnown, null); } /** * @param i18nKey * The i18nKey has to look like every dialog i18n: gui.dialog.KEY.title etc. * @param args * Will be used when i18nKey is set. If i18nKey is <code>null</code> it will be not * be used * @deprecated use * {@link #getPasswordAuthentication(String, String, boolean, boolean, String, Object...)} * instead. **/ @Deprecated public static PasswordAuthentication getPasswordAuthentication(String forUrl, boolean forceRefresh, boolean hideDialogIfPasswordKnown, String i18nKey, Object... args) throws PasswordInputCanceledException { return getPasswordAuthentication(null, forUrl, forceRefresh, hideDialogIfPasswordKnown, null); } }