mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
feat: Remember the last read-only state of each database
The app has supported a global setting for opening (all) databases in read-only mode. But that's not particularly flexible for the use case where you have one database that should be read-only and one that should be read-write. Previously, to handle this use case you could open one database in read-only mode, but the next time you attempted to open the same database, it would "forget" that, so you would have to toggle it to read-only mode again manually. This commit changes that behavior so that if you toggle a database to read-only mode, it'll be remembered the next time you open the database. (You can still toggle it back to read-write if you change your mind, and that, too, will be remembered the next time you open the database.)
This commit is contained in:
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 3,
|
||||||
|
"identityHash": "a20aec7cf09664b1102ec659fa51160a",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "file_database_history",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `database_alias` TEXT NOT NULL, `keyfile_uri` TEXT, `hardware_key` TEXT, `read_only` INTEGER, `updated` INTEGER NOT NULL, PRIMARY KEY(`database_uri`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "databaseUri",
|
||||||
|
"columnName": "database_uri",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "databaseAlias",
|
||||||
|
"columnName": "database_alias",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "keyFileUri",
|
||||||
|
"columnName": "keyfile_uri",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hardwareKey",
|
||||||
|
"columnName": "hardware_key",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "readOnly",
|
||||||
|
"columnName": "read_only",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "updated",
|
||||||
|
"columnName": "updated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"database_uri"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "cipher_database",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `encrypted_value` TEXT NOT NULL, `specs_parameters` TEXT NOT NULL, PRIMARY KEY(`database_uri`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "databaseUri",
|
||||||
|
"columnName": "database_uri",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "encryptedValue",
|
||||||
|
"columnName": "encrypted_value",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "specParameters",
|
||||||
|
"columnName": "specs_parameters",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"database_uri"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a20aec7cf09664b1102ec659fa51160a')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
|||||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
||||||
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
||||||
import com.kunzisoft.keepass.autofill.AutofillComponent
|
import com.kunzisoft.keepass.autofill.AutofillComponent
|
||||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||||
import com.kunzisoft.keepass.biometric.DeviceUnlockFragment
|
import com.kunzisoft.keepass.biometric.DeviceUnlockFragment
|
||||||
@@ -203,6 +204,13 @@ class MainCredentialActivity : DatabaseModeActivity() {
|
|||||||
}
|
}
|
||||||
mForceReadOnly = databaseFileNotExists
|
mForceReadOnly = databaseFileNotExists
|
||||||
|
|
||||||
|
// Restore read-only state from database file if not forced
|
||||||
|
if (!mForceReadOnly) {
|
||||||
|
databaseFile?.readOnly?.let { savedReadOnlyState ->
|
||||||
|
mReadOnly = savedReadOnlyState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
|
|
||||||
// Post init uri with KeyFile only if needed
|
// Post init uri with KeyFile only if needed
|
||||||
@@ -702,6 +710,12 @@ class MainCredentialActivity : DatabaseModeActivity() {
|
|||||||
R.id.menu_open_file_read_mode_key -> {
|
R.id.menu_open_file_read_mode_key -> {
|
||||||
mReadOnly = !mReadOnly
|
mReadOnly = !mReadOnly
|
||||||
changeOpenFileReadIcon(item)
|
changeOpenFileReadIcon(item)
|
||||||
|
// Save the read-only state to database
|
||||||
|
mDatabaseFileUri?.let { databaseUri ->
|
||||||
|
FileDatabaseHistoryAction.getInstance(applicationContext).addOrUpdateDatabaseFile(
|
||||||
|
DatabaseFile(databaseUri = databaseUri, readOnly = mReadOnly)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
|
else -> MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ import android.content.Context
|
|||||||
import androidx.room.AutoMigration
|
import androidx.room.AutoMigration
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 2,
|
version = 3,
|
||||||
entities = [FileDatabaseHistoryEntity::class, CipherDatabaseEntity::class],
|
entities = [FileDatabaseHistoryEntity::class, CipherDatabaseEntity::class],
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
AutoMigration (from = 1, to = 2)
|
AutoMigration (from = 1, to = 2),
|
||||||
|
AutoMigration (from = 2, to = 3)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
|
|||||||
databaseUri,
|
databaseUri,
|
||||||
fileDatabaseHistoryEntity?.keyFileUri?.parseUri(),
|
fileDatabaseHistoryEntity?.keyFileUri?.parseUri(),
|
||||||
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey),
|
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey),
|
||||||
|
fileDatabaseHistoryEntity?.readOnly,
|
||||||
fileDatabaseHistoryEntity?.databaseUri?.decodeUri(),
|
fileDatabaseHistoryEntity?.databaseUri?.decodeUri(),
|
||||||
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias
|
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias
|
||||||
?: ""),
|
?: ""),
|
||||||
@@ -99,6 +100,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
|
|||||||
fileDatabaseHistoryEntity.databaseUri.parseUri(),
|
fileDatabaseHistoryEntity.databaseUri.parseUri(),
|
||||||
fileDatabaseHistoryEntity.keyFileUri?.parseUri(),
|
fileDatabaseHistoryEntity.keyFileUri?.parseUri(),
|
||||||
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey),
|
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey),
|
||||||
|
fileDatabaseHistoryEntity.readOnly,
|
||||||
fileDatabaseHistoryEntity.databaseUri.decodeUri(),
|
fileDatabaseHistoryEntity.databaseUri.decodeUri(),
|
||||||
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
|
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
|
||||||
fileDatabaseInfo.exists,
|
fileDatabaseInfo.exists,
|
||||||
@@ -147,6 +149,8 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
|
|||||||
?: "",
|
?: "",
|
||||||
databaseFileToAddOrUpdate.keyFileUri?.toString(),
|
databaseFileToAddOrUpdate.keyFileUri?.toString(),
|
||||||
databaseFileToAddOrUpdate.hardwareKey?.value,
|
databaseFileToAddOrUpdate.hardwareKey?.value,
|
||||||
|
databaseFileToAddOrUpdate.readOnly
|
||||||
|
?: fileDatabaseHistoryRetrieve?.readOnly,
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -168,6 +172,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
|
|||||||
fileDatabaseHistory.databaseUri.parseUri(),
|
fileDatabaseHistory.databaseUri.parseUri(),
|
||||||
fileDatabaseHistory.keyFileUri?.parseUri(),
|
fileDatabaseHistory.keyFileUri?.parseUri(),
|
||||||
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
|
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
|
||||||
|
fileDatabaseHistory.readOnly,
|
||||||
fileDatabaseHistory.databaseUri.decodeUri(),
|
fileDatabaseHistory.databaseUri.decodeUri(),
|
||||||
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
|
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
|
||||||
fileDatabaseInfo.exists,
|
fileDatabaseInfo.exists,
|
||||||
@@ -195,6 +200,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
|
|||||||
fileDatabaseHistory.databaseUri.parseUri(),
|
fileDatabaseHistory.databaseUri.parseUri(),
|
||||||
fileDatabaseHistory.keyFileUri?.parseUri(),
|
fileDatabaseHistory.keyFileUri?.parseUri(),
|
||||||
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
|
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
|
||||||
|
fileDatabaseHistory.readOnly,
|
||||||
fileDatabaseHistory.databaseUri.decodeUri(),
|
fileDatabaseHistory.databaseUri.decodeUri(),
|
||||||
databaseFileToDelete.databaseAlias
|
databaseFileToDelete.databaseAlias
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ data class FileDatabaseHistoryEntity(
|
|||||||
@ColumnInfo(name = "hardware_key")
|
@ColumnInfo(name = "hardware_key")
|
||||||
var hardwareKey: String?,
|
var hardwareKey: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "read_only")
|
||||||
|
var readOnly: Boolean?,
|
||||||
|
|
||||||
@ColumnInfo(name = "updated")
|
@ColumnInfo(name = "updated")
|
||||||
val updated: Long
|
val updated: Long
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.kunzisoft.keepass.hardware.HardwareKey
|
|||||||
data class DatabaseFile(var databaseUri: Uri? = null,
|
data class DatabaseFile(var databaseUri: Uri? = null,
|
||||||
var keyFileUri: Uri? = null,
|
var keyFileUri: Uri? = null,
|
||||||
var hardwareKey: HardwareKey? = null,
|
var hardwareKey: HardwareKey? = null,
|
||||||
|
var readOnly: Boolean? = null,
|
||||||
var databaseDecodedPath: String? = null,
|
var databaseDecodedPath: String? = null,
|
||||||
var databaseAlias: String? = null,
|
var databaseAlias: String? = null,
|
||||||
var databaseFileExists: Boolean = false,
|
var databaseFileExists: Boolean = false,
|
||||||
|
|||||||
Reference in New Issue
Block a user