Ingeniería de Software y Sistemas Computacionales | La Salle Nezahualcóyotl
En esta práctica, la aplicación dejará de ser estática. Implementaremos una arquitectura donde el Frontend valida la identidad contra un LDAP y utiliza esas credenciales para "firmar" peticiones hacia un Middleware API que gestiona mensajes en una base de datos.
Añade estas librerías dentro del bloque dependencies { ... }:
dependencies {
// 1. Cliente LDAP (Protocolo de Identidad)
implementation("com.unboundid:unboundid-ldapsdk:6.0.8")
// 2. Retrofit y Gson (Protocolo de Datos)
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// 3. Corrutinas (Gestión de Hilos)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
}
⚠️ Haz clic en "Sync Now" y espera a que Android Studio termine la indexación.
/api/mensajes.
package mx.lasalle.ciclovida
data class Mensaje(
val id: Int,
val id_usuario: String,
val mensaje: String,
val fecha: String
)
@Header es vital: la API no procesará el POST o DELETE si los encabezados x-user y x-password no son válidos en el LDAP.
package mx.lasalle.ciclovida
import retrofit2.Response
import retrofit2.http.*
interface ApiService {
// Consulta pública de todos los mensajes
@GET("/api/mensajes")
suspend fun consultarMensajes(): Response<List<Mensaje>>
// Publicación protegida por identidad LDAP
@POST("/api/mensajes")
suspend fun publicarMensaje(
@Header("x-user") user: String,
@Header("x-password") pass: String,
@Body body: Map<String, String>
): Response<Void>
}
// Asegúrate de usar ViewBinding
binding.btnEnviar.setOnClickListener {
val idCapturado = binding.etNombre.text.toString()
val passCapturada = binding.etPassword.text.toString()
if (idCapturado.isNotEmpty() && passCapturada.isNotEmpty()) {
CoroutineScope(Dispatchers.IO).launch {
try {
// Conectando al OpenLDAP en el Docker del Profesor
val connection = LDAPConnection("10.118.137.27", 389)
val userDN = "cn=$idCapturado,dc=grupov4,dc=lasalle,dc=local"
val result = connection.bind(userDN, passCapturada)
if (result.resultCode == ResultCode.SUCCESS) {
withContext(Dispatchers.Main) {
// PERSISTENCIA: Guardamos la identidad para el CRUD posterior
val prefs = getSharedPreferences("PREFS_SESION", MODE_PRIVATE).edit()
prefs.putString("USER_NAME", idCapturado)
prefs.putString("USER_PASS", passCapturada)
prefs.apply()
startActivity(Intent(this@MainActivity, HomeActivity::class.java))
finish()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(this@MainActivity, "Identidad LDAP no válida", Toast.LENGTH_SHORT).show()
}
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
// Recuperar credenciales validadas en el Login
val user = getSharedPreferences("PREFS_SESION", MODE_PRIVATE).getString("USER_NAME", "") ?: ""
val pass = getSharedPreferences("PREFS_SESION", MODE_PRIVATE).getString("USER_PASS", "") ?: ""
// Cargar los mensajes desde la API REST
obtenerDatosDelServidor()
}
private fun obtenerDatosDelServidor() {
val retrofit = Retrofit.Builder()
.baseUrl("http://10.118.137.27:8080") // Middleware API
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(ApiService::class.java)
CoroutineScope(Dispatchers.IO).launch {
val response = service.consultarMensajes()
if (response.isSuccessful) {
val lista = response.body()?.map { "${it.id_usuario}: ${it.mensaje}" } ?: emptyList()
withContext(Dispatchers.Main) {
// Actualizamos la vista (RecyclerView)
binding.rvUsuarios.adapter = UserAdapter(lista)
}
}
}
}
1. Autenticación Delegada: ¿Por qué la aplicación móvil no tiene permiso de "ver" las contraseñas de otros usuarios en el LDAP y solo puede intentar un Bind con la suya propia?
2. Arquitectura Middleware: Si la API REST fallara pero el servidor LDAP siguiera encendido, ¿qué partes de la aplicación seguirían funcionando y cuáles no? Justifica tu respuesta.
3. Seguridad de Red: Estamos usando HTTP en lugar de HTTPS para el laboratorio. Desde la perspectiva de un analista de seguridad, ¿qué herramienta permitiría capturar las contraseñas que viajan en el aire entre el móvil y el hotspot?
💡 Entregable: Realiza un registro exitoso, inicia sesión, publica un mensaje con tu nombre y toma captura de pantalla del RecyclerView actualizado con tu mensaje proveniente de la API.