mirror of
https://github.com/bitwarden/android.git
synced 2026-03-24 15:21:42 -05:00
Enforce Base32 key values (#82)
This commit is contained in:
@@ -40,6 +40,7 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@@ -255,6 +256,7 @@ fun EditItemContent(
|
||||
value = viewState.itemData.totpCode,
|
||||
onValueChange = onTotpCodeTextChange,
|
||||
singleLine = true,
|
||||
capitalization = KeyboardCapitalization.Characters,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.bitwarden.authenticator.ui.authenticator.feature.edititem
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.toUpperCase
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.authenticator.R
|
||||
@@ -18,6 +20,7 @@ import com.bitwarden.authenticator.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.Text
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.asText
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.concat
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.isBase32
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -93,7 +96,17 @@ class EditItemViewModel @Inject constructor(
|
||||
it.copy(
|
||||
dialog = EditItemState.DialogState.Generic(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.validation_field_required.asText(R.string.secret_key),
|
||||
message = R.string.validation_field_required.asText(R.string.key),
|
||||
)
|
||||
)
|
||||
}
|
||||
return@onContent
|
||||
} else if (!content.itemData.totpCode.isBase32()) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = EditItemState.DialogState.Generic(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = R.string.key_is_invalid.asText()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -320,7 +333,7 @@ class EditItemViewModel @Inject constructor(
|
||||
itemData = EditItemData(
|
||||
refreshPeriod = AuthenticatorRefreshPeriodOption.fromSeconds(period)
|
||||
?: AuthenticatorRefreshPeriodOption.THIRTY,
|
||||
totpCode = key,
|
||||
totpCode = key.toUpperCase(Locale.current),
|
||||
type = type,
|
||||
username = accountName,
|
||||
issuer = issuer,
|
||||
|
||||
@@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -40,6 +41,7 @@ import com.bitwarden.authenticator.ui.platform.components.dialog.BitwardenBasicD
|
||||
import com.bitwarden.authenticator.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.bitwarden.authenticator.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.bitwarden.authenticator.ui.platform.components.dialog.LoadingDialogState
|
||||
import com.bitwarden.authenticator.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.bitwarden.authenticator.ui.platform.components.field.BitwardenTextField
|
||||
import com.bitwarden.authenticator.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.bitwarden.authenticator.ui.platform.manager.intent.IntentManager
|
||||
@@ -177,7 +179,7 @@ fun ManualCodeEntryScreen(
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
BitwardenPasswordField(
|
||||
singleLine = false,
|
||||
label = stringResource(id = R.string.key),
|
||||
value = state.code,
|
||||
@@ -188,6 +190,7 @@ fun ManualCodeEntryScreen(
|
||||
)
|
||||
}
|
||||
},
|
||||
capitalization = KeyboardCapitalization.Characters,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.bitwarden.authenticator.data.authenticator.repository.model.CreateIte
|
||||
import com.bitwarden.authenticator.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.Text
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.asText
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.isBase32
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -82,6 +83,17 @@ class ManualCodeEntryViewModel @Inject constructor(
|
||||
return
|
||||
}
|
||||
|
||||
if (!state.code.isBase32()) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = ManualCodeEntryState.DialogState.Error(
|
||||
message = R.string.key_is_invalid.asText()
|
||||
)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (state.issuer.isBlank()) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
@@ -92,6 +104,7 @@ class ManualCodeEntryViewModel @Inject constructor(
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
val result = authenticatorRepository.createItem(
|
||||
AuthenticatorItemEntity(
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.bitwarden.authenticator.data.authenticator.repository.AuthenticatorRe
|
||||
import com.bitwarden.authenticator.data.authenticator.repository.model.TotpCodeResult
|
||||
import com.bitwarden.authenticator.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.Text
|
||||
import com.bitwarden.authenticator.ui.platform.base.util.isBase32
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -160,11 +161,3 @@ sealed class QrCodeScanAction {
|
||||
*/
|
||||
data object CameraSetupErrorReceive : QrCodeScanAction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is using base32 digits.
|
||||
*/
|
||||
private fun String.isBase32(): Boolean {
|
||||
val regex = ("^[A-Z2-7]+=*$").toRegex()
|
||||
return regex.matches(this)
|
||||
}
|
||||
|
||||
@@ -72,3 +72,11 @@ fun String.removeDiacritics(): String =
|
||||
Normalizer.normalize(this, Normalizer.Form.NFKD),
|
||||
"",
|
||||
)
|
||||
|
||||
/**
|
||||
* Checks if a string is using base32 digits.
|
||||
*/
|
||||
fun String.isBase32(): Boolean {
|
||||
val regex = ("^[A-Z2-7]+=*$").toRegex()
|
||||
return regex.matches(this)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
@@ -67,6 +68,7 @@ fun BitwardenPasswordField(
|
||||
autoFocus: Boolean = false,
|
||||
keyboardType: KeyboardType = KeyboardType.Password,
|
||||
imeAction: ImeAction = ImeAction.Default,
|
||||
capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
OutlinedTextField(
|
||||
@@ -83,6 +85,7 @@ fun BitwardenPasswordField(
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = capitalization,
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction,
|
||||
),
|
||||
@@ -156,6 +159,7 @@ fun BitwardenPasswordField(
|
||||
autoFocus: Boolean = false,
|
||||
keyboardType: KeyboardType = KeyboardType.Password,
|
||||
imeAction: ImeAction = ImeAction.Default,
|
||||
capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
|
||||
) {
|
||||
var showPassword by rememberSaveable { mutableStateOf(initialShowPassword) }
|
||||
BitwardenPasswordField(
|
||||
@@ -172,6 +176,7 @@ fun BitwardenPasswordField(
|
||||
autoFocus = autoFocus,
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction,
|
||||
capitalization = capitalization,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
<string name="number_of_digits">Number of digits</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="validation_field_required">The %1$s field is required.</string>
|
||||
<string name="secret_key">Secret key</string>
|
||||
<string name="refresh_period_seconds" tools:ignore="PluralsCandidate">%d seconds</string>
|
||||
<string name="saving">Saving</string>
|
||||
<string name="item_saved">Item saved</string>
|
||||
@@ -108,4 +107,9 @@
|
||||
<string name="key_is_required">Key is required.</string>
|
||||
<string name="name_is_required">Name is required.</string>
|
||||
<string name="submit_crash_logs">Submit crash logs</string>
|
||||
<string name="import_vault_failure">There was a problem importing your vault.</string>
|
||||
<string name="file_source">File Source</string>
|
||||
<string name="import_vault">Import</string>
|
||||
<string name="import_success">Vault import successful</string>
|
||||
<string name="key_is_invalid">Key is invalid.</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user