diff options
author | Tor Norbye <tnorbye@google.com> | 2014-08-20 17:01:23 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2014-08-20 17:01:23 -0700 |
commit | 1aa2e09bdbd413eacb677e9fa4b50630530d0656 (patch) | |
tree | 2f4cc6d69645bd460aa253fdecb606d764fbd25d /platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java | |
parent | 02cf98d65c798d368fcec43ed64a001d513bdd4f (diff) | |
download | idea-1aa2e09bdbd413eacb677e9fa4b50630530d0656.tar.gz |
Snapshot idea/138.1696 from git://git.jetbrains.org/idea/community.git
Change-Id: I50c97b83a815ce635e49a38380ba5b8765e4b16a
Diffstat (limited to 'platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java')
-rw-r--r-- | platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java | 172 |
1 files changed, 97 insertions, 75 deletions
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(); } } |