Merge pull request #2948 from dlvoy/changed-masterpwd

Add additional prompt for old master password
This commit is contained in:
Milos Kozak 2020-09-04 08:41:43 +02:00 committed by GitHub
commit 8271f1fc64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 20 deletions

View file

@ -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,11 +289,13 @@ 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)
promptForDecryptionPasswordIfNeeded(activity, prefsAttempted, importOkAttempted, format, importFile) { prefs, importOk ->
// if at end we allow to import preferences // if at end we allow to import preferences
val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0) val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0)
@ -285,6 +318,8 @@ class ImportExportPrefs @Inject constructor(
} }
}) })
}
} catch (e: PrefFileNotFoundError) { } catch (e: PrefFileNotFoundError) {
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + importFile) ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + importFile)
log.error(TAG, "Unhandled exception", e) log.error(TAG, "Unhandled exception", e)

View file

@ -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>

View file

@ -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()
}
} }

View file

@ -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"

View file

@ -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>