diff --git a/.idea/rust-crate-template.iml b/.idea/rust-crate-template.iml index cf84ae4..bbe0a70 100644 --- a/.idea/rust-crate-template.iml +++ b/.idea/rust-crate-template.iml @@ -3,6 +3,7 @@ + diff --git a/AGENTS.md b/AGENTS.md index 9976f22..8cc90bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,14 @@ # AGENTS.md -Dieses Dokument definiert Richtlinien, Konventionen und Arbeitsanweisungen für KI-Agenten und LLM-Tools, die an diesem Repository oder daraus erstellten Rust-Crates arbeiten. +Dieses Dokument definiert Richtlinien, Konventionen und Arbeitsanweisungen für KI-Agenten und LLM-Tools, die an diesem Repository (`sudo-ctdra`) arbeiten. --- ## 1. Projektübersicht & Kontext -- **Typ:** Rust Library Crate Template +- **Projektname:** `sudo-ctdra` +- **Typ:** Rust Library +- **Zweck:** Bereitstellung von Hilfsfunktionen zur Überprüfung (`is_run_as_root`) und Anforderung von Root-Rechten via `sudo` (`run_as_root`). - **Rust Edition:** `2024` - **Einstiegspunkt:** `src/lib.rs` - **CI/CD Plattform:** Gitea Actions (`.gitea/workflows/`) @@ -20,18 +22,19 @@ Dieses Dokument definiert Richtlinien, Konventionen und Arbeitsanweisungen für ### 2.1 Sprache & Idiomatik - Verwende modernes, idiomatisches Rust (Edition 2024). - Bevorzuge explizite Typen und klare Signaturen in öffentlichen Schnittstellen (`pub`). -- Halte die API ergonomisch und benutzerfreundlich. +- Halte die API ergonomisch, leichtgewichtig und benutzerfreundlich. ### 2.2 Fehlerbehandlung -- Nutze `Result` und `Option` für alle potenziell fehlschlagenden Operationen. -- Definiere aussagekräftige, domänenspezifische Fehlertypen (z. B. via `thiserror` oder standardmäßig `std::error::Error`). +- Nutze `Result`, `Option` oder explizite Fehlerrückgaben (`std::io::Error`) für alle potenziell fehlschlagenden Operationen. - **Verboten im produktiven Bibliothekscode (`src/`):** - Unbegründete `unwrap()`, `expect()` oder `panic!()` Aufrufe. + - Abrupter Programmabbruch via `std::process::exit` innerhalb von Bibliotheksfunktionen (Fehler müssen stattdessen an den Aufrufer zurückgegeben werden). + - Direktes oder ungefragtes Logging via Hilfsfunktionen oder Makros im Bibliothekscode; Fehlermeldungen / Fehlerstrukturen sind als Rückgabewerte zu liefern. - Ignorieren von Fehlern via `let _ = ...`, es sei denn, es ist explizit begründet und dokumentiert. ### 2.3 Dokumentation - Dokumentiere alle öffentlichen Module, Structs, Enums, Traits und Funktionen mit Rustdoc-Kommentaren (`///` bzw. Modul-Ebene `//!`). -- Füge für öffentliche Schnittstellen nach Möglichkeit Code-Beispiele ein, die über `cargo test --doc` automatisch validiert werden. +- Füge für öffentliche Schnittstellen Code-Beispiele ein, die über `cargo test --doc` automatisch validiert werden. ### 2.4 Code-Qualität & Formatierung - Halte den Code stets formatiert gemäß `rustfmt` (`cargo fmt`). @@ -92,10 +95,11 @@ Die CI/CD-Pipelines werden über Gitea Actions gesteuert: --- -## 5. Arbeitsanweisungen für Agenten bei Projekt-Initialisierung +## 5. Arbeitsanweisungen für Agenten -Wenn dieser Template-Stand verwendet wird, um eine neue Crate zu erstellen: -1. Aktualisiere die `Cargo.toml`-Metadaten (`name`, `version`, `authors`, `repository`, `description`). -2. Passe die `README.md` an die konkrete Funktionalität der neuen Crate an. -3. Behalte die Gitea-Workflows bei oder passe sie an das Ziel-Repository an. +Bei Änderungen an diesem Repository: +1. Pflege die Metadaten in `Cargo.toml` (`version`, `description`, etc.). +2. Halte `README.md` und `AGENTS.md` aktuell bezüglich Funktionsumfang und Konventionen. +3. Behalte die Gitea-Workflows bei bzw. passe sie bei Änderungen an. 4. Stelle sicher, dass keine Secrets, temporären Build-Dateien (`target/`) oder IDE-spezifischen Caches (außer `.idea` Konfigurationen) committed werden. +5. Führe stets alle Prüfungen gemäß Abschnitt 3.2 vor Abschluss der Arbeiten durch. diff --git a/Cargo.lock b/Cargo.lock index 1c5dbce..5f0a502 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,5 +3,14 @@ version = 4 [[package]] -name = "rust-creat-template" +name = "libc" +version = "1.0.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0f24f33af482526a4e3f9b47f0abb2c6377a1713c8aa4a8106994689a4cfa5" + +[[package]] +name = "sudo-ctdra" version = "1.0.0" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index b5e8b55..a29e329 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,15 @@ [package] -name = "rust-creat-template" # TODO Setzen -version = "1.0.0" # TODO Setzen +name = "sudo-ctdra" +version = "1.0.0" edition = "2024" -authors = ['DragonSlayer_14'] # TODO Setzen +authors = ['DragonSlayer_14'] readme = "README.md" license = "GPL-3.0-or-later" -repository = "https://gitea.creative-dragonslayer.de/Templates/rust-crate-template" # TODO Setzen -description = "Ein Template-Projekt, das fürs erstellen von Rust-Crates verwendet werden kann." # TODO Setzen +repository = "https://gitea.creative-dragonslayer.de/Rust-Crates/sudo" +description = "Eine Rust-Bibliothek zur Überprüfung und Anforderung von Root-Rechten über sudo unter Linux." [dependencies] +libc = "1.0.0-alpha.4" [profile.release] debug = "none" - -[registry] -default = "gitea" - -[registries.gitea] -index = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" - -[net] -git-fetch-with-cli = true diff --git a/LICENSE b/LICENSE index 41f5d3a..e627efd 100644 --- a/LICENSE +++ b/LICENSE @@ -208,7 +208,7 @@ If you develop a new program, and you want it to be of the greatest possible use To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. - rust-crate-template + sudo Copyright (C) 2026 Rust-Crates This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. @@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - rust-crate-template Copyright (C) 2026 Rust-Crates + sudo Copyright (C) 2026 Rust-Crates This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/README.md b/README.md index f532ff6..7ead32a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,54 @@ -# Rust Crate Template +# sudo-ctdra -Ein vorkonfiguriertes Template-Projekt für die schnelle und standardisierte Entwicklung von Rust-Crates (Libraries) mit automatisierter CI/CD-Pipeline für Gitea. +Eine kompakte, leichtgewichtige Rust-Bibliothek zur Überprüfung und Anforderung von Root-/Administrator-Rechten über `sudo` unter Linux und Unix-Systemen. --- ## 🚀 Übersicht & Features -- **Rust Edition 2024**: Moderner Rust-Standard mit optimierten Profil-Einstellungen (`profile.release.debug = "none"`). -- **Automatisierte CI/CD-Workflows (Gitea Actions)**: - - **Testing-Pipeline (`testing`-Branch)**: Führt Tests und Compiler-Checks aus und erstellt automatisch ein Gitea Pre-Release (`v-preview`) inklusive `.crate`-Paket als Release-Asset. - - **Main-Release-Pipeline (`main`-Branch)**: Führt Tests und Compiler-Checks aus, paketiert die Crate, veröffentlicht sie in der Gitea Cargo Package Registry und erstellt ein offizielles Gitea Release (`v`) mit Asset. -- **Integrierte Gitea Package Registry**: Vorkonfigurierte sparse index Registry-Anbindung. -- **GPL-3.0-or-later Lizenz**: Vorkonfiguriert mit Lizenzdatei und Metadaten. +- **Root-Prüfung (`is_run_as_root`)**: Schnelle und zuverlässige Überprüfung der effektiven Benutzer-ID (`libc::geteuid() == 0`). +- **Prozess-Eskalation (`run_as_root`)**: Startet das aktuelle Programm über `sudo` neu, übergibt alle Befehlszeilenargumente (`std::env::args`) und ersetzt den aktuellen Prozess ([`CommandExt::exec`](https://doc.rust-lang.org/std/os/unix/process/trait.CommandExt.html#tymethod.exec)). +- **Saubere Fehlerbehandlung**: Gibt bei Fehlschlägen einen [`std::io::Error`] zurück, anstatt das Programm unkontrolliert zu beenden oder ungefragt zu loggen. +- **Rust Edition 2024**: Moderner Rust-Standard mit optimierten Profileinstellungen. +- **Automatisierte CI/CD-Workflows**: Vorkonfigurierte Gitea Actions für Tests, Compiler-Checks, Paketierung und Releases. + +--- + +## 📦 Einbindung + +Füge die Crate zu deiner `Cargo.toml` hinzu: + +```toml +[dependencies] +sudo-ctdra = { version = "1.0.0", registry = "gitea" } +``` + +Falls die Gitea Package Registry genutzt wird, trage diese in deiner `.cargo/config.toml` ein: + +```toml +[registries.gitea] +index = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" +``` + +--- + +## 💡 Anwendungsbeispiel + +```rust +use sudo_ctdra::{is_run_as_root, run_as_root}; + +fn main() { + if is_run_as_root() { + println!("Programm läuft mit Root-Rechten."); + // Privilegierte Aktionen durchführen... + } else { + println!("Normale Benutzerrechte erkannt. Starte neu als Root über sudo..."); + let err = run_as_root(); + eprintln!("Fehler beim Ausführen von 'sudo': {err}"); + std::process::exit(1); + } +} +``` --- @@ -25,7 +62,9 @@ Ein vorkonfiguriertes Template-Projekt für die schnelle und standardisierte Ent │ └── testing.yaml # CI/CD: Test, Check & Pre-Release für 'testing' ├── .idea/ # Vorkonfigurierte JetBrains IDE Einstellungen ├── src/ -│ └── lib.rs # Einstiegspunkt der Crate / Library +│ └── lib.rs # Einstiegspunkt der Crate / Bibliotheksfunktionen +├── tests/ +│ └── integration_tests.rs # Integrations- und Subprozess-Tests ├── Cargo.lock ├── Cargo.toml # Crate-Manifest & Metadaten ├── LICENSE # GNU General Public License v3.0 @@ -35,39 +74,16 @@ Ein vorkonfiguriertes Template-Projekt für die schnelle und standardisierte Ent --- -## 🛠️ Verwendung als Template - -### 1. Template initialisieren & Metadaten anpassen -Passe nach dem Klonen bzw. Erstellen des neuen Repositories die Platzhalter in `Cargo.toml` an: - -```toml -[package] -name = "mein-crate-name" -version = "0.1.0" -edition = "2024" -authors = ['DeinName '] -readme = "README.md" -license = "GPL-3.0-or-later" -repository = "https://gitea.example.com/Organisation/mein-crate-name" -description = "Beschreibung der Crate." -``` - -### 2. Gitea Repository Secrets einrichten -Für die Veröffentlichung und Release-Erstellung in Gitea Actions muss mindestens ein Access-Token als Repository-Secret hinterlegt werden (z. B. unter `Einstellungen -> Secrets -> Actions`): -- `PACKAGE_TOKEN` (oder alternativ `RELEASE_TOKEN`, `GITEA_TOKEN`): Ein Personal Access Token mit Berechtigungen für Packages (`write:package`) und Repositories/Releases (`write:repository`). - ---- - ## 💻 Lokale Entwicklung & Befehle -Die gängigen Cargo-Befehle zur Entwicklung: +Die gängigen Cargo-Befehle zur Entwicklung und Validierung: - **Kompilierung prüfen:** ```bash cargo check --all-targets ``` -- **Tests ausführen:** +- **Tests ausführen (Unit-, Integrations- und Doc-Tests):** ```bash cargo test ``` @@ -97,25 +113,7 @@ Die gängigen Cargo-Befehle zur Entwicklung: | `testing` | Push auf `testing` | • `cargo test`
• `cargo check --all-targets`
• `cargo package`
• Erstellt/Aktualisiert Pre-Release `v-preview` mit `.crate`-Asset | | `main` | Push auf `main` | • `cargo test`
• `cargo check --all-targets`
• `cargo package`
• Veröffentlicht Crate in Gitea Package Registry
• Erstellt/Aktualisiert Release `v` mit `.crate`-Asset | -> **Hinweis zur Versionierung:** Die Versionsnummer wird automatisch aus `Cargo.toml` (`version = "..."`) ausgelesen. Passe vor einem Merge auf `main` oder `testing` die Version in `Cargo.toml` entsprechend SemVer an. - ---- - -## 📦 Verwenden der Crate in anderen Projekten - -Um die in der Gitea Package Registry veröffentlichte Crate in einem anderen Cargo-Projekt zu verwenden: - -1. Trage die Registry in deiner lokalen `~/.cargo/config.toml` oder projektweiten `.cargo/config.toml` ein: - ```toml - [registries.gitea] - index = "sparse+https://gitea.creative-dragonslayer.de/api/packages/Rust-Crates/cargo/" - ``` - -2. Binde die Crate in deiner `Cargo.toml` ein: - ```toml - [dependencies] - mein-crate-name = { version = "1.0.0", registry = "gitea" } - ``` +> **Hinweis zur Versionierung:** Die Versionsnummer wird automatisch aus `Cargo.toml` (`version = "..."`) ausgelesen. Passe vor einem Release / Merge die Version in `Cargo.toml` entsprechend **SemVer** an. --- diff --git a/src/lib.rs b/src/lib.rs index e69de29..7581470 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1,86 @@ +//! Eine leichtgewichtige Rust-Bibliothek zur Überprüfung und Anforderung von Root-Rechten über `sudo`. +//! +//! # Beispiele +//! +//! ```rust +//! use sudo_ctdra::is_run_as_root; +//! +//! if is_run_as_root() { +//! println!("Das Programm läuft mit Root-Rechten."); +//! } else { +//! println!("Das Programm läuft mit normalen Benutzerrechten."); +//! } +//! ``` + +use std::env; +use std::io; +use std::os::unix::process::CommandExt; +use std::process::Command; + +/// Prüft, ob das Programm mit Root-/Administrator-Rechten ausgeführt wird. +/// +/// Ermittelt anhand der effektiven Benutzer-ID (`geteuid() == 0`), ob der aktuelle +/// Prozess über Root-Rechte verfügt. +/// +/// # Returns +/// +/// * `true` - Das Programm läuft mit erhöhten Rechten (EUID == 0) +/// * `false` - Das Programm läuft mit normalen Benutzerrechten +/// +/// # Beispiele +/// +/// ```rust +/// use sudo_ctdra::is_run_as_root; +/// +/// let is_root = is_run_as_root(); +/// println!("Läuft als Root: {}", is_root); +/// ``` +#[must_use] +pub fn is_run_as_root() -> bool { + unsafe { libc::geteuid() == 0 } +} + +/// Startet das Programm mit Root-Rechten über `sudo` neu. +/// +/// Diese Funktion versucht das Programm mit erhöhten Rechten via `sudo` neu zu starten +/// und übergibt dabei alle bisherigen Befehlszeilenargumente (`std::env::args`). +/// +/// Da bei erfolgreicher Ausführung der bestehende Prozess durch den `sudo`-Aufruf +/// ersetzt wird ([`CommandExt::exec`]), kehrt diese Funktion im Erfolgsfall nicht zurück. +/// +/// # Returns +/// +/// Gibt einen [`std::io::Error`] zurück, falls die Ausführung von `sudo` fehlschlägt. +/// +/// # Beispiele +/// +/// ```no_run +/// use sudo_ctdra::{is_run_as_root, run_as_root}; +/// +/// if !is_run_as_root() { +/// let err = run_as_root(); +/// eprintln!("Fehler beim Neustart mit Root-Rechten: {err}"); +/// } +/// ``` +pub fn run_as_root() -> io::Error { + let commandline_args: Vec = env::args().collect(); + Command::new("sudo").args(&commandline_args).exec() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_run_as_root_matches_libc_geteuid() { + let expected = unsafe { libc::geteuid() == 0 }; + assert_eq!(is_run_as_root(), expected); + } + + #[test] + fn test_is_run_as_root_consistency() { + let first = is_run_as_root(); + let second = is_run_as_root(); + assert_eq!(first, second); + } +} diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs new file mode 100644 index 0000000..b7036dc --- /dev/null +++ b/tests/integration_tests.rs @@ -0,0 +1,39 @@ +use std::process::Command; +use sudo_ctdra::{is_run_as_root, run_as_root}; + +#[test] +fn test_public_api_is_run_as_root() { + let is_root = is_run_as_root(); + let expected = unsafe { libc::geteuid() == 0 }; + assert_eq!(is_root, expected); +} + +#[test] +fn test_run_as_root_returns_io_error_when_command_fails() { + if std::env::var("TEST_RUN_AS_ROOT_SUBPROCESS").is_ok() { + let err = run_as_root(); + if err.kind() == std::io::ErrorKind::NotFound { + std::process::exit(42); + } else { + std::process::exit(1); + } + } + + let current_exe = + std::env::current_exe().expect("Pfad zur Test-Executable konnte nicht ermittelt werden"); + let output = Command::new(current_exe) + .arg("test_run_as_root_returns_io_error_when_command_fails") + .arg("--exact") + .arg("--nocapture") + .env("TEST_RUN_AS_ROOT_SUBPROCESS", "1") + .env("PATH", "") + .output() + .expect("Subprozess konnte nicht ausgeführt werden"); + + assert_eq!( + output.status.code(), + Some(42), + "Subprozess sollte mit Exit-Code 42 beendet worden sein (ErrorKind::NotFound). Stderr: {}", + String::from_utf8_lossy(&output.stderr) + ); +}