Preferences Encryption - export as JSON

This commit is contained in:
dlvoy 2020-02-21 08:26:58 +01:00 committed by Dominik Dzienia
parent f4b7f642c9
commit fde84207ab
8 changed files with 291 additions and 135 deletions

View file

@ -36,6 +36,7 @@ import info.nightscout.androidaps.plugins.general.automation.actions.*
import info.nightscout.androidaps.plugins.general.automation.elements.*
import info.nightscout.androidaps.plugins.general.automation.triggers.*
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
import info.nightscout.androidaps.plugins.general.smsCommunicator.AuthRequest
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData
@ -249,6 +250,8 @@ open class AppModule {
@ContributesAndroidInjector fun graphDataInjector(): GraphData
@ContributesAndroidInjector fun importExportPrefsInjector(): ImportExportPrefs
@Binds fun bindContext(mainApp: MainApp): Context
@Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector

View file

@ -1,129 +0,0 @@
package info.nightscout.androidaps.plugins.general.maintenance;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Environment;
import androidx.preference.PreferenceManager;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.utils.OKDialog;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.ToastUtils;
/**
* Created by mike on 03.07.2016.
*/
public class ImportExportPrefs {
private static Logger log = StacktraceLoggerWrapper.getLogger(L.CORE);
private static File path = new File(Environment.getExternalStorageDirectory().toString());
static public final File file = new File(path, MainApp.gs(R.string.app_name) + "Preferences");
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
static void verifyStoragePermissions(Fragment fragment) {
int permission = ContextCompat.checkSelfPermission(fragment.getContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
fragment.requestPermissions(PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
}
static void exportSharedPreferences(final Fragment f) {
exportSharedPreferences(f.getContext());
}
private static void exportSharedPreferences(final Context context) {
OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.export_to) + " " + file + " ?", () -> {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
try {
FileWriter fw = new FileWriter(file);
PrintWriter pw = new PrintWriter(fw);
Map<String, ?> prefsMap = prefs.getAll();
for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
pw.println(entry.getKey() + "::" + entry.getValue().toString());
}
pw.close();
fw.close();
ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.exported));
} catch (FileNotFoundException e) {
ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file);
log.error("Unhandled exception", e);
} catch (IOException e) {
log.error("Unhandled exception", e);
}
});
}
static void importSharedPreferences(final Fragment fragment) {
importSharedPreferences(fragment.getContext());
}
public static void importSharedPreferences(final Context context) {
OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.import_from) + " " + file + " ?", () -> {
String line;
String[] lineParts;
try {
SP.clear();
BufferedReader reader = new BufferedReader(new FileReader(file));
while ((line = reader.readLine()) != null) {
lineParts = line.split("::");
if (lineParts.length == 2) {
if (lineParts[1].equals("true") || lineParts[1].equals("false")) {
SP.putBoolean(lineParts[0], Boolean.parseBoolean(lineParts[1]));
} else {
SP.putString(lineParts[0], lineParts[1]);
}
}
}
reader.close();
SP.putBoolean(R.string.key_setupwizard_processed, true);
OKDialog.show(context, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), () -> {
log.debug("Exiting");
RxBus.Companion.getINSTANCE().send(new EventAppExit());
if (context instanceof Activity) {
((Activity) context).finish();
}
System.runFinalization();
System.exit(0);
});
} catch (FileNotFoundException e) {
ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file);
log.error("Unhandled exception", e);
} catch (IOException e) {
log.error("Unhandled exception", e);
}
});
}
}

View file

