From d841c25bd33f2ff9d7b9aebd243fb62c28c604ff Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 21 Aug 2021 16:52:13 +0200 Subject: [PATCH] Check URI permissions #626 --- CHANGELOG | 3 +- .../activities/FileDatabaseSelectActivity.kt | 6 ++ .../activities/helpers/ExternalFileHelper.kt | 12 +-- .../keepass/database/element/Database.kt | 2 + .../keepass/utils/BroadcastAction.kt | 18 ++++ .../com/kunzisoft/keepass/utils/UriUtil.kt | 84 ++++++++++++++++++- .../keepass/viewmodels/FileDatabaseInfo.kt | 4 + .../metadata/android/en-US/changelogs/84.txt | 5 +- .../metadata/android/fr-FR/changelogs/84.txt | 5 +- 9 files changed, 120 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d2b8abcb5..b992d48f0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,9 @@ KeePassDX(3.0.0) * Add / Manage dynamic templates #191 * Manually select RecycleBin group and Templates group #191 - * Small changes #1035 * Fix timeout in dialogs #716 + * Check URI permissions #626 + * Small changes #1035 KeePassDX(2.10.5) * Increase the saving speed of database #1028 diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 03b66a08c..7cbd2d98b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -126,6 +126,12 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(), } mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete -> // Remove from app database + fileDatabaseHistoryToDelete.databaseUri?.let { databaseUri -> + UriUtil.releaseUriPermission( + contentResolver, + databaseUri + ) + } databaseFilesViewModel.deleteDatabaseFile(fileDatabaseHistoryToDelete) true } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt index 9dbfd3a97..2792c35ef 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt @@ -127,17 +127,7 @@ class ExternalFileHelper { if (data != null) { val uri = data.data if (uri != null) { - try { - // try to persist read and write permissions - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - activity?.contentResolver?.apply { - takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) - takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - } - } - } catch (e: Exception) { - // nop - } + UriUtil.takeUriPermission(activity?.contentResolver, uri) onFileSelected?.invoke(uri) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 08ff43714..e604b7fbf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -23,7 +23,9 @@ import android.content.ContentResolver import android.content.Context import android.content.res.Resources import android.net.Uri +import android.os.Build import android.util.Log +import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.action.node.NodeHandler import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt b/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt index d7d84d445..96456637d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt @@ -30,6 +30,7 @@ import android.content.IntentFilter import android.os.Build import android.util.Log import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.magikeyboard.MagikeyboardService import com.kunzisoft.keepass.services.ClipboardEntryNotificationService @@ -139,4 +140,21 @@ fun Context.closeDatabase(database: Database?) { } // Clear data database?.clearAndClose(this) + + // Release not useful URI permission + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + applicationContext?.let { appContext -> + val fileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(appContext) + fileDatabaseHistoryAction.getDatabaseFileList { databaseFileList -> + val listToNotRemove = databaseFileList.map { it.databaseUri } + // Remove URI permission for not database files + val resolver = appContext.contentResolver + resolver.persistedUriPermissions.forEach { uriPermission -> + val uri = uriPermission.uri + if (!listToNotRemove.contains(uri)) + UriUtil.releaseUriPermission(resolver, uri) + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt index 34ae4431e..4225eb05a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt @@ -56,7 +56,7 @@ object UriUtil { } } } catch (e: Exception) { - Log.e("FileData", "Unable to get document file", e) + Log.e(TAG, "Unable to get document file", e) null } } @@ -110,6 +110,83 @@ object UriUtil { return Uri.decode(uri) ?: "" } + private fun persistUriPermission(contentResolver: ContentResolver?, + uri: Uri, + release: Boolean, + readOnly: Boolean) { + try { + // try to persist read and write permissions + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + contentResolver?.apply { + var readPermissionAllowed = false + var writePermissionAllowed = false + // Check current permissions allowed + persistedUriPermissions.find { uriPermission -> + uriPermission.uri == uri + }?.let { uriPermission -> + Log.d(TAG, "Check URI permission : $uriPermission") + if (uriPermission.isReadPermission) { + readPermissionAllowed = true + } + if (uriPermission.isWritePermission) { + writePermissionAllowed = true + } + } + + // Release permission + if (release) { + if (writePermissionAllowed) { + Log.d(TAG, "Release write permission : $uri") + val removeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + releasePersistableUriPermission(uri, removeFlags) + } + if (readPermissionAllowed) { + Log.d(TAG, "Release read permission $uri") + val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION + releasePersistableUriPermission(uri, takeFlags) + } + } + + // Take missing permission + if (!readPermissionAllowed) { + Log.d(TAG, "Take read permission $uri") + val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION + takePersistableUriPermission(uri, takeFlags) + } + if (readOnly) { + if (writePermissionAllowed) { + Log.d(TAG, "Release write permission $uri") + val removeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + releasePersistableUriPermission(uri, removeFlags) + } + } else { + if (!writePermissionAllowed) { + Log.d(TAG, "Take write permission $uri") + val takeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + takePersistableUriPermission(uri, takeFlags) + } + } + } + } + } catch (e: Exception) { + if (release) + Log.e(TAG, "Unable to release persistable URI permission", e) + else + Log.e(TAG, "Unable to take persistable URI permission", e) + } + } + + fun takeUriPermission(contentResolver: ContentResolver?, + uri: Uri, + readOnly: Boolean = false) { + persistUriPermission(contentResolver, uri, false, readOnly) + } + + fun releaseUriPermission(contentResolver: ContentResolver?, + uri: Uri) { + persistUriPermission(contentResolver, uri, release = true, readOnly = false) + } + fun getUriFromIntent(intent: Intent, key: String): Uri? { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { @@ -156,7 +233,7 @@ object UriUtil { context.applicationContext.packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) return true } catch (e: Exception) { - Log.e(javaClass.simpleName, "App not accessible", e) + Log.e(TAG, "App not accessible", e) } return false } @@ -165,7 +242,7 @@ object UriUtil { try { context.startActivity(context.applicationContext.packageManager.getLaunchIntentForPackage(packageName)) } catch (e: Exception) { - Log.e(javaClass.simpleName, "App cannot be open", e) + Log.e(TAG, "App cannot be open", e) } } @@ -177,4 +254,5 @@ object UriUtil { } } + private const val TAG = "UriUtil" } diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/FileDatabaseInfo.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/FileDatabaseInfo.kt index 6d9df989c..3e8a8fe15 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/FileDatabaseInfo.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/FileDatabaseInfo.kt @@ -48,6 +48,10 @@ class FileDatabaseInfo : Serializable { } fun init() { + // Check permission + fileUri?.let { uri -> + UriUtil.takeUriPermission(context.contentResolver, uri) + } documentFile = UriUtil.getFileData(context, fileUri) } diff --git a/fastlane/metadata/android/en-US/changelogs/84.txt b/fastlane/metadata/android/en-US/changelogs/84.txt index b2bd51e0a..252d0e820 100644 --- a/fastlane/metadata/android/en-US/changelogs/84.txt +++ b/fastlane/metadata/android/en-US/changelogs/84.txt @@ -1,4 +1,5 @@ * Add / Manage dynamic templates #191 * Allow to manually select RecycleBin group and Templates group #191 - * Small changes #1035 - * Fix timeout in dialogs #716 \ No newline at end of file + * Fix timeout in dialogs #716 + * Check URI permissions #626 + * Small changes #1035 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/84.txt b/fastlane/metadata/android/fr-FR/changelogs/84.txt index 3823173c8..7f8838e1e 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/84.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/84.txt @@ -1,4 +1,5 @@ * Ajout / Gestion dynamique des templates #191 * Sélection manuelle des groupes de la corbeille et des templates #191 - * Petits changements #1035 - * Correction du délai d'expiration dans les dialogues #716 \ No newline at end of file + * Correction du délai d'expiration dans les dialogues #716 + * Vérification des permissions URI #626 + * Petits changements #1035 \ No newline at end of file