IDEA-127343 Pass ModalityState to PasswordSafe.getPassword

Since PasswordSafe.getPassword may request for the master password,
and since this request can be made from any thread, it needs the proper
ModalityState to execute invokeAndWait(), otherwise the master password
dialog won't appear above some modal progresses which requested the
password, just like in IDEA-127343.

=>
* Deprecate the existing getPassword() and create new one accepting
  ModalityState.
* Pass the ModalityState down to the MasterKeyPasswordSafe#key which
  actually shows the dialog.
* Some providers (like Nil or Memory) can safely ignore the modality
  state, since they don't show any dialogs.
This commit is contained in:
Kirill Likhodedov
2014-07-16 17:53:58 +04:00
parent 7eb4791539
commit 13963225d1
11 changed files with 66 additions and 27 deletions
@@ -41,9 +41,16 @@ public interface PasswordStorage {
* @return the stored password or null if the password record was not found or was removed
* @throws PasswordSafeException if password safe cannot be accessed
* @throws IllegalStateException if the method is called from the read action.
*
* @deprecated To remove in IDEA 15. Use {@link #getPassword(Project, Class, String, ModalityState)}
*/
@Deprecated
@Nullable
String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException;
@Nullable
String getPassword(@Nullable Project project, @NotNull Class requestor, String key,
@Nullable ModalityState state) throws PasswordSafeException;
/**
* Remove password stored in a password safe
*
@@ -22,6 +22,7 @@ import com.intellij.ide.passwordSafe.impl.providers.masterKey.MasterKeyPasswordS
import com.intellij.ide.passwordSafe.impl.providers.masterKey.PasswordDatabase;
import com.intellij.ide.passwordSafe.impl.providers.memory.MemoryPasswordSafe;
import com.intellij.ide.passwordSafe.impl.providers.nil.NilProvider;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
@@ -71,10 +72,17 @@ public class PasswordSafeImpl extends PasswordSafe {
@Nullable
public String getPassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException {
return getPassword(project, requester, key, null);
}
@Nullable
@Override
public String getPassword(@Nullable Project project, @NotNull Class requester, String key,
@Nullable ModalityState modalityState) throws PasswordSafeException {
if (mySettings.getProviderType().equals(PasswordSafeSettings.ProviderType.MASTER_PASSWORD)) {
String password = getMemoryProvider().getPassword(project, requester, key);
String password = getMemoryProvider().getPassword(project, requester, key, modalityState);
if (password == null) {
password = provider().getPassword(project, requester, key);
password = provider().getPassword(project, requester, key, modalityState);
if (password != null) {
// cache the password in memory as well for easier access during the session
getMemoryProvider().storePassword(project, requester, key, password);
@@ -82,7 +90,7 @@ public class PasswordSafeImpl extends PasswordSafe {
}
return password;
}
return provider().getPassword(project, requester, key);
return provider().getPassword(project, requester, key, modalityState);
}
public void removePassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException {
@@ -15,7 +15,11 @@
*/
package com.intellij.ide.passwordSafe.impl;
import com.intellij.ide.passwordSafe.PasswordSafeException;
import com.intellij.ide.passwordSafe.PasswordStorage;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The provider for password safe component
@@ -34,4 +38,11 @@ public abstract class PasswordSafeProvider implements PasswordStorage {
* @return the name of provider
*/
public abstract String getName();
@Nullable
@Override
public String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException {
return getPassword(project, requestor, key, null);
}
}
@@ -43,13 +43,15 @@ public abstract class BasePasswordSafeProvider extends PasswordSafeProvider {
* @throws PasswordSafeException in case of problems with access to the password database.
* @throws IllegalStateException if the method is called from the read action.
*/
protected abstract byte[] key(@Nullable Project project, @NotNull Class requestor) throws PasswordSafeException;
protected abstract byte[] key(@Nullable Project project, @NotNull Class requestor,
@Nullable ModalityState modalityState) throws PasswordSafeException;
@Nullable
public String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException {
byte[] k = dbKey(project, requestor, key);
public String getPassword(@Nullable Project project, @NotNull Class requestor, String key,
@Nullable ModalityState modalityState) throws PasswordSafeException {
byte[] k = dbKey(project, requestor, key, modalityState);
byte[] ct = getEncryptedPassword(k);
return ct == null ? null : EncryptionUtil.decryptText(key(project, requestor), ct);
return ct == null ? null : EncryptionUtil.decryptText(key(project, requestor, modalityState), ct);
}
/**
@@ -66,14 +68,16 @@ public abstract class BasePasswordSafeProvider extends PasswordSafeProvider {
* @param project
* @param requestor the requestor class
* @param key the key to use
* @param modalityState
* @return the key to use for map
*/
private byte[] dbKey(@Nullable Project project, Class requestor, String key) throws PasswordSafeException {
return EncryptionUtil.dbKey(key(project, requestor), requestor, key);
private byte[] dbKey(@Nullable Project project, @NotNull Class requestor, String key,
@Nullable ModalityState modalityState) throws PasswordSafeException {
return EncryptionUtil.dbKey(key(project, requestor, modalityState), requestor, key);
}
public void removePassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException {
byte[] k = dbKey(project, requester, key);
byte[] k = dbKey(project, requester, key, null);
removeEncryptedPassword(k);
}
@@ -85,8 +89,8 @@ public abstract class BasePasswordSafeProvider extends PasswordSafeProvider {
protected abstract void removeEncryptedPassword(byte[] key);
public void storePassword(@Nullable Project project, @NotNull Class requestor, String key, String value) throws PasswordSafeException {
byte[] k = dbKey(project, requestor, key);
byte[] ct = EncryptionUtil.encryptText(key(project, requestor), value);
byte[] k = dbKey(project, requestor, key, null);
byte[] ct = EncryptionUtil.encryptText(key(project, requestor, null), value);
storeEncryptedPassword(k, ct);
}
@@ -154,7 +154,8 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
}
@Override
protected byte[] key(@Nullable final Project project, @NotNull final Class requestor) throws PasswordSafeException {
protected byte[] key(@Nullable final Project project, @NotNull final Class requestor,
@Nullable ModalityState modalityState) throws PasswordSafeException {
Application application = ApplicationManager.getApplication();
if (!isTestMode() && application.isHeadlessEnvironment()) {
throw new MasterPasswordUnavailableException("The provider is not available in headless environment");
@@ -199,7 +200,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
}
}
}
}, ModalityState.defaultModalityState());
}, modalityState == null ? ModalityState.defaultModalityState() : modalityState);
//noinspection ThrowableResultOfMethodCallIgnored
if (ex.get() != null) {
throw ex.get();
@@ -210,11 +211,12 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
}
@Override
public String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException {
public String getPassword(@Nullable Project project, @NotNull Class requestor, String key,
@Nullable ModalityState modalityState) throws PasswordSafeException {
if (database.isEmpty()) {
return null;
}
return super.getPassword(project, requestor, key);
return super.getPassword(project, requestor, key, modalityState);
}
@Override
@@ -19,10 +19,12 @@ import com.intellij.ide.passwordSafe.impl.PasswordSafeTimed;
import com.intellij.ide.passwordSafe.impl.providers.BasePasswordSafeProvider;
import com.intellij.ide.passwordSafe.impl.providers.ByteArrayWrapper;
import com.intellij.ide.passwordSafe.impl.providers.EncryptionUtil;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.security.SecureRandom;
import java.util.Collections;
@@ -63,7 +65,7 @@ public class MemoryPasswordSafe extends BasePasswordSafeProvider {
* @return the secret key used by provider
*/
@Override
protected byte[] key(Project project, @NotNull Class requestor) {
protected byte[] key(Project project, @NotNull Class requestor, @Nullable ModalityState modalityState) {
if (key.get() == null) {
byte[] rnd = new byte[EncryptionUtil.SECRET_KEY_SIZE_BYTES * 16];
new SecureRandom().nextBytes(rnd);
@@ -17,6 +17,7 @@ package com.intellij.ide.passwordSafe.impl.providers.nil;
import com.intellij.ide.passwordSafe.PasswordSafeException;
import com.intellij.ide.passwordSafe.impl.PasswordSafeProvider;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,7 +42,9 @@ public final class NilProvider extends PasswordSafeProvider {
return "Do not Store";
}
public String getPassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException {
@Nullable
public String getPassword(@Nullable Project project, @NotNull Class requester, String key,
@Nullable ModalityState modalityState) throws PasswordSafeException {
// nothing is stored
return null;
}
@@ -176,7 +176,7 @@ public class PasswordSafePromptDialog extends DialogWrapper {
ps.removePassword(project, requestor, key);
}
else {
String pw = ps.getPassword(project, requestor, key);
String pw = ps.getPassword(project, requestor, key, modalityState);
if (pw != null) {
return pw;
}
@@ -87,7 +87,7 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
return "";
}
url = adjustUrl(url);
Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url);
Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url, myModalityState);
if (authData != null && authData.second.getPassword() != null) {
String password = authData.second.getPassword();
myDataProvider = authData.first;
@@ -114,7 +114,7 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
@NotNull
public String askUsername(@NotNull String url) {
url = adjustUrl(url);
Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url);
Pair<GitHttpAuthDataProvider, AuthData> authData = findBestAuthData(url, myModalityState);
String login = null;
String password = null;
if (authData != null) {
@@ -223,10 +223,10 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
// return the first that knows username + password; otherwise return the first that knows just the username
@Nullable
private Pair<GitHttpAuthDataProvider, AuthData> findBestAuthData(@NotNull String url) {
private Pair<GitHttpAuthDataProvider, AuthData> findBestAuthData(@NotNull String url, @Nullable ModalityState modalityState) {
Pair<GitHttpAuthDataProvider, AuthData> candidate = null;
for (GitHttpAuthDataProvider provider : getProviders()) {
AuthData data = provider.getAuthData(url);
AuthData data = provider.getAuthData(url, modalityState);
if (data != null) {
Pair<GitHttpAuthDataProvider, AuthData> pair = Pair.create(provider, data);
if (data.getPassword() != null) {
@@ -268,12 +268,12 @@ class GitHttpGuiAuthenticator implements GitHttpAuthenticator {
@Nullable
@Override
public AuthData getAuthData(@NotNull String url) {
public AuthData getAuthData(@NotNull String url, @Nullable ModalityState modalityState) {
String userName = getUsername(url);
String key = makeKey(url, userName);
final PasswordSafe passwordSafe = PasswordSafe.getInstance();
try {
String password = passwordSafe.getPassword(myProject, PASS_REQUESTER, key);
String password = passwordSafe.getPassword(myProject, PASS_REQUESTER, key, modalityState);
return new AuthData(StringUtil.notNullize(userName), password);
}
catch (PasswordSafeException e) {
@@ -15,6 +15,7 @@
*/
package git4idea.remote;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.util.AuthData;
import org.jetbrains.annotations.NotNull;
@@ -31,7 +32,7 @@ public interface GitHttpAuthDataProvider {
ExtensionPointName<GitHttpAuthDataProvider> EP_NAME = ExtensionPointName.create("Git4Idea.GitHttpAuthDataProvider");
@Nullable
AuthData getAuthData(@NotNull String url);
AuthData getAuthData(@NotNull String url, @Nullable ModalityState modalityState);
void forgetPassword(@NotNull String url);
@@ -15,6 +15,7 @@
*/
package org.jetbrains.plugins.github.extensions;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.AuthData;
import git4idea.remote.GitHttpAuthDataProvider;
@@ -31,7 +32,7 @@ public class GithubHttpAuthDataProvider implements GitHttpAuthDataProvider {
@Nullable
@Override
public AuthData getAuthData(@NotNull String url) {
public AuthData getAuthData(@NotNull String url, @Nullable ModalityState modalityState) {
if (!GithubUrlUtil.isGithubUrl(url)) {
return null;
}