kanidm_unix_resolver/
lib.rs

1#![deny(warnings)]
2#![warn(unused_extern_crates)]
3#![deny(clippy::todo)]
4#![deny(clippy::unimplemented)]
5#![deny(clippy::unwrap_used)]
6#![deny(clippy::expect_used)]
7#![deny(clippy::panic)]
8#![deny(clippy::unreachable)]
9#![deny(clippy::await_holding_lock)]
10#![deny(clippy::needless_pass_by_value)]
11#![deny(clippy::trivially_copy_pass_by_ref)]
12
13use std::{fs, path::PathBuf};
14
15#[cfg(target_family = "unix")]
16#[macro_use]
17extern crate tracing;
18#[cfg(target_family = "unix")]
19#[macro_use]
20extern crate rusqlite;
21
22#[cfg(target_family = "unix")]
23pub mod db;
24#[cfg(target_family = "unix")]
25pub mod idprovider;
26#[cfg(target_family = "unix")]
27pub mod resolver;
28
29/// Check for passwd and groups lines and return them if they're missing, this allows us to test parsing
30pub fn parse_nsswitch_contents_return_missing(contents: &str) -> Vec<String> {
31    contents
32        .lines()
33        .filter_map(|line| {
34            let line = line.trim();
35            for tag in ["passwd:", "group:"] {
36                if line.starts_with(tag) {
37                    debug!(
38                        "Found {} line in nsswitch.conf: {}",
39                        tag.trim_end_matches(':'),
40                        line
41                    );
42                    if !line.contains("kanidm") {
43                        warn!(
44                            "nsswitch.conf {} line does not contain string 'kanidm': {}",
45                            tag.trim_end_matches(':'),
46                            line
47                        );
48                        return Some(line.to_string());
49                    } else {
50                        debug!(
51                            "nsswitch.conf {} line contains string 'kanidm': {}",
52                            tag.trim_end_matches(':'),
53                            line
54                        );
55                        return None;
56                    }
57                }
58            }
59            None
60        })
61        .collect()
62}
63
64/// Warn the admin that user/group resolution may fail if Kanidm is not configured in nsswitch.conf. This is a common misconfiguration that can lead to confusion, and is worth proactively warning about.
65pub fn check_nsswitch_has_kanidm(path: Option<PathBuf>) -> bool {
66    // returns true if kanidm is configured in nsswitch.conf, false otherwise.
67    let nsswitch_conf = path.unwrap_or_else(|| PathBuf::from("/etc/nsswitch.conf"));
68    if nsswitch_conf.exists() {
69        match fs::read_to_string(&nsswitch_conf) {
70            Ok(contents) => {
71                let missing_lines = parse_nsswitch_contents_return_missing(&contents);
72                if missing_lines.is_empty() {
73                    debug!(
74                        "{} appears to have Kanidm configured OK for passwd/group resolution",
75                        nsswitch_conf.display()
76                    );
77                    true
78                } else {
79                    warn!("Kanidm does not appear to be configured in {} for passwd/group resolution. Lines of interest: {:?}", nsswitch_conf.display(), missing_lines);
80                    false
81                }
82            }
83            Err(err) => {
84                debug!(
85                    ?err,
86                    "Couldn't read {} to check for Kanidm presence",
87                    nsswitch_conf.display()
88                );
89                false
90            }
91        }
92    } else {
93        debug!(
94            "Couldn't read {} to check for Kanidm presence - file does not exist",
95            nsswitch_conf.display()
96        );
97        false
98    }
99}