Cómo Validar Contraseñas en Clean Architecture y Jetpack Compose

Cómo Validar Contraseñas en Clean Architecture y Jetpack Compose

 

Introducción

En el desarrollo de aplicaciones, la validación de contraseñas es un aspecto crucial para asegurar la seguridad de los usuarios. En este tutorial, aprenderemos cómo implementar un validador de contraseñas en Android utilizando los principios de Clean Architecture y la potente biblioteca de Jetpack Compose.

 

Si preferis en formato video, miralo a continuación:

 

Clean Architecture y Jetpack Compose:

Clean Architecture es una arquitectura de software que promueve la separación de preocupaciones y mejora la modularidad del código. Por otro lado, Jetpack Compose es una biblioteca moderna para la creación de interfaces de usuario en Android, que nos brinda una manera declarativa de construir interfaces de usuario interactivas.

 

Código del proyecto:

Descargar el código inicial: https://github.com/MKiperszmid/MisTutorialesYouTube/commit/2d13ec3b277ee85301619d70f01df65e71bd40ee

Descargar el código finalizado: https://github.com/MKiperszmid/MisTutorialesYouTube/tree/clean_architecture_password_validation

 

Implementación del validador de contraseñas:

 

1. Definición de requisitos:

- Una contraseña debe tener al menos 8 caracteres.

- Debe tener al menos una mayúscula

- Una minúscula

- Un número 

- Un carácter especial.

 

2. Creación de la capa de dominio:

En la capa de dominio, definiremos una clase llamada ValidatePasswordUseCase, el cual va a tener función de validación de contraseñas que cumpla con los requisitos establecidos. La función se va a llamar validate. Para manejar los posibles errores de validación, crearemos un enum llamado InvalidErrorcon diferentes casos, como "LOW_CHARACTERS", "NO_LOWERCASE", "NO_UPPERCASE", "NO_DIGITS" y "NO_SPECIAL_CHARACTERS".

enum class InvalidError { 

    LOW_CHARACTERS, 

    NO_LOWERCASE, 

    NO_UPPERCASE, 

    NO_DIGITS, 

    NO_SPECIAL_CHARACTERS, 

}

Y luego creamos una sealed interface llamada ValidationResult, que va a ser la clase que vamos a retornar en nuestra función de validación. Ya que vamos a poder diferenciar una contraseña Valida, de una Invalida. Y en caso de Invalida, devolver el enum

sealed interface ValidationResult { 

    object Valid : ValidationResult 

    data class Invalid(val error: InvalidError) : ValidationResult 

}

Luego, implementaremos la función de validación de contraseñas en la capa de dominio, que tomará como parámetro la contraseña a validar y devolverá un objeto `ValidationResult` que contiene el resultado de la validación y un mensaje de error en caso de que la contraseña sea inválida.

fun validate(password: String): ValidationResult { 

    if (password.length < 8) { 

        return ValidationResult.Invalid(InvalidError.LOW_CHARACTERS) 

    } 

    if (!password.any { it.isLowerCase() }) { 

        return ValidationResult.Invalid(InvalidError.NO_LOWERCASE) 

    } 

    if (!password.any { it.isUpperCase() }) { 

        return ValidationResult.Invalid(InvalidError.NO_UPPERCASE) 

    } 

    if (!password.any { it.isDigit() }) { 

        return ValidationResult.Invalid(InvalidError.NO_DIGITS) 

    } 

    if (!password.any { !it.isLetter() && !it.isDigit() }) { 

        return ValidationResult.Invalid(InvalidError.NO_SPECIAL_CHARACTERS) 

    } 

    return ValidationResult.Valid 

}

3. Implementación en la capa de presentación:

En la capa de presentación, utilizaremos Jetpack Compose para construir la interfaz de usuario y comunicarnos con la capa de dominio. 

Creamos una pantalla que incluye un TextField para ingresar la contraseña y un botón para validarla. También agregaremos un Texto, sólo cuando la contraseña es inválida.

En el TextField, vinculamos una función changePassword que se encargará de actualizar el valor de la contraseña en el ViewModel. 

Luego, llamamos a la función de validación de contraseñas validatePassword en el ViewModel al hacer clic en el botón de validación.

Para mostrar el mensaje de error, dado que vamos a devolver un Enum, tenemos que crear una función que convierta de Enum, a mensaje de error. Por lo que en nuestro ViewModel vamos a crear una función llamada mapErrorToString, que reciba el enum de InvalidError, y devuelva un String para mostrar el error.

 

HomeScreen.kt

@Composable fun HomeScreen(viewModel: HomeViewModel) { 

    val state = viewModel.state 

    Column( modifier = Modifier.fillMaxSize().padding(16.dp),horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) {

        val requirements = buildAnnotatedString { 

            append("Requisitos:\n") 

            append("- +8 Caracteres\n") 

            append("- 1 Mayúscula\n") 

            append("- 1 Minúscula\n") 

            append("- 1 Núero\n") 

            append("- 1 Caracter especial\n") 

        } 

    Text(text = requirements, modifier = Modifier.align(Alignment.Start)) 

    TextField(value = state.password, placeholder = { Text(text ="Contraseña") }, onValueChange = viewModel::changePassword, modifier = Modifier.fillMaxWidth()) 

    if (state.passwordError != null) { 

        Text(text = state.passwordError, color = Color.Red) 

    } 

    Button( onClick = viewModel::validatePassword, modifier = Modifier.fillMaxWidth(),) { 

        Text(text ="Validar") 

    } 

}

}

HomeViewModel.kt

class HomeViewModel(private val validatePasswordUseCase: ValidatePasswordUseCase = ValidatePasswordUseCase()) : ViewModel() { 

    var state by mutableStateOf(HomeState()) 

        private set 

    fun changePassword(password: String) { 

        state = state.copy( password = password, ) 

    } 

    fun validatePassword() { 

        val validationResult = validatePasswordUseCase.validate(state.password) 

        state = when (validationResult) { 

            is ValidationResult.Invalid -> { state.copy( passwordError = mapErrorToString(validationResult.error),) } 

            ValidationResult.Valid -> { state.copy( passwordError = null, ) } 

        } 

    } 

    private fun mapErrorToString(error: InvalidError): String { 

        return when (error) { 

            InvalidError.LOW_CHARACTERS -> "No hay caracteres suficientes" 

            InvalidError.NO_LOWERCASE -> "No hay minuscula" 

            InvalidError.NO_UPPERCASE -> "No hay mayuscula" 

            InvalidError.NO_DIGITS -> "No hay numeros" 

            InvalidError.NO_SPECIAL_CHARACTERS -> "No hay caracteres especiales" 

        } 

    } 

}

data class HomeState( val password: String ="", val passwordError: String? = null )

 

Y con eso creamos un validador de contraseñas con Clean Architecture y Jetpack Compose

 

Conclusión:

En este tutorial, hemos aprendido cómo implementar un validador de contraseñas en Android utilizando los principios de Clean Architecture y la biblioteca de Jetpack Compose. Hemos discutido la importancia de la validación de contraseñas, la configuración del proyecto, la implementación en la capa de dominio y presentación, y el manejo de errores. 

Espero que esta guía te haya sido útil para mejorar la seguridad en tus aplicaciones Android. ¡Feliz desarrollo!

 

Para ver más contenido, seguime en mis redes:

https://www.youtube.com/@DevKiper

https://twitter.com/DevKiper

https://www.AndroidAvanzado.com/

https://instagram.com/Martin_Kiper

 

Martin Kiperszmid

Artículo por Martin Kiperszmid

Publicado 27 Jul 2023