8a15b812f9
Base del DM/greeter de carmen. Contrato Authenticator agnóstico: authenticate(usuario, secreto) -> UserInfo (uid/gid/home/shell). PamAuthenticator verifica contra PAM (/etc/pam.d/carmen); MockAuthenticator con credenciales en memoria para tests. AuthError grueso: BadCredentials vs AccountUnavailable, sin filtrar existencia de cuentas. resolve_user vía getpwnam. data/carmen como servicio PAM; ejemplo auth-probe. 11 tests; el camino PAM real se ejercita. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
80 lines
2.3 KiB
Rust
80 lines
2.3 KiB
Rust
//! Resolución de la identidad de un usuario del sistema.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use crate::AuthError;
|
|
|
|
/// Identidad de un usuario en el sistema: lo que el compositor necesita
|
|
/// para arrancar una sesión — fijar uid/gid, `cd` al home, ejecutar el
|
|
/// shell o la sesión de escritorio.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct UserInfo {
|
|
/// Nombre de login.
|
|
pub name: String,
|
|
/// User ID.
|
|
pub uid: u32,
|
|
/// Group ID primario.
|
|
pub gid: u32,
|
|
/// Directorio personal.
|
|
pub home: PathBuf,
|
|
/// Shell de login.
|
|
pub shell: PathBuf,
|
|
}
|
|
|
|
impl UserInfo {
|
|
/// Identidad sintética para tests y para cajas donde el usuario no
|
|
/// está en `/etc/passwd`. **No** representa a un usuario real del SO
|
|
/// — no usar para fijar privilegios de un proceso real.
|
|
pub fn synthetic(name: &str) -> Self {
|
|
Self {
|
|
name: name.to_string(),
|
|
uid: 1000,
|
|
gid: 1000,
|
|
home: PathBuf::from(format!("/home/{name}")),
|
|
shell: PathBuf::from("/bin/sh"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Resuelve un usuario por nombre vía `getpwnam`. `Err` si no existe o
|
|
/// si la consulta a `/etc/passwd` (o NSS) falla.
|
|
pub fn resolve_user(name: &str) -> Result<UserInfo, AuthError> {
|
|
match nix::unistd::User::from_name(name) {
|
|
Ok(Some(u)) => Ok(UserInfo {
|
|
name: u.name,
|
|
uid: u.uid.as_raw(),
|
|
gid: u.gid.as_raw(),
|
|
home: u.dir,
|
|
shell: u.shell,
|
|
}),
|
|
Ok(None) => Err(AuthError::UnresolvedUser(name.to_string())),
|
|
Err(e) => Err(AuthError::Pam(format!("getpwnam({name}): {e}"))),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn resolves_root() {
|
|
// root (uid 0) existe en todo sistema Unix.
|
|
let info = resolve_user("root").expect("root debe existir");
|
|
assert_eq!(info.uid, 0);
|
|
assert_eq!(info.name, "root");
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_user_errs() {
|
|
let r = resolve_user("usuario-que-no-existe-xyzzy");
|
|
assert!(matches!(r, Err(AuthError::UnresolvedUser(_))));
|
|
}
|
|
|
|
#[test]
|
|
fn synthetic_has_home_under_slash_home() {
|
|
let info = UserInfo::synthetic("prueba");
|
|
assert_eq!(info.home, PathBuf::from("/home/prueba"));
|
|
assert_eq!(info.uid, 1000);
|
|
}
|
|
}
|