Given the Entity:
@Entity(tableName = "otp_tokens")
data class DBEntity (
@PrimaryKey(autoGenerate = true)
val id: Long,
val data: String,
val encryptionType: EncryptionType
)
EncryptionType
:
enum class EncryptionType {
PLAIN_TEXT,
ENCRYPTED
}
I want to use a @TypeConverter
to encrypt/decrypt the data
String field when accessing the database, based on the value of the encryptionType
field value. So for example if encryptionType == PLAIN_TEXT
the TypeConverter should not do anything with the String value, but if encryptionType == ENCRPYTED
it should encrypt/decrypt the data
field.
When I write a TypeConverter
for the data
field I only get the String value of data
in the encrypt/decrypt methods of the Converter and have no access to the encryptionType
field, so how can I implement this?
CodePudding user response:
Type Converters will only convert an unhandled Type to a Type that can be handled (stored).
So your options would be to
- either have the Type for the
data
member/field as such a (unhandled) Type, or - to have DAO function that encrypts the data when inserting (and possibly similar when extracting the data)
Here's an example of the latter.
First the data field is changed to var to allow it's manipulation (encryption/decryption). Then the encryption/decryption functions are added so DBEntity becomes:-
@Entity(tableName = "otp_tokens")
data class DBEntity (
@PrimaryKey(autoGenerate = true)
val id: Long,
var data: String,
val encryptionType: EncryptionType
) {
fun encryptData() {
if (this.encryptionType.equals(EncryptionType.ENCRYPTED) ) {
val sb = StringBuilder()
for (i in 0..this.data.length - 1) {
sb.append("~", this.data[i])
}
this.data = sb.toString()
}
}
/* Not Tested/Used in the demo */
fun decryptData(): String {
if (this.encryptionType.equals(EncryptionType.ENCRYPTED)) {
val sb = StringBuilder()
for (i in 0..this.data.length - 2 step 2) {
sb.append(this.data[i 1])
}
return this.data
} else {
return this.data
}
}
}
obviously the encryption is just a very simple change to the data and that whatever encryption/decryption process you would want to implement would replace the method used for the example/demo.
- Obviously there would be issues, if for example you encrypted the already encrypted or decrypted the already decrypted.
As stated a DAO function could then utilise the above to encrypt or not encrypt when inserting, so the @Dao annotated interface could include:-
@Dao
interface DBEntityDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(dbEntity: DBEntity)
fun insertAndEncrypt(dbEntity: DBEntity) {
dbEntity.encryptData()
insert(dbEntity)
}
}
Putting this into action (note .allowMainThreadQueries
has been utilised for brevity and convenience), the following is coded in an activity:-
db = TheDatabase.getInstance(this)
dao = db.getDBEntityDao()
val d1 = DBEntity(0,"Rumplestiltskin",EncryptionType.PLAIN_TEXT)
val d2 = DBEntity(0,"Rumplestiltskin",EncryptionType.ENCRYPTED)
dao.insert(d1)
dao.insert(d2)
dao.insertAndEncrypt(d1)
dao.insertAndEncrypt(d2)
The resultant data, view via App Inspection:-
As can be seen:-
- The first two rows have been inserted without encryption using the convenience @Insert annotated insert function.
- The 3rd row was inserted with encryption, i.e. using the insertAndEncrypt function, BUT due to the state of the encryptionType field (PLAIN_TYPE) the data was not encrypted.
- The 4th row has been encrypted as the encryptionType is ENCRYPTED and the insertAndEncrypt function was used.