IDEA-163385 Cannot erase password on macOs
authorVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Wed, 2 Nov 2016 13:13:01 +0000 (14:13 +0100)
committerVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Wed, 2 Nov 2016 13:14:04 +0000 (14:14 +0100)
platform/credential-store/src/PasswordSafeImpl.kt
platform/credential-store/src/PasswordSafeSettings.java
platform/credential-store/src/macOsKeychainLibrary.kt
platform/credential-store/test/PasswordSafeTest.kt [new file with mode: 0644]

index 379cf77abf520bee03178497f715d739b171b73f..c1ea8e1dfc850335a76a54daa6102289c3005578 100644 (file)
@@ -26,30 +26,29 @@ import com.intellij.openapi.diagnostic.catchAndLog
 import org.jetbrains.concurrency.runAsync
 import java.nio.file.Paths
 
-class PasswordSafeImpl(/* public - backward compatibility */val settings: PasswordSafeSettings) : PasswordSafe(), SettingsSavingComponent {
-  internal @Volatile var currentProvider: PasswordStorage
+fun computeProvider(settings: PasswordSafeSettings): CredentialStore {
+  if (settings.providerType == ProviderType.MEMORY_ONLY || (ApplicationManager.getApplication()?.isUnitTestMode ?: false)) {
+    return KeePassCredentialStore(memoryOnly = true)
+  }
+  else if (settings.providerType == ProviderType.KEEPASS) {
+    val dbFile = settings.state.keepassDb?.let {
+      LOG.catchAndLog { return@let Paths.get(it) }
+      return@let null
+    }
+    return KeePassCredentialStore(dbFile = dbFile)
+  }
+  else {
+    return createPersistentCredentialStore()
+  }
+}
 
+class PasswordSafeImpl @JvmOverloads constructor(val settings: PasswordSafeSettings /* public - backward compatibility */,
+                                                 internal @Volatile var currentProvider: CredentialStore = computeProvider(settings)) : PasswordSafe(), SettingsSavingComponent {
   // it is helper storage to support set password as memory-only (see setPassword memoryOnly flag)
   private val memoryHelperProvider = lazy { KeePassCredentialStore(emptyMap(), memoryOnly = true) }
 
   override fun isMemoryOnly() = settings.providerType == ProviderType.MEMORY_ONLY
 
-  init {
-    if (settings.providerType == ProviderType.MEMORY_ONLY || ApplicationManager.getApplication().isUnitTestMode) {
-      currentProvider = KeePassCredentialStore(memoryOnly = true)
-    }
-    else if (settings.providerType == ProviderType.KEEPASS) {
-      val dbFile = settings.state.keepassDb?.let {
-        LOG.catchAndLog { return@let Paths.get(it) }
-        return@let null
-      }
-      currentProvider = KeePassCredentialStore(dbFile = dbFile)
-    }
-    else {
-      currentProvider = createPersistentCredentialStore()
-    }
-  }
-
   override fun get(attributes: CredentialAttributes): Credentials? {
     val value = currentProvider.get(attributes)
     if ((value == null || value.password.isNullOrEmpty()) && memoryHelperProvider.isInitialized()) {
@@ -123,7 +122,7 @@ class PasswordSafeImpl(/* public - backward compatibility */val settings: Passwo
   // public - backward compatibility
   @Suppress("unused", "DeprecatedCallableAddReplaceWith")
   @Deprecated("Do not use it")
-  val masterKeyProvider: PasswordStorage
+  val masterKeyProvider: CredentialStore
     get() = currentProvider
 
   @Suppress("unused")
index dd38d92f6d678deb2b0ac6e59bd361512244d1f8..85d6e8866b1c727231d2c0cba4cd3ffe9503a64a 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.intellij.credentialStore;
 
+import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.components.PersistentStateComponent;
 import com.intellij.openapi.components.RoamingType;
@@ -52,7 +53,10 @@ public class PasswordSafeSettings implements PersistentStateComponent<PasswordSa
     ProviderType oldValue = myProviderType;
     if (value != oldValue) {
       myProviderType = value;
-      ApplicationManager.getApplication().getMessageBus().syncPublisher(TOPIC).typeChanged(oldValue, value);
+      Application app = ApplicationManager.getApplication();
+      if (app != null) {
+        app.getMessageBus().syncPublisher(TOPIC).typeChanged(oldValue, value);
+      }
     }
   }
 
index 5d2c0809bf51cff7a789a951209d1ae53f1d4362..760416cd13d62ac3ae957e49607c8faa0870644c 100644 (file)
@@ -16,6 +16,7 @@
 package com.intellij.credentialStore
 
 import com.intellij.openapi.util.SystemInfo
+import com.intellij.util.ArrayUtilRt
 import com.sun.jna.*
 import com.sun.jna.ptr.IntByReference
 import com.sun.jna.ptr.PointerByReference
@@ -82,7 +83,7 @@ internal class KeyChainCredentialStore() : CredentialStore {
       attributeList.count = 1
       attribute.write()
       attributeList.attr = attribute.pointer
-      checkForError("save (update)", library.SecKeychainItemModifyContent(pointer, attributeList, password?.size ?: 0, password))
+      checkForError("save (update)", library.SecKeychainItemModifyContent(pointer, attributeList, password?.size ?: 0, password ?: ArrayUtilRt.EMPTY_BYTE_ARRAY))
       library.CFRelease(pointer)
     }
 
diff --git a/platform/credential-store/test/PasswordSafeTest.kt b/platform/credential-store/test/PasswordSafeTest.kt
new file mode 100644 (file)
index 0000000..c515f39
--- /dev/null
@@ -0,0 +1,61 @@
+package com.intellij.credentialStore
+
+import com.intellij.ide.passwordSafe.impl.PasswordSafeImpl
+import com.intellij.openapi.util.SystemInfo
+import com.intellij.testFramework.ApplicationRule
+import com.intellij.testFramework.Assertions.assertThat
+import com.intellij.testFramework.RuleChain
+import com.intellij.testFramework.TemporaryDirectory
+import com.intellij.testFramework.UsefulTestCase
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+
+class PasswordSafeTest {
+  companion object {
+    @JvmField
+    @ClassRule
+    val projectRule = ApplicationRule()
+  }
+
+  private val tempDirManager = TemporaryDirectory()
+
+  @Rule
+  @JvmField
+  val ruleChain = RuleChain(tempDirManager)
+
+  @Test
+  fun `erase password - KeePass`() {
+    val settings = PasswordSafeSettings()
+    settings.providerType = ProviderType.KEEPASS
+    val ps = PasswordSafeImpl(settings, KeePassCredentialStore(baseDirectory = tempDirManager.newPath()))
+
+    val attributes = CredentialAttributes("aaa", null, null, false)
+    ps.set(attributes, Credentials("a", "b"))
+    ps.set(attributes, Credentials("a", null as String?))
+
+    val saved = ps.get(attributes)!!
+    assertThat(saved.userName).isEqualTo("a")
+    assertThat(saved.password).isNullOrEmpty()
+  }
+
+  @Test
+  fun `erase password - macOs`() {
+    if (!(SystemInfo.isMacIntel64 && !UsefulTestCase.IS_UNDER_TEAMCITY)) {
+      return
+    }
+
+    val settings = PasswordSafeSettings()
+    settings.providerType = ProviderType.KEYCHAIN
+    val ps = PasswordSafeImpl(settings, KeyChainCredentialStore())
+
+    val attributes = CredentialAttributes("aaa", null, null, false)
+    ps.set(attributes, Credentials("a", "b"))
+    ps.set(attributes, Credentials("a", null as String?))
+
+    val saved = ps.get(attributes)!!
+    assertThat(saved.userName).isEqualTo("a")
+    assertThat(saved.password).isNullOrEmpty()
+  }
+}
+