Ticket #3112: npm_patch

File npm_patch, 16.5 KB (added by bastiK, 3 years ago)
Line 
1Index: src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java
2===================================================================
3--- src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java      (revision 1859)
4+++ src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java      (working copy)
5@@ -11,6 +11,8 @@
6 
7 import org.openstreetmap.josm.Main;
8 import org.openstreetmap.josm.tools.GBC;
9+import org.openstreetmap.josm.io.OsmConnection;
10+import org.openstreetmap.josm.io.CredentialsManager.PreferenceAdditions;
11 
12 public class ServerAccessPreference implements PreferenceSetting {
13 
14@@ -25,41 +27,22 @@
15      */
16     private JTextField osmDataServer = new JTextField(20);
17     /**
18-     * Editfield for the username to the OSM account.
19+     * Provide username and password input editfields.
20+     * Store the values if user hits OK.
21      */
22-    private JTextField osmDataUsername = new JTextField(20);
23-    /**
24-     * Passwordfield for the userpassword of the REST API.
25-     */
26-    private JPasswordField osmDataPassword = new JPasswordField(20);
27+    private PreferenceAdditions credentialsPA = OsmConnection.credentialsManager.newPreferenceAdditions();
28 
29     public void addGui(PreferenceDialog gui) {
30         osmDataServer.setText(Main.pref.get("osm-server.url", "http://api.openstreetmap.org/api"));
31-        osmDataUsername.setText(Main.pref.get("osm-server.username"));
32-        osmDataPassword.setText(Main.pref.get("osm-server.password"));
33-
34         osmDataServer.setToolTipText(tr("The base URL for the OSM server (REST API)"));
35-        osmDataUsername.setToolTipText(tr("Login name (e-mail) to the OSM account."));
36-        osmDataPassword.setToolTipText(tr("Login password to the OSM account. Leave blank to not store any password."));
37-
38         gui.connection.add(new JLabel(tr("Base Server URL")), GBC.std());
39         gui.connection.add(osmDataServer, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
40-        gui.connection.add(new JLabel(tr("OSM username (e-mail)")), GBC.std());
41-        gui.connection.add(osmDataUsername, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
42-        gui.connection.add(new JLabel(tr("OSM password")), GBC.std());
43-        gui.connection.add(osmDataPassword, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,0));
44-        JLabel warning = new JLabel(tr("<html>" +
45-                "WARNING: The password is stored in plain text in the preferences file.<br>" +
46-                "The password is transferred in plain text to the server, encoded in the URL.<br>" +
47-        "<b>Do not use a valuable Password.</b></html>"));
48-        warning.setFont(warning.getFont().deriveFont(Font.ITALIC));
49-        gui.connection.add(warning, GBC.eop().fill(GBC.HORIZONTAL));
50+        credentialsPA.addPreferenceOptions(gui.connection);
51     }
52 
53     public boolean ok() {
54         Main.pref.put("osm-server.url", osmDataServer.getText());
55-        Main.pref.put("osm-server.username", osmDataUsername.getText());
56-        Main.pref.put("osm-server.password", String.valueOf(osmDataPassword.getPassword()));
57+        credentialsPA.preferencesChanged();
58         return false;
59     }
60 }
61Index: src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java
62===================================================================
63--- src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java    (revision 1859)
64+++ src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java    (working copy)
65@@ -39,7 +39,7 @@
66 
67     // some common tabs
68     public final JPanel display = createPreferenceTab("display", tr("Display Settings"), tr("Various settings that influence the visual representation of the whole program."));
69-    public final JPanel connection = createPreferenceTab("connection", I18n.tr("Connection Settings"), I18n.tr("Connection Settings for the OSM server."));
70+    public final JPanel connection = createPreferenceTab("connection", I18n.tr("Connection Settings"), I18n.tr("Connection Settings for the OSM server."),true);
71     public final JPanel map = createPreferenceTab("map", I18n.tr("Map Settings"), I18n.tr("Settings for the map projection and data interpretation."));
72     public final JPanel audio = createPreferenceTab("audio", I18n.tr("Audio Settings"), I18n.tr("Settings for the audio player and audio markers."));
73 
74Index: src/org/openstreetmap/josm/io/CredentialsManager.java
75===================================================================
76--- src/org/openstreetmap/josm/io/CredentialsManager.java       (revision 0)
77+++ src/org/openstreetmap/josm/io/CredentialsManager.java       (revision 0)
78@@ -0,0 +1,54 @@
79+// License: GPL. For details, see LICENSE file.
80+package org.openstreetmap.josm.io;
81+
82+import org.openstreetmap.josm.io.OsmConnection.OsmAuth;
83+
84+/**
85+ * Manages how username and password are stored. In addition all
86+ * username/password-related user interaction is encapsulated here.
87+ */
88+public interface CredentialsManager {
89+    /**
90+     * lookupUsername, lookupPassword:
91+     *
92+     * Should throw or return non-null, possibly empty String.
93+     */
94+    public String lookupUsername() throws CMException;
95+    public String lookupPassword() throws CMException;
96+
97+    /**
98+     * storeUsername, storePassword:
99+     *
100+     * May silently fail to store.
101+     */
102+    public void storeUsername(String username) throws CMException;
103+    public void storePassword(String password) throws CMException;
104+
105+    /**
106+     * If authentication using the stored credentials fails, this method is
107+     * called to promt for new username/password.
108+     */
109+    public java.net.PasswordAuthentication getPasswordAuthentication(OsmAuth caller);
110+
111+    /**
112+     * Credentials-related preference gui.
113+     */
114+    public interface PreferenceAdditions {
115+        public void addPreferenceOptions(javax.swing.JPanel panel);
116+        public void preferencesChanged();
117+    }
118+    public PreferenceAdditions newPreferenceAdditions();
119+
120+    public class CMException extends Exception {
121+        public CMException() {super();}
122+        public CMException(String message, Throwable cause) {super(message, cause);}
123+        public CMException(String message) {super(message);}
124+        public CMException(Throwable cause) {super(cause);}
125+    }
126+    public class NoContentException extends CMException {
127+        public NoContentException() {super();}
128+        public NoContentException(String message, Throwable cause) {super(message, cause);}
129+        public NoContentException(String message) {super(message);}
130+        public NoContentException(Throwable cause) {super(cause);}
131+    }
132+}
133Index: src/org/openstreetmap/josm/io/OsmConnection.java
134===================================================================
135--- src/org/openstreetmap/josm/io/OsmConnection.java    (revision 1859)
136+++ src/org/openstreetmap/josm/io/OsmConnection.java    (working copy)
137@@ -24,6 +24,7 @@
138 import org.openstreetmap.josm.gui.ExtendedDialog;
139 import org.openstreetmap.josm.tools.Base64;
140 import org.openstreetmap.josm.tools.GBC;
141+import org.openstreetmap.josm.io.CredentialsManager.CMException;
142 
143 /**
144  * Base class that handles common things like authentication for the reader and writer
145@@ -35,8 +36,17 @@
146 
147     protected boolean cancel = false;
148     protected HttpURLConnection activeConnection;
149+    /**
150+     * Handles password storage and some related gui-components.
151+     * It can be set by a plugin. This may happen at startup and
152+     * by changing the preferences.
153+     * Syncronize on this object to get or set a consistent
154+     * username/password pair.
155+     */
156+    public static CredentialsManager credentialsManager = new PlainCredentialsManager();
157 
158     private static OsmAuth authentication = new OsmAuth();
159+
160     /**
161      * Initialize the http defaults and the authenticator.
162      */
163@@ -54,20 +64,93 @@
164     /**
165      * The authentication class handling the login requests.
166      */
167-    private static class OsmAuth extends Authenticator {
168+    public static class OsmAuth extends Authenticator {
169         /**
170          * Set to true, when the autenticator tried the password once.
171          */
172-        boolean passwordtried = false;
173+        public boolean passwordtried = false;
174         /**
175          * Whether the user cancelled the password dialog
176          */
177-        boolean authCancelled = false;
178+        public boolean authCancelled = false;
179+        @Override protected PasswordAuthentication getPasswordAuthentication() {
180+            return credentialsManager.getPasswordAuthentication(this);
181+        }
182+    }
183 
184-        @Override protected PasswordAuthentication getPasswordAuthentication() {
185-            String username = Main.pref.get("osm-server.username");
186+    /**
187+     * Must be called before each connection attemp to initialize the authentication.
188+     */
189+    protected final void initAuthentication() {
190+        authentication.authCancelled = false;
191+        authentication.passwordtried = false;
192+    }
193+
194+    /**
195+     * @return Whether the connection was cancelled.
196+     */
197+    protected final boolean isAuthCancelled() {
198+        return authentication.authCancelled;
199+    }
200+
201+    public void cancel() {
202+        //TODO
203+        //Main.pleaseWaitDlg.currentAction.setText(tr("Aborting..."));
204+        cancel = true;
205+        if (activeConnection != null) {
206+            activeConnection.setConnectTimeout(100);
207+            activeConnection.setReadTimeout(100);
208+            try {
209+                Thread.sleep(100);
210+            } catch (InterruptedException ex) {}
211+            activeConnection.disconnect();
212+        }
213+    }
214+
215+    protected void addAuth(HttpURLConnection con) throws CharacterCodingException {
216+        CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
217+        String auth;
218+        try {
219+            synchronized (credentialsManager) {
220+                auth = credentialsManager.lookupUsername() + ":" + credentialsManager.lookupPassword();
221+            }
222+        } catch (CMException e) {
223+            auth = ":";
224+        }
225+        ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth));
226+        con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes));
227+    }
228+
229+    public static class PlainCredentialsManager implements CredentialsManager {
230+        public String lookupUsername() throws CMException {
231+            String username = Main.pref.get("osm-server.username", null);
232+            if (username == null) throw new CredentialsManager.NoContentException();
233+            return username;
234+        }
235+        public String lookupPassword() throws CMException {
236             String password = Main.pref.get("osm-server.password");
237-            if (passwordtried || username.equals("") || password.equals("")) {
238+            if (password == null) throw new CredentialsManager.NoContentException();
239+            return password;
240+        }
241+        public void storeUsername(String username) {
242+            Main.pref.put("osm-server.username", username);
243+        }
244+        public void storePassword(String password) {
245+            Main.pref.put("osm-server.password", password);
246+        }
247+        public PasswordAuthentication getPasswordAuthentication(OsmAuth caller) {
248+            String username, password;
249+            try {
250+                username = lookupUsername();
251+            } catch (CMException e) {
252+                username = "";
253+            }
254+            try {
255+                password = lookupPassword();
256+            } catch (CMException e) {
257+                password = "";
258+            }
259+            if (caller.passwordtried || username.equals("") || password.equals("")) {
260                 JPanel p = new JPanel(new GridBagLayout());
261                 if (!username.equals("") && !password.equals("")) {
262                     p.add(new JLabel(tr("Incorrect password or username.")), GBC.eop());
263@@ -92,56 +175,71 @@
264                         new String[] {"ok.png", "cancel.png"}).getValue();
265 
266                 if (choice != 1) {
267-                    authCancelled = true;
268+                    caller.authCancelled = true;
269                     return null;
270                 }
271                 username = usernameField.getText();
272                 password = String.valueOf(passwordField.getPassword());
273                 if (savePassword.isSelected()) {
274-                    Main.pref.put("osm-server.username", username);
275-                    Main.pref.put("osm-server.password", password);
276+                    storeUsername(username);
277+                    storePassword(password);
278                 }
279                 if (username.equals(""))
280                     return null;
281             }
282-            passwordtried = true;
283+            caller.passwordtried = true;
284             return new PasswordAuthentication(username, password.toCharArray());
285         }
286-    }
287+        public PreferenceAdditions newPreferenceAdditions() {
288+            return new PreferenceAdditions() {
289+                /**
290+                 * Editfield for the username to the OSM account.
291+                 */
292+                private JTextField osmDataUsername = new JTextField(20);
293+                /**
294+                 * Passwordfield for the userpassword of the REST API.
295+                 */
296+                private JPasswordField osmDataPassword = new JPasswordField(20);
297 
298-    /**
299-     * Must be called before each connection attemp to initialize the authentication.
300-     */
301-    protected final void initAuthentication() {
302-        authentication.authCancelled = false;
303-        authentication.passwordtried = false;
304-    }
305+                private String oldUsername = "";
306+                private String oldPassword = "";
307 
308-    /**
309-     * @return Whether the connection was cancelled.
310-     */
311-    protected final boolean isAuthCancelled() {
312-        return authentication.authCancelled;
313-    }
314-
315-    public void cancel() {
316-        //TODO
317-        //Main.pleaseWaitDlg.currentAction.setText(tr("Aborting..."));
318-        cancel = true;
319-        if (activeConnection != null) {
320-            activeConnection.setConnectTimeout(100);
321-            activeConnection.setReadTimeout(100);
322-            try {
323-                Thread.sleep(100);
324-            } catch (InterruptedException ex) {}
325-            activeConnection.disconnect();
326+                public void addPreferenceOptions(JPanel panel) {
327+                    try {
328+                        oldUsername = lookupUsername();
329+                    } catch (CMException e) {
330+                        oldUsername = "";
331+                    }
332+                    try {
333+                        oldPassword = lookupPassword();
334+                    } catch (CMException e) {
335+                        oldPassword = "";
336+                    }
337+                    osmDataUsername.setText(oldUsername);
338+                    osmDataPassword.setText(oldPassword);
339+                    osmDataUsername.setToolTipText(tr("Login name (e-mail) to the OSM account."));
340+                    osmDataPassword.setToolTipText(tr("Login password to the OSM account. Leave blank to not store any password."));
341+                    panel.add(new JLabel(tr("OSM username (e-mail)")), GBC.std());
342+                    panel.add(osmDataUsername, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
343+                    panel.add(new JLabel(tr("OSM password")), GBC.std());
344+                    panel.add(osmDataPassword, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,0));
345+                    JLabel warning = new JLabel(tr("<html>" +
346+                            "WARNING: The password is stored in plain text in the preferences file.<br>" +
347+                            "The password is transferred in plain text to the server, encoded in the URL.<br>" +
348+                            "<b>Do not use a valuable Password.</b></html>"));
349+                    warning.setFont(warning.getFont().deriveFont(Font.ITALIC));
350+                    panel.add(warning, GBC.eop().fill(GBC.HORIZONTAL));
351+                }
352+                public void preferencesChanged() {
353+                    String newUsername = osmDataUsername.getText();
354+                    String newPassword = String.valueOf(osmDataPassword.getPassword());
355+                    if (!oldUsername.equals(newUsername))
356+                        storeUsername(newUsername);
357+                    if (!oldPassword.equals(newPassword))
358+                        storePassword(newPassword);
359+                }
360+            };
361         }
362     }
363+}
364 
365-    protected void addAuth(HttpURLConnection con) throws CharacterCodingException {
366-        CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
367-        String auth = Main.pref.get("osm-server.username") + ":" + Main.pref.get("osm-server.password");
368-        ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth));
369-        con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes));
370-    }
371-}