Skip to content

Add ability to enter DB password on runtime #238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,6 @@ If you are using `build.gradle.kts`, add the following:
debugImplementation("com.github.amitshekhariitbhu.Android-Debug-Database:debug-db-encrypt:1.0.7")
```

And to provide the password for the DB, you should add this in the Gradle:
DB_PASSWORD_{VARIABLE}, if for example, PERSON is the database name: DB_PASSWORD_PERSON
```groovy
debug {
resValue("string", "DB_PASSWORD_PERSON", "password")
}
```

Use `debugImplementation` so that it will only compile in your debug build and not in your release build.

That’s all, just start the application, you will see in the logcat an entry like follows :
Expand Down Expand Up @@ -147,11 +139,11 @@ public static void setCustomDatabaseFiles(Context context) {
Class<?> debugDB = Class.forName("com.amitshekhar.DebugDB");
Class[] argTypes = new Class[]{HashMap.class};
Method setCustomDatabaseFiles = debugDB.getMethod("setCustomDatabaseFiles", argTypes);
HashMap<String, Pair<File, String>> customDatabaseFiles = new HashMap<>();
HashMap<String, File> customDatabaseFiles = new HashMap<>();
// set your custom database files
customDatabaseFiles.put(ExtTestDBHelper.DATABASE_NAME,
new Pair<>(new File(context.getFilesDir() + "/" + ExtTestDBHelper.DIR_NAME +
"/" + ExtTestDBHelper.DATABASE_NAME), ""));
new File(context.getFilesDir() + "/" + ExtTestDBHelper.DIR_NAME +
"/" + ExtTestDBHelper.DATABASE_NAME));
setCustomDatabaseFiles.invoke(null, customDatabaseFiles);
} catch (Exception ignore) {

Expand Down
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
compileSdk = 33
targetSdk = 33
minSdk = 14
}
compileSdk = 34
targetSdk = 34
minSdk = 21
}
12 changes: 10 additions & 2 deletions debug-db-base/src/main/assets/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ function getDBList() {
$.ajax({url: "getDbList", success: function(result){

result = JSON.parse(result);
if (!result.supportEncryptedDb) {
$('#password').hide();
$('#password-label').hide();
}
var dbList = result.rows;
$('#db-list').empty();
var isSelectionDone = false;
Expand Down Expand Up @@ -130,14 +134,18 @@ function openDatabaseAndGetTableList(db, isDownloadable) {
isDatabaseSelected = true;
}


$.ajax({url: "getTableList?database="+db, success: function(result){
var password = $('#password').val();
$.ajax({url: "getTableList?database="+db+"&password="+password, success: function(result){

result = JSON.parse(result);
var tableList = result.rows;
var dbVersion = result.dbVersion;
var error = result.error;
if("APP_SHARED_PREFERENCES" != db) {
if (error == null)
$("#selected-db-info").text("Selected Database : "+db +" Version : "+dbVersion);
else
$("#selected-db-info").text(error);
}
$('#table-list').empty()
for(var count = 0; count < tableList.length; count++){
Expand Down
2 changes: 2 additions & 0 deletions debug-db-base/src/main/assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
<div class="row padding-twenty">
<div class="col-sm-12">
<div class="form-group">
<label for="password" id="password-label">DB password</label>
<input class="form-control" id="password">
<label for="query">Query</label>
<input class="form-control" id="query">
</div>
Expand Down
2 changes: 1 addition & 1 deletion debug-db-base/src/main/java/com/amitshekhar/DebugDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static void shutDown() {
}
}

public static void setCustomDatabaseFiles(HashMap<String, Pair<File, String>> customDatabaseFiles) {
public static void setCustomDatabaseFiles(HashMap<String, File> customDatabaseFiles) {
if (clientServer != null) {
clientServer.setCustomDatabaseFiles(customDatabaseFiles);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class Response {
public boolean isSuccessful;
public String error;
public int dbVersion;
public boolean supportEncryptedDb;

public Response() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void run() {
}
}

public void setCustomDatabaseFiles(HashMap<String, Pair<File, String>> customDatabaseFiles) {
public void setCustomDatabaseFiles(HashMap<String, File> customDatabaseFiles) {
mRequestHandler.setCustomDatabaseFiles(customDatabaseFiles);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import android.content.res.AssetManager;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Pair;

import androidx.sqlite.db.SupportSQLiteDatabase;

Expand Down Expand Up @@ -65,8 +64,8 @@ public class RequestHandler {
private final DBFactory mDbFactory;
private boolean isDbOpened;
private SQLiteDB sqLiteDB;
private HashMap<String, Pair<File, String>> mDatabaseFiles;
private HashMap<String, Pair<File, String>> mCustomDatabaseFiles;
private HashMap<String, File> mDatabaseFiles;
private HashMap<String, File> mCustomDatabaseFiles;
private String mSelectedDatabase = null;
private HashMap<String, SupportSQLiteDatabase> mRoomInMemoryDatabases = new HashMap<>();

Expand Down Expand Up @@ -165,7 +164,7 @@ public void handle(Socket socket) throws IOException {
}
}

public void setCustomDatabaseFiles(HashMap<String, Pair<File, String>> customDatabaseFiles) {
public void setCustomDatabaseFiles(HashMap<String, File> customDatabaseFiles) {
mCustomDatabaseFiles = customDatabaseFiles;
}

Expand All @@ -178,13 +177,12 @@ private void writeServerError(PrintStream output) {
output.flush();
}

private void openDatabase(String database) {
private void openDatabase(String database, String password) {
closeDatabase();
if (mRoomInMemoryDatabases.containsKey(database)) {
sqLiteDB = new InMemoryDebugSQLiteDB(mRoomInMemoryDatabases.get(database));
} else {
File databaseFile = mDatabaseFiles.get(database).first;
String password = mDatabaseFiles.get(database).second;
File databaseFile = mDatabaseFiles.get(database);
sqLiteDB = mDbFactory.create(mContext, databaseFile.getAbsolutePath(), password);
}
isDbOpened = true;
Expand All @@ -205,8 +203,10 @@ private String getDBListResponse() {
}
Response response = new Response();
if (mDatabaseFiles != null) {
for (HashMap.Entry<String, Pair<File, String>> entry : mDatabaseFiles.entrySet()) {
String[] dbEntry = {entry.getKey(), !entry.getValue().second.equals("") ? "true" : "false", "true"};
for (HashMap.Entry<String, File> entry : mDatabaseFiles.entrySet()) {
String[] dbEntry = { entry.getKey(),
Utils.isDbEncrypted(entry.getKey(), mDatabaseFiles) ? "true" : "false",
"true" };
response.rows.add(dbEntry);
}
}
Expand All @@ -218,6 +218,7 @@ private String getDBListResponse() {
}
response.rows.add(new String[]{Constants.APP_SHARED_PREFERENCES, "false", "false"});
response.isSuccessful = true;
response.supportEncryptedDb = mDbFactory.supportEncryptedDb();
return mGson.toJson(response);
}

Expand Down Expand Up @@ -292,10 +293,9 @@ private String executeQueryAndGetResponse(String route) {
}

private String getTableListResponse(String route) {
String database = null;
if (route.contains("?database=")) {
database = route.substring(route.indexOf("=") + 1, route.length());
}
Uri uri = Uri.parse(route);
String database = uri.getQueryParameter("database");
String password = uri.getQueryParameter("password");

Response response;

Expand All @@ -304,8 +304,19 @@ private String getTableListResponse(String route) {
closeDatabase();
mSelectedDatabase = Constants.APP_SHARED_PREFERENCES;
} else {
openDatabase(database);
response = DatabaseHelper.getAllTableName(sqLiteDB);
try {
if (Utils.isDbEncrypted(database, mDatabaseFiles)) {
openDatabase(database, password);
} else {
openDatabase(database, null);
}
response = DatabaseHelper.getAllTableName(sqLiteDB);
} catch (Exception e) {
response = new Response();
response.isSuccessful = false;
response.dbVersion = 0;
response.error = e.toString();
}
mSelectedDatabase = database;
}
return mGson.toJson(response);
Expand Down Expand Up @@ -390,7 +401,7 @@ private String deleteSelectedDatabaseAndGetResponse() {
try {
closeDatabase();

File dbFile = mDatabaseFiles.get(mSelectedDatabase).first;
File dbFile = mDatabaseFiles.get(mSelectedDatabase);
response.isSuccessful = dbFile.delete();

if (response.isSuccessful) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ public interface DBFactory {

SQLiteDB create(Context context, String path, String password);

boolean supportEncryptedDb();

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@
package com.amitshekhar.utils;

import android.content.Context;
import android.util.Pair;

import java.io.File;
import java.text.MessageFormat;
import java.util.HashMap;

/**
Expand All @@ -32,39 +30,19 @@

public class DatabaseFileProvider {

private final static String DB_PASSWORD_RESOURCE = "DB_PASSWORD_{0}";

private DatabaseFileProvider() {
// This class in not publicly instantiable
}

public static HashMap<String, Pair<File, String>> getDatabaseFiles(Context context) {
HashMap<String, Pair<File, String>> databaseFiles = new HashMap<>();
public static HashMap<String, File> getDatabaseFiles(Context context) {
HashMap<String, File> databaseFiles = new HashMap<>();
try {
for (String databaseName : context.databaseList()) {
String password = getDbPasswordFromStringResources(context, databaseName);
databaseFiles.put(databaseName, new Pair<>(context.getDatabasePath(databaseName), password));
databaseFiles.put(databaseName, context.getDatabasePath(databaseName));
}
} catch (Exception e) {
e.printStackTrace();
}
return databaseFiles;
}

private static String getDbPasswordFromStringResources(Context context, String name) {
String nameWithoutExt = name;
if (nameWithoutExt.endsWith(".db")) {
nameWithoutExt = nameWithoutExt.substring(0, nameWithoutExt.lastIndexOf('.'));
}
String resourceName = MessageFormat.format(DB_PASSWORD_RESOURCE, nameWithoutExt.toUpperCase());
String password = "";

int resourceId = context.getResources().getIdentifier(resourceName, "string", context.getPackageName());

if (resourceId != 0) {
password = context.getString(resourceId);
}

return password;
}
}
46 changes: 27 additions & 19 deletions debug-db-base/src/main/java/com/amitshekhar/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@

import android.content.res.AssetManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
* Created by amitshekhar on 06/02/17.
Expand Down Expand Up @@ -83,35 +82,44 @@ public static byte[] loadContent(String fileName, AssetManager assetManager) thr
}
}

public static byte[] getDatabase(String selectedDatabase, HashMap<String, Pair<File, String>> databaseFiles) {
public static byte[] getDatabase(String selectedDatabase, Map<String, File> databaseFiles) {
if (TextUtils.isEmpty(selectedDatabase) || !databaseFiles.containsKey(selectedDatabase)) {
return null;
}

byte[] byteArray = new byte[0];
try {
File file = databaseFiles.get(selectedDatabase).first;
File file = databaseFiles.get(selectedDatabase);

byteArray = null;
try {
InputStream inputStream = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[(int) file.length()];
int bytesRead;
try (InputStream is = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] b = new byte[(int) file.length()];
int bytesRead;

while ((bytesRead = inputStream.read(b)) != -1) {
bos.write(b, 0, bytesRead);
}

byteArray = bos.toByteArray();
} catch (IOException e) {
Log.e(TAG, "getDatabase: ", e);
while ((bytesRead = is.read(b)) != -1) {
bos.write(b, 0, bytesRead);
}

byteArray = bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}

return byteArray;
}

public static boolean isDbEncrypted(String database, Map<String, File> databaseFiles) {
if (TextUtils.isEmpty(database) || !databaseFiles.containsKey(database)) {
return false;
}
File file = databaseFiles.get(database);
try (DataInputStream is = new DataInputStream(new FileInputStream(file))) {
byte[] b = new byte[16];
is.readFully(b);
String string = new String(b);
return !"SQLite format 3\000".equals(string);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
5 changes: 3 additions & 2 deletions debug-db-encrypt/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ android {

dependencies {
api project(':debug-db-base')
implementation 'net.zetetic:android-database-sqlcipher:3.5.9'
implementation 'androidx.sqlite:sqlite:2.4.0'
implementation 'net.zetetic:sqlcipher-android:4.6.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

import com.amitshekhar.sqlite.DBFactory;
import com.amitshekhar.sqlite.SQLiteDB;

import net.sqlcipher.database.SQLiteDatabase;
import net.zetetic.database.sqlcipher.SQLiteDatabase;

public class DebugDBEncryptFactory implements DBFactory {

static {
System.loadLibrary("sqlcipher");
}

@Override
public SQLiteDB create(Context context, String path, String password) {
SQLiteDatabase.loadLibs(context);
return new DebugEncryptSQLiteDB(SQLiteDatabase.openOrCreateDatabase(path, password, null));
return new DebugEncryptSQLiteDB(SQLiteDatabase.openOrCreateDatabase(path, password, null, null));
}

@Override
public boolean supportEncryptedDb() {
return true;
}
}
Loading