Merge pull request #2948 from dlvoy/changed-masterpwd
Add additional prompt for old master password
This commit is contained in:
commit
8271f1fc64
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