IDEA-161674 Native "Deny" answer is not respected — IDEA continues to ask access

first quick solution — keep decision for 1 minute
This commit is contained in:
Vladimir Krivosheev
2018-07-09 16:49:15 +02:00
parent d2e8cc872c
commit 265dbfe243
7 changed files with 35 additions and 42 deletions
@@ -1,6 +1,7 @@
// 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.credentialStore
import com.google.common.cache.CacheBuilder
import com.intellij.ide.passwordSafe.PasswordStorage
import com.intellij.notification.NotificationGroup
import com.intellij.notification.NotificationType
@@ -11,6 +12,7 @@ import com.intellij.openapi.diagnostic.runAndLogException
import com.intellij.openapi.util.SystemInfo
import com.intellij.util.SystemProperties
import com.intellij.util.concurrency.QueueProcessor
import java.util.concurrent.TimeUnit
private val nullCredentials = Credentials("\u0000", OneTimeString("\u0000"))
@@ -22,20 +24,30 @@ internal val NOTIFICATION_MANAGER by lazy {
private class CredentialStoreWrapper(private val store: CredentialStore) : PasswordStorage {
private val fallbackStore = lazy { KeePassCredentialStore(memoryOnly = true) }
private val queueProcessor = QueueProcessor<() -> Unit>({ it() })
private val queueProcessor = QueueProcessor<() -> Unit> { it() }
private val postponedCredentials = KeePassCredentialStore(memoryOnly = true)
private val deniedItems = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.MINUTES).build<CredentialAttributes, Boolean>()
override fun get(attributes: CredentialAttributes): Credentials? {
postponedCredentials.get(attributes)?.let {
return if (it == nullCredentials) null else it
}
if (deniedItems.getIfPresent(attributes) != null) {
LOG.warn("User denied access to $attributes")
return null
}
var store = if (fallbackStore.isInitialized()) fallbackStore.value else store
val requestor = attributes.requestor
val userName = attributes.userName
try {
val value = store.get(attributes)
if (value === ACCESS_TO_KEY_CHAIN_DENIED) {
deniedItems.put(attributes, true)
return null
}
if (value != null || requestor == null || userName == null) {
return value
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.credentialStore
import com.intellij.openapi.diagnostic.Logger
@@ -100,9 +86,10 @@ private fun parseString(data: String, delimiter: Char): List<String> {
@JvmOverloads
fun Credentials.serialize(storePassword: Boolean = true): ByteArray = joinData(userName, if (storePassword) password else null)!!
fun SecureString(value: CharSequence): SecureString = SecureString(Charsets.UTF_8.encode(CharBuffer.wrap(value)).toByteArray())
@Suppress("FunctionName")
internal fun SecureString(value: CharSequence): SecureString = SecureString(Charsets.UTF_8.encode(CharBuffer.wrap(value)).toByteArray())
class SecureString(value: ByteArray) {
internal class SecureString(value: ByteArray) {
companion object {
private val encryptionSupport = EncryptionSupport(SecretKeySpec(generateAesKey(), "AES"))
}
@@ -110,4 +97,6 @@ class SecureString(value: ByteArray) {
private val data = encryptionSupport.encrypt(value)
fun get(clearable: Boolean = true): OneTimeString = OneTimeString(encryptionSupport.decrypt(data), clearable = clearable)
}
}
internal val ACCESS_TO_KEY_CHAIN_DENIED = Credentials(null, null as OneTimeString?)
@@ -22,7 +22,7 @@ import org.jdom.Element
private const val VALUE_ELEMENT_NAME = "Value"
class KdbxEntry(private val element: Element, private val database: KeePassDatabase, internal @Volatile var group: KdbxGroup?) {
internal class KdbxEntry(private val element: Element, private val database: KeePassDatabase, internal @Volatile var group: KdbxGroup?) {
@Volatile var title: String? = element.removeProperty("Title")
set(value) {
if (field != value) {
@@ -1,3 +1,4 @@
// 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.credentialStore.kdbx
import com.intellij.util.containers.ContainerUtil
@@ -10,7 +11,7 @@ import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset
class KdbxGroup(private val element: Element, private val database: KeePassDatabase, private @Volatile var parent: KdbxGroup?) {
internal class KdbxGroup(private val element: Element, private val database: KeePassDatabase, private @Volatile var parent: KdbxGroup?) {
@Volatile var name: String = element.getChildText(NAME_ELEMENT_NAME) ?: "Unnamed"
set(value) {
if (field != value) {
@@ -29,7 +29,7 @@ private const val ROOT_ELEMENT_NAME = "Root"
internal var dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
class KeePassDatabase(private val rootElement: Element = createEmptyDatabase()) {
internal class KeePassDatabase(private val rootElement: Element = createEmptyDatabase()) {
private val dbMeta: Element
get() = rootElement.getChild("Meta") ?: throw IllegalStateException("no meta")
@@ -79,12 +79,15 @@ internal class KeyChainCredentialStore : CredentialStore {
}
}
fun findGenericPassword(serviceName: ByteArray, accountName: String?): Credentials? {
private fun findGenericPassword(serviceName: ByteArray, accountName: String?): Credentials? {
val accountNameBytes = accountName?.toByteArray()
val passwordSize = IntArray(1)
val passwordRef = PointerByReference()
val itemRef = PointerByReference()
checkForError("find", LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes?.size ?: 0, accountNameBytes, passwordSize, passwordRef, itemRef))
val errorCode = checkForError("find", LIBRARY.SecKeychainFindGenericPassword(null, serviceName.size, serviceName, accountNameBytes?.size ?: 0, accountNameBytes, passwordSize, passwordRef, itemRef))
if (errorCode == errSecUserCanceled) {
return ACCESS_TO_KEY_CHAIN_DENIED
}
val pointer = passwordRef.value ?: return null
val password = OneTimeString(pointer.getByteArray(0, passwordSize.get(0)))
@@ -158,9 +161,9 @@ internal class SecKeychainAttributeInfo : Structure() {
override fun getFieldOrder() = listOf("count", "tag", "format")
}
private fun checkForError(message: String, code: Int) {
private fun checkForError(message: String, code: Int): Int {
if (code == errSecSuccess || code == errSecItemNotFound) {
return
return code
}
val translated = LIBRARY.SecCopyErrorMessageString(code, null)
@@ -183,6 +186,8 @@ private fun checkForError(message: String, code: Int) {
else {
LOG.error(builder.toString())
}
return code
}
@Suppress("FunctionName")
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.ide.passwordSafe.impl.providers.memory;
import com.intellij.ide.passwordSafe.impl.PasswordSafeTimed;
@@ -46,7 +32,7 @@ public class MemoryPasswordSafe extends BasePasswordSafeProvider {
private final transient PasswordSafeTimed<Map<ByteArrayWrapper, byte[]>> database = new PasswordSafeTimed<Map<ByteArrayWrapper, byte[]>>() {
@Override
protected Map<ByteArrayWrapper, byte[]> compute() {
return Collections.synchronizedMap(ContainerUtil.<ByteArrayWrapper, byte[]>newHashMap());
return Collections.synchronizedMap(ContainerUtil.newHashMap());
}
@Override