[Mercurial] IDEA-56180: remembering visited URLs, logins and passwords. Also fixed...
authorKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Mon, 5 Jul 2010 12:03:26 +0000 (16:03 +0400)
committerKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Mon, 5 Jul 2010 12:03:26 +0000 (16:03 +0400)
plugins/hg4idea/hg4idea.iml
plugins/hg4idea/src/org/zmlx/hg4idea/HgGlobalSettings.java
plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java
plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java
plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java

index 169abf6dc8a0edcaea860d76b97009bfce785c1f..f7efe10c0e5fbeae7429e4b25f66bd795b7efb5c 100644 (file)
@@ -15,6 +15,7 @@
     <orderEntry type="module" module-name="testFramework" scope="TEST" />
     <orderEntry type="module" module-name="platform-api" />
     <orderEntry type="module" module-name="vcs-impl" />
+    <orderEntry type="module" module-name="platform-impl" />
   </component>
 </module>
 
index 897a027919a595abc0a1da73a2cc7418c58e0bf1..63b7c999014bcb22cf5536d77cef0ef71f8560ce 100644 (file)
@@ -15,6 +15,12 @@ package org.zmlx.hg4idea;
 import com.intellij.openapi.components.PersistentStateComponent;
 import com.intellij.openapi.components.State;
 import com.intellij.openapi.components.Storage;
+import com.intellij.util.containers.HashMap;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 
 @State(
   name = "hg4idea.settings",
@@ -27,6 +33,32 @@ public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettin
 
   private String hgExecutable = HG;
 
+  // visited URL -> list of logins for this URL. Passwords are remembered in the PasswordSafe.
+  private Map<String, List<String>> myRememberedUrls = new HashMap<String, List<String>>();
+
+  /**
+   * Returns the rememebered urls which were accessed while working in the plugin.
+   * @return key is a String representation of a URL, value is the list (probably empty) of logins remembered for this URL.
+   */
+  @NotNull
+  public Map<String, List<String>> getRememberedUrls() {
+    return myRememberedUrls;
+  }
+
+  /**
+   * Adds the information about visited URL.
+   * @param stringUrl String representation of the URL.
+   * @param username  Login used to access the URL.
+   */
+  public void addRememberedUrl(@NotNull String stringUrl, @NotNull String username) {
+    List<String> list = myRememberedUrls.get(stringUrl);
+    if (list == null) {
+      list = new LinkedList<String>();
+      myRememberedUrls.put(stringUrl, list);
+    }
+    list.add(username);
+  }
+
   public static String getDefaultExecutable() {
     return HG;
   }
index 4d6c5f29180eed1a9ae243e2b6aa5e57ed086d6b..52e92e0ef1098ed42fd9a6341914c116637d30cd 100644 (file)
@@ -341,4 +341,7 @@ public class HgVcs extends AbstractVcs {
     return globalSettings.getHgExecutable();
   }
 
+  public HgGlobalSettings getGlobalSettings() {
+    return globalSettings;
+  }
 }
index 019d02cf257284e3d1b471a6a9679f094872b380..bf52bf7bedc1d3d97f39c5546cee3505c8d41aa1 100644 (file)
 // limitations under the License.
 package org.zmlx.hg4idea.command;
 
+import com.intellij.ide.passwordSafe.PasswordSafe;
+import com.intellij.ide.passwordSafe.PasswordSafeException;
+import com.intellij.idea.LoggerFactory;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.vcsUtil.VcsUtil;
+import org.apache.commons.lang.StringUtils;
 import org.jetbrains.annotations.Nullable;
+import org.zmlx.hg4idea.HgGlobalSettings;
+import org.zmlx.hg4idea.HgVcs;
 import org.zmlx.hg4idea.ui.HgUsernamePasswordDialog;
 
