Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions rfirma-app/src-tauri/src/desktop/adapters/card_drivers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Los controladores de tarjeta (`.bundle`) que macOS trae para el servicio de tarjetas inteligentes.

use std::path::Path;

const SYSTEM_DRIVERS: &str = "/usr/libexec/SmartCardServices/drivers";

/// Los nombres de los controladores del sistema, ordenados; vacío si no hay ninguno o no se leen.
pub fn system_card_drivers() -> Vec<String> {
card_drivers_in(Path::new(SYSTEM_DRIVERS))
}

fn card_drivers_in(directory: &Path) -> Vec<String> {
let Ok(entries) = std::fs::read_dir(directory) else {
return Vec::new();
};
let mut names: Vec<String> = entries
.filter_map(Result::ok)
.map(|entry| entry.path())
.filter(|path| {
path.extension()
.is_some_and(|extension| extension == "bundle")
})
.filter_map(|path| Some(path.file_stem()?.to_string_lossy().into_owned()))
.collect();
names.sort();
names
}

#[cfg(test)]
mod tests;
19 changes: 19 additions & 0 deletions rfirma-app/src-tauri/src/desktop/adapters/card_drivers/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::*;

#[test]
fn lists_the_bundles_sorted_by_name_and_ignores_other_files() {
let directory = tempfile::tempdir().unwrap();
for name in ["ifd-gempc.bundle", "ifd-ccid.bundle", "readme.txt"] {
std::fs::create_dir(directory.path().join(name)).unwrap();
}

assert_eq!(
card_drivers_in(directory.path()),
vec!["ifd-ccid", "ifd-gempc"]
);
}

#[test]
fn a_missing_directory_has_no_drivers() {
assert!(card_drivers_in(Path::new("/nonexistent/drivers")).is_empty());
}
32 changes: 20 additions & 12 deletions rfirma-app/src-tauri/src/desktop/adapters/debug_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use crate::desktop::adapters::installation::{this_glibc, this_installation, this
use crate::desktop::adapters::paths::{Paths, Platform};
use crate::desktop::adapters::registry::this_desktop;
use crate::desktop::application::debug_report::{
DebugReport, DiscardReason, LinuxEnvironment, ModuleStatus, NativeLibrary, NativeLibraryStatus,
NssProfile, NssProfileState, NssStores, OwnerFolder, PcscStatus, Pkcs11Module, Pkcs11Modules,
ProtocolHandlerStatus, ReportOwner, WindowsStores,
DebugReport, DiscardReason, LinuxEnvironment, MacosStores, ModuleStatus, NativeLibrary,
NativeLibraryStatus, NssProfile, NssProfileState, NssStores, OwnerFolder, PcscStatus,
Pkcs11Module, Pkcs11Modules, ProtocolHandlerStatus, ReportOwner, WindowsStores,
};
use crate::desktop::domain::channel::Channel;
use crate::identity::adapters as identity_adapters;
Expand Down Expand Up @@ -57,6 +57,7 @@ pub fn this_process_report() -> DebugReport {
pkcs11_modules: pkcs11_modules(),
nss_stores: nss_stores(),
windows_stores: windows_stores(channel),
macos_stores: macos_stores(channel),
}
}

Expand Down Expand Up @@ -274,9 +275,20 @@ fn native_library() -> NativeLibrary {
}

fn windows_stores(channel: Channel) -> Option<WindowsStores> {
if channel != Channel::Windows {
return None;
}
(channel == Channel::Windows).then(|| WindowsStores {
local_channel: user_trust_state(),
minidrivers: crate::desktop::adapters::minidrivers::registered_minidrivers(),
})
}

fn macos_stores(channel: Channel) -> Option<MacosStores> {
(channel == Channel::MacOs).then(|| MacosStores {
local_channel: user_trust_state(),
card_drivers: crate::desktop::adapters::card_drivers::system_card_drivers(),
})
}

fn user_trust_state() -> NssProfileState {
let local_ca = Paths::from_environment()
.ok()
.map(|paths| LocalCaStore::of(&paths));
Expand All @@ -287,17 +299,13 @@ fn windows_stores(channel: Channel) -> Option<WindowsStores> {
.copied()
.unwrap_or(false)
});
let local_channel = if trusted {
if trusted {
NssProfileState::TrustsLocalChannel {
until: local_ca.as_ref().and_then(local_ca_expiry),
}
} else {
NssProfileState::DoesNotTrustLocalChannel
};
Some(WindowsStores {
local_channel,
minidrivers: crate::desktop::adapters::minidrivers::registered_minidrivers(),
})
}
}

