Index: src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java =================================================================== --- src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java (revision 1953) +++ src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java (working copy) @@ -11,6 +11,8 @@ import org.openstreetmap.josm.Main; import org.openstreetmap.josm.tools.GBC; +import org.openstreetmap.josm.io.OsmConnection; +import org.openstreetmap.josm.io.CredentialsManager.PreferenceAdditions; public class ServerAccessPreference implements PreferenceSetting { @@ -25,41 +27,22 @@ */ private JTextField osmDataServer = new JTextField(20); /** - * Editfield for the username to the OSM account. + * Provide username and password input editfields. + * Store the values if user hits OK. */ - private JTextField osmDataUsername = new JTextField(20); - /** - * Passwordfield for the userpassword of the REST API. - */ - private JPasswordField osmDataPassword = new JPasswordField(20); + private PreferenceAdditions credentialsPA = OsmConnection.credentialsManager.newPreferenceAdditions(); public void addGui(PreferenceDialog gui) { osmDataServer.setText(Main.pref.get("osm-server.url", "http://api.openstreetmap.org/api")); - osmDataUsername.setText(Main.pref.get("osm-server.username")); - osmDataPassword.setText(Main.pref.get("osm-server.password")); - osmDataServer.setToolTipText(tr("The base URL for the OSM server (REST API)")); - osmDataUsername.setToolTipText(tr("Login name (e-mail) to the OSM account.")); - osmDataPassword.setToolTipText(tr("Login password to the OSM account. Leave blank to not store any password.")); - gui.connection.add(new JLabel(tr("Base Server URL")), GBC.std()); gui.connection.add(osmDataServer, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); - gui.connection.add(new JLabel(tr("OSM username (e-mail)")), GBC.std()); - gui.connection.add(osmDataUsername, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); - gui.connection.add(new JLabel(tr("OSM password")), GBC.std()); - gui.connection.add(osmDataPassword, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,0)); - JLabel warning = new JLabel(tr("" + - "WARNING: The password is stored in plain text in the preferences file.
" + - "The password is transferred in plain text to the server, encoded in the URL.
" + - "Do not use a valuable Password.")); - warning.setFont(warning.getFont().deriveFont(Font.ITALIC)); - gui.connection.add(warning, GBC.eop().fill(GBC.HORIZONTAL)); + credentialsPA.addPreferenceOptions(gui.connection); } public boolean ok() { Main.pref.put("osm-server.url", osmDataServer.getText()); - Main.pref.put("osm-server.username", osmDataUsername.getText()); - Main.pref.put("osm-server.password", String.valueOf(osmDataPassword.getPassword())); + credentialsPA.preferencesChanged(); return false; } } Index: src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java =================================================================== --- src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java (revision 1953) +++ src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java (working copy) @@ -40,7 +40,7 @@ // some common tabs public final JPanel display = createPreferenceTab("display", tr("Display Settings"), tr("Various settings that influence the visual representation of the whole program.")); - public final JPanel connection = createPreferenceTab("connection", I18n.tr("Connection Settings"), I18n.tr("Connection Settings for the OSM server.")); + public final JPanel connection = createPreferenceTab("connection", I18n.tr("Connection Settings"), I18n.tr("Connection Settings for the OSM server."),true); public final JPanel map = createPreferenceTab("map", I18n.tr("Map Settings"), I18n.tr("Settings for the map projection and data interpretation.")); public final JPanel audio = createPreferenceTab("audio", I18n.tr("Audio Settings"), I18n.tr("Settings for the audio player and audio markers.")); Index: src/org/openstreetmap/josm/io/CredentialsManager.java =================================================================== --- src/org/openstreetmap/josm/io/CredentialsManager.java (revision 0) +++ src/org/openstreetmap/josm/io/CredentialsManager.java (revision 0) @@ -0,0 +1,54 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.io; + +import org.openstreetmap.josm.io.OsmConnection.OsmAuth; + +/** + * Manages how username and password are stored. In addition all + * username/password-related user interaction is encapsulated here. + */ +public interface CredentialsManager { + /** + * lookupUsername, lookupPassword: + * + * Should throw or return non-null, possibly empty String. + */ + public String lookupUsername() throws CMException; + public String lookupPassword() throws CMException; + + /** + * storeUsername, storePassword: + * + * May silently fail to store. + */ + public void storeUsername(String username) throws CMException; + public void storePassword(String password) throws CMException; + + /** + * If authentication using the stored credentials fails, this method is + * called to promt for new username/password. + */ + public java.net.PasswordAuthentication getPasswordAuthentication(OsmAuth caller); + + /** + * Credentials-related preference gui. + */ + public interface PreferenceAdditions { + public void addPreferenceOptions(javax.swing.JPanel panel); + public void preferencesChanged(); + } + public PreferenceAdditions newPreferenceAdditions(); + + public class CMException extends Exception { + public CMException() {super();} + public CMException(String message, Throwable cause) {super(message, cause);} + public CMException(String message) {super(message);} + public CMException(Throwable cause) {super(cause);} + } + public class NoContentException extends CMException { + public NoContentException() {super();} + public NoContentException(String message, Throwable cause) {super(message, cause);} + public NoContentException(String message) {super(message);} + public NoContentException(Throwable cause) {super(cause);} + } +} Index: src/org/openstreetmap/josm/io/OsmConnection.java =================================================================== --- src/org/openstreetmap/josm/io/OsmConnection.java (revision 1953) +++ src/org/openstreetmap/josm/io/OsmConnection.java (working copy) @@ -24,6 +24,7 @@ import org.openstreetmap.josm.gui.ExtendedDialog; import org.openstreetmap.josm.tools.Base64; import org.openstreetmap.josm.tools.GBC; +import org.openstreetmap.josm.io.CredentialsManager.CMException; /** * Base class that handles common things like authentication for the reader and writer @@ -35,8 +36,17 @@ protected boolean cancel = false; protected HttpURLConnection activeConnection; + /** + * Handles password storage and some related gui-components. + * It can be set by a plugin. This may happen at startup and + * by changing the preferences. + * Syncronize on this object to get or set a consistent + * username/password pair. + */ + public static CredentialsManager credentialsManager = new PlainCredentialsManager(); private static OsmAuth authentication = new OsmAuth(); + /** * Initialize the http defaults and the authenticator. */ @@ -54,58 +64,17 @@ /** * The authentication class handling the login requests. */ - private static class OsmAuth extends Authenticator { + public static class OsmAuth extends Authenticator { /** * Set to true, when the autenticator tried the password once. */ - boolean passwordtried = false; + public boolean passwordtried = false; /** * Whether the user cancelled the password dialog */ - boolean authCancelled = false; - + public boolean authCancelled = false; @Override protected PasswordAuthentication getPasswordAuthentication() { - String username = Main.pref.get("osm-server.username"); - String password = Main.pref.get("osm-server.password"); - if (passwordtried || username.equals("") || password.equals("")) { - JPanel p = new JPanel(new GridBagLayout()); - if (!username.equals("") && !password.equals("")) { - p.add(new JLabel(tr("Incorrect password or username.")), GBC.eop()); - } - p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,10,0)); - JTextField usernameField = new JTextField(username, 20); - p.add(usernameField, GBC.eol()); - p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,10,0)); - JPasswordField passwordField = new JPasswordField(password, 20); - p.add(passwordField, GBC.eol()); - JLabel warning = new JLabel(tr("Warning: The password is transferred unencrypted.")); - warning.setFont(warning.getFont().deriveFont(Font.ITALIC)); - p.add(warning, GBC.eop()); - - JCheckBox savePassword = new JCheckBox(tr("Save user and password (unencrypted)"), !username.equals("") && !password.equals("")); - p.add(savePassword, GBC.eop()); - - int choice = new ExtendedDialog(Main.parent, - tr("Enter Password"), - p, - new String[] {tr("Login"), tr("Cancel")}, - new String[] {"ok.png", "cancel.png"}).getValue(); - - if (choice != 1) { - authCancelled = true; - return null; - } - username = usernameField.getText(); - password = String.valueOf(passwordField.getPassword()); - if (savePassword.isSelected()) { - Main.pref.put("osm-server.username", username); - Main.pref.put("osm-server.password", password); - } - if (username.equals("")) - return null; - } - passwordtried = true; - return new PasswordAuthentication(username, password.toCharArray()); + return credentialsManager.getPasswordAuthentication(this); } } @@ -140,7 +109,14 @@ protected void addAuth(HttpURLConnection con) throws CharacterCodingException { CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); - String auth = Main.pref.get("osm-server.username") + ":" + Main.pref.get("osm-server.password"); + String auth; + try { + synchronized (credentialsManager) { + auth = credentialsManager.lookupUsername() + ":" + credentialsManager.lookupPassword(); + } + } catch (CMException e) { + auth = ":"; + } ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth)); con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes)); } @@ -154,4 +130,125 @@ public boolean isCanceled() { return cancel; } + + public static class PlainCredentialsManager implements CredentialsManager { + public String lookupUsername() throws CMException { + String username = Main.pref.get("osm-server.username", null); + if (username == null) throw new CredentialsManager.NoContentException(); + return username; + } + public String lookupPassword() throws CMException { + String password = Main.pref.get("osm-server.password"); + if (password == null) throw new CredentialsManager.NoContentException(); + return password; + } + public void storeUsername(String username) { + Main.pref.put("osm-server.username", username); + } + public void storePassword(String password) { + Main.pref.put("osm-server.password", password); + } + public PasswordAuthentication getPasswordAuthentication(OsmAuth caller) { + String username, password; + try { + username = lookupUsername(); + } catch (CMException e) { + username = ""; + } + try { + password = lookupPassword(); + } catch (CMException e) { + password = ""; + } + if (caller.passwordtried || username.equals("") || password.equals("")) { + JPanel p = new JPanel(new GridBagLayout()); + if (!username.equals("") && !password.equals("")) { + p.add(new JLabel(tr("Incorrect password or username.")), GBC.eop()); + } + p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,10,0)); + JTextField usernameField = new JTextField(username, 20); + p.add(usernameField, GBC.eol()); + p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,10,0)); + JPasswordField passwordField = new JPasswordField(password, 20); + p.add(passwordField, GBC.eol()); + JLabel warning = new JLabel(tr("Warning: The password is transferred unencrypted.")); + warning.setFont(warning.getFont().deriveFont(Font.ITALIC)); + p.add(warning, GBC.eop()); + + JCheckBox savePassword = new JCheckBox(tr("Save user and password (unencrypted)"), !username.equals("") && !password.equals("")); + p.add(savePassword, GBC.eop()); + + int choice = new ExtendedDialog(Main.parent, + tr("Enter Password"), + p, + new String[] {tr("Login"), tr("Cancel")}, + new String[] {"ok.png", "cancel.png"}).getValue(); + + if (choice != 1) { + caller.authCancelled = true; + return null; + } + username = usernameField.getText(); + password = String.valueOf(passwordField.getPassword()); + if (savePassword.isSelected()) { + storeUsername(username); + storePassword(password); + } + if (username.equals("")) + return null; + } + caller.passwordtried = true; + return new PasswordAuthentication(username, password.toCharArray()); + } + public PreferenceAdditions newPreferenceAdditions() { + return new PreferenceAdditions() { + /** + * Editfield for the username to the OSM account. + */ + private JTextField osmDataUsername = new JTextField(20); + /** + * Passwordfield for the userpassword of the REST API. + */ + private JPasswordField osmDataPassword = new JPasswordField(20); + + private String oldUsername = ""; + private String oldPassword = ""; + + public void addPreferenceOptions(JPanel panel) { + try { + oldUsername = lookupUsername(); + } catch (CMException e) { + oldUsername = ""; + } + try { + oldPassword = lookupPassword(); + } catch (CMException e) { + oldPassword = ""; + } + osmDataUsername.setText(oldUsername); + osmDataPassword.setText(oldPassword); + osmDataUsername.setToolTipText(tr("Login name (e-mail) to the OSM account.")); + osmDataPassword.setToolTipText(tr("Login password to the OSM account. Leave blank to not store any password.")); + panel.add(new JLabel(tr("OSM username (e-mail)")), GBC.std()); + panel.add(osmDataUsername, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); + panel.add(new JLabel(tr("OSM password")), GBC.std()); + panel.add(osmDataPassword, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,0)); + JLabel warning = new JLabel(tr("" + + "WARNING: The password is stored in plain text in the preferences file.
" + + "The password is transferred in plain text to the server, encoded in the URL.
" + + "Do not use a valuable Password.")); + warning.setFont(warning.getFont().deriveFont(Font.ITALIC)); + panel.add(warning, GBC.eop().fill(GBC.HORIZONTAL)); + } + public void preferencesChanged() { + String newUsername = osmDataUsername.getText(); + String newPassword = String.valueOf(osmDataPassword.getPassword()); + if (!oldUsername.equals(newUsername)) + storeUsername(newUsername); + if (!oldPassword.equals(newPassword)) + storePassword(newPassword); + } + }; + } + } }