mirror of
https://github.com/bitwarden/android.git
synced 2024-11-24 10:25:57 +03:00
[PM-12739] adjusted generator length to not be lower than minimum length (#4016)
This commit is contained in:
parent
0c83a1099f
commit
e2e5042be5
3 changed files with 70 additions and 16 deletions
|
@ -507,7 +507,7 @@ private fun ColumnScope.PasswordTypeContent(
|
||||||
BitwardenSlider(
|
BitwardenSlider(
|
||||||
value = passwordTypeState.length,
|
value = passwordTypeState.length,
|
||||||
onValueChange = passwordHandlers.onPasswordSliderLengthChange,
|
onValueChange = passwordHandlers.onPasswordSliderLengthChange,
|
||||||
range = passwordTypeState.minLength..passwordTypeState.maxLength,
|
range = passwordTypeState.computedMinimumLength..passwordTypeState.maxLength,
|
||||||
sliderTag = "PasswordLengthSlider",
|
sliderTag = "PasswordLengthSlider",
|
||||||
valueTag = "PasswordLengthLabel",
|
valueTag = "PasswordLengthLabel",
|
||||||
)
|
)
|
||||||
|
|
|
@ -578,7 +578,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
uppercase = password.useCapitals,
|
uppercase = password.useCapitals,
|
||||||
numbers = password.useNumbers,
|
numbers = password.useNumbers,
|
||||||
special = password.useSpecialChars,
|
special = password.useSpecialChars,
|
||||||
length = password.length.toUByte(),
|
length = max(password.computedMinimumLength, password.length).toUByte(),
|
||||||
avoidAmbiguous = password.avoidAmbiguousChars,
|
avoidAmbiguous = password.avoidAmbiguousChars,
|
||||||
minLowercase = null,
|
minLowercase = null,
|
||||||
minUppercase = null,
|
minUppercase = null,
|
||||||
|
@ -829,7 +829,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
|
|
||||||
updatePasswordType { currentPasswordType ->
|
updatePasswordType { currentPasswordType ->
|
||||||
currentPasswordType.copy(
|
currentPasswordType.copy(
|
||||||
length = max(adjustedLength, currentPasswordType.minimumLength),
|
length = max(adjustedLength, currentPasswordType.computedMinimumLength),
|
||||||
isUserInteracting = action.isUserInteracting,
|
isUserInteracting = action.isUserInteracting,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1477,7 +1477,10 @@ class GeneratorViewModel @Inject constructor(
|
||||||
private fun updatePasswordLength() {
|
private fun updatePasswordLength() {
|
||||||
updatePasswordType { currentPasswordType ->
|
updatePasswordType { currentPasswordType ->
|
||||||
currentPasswordType.copy(
|
currentPasswordType.copy(
|
||||||
length = max(currentPasswordType.length, currentPasswordType.minimumLength),
|
length = max(
|
||||||
|
currentPasswordType.length,
|
||||||
|
currentPasswordType.computedMinimumLength,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1853,6 +1856,22 @@ data class GeneratorState(
|
||||||
override val displayStringResId: Int
|
override val displayStringResId: Int
|
||||||
get() = PasscodeTypeOption.PASSWORD.labelRes
|
get() = PasscodeTypeOption.PASSWORD.labelRes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The computed minimum length for the generated Password
|
||||||
|
* based on what characters must be included.
|
||||||
|
*/
|
||||||
|
val computedMinimumLength: Int
|
||||||
|
get() {
|
||||||
|
val minLowercase = if (useLowercase) 1 else 0
|
||||||
|
val minUppercase = if (useCapitals) 1 else 0
|
||||||
|
val minimumNumbers = if (useNumbers) max(1, minNumbers) else 0
|
||||||
|
val minimumSpecial = if (useSpecialChars) max(1, minSpecial) else 0
|
||||||
|
return max(
|
||||||
|
minLength,
|
||||||
|
minLowercase + minUppercase + minimumNumbers + minimumSpecial,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UndocumentedPublicClass")
|
@Suppress("UndocumentedPublicClass")
|
||||||
companion object {
|
companion object {
|
||||||
private const val DEFAULT_PASSWORD_LENGTH: Int = 14
|
private const val DEFAULT_PASSWORD_LENGTH: Int = 14
|
||||||
|
@ -2625,18 +2644,6 @@ private fun Password.enforceAtLeastOneToggleOn(): Password =
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The computed minimum length for the generated Password based on what characters must be included.
|
|
||||||
*/
|
|
||||||
private val Password.minimumLength: Int
|
|
||||||
get() {
|
|
||||||
val minLowercase = if (useLowercase) 1 else 0
|
|
||||||
val minUppercase = if (useCapitals) 1 else 0
|
|
||||||
val minimumNumbers = if (useNumbers) max(1, minNumbers) else 0
|
|
||||||
val minimumSpecial = if (useSpecialChars) max(1, minSpecial) else 0
|
|
||||||
return minLowercase + minUppercase + minimumNumbers + minimumSpecial
|
|
||||||
}
|
|
||||||
|
|
||||||
private val PasscodeGenerationOptions?.passcodeType: Passcode.PasscodeType
|
private val PasscodeGenerationOptions?.passcodeType: Passcode.PasscodeType
|
||||||
get() = when (this?.type) {
|
get() = when (this?.type) {
|
||||||
PasscodeGenerationOptions.PasscodeType.PASSWORD -> Password()
|
PasscodeGenerationOptions.PasscodeType.PASSWORD -> Password()
|
||||||
|
|
|
@ -2304,6 +2304,53 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Password minimumLength should be at least as long as the sum of the minimums`() {
|
||||||
|
val password =
|
||||||
|
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||||
|
length = 14,
|
||||||
|
minLength = 10,
|
||||||
|
useCapitals = true,
|
||||||
|
capitalsEnabled = false,
|
||||||
|
useLowercase = true,
|
||||||
|
lowercaseEnabled = false,
|
||||||
|
useNumbers = true,
|
||||||
|
numbersEnabled = false,
|
||||||
|
useSpecialChars = true,
|
||||||
|
specialCharsEnabled = false,
|
||||||
|
minNumbers = 9,
|
||||||
|
minNumbersAllowed = 3,
|
||||||
|
minSpecial = 9,
|
||||||
|
minSpecialAllowed = 3,
|
||||||
|
avoidAmbiguousChars = false,
|
||||||
|
)
|
||||||
|
// 9 numbers + 9 special + 1 lowercase + 1 uppercase
|
||||||
|
assertEquals(20, password.computedMinimumLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Password minimumLength should use minLength if higher than sum of the minimums`() {
|
||||||
|
val password =
|
||||||
|
GeneratorState.MainType.Passcode.PasscodeType.Password(
|
||||||
|
length = 14,
|
||||||
|
minLength = 10,
|
||||||
|
useCapitals = true,
|
||||||
|
capitalsEnabled = false,
|
||||||
|
useLowercase = true,
|
||||||
|
lowercaseEnabled = false,
|
||||||
|
useNumbers = true,
|
||||||
|
numbersEnabled = false,
|
||||||
|
useSpecialChars = true,
|
||||||
|
specialCharsEnabled = false,
|
||||||
|
minNumbers = 1,
|
||||||
|
minNumbersAllowed = 3,
|
||||||
|
minSpecial = 1,
|
||||||
|
minSpecialAllowed = 3,
|
||||||
|
avoidAmbiguousChars = false,
|
||||||
|
)
|
||||||
|
assertEquals(10, password.computedMinimumLength)
|
||||||
|
}
|
||||||
//region Helper Functions
|
//region Helper Functions
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
|
|
Loading…
Reference in a new issue