@ -0,0 +1,142 @@
package info.nightscout.androidaps.plugins.general.maintenance
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Environment
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.maintenance.formats.*
import info.nightscout.androidaps.utils.OKDialog.show
import info.nightscout.androidaps.utils.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton
/**
* Created by mike on 03.07.2016.
*/
private const val REQUEST_EXTERNAL_STORAGE = 1
private val PERMISSIONS_STORAGE = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
@Singleton
class ImportExportPrefs @Inject constructor (
private var log: AAPSLogger,
private val resourceHelper: ResourceHelper,
private val sp : SP,
private val rxBus: RxBusWrapper
)
{
val TAG = LTag.CORE
private val path = File(Environment.getExternalStorageDirectory().toString())
private val file = File(path, resourceHelper.gs(R.string.app_name) + "Preferences")
private val encFile = File(path, resourceHelper.gs(R.string.app_name) + "Preferences.json")
fun prefsImportFile() : File {
return if (encFile.exists()) encFile else file
}
fun prefsFileExists() : Boolean {
return encFile.exists() || file.exists()
}
fun exportSharedPreferences(f: Fragment) {
exportSharedPreferences(f.context)
}
fun verifyStoragePermissions(fragment: Fragment) {
val permission = ContextCompat.checkSelfPermission(fragment.context!!,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
fragment.requestPermissions(PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE)
}
}
private fun exportSharedPreferences(context: Context?) {
showConfirmation(context!!, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.export_to) + " " + encFile + " ?", Runnable {
try {
val entries: MutableMap<String, String> = mutableMapOf()
for ((key, value) in sp.getAll()) {
entries[key] = value.toString()
}
val prefs = Prefs(entries, mapOf())
ClassicPrefsFormat.savePreferences(file, prefs)
EncryptedPrefsFormat.savePreferences(encFile, prefs)
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.exported))
} catch (e: FileNotFoundException) {
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.filenotfound) + " " + encFile)
log.error(TAG,"Unhandled exception", e)
} catch (e: IOException) {
log.error(TAG,"Unhandled exception", e)
}
})
}
fun importSharedPreferences(fragment: Fragment) {
importSharedPreferences(fragment.context)
}
fun importSharedPreferences(context: Context?) {
val importFile = prefsImportFile()
showConfirmation(context!!, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.import_from) + " " + importFile + " ?", Runnable {
val format : PrefsFormat = if (encFile.exists()) EncryptedPrefsFormat else ClassicPrefsFormat
try {
val prefs = format.loadPreferences(importFile)
sp.clear()
for ((key, value) in prefs.values) {
if (value == "true" || value == "false") {
sp.putBoolean(key, value.toBoolean())
} else {
sp.putString(key, value)
}
}
sp.putBoolean(R.string.key_setupwizard_processed, true)
show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable {
log.debug(TAG,"Exiting")
rxBus.send(EventAppExit())
if (context is Activity) {
context.finish()
}
System.runFinalization()
System.exit(0)
})
} catch (e: PrefFileNotFoundError) {
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.filenotfound) + " " + importFile)
log.error(TAG,"Unhandled exception", e)
} catch (e: PrefIOError) {
log.error(TAG,"Unhandled exception", e)
}
})
}
}

View file

@ -23,6 +23,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var importExportPrefs: ImportExportPrefs
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.maintenance_fragment, container, false)
@ -45,13 +46,13 @@ class MaintenanceFragment : DaggerFragment() {
}
nav_export.setOnClickListener {
// start activity for checking permissions...
ImportExportPrefs.verifyStoragePermissions(this)
ImportExportPrefs.exportSharedPreferences(this)
importExportPrefs.verifyStoragePermissions(this)
importExportPrefs.exportSharedPreferences(this)
}
nav_import.setOnClickListener {
// start activity for checking permissions...
ImportExportPrefs.verifyStoragePermissions(this)
ImportExportPrefs.importSharedPreferences(this)
importExportPrefs.verifyStoragePermissions(this)
importExportPrefs.importSharedPreferences(this)
}
nav_logsettings.setOnClickListener { startActivity(Intent(activity, LogSettingActivity::class.java)) }
}

View file

