extract *KeePassDatabaseAction, move SynchronizedClearableLazy to platform

This commit is contained in:
Vladimir Krivosheev
2018-10-02 20:10:00 +02:00
parent d425a0cfc4
commit eb60d8172b
7 changed files with 72 additions and 54 deletions
@@ -168,49 +168,14 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
}
keePassDbFile = textFieldWithBrowseButton("KeePass Database File",
fileChooserDescriptor = fileChooserDescriptor,
fileChosen = ::normalizeSelectedFile,
fileChosen = {
normalizeSelectedFile(it)
},
comment = if (SystemInfo.isWindows) null else "Stored using weak encryption. It is recommended to store on encrypted volume for additional security.")
gearButton(
object : DumbAwareAction("Clear") {
override fun actionPerformed(event: AnActionEvent) {
if (!MessageDialogBuilder.yesNo("Clear Passwords", "Are you sure want to remove all passwords?").yesText("Remove Passwords").isYes) {
return
}
LOG.info("Passwords cleared", Error())
createKeePassFileManager()?.clear()
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabled = getNewDbFile()?.exists() ?: false
}
},
object : DumbAwareAction("Import") {
override fun actionPerformed(event: AnActionEvent) {
chooseFile(fileChooserDescriptor, event) {
createKeePassFileManager()?.import(Paths.get(normalizeSelectedFile(it)), event)
// force reload KeePass Store
passwordSafe.closeCurrentProvider()
}
}
},
object : DumbAwareAction("${if (MasterKeyFileStorage(getDefaultMasterPasswordFile()).isAutoGenerated()) "Set" else "Change"} Master Password") {
override fun actionPerformed(event: AnActionEvent) {
// even if current provider is not KEEPASS, all actions for db file must be applied immediately (show error if new master password not applicable for existing db file)
if (createKeePassFileManager()?.askAndSetMasterKey(event) == true) {
if (passwordSafe.settings.providerType == ProviderType.KEEPASS) {
// force reload KeePass Store
passwordSafe.closeCurrentProvider()
}
templatePresentation.text = "Change Master Password"
}
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabled = getNewDbFileAsString() != null
}
}
ClearKeePassDatabaseAction(),
ImportKeePassDatabaseAction(passwordSafe),
ChangeKeePassDatabaseMasterPasswordAction()
)
}
}
@@ -237,6 +202,49 @@ internal class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings>
else -> ProviderType.KEYCHAIN
}
}
private inner class ClearKeePassDatabaseAction : DumbAwareAction("Clear") {
override fun actionPerformed(event: AnActionEvent) {
if (!MessageDialogBuilder.yesNo("Clear Passwords", "Are you sure want to remove all passwords?").yesText("Remove Passwords").isYes) {
return
}
LOG.info("Passwords cleared", Error())
createKeePassFileManager()?.clear() ?: return
(PasswordSafe.instance as PasswordSafeImpl).closeCurrentStoreIfKeePass()
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabled = getNewDbFile()?.exists() ?: false
}
}
private inner class ImportKeePassDatabaseAction(private val passwordSafe: PasswordSafeImpl) : DumbAwareAction("Import") {
override fun actionPerformed(event: AnActionEvent) {
FileChooserDescriptorFactory.createSingleLocalFileDescriptor()
.withFileFilter {
it.nameSequence.endsWith(".kdbx")
}
.chooseFile(event) {
createKeePassFileManager()?.import(Paths.get(normalizeSelectedFile(it)), event)
passwordSafe.closeCurrentStoreIfKeePass()
}
}
}
private inner class ChangeKeePassDatabaseMasterPasswordAction : DumbAwareAction("${if (MasterKeyFileStorage(getDefaultMasterPasswordFile()).isAutoGenerated()) "Set" else "Change"} Master Password") {
override fun actionPerformed(event: AnActionEvent) {
// even if current provider is not KEEPASS, all actions for db file must be applied immediately (show error if new master password not applicable for existing db file)
if (createKeePassFileManager()?.askAndSetMasterKey(event) == true) {
(PasswordSafe.instance as PasswordSafeImpl).closeCurrentStoreIfKeePass()
templatePresentation.text = "Change Master Password"
}
}
override fun update(e: AnActionEvent) {
e.presentation.isEnabled = getNewDbFileAsString() != null
}
}
}
private fun normalizeSelectedFile(file: VirtualFile): String {
@@ -17,6 +17,7 @@ import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.util.ShutDownTracker
import com.intellij.util.Alarm
import com.intellij.util.SingleAlarm
import com.intellij.util.concurrency.SynchronizedClearableLazy
import org.jetbrains.annotations.TestOnly
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.runAsync
@@ -62,14 +63,14 @@ private fun computeProvider(settings: PasswordSafeSettings): CredentialStore {
}
class PasswordSafeImpl @JvmOverloads constructor(val settings: PasswordSafeSettings /* public - backward compatibility */,
provider: CredentialStore? = null) : PasswordSafe(), SettingsSavingComponent {
provider: CredentialStore? = null /* TestOnly */) : PasswordSafe(), SettingsSavingComponent {
override var isRememberPasswordByDefault: Boolean
get() = settings.state.isRememberPasswordByDefault
set(value) {
settings.state.isRememberPasswordByDefault = value
}
private var _currentProvider: Lazy<CredentialStore> = if (provider == null) lazy { computeProvider(settings) } else lazyOf(provider)
private var _currentProvider: Lazy<CredentialStore> = if (provider == null) SynchronizedClearableLazy { computeProvider(settings) } else lazyOf(provider)
internal val currentProviderIfComputed: CredentialStore?
get() = if (_currentProvider.isInitialized()) _currentProvider.value else null
@@ -80,8 +81,12 @@ class PasswordSafeImpl @JvmOverloads constructor(val settings: PasswordSafeSetti
_currentProvider = lazyOf(value)
}
internal fun closeCurrentProvider() {
_currentProvider = lazy { computeProvider(settings) }
// force reload KeePass Store if settings changed
internal fun closeCurrentStoreIfKeePass() {
val store = currentProviderIfComputed
if (store is KeePassCredentialStore && !store.isMemoryOnly) {
(_currentProvider as SynchronizedClearableLazy).drop()
}
}
// it is helper storage to support set password as memory-only (see setPassword memoryOnly flag)
@@ -48,13 +48,13 @@ class LayoutBuilder @PublishedApi internal constructor(@PublishedApi internal va
return group
}
fun chooseFile(descriptor: FileChooserDescriptor, event: AnActionEvent, fileChosen: (chosenFile: VirtualFile) -> Unit) {
FileChooser.chooseFile(descriptor, event.getData(PlatformDataKeys.PROJECT), event.getData(PlatformDataKeys.CONTEXT_COMPONENT), null, fileChosen)
}
@Suppress("PropertyName")
@PublishedApi
@Deprecated("", replaceWith = ReplaceWith("builder"), level = DeprecationLevel.ERROR)
internal val `$`: LayoutBuilderImpl
get() = builder
}
fun FileChooserDescriptor.chooseFile(event: AnActionEvent, fileChosen: (chosenFile: VirtualFile) -> Unit) {
FileChooser.chooseFile(this, event.getData(PlatformDataKeys.PROJECT), event.getData(PlatformDataKeys.CONTEXT_COMPONENT), null, fileChosen)
}
@@ -1,11 +1,15 @@
@file:Suppress("ClassName")
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.configurationScript
package com.intellij.util.concurrency
@Suppress("ClassName")
private object UNINITIALIZED_VALUE
/**
* Kotlin-friendly version of ClearableLazyValue
*/
@Suppress("LocalVariableName")
internal class SynchronizedClearableLazy<out T>(private val initializer: () -> T) : Lazy<T> {
class SynchronizedClearableLazy<out T>(private val initializer: () -> T) : Lazy<T> {
@Volatile
private var _value: Any? = UNINITIALIZED_VALUE
@@ -9,6 +9,7 @@ import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.util.concurrency.SynchronizedClearableLazy
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.io.exists
import com.intellij.util.io.inputStreamIfExists
@@ -3,7 +3,6 @@ package com.intellij.configurationScript.providers
import com.intellij.configurationScript.ConfigurationFileManager
import com.intellij.configurationScript.Keys
import com.intellij.configurationScript.RunConfigurationListReader
import com.intellij.configurationScript.SynchronizedClearableLazy
import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.execution.configurations.RunConfigurationBase
import com.intellij.execution.impl.RunConfigurationTemplateProvider
@@ -13,6 +12,7 @@ import com.intellij.openapi.components.BaseState
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.util.concurrency.SynchronizedClearableLazy
import gnu.trove.THashMap
import org.yaml.snakeyaml.nodes.MappingNode
import org.yaml.snakeyaml.nodes.ScalarNode
@@ -2,12 +2,12 @@ package com.intellij.configurationScript.providers
import com.intellij.configurationScript.ConfigurationFileManager
import com.intellij.configurationScript.Keys
import com.intellij.configurationScript.SynchronizedClearableLazy
import com.intellij.configurationScript.readObject
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.updateSettings.impl.UpdateSettingsProvider
import com.intellij.util.concurrency.SynchronizedClearableLazy
import org.yaml.snakeyaml.nodes.MappingNode
import org.yaml.snakeyaml.nodes.ScalarNode