/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.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.io.File;
import java.io.IOException;
import java.net.PasswordAuthentication;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
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 javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.rapidminer.RapidMiner;
import com.rapidminer.gui.tools.dialogs.ButtonDialog;
import com.rapidminer.io.Base64;
import com.rapidminer.io.process.XMLTools;
import com.rapidminer.tools.FileSystemService;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.XMLException;
/** Dialog asking for username and passwords. Answers may be cached (if chosen by user).
*
* @author Simon Fischer
*
*/
public class PasswordDialog extends ButtonDialog {
private static final String CACHE_FILE_NAME = "secrets.xml";
/** Maps URLs to authentications. */
private static Map<String,PasswordAuthentication> CACHE = new HashMap<String,PasswordAuthentication>();
static {
readCache();
}
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(PasswordAuthentication preset, String url) {
this("authentication", preset, url);
}
private PasswordDialog(String i18nKey, PasswordAuthentication preset, String url) {
super(i18nKey, url);
setModal(true);
if (preset != null) {
usernameField.setText(preset.getUserName());
}
if (preset!= null) {
passwordField.setText(new String(preset.getPassword()));
rememberBox.setSelected(true);
}
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, makeCancelButton(), makeOkButton());
}
public PasswordAuthentication makeAuthentication() {
return new PasswordAuthentication(usernameField.getText(), passwordField.getPassword());
}
public static PasswordAuthentication getPasswordAuthentication(String forUrl, boolean forceRefresh) {
return getPasswordAuthentication(forUrl, forceRefresh, false);
}
public static PasswordAuthentication getPasswordAuthentication(String forUrl, boolean forceRefresh, boolean hideDialogIfPasswordKnown) {
if (RapidMiner.getExecutionMode().isHeadless()) {
LogService.getRoot().warning("Cannot query password in batch mode. Password was requested for "+forUrl+".");
return null;
}
PasswordAuthentication authentication = CACHE.get(forUrl);
// return immediately if known and desired
if (hideDialogIfPasswordKnown && !forceRefresh && (authentication != null) && (authentication.getPassword() != null)) {
LogService.getRoot().config("Reusing cached password for "+forUrl+".");
return authentication;
}
// clear cache if refresh forced
if (forceRefresh && authentication != null) {
authentication = new PasswordAuthentication(authentication.getUserName(), null);
CACHE.put(forUrl, authentication);
}
final PasswordDialog pd = new PasswordDialog(authentication, forUrl);
pd.setVisible(true);
if (pd.wasConfirmed()) {
PasswordAuthentication result = pd.makeAuthentication();
if (pd.rememberBox.isSelected()) {
CACHE.put(forUrl, result);
} else {
CACHE.remove(forUrl);
}
saveCache();
return result;
} else {
return null;
}
}
private static void saveCache() {
LogService.getRoot().config("Saving secrets file.");
Document doc;
try {
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
} catch (ParserConfigurationException e) {
LogService.getRoot().log(Level.WARNING, "Failed to create XML document: "+e, e);
return;
}
Element root = doc.createElement(CACHE_FILE_NAME);
doc.appendChild(root);
for (Entry<String, PasswordAuthentication> entry : CACHE.entrySet()) {
Element entryElem = doc.createElement("secret");
root.appendChild(entryElem);
XMLTools.setTagContents(entryElem, "url", entry.getKey());
XMLTools.setTagContents(entryElem, "user", entry.getValue().getUserName());
XMLTools.setTagContents(entryElem, "password", Base64.encodeBytes(new String(entry.getValue().getPassword()).getBytes()));
}
File file = FileSystemService.getUserConfigFile(CACHE_FILE_NAME);
try {
XMLTools.stream(doc, file, null);
} catch (XMLException e) {
LogService.getRoot().log(Level.WARNING, "Failed to save secrets file: "+e, e);
}
}
private static void readCache() {
final File userConfigFile = FileSystemService.getUserConfigFile(CACHE_FILE_NAME);
if (!userConfigFile.exists()) {
return;
}
LogService.getRoot().config("Reading secrets file.");
Document doc;
try {
doc = XMLTools.parse(userConfigFile);
} catch (Exception e) {
LogService.getRoot().log(Level.WARNING, "Failed to read secrets file: "+e, e);
return;
}
NodeList secretElems = doc.getDocumentElement().getElementsByTagName("secret");
for (int i = 0; i < secretElems.getLength(); i++) {
Element secretElem = (Element) secretElems.item(i);
String url = XMLTools.getTagContents(secretElem, "url");
String user = XMLTools.getTagContents(secretElem, "user");
String password;
try {
password = new String(Base64.decode(XMLTools.getTagContents(secretElem, "password")));
} catch (IOException e) {
LogService.getRoot().log(Level.WARNING, "Failed to read entry in secrets file: "+e, e);
continue;
}
CACHE.put(url, new PasswordAuthentication(user, password.toCharArray()));
}
}
}