@ -0,0 +1,54 @@
package info.nightscout.androidaps.plugins.general.maintenance.formats
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import java.io.*
object ClassicPrefsFormat : PrefsFormat {
const val FORMAT_KEY = "old"
override fun savePreferences(file:File, prefs: Prefs) {
try {
val fw = FileWriter(file)
val pw = PrintWriter(fw)
for ((key, value) in prefs.values) {
pw.println(key + "::" + value)
}
pw.close()
fw.close()
} catch (e: FileNotFoundException) {
throw PrefFileNotFoundError(file.absolutePath)
} catch (e: IOException) {
throw PrefIOError(file.absolutePath)
}
}
override fun loadPreferences(file:File): Prefs {
var line: String
var lineParts: Array<String>
val entries: MutableMap<String, String> = mutableMapOf()
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
try {
val reader = BufferedReader(FileReader(file))
while (reader.readLine().also { line = it } != null) {
lineParts = line.split("::").toTypedArray()
if (lineParts.size == 2) {
entries[lineParts[0]] = lineParts[1]
}
}
reader.close()
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.WARN)
return Prefs(entries, metadata)
} catch (e: FileNotFoundException) {
throw PrefFileNotFoundError(file.absolutePath)
} catch (e: IOException) {
throw PrefIOError(file.absolutePath)
}
}
}

View file

@ -0,0 +1,58 @@
package info.nightscout.androidaps.plugins.general.maintenance.formats
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
object EncryptedPrefsFormat : PrefsFormat {
const val FORMAT_KEY = "new_v1"
override fun savePreferences(file:File, prefs: Prefs) {
val container = JSONObject()
try {
for ((key, value) in prefs.values) {
container.put(key, value)
}
file.writeText(container.toString(2));
} catch (e: FileNotFoundException) {
throw PrefFileNotFoundError(file.absolutePath)
} catch (e: IOException) {
throw PrefIOError(file.absolutePath)
}
}
override fun loadPreferences(file:File): Prefs {
val entries: MutableMap<String, String> = mutableMapOf()
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
try {
val jsonBody = file.readText()
val container = JSONObject(jsonBody)
for (key in container.keys()) {
entries.put(key, container[key].toString())
}
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.OK)
return Prefs(entries, metadata)
} catch (e: FileNotFoundException) {
throw PrefFileNotFoundError(file.absolutePath)
} catch (e: IOException) {
throw PrefIOError(file.absolutePath)
} catch (e: JSONException){
throw PrefFormatError("Mallformed preferences JSON file: "+e)
}
}
}

View file

@ -0,0 +1,26 @@
package info.nightscout.androidaps.plugins.general.maintenance.formats
import java.io.File
enum class PrefsMetadataKey(val key: String) {
FILE_FORMAT("fileFormat")
}
data class PrefMetadata(var value : String, var status : PrefsStatus)
data class Prefs(val values : Map<String, String>, val metadata : Map<PrefsMetadataKey, PrefMetadata>)
interface PrefsFormat {
fun savePreferences(file: File, prefs: Prefs)
fun loadPreferences(file: File) : Prefs
}
enum class PrefsStatus {
OK,
WARN,
ERROR
}
class PrefFileNotFoundError(message: String) : Exception(message)
class PrefIOError(message: String) : Exception(message)
class PrefFormatError(message: String) : Exception(message)

View file

@ -59,6 +59,7 @@ class SWDefinition @Inject constructor(
private val nsClientPlugin: NSClientPlugin,
private val nsProfilePlugin: NSProfilePlugin,
private val protectionCheck: ProtectionCheck,
private val importExportPrefs: ImportExportPrefs,
private val androidPermission: AndroidPermission
) {
@ -160,8 +161,8 @@ class SWDefinition @Inject constructor(
.add(SWBreak(injector))
.add(SWButton(injector)
.text(R.string.nav_import)
.action(Runnable { ImportExportPrefs.importSharedPreferences(activity) }))
.visibility(SWValidator { ImportExportPrefs.file.exists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) })
.action(Runnable { importExportPrefs.importSharedPreferences(activity) }))
.visibility(SWValidator { importExportPrefs.prefsFileExists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) })
private val screenNsClient = SWScreen(injector, R.string.nsclientinternal_title)
.skippable(true)
.add(SWInfotext(injector)