summaryrefslogtreecommitdiff
path: root/platform/platform-impl/src/com/intellij/ide/passwordSafe
diff options
context:
space:
mode:
Diffstat (limited to 'platform/platform-impl/src/com/intellij/ide/passwordSafe')
-rw-r--r--platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordSafeException.java29
-rw-r--r--platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java1
-rw-r--r--platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java12
-rw-r--r--platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java172
-rw-r--r--platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java1
-rw-r--r--platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java6
6 files changed, 129 insertions, 92 deletions
diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordSafeException.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordSafeException.java
index 00b529933f68..20d7d5188efb 100644
--- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordSafeException.java
+++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/PasswordSafeException.java
@@ -19,22 +19,29 @@ package com.intellij.ide.passwordSafe;
* The exception that is thrown when password safe is not available (unable to ask for master password)
*/
public class PasswordSafeException extends Exception {
- /**
- * The constructor
- *
- * @param message the message
- * @param cause the cause
- */
+
+ private static final long MIN_INTERVAL = 1000L;
+
+ private long myTimeMillis = System.currentTimeMillis();
+
public PasswordSafeException(String message, Throwable cause) {
super(message, cause);
}
- /**
- * The constructor
- *
- * @param message the message
- */
public PasswordSafeException(String message) {
super(message);
}
+
+ public long getTimeMillis() {
+ return myTimeMillis;
+ }
+
+ public boolean justHappened() {
+ long timeMillis = System.currentTimeMillis();
+ if (timeMillis - myTimeMillis < MIN_INTERVAL) {
+ myTimeMillis = timeMillis;
+ return true;
+ }
+ return false;
+ }
}
diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java
index a0b001824d90..8bfa859d5ceb 100644
--- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java
+++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/BasePasswordSafeProvider.java
@@ -43,6 +43,7 @@ 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.
*/
+ @NotNull
protected abstract byte[] key(@Nullable Project project, @NotNull Class requestor) throws PasswordSafeException;
@Nullable
diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java
index 01fef01cecb1..7bbb8bc80789 100644
--- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java
+++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/EncryptionUtil.java
@@ -15,6 +15,8 @@
*/
package com.intellij.ide.passwordSafe.impl.providers;
+import org.jetbrains.annotations.NotNull;
+
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
@@ -115,7 +117,7 @@ public class EncryptionUtil {
* @param rawKey the raw key to encrypt
* @return the encrypted key
*/
- public static byte[] encryptKey(byte[] password, byte[] rawKey) {
+ public static byte[] encryptKey(@NotNull byte[] password, byte[] rawKey) {
try {
Cipher c = Cipher.getInstance(ENCRYPT_KEY_ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(password, SECRET_KEY_ALGORITHM), CBC_SALT_KEY);
@@ -130,12 +132,12 @@ public class EncryptionUtil {
* Create encrypted db key
*
* @param password the password to protect the key
- * @param requester the requester for the key
- * @param key the key within requester
+ * @param requestor the requestor for the key
+ * @param key the key within requestor
* @return the key to use in the database
*/
- public static byte[] dbKey(byte[] password, Class requester, String key) {
- return encryptKey(password, rawKey(requester, key));
+ public static byte[] dbKey(@NotNull byte[] password, Class requestor, String key) {
+ return encryptKey(password, rawKey(requestor, key));
}
diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java
index 840159270a8d..6b0b9814698b 100644
--- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java
+++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java
@@ -22,12 +22,14 @@ 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.ide.passwordSafe.impl.providers.masterKey.windows.WindowsCryptUtils;
-import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
@@ -36,7 +38,6 @@ import org.jetbrains.annotations.Nullable;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
/**
* The password safe that stores information in configuration file encrypted by master password
@@ -44,13 +45,11 @@ import java.util.concurrent.atomic.AtomicReference;
public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
private static final String TEST_PASSWORD_KEY = "TEST_PASSWORD:";
private static final String TEST_PASSWORD_VALUE = "test password";
- final PasswordDatabase database;
- /**
- * The key to use to encrypt data
- */
- private transient final PasswordSafeTimed<AtomicReference<byte[]>> key = new PasswordSafeTimed<AtomicReference<byte[]>>() {
- protected AtomicReference<byte[]> compute() {
- return new AtomicReference<byte[]>();
+
+ private final PasswordDatabase myDatabase;
+ private transient final PasswordSafeTimed<Ref<Object>> myKey = new PasswordSafeTimed<Ref<Object>>() {
+ protected Ref<Object> compute() {
+ return Ref.create();
}
@Override
@@ -60,11 +59,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
};
public MasterKeyPasswordSafe(PasswordDatabase database) {
- this.database = database;
- }
-
- protected boolean isTestMode() {
- return false;
+ this.myDatabase = database;
}
/**
@@ -74,15 +69,15 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
* @param encrypt if the password should be encrypted an stored is master database
*/
void resetMasterPassword(String password, boolean encrypt) {
- key.get().set(EncryptionUtil.genPasswordKey(password));
- database.clear();
+ myKey.get().set(EncryptionUtil.genPasswordKey(password));
+ myDatabase.clear();
try {
storePassword(null, MasterKeyPasswordSafe.class, testKey(password), TEST_PASSWORD_VALUE);
if (encrypt) {
- database.setPasswordInfo(encryptPassword(password));
+ myDatabase.setPasswordInfo(encryptPassword(password));
}
else {
- database.setPasswordInfo(ArrayUtil.EMPTY_BYTE_ARRAY);
+ myDatabase.setPasswordInfo(ArrayUtil.EMPTY_BYTE_ARRAY);
}
}
catch (PasswordSafeException e) {
@@ -97,8 +92,8 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
* @return true, if password is a correct one
*/
boolean setMasterPassword(String password) {
- byte[] savedKey = key.get().get();
- key.get().set(EncryptionUtil.genPasswordKey(password));
+ Object savedKey = myKey.get().get();
+ myKey.get().set(EncryptionUtil.genPasswordKey(password));
String rc;
try {
rc = getPassword(null, MasterKeyPasswordSafe.class, testKey(password));
@@ -107,7 +102,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
throw new IllegalStateException("There should be no problem with password at this point", e);
}
if (!TEST_PASSWORD_VALUE.equals(rc)) {
- key.get().set(savedKey);
+ myKey.get().set(savedKey);
return false;
}
else {
@@ -127,11 +122,11 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
if (!setMasterPassword(oldPassword)) {
return false;
}
- byte[] oldKey = key.get().get(); // set right in the previous call
+ byte[] oldKey = (byte[])myKey.get().get(); // set right in the previous call
byte[] newKey = EncryptionUtil.genPasswordKey(newPassword);
ByteArrayWrapper testKey = new ByteArrayWrapper(EncryptionUtil.dbKey(oldKey, MasterKeyPasswordSafe.class, testKey(oldPassword)));
HashMap<ByteArrayWrapper, byte[]> oldDb = new HashMap<ByteArrayWrapper, byte[]>();
- database.copyTo(oldDb);
+ myDatabase.copyTo(oldDb);
HashMap<ByteArrayWrapper, byte[]> newDb = new HashMap<ByteArrayWrapper, byte[]>();
for (Map.Entry<ByteArrayWrapper, byte[]> e : oldDb.entrySet()) {
if (testKey.equals(e.getKey())) {
@@ -141,9 +136,9 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
String decryptedText = EncryptionUtil.decryptText(oldKey, e.getValue());
newDb.put(new ByteArrayWrapper(EncryptionUtil.encryptKey(newKey, decryptedKey)), EncryptionUtil.encryptText(newKey, decryptedText));
}
- synchronized (database.getDbLock()) {
+ synchronized (myDatabase.getDbLock()) {
resetMasterPassword(newPassword, encrypt);
- database.putAll(newDb);
+ myDatabase.putAll(newDb);
}
return true;
}
@@ -153,65 +148,92 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
return TEST_PASSWORD_KEY + password;
}
+ @NotNull
@Override
protected byte[] key(@Nullable final Project project, @NotNull final Class requestor) throws PasswordSafeException {
- Application application = ApplicationManager.getApplication();
- if (!isTestMode() && application.isHeadlessEnvironment()) {
+ Object key = myKey.get().get();
+ if (key instanceof byte[]) return (byte[])key;
+ if (key instanceof PasswordSafeException && ((PasswordSafeException)key).justHappened()) throw (PasswordSafeException)key;
+
+ if (isPasswordEncrypted()) {
+ try {
+ setMasterPassword(decryptPassword(myDatabase.getPasswordInfo()));
+ key = myKey.get().get();
+ if (key instanceof byte[]) return (byte[])key;
+ }
+ catch (PasswordSafeException e) {
+ // ignore exception and ask password
+ }
+ }
+
+ if (ApplicationManager.getApplication().isHeadlessEnvironment()) {
throw new MasterPasswordUnavailableException("The provider is not available in headless environment");
}
- final Ref<byte[]> result = Ref.create(key.get().get());
- if (result.isNull()) {
- if (isPasswordEncrypted()) {
+
+ if (myDatabase.isEmpty()) {
+ if (!MasterPasswordDialog.resetMasterPasswordDialog(project, this, requestor).showAndGet()) {
+ throw new MasterPasswordUnavailableException("Master password is required to store passwords in the database.");
+ }
+ }
+
+ key = invokeAndWait(new ThrowableComputable<Object, PasswordSafeException>() {
+ @Override
+ public Object compute() throws PasswordSafeException {
+ Object key = myKey.get().get();
+ if (key instanceof byte[] || key instanceof PasswordSafeException && ((PasswordSafeException)key).justHappened()) {
+ return key;
+ }
try {
- setMasterPassword(decryptPassword(database.getPasswordInfo()));
- result.set(key.get().get());
+ MasterPasswordDialog.askPassword(project, MasterKeyPasswordSafe.this, requestor);
}
catch (PasswordSafeException e) {
- // ignore exception and ask password
+ myKey.get().set(e);
+ throw e;
}
+ return myKey.get().get();
+ }
+ }, project == null ? Condition.FALSE : project.getDisposed());
+ if (key instanceof byte[]) return (byte[])key;
+ if (key instanceof PasswordSafeException) throw (PasswordSafeException)key;
+
+ throw new AssertionError();
+ }
+
+ private static final Object ourEDTLock = new Object();
+ public <T, E extends Throwable> T invokeAndWait(@NotNull final ThrowableComputable<T, E> computable, @NotNull final Condition expired) throws E {
+ if (ApplicationManager.getApplication().isDispatchThread()) {
+ return computable.compute();
+ }
+ final Ref<Throwable> exRef = Ref.create();
+ final Ref<T> ref = Ref.create();
+ synchronized (ourEDTLock) {
+ if (expired.value(null)) {
+ throw new ProcessCanceledException();
}
- if (result.isNull()) {
- final Ref<PasswordSafeException> ex = new Ref<PasswordSafeException>();
- application.invokeAndWait(new Runnable() {
- public void run() {
- result.set(key.get().get());
- if (result.isNull()) {
- try {
- if (isTestMode()) {
- throw new MasterPasswordUnavailableException("Master password must be specified in test mode.");
- }
- if (database.isEmpty()) {
- if (!MasterPasswordDialog.resetMasterPasswordDialog(project, MasterKeyPasswordSafe.this, requestor).showAndGet()) {
- throw new MasterPasswordUnavailableException("Master password is required to store passwords in the database.");
- }
- }
- else {
- MasterPasswordDialog.askPassword(project, MasterKeyPasswordSafe.this, requestor);
- }
- result.set(key.get().get());
- }
- catch (PasswordSafeException e) {
- ex.set(e);
- }
- catch (Exception e) {
- //noinspection ThrowableInstanceNeverThrown
- ex.set(new MasterPasswordUnavailableException("The problem with retrieving the password", e));
- }
- }
+ ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if (expired.value(null)) {
+ exRef.set(new ProcessCanceledException());
+ return;
+ }
+
+ try {
+ ref.set(computable.compute());
+ }
+ catch (Throwable e) {
+ exRef.set(e);
}
- }, ModalityState.any());
- //noinspection ThrowableResultOfMethodCallIgnored
- if (ex.get() != null) {
- throw ex.get();
}
- }
+ }, ModalityState.any());
}
- return result.get();
+ if (!exRef.isNull()) throw (E)exRef.get();
+ return ref.get();
}
@Override
public String getPassword(@Nullable Project project, @NotNull Class requestor, String key) throws PasswordSafeException {
- if (database.isEmpty()) {
+ if (myDatabase.isEmpty()) {
return null;
}
return super.getPassword(project, requestor, key);
@@ -219,7 +241,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
@Override
public void removePassword(@Nullable Project project, @NotNull Class requester, String key) throws PasswordSafeException {
- if (database.isEmpty()) {
+ if (myDatabase.isEmpty()) {
return;
}
super.removePassword(project, requester, key);
@@ -227,17 +249,17 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
@Override
protected byte[] getEncryptedPassword(byte[] key) {
- return database.get(key);
+ return myDatabase.get(key);
}
@Override
protected void removeEncryptedPassword(byte[] key) {
- database.remove(key);
+ myDatabase.remove(key);
}
@Override
protected void storeEncryptedPassword(byte[] key, byte[] encryptedPassword) {
- database.put(key, encryptedPassword);
+ myDatabase.put(key, encryptedPassword);
}
@Override
@@ -303,11 +325,11 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider {
public boolean isPasswordEncrypted() {
if (!isOsProtectedPasswordSupported()) return false;
- byte[] i = database.getPasswordInfo();
- return i != null && i.length > 0;
+ byte[] info = myDatabase.getPasswordInfo();
+ return info != null && info.length > 0;
}
public boolean isEmpty() {
- return database.isEmpty();
+ return myDatabase.isEmpty();
}
}
diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java
index 4989789b4ec0..5057b0ee48d6 100644
--- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java
+++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/memory/MemoryPasswordSafe.java
@@ -57,6 +57,7 @@ public class MemoryPasswordSafe extends BasePasswordSafeProvider {
return Registry.intValue("passwordSafe.memorySafe.ttl");
}
+ @NotNull
@Override
protected byte[] key(Project project, @NotNull Class requestor) {
if (key.get() == null) {
diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java
index 6ea43344bda4..b31599816435 100644
--- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java
+++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java
@@ -18,6 +18,7 @@ package com.intellij.ide.passwordSafe.ui;
import com.intellij.ide.passwordSafe.config.PasswordSafeSettings;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ui.DialogUtil;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
@@ -49,7 +50,10 @@ public class PasswordPromptComponent {
setTargetProviderType(type);
setUserInputVisible(showUserName);
if (passwordPrompt != null) myPasswordLabel.setText(passwordPrompt);
- if (rememberPrompt != null) myRememberCheckBox.setText(rememberPrompt);
+ if (rememberPrompt != null) {
+ myRememberCheckBox.setText(rememberPrompt);
+ DialogUtil.registerMnemonic(myRememberCheckBox);
+ }
}
public JComponent getComponent() {