Add editable chars fields #539

This commit is contained in:
J-Jamet
2022-03-28 22:14:24 +02:00
parent a55488846b
commit 32cc57dd03
7 changed files with 89 additions and 47 deletions

View File

@@ -2,6 +2,7 @@ KeePassDX(3.4.0)
* Show visual password strength indicator with entropy #631 #869
* Dynamically save password generator configuration #618
* Add advanced password filters #1052
* Add editable chars fields #539
KeePassDX(3.3.3)
* Fix shared otpauth link if database not open #1274

View File

@@ -57,6 +57,8 @@ class GeneratePasswordDialogFragment : DatabaseDialogFragment() {
private var specialsCompound: CompoundButton? = null
private var bracketsCompound: CompoundButton? = null
private var extendedCompound: CompoundButton? = null
private var considerCharsEditText: EditText? = null
private var ignoreCharsEditText: EditText? = null
private var atLeastOneCompound: CompoundButton? = null
private var excludeAmbiguousCompound: CompoundButton? = null
@@ -104,6 +106,8 @@ class GeneratePasswordDialogFragment : DatabaseDialogFragment() {
specialsCompound = root?.findViewById(R.id.special_filter)
bracketsCompound = root?.findViewById(R.id.brackets_filter)
extendedCompound = root?.findViewById(R.id.extendedASCII_filter)
considerCharsEditText = root?.findViewById(R.id.consider_chars_filter)
ignoreCharsEditText = root?.findViewById(R.id.ignore_chars_filter)
atLeastOneCompound = root?.findViewById(R.id.atLeastOne_filter)
excludeAmbiguousCompound = root?.findViewById(R.id.excludeAmbiguous_filter)
@@ -138,6 +142,12 @@ class GeneratePasswordDialogFragment : DatabaseDialogFragment() {
extendedCompound?.setOnCheckedChangeListener { _, _ ->
fillPassword()
}
considerCharsEditText?.doOnTextChanged { _, _, _, _ ->
fillPassword()
}
ignoreCharsEditText?.doOnTextChanged { _, _, _, _ ->
fillPassword()
}
atLeastOneCompound?.setOnCheckedChangeListener { _, _ ->
fillPassword()
}
@@ -325,6 +335,8 @@ class GeneratePasswordDialogFragment : DatabaseDialogFragment() {
specialsCompound?.isChecked == true,
bracketsCompound?.isChecked == true,
extendedCompound?.isChecked == true,
considerCharsEditText?.text?.toString() ?: "",
ignoreCharsEditText?.text?.toString() ?: "",
atLeastOneCompound?.isChecked == true,
excludeAmbiguousCompound?.isChecked == true)
} catch (e: Exception) {

View File

@@ -37,6 +37,8 @@ class PasswordGenerator(private val resources: Resources) {
specials: Boolean,
brackets: Boolean,
extended: Boolean,
considerChars: String,
ignoreChars: String,
atLeastOneFromEach: Boolean,
excludeAmbiguousChar: Boolean): String {
// Desired password length is 0 or less
@@ -53,83 +55,75 @@ class PasswordGenerator(private val resources: Resources) {
&& !space
&& !specials
&& !brackets
&& !extended) {
&& !extended
&& considerChars.isEmpty()) {
throw IllegalArgumentException(resources.getString(R.string.error_pass_gen_type))
}
// Filter builder
val passwordFilters = PasswordFilters().apply {
this.length = length
this.ignoreChars = ignoreChars
if (excludeAmbiguousChar)
this.ignoreChars += AMBIGUOUS_CHARS
if (upperCase) {
addFilter(
Filter(
if (excludeAmbiguousChar) UPPERCASE_NON_AMBIGUOUS_CHARS else UPPERCASE_CHARS,
UPPERCASE_CHARS,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (lowerCase) {
addFilter(
Filter(
if (excludeAmbiguousChar) LOWERCASE_NON_AMBIGUOUS_CHARS else LOWERCASE_CHARS,
LOWERCASE_CHARS,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (digits) {
addFilter(
Filter(
if (excludeAmbiguousChar) DIGIT_NON_AMBIGUOUS_CHARS else DIGIT_CHARS,
DIGIT_CHARS,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (minus) {
addFilter(
Filter(
MINUS_CHAR,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (underline) {
addFilter(
Filter(
UNDERLINE_CHAR,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (space) {
addFilter(
Filter(
SPACE_CHAR,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (specials) {
addFilter(
Filter(
SPECIAL_CHARS,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (brackets) {
addFilter(
Filter(
BRACKET_CHARS,
if (atLeastOneFromEach) 1 else 0
)
)
}
if (extended) {
addFilter(
Filter(
extendedChars(),
if (atLeastOneFromEach) 1 else 0
)
}
if (considerChars.isNotEmpty()) {
addFilter(
considerChars,
if (atLeastOneFromEach) 1 else 0
)
}
}
@@ -145,21 +139,24 @@ class PasswordGenerator(private val resources: Resources) {
// Build the password.
for (i in 0 until passwordFilters.length) {
val selectableChars: String = if (requiredCharactersLeft < passwordFilters.length - i) {
var selectableChars: String = if (requiredCharactersLeft < passwordFilters.length - i) {
// choose from any group at random
passwordFilters.getSelectableChars()
} else {
// choose only from a group that we need to satisfy a minimum for.
passwordFilters.getSelectableCharsForNeed()
}
passwordFilters.ignoreChars.forEach {
selectableChars = selectableChars.replace(it.toString(), "")
}
// Now that the string is built, get the next random character.
val selectableCharsMaxIndex = selectableChars.length - 1
val selectableCharsMaxIndex = selectableChars.length
val randomSelectableCharsIndex = if (selectableCharsMaxIndex > 0) random.nextInt(selectableCharsMaxIndex) else 0
val nextChar = selectableChars[randomSelectableCharsIndex]
// Put at random position
val randomStringMaxIndex = randomString.length - 1
val randomStringMaxIndex = randomString.length
val randomStringIndex = if (randomStringMaxIndex > 0) random.nextInt(randomStringMaxIndex) else 0
randomString.insert(randomStringIndex, nextChar)
@@ -179,10 +176,11 @@ class PasswordGenerator(private val resources: Resources) {
private class PasswordFilters {
var length: Int = 0
var ignoreChars = ""
val filters = mutableListOf<Filter>()
fun addFilter(filter: Filter) {
filters.add(filter)
fun addFilter(chars: String, minCharsNeeded: Int) {
filters.add(Filter(chars, minCharsNeeded))
}
fun getRequiredCharactersLeft(): Int {
@@ -241,15 +239,13 @@ class PasswordGenerator(private val resources: Resources) {
companion object {
private const val UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
private const val UPPERCASE_NON_AMBIGUOUS_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ"
private const val LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz"
private const val LOWERCASE_NON_AMBIGUOUS_CHARS = "abcdefghjkmnpqrstuvwxyz"
private const val DIGIT_CHARS = "0123456789"
private const val DIGIT_NON_AMBIGUOUS_CHARS = "23456789"
private const val MINUS_CHAR = "-"
private const val UNDERLINE_CHAR = "_"
private const val SPACE_CHAR = " "
private const val SPECIAL_CHARS = "!\"#$%&'*+,./:;=?@\\^`"
private const val BRACKET_CHARS = "[]{}()<>"
private const val AMBIGUOUS_CHARS = "iI|lLoO01"
}
}

View File

@@ -196,6 +196,35 @@
</com.google.android.material.chip.ChipGroup>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/consider_chars_filter_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/ignore_chars_filter_layout"
android:layout_width="0dp"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/consider_chars_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/consider_chars_filter"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/ignore_chars_filter_layout"
app:layout_constraintStart_toEndOf="@+id/consider_chars_filter_layout"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ignore_chars_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/ignore_chars_filter"/>
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.chip.ChipGroup
android:id="@+id/password_advanced"
android:layout_width="match_parent"

View File

@@ -608,6 +608,8 @@
<string name="entropy_calculate">Entropy: Calculate…</string>
<string name="at_least_one_char">At least one character from each</string>
<string name="exclude_ambiguous_chars">Exclude ambiguous characters</string>
<string name="consider_chars_filter">Consider characters</string>
<string name="ignore_chars_filter">Ignore characters</string>
<string-array name="timeout_options">
<item>5 seconds</item>
<item>10 seconds</item>

View File

@@ -1,3 +1,4 @@
* Show visual password strength indicator with entropy #631 #869
* Dynamically save password generator configuration #618
* Add advanced password filters #1052
* Add editable chars fields #539

View File

@@ -1,3 +1,4 @@
* Affichage d'un indicateur visuel de la force du mot de passe avec entropie #631 #869
* Sauvegarde dynamique de la configuration du générateur de mots de passe #618
* Ajout des filtres de mots de passe avancés #1052
* Ajout de champs éditable de génération #539