cli.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. use crate::{
  2. client_management, config,
  3. key::{self, KeyType},
  4. schema::{self, UIDCDatabase},
  5. scope_management, server, token_management, UIDCError,
  6. };
  7. use clap::{Parser, Subcommand};
  8. use microrm::{prelude::*, schema::Stored};
  9. use microrm::cli::Autogenerate;
  10. mod role;
  11. mod user;
  12. impl microrm::cli::CLIError for UIDCError {
  13. fn no_such_entity(ename: &'static str, keys: String) -> Self {
  14. UIDCError::AbortString(format!("no such {ename} matching {keys}"))
  15. }
  16. }
  17. #[derive(Debug, Parser)]
  18. #[clap(author, version, about, long_about = None)]
  19. struct RootArgs {
  20. #[clap(short, long, env = "UIDC_DB", default_value_t = String::from("uidc.db"))]
  21. /// Database path
  22. db: String,
  23. #[clap(short, long, default_value_t = String::from("primary"))]
  24. /// Which realm to use, for non-server only
  25. realm: String,
  26. #[clap(subcommand)]
  27. command: Command,
  28. }
  29. #[derive(Debug, Subcommand)]
  30. enum Command {
  31. /// database initialization
  32. Init,
  33. /// OAuth2 client management
  34. Client(ClientArgs),
  35. /// general configuration
  36. Config(ConfigArgs),
  37. /// permissions grouping management
  38. Group(GroupArgs),
  39. /// key management
  40. Key(KeyArgs),
  41. /// scope management
  42. Scope(ScopeArgs),
  43. /// run the actual OIDC server
  44. Server(ServerArgs),
  45. /// manual token generation and inspection
  46. Token(TokenArgs),
  47. /// role management
  48. Role(RoleArgs),
  49. /// user management
  50. User(UserArgs),
  51. }
  52. struct RunArgs {
  53. db: UIDCDatabase,
  54. realm: Stored<schema::Realm>,
  55. }
  56. impl RootArgs {
  57. async fn run(self) -> Result<(), UIDCError> {
  58. if let Command::Init = self.command {
  59. return self.init().await;
  60. }
  61. let db = UIDCDatabase::open_path(&self.db)
  62. .map_err(|e| UIDCError::AbortString(format!("Error accessing database: {:?}", e)))?;
  63. let realm = db
  64. .realms
  65. .keyed(&self.realm)
  66. .get()?
  67. .ok_or(UIDCError::Abort("no such realm"))?;
  68. let ra = RunArgs {
  69. db,
  70. realm,
  71. };
  72. match self.command {
  73. Command::Init => unreachable!(),
  74. Command::Config(v) => v.run(ra).await,
  75. Command::Key(v) => v.run(ra).await,
  76. Command::Client(v) => v.run(ra).await,
  77. Command::Scope(v) => v.run(ra).await,
  78. Command::Group(v) => v.run(ra).await,
  79. Command::Server(v) => v.run(ra).await,
  80. Command::Token(v) => v.run(ra).await,
  81. Command::Role(v) => v.run(ra).await,
  82. Command::User(v) => v.run(ra).await,
  83. }
  84. }
  85. async fn init(&self) -> Result<(), UIDCError> {
  86. // first check to see if the database is already vaguely set up
  87. let db = UIDCDatabase::open_path(&self.db)
  88. .map_err(|e| UIDCError::AbortString(format!("Error accessing database: {:?}", e)))?;
  89. log::info!("Initializing!");
  90. let primary_realm = "primary".to_string();
  91. if db.realms.keyed(&primary_realm).get()?.is_some() {
  92. log::warn!("Already initialized with primary realm!");
  93. return Ok(());
  94. }
  95. // create primary realm
  96. db.realms.insert(schema::Realm {
  97. shortname: primary_realm,
  98. ..Default::default()
  99. })?;
  100. Ok(())
  101. }
  102. }
  103. #[derive(Debug, Subcommand)]
  104. enum KeyCommand {
  105. /// Print details of all keys
  106. List,
  107. /// Generate a new key; see types subcommand for valid options.
  108. Generate { key_type: KeyType },
  109. /// List what keytypes are supported
  110. Types,
  111. /// Remove a key from use
  112. Remove { key_id: String },
  113. }
  114. #[derive(Debug, Parser)]
  115. struct KeyArgs {
  116. #[clap(subcommand)]
  117. command: KeyCommand,
  118. }
  119. impl KeyArgs {
  120. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  121. match &self.command {
  122. KeyCommand::List => key::list(&args.realm),
  123. KeyCommand::Generate { key_type } => {
  124. key::generate_in(&args.realm, *key_type)?;
  125. return Ok(());
  126. }
  127. KeyCommand::Types => {
  128. for (spec, _kty) in key::KEY_TYPE_NAMES {
  129. println!("- {}", spec);
  130. }
  131. Ok(())
  132. }
  133. KeyCommand::Remove { key_id } => key::remove(&args.realm, key_id),
  134. }
  135. }
  136. }
  137. #[derive(Debug, Subcommand)]
  138. enum ClientCommand {
  139. Create {
  140. /// Name for the new client
  141. name: String,
  142. /// Signing key type to use for this client. Default is ed25519.
  143. key_type: Option<KeyType>,
  144. },
  145. List,
  146. Inspect {
  147. name: String,
  148. },
  149. }
  150. #[derive(Debug, Parser)]
  151. struct ClientArgs {
  152. #[clap(subcommand)]
  153. command: ClientCommand,
  154. }
  155. impl ClientArgs {
  156. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  157. todo!()
  158. /*
  159. match self.command {
  160. ClientCommand::Create { name, key_type } => {
  161. client_management::create(&args.realm, name, key_type.unwrap_or(KeyType::Ed25519))
  162. }
  163. ClientCommand::List => {
  164. todo!()
  165. }
  166. ClientCommand::Inspect { name } => client_management::inspect(&args.realm, name),
  167. }
  168. */
  169. }
  170. }
  171. #[derive(Debug, Subcommand)]
  172. enum ConfigCommand {
  173. Dump,
  174. Load { toml_path: String },
  175. Set { key: String, value: String },
  176. }
  177. #[derive(Debug, Parser)]
  178. struct ConfigArgs {
  179. #[clap(subcommand)]
  180. command: ConfigCommand,
  181. }
  182. impl ConfigArgs {
  183. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  184. match &self.command {
  185. ConfigCommand::Dump => {
  186. let config = config::Config::build_from(&args.db, None);
  187. println!("{:?}", config);
  188. }
  189. ConfigCommand::Set { key, value } => {
  190. args.db.persistent_config.keyed(key).delete()?;
  191. args.db.persistent_config.insert(schema::PersistentConfig {
  192. key: key.clone(),
  193. value: value.clone(),
  194. })?;
  195. }
  196. ConfigCommand::Load { toml_path } => {
  197. let config = config::Config::build_from(&args.db, Some(toml_path));
  198. config.save(&args.db);
  199. }
  200. }
  201. Ok(())
  202. }
  203. }
  204. #[derive(Debug, Subcommand)]
  205. enum GroupCommand {
  206. Create {
  207. group_name: String,
  208. },
  209. Members {
  210. group_name: String,
  211. },
  212. Roles {
  213. group_name: String,
  214. },
  215. List,
  216. AttachRole {
  217. group_name: String,
  218. role_name: String,
  219. },
  220. DetachRole {
  221. group_name: String,
  222. role_name: String,
  223. },
  224. AttachUser {
  225. group_name: String,
  226. username: String,
  227. },
  228. DetachUser {
  229. group_name: String,
  230. username: String,
  231. },
  232. }
  233. #[derive(Debug, Parser)]
  234. struct GroupArgs {
  235. #[clap(subcommand)]
  236. command: GroupCommand,
  237. }
  238. impl GroupArgs {
  239. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  240. todo!()
  241. /*match &self.command {
  242. GroupCommand::Create { group_name } => {
  243. group_management::create_group(&args.realm, group_name)?;
  244. }
  245. GroupCommand::Members { group_name } => {
  246. group_management::list_members(&args.realm, group_name)?;
  247. }
  248. GroupCommand::Roles { group_name } => {
  249. group_management::list_roles(&args.realm, group_name)?;
  250. }
  251. GroupCommand::List => {
  252. group_management::list_groups(&args.realm)?;
  253. }
  254. GroupCommand::AttachRole {
  255. group_name,
  256. role_name,
  257. } => {
  258. group_management::attach_role(&args.realm, group_name, role_name)?;
  259. }
  260. GroupCommand::DetachRole {
  261. group_name,
  262. role_name,
  263. } => {
  264. group_management::detach_role(&args.realm, group_name, role_name)?;
  265. }
  266. GroupCommand::AttachUser {
  267. group_name,
  268. username,
  269. } => {
  270. group_management::attach_user(&args.realm, group_name, username)?;
  271. }
  272. GroupCommand::DetachUser {
  273. group_name,
  274. username,
  275. } => {
  276. group_management::detach_user(&args.realm, group_name, username)?;
  277. }
  278. }*/
  279. // Ok(())
  280. }
  281. }
  282. #[derive(Debug, Subcommand)]
  283. enum ScopeCommand {
  284. AttachRole {
  285. scope_name: String,
  286. role_name: String,
  287. },
  288. Create {
  289. scope_name: String,
  290. },
  291. DetachRole {
  292. scope_name: String,
  293. role_name: String,
  294. },
  295. Inspect {
  296. scope_name: String,
  297. },
  298. List,
  299. }
  300. #[derive(Debug, Parser)]
  301. struct ScopeArgs {
  302. #[clap(subcommand)]
  303. command: ScopeCommand,
  304. }
  305. impl ScopeArgs {
  306. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  307. match &self.command {
  308. ScopeCommand::AttachRole {
  309. scope_name,
  310. role_name,
  311. } => scope_management::attach_role(&args.realm, scope_name, role_name),
  312. ScopeCommand::Create { scope_name } => {
  313. scope_management::create_scope(&args.realm, scope_name)
  314. }
  315. ScopeCommand::DetachRole {
  316. scope_name,
  317. role_name,
  318. } => scope_management::detach_role(&args.realm, scope_name, role_name),
  319. ScopeCommand::Inspect { scope_name } => {
  320. scope_management::inspect_scope(&args.realm, scope_name)
  321. }
  322. ScopeCommand::List => scope_management::list_scopes(&args.realm),
  323. }
  324. }
  325. }
  326. #[derive(Debug, Parser)]
  327. struct ServerArgs {
  328. #[clap(short, long)]
  329. port: Option<u16>,
  330. }
  331. impl ServerArgs {
  332. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  333. let config = config::Config::build_from(&args.db, None);
  334. server::run_server(args.db, config, self.port.unwrap_or(2114)).await
  335. }
  336. }
  337. #[derive(Debug, Subcommand)]
  338. enum TokenCommand {
  339. GenerateAuth {
  340. #[clap(short, long)]
  341. client: String,
  342. #[clap(short, long)]
  343. username: String,
  344. #[clap(short, long)]
  345. scopes: String,
  346. },
  347. GenerateRefresh {
  348. #[clap(short, long)]
  349. client: String,
  350. #[clap(short, long)]
  351. username: String,
  352. #[clap(short, long)]
  353. scopes: String,
  354. },
  355. Inspect {
  356. token: Option<String>,
  357. },
  358. }
  359. #[derive(Debug, Parser)]
  360. struct TokenArgs {
  361. #[clap(subcommand)]
  362. command: TokenCommand,
  363. }
  364. impl TokenArgs {
  365. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  366. let config = config::Config::build_from(&args.db, None);
  367. match &self.command {
  368. TokenCommand::GenerateAuth {
  369. client,
  370. username,
  371. scopes,
  372. } => {
  373. let token = token_management::create_auth_token(
  374. &args.realm,
  375. &config,
  376. client,
  377. username,
  378. scopes,
  379. )?;
  380. println!("{}", token);
  381. Ok(())
  382. }
  383. TokenCommand::GenerateRefresh {
  384. client,
  385. username,
  386. scopes,
  387. } => {
  388. let token = token_management::create_refresh_token(
  389. &args.realm,
  390. &config,
  391. client,
  392. username,
  393. scopes,
  394. )?;
  395. println!("{}", token);
  396. Ok(())
  397. }
  398. TokenCommand::Inspect { token } => {
  399. token_management::inspect_token(&config, &args.realm, token.as_ref())
  400. }
  401. }
  402. }
  403. }
  404. #[derive(Debug, Subcommand)]
  405. enum RoleCommand {
  406. List,
  407. Create { name: String },
  408. Delete { name: String },
  409. }
  410. #[derive(Debug, Parser)]
  411. struct RoleArgs {
  412. #[clap(subcommand)]
  413. command: Autogenerate<role::RoleInterface>,
  414. }
  415. impl RoleArgs {
  416. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  417. self.command.perform(&args.realm, &args.realm.roles, &args.realm.roles)
  418. }
  419. }
  420. #[derive(Debug, Subcommand)]
  421. enum UserCommand {
  422. Auth {
  423. username: String,
  424. #[clap(short = 'p', long, action = clap::ArgAction::Count)]
  425. change_password: usize,
  426. #[clap(short = 't', long, action = clap::ArgAction::Count)]
  427. change_totp: usize,
  428. },
  429. }
  430. #[derive(Debug, Parser)]
  431. struct UserArgs {
  432. #[clap(subcommand)]
  433. command: Autogenerate<user::UserInterface>,
  434. }
  435. impl UserArgs {
  436. async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
  437. self.command.perform(&args.realm, &args.realm.users, &args.realm.users)
  438. }
  439. }
  440. pub fn invoked() {
  441. let args = RootArgs::parse();
  442. match smol::block_on(args.run()) {
  443. Ok(_) => (),
  444. Err(e) => {
  445. log::error!("Error occured while running command: {}", e);
  446. }
  447. }
  448. }