feat: protection custom PIN

This commit is contained in:
Andries Smit 2022-03-04 17:11:11 +01:00
parent 6b526f1a9c
commit 9ca96901b7
8 changed files with 121 additions and 43 deletions

View file

@ -8,17 +8,17 @@ import android.os.Bundle
import androidx.annotation.XmlRes
import androidx.preference.*
import dagger.android.support.AndroidSupportInjection
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
@ -43,17 +43,12 @@ import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.plugins.source.DexcomPlugin
import info.nightscout.androidaps.plugins.source.EversensePlugin
import info.nightscout.androidaps.plugins.source.GlimpPlugin
import info.nightscout.androidaps.plugins.source.PoctechPlugin
import info.nightscout.androidaps.plugins.source.TomatoPlugin
import info.nightscout.androidaps.plugins.source.GlunovoPlugin
import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.plugins.source.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionType.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.SafeParse
import info.nightscout.shared.sharedPreferences.SP
import javax.inject.Inject
@ -239,7 +234,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
rh.gs(R.string.key_application_protection) == key ||
rh.gs(R.string.key_bolus_protection) == key) &&
sp.getString(R.string.key_master_password, "") == "" &&
sp.getInt(key, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal
sp.getInt(key, NONE.ordinal) == BIOMETRIC.ordinal
) {
activity?.let {
val title = rh.gs(R.string.unsecure_fallback_biometric)
@ -249,9 +244,9 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
}
// Master password erased with activated Biometric protection
val isBiometricActivated = sp.getInt(R.string.key_settings_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
sp.getInt(R.string.key_application_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
sp.getInt(R.string.key_bolus_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal
val isBiometricActivated = sp.getInt(R.string.key_settings_protection, NONE.ordinal) == BIOMETRIC.ordinal ||
sp.getInt(R.string.key_application_protection, NONE.ordinal) == BIOMETRIC.ordinal ||
sp.getInt(R.string.key_bolus_protection, NONE.ordinal) == BIOMETRIC.ordinal
if (rh.gs(R.string.key_master_password) == key && sp.getString(key, "") == "" && isBiometricActivated) {
activity?.let {
val title = rh.gs(R.string.unsecure_fallback_biometric)
@ -322,26 +317,35 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (pref is ListPreference) {
pref.setSummary(pref.entry)
// Preferences
// Preferences
if (pref.getKey() == rh.gs(R.string.key_settings_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_settings_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
pass?.let { it.isVisible = usePassword }
val pin: Preference? = findPreference(rh.gs(R.string.key_settings_pin))
val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
pin?.let { it.isVisible = usePin }
}
// Application
// Application
if (pref.getKey() == rh.gs(R.string.key_application_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_application_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
pass?.let { it.isVisible = usePassword }
val pin: Preference? = findPreference(rh.gs(R.string.key_application_pin))
val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
pin?.let { it.isVisible = usePin }
}
// Bolus
// Bolus
if (pref.getKey() == rh.gs(R.string.key_bolus_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_bolus_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
pass?.let { it.isVisible = usePassword }
val pin: Preference? = findPreference(rh.gs(R.string.key_bolus_pin))
val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
pin?.let { it.isVisible = usePin }
}
}
if (pref is EditTextPreference) {
if (pref.getKey().contains("password") || pref.getKey().contains("secret")) {
if (pref.getKey().contains("password") || pref.getKey().contains("pin") || pref.getKey().contains("secret")) {
pref.setSummary("******")
} else if (pref.text != null) {
pref.dialogMessage = pref.dialogMessage
@ -357,7 +361,10 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
rh.gs(R.string.key_bolus_password),
rh.gs(R.string.key_master_password),
rh.gs(R.string.key_application_password),
rh.gs(R.string.key_settings_password)
rh.gs(R.string.key_settings_password),
rh.gs(R.string.key_bolus_pin),
rh.gs(R.string.key_application_pin),
rh.gs(R.string.key_settings_pin)
)
if (pref is Preference) {
@ -365,7 +372,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (sp.getString(pref.key, "").startsWith("hmac:")) {
pref.summary = "******"
} else {
pref.summary = rh.gs(R.string.password_not_set)
if (pref.getKey().contains("pin")) {
pref.summary = rh.gs(R.string.pin_not_set)
}else {
pref.summary = rh.gs(R.string.password_not_set)
}
}
}
}
@ -411,6 +422,18 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password)
return true
}
if (preference.key == rh.gs(R.string.key_settings_pin)) {
passwordCheck.setPassword(context, R.string.settings_pin, R.string.key_settings_pin, pinInput = true)
return true
}
if (preference.key == rh.gs(R.string.key_bolus_pin)) {
passwordCheck.setPassword(context, R.string.bolus_pin, R.string.key_bolus_pin, pinInput = true)
return true
}
if (preference.key == rh.gs(R.string.key_application_pin)) {
passwordCheck.setPassword(context, R.string.application_pin, R.string.key_application_pin, pinInput = true)
return true
}
// NSClient copy settings
if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) {
nsSettingStatus.copyStatusLightsNsSettings(context)

View file

@ -136,6 +136,7 @@
<item>@string/biometric</item>
<item>@string/master_password</item>
<item>@string/custom_password</item>
<item>@string/custom_pin</item>
</string-array>
<string-array name="protectiontypeValues">
@ -143,6 +144,7 @@
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</string-array>
</resources>

View file

@ -46,10 +46,13 @@
android:title="@string/settings_protection" />
<Preference
android:inputType="textPassword"
android:key="@string/key_settings_password"
android:title="@string/settings_password" />
<Preference
android:key="@string/key_settings_pin"
android:title="@string/settings_pin" />
<ListPreference
android:defaultValue="0"
android:entries="@array/protectiontype"
@ -58,10 +61,13 @@
android:title="@string/application_protection" />
<Preference
android:inputType="textPassword"
android:key="@string/key_application_password"
android:title="@string/application_password" />
<Preference
android:key="@string/key_application_pin"
android:title="@string/application_pin" />
<ListPreference
android:defaultValue="0"
android:entries="@array/protectiontype"
@ -70,10 +76,13 @@
android:title="@string/bolus_protection" />
<Preference
android:inputType="textPassword"
android:key="@string/key_bolus_password"
android:title="@string/bolus_password" />
<Preference
android:key="@string/key_bolus_pin"
android:title="@string/bolus_pin" />
</PreferenceCategory>
<info.nightscout.androidaps.skins.SkinListPreference

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.utils.protection
import android.annotation.SuppressLint
import android.content.Context
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
@ -33,13 +34,12 @@ class PasswordCheck @Inject constructor(
Asks for "managed" kind of password, checking if it is valid.
*/
@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, pinInput: Boolean = false) {
val password = sp.getString(preference, "")
if (password == "") {
ok?.invoke("")
return
}
val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null)
val alertDialogBuilder = AlertDialogHelper.Builder(context, R.style.DialogTheme)
alertDialogBuilder.setView(promptsView)
@ -48,7 +48,10 @@ class PasswordCheck @Inject constructor(
val userInput2 = promptsView.findViewById<View>(R.id.password_prompt_pass_confirm) as EditText
userInput2.visibility = View.GONE
if (pinInput) {
userInput.setHint(R.string.pin_hint)
userInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
}
val autoFillHintPasswordKind = context.getString(preference)
userInput.setAutofillHints(View.AUTOFILL_HINT_PASSWORD, "aaps_${autoFillHintPasswordKind}")
userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
@ -60,7 +63,8 @@ class PasswordCheck @Inject constructor(
val enteredPassword = userInput.text.toString()
if (cryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword)
else {
ToastUtils.errorToast(context, context.getString(R.string.wrongpassword))
val msg = if (pinInput) R.string.wrongpin else R.string.wrongpassword
ToastUtils.errorToast(context, context.getString(msg))
fail?.invoke()
}
}
@ -74,14 +78,19 @@ class PasswordCheck @Inject constructor(
}
@SuppressLint("InflateParams")
fun setPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)? = null, cancel: (() -> Unit)? = null, clear: (() -> Unit)? = null) {
fun setPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)? = null, cancel: (() -> Unit)? = null, clear: (() -> Unit)? = null, pinInput: Boolean = false) {
val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null)
val alertDialogBuilder = AlertDialogHelper.Builder(context, R.style.DialogTheme)
alertDialogBuilder.setView(promptsView)
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
if (pinInput) {
userInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
userInput2.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
userInput.setHint(R.string.pin_hint)
userInput2.setHint(R.string.pin_hint)
}
val autoFillHintPasswordKind = context.getString(preference)
userInput.setAutofillHints(AUTOFILL_HINT_NEW_PASSWORD, "aaps_${autoFillHintPasswordKind}")
userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
@ -93,18 +102,22 @@ class PasswordCheck @Inject constructor(
val enteredPassword = userInput.text.toString()
val enteredPassword2 = userInput2.text.toString()
if (enteredPassword != enteredPassword2) {
ToastUtils.errorToast(context, context.getString(R.string.passwords_dont_match))
val msg = if (pinInput) R.string.pin_dont_match else R.string.passwords_dont_match
ToastUtils.errorToast(context, context.getString(msg))
} else if (enteredPassword.isNotEmpty()) {
sp.putString(preference, cryptoUtil.hashPassword(enteredPassword))
ToastUtils.okToast(context, context.getString(R.string.password_set))
val msg = if (pinInput) R.string.pin_set else R.string.password_set
ToastUtils.okToast(context, context.getString(msg))
ok?.invoke(enteredPassword)
} else {
if (sp.contains(preference)) {
sp.remove(preference)
ToastUtils.graphicalToast(context, context.getString(R.string.password_cleared), R.drawable.ic_toast_delete_confirm)
val msg = if (pinInput) R.string.pin_cleared else R.string.password_cleared
ToastUtils.graphicalToast(context, context.getString(msg), R.drawable.ic_toast_delete_confirm)
clear?.invoke()
} else {
ToastUtils.warnToast(context, context.getString(R.string.password_not_changed))
val msg = if (pinInput) R.string.pin_not_changed else R.string.password_not_changed
ToastUtils.warnToast(context, context.getString(msg))
cancel?.invoke()
}
}
@ -112,7 +125,8 @@ class PasswordCheck @Inject constructor(
}
.setNegativeButton(context.getString(R.string.cancel)
) { dialog, _ ->
ToastUtils.infoToast(context, context.getString(R.string.password_not_changed))
val msg = if (pinInput) R.string.pin_not_changed else R.string.password_not_changed
ToastUtils.infoToast(context, context.getString(msg))
cancel?.invoke()
dialog.cancel()
}

View file

@ -22,7 +22,8 @@ class ProtectionCheck @Inject constructor(
NONE,
BIOMETRIC,
MASTER_PASSWORD,
CUSTOM_PASSWORD
CUSTOM_PASSWORD,
CUSTOM_PIN
}
private val passwordsResourceIDs = listOf(
@ -30,22 +31,33 @@ class ProtectionCheck @Inject constructor(
R.string.key_application_password,
R.string.key_bolus_password)
private val pinsResourceIDs = listOf(
R.string.key_settings_pin,
R.string.key_application_pin,
R.string.key_bolus_pin)
private val protectionTypeResourceIDs = listOf(
R.string.key_settings_protection,
R.string.key_application_protection,
R.string.key_bolus_protection)
private val titleResourceIDs = listOf(
private val titlePassResourceIDs = listOf(
R.string.settings_password,
R.string.application_password,
R.string.bolus_password)
private val titlePinResourceIDs = listOf(
R.string.settings_pin,
R.string.application_pin,
R.string.bolus_pin)
fun isLocked(protection: Protection): Boolean {
return when (ProtectionType.values()[sp.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) {
ProtectionType.NONE -> false
ProtectionType.BIOMETRIC -> true
ProtectionType.MASTER_PASSWORD -> sp.getString(R.string.key_master_password, "") != ""
ProtectionType.CUSTOM_PASSWORD -> sp.getString(passwordsResourceIDs[protection.ordinal], "") != ""
ProtectionType.CUSTOM_PIN -> sp.getString(pinsResourceIDs[protection.ordinal], "") != ""
}
}
@ -55,11 +67,13 @@ class ProtectionCheck @Inject constructor(
ProtectionType.NONE ->
ok?.run()
ProtectionType.BIOMETRIC ->
BiometricCheck.biometricPrompt(activity, titleResourceIDs[protection.ordinal], ok, cancel, fail, passwordCheck)
BiometricCheck.biometricPrompt(activity, titlePassResourceIDs[protection.ordinal], ok, cancel, fail, passwordCheck)
ProtectionType.MASTER_PASSWORD ->
passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { ok?.run() }, { cancel?.run() }, { fail?.run() })
ProtectionType.CUSTOM_PASSWORD ->
passwordCheck.queryPassword(activity, titleResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() })
passwordCheck.queryPassword(activity, titlePassResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() })
ProtectionType.CUSTOM_PIN ->
passwordCheck.queryPassword(activity, titlePinResourceIDs[protection.ordinal], pinsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() }, true)
}
}
}

View file

@ -2,8 +2,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:padding="10dp">
android:padding="10dp"
tools:context=".utils.protection.PasswordCheck">
<TextView
android:id="@+id/password_prompt_extra_message"

View file

@ -7,11 +7,15 @@
<string name="bolus_protection">Bolus protection</string>
<string name="master_password">Master password</string>
<string name="settings_password">Settings password</string>
<string name="settings_pin">Settings PIN</string>
<string name="application_password">Application password</string>
<string name="application_pin">Application PIN</string>
<string name="bolus_password">Bolus password</string>
<string name="bolus_pin">Bolus PIN</string>
<string name="unlock_settings">Unlock settings</string>
<string name="biometric">Biometric</string>
<string name="custom_password">Custom password</string>
<string name="custom_pin">Custom PIN</string>
<string name="noprotection">No protection</string>
<string name="protection">Protection</string>
<string name="master_password_missing">Master password is not set!\n\nPlease set your Master password in Preferences (%1$s &#8594; %2$s)</string>
@ -19,15 +23,23 @@
<string name="unsecure_fallback_descriotion_biometric">In order to be effective, biometric protection needs a master password set for fallback.\n\nPlease set a master password!</string>
<string name="password_set">Password set!</string>
<string name="pin_set">PIN set!</string>
<string name="password_not_set">Password not set</string>
<string name="pin_not_set">PIN not set</string>
<string name="password_not_changed">Password not changed</string>
<string name="pin_not_changed">PIN not changed</string>
<string name="password_cleared">Password cleared!</string>
<string name="pin_cleared">PIN cleared!</string>
<string name="password_hint">Enter password here</string>
<string name="pin_hint">Enter PIN here</string>
<string name="key_master_password" translatable="false">master_password</string>
<string name="key_settings_password" translatable="false">settings_password</string>
<string name="key_settings_pin" translatable="false">settings_pin</string>
<string name="key_application_password" translatable="false">application_password</string>
<string name="key_application_pin" translatable="false">application_pin</string>
<string name="key_bolus_password" translatable="false">bolus_password</string>
<string name="key_bolus_pin" translatable="false">bolus_pin</string>
<string name="key_settings_protection" translatable="false">settings_protection</string>
<string name="key_application_protection" translatable="false">application_protection</string>
<string name="key_bolus_protection" translatable="false">bolus_protection</string>

View file

@ -222,7 +222,9 @@
<!-- Protection-->
<string name="wrongpassword">Wrong password</string>
<string name="wrongpin">Wrong PIN</string>
<string name="passwords_dont_match">Passwords don\'t match</string>
<string name="pin_dont_match">PINs don\'t match</string>
<!-- Profile-->
<string name="basalprofilenotaligned">Basal values not aligned to hours: %1$s</string>