Skip to main content

bitwarden_access_connector/
cli.rs

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