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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ jobs:

- name: Sicherheits-Advisories pruefen
working-directory: app/src-tauri
run: cargo audit
run: cargo audit --deny warnings

build:
name: Tauri-Build (Windows)
Expand Down
26 changes: 26 additions & 0 deletions app/src-tauri/.cargo/audit.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# cargo-audit: gezielt ignorierte, geprüfte Advisories. Jeder Eintrag mit Begründung;
# bei naechster Dependency-Aktualisierung erneut pruefen (`cargo audit` ohne Ignore-Filter).
[advisories]
ignore = [
# gtk-rs GTK3-Bindings: nur als Linux-Backend-Option in tauri/wry vorhanden,
# auf diesem Windows-only-Target (bundle.targets = ["msi"]) nie kompiliert/erreichbar.
"RUSTSEC-2024-0411", # gdkwayland-sys unmaintained
"RUSTSEC-2024-0412", # gdk unmaintained
"RUSTSEC-2024-0413", # atk unmaintained
"RUSTSEC-2024-0414", # gdkx11-sys unmaintained
"RUSTSEC-2024-0415", # gtk unmaintained
"RUSTSEC-2024-0416", # atk-sys unmaintained
"RUSTSEC-2024-0417", # gdkx11 unmaintained
"RUSTSEC-2024-0418", # gdk-sys unmaintained
"RUSTSEC-2024-0419", # gtk3-macros unmaintained
"RUSTSEC-2024-0420", # gtk-sys unmaintained
"RUSTSEC-2024-0429", # glib unsound iterator impl (selbe Linux-only GTK-Kette)
# Build-/Proc-Macro-Zeit, kein Laufzeitcode im Release-Binary.
"RUSTSEC-2024-0370", # proc-macro-error unmaintained
# Transitive Unicode-Hilfscrates, nur "unmaintained", kein bekannter Vuln.
"RUSTSEC-2025-0075", # unic-char-range
"RUSTSEC-2025-0080", # unic-common
"RUSTSEC-2025-0081", # unic-char-property
"RUSTSEC-2025-0098", # unic-ucd-version
"RUSTSEC-2025-0100", # unic-ucd-ident
]
5 changes: 3 additions & 2 deletions app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ serde_json = "1"
chrono = { version = "0.4", features = ["clock"] }
csv = "1"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_System_SystemInformation",
"Win32_Security_Authentication_Identity",
] }

[profile.release]
panic = "abort"
codegen-units = 1
Expand Down
16 changes: 1 addition & 15 deletions app/src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Tauri-Befehle (Bruecke Frontend <-> Backend). Halten Geraeteliste & AD-Cache im State.
use crate::ad;
use crate::identity::synth_sam;
use crate::identity::{current_user_domain, synth_sam};
use crate::model::*;
use crate::store;
use std::collections::BTreeSet;
Expand Down Expand Up @@ -277,17 +277,3 @@ pub fn export_devices(state: State<AppState>, format: String) -> Result<serde_js
let (file, rows) = crate::export::write_devices_csv(&devs)?;
Ok(serde_json::json!({ "ok": true, "path": file.to_string_lossy(), "rows": rows }))
}

fn current_user_domain() -> (String, String) {
let user = std::env::var("USERNAME").unwrap_or_else(|_| "Unbekannt".into());
let domain = std::env::var("USERDNSDOMAIN")
.or_else(|_| std::env::var("USERDOMAIN"))
.unwrap_or_else(|_| "corp.local".into())
.to_lowercase();
let full = format!(
"{}\\{}",
std::env::var("USERDOMAIN").unwrap_or_else(|_| "CORP".into()),
user
);
(full, domain)
}
18 changes: 17 additions & 1 deletion app/src-tauri/src/commands_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
use crate::identity::synth_sam;
use crate::identity::{current_user_domain, synth_sam};

#[test]
fn current_user_domain_never_returns_blank_identity() {
// Kann den exakten Wert nicht pruefen (haengt vom ausfuehrenden Konto ab, z. B.
// GitHub-Actions-Runner statt Domaenen-Benutzer) - stellt aber sicher, dass die
// Win32-Ermittlung (oder ihr Umgebungsvariablen-Fallback) nie leer/panisch ist,
// sondern immer eine anzeigbare "DOMAENE\Benutzer"-Kennung liefert.
let (full, domain) = current_user_domain();
assert!(!full.trim().is_empty(), "Identitaet darf nicht leer sein");
assert!(
full.contains('\\'),
"erwarte DOMAENE\\Benutzer-Format: {}",
full
);
assert!(!domain.trim().is_empty(), "Domaene darf nicht leer sein");
}

