1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use clap::{Parser, Subcommand};
use std::fmt;
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "kaji", author, version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
/// Keycloak Server URL
#[arg(long, env = "KEYCLOAK_URL", required_unless_present = "profile")]
pub server: Option<String>,
/// Keycloak Realms to consider. If empty, all realms are considered.
#[arg(long, env = "KEYCLOAK_REALMS", value_delimiter = ',')]
pub realms: Vec<String>,
/// Keycloak Admin User
#[arg(long, env = "KEYCLOAK_USER")]
pub user: Option<String>,
/// Keycloak Admin Password
#[arg(skip)]
pub password: Option<String>,
/// Keycloak Client ID (for client credentials grant)
#[arg(long, env = "KEYCLOAK_CLIENT_ID", default_value = "admin-cli")]
pub client_id: String,
/// Keycloak Client Secret (for client credentials grant)
#[arg(skip)]
pub client_secret: Option<String>,
/// Profile name to load from profiles/ directory
#[arg(long, short = 'p')]
pub profile: Option<String>,
/// HashiCorp Vault URL
#[arg(long, env = "VAULT_ADDR")]
pub vault_addr: Option<String>,
/// HashiCorp Vault Token
#[arg(long, env = "VAULT_TOKEN")]
pub vault_token: Option<String>,
}
impl fmt::Debug for Cli {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Cli")
.field("command", &self.command)
.field("server", &self.server)
.field("realms", &self.realms)
.field("user", &self.user)
.field("password", &self.password.as_ref().map(|_| "********"))
.field("client_id", &self.client_id)
.field(
"client_secret",
&self.client_secret.as_ref().map(|_| "********"),
)
.field("profile", &self.profile)
.field("vault_addr", &self.vault_addr)
.field(
"vault_token",
&self.vault_token.as_ref().map(|_| "********"),
)
.finish()
}
}
#[derive(Subcommand, Debug)]
pub enum Commands {
/// Inspect the current Keycloak configuration and dump to files
Inspect {
/// Workspace directory for configuration files
#[arg(long, short = 'w', default_value = "workspace")]
workspace: PathBuf,
/// Skip confirmation prompt when overwriting local files
#[arg(long, short = 'y', default_value = "false")]
yes: bool,
},
/// Validate the local Keycloak configuration files
Validate {
/// Workspace directory containing configuration files
#[arg(long, short = 'w', default_value = "workspace")]
workspace: PathBuf,
},
/// Apply the local Keycloak configuration to the server
Apply {
/// Workspace directory containing configuration files
#[arg(long, short = 'w', default_value = "workspace")]
workspace: PathBuf,
/// Skip confirmation prompt
#[arg(long, short = 'y', default_value = "false")]
yes: bool,
/// Ask for confirmation before applying each resource
#[arg(long, short = 'r', default_value = "false")]
review: bool,
},
/// Plan the application of the local Keycloak configuration
Plan {
/// Workspace directory containing configuration files
#[arg(long, short = 'w', default_value = "workspace")]
workspace: PathBuf,
/// Show only changes, suppressing "No changes" messages
#[arg(long, short = 'c')]
changes_only: bool,
/// Ask interactively whether to include each change in the plan
#[arg(long, short = 'i', default_value = "false")]
interactive: bool,
},
/// Check for drift between local configuration and server
Drift {
/// Workspace directory containing configuration files
#[arg(long, short = 'w', default_value = "workspace")]
workspace: PathBuf,
},
/// Interactive CLI mode to generate local configuration
Cli {
/// Workspace directory for configuration files
#[arg(long, short = 'w', default_value = "workspace")]
workspace: PathBuf,
},
/// Clean the local configuration files
Clean {
/// Workspace directory containing configuration files
#[arg(long, short = 'w', default_value = "workspace")]
workspace: PathBuf,
/// Skip confirmation prompt
#[arg(long, short = 'y', default_value = "false")]
yes: bool,
},
}