Skip to content

Commit 5f6407e

Browse files
committed
feat: Add integration tests and --version
1 parent 1e48dcb commit 5f6407e

File tree

18 files changed

+892
-66
lines changed

18 files changed

+892
-66
lines changed

.cargo/config.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,22 @@ test-root = [
1010
"--"
1111
]
1212

13-
coverage = [
13+
integration-test = [
14+
"test",
15+
"--test", "integration_tests",
16+
"--features", "finder",
1417
"--config",
1518
"target.\"cfg(all())\".runner=\"/usr/bin/sudo -E\"",
19+
"--",
20+
"--nocapture",
21+
"--test-threads=1"
22+
]
23+
24+
coverage = [
1625
"tarpaulin",
1726
"--workspace",
1827
"--all-features",
28+
"--all-targets",
1929
"--timeout",
2030
"120",
2131
"--exclude-files",

.github/workflows/tests.yml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,17 @@ jobs:
2828
- name: Install Dependencies
2929
run: cargo xtask dependencies -dip sudo
3030

31-
- name: run tests with coverage
32-
run: cargo +nightly tarpaulin --verbose --all-features --workspace --timeout 120 --exclude-files build.rs xtask/src/* -e xtask --out Xml
31+
- name: build integration coverage
32+
run: sudo -E cargo +nightly coverage --out Xml
33+
env:
34+
RAR_AUTHENTICATION: skip
35+
RAR_CFG_PATH: target/rootasrole.json
36+
SKIP_BUILD: true
3337

3438
- name: Upload coverage reports to Codecov
3539
uses: codecov/codecov-action@v3
3640
env:
3741
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
3842
with:
3943
file: cobertura.xml
40-
flags: unittests
41-
42-
- name: run tests with coverage as Admin
43-
run: sudo -E /usr/local/cargo/bin/cargo +nightly tarpaulin --verbose --all-features --workspace --timeout 120 --exclude-files build.rs xtask/src/* -e xtask --out Xml
44-
45-
- name: Upload coverage reports to Codecov as Admin
46-
uses: codecov/codecov-action@v3
47-
env:
48-
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
49-
with:
50-
file: cobertura.xml
51-
flags: admin-unittests
44+
flags: unittests

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ name = "chsr"
5151
path = "src/chsr/main.rs"
5252
required-features = ["editor"]
5353

54-
# Integration tests that require root privileges
5554
[[test]]
5655
name = "integration_tests"
5756
path = "tests/integration_tests.rs"

rar-common/src/util.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,12 @@ pub fn subsribe(_: &str) -> io::Result<()> {
260260
pub fn subsribe(tool: &str) -> io::Result<()> {
261261
use log::LevelFilter;
262262
use syslog::Facility;
263-
syslog::init(Facility::LOG_AUTH, LevelFilter::Info, Some(tool))?;
263+
syslog::init(Facility::LOG_AUTH, LevelFilter::Info, Some(tool)).map_err(|e| {
264+
io::Error::new(
265+
io::ErrorKind::Other,
266+
format!("Failed to connect to syslog: {}", e),
267+
)
268+
})?;
264269
Ok(())
265270
}
266271

@@ -347,6 +352,7 @@ pub fn open_lock_with_privileges<P: AsRef<Path>>(
347352
}
348353

349354
pub fn read_with_privileges<P: AsRef<Path>>(p: P) -> std::io::Result<File> {
355+
debug!("Opening file {:?}", p.as_ref());
350356
std::fs::File::open(&p).or_else(|e| {
351357
if e.kind() != std::io::ErrorKind::PermissionDenied {
352358
return Err(e);

src/chsr/cli/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
pub(crate) mod data;
2+
#[cfg(not(tarpaulin_include))]
23
#[cfg(feature = "editor")]
34
pub(crate) mod editor;
45
pub(crate) mod pair;
56
pub(crate) mod process;
7+
//TODO: UI miri tests
8+
#[cfg(not(tarpaulin_include))]
69
pub(crate) mod usage;
710

811
use std::{cell::RefCell, error::Error, path::PathBuf, rc::Rc};

src/chsr/util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,4 @@ where
6868
.map(|s| escape_parser_string(s))
6969
.collect::<Vec<String>>()
7070
.join(" ")
71-
}
71+
}

src/sr/finder/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use api::{register_plugins, Api, ApiEvent};
1111
use capctl::CapSet;
1212
use de::{ConfigFinderDeserializer, DConfigFinder, DLinkedCommand, DLinkedRole, DLinkedTask};
1313
use log::debug;
14+
use nix::unistd::User;
1415
use options::BorrowedOptStack;
1516
use rar_common::{
1617
database::{
@@ -139,7 +140,15 @@ impl BestExecSettings {
139140
}
140141
result.env = opt_stack
141142
.calc_temp_env(opt_stack.calc_override_behavior(), &cli.opt_filter)
142-
.calc_final_env(env_vars, opt_stack.calc_path(env_path), cred)?;
143+
.calc_final_env(
144+
env_vars,
145+
opt_stack.calc_path(env_path),
146+
cred,
147+
result
148+
.setuid
149+
.and_then(|x| User::from_uid(x.into()).expect("Target user do not exist")),
150+
format!("{}{}",cli.cmd_path.display(), if cli.cmd_args.is_empty() { "".into() } else { format!(" {}", cli.cmd_args.join(" ")) } )
151+
)?;
143152
result.auth = opt_stack.calc_authentication();
144153
result.bounding = opt_stack.calc_bounding();
145154
result.timeout = opt_stack.calc_timeout();

src/sr/finder/options.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use chrono::Duration;
77
use konst::primitive::parse_i64;
88
use konst::{iter, option, result, slice, string, unwrap_ctx};
99
use libc::PATH_MAX;
10+
use nix::unistd::User;
1011
use rar_common::database::options::{
1112
EnvBehavior, Level, PathBehavior, SAuthentication, SBounding, SPathOptions, SPrivileged,
1213
STimeout, TimestampType,
@@ -283,7 +284,9 @@ impl<'a> DEnvOptions<'a> {
283284
&self,
284285
env_vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
285286
env_path: impl IntoIterator<Item = impl AsRef<str>>,
286-
target: &Cred,
287+
current_user: &Cred,
288+
target: Option<User>,
289+
command: String,
287290
) -> SrResult<HashMap<String, String>> {
288291
let mut final_set = match self.default_behavior {
289292
EnvBehavior::Inherit => {
@@ -329,16 +332,21 @@ impl<'a> DEnvOptions<'a> {
329332
}
330333
}),
331334
);
332-
final_set.insert("LOGNAME".into(), target.user.name.clone());
333-
final_set.insert("USER".into(), target.user.name.clone());
334-
final_set.insert("HOME".into(), target.user.dir.to_string_lossy().to_string());
335-
final_set
336-
.entry("TERM".into())
337-
.or_insert_with(|| "unknown".into());
335+
let target_user = target.unwrap_or_else(|| current_user.user.clone());
336+
final_set.insert("LOGNAME".into(), target_user.name.clone());
337+
final_set.insert("USER".into(), target_user.name.clone());
338+
final_set.insert("HOME".into(), target_user.dir.to_string_lossy().to_string());
338339
final_set.insert(
339340
"SHELL".into(),
340-
target.user.shell.to_string_lossy().to_string(),
341+
target_user.shell.to_string_lossy().to_string(),
341342
);
343+
final_set.insert("RAR_UID".into(), current_user.user.uid.to_string());
344+
final_set.insert("RAR_GID".into(), current_user.user.gid.to_string());
345+
final_set.insert("RAR_USER".into(), current_user.user.name.clone());
346+
final_set.insert("RAR_COMMAND".into(), command);
347+
final_set
348+
.entry("TERM".into())
349+
.or_insert_with(|| "unknown".into());
342350
final_set.extend(
343351
self.set
344352
.iter()
@@ -1065,16 +1073,15 @@ mod tests {
10651073
];
10661074
let env_path = vec!["/usr/local/bin", "/usr/bin"];
10671075
let target = Cred::builder().build();
1068-
let result = env_options.calc_final_env(env_vars, &env_path, &target);
1076+
let result = env_options.calc_final_env(env_vars, &env_path, &target, None, String::new());
10691077
assert!(
10701078
result.is_ok(),
10711079
"Failed to calculate final env {}",
10721080
result.unwrap_err()
10731081
);
10741082
let final_env = result.unwrap();
10751083
assert_eq!(final_env.get("PATH").unwrap(), "/usr/local/bin:/usr/bin");
1076-
assert_eq!(*final_env.get("LOGNAME").unwrap(), target.user.name);
1077-
assert_eq!(*final_env.get("USER").unwrap(), target.user.name);
1084+
assert_eq!(*final_env.get("RAR_USER").unwrap(), target.user.name);
10781085
assert_eq!(
10791086
*final_env.get("HOME").unwrap(),
10801087
target.user.dir.to_string_lossy()
@@ -1107,7 +1114,7 @@ mod tests {
11071114
];
11081115
let env_path = vec!["/usr/local/bin", "/usr/bin"];
11091116
let target = Cred::builder().build();
1110-
let result = env_options.calc_final_env(env_vars, &env_path, &target);
1117+
let result = env_options.calc_final_env(env_vars, &env_path, &target, None, String::new());
11111118
assert!(
11121119
result.is_ok(),
11131120
"Failed to calculate final env {}",
@@ -1149,7 +1156,7 @@ mod tests {
11491156
];
11501157
let env_path = vec!["/usr/local/bin", "/usr/bin"];
11511158
let target = Cred::builder().build();
1152-
let result = env_options.calc_final_env(env_vars, &env_path, &target);
1159+
let result = env_options.calc_final_env(env_vars, &env_path, &target, None, String::new());
11531160
assert!(result.is_err());
11541161
}
11551162

src/sr/main.rs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const ROOTASROLE: &str = "target/rootasrole.json";
4141
//and each role has a set of rights to execute commands.";
4242

4343
const USAGE: &str = formatcp!(
44-
r#"{UNDERLINE}{BOLD}Usage:{RST} {BOLD}sr{RST} [OPTIONS] [COMMAND]...
44+
r#"{UNDERLINE}{BOLD}Usage:{RST} {BOLD}dosr{RST} [OPTIONS] [COMMAND]...
4545
4646
{UNDERLINE}{BOLD}Arguments:{RST}
4747
[COMMAND]...
@@ -59,17 +59,20 @@ const USAGE: &str = formatcp!(
5959
6060
{BOLD}-p, --prompt <PROMPT>{RST}
6161
Prompt option allows you to override the default password prompt and use a custom one
62-
6362
[default: "Password: "]
6463
6564
{BOLD}-u, --user <USER>{RST}
6665
Specify the user to execute the command as
66+
6767
{BOLD}-g --group <GROUP>(,<GROUP>...){RST}
6868
Specify the group to execute the command as
6969
7070
{BOLD}-i, --info{RST}
7171
Display rights of executor
7272
73+
{BOLD}-v, --version{RST}
74+
Print dosr version
75+
7376
{BOLD}-h, --help{RST}
7477
Print help (see a summary with '-h')"#,
7578
UNDERLINE = UNDERLINE,
@@ -179,6 +182,10 @@ where
179182
"-h" | "--help" => {
180183
args.help = true;
181184
}
185+
"-v" | "--version" => {
186+
println!("dosr: version {}", env!("CARGO_PKG_VERSION"));
187+
std::process::exit(0);
188+
}
182189
_ => {
183190
if arg.as_ref().starts_with('-') {
184191
error!("Unknown option: {}", arg.as_ref());
@@ -214,13 +221,51 @@ where
214221
}
215222

216223
#[cfg(not(tarpaulin_include))]
217-
fn main() -> SrResult<()> {
224+
fn main() {
225+
if let Err(e) = subsribe("sr") {
226+
eprintln!("sr: Failed to initialize logging: {}", e);
227+
std::process::exit(1);
228+
}
229+
if let Err(e) = main_inner() {
230+
eprintln!("sr: {}", e);
231+
232+
use nix::unistd::{Uid, User};
233+
if let SrError::InsufficientPrivileges = e {
234+
error!("Insufficient privileges to run sr. {}", CAPABILITIES_ERROR);
235+
} else if let SrError::AuthenticationFailed = e {
236+
error!(
237+
"Authentication failed for user '{}', when trying running '''{}'''",
238+
User::from_uid(Uid::current())
239+
.and_then(|u| u
240+
.and_then(|u| Some(u.name))
241+
.ok_or(nix::errno::Errno::EAGAIN))
242+
.unwrap_or(Uid::current().to_string()),
243+
std::env::args().skip(1).collect::<Vec<_>>().join(" ")
244+
);
245+
eprintln!("This incident is reported.");
246+
} else {
247+
error!(
248+
"User '{}' got a '{}' when trying running '''{}'''",
249+
User::from_uid(Uid::current())
250+
.and_then(|u| u
251+
.and_then(|u| Some(u.name))
252+
.ok_or(nix::errno::Errno::EAGAIN))
253+
.unwrap_or(Uid::current().to_string()),
254+
e,
255+
std::env::args().skip(1).collect::<Vec<_>>().join(" ")
256+
);
257+
}
258+
std::process::exit(1);
259+
}
260+
}
261+
262+
#[cfg(not(tarpaulin_include))]
263+
fn main_inner() -> SrResult<()> {
218264
use std::env;
219265

220266
use crate::{pam::check_auth, ROOTASROLE};
221267
use finder::find_best_exec_settings;
222268

223-
subsribe("sr")?;
224269
debug!("Started with capabilities: {:?}", CapState::get_current()?);
225270
drop_effective()?;
226271
let args = std::env::args();

tests/fixtures/env_override.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"version": "3.2.0",
3+
"options": {
4+
"env": {
5+
"default": "delete",
6+
"override_behavior": false,
7+
"keep": [
8+
"KEEP"
9+
],
10+
"check": [
11+
"TZ"
12+
],
13+
"delete": [
14+
"DELETE"
15+
]
16+
}
17+
},
18+
"roles": [
19+
{
20+
"name": "env",
21+
"actors": [
22+
{
23+
"type": "user",
24+
"name": "root"
25+
}
26+
],
27+
"tasks": [
28+
{
29+
"name": "allowed",
30+
"purpose": "Commands that allow environment override",
31+
"commands": [
32+
"/usr/bin/env ^.*$"
33+
],
34+
"options": {
35+
"env": {
36+
"override_behavior": true,
37+
"set": {
38+
"TASK": "allowed"
39+
}
40+
}
41+
}
42+
},
43+
{
44+
"name": "denied",
45+
"purpose": "Commands that deny environment override",
46+
"commands": [
47+
"/usr/bin/env ^.*$"
48+
],
49+
"options": {
50+
"env": {
51+
"override_behavior": false,
52+
"set": {
53+
"TASK": "denied"
54+
}
55+
}
56+
}
57+
}
58+
],
59+
"options": {
60+
"env": {
61+
"set": {
62+
"ROLE": "env"
63+
}
64+
}
65+
}
66+
}
67+
]
68+
}

0 commit comments

Comments
 (0)