fn nss_stores() -> Option<NssStores> {
Expand Down
1 change: 1 addition & 0 deletions rfirma-app/src-tauri/src/desktop/adapters/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Adaptadores de `desktop`: todo lo que toca el mundo, incluidas las órdenes y las vistas de Tauri.

pub mod card_drivers;
pub mod channel;
pub mod choice;
pub mod command_line_ports;
Expand Down
12 changes: 12 additions & 0 deletions rfirma-app/src-tauri/src/desktop/application/debug_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ pub struct WindowsStores {
pub minidrivers: Vec<String>,
}

/// Lo que solo existe en macOS: la confianza del usuario y los controladores de tarjeta del sistema.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MacosStores {
/// Si la confianza del usuario en el Almacén de macOS acepta el canal local.
pub local_channel: NssProfileState,
/// Controladores de tarjeta que macOS tiene instalados.
pub card_drivers: Vec<String>,
}

/// Dónde y con qué instalador está el flatpak.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FlatpakInstallation {
Expand Down Expand Up @@ -263,6 +272,8 @@ pub struct DebugReport {
pub nss_stores: Option<NssStores>,
/// Almacén de Windows y minidrivers; `None` fuera de Windows.
pub windows_stores: Option<WindowsStores>,
/// Almacén de macOS y controladores de tarjeta; `None` fuera de macOS.
pub macos_stores: Option<MacosStores>,
}

/// Una cabecera y una sección por bloque, con el patrón `clave: estado · detalle` y las rutas anonimizadas.
Expand Down Expand Up @@ -293,6 +304,7 @@ pub fn debug_report_text(report: &DebugReport, owner: &ReportOwner) -> String {
.map(|stores| section("Almacenes NSS", &stores::nss_lines(stores, owner))),
)
.chain(stores::windows_sections(report.windows_stores.as_ref()))
.chain(stores::macos_sections(report.macos_stores.as_ref()))
.flatten()
.collect::<Vec<_>>()
.join("\n\n")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Las líneas de los almacenes del informe: los perfiles NSS, y el almacén de Windows con sus minidrivers.
//! Las líneas de los almacenes del informe: los perfiles NSS, y el almacén de Windows con sus minidrivers y el de macOS con sus controladores.

use super::{section, NssProfileState, NssStores, ReportOwner, WindowsStores};
use super::{section, MacosStores, NssProfileState, NssStores, ReportOwner, WindowsStores};

pub(super) fn windows_sections(stores: Option<&WindowsStores>) -> [Option<String>; 2] {
let Some(stores) = stores else {
Expand All @@ -22,6 +22,29 @@ pub(super) fn windows_sections(stores: Option<&WindowsStores>) -> [Option<String
]
}

pub(super) fn macos_sections(stores: Option<&MacosStores>) -> [Option<String>; 2] {
let Some(stores) = stores else {
return [None, None];
};
let trust = format!(
"Confianza del usuario: {}",
profile_state(&stores.local_channel)
);
let drivers = if stores.card_drivers.is_empty() {
vec!["tarjetas: ninguno instalado".to_owned()]
} else {
stores
.card_drivers
.iter()
.map(|name| format!("{name}: instalado"))
.collect()
};
[
section("Almacén de macOS", &[trust]),
section("Controladores de tarjeta", &drivers),
]
}

pub(super) fn nss_lines(stores: &NssStores, owner: &ReportOwner) -> Vec<String> {
let rfirma_store = if stores.rfirma_store_installed {
"instalado"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,59 @@ fn windows_without_registered_minidrivers_says_none() {
);
}

fn report_with_macos(trust: NssProfileState, drivers: Vec<&str>) -> String {
let mut report = a_flatpak_report();
report.pcsc = None;
report.macos_stores = Some(MacosStores {
local_channel: trust,
card_drivers: drivers.into_iter().map(str::to_owned).collect(),
});
debug_report_text(&report, &ana())
}

#[test]
fn macos_replaces_the_nss_stores_with_the_macos_store_and_card_drivers() {
let text = report_with_macos(
NssProfileState::TrustsLocalChannel {
until: Some("2027-03-14".to_owned()),
},
vec!["ifd-ccid", "ifd-gempc"],
);

assert!(
text.ends_with(
"Almacén de macOS\n \
Confianza del usuario: confía en el canal local · hasta 2027-03-14\n\
\n\
Controladores de tarjeta\n \
ifd-ccid: instalado\n \
ifd-gempc: instalado"
),
"{text}"
);
assert!(!text.contains("Almacenes NSS"), "{text}");
}

#[test]
fn macos_without_trust_in_the_local_channel_says_so() {
let text = report_with_macos(NssProfileState::DoesNotTrustLocalChannel, vec!["ifd-ccid"]);

assert!(
text.contains("Almacén de macOS\n Confianza del usuario: no confía en el canal local\n"),
"{text}"
);
}

#[test]
fn macos_without_card_drivers_says_none() {
let text = report_with_macos(NssProfileState::DoesNotTrustLocalChannel, Vec::new());

assert!(
text.ends_with("Controladores de tarjeta\n tarjetas: ninguno instalado"),
"{text}"
);
}

#[test]
fn a_flatpak_store_says_its_browser_and_channel_and_its_full_path() {
let section = report_with_nss(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub(super) fn a_flatpak_report() -> DebugReport {
pkcs11_modules: None,
nss_stores: None,
windows_stores: None,
macos_stores: None,
}
}

Expand Down
Loading