diff --git a/plugins/hg4idea/hg4idea.iml b/plugins/hg4idea/hg4idea.iml index 169abf6dc8a0..f7efe10c0e5f 100644 --- a/plugins/hg4idea/hg4idea.iml +++ b/plugins/hg4idea/hg4idea.iml @@ -15,6 +15,7 @@ + diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgGlobalSettings.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgGlobalSettings.java index 897a027919a5..63b7c999014b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgGlobalSettings.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgGlobalSettings.java @@ -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 list of logins for this URL. Passwords are remembered in the PasswordSafe. + private Map> myRememberedUrls = new HashMap>(); + + /** + * 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> 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 list = myRememberedUrls.get(stringUrl); + if (list == null) { + list = new LinkedList(); + myRememberedUrls.put(stringUrl, list); + } + list.add(username); + } + public static String getDefaultExecutable() { return HG; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java index 4d6c5f29180e..52e92e0ef109 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java @@ -341,4 +341,7 @@ public class HgVcs extends AbstractVcs { return globalSettings.getHgExecutable(); } + public HgGlobalSettings getGlobalSettings() { + return globalSettings; + } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java index 019d02cf2572..bf52bf7bedc1 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java @@ -12,22 +12,32 @@ // 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 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> urls = hgGlobalSettings.getRememberedUrls(); + @Nullable List 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; } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java index f7bee725727d..8d156eff623d 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java @@ -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