cli.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. use crate::{
  2. key, client_management, config, schema::{self, RealmID}, server, token, user_management, UIDCError, group_management, token_management,
  3. };
  4. use clap::{Parser, Subcommand};
  5. use microrm::prelude::*;
  6. #[derive(Debug, Parser)]
  7. #[clap(author, version, about, long_about = None)]
  8. struct RootArgs {
  9. #[clap(short, long, default_value_t = String::from("uidc.db"))]
  10. /// Database path
  11. db: String,
  12. #[clap(short, long, default_value_t = String::from("primary"))]
  13. /// Which realm to use, for non-server only
  14. realm: String,
  15. #[clap(subcommand)]
  16. command: Command,
  17. }
  18. #[derive(Debug, Subcommand)]
  19. enum Command {
  20. /// database initialization
  21. Init,
  22. /// OAuth2 client management
  23. Client(ClientArgs),
  24. /// general configuration
  25. Config(ConfigArgs),
  26. /// permissions grouping management
  27. Group(GroupArgs),
  28. /// key management
  29. Key(KeyArgs),
  30. /// run the actual OIDC server
  31. Server(ServerArgs),
  32. /// manual token generation and inspection
  33. Token(TokenArgs),
  34. /// role management
  35. Role(RoleArgs),
  36. /// user management
  37. User(UserArgs),
  38. }
  39. struct RunArgs {
  40. db: microrm::DB,
  41. realm_id: RealmID,
  42. }
  43. impl RootArgs {
  44. async fn run(&self) -> Result<(), UIDCError> {
  45. if let Command::Init = self.command {
  46. return self.init().await;
  47. }
  48. let db = microrm::DB::new(schema::schema(), &self.db, microrm::CreateMode::MustExist).map_err(|_| UIDCError::Abort("Error accessing database"))?;
  49. let realm_id = db.query_interface().get().by(schema::Realm::Shortname, self.realm.as_str()).one()?.ok_or(UIDCError::Abort("no such realm"))?.id();
  50. let ra = RunArgs { db: db, realm_id };
  51. match &self.command {
  52. Command::Init => unreachable!(),
  53. Command::Config(v) => v.run(ra).await,
  54. Command::Client(v) => v.run(ra).await,
  55. Command::Group(v) => v.run(ra).await,
  56. Command::Key(v) => v.run(ra).await,
  57. Command::Server(v) => v.run(ra).await,
  58. Command::Token(v) => v.run(ra).await,
  59. Command::Role(v) => v.run(ra).await,
  60. Command::User(v) => v.run(ra).await,
  61. }
  62. }
  63. async fn init(&self) -> Result<(), UIDCError> {
  64. // first check to see if the database is already vaguely set up
  65. let maybedb = microrm::DB::new(schema::schema(), &self.db, microrm::CreateMode::MustExist);
  66. if maybedb.is_ok() {
  67. return Err(UIDCError::Abort("Database already initialized, not overwriting!"));
  68. }
  69. log::info!("Initializing!");
  70. let db = microrm::DB::new(
  71. schema::schema(),
  72. &self.db,
  73. microrm::CreateMode::AllowNewDatabase,
  74. )
  75. .expect("Unable to initialize database!");
  76. // create primary realm
  77. db.query_interface()
  78. .add(&schema::Realm {
  79. shortname: "primary".to_string(),
  80. })?;
  81. Ok(())
  82. }
  83. }
  84. #[derive(Debug, Subcommand)]
  85. enum KeyCommand {
  86. Inspect,
  87. Generate,
  88. }
  89. #[derive(Debug, Parser)]
  90. struct KeyArgs {
  91. #[clap(subcommand)]
  92. command: KeyCommand,
  93. }
  94. impl KeyArgs {
  95. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  96. match &self.command {
  97. KeyCommand::Inspect => {
  98. key::inspect(&args.db, args.realm_id)
  99. }
  100. KeyCommand::Generate => {
  101. key::generate(&args.db, args.realm_id)
  102. }
  103. }
  104. }
  105. }
  106. #[derive(Debug, Subcommand)]
  107. enum ClientCommand {
  108. Create { name: String },
  109. List,
  110. Inspect { name: String },
  111. }
  112. #[derive(Debug, Parser)]
  113. struct ClientArgs {
  114. #[clap(subcommand)]
  115. command: ClientCommand,
  116. }
  117. impl ClientArgs {
  118. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  119. match &self.command {
  120. ClientCommand::Create { name } => {
  121. client_management::create(&args.db, args.realm_id, name)
  122. }
  123. ClientCommand::List => { todo!() }
  124. ClientCommand::Inspect { name } => {
  125. client_management::inspect(&args.db, args.realm_id, name)
  126. }
  127. }
  128. }
  129. }
  130. #[derive(Debug, Subcommand)]
  131. enum ConfigCommand {
  132. Dump,
  133. Load { toml_path: String },
  134. Set { key: String, value: String },
  135. }
  136. #[derive(Debug, Parser)]
  137. struct ConfigArgs {
  138. #[clap(subcommand)]
  139. command: ConfigCommand,
  140. }
  141. impl ConfigArgs {
  142. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  143. let qi = args.db.query_interface();
  144. match &self.command {
  145. ConfigCommand::Dump => {
  146. let config = config::Config::build_from(&qi, None);
  147. println!("config: {:?}", config);
  148. }
  149. ConfigCommand::Set { key, value } => {
  150. todo!()
  151. }
  152. ConfigCommand::Load { toml_path } => {
  153. let config = config::Config::build_from(&qi, Some(toml_path));
  154. config.save(&qi);
  155. }
  156. }
  157. Ok(())
  158. }
  159. }
  160. #[derive(Debug, Subcommand)]
  161. enum GroupCommand {
  162. Create { group_name: String },
  163. Members { group_name: String },
  164. AttachRole { group_name: String, role_name: String },
  165. DetachRole { group_name: String, role_name: String },
  166. AttachUser { group_name: String, username: String },
  167. DetachUser { group_name: String, username: String },
  168. }
  169. #[derive(Debug, Parser)]
  170. struct GroupArgs {
  171. #[clap(subcommand)]
  172. command: GroupCommand,
  173. }
  174. impl GroupArgs {
  175. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  176. let qi = args.db.query_interface();
  177. match &self.command {
  178. GroupCommand::Create { group_name } => {
  179. group_management::create_group(&qi, args.realm_id, group_name)?;
  180. },
  181. GroupCommand::Members { group_name } => {
  182. group_management::list_members(&qi, args.realm_id, group_name.as_str())?;
  183. },
  184. GroupCommand::AttachRole { group_name, role_name } => {
  185. group_management::attach_role(&qi, args.realm_id, group_name.as_str(), role_name.as_str())?;
  186. },
  187. GroupCommand::DetachRole { group_name, role_name } => {
  188. group_management::detach_role(&qi, args.realm_id, group_name.as_str(), role_name.as_str())?;
  189. },
  190. GroupCommand::AttachUser { group_name, username } => {
  191. group_management::attach_user(&qi, args.realm_id, group_name.as_str(), username.as_str())?;
  192. },
  193. GroupCommand::DetachUser { group_name, username } => {
  194. group_management::detach_user(&qi, args.realm_id, group_name.as_str(), username.as_str())?;
  195. }
  196. }
  197. Ok(())
  198. }
  199. }
  200. #[derive(Debug, Parser)]
  201. struct ServerArgs {
  202. #[clap(short, long)]
  203. port: Option<u16>,
  204. }
  205. impl ServerArgs {
  206. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  207. let config = config::Config::build_from(&args.db.query_interface(), None);
  208. server::run_server(args.db, config, self.port.unwrap_or(2114)).await
  209. }
  210. }
  211. #[derive(Debug, Subcommand)]
  212. enum TokenCommand {
  213. GenerateAuth {
  214. #[clap(short, long)]
  215. client: String,
  216. #[clap(short, long)]
  217. username: String,
  218. #[clap(short, long)]
  219. scopes: String,
  220. },
  221. GenerateRefresh {
  222. #[clap(short, long)]
  223. client: String,
  224. #[clap(short, long)]
  225. username: String,
  226. #[clap(short, long)]
  227. scopes: String,
  228. },
  229. Inspect {
  230. token: String,
  231. },
  232. }
  233. #[derive(Debug, Parser)]
  234. struct TokenArgs {
  235. #[clap(subcommand)]
  236. command: TokenCommand,
  237. }
  238. impl TokenArgs {
  239. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  240. let config = config::Config::build_from(&args.db.query_interface(), None);
  241. let qi = args.db.query_interface();
  242. match &self.command {
  243. TokenCommand::GenerateAuth {
  244. client,
  245. username,
  246. scopes,
  247. } => {
  248. let token = token_management::create_auth_token(&qi, &config, args.realm_id, client.as_str(), username.as_str(), scopes.as_str())?;
  249. println!("{}", token);
  250. Ok(())
  251. }
  252. TokenCommand::GenerateRefresh {
  253. client,
  254. username,
  255. scopes,
  256. } => { todo!() }
  257. TokenCommand::Inspect { token } => { todo!() }
  258. }
  259. }
  260. }
  261. #[derive(Debug, Subcommand)]
  262. enum RoleCommand {
  263. List,
  264. Create { name: String },
  265. Delete { name: String },
  266. }
  267. #[derive(Debug, Parser)]
  268. struct RoleArgs {
  269. #[clap(subcommand)]
  270. command: RoleCommand,
  271. }
  272. impl RoleArgs {
  273. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  274. let qi = args.db.query_interface();
  275. // let config = config::Config::build_from(&qi, None);
  276. match &self.command {
  277. RoleCommand::List => {
  278. todo!()
  279. },
  280. RoleCommand::Create { name } => {
  281. let add_result = qi.add(&schema::Role {
  282. realm: args.realm_id,
  283. shortname: name.clone()
  284. });
  285. match add_result {
  286. Ok(_role_id) => {
  287. println!("Added role {}", name);
  288. }
  289. Err(_) => {
  290. println!("Failed to add role {}!", name);
  291. },
  292. }
  293. },
  294. RoleCommand::Delete { name } => {
  295. qi.delete().by(schema::Role::Realm, &args.realm_id).by(schema::Role::Shortname, name.as_str()).exec().unwrap();
  296. },
  297. }
  298. Ok(())
  299. }
  300. }
  301. #[derive(Debug, Subcommand)]
  302. enum UserCommand {
  303. List,
  304. Create {
  305. username: String
  306. },
  307. Auth {
  308. username: String,
  309. #[clap(short = 'p', long, parse(from_occurrences))]
  310. change_password: usize,
  311. },
  312. Inspect {
  313. username: String,
  314. }
  315. }
  316. #[derive(Debug, Parser)]
  317. struct UserArgs {
  318. #[clap(subcommand)]
  319. command: UserCommand,
  320. }
  321. impl UserArgs {
  322. async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
  323. let qi = args.db.query_interface();
  324. match &self.command {
  325. UserCommand::List => user_management::list(&qi, args.realm_id),
  326. UserCommand::Create { username } => {
  327. user_management::create(&qi, args.realm_id, username.as_str())
  328. }
  329. UserCommand::Auth { username, change_password } => user_management::change_auth(
  330. &qi,
  331. args.realm_id,
  332. username.as_str(),
  333. *change_password > 0,
  334. ),
  335. UserCommand::Inspect { username } => user_management::inspect(&qi, args.realm_id, username.as_str()),
  336. }
  337. }
  338. }
  339. pub fn invoked() {
  340. let args = RootArgs::parse();
  341. match smol::block_on(args.run()) {
  342. Ok(_) => (),
  343. Err(e) => {
  344. log::error!("Error occured while running command: {}", e);
  345. }
  346. }
  347. }