Skip to main content

bitwarden_access_connector/
cli.rs

1//! Command-line arguments for the access connector.
2//!
3//! Settings live in the TOML config file, not flags; see [`crate::config::Config::from_cli`]. The
4//! token comes only from `BWAC_TOKEN`, since `argv` is visible through `ps` and
5//! `/proc/<pid>/cmdline`.
6
7use std::path::PathBuf;
8
9use clap::{Args, Parser, Subcommand};
10
11/// Bitwarden PAM access connector.
12#[derive(Debug, Parser)]
13#[command(name = "bwac", version)]
14pub struct Cli {
15    /// The subcommand to execute.
16    #[command(subcommand)]
17    pub command: Command,
18}
19
20/// Available subcommands.
21#[derive(Debug, Subcommand)]
22pub enum Command {
23    /// Start the access connector poll loop.
24    Run(RunArgs),
25}
26
27/// Arguments for the `run` subcommand.
28#[derive(Debug, Args)]
29pub struct RunArgs {
30    /// Path to the TOML configuration file.
31    #[arg(long, env = "BWAC_CONFIG", value_name = "PATH")]
32    pub(crate) config: Option<PathBuf>,
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn cli_run_parses_with_no_flags() {
41        let cli = Cli::try_parse_from(["bwac", "run"]).expect("should parse with no flags");
42
43        let Command::Run(args) = cli.command;
44        assert!(args.config.is_none());
45    }
46
47    #[test]
48    fn cli_config_path_parses() {
49        let cli = Cli::try_parse_from(["bwac", "run", "--config", "/etc/bwac/config.toml"])
50            .expect("should parse --config");
51
52        let Command::Run(args) = cli.command;
53        assert_eq!(
54            args.config,
55            Some(std::path::PathBuf::from("/etc/bwac/config.toml"))
56        );
57    }
58
59    #[test]
60    fn cli_rejects_unknown_token_arg() {
61        // `--token` must not be accepted as a clap arg (would expose value via ps).
62        let result = Cli::try_parse_from([
63            "bwac",
64            "run",
65            "--token",
66            "0.access-connector.some-id.secret:key==",
67        ]);
68        assert!(
69            result.is_err(),
70            "--token must not be an accepted arg; got: {result:?}"
71        );
72    }
73
74    #[test]
75    fn cli_rejects_unknown_token_file_arg() {
76        // `--token-file` must not be accepted either; the token is env-only.
77        let result = Cli::try_parse_from(["bwac", "run", "--token-file", "/etc/bwac/token"]);
78        assert!(
79            result.is_err(),
80            "--token-file must not be an accepted arg; got: {result:?}"
81        );
82    }
83
84    #[test]
85    fn cli_rejects_removed_settings_flags() {
86        // Settings live in the config file only.
87        for args in [
88            ["bwac", "run", "--poll-interval", "30"].as_slice(),
89            ["bwac", "run", "--api-url", "https://api.example.com"].as_slice(),
90            ["bwac", "run", "--entra-verify-probe"].as_slice(),
91        ] {
92            let result = Cli::try_parse_from(args.iter().copied());
93            assert!(
94                result.is_err(),
95                "removed settings flag must not parse: {args:?}"
96            );
97        }
98    }
99}