123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- use crate::{
- client_management, config,
- key::{self, KeyType},
- schema::{self, UIDCDatabase},
- scope_management, server, token_management, UIDCError,
- };
- use clap::{Parser, Subcommand};
- use microrm::{prelude::*, schema::Stored};
- use microrm::cli::Autogenerate;
- mod role;
- mod user;
- impl microrm::cli::CLIError for UIDCError {
- fn no_such_entity(ename: &'static str, keys: String) -> Self {
- UIDCError::AbortString(format!("no such {ename} matching {keys}"))
- }
- }
- #[derive(Debug, Parser)]
- #[clap(author, version, about, long_about = None)]
- struct RootArgs {
- #[clap(short, long, env = "UIDC_DB", default_value_t = String::from("uidc.db"))]
- /// Database path
- db: String,
- #[clap(short, long, default_value_t = String::from("primary"))]
- /// Which realm to use, for non-server only
- realm: String,
- #[clap(subcommand)]
- command: Command,
- }
- #[derive(Debug, Subcommand)]
- enum Command {
- /// database initialization
- Init,
- /// OAuth2 client management
- Client(ClientArgs),
- /// general configuration
- Config(ConfigArgs),
- /// permissions grouping management
- Group(GroupArgs),
- /// key management
- Key(KeyArgs),
- /// scope management
- Scope(ScopeArgs),
- /// run the actual OIDC server
- Server(ServerArgs),
- /// manual token generation and inspection
- Token(TokenArgs),
- /// role management
- Role(RoleArgs),
- /// user management
- User(UserArgs),
- }
- struct RunArgs {
- db: UIDCDatabase,
- realm: Stored<schema::Realm>,
- }
- impl RootArgs {
- async fn run(self) -> Result<(), UIDCError> {
- if let Command::Init = self.command {
- return self.init().await;
- }
- let db = UIDCDatabase::open_path(&self.db)
- .map_err(|e| UIDCError::AbortString(format!("Error accessing database: {:?}", e)))?;
- let realm = db
- .realms
- .keyed(&self.realm)
- .get()?
- .ok_or(UIDCError::Abort("no such realm"))?;
- let ra = RunArgs {
- db,
- realm,
- };
- match self.command {
- Command::Init => unreachable!(),
- Command::Config(v) => v.run(ra).await,
- Command::Key(v) => v.run(ra).await,
- Command::Client(v) => v.run(ra).await,
- Command::Scope(v) => v.run(ra).await,
- Command::Group(v) => v.run(ra).await,
- Command::Server(v) => v.run(ra).await,
- Command::Token(v) => v.run(ra).await,
- Command::Role(v) => v.run(ra).await,
- Command::User(v) => v.run(ra).await,
- }
- }
- async fn init(&self) -> Result<(), UIDCError> {
- // first check to see if the database is already vaguely set up
- let db = UIDCDatabase::open_path(&self.db)
- .map_err(|e| UIDCError::AbortString(format!("Error accessing database: {:?}", e)))?;
- log::info!("Initializing!");
- let primary_realm = "primary".to_string();
- if db.realms.keyed(&primary_realm).get()?.is_some() {
- log::warn!("Already initialized with primary realm!");
- return Ok(());
- }
- // create primary realm
- db.realms.insert(schema::Realm {
- shortname: primary_realm,
- ..Default::default()
- })?;
- Ok(())
- }
- }
- #[derive(Debug, Subcommand)]
- enum KeyCommand {
- /// Print details of all keys
- List,
- /// Generate a new key; see types subcommand for valid options.
- Generate { key_type: KeyType },
- /// List what keytypes are supported
- Types,
- /// Remove a key from use
- Remove { key_id: String },
- }
- #[derive(Debug, Parser)]
- struct KeyArgs {
- #[clap(subcommand)]
- command: KeyCommand,
- }
- impl KeyArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- match &self.command {
- KeyCommand::List => key::list(&args.realm),
- KeyCommand::Generate { key_type } => {
- key::generate_in(&args.realm, *key_type)?;
- return Ok(());
- }
- KeyCommand::Types => {
- for (spec, _kty) in key::KEY_TYPE_NAMES {
- println!("- {}", spec);
- }
- Ok(())
- }
- KeyCommand::Remove { key_id } => key::remove(&args.realm, key_id),
- }
- }
- }
- #[derive(Debug, Subcommand)]
- enum ClientCommand {
- Create {
- /// Name for the new client
- name: String,
- /// Signing key type to use for this client. Default is ed25519.
- key_type: Option<KeyType>,
- },
- List,
- Inspect {
- name: String,
- },
- }
- #[derive(Debug, Parser)]
- struct ClientArgs {
- #[clap(subcommand)]
- command: ClientCommand,
- }
- impl ClientArgs {
- async fn run(&self, args: RunArgs) -> Result<(), UIDCError> {
- todo!()
- /*
- match self.command {
- ClientCommand::Create { name, key_type } => {
- client_management::create(&args.realm, name, key_type.unwrap_or(KeyType::Ed25519))
- }
- ClientCommand::List => {
- todo!()
- }
- ClientCommand::Inspect { name } => client_management::inspect(&args.realm, name),
- }
- */
- }
- }
- #[derive(Debug, Subcommand)]
- enum ConfigCommand {
- Dump,
- Load { toml_path: String },
- Set { key: String, value: String },
- }
- #[derive(Debug, Parser)]
- struct ConfigArgs {
- #[clap(subcommand)]
- command: ConfigCommand,
- }
- impl ConfigArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- match &self.command {
- ConfigCommand::Dump => {
- let config = config::Config::build_from(&args.db, None);
- println!("{:?}", config);
- }
- ConfigCommand::Set { key, value } => {
- args.db.persistent_config.keyed(key).delete()?;
- args.db.persistent_config.insert(schema::PersistentConfig {
- key: key.clone(),
- value: value.clone(),
- })?;
- }
- ConfigCommand::Load { toml_path } => {
- let config = config::Config::build_from(&args.db, Some(toml_path));
- config.save(&args.db);
- }
- }
- Ok(())
- }
- }
- #[derive(Debug, Subcommand)]
- enum GroupCommand {
- Create {
- group_name: String,
- },
- Members {
- group_name: String,
- },
- Roles {
- group_name: String,
- },
- List,
- AttachRole {
- group_name: String,
- role_name: String,
- },
- DetachRole {
- group_name: String,
- role_name: String,
- },
- AttachUser {
- group_name: String,
- username: String,
- },
- DetachUser {
- group_name: String,
- username: String,
- },
- }
- #[derive(Debug, Parser)]
- struct GroupArgs {
- #[clap(subcommand)]
- command: GroupCommand,
- }
- impl GroupArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- todo!()
- /*match &self.command {
- GroupCommand::Create { group_name } => {
- group_management::create_group(&args.realm, group_name)?;
- }
- GroupCommand::Members { group_name } => {
- group_management::list_members(&args.realm, group_name)?;
- }
- GroupCommand::Roles { group_name } => {
- group_management::list_roles(&args.realm, group_name)?;
- }
- GroupCommand::List => {
- group_management::list_groups(&args.realm)?;
- }
- GroupCommand::AttachRole {
- group_name,
- role_name,
- } => {
- group_management::attach_role(&args.realm, group_name, role_name)?;
- }
- GroupCommand::DetachRole {
- group_name,
- role_name,
- } => {
- group_management::detach_role(&args.realm, group_name, role_name)?;
- }
- GroupCommand::AttachUser {
- group_name,
- username,
- } => {
- group_management::attach_user(&args.realm, group_name, username)?;
- }
- GroupCommand::DetachUser {
- group_name,
- username,
- } => {
- group_management::detach_user(&args.realm, group_name, username)?;
- }
- }*/
- // Ok(())
- }
- }
- #[derive(Debug, Subcommand)]
- enum ScopeCommand {
- AttachRole {
- scope_name: String,
- role_name: String,
- },
- Create {
- scope_name: String,
- },
- DetachRole {
- scope_name: String,
- role_name: String,
- },
- Inspect {
- scope_name: String,
- },
- List,
- }
- #[derive(Debug, Parser)]
- struct ScopeArgs {
- #[clap(subcommand)]
- command: ScopeCommand,
- }
- impl ScopeArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- match &self.command {
- ScopeCommand::AttachRole {
- scope_name,
- role_name,
- } => scope_management::attach_role(&args.realm, scope_name, role_name),
- ScopeCommand::Create { scope_name } => {
- scope_management::create_scope(&args.realm, scope_name)
- }
- ScopeCommand::DetachRole {
- scope_name,
- role_name,
- } => scope_management::detach_role(&args.realm, scope_name, role_name),
- ScopeCommand::Inspect { scope_name } => {
- scope_management::inspect_scope(&args.realm, scope_name)
- }
- ScopeCommand::List => scope_management::list_scopes(&args.realm),
- }
- }
- }
- #[derive(Debug, Parser)]
- struct ServerArgs {
- #[clap(short, long)]
- port: Option<u16>,
- }
- impl ServerArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- let config = config::Config::build_from(&args.db, None);
- server::run_server(args.db, config, self.port.unwrap_or(2114)).await
- }
- }
- #[derive(Debug, Subcommand)]
- enum TokenCommand {
- GenerateAuth {
- #[clap(short, long)]
- client: String,
- #[clap(short, long)]
- username: String,
- #[clap(short, long)]
- scopes: String,
- },
- GenerateRefresh {
- #[clap(short, long)]
- client: String,
- #[clap(short, long)]
- username: String,
- #[clap(short, long)]
- scopes: String,
- },
- Inspect {
- token: Option<String>,
- },
- }
- #[derive(Debug, Parser)]
- struct TokenArgs {
- #[clap(subcommand)]
- command: TokenCommand,
- }
- impl TokenArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- let config = config::Config::build_from(&args.db, None);
- match &self.command {
- TokenCommand::GenerateAuth {
- client,
- username,
- scopes,
- } => {
- let token = token_management::create_auth_token(
- &args.realm,
- &config,
- client,
- username,
- scopes,
- )?;
- println!("{}", token);
- Ok(())
- }
- TokenCommand::GenerateRefresh {
- client,
- username,
- scopes,
- } => {
- let token = token_management::create_refresh_token(
- &args.realm,
- &config,
- client,
- username,
- scopes,
- )?;
- println!("{}", token);
- Ok(())
- }
- TokenCommand::Inspect { token } => {
- token_management::inspect_token(&config, &args.realm, token.as_ref())
- }
- }
- }
- }
- #[derive(Debug, Subcommand)]
- enum RoleCommand {
- List,
- Create { name: String },
- Delete { name: String },
- }
- #[derive(Debug, Parser)]
- struct RoleArgs {
- #[clap(subcommand)]
- command: Autogenerate<role::RoleInterface>,
- }
- impl RoleArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- self.command.perform(&args.realm, &args.realm.roles, &args.realm.roles)
- }
- }
- #[derive(Debug, Subcommand)]
- enum UserCommand {
- Auth {
- username: String,
- #[clap(short = 'p', long, action = clap::ArgAction::Count)]
- change_password: usize,
- #[clap(short = 't', long, action = clap::ArgAction::Count)]
- change_totp: usize,
- },
- }
- #[derive(Debug, Parser)]
- struct UserArgs {
- #[clap(subcommand)]
- command: Autogenerate<user::UserInterface>,
- }
- impl UserArgs {
- async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
- self.command.perform(&args.realm, &args.realm.users, &args.realm.users)
- }
- }
- pub fn invoked() {
- let args = RootArgs::parse();
- match smol::block_on(args.run()) {
- Ok(_) => (),
- Err(e) => {
- log::error!("Error occured while running command: {}", e);
- }
- }
- }
|