+import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.List;
+import java.util.*;
 
 /**
  * Base class for any command interacting with a remote repository and which needs authentication.
  */
 class HgCommandAuthenticator {
 
+  private static final Logger LOG = Logger.getInstance(HgCommandAuthenticator.class.getName());
+
   @Nullable
   protected HgCommandResult executeCommandAndAuthenticateIfNecessary(Project project, VirtualFile localRepository, String remoteRepository, String command, List<String> arguments) {
     HgCommandService service = HgCommandService.getInstance(project);
@@ -47,8 +57,20 @@ class HgCommandAuthenticator {
             hgUrl.setUsername( runnable.getUserName() );
             hgUrl.setPassword(String.valueOf( runnable.getPassword() ));
 
-            arguments.set(arguments.size() - 1, hgUrl.asString());
+            arguments.set(0, hgUrl.asString());
             result = service.execute(localRepository, command, arguments);
+
+            if (result != null && result.getExitValue() == 0) {
+              final String key = keyForUrlAndLogin(runnable.getURL(), runnable.getUserName());
+              try {
+                PasswordSafe.getInstance().storePassword(project, HgCommandAuthenticator.class, key, runnable.getPassword());
+                HgVcs.getInstance(project).getGlobalSettings().addRememberedUrl(runnable.getURL(), runnable.getUserName());
+              }
+              catch (PasswordSafeException e) {
+                LOG.error("Couldn't store the password for key [" + key + "]", e);
+              }
+            }
+
           }
         }
       } catch (URISyntaxException e) {
@@ -62,9 +84,11 @@ class HgCommandAuthenticator {
 
     private final HgUrl hgUrl;
     private String userName;
-    private char[] password;
+    private String myPassword;
     private Project project;
     private boolean ok = false;
+    private static final Logger LOG = Logger.getInstance(GetPasswordRunnable.class.getName());
+    private String myURL;
 
     public GetPasswordRunnable(Project project, HgUrl hgUrl) {
       this.hgUrl = hgUrl;
@@ -72,13 +96,50 @@ class HgCommandAuthenticator {
     }
 
     public void run() {
-      final HgUsernamePasswordDialog dialog = new HgUsernamePasswordDialog(project, hgUrl.getUsername());
-      dialog.show();
 
+      // get the string representation of the url
+      @Nullable String stringUrl = null;
+      try {
+        stringUrl = hgUrl.asString();
+      }
+      catch (URISyntaxException e) {
+        LOG.warn("Couldn't parse hgUrl: [" + hgUrl + "]", e);
+      }
+
+      // find if we've already been here
+      final HgGlobalSettings hgGlobalSettings = HgVcs.getInstance(project).getGlobalSettings();
+      final Map<String, List<String>> urls = hgGlobalSettings.getRememberedUrls();
+      @Nullable List<String> rememberedLoginsForUrl = urls.get(stringUrl);
+
+      String login = hgUrl.getUsername();
+      if (StringUtils.isBlank(login)) {
+        // find the last used login
+        if (rememberedLoginsForUrl != null && !rememberedLoginsForUrl.isEmpty()) {
+          login = rememberedLoginsForUrl.get(0);
+        }
+      }
+
+      String password = hgUrl.getPassword();
+      if (StringUtils.isBlank(password) && stringUrl != null) {
+        // if we've logged in with this login, search for password
+        final String key = keyForUrlAndLogin(stringUrl, login);
+        try {
+          password = PasswordSafe.getInstance().getPassword(project, HgCommandAuthenticator.class, key);
+        } catch (PasswordSafeException e) {
+          LOG.error("Couldn't get password for key [" + key + "]", e);
+        }
+      }
+      
+      final HgUsernamePasswordDialog dialog = new HgUsernamePasswordDialog(project, login, password);
+      dialog.show();
       if (dialog.isOK()) {
         userName = dialog.getUsername();
-        password = dialog.getPassword();
+        myPassword = dialog.getPassword();
         ok = true;
+
+        if (dialog.isRememberPassword() && stringUrl != null) {
+          myURL = stringUrl;
+        }
       }
     }
 
@@ -86,13 +147,21 @@ class HgCommandAuthenticator {
       return userName;
     }
 
-    public char[] getPassword() {
-      return password.clone();
+    public String getPassword() {
+      return myPassword;
     }
 
     public boolean isOk() {
       return ok;
     }
+
+    public String getURL() {
+      return myURL;
+    }
+  }
+
+  private static String keyForUrlAndLogin(String stringUrl, String login) {
+    return stringUrl + login;
   }
 
 }
index f7bee725727de60f699197d1a91be1083cacf77c..8d156eff623d39c26ebdb62155d47fc0bad355a2 100644 (file)
@@ -15,6 +15,7 @@ package org.zmlx.hg4idea.ui;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.util.net.AuthenticationPanel;
+import org.apache.commons.lang.StringUtils;
 import org.zmlx.hg4idea.HgVcsMessages;
 
 import javax.swing.*;
@@ -22,10 +23,10 @@ import javax.swing.*;
 public class HgUsernamePasswordDialog extends DialogWrapper {
   private AuthenticationPanel authPanel;
 
-  public HgUsernamePasswordDialog(Project project, String login) {
+  public HgUsernamePasswordDialog(Project project, String login, String password) {
     super(project, false);
     setTitle(HgVcsMessages.message("hgidea.dialog.login.password.required"));
-    authPanel = new AuthenticationPanel(null, login, "", false);
+    authPanel = new AuthenticationPanel(null, login, password, !StringUtils.isBlank(password));
     init();
   }
 
@@ -42,8 +43,12 @@ public class HgUsernamePasswordDialog extends DialogWrapper {
     return authPanel.getLogin();
   }
 
-  public char[] getPassword() {
-    return authPanel.getPassword().toCharArray();
+  public String getPassword() {
+    return authPanel.getPassword();
   }
 
+  public boolean isRememberPassword() {
+    return authPanel.isRememberPassword();
+  }
+  
 }
\ No newline at end of file