Add additional prompt for old master password
Prompt for old master password (used to encrypt exported preferences) if current master password is different and cannot decrypt file.
This commit is contained in:
parent
3457f8f511
commit
651c3fc426
5 changed files with 122 additions and 20 deletions
|
@ -130,6 +130,15 @@ class ImportExportPrefs @Inject constructor(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun askForEncryptionPass(activity: Activity, @StringRes canceledMsg: Int, @StringRes passwordName: Int, @StringRes passwordExplanation: Int?,
|
||||||
|
@StringRes passwordWarning: Int?, then: ((password: String) -> Unit)) {
|
||||||
|
passwordCheck.queryAnyPassword(activity, passwordName, R.string.key_master_password, passwordExplanation, passwordWarning, { password ->
|
||||||
|
then(password)
|
||||||
|
}, {
|
||||||
|
ToastUtils.warnToast(activity, resourceHelper.gs(canceledMsg))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private fun askForMasterPassIfNeeded(activity: Activity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
|
private fun askForMasterPassIfNeeded(activity: Activity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
|
||||||
if (prefsEncryptionIsDisabled()) {
|
if (prefsEncryptionIsDisabled()) {
|
||||||
then("")
|
then("")
|
||||||
|
@ -182,6 +191,28 @@ class ImportExportPrefs @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun promptForDecryptionPasswordIfNeeded(activity: Activity, prefs: Prefs, importOk: Boolean,
|
||||||
|
format: PrefsFormat, importFile: PrefsFile, then: ((prefs: Prefs, importOk: Boolean) -> Unit)) {
|
||||||
|
|
||||||
|
// current master password was not the one used for decryption, so we prompt for old password...
|
||||||
|
if (!importOk && (prefs.metadata[PrefsMetadataKey.ENCRYPTION]?.status == PrefsStatus.ERROR)) {
|
||||||
|
askForEncryptionPass(activity, R.string.preferences_import_canceled, R.string.old_master_password,
|
||||||
|
R.string.different_password_used, R.string.master_password_will_be_replaced) { password ->
|
||||||
|
|
||||||
|
// ...and use it to load & decrypt file again
|
||||||
|
val prefsReloaded = format.loadPreferences(importFile.file, password)
|
||||||
|
prefsReloaded.metadata = prefFileList.checkMetadata(prefsReloaded.metadata)
|
||||||
|
|
||||||
|
// import is OK when we do not have errors (warnings are allowed)
|
||||||
|
val importOkCheckedAgain = checkIfImportIsOk(prefsReloaded)
|
||||||
|
|
||||||
|
then(prefsReloaded, importOkCheckedAgain);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
then(prefs, importOk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun exportSharedPreferences(activity: Activity) {
|
private fun exportSharedPreferences(activity: Activity) {
|
||||||
|
|
||||||
prefFileList.ensureExportDirExists()
|
prefFileList.ensureExportDirExists()
|
||||||
|
@ -258,32 +289,36 @@ class ImportExportPrefs @Inject constructor(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
val prefs = format.loadPreferences(importFile.file, password)
|
val prefsAttempted = format.loadPreferences(importFile.file, password)
|
||||||
prefs.metadata = prefFileList.checkMetadata(prefs.metadata)
|
prefsAttempted.metadata = prefFileList.checkMetadata(prefsAttempted.metadata)
|
||||||
|
|
||||||
// import is OK when we do not have errors (warnings are allowed)
|
// import is OK when we do not have errors (warnings are allowed)
|
||||||
val importOk = checkIfImportIsOk(prefs)
|
val importOkAttempted = checkIfImportIsOk(prefsAttempted)
|
||||||
|
|
||||||
// if at end we allow to import preferences
|
promptForDecryptionPasswordIfNeeded(activity, prefsAttempted, importOkAttempted, format, importFile) { prefs, importOk ->
|
||||||
val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0)
|
|
||||||
|
|
||||||
PrefImportSummaryDialog.showSummary(activity, importOk, importPossible, prefs, {
|
// if at end we allow to import preferences
|
||||||
if (importPossible) {
|
val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0)
|
||||||
sp.clear()
|
|
||||||
for ((key, value) in prefs.values) {
|
PrefImportSummaryDialog.showSummary(activity, importOk, importPossible, prefs, {
|
||||||
if (value == "true" || value == "false") {
|
if (importPossible) {
|
||||||
sp.putBoolean(key, value.toBoolean())
|
sp.clear()
|
||||||
} else {
|
for ((key, value) in prefs.values) {
|
||||||
sp.putString(key, value)
|
if (value == "true" || value == "false") {
|
||||||
|
sp.putBoolean(key, value.toBoolean())
|
||||||
|
} else {
|
||||||
|
sp.putString(key, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
restartAppAfterImport(activity)
|
restartAppAfterImport(activity)
|
||||||
} else {
|
} else {
|
||||||
// for impossible imports it should not be called
|
// for impossible imports it should not be called
|
||||||
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.preferences_import_impossible))
|
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.preferences_import_impossible))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e: PrefFileNotFoundError) {
|
} catch (e: PrefFileNotFoundError) {
|
||||||
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + importFile)
|
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + importFile)
|
||||||
|
|
|
@ -208,6 +208,9 @@
|
||||||
<string name="preferences_import_canceled">Import canceled! Preferences were NOT imported!</string>
|
<string name="preferences_import_canceled">Import canceled! Preferences were NOT imported!</string>
|
||||||
<string name="preferences_import_impossible">Cannot import preferences!</string>
|
<string name="preferences_import_impossible">Cannot import preferences!</string>
|
||||||
<string name="goto_main_try_again">Please go back to main screen and try again.</string>
|
<string name="goto_main_try_again">Please go back to main screen and try again.</string>
|
||||||
|
<string name="old_master_password">Old Master Password</string>
|
||||||
|
<string name="different_password_used">This file was exported and encrypted with different master password. Provide old master password to decrypt file.</string>
|
||||||
|
<string name="master_password_will_be_replaced">As a result of successful import current master password WILL BE REPLACED with that old master password!</string>
|
||||||
|
|
||||||
<string name="preferences_import_list_title">Select file to import</string>
|
<string name="preferences_import_list_title">Select file to import</string>
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.os.Build
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import info.nightscout.androidaps.core.R
|
import info.nightscout.androidaps.core.R
|
||||||
import info.nightscout.androidaps.utils.CryptoUtil
|
import info.nightscout.androidaps.utils.CryptoUtil
|
||||||
|
@ -24,6 +25,9 @@ class PasswordCheck @Inject constructor(
|
||||||
private val cryptoUtil: CryptoUtil
|
private val cryptoUtil: CryptoUtil
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asks for "managed" kind of password, checking if it is valid.
|
||||||
|
*/
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
fun queryPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)?, cancel: (() -> Unit)? = null, fail: (() -> Unit)? = null) {
|
fun queryPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)?, cancel: (() -> Unit)? = null, fail: (() -> Unit)? = null) {
|
||||||
val password = sp.getString(preference, "")
|
val password = sp.getString(preference, "")
|
||||||
|
@ -115,4 +119,51 @@ class PasswordCheck @Inject constructor(
|
||||||
|
|
||||||
alertDialogBuilder.create().show()
|
alertDialogBuilder.create().show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Prompt free-form password, with additional help and warning messages.
|
||||||
|
Preference ID (preference) is used only to generate ID for password managers,
|
||||||
|
since this query does NOT check validity of password.
|
||||||
|
*/
|
||||||
|
@SuppressLint("InflateParams")
|
||||||
|
fun queryAnyPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, @StringRes passwordExplanation: Int?,
|
||||||
|
@StringRes passwordWarning: Int?, ok: ((String) -> Unit)?, cancel: (() -> Unit)? = null) {
|
||||||
|
|
||||||
|
val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null)
|
||||||
|
val alertDialogBuilder = AlertDialogHelper.Builder(context)
|
||||||
|
alertDialogBuilder.setView(promptsView)
|
||||||
|
passwordExplanation?.let { alertDialogBuilder.setMessage(it) }
|
||||||
|
|
||||||
|
passwordWarning?.let {
|
||||||
|
val extraWarning: TextView = promptsView.findViewById<View>(R.id.password_prompt_extra_message) as TextView;
|
||||||
|
extraWarning.text = context.getString(it);
|
||||||
|
extraWarning.visibility = View.VISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
val userInput = promptsView.findViewById<View>(R.id.password_prompt_pass) as EditText
|
||||||
|
val userInput2 = promptsView.findViewById<View>(R.id.password_prompt_pass_confirm) as EditText
|
||||||
|
|
||||||
|
userInput2.visibility = View.GONE
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val autoFillHintPasswordKind = context.getString(preference)
|
||||||
|
userInput.setAutofillHints(View.AUTOFILL_HINT_PASSWORD, "aaps_${autoFillHintPasswordKind}")
|
||||||
|
userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
|
||||||
|
}
|
||||||
|
|
||||||
|
alertDialogBuilder
|
||||||
|
.setCancelable(false)
|
||||||
|
.setCustomTitle(AlertDialogHelper.buildCustomTitle(context, context.getString(labelId), R.drawable.ic_header_key))
|
||||||
|
.setPositiveButton(context.getString(R.string.ok)) { _, _ ->
|
||||||
|
val enteredPassword = userInput.text.toString()
|
||||||
|
ok?.invoke(enteredPassword)
|
||||||
|
}
|
||||||
|
.setNegativeButton(context.getString(R.string.cancel)
|
||||||
|
) { dialog, _ ->
|
||||||
|
cancel?.invoke()
|
||||||
|
dialog.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
alertDialogBuilder.create().show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,18 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="10dp">
|
android:padding="10dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/password_prompt_extra_message"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="@color/warningAccentText"
|
||||||
|
android:visibility="gone"
|
||||||
|
/>
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/password_prompt_pass"
|
android:id="@+id/password_prompt_pass"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
<color name="dialog_title_icon_tint">#FFFFFF</color>
|
<color name="dialog_title_icon_tint">#FFFFFF</color>
|
||||||
<color name="warningAlertBackground">#FFFB8C00</color>
|
<color name="warningAlertBackground">#FFFB8C00</color>
|
||||||
<color name="warningAlertHeaderText">#FF000000</color>
|
<color name="warningAlertHeaderText">#FF000000</color>
|
||||||
|
<color name="warningAccentText">#FFFB8C00</color>
|
||||||
<color name="errorAlertBackground">#FFFF5555</color>
|
<color name="errorAlertBackground">#FFFF5555</color>
|
||||||
<color name="errorAlertHeaderText">#FF000000</color>
|
<color name="errorAlertHeaderText">#FF000000</color>
|
||||||
<color name="examinedProfile">#FFFF5555</color>
|
<color name="examinedProfile">#FFFF5555</color>
|
||||||
|
|
Loading…
Reference in a new issue