diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index e7a10029e..d4963ef3c 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -6,6 +6,10 @@ # we keep it here. -keep class com.bitwarden.** { *; } +# The Android Verifier component must be kept because it looks like dead code. Proguard is unable to +# see any JNI usage, so our rules must manually opt into keeping it. +-keep, includedescriptorclasses class org.rustls.platformverifier.** { *; } + ################################################################################ # Bitwarden Models ################################################################################ diff --git a/app/schemas/com.x8bit.bitwarden.data.vault.datasource.disk.database.VaultDatabase/4.json b/app/schemas/com.x8bit.bitwarden.data.vault.datasource.disk.database.VaultDatabase/4.json new file mode 100644 index 000000000..88746cf79 --- /dev/null +++ b/app/schemas/com.x8bit.bitwarden.data.vault.datasource.disk.database.VaultDatabase/4.json @@ -0,0 +1,256 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "f7906c69e0a2c065d4d3be140fc721b6", + "entities": [ + { + "tableName": "ciphers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `cipher_type` TEXT NOT NULL, `cipher_json` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cipherType", + "columnName": "cipher_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cipherJson", + "columnName": "cipher_json", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_ciphers_user_id", + "unique": false, + "columnNames": [ + "user_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ciphers_user_id` ON `${TABLE_NAME}` (`user_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "collections", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `organization_id` TEXT NOT NULL, `should_hide_passwords` INTEGER NOT NULL, `name` TEXT NOT NULL, `external_id` TEXT, `read_only` INTEGER NOT NULL, `manage` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "organizationId", + "columnName": "organization_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shouldHidePasswords", + "columnName": "should_hide_passwords", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "externalId", + "columnName": "external_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isReadOnly", + "columnName": "read_only", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canManage", + "columnName": "manage", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_collections_user_id", + "unique": false, + "columnNames": [ + "user_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_collections_user_id` ON `${TABLE_NAME}` (`user_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "domains", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `domains_json` TEXT NOT NULL, PRIMARY KEY(`user_id`))", + "fields": [ + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "domainsJson", + "columnName": "domains_json", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "user_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `name` TEXT, `revision_date` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "revisionDate", + "columnName": "revision_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_folders_user_id", + "unique": false, + "columnNames": [ + "user_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_folders_user_id` ON `${TABLE_NAME}` (`user_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "sends", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `send_type` TEXT NOT NULL, `send_json` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sendType", + "columnName": "send_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sendJson", + "columnName": "send_json", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_sends_user_id", + "unique": false, + "columnNames": [ + "user_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_sends_user_id` ON `${TABLE_NAME}` (`user_id`)" + } + ], + "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, 'f7906c69e0a2c065d4d3be140fc721b6')" + ] + } +} \ No newline at end of file diff --git a/app/src/beta/res/xml/shortcuts.xml b/app/src/beta/res/xml/shortcuts.xml new file mode 100644 index 000000000..d82905194 --- /dev/null +++ b/app/src/beta/res/xml/shortcuts.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt index 60c42afbd..91a6b8fe5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt @@ -94,6 +94,7 @@ class VaultDiskSourceImpl( shouldHidePasswords = collection.shouldHidePasswords, externalId = collection.externalId, isReadOnly = collection.isReadOnly, + canManage = collection.canManage, ), ) } @@ -114,6 +115,7 @@ class VaultDiskSourceImpl( shouldHidePasswords = entity.shouldHidePasswords, externalId = entity.externalId, isReadOnly = entity.isReadOnly, + canManage = entity.canManage, ) } }, @@ -229,6 +231,7 @@ class VaultDiskSourceImpl( shouldHidePasswords = collection.shouldHidePasswords, externalId = collection.externalId, isReadOnly = collection.isReadOnly, + canManage = collection.canManage, ) }, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/database/VaultDatabase.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/database/VaultDatabase.kt index 495e038d3..23511a03f 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/database/VaultDatabase.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/database/VaultDatabase.kt @@ -26,7 +26,7 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.entity.SendEntity FolderEntity::class, SendEntity::class, ], - version = 3, + version = 4, exportSchema = true, ) @TypeConverters(ZonedDateTimeTypeConverter::class) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/entity/CollectionEntity.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/entity/CollectionEntity.kt index f734099c4..5370642f9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/entity/CollectionEntity.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/entity/CollectionEntity.kt @@ -30,4 +30,7 @@ data class CollectionEntity( @ColumnInfo(name = "read_only") val isReadOnly: Boolean, + + @ColumnInfo(name = "manage") + val canManage: Boolean, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt index 8e47a2624..384e2599f 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt @@ -969,5 +969,8 @@ data class SyncResponseJson( @SerialName("id") val id: String, + + @SerialName("manage") + val canManage: Boolean, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensions.kt index bf485353c..42d9e373e 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensions.kt @@ -17,6 +17,7 @@ fun SyncResponseJson.Collection.toEncryptedSdkCollection(): Collection = externalId = this.externalId, hidePasswords = this.shouldHidePasswords, readOnly = this.isReadOnly, + manage = this.canManage, ) /** diff --git a/app/src/standard/java/com/x8bit/bitwarden/data/platform/manager/LogsManagerImpl.kt b/app/src/standard/java/com/x8bit/bitwarden/data/platform/manager/LogsManagerImpl.kt index 0947f8b25..060513ce9 100644 --- a/app/src/standard/java/com/x8bit/bitwarden/data/platform/manager/LogsManagerImpl.kt +++ b/app/src/standard/java/com/x8bit/bitwarden/data/platform/manager/LogsManagerImpl.kt @@ -30,7 +30,7 @@ class LogsManagerImpl( } if (value) { Timber.plant(nonfatalErrorTree) - } else { + } else if (Timber.forest().contains(nonfatalErrorTree)) { Timber.uproot(nonfatalErrorTree) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt index 085611f39..7c5719b7b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceTest.kt @@ -431,6 +431,7 @@ private val COLLECTION_ENTITY = CollectionEntity( name = "mockName-3", externalId = "mockExternalId-3", isReadOnly = false, + canManage = true, ) private const val DOMAINS_JSON = """ diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseCollectionUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseCollectionUtil.kt index b97b2ead1..dfa9c40e0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseCollectionUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseCollectionUtil.kt @@ -11,4 +11,5 @@ fun createMockCollection(number: Int): SyncResponseJson.Collection = externalId = "mockExternalId-$number", isReadOnly = false, id = "mockId-$number", + canManage = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SyncServiceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SyncServiceTest.kt index 8ca29fa96..4fcac144f 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SyncServiceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/service/SyncServiceTest.kt @@ -193,7 +193,8 @@ private const val SYNC_SUCCESS_JSON = """ "name": "mockName-1", "externalId": "mockExternalId-1", "readOnly": false, - "id": "mockId-1" + "id": "mockId-1", + "manage": true } ], "ciphers": [ diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CollectionViewUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CollectionViewUtil.kt index 91a4b85b8..379ad075f 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CollectionViewUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CollectionViewUtil.kt @@ -13,4 +13,5 @@ fun createMockCollectionView(number: Int, name: String? = null): CollectionView name = name ?: "mockName-$number", externalId = "mockExternalId-$number", readOnly = false, + manage = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/VaultSdkCollectionUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/VaultSdkCollectionUtil.kt index faa8f122d..75b66fd57 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/VaultSdkCollectionUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/VaultSdkCollectionUtil.kt @@ -13,4 +13,5 @@ fun createMockSdkCollection(number: Int): Collection = name = "mockName-$number", externalId = "mockExternalId-$number", readOnly = false, + manage = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensionsTest.kt index 4e1458a2b..490cab5f6 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCollectionExtensionsTest.kt @@ -17,6 +17,7 @@ class VaultSdkCollectionExtensionsTest { externalId = "externalId", readOnly = true, id = "id", + manage = true, ), SyncResponseJson.Collection( organizationId = "organizationId", @@ -25,6 +26,7 @@ class VaultSdkCollectionExtensionsTest { externalId = "externalId", isReadOnly = true, id = "id", + canManage = true, ) .toEncryptedSdkCollection(), ) @@ -42,6 +44,7 @@ class VaultSdkCollectionExtensionsTest { externalId = "externalId", readOnly = true, id = "id", + manage = true, ), ), listOf( @@ -52,6 +55,7 @@ class VaultSdkCollectionExtensionsTest { externalId = "externalId", isReadOnly = true, id = "id", + canManage = true, ) .toEncryptedSdkCollection(), ), diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2049224d1..a652db318 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ androidxSplash = "1.1.0-rc01" androidXAppCompat = "1.7.0" androdixAutofill = "1.1.0" androidxWork = "2.9.1" -bitwardenSdk = "1.0.0-20241021.160919-71" +bitwardenSdk = "1.0.0-20241024.173753-4" crashlytics = "3.0.2" detekt = "1.23.7" firebaseBom = "33.5.1" @@ -84,7 +84,7 @@ androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = " androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "androidXSecurityCrypto" } androidx-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidxSplash" } androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidxWork" } -bitwarden-sdk = { module = "com.bitwarden:sdk-android", version.ref = "bitwardenSdk" } +bitwarden-sdk = { module = "com.bitwarden:sdk-android-temp", version.ref = "bitwardenSdk" } bumptech-glide = { module = "com.github.bumptech.glide:compose", version.ref = "glide" } detekt-detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } detekt-detekt-rules = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "detekt" }