#[test]
fn synth_sam_transliterates_umlauts() {
Expand Down
92 changes: 92 additions & 0 deletions app/src-tauri/src/identity.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,95 @@
/// Ermittelt die angemeldete Windows-Identitaet fuer den Zuordnungs-Audit-Trail
/// (`confirmedBy`/`updatedBy` in assignments.json) und die Sidebar-Anzeige.
/// Liest die SAM-kompatible Kennung ("DOMAENE\Benutzer") und die DNS-Domaene direkt aus dem
/// authentifizierten Token/System (statt aus `%USERNAME%`/`%USERDOMAIN%`-Umgebungsvariablen,
/// die vor dem Start des Prozesses beliebig gesetzt werden koennen). Faellt bei Fehlern
/// (z. B. nicht domaenengebundener Rechner) je Feld unabhaengig auf die bisherige,
/// umgebungsvariablenbasierte Ermittlung zurueck.
pub(crate) fn current_user_domain() -> (String, String) {
let full = windows_sam_compatible_name().unwrap_or_else(env_full_identity);
let domain = windows_dns_domain().unwrap_or_else(env_dns_domain);
(full, domain)
}

fn env_full_identity() -> String {
format!(
"{}\\{}",
std::env::var("USERDOMAIN").unwrap_or_else(|_| "CORP".into()),
std::env::var("USERNAME").unwrap_or_else(|_| "Unbekannt".into())
)
}

fn env_dns_domain() -> String {
std::env::var("USERDNSDOMAIN")
.or_else(|_| std::env::var("USERDOMAIN"))
.unwrap_or_else(|_| "corp.local".into())
.to_lowercase()
}

/// SAM-kompatible Anmeldekennung ("DOMAENE\Benutzer") ueber `GetUserNameExW`, direkt aus dem
/// Sicherheitstoken des laufenden Prozesses — nicht aus einer Umgebungsvariable ableitbar.
#[cfg(windows)]
fn windows_sam_compatible_name() -> Option<String> {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use windows_sys::Win32::Security::Authentication::Identity::{
GetUserNameExW, NameSamCompatible,
};

let mut buffer = [0u16; 256];
let mut len = buffer.len() as u32;
let ok = unsafe { GetUserNameExW(NameSamCompatible, buffer.as_mut_ptr(), &mut len) };
if !ok || len == 0 {
return None;
}
let name = OsString::from_wide(&buffer[..len as usize])
.to_string_lossy()
.into_owned();
if name.is_empty() {
None
} else {
Some(name)
}
}

#[cfg(not(windows))]
fn windows_sam_compatible_name() -> Option<String> {
None
}

/// DNS-Domaene des Rechners ueber `GetComputerNameExW` (nur Anzeige in der Sidebar).
/// Auf einem nicht domaenengebundenen Rechner liefert die API erfolgreich einen leeren
/// String zurueck — das behandeln wir wie einen Fehlschlag, um auf den bisherigen
/// "corp.local"-Default auszuweichen statt eine leere Sidebar-Zeile zu zeigen.
#[cfg(windows)]
fn windows_dns_domain() -> Option<String> {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use windows_sys::Win32::System::SystemInformation::{
ComputerNameDnsDomain, GetComputerNameExW,
};

let mut buffer = [0u16; 256];
let mut len = buffer.len() as u32;
let ok = unsafe { GetComputerNameExW(ComputerNameDnsDomain, buffer.as_mut_ptr(), &mut len) };
if ok == 0 {
return None;
}
let name = OsString::from_wide(&buffer[..len as usize])
.to_string_lossy()
.into_owned();
if name.is_empty() {
None
} else {
Some(name.to_lowercase())
}
}

#[cfg(not(windows))]
fn windows_dns_domain() -> Option<String> {
None
}

/// Leitet aus einem Anzeigenamen einen plausiblen SAM-Account ab — nur als
/// CSV-Fallback, wenn kein AD verfuegbar ist. Deutsche Umlaute werden
/// transliteriert, damit der Wert ASCII-stabil und deterministisch bleibt.
Expand Down
2 changes: 1 addition & 1 deletion app/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
}
],
"security": {
"csp": "default-src 'self'; connect-src 'self' ipc: https://ipc.localhost; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self';"
"csp": "default-src 'self'; connect-src 'self' ipc: https://ipc.localhost; style-src 'self'; font-src 'self'; img-src 'self'; object-src 'none'; base-uri 'self';"
}
},
"bundle": {
Expand Down
Loading