1use clap::{builder::PossibleValue, Args, Subcommand, ValueEnum};
2use kanidm_proto::constants::CLIENT_TOKEN_CACHE;
3use kanidm_proto::internal::ImageType;
4use kanidm_proto::scim_v1::ScimFilter;
5use std::fmt;
6use time::format_description::well_known::Rfc3339;
7use time::OffsetDateTime;
8
9fn parse_rfc3339(input: &str) -> Result<OffsetDateTime, time::error::Parse> {
10 if input == "now" {
11 #[allow(clippy::disallowed_methods)]
12 Ok(OffsetDateTime::now_utc())
14 } else {
15 OffsetDateTime::parse(input, &Rfc3339)
16 }
17}
18
19#[derive(Debug, Args, Clone)]
20pub struct Named {
21 pub name: String,
22}
23
24#[derive(Debug, Args, Clone)]
25pub struct DebugOpt {
26 #[clap(short, long, env = "KANIDM_DEBUG")]
28 pub debug: bool,
29}
30
31#[derive(Debug, Clone, Copy, Default)]
32pub enum OutputMode {
34 #[default]
35 Text,
36 Json,
37}
38
39impl From<OutputMode> for clap::builder::OsStr {
40 fn from(output_mode: OutputMode) -> Self {
41 match output_mode {
42 OutputMode::Text => "text".into(),
43 OutputMode::Json => "json".into(),
44 }
45 }
46}
47
48impl std::str::FromStr for OutputMode {
49 type Err = String;
50 fn from_str(s: &str) -> Result<OutputMode, std::string::String> {
51 match s.to_lowercase().as_str() {
52 "text" => Ok(OutputMode::Text),
53 "json" => Ok(OutputMode::Json),
54 _ => Ok(OutputMode::Text),
55 }
56 }
57}
58
59impl OutputMode {
60 pub fn print_message<T>(self, input: T)
61 where
62 T: serde::Serialize + fmt::Debug + fmt::Display,
63 {
64 match self {
65 OutputMode::Json => {
66 println!(
67 "{}",
68 serde_json::to_string(&input).unwrap_or(format!("{input:?}"))
69 );
70 }
71 OutputMode::Text => {
72 println!("{input}");
73 }
74 }
75 }
76
77 pub fn print_struct<T>(self, input: T)
78 where
79 T: serde::Serialize + fmt::Debug,
80 {
81 match self {
82 OutputMode::Json => {
83 println!(
84 "{}",
85 serde_json::to_string(&input).unwrap_or(format!("{input:?}"))
86 );
87 }
88 OutputMode::Text => {
89 println!(
90 "{}",
91 serde_json::to_string_pretty(&input).unwrap_or(format!("{input:?}"))
92 );
93 }
94 }
95 }
96}
97
98#[derive(Debug, Args, Clone)]
99pub struct GroupNamedMembers {
100 name: String,
101 #[clap(required = true, num_args(1..))]
102 members: Vec<String>,
103}
104
105#[derive(Debug, Args, Clone)]
106pub struct GroupPosixOpt {
107 name: String,
108 #[clap(long)]
109 gidnumber: Option<u32>,
110}
111
112#[derive(Debug, Subcommand, Clone)]
113pub enum GroupPosix {
114 #[clap(name = "show")]
116 Show(Named),
117 #[clap(name = "set")]
119 Set(GroupPosixOpt),
120 #[clap(name = "reset-gidnumber")]
122 ResetGidnumber { group_id: String },
123}
124
125#[derive(Debug, Clone, Copy, Eq, PartialEq)]
126pub enum AccountPolicyCredentialType {
127 Any,
128 Mfa,
129 Passkey,
130 AttestedPasskey,
131}
132
133impl AccountPolicyCredentialType {
134 pub fn as_str(&self) -> &'static str {
135 match self {
136 Self::Any => "any",
137 Self::Mfa => "mfa",
138 Self::Passkey => "passkey",
139 Self::AttestedPasskey => "attested_passkey",
140 }
141 }
142}
143
144impl ValueEnum for AccountPolicyCredentialType {
145 fn value_variants<'a>() -> &'a [Self] {
146 &[Self::Any, Self::Mfa, Self::Passkey, Self::AttestedPasskey]
147 }
148
149 fn to_possible_value(&self) -> Option<PossibleValue> {
150 Some(self.as_str().into())
151 }
152}
153
154#[derive(Debug, Subcommand, Clone)]
155pub enum GroupAccountPolicyOpt {
156 #[clap(name = "enable")]
158 Enable { name: String },
159 #[clap(name = "auth-expiry")]
161 AuthSessionExpiry { name: String, expiry: u32 },
162 #[clap(name = "credential-type-minimum")]
165 CredentialTypeMinimum {
166 name: String,
167 #[clap(value_enum)]
168 value: AccountPolicyCredentialType,
169 },
170 #[clap(name = "password-minimum-length")]
172 PasswordMinimumLength { name: String, length: u32 },
173
174 #[clap(name = "privilege-expiry")]
176 PrivilegedSessionExpiry { name: String, expiry: u32 },
177
178 #[clap(name = "webauthn-attestation-ca-list")]
183 WebauthnAttestationCaList {
184 name: String,
185 attestation_ca_list_json_file: PathBuf,
186 },
187
188 #[clap(name = "limit-search-max-results")]
191 LimitSearchMaxResults { name: String, maximum: u32 },
192 #[clap(name = "limit-search-max-filter-test")]
196 LimitSearchMaxFilterTest { name: String, maximum: u32 },
197 #[clap(name = "allow-primary-cred-fallback")]
200 AllowPrimaryCredFallback {
201 name: String,
202 #[clap(name = "allow", action = clap::ArgAction::Set)]
203 allow: bool,
204 },
205
206 #[clap(name = "reset-auth-expiry")]
208 ResetAuthSessionExpiry { name: String },
209 #[clap(name = "reset-password-minimum-length")]
211 ResetPasswordMinimumLength { name: String },
212 #[clap(name = "reset-privilege-expiry")]
214 ResetPrivilegedSessionExpiry { name: String },
215 #[clap(name = "reset-webauthn-attestation-ca-list")]
218 ResetWebauthnAttestationCaList { name: String },
219 #[clap(name = "reset-limit-search-max-results")]
221 ResetLimitSearchMaxResults { name: String },
222 #[clap(name = "reset-limit-search-max-filter-test")]
224 ResetLimitSearchMaxFilterTest { name: String },
225}
226
227#[derive(Debug, Subcommand, Clone)]
228pub enum GroupOpt {
229 #[clap(name = "list")]
231 List,
232 #[clap(name = "get")]
234 Get(Named),
235 #[clap(name = "search")]
237 Search {
238 name: String,
240 },
241 #[clap(name = "create")]
243 Create {
244 name: String,
246 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
248 entry_managed_by: Option<String>,
249 },
250 #[clap(name = "delete")]
252 Delete(Named),
253 #[clap(name = "list-members")]
255 ListMembers(Named),
256 #[clap(name = "set-members")]
259 SetMembers(GroupNamedMembers),
260 #[clap(name = "set-mail")]
264 SetMail { name: String, mail: Vec<String> },
265 #[clap(name = "set-description")]
267 SetDescription {
268 name: String,
269 description: Option<String>,
270 },
271 #[clap(name = "set-entry-manager")]
273 SetEntryManagedBy {
274 name: String,
276 entry_managed_by: String,
278 },
279 #[clap(name = "rename")]
281 Rename {
282 name: String,
284 new_name: String,
286 },
287 #[clap(name = "purge-members")]
289 PurgeMembers(Named),
290 #[clap(name = "add-members")]
292 AddMembers(GroupNamedMembers),
293 #[clap(name = "remove-members")]
295 RemoveMembers(GroupNamedMembers),
296 #[clap(name = "posix")]
298 Posix {
299 #[clap(subcommand)]
300 commands: GroupPosix,
301 },
302 #[clap(name = "account-policy")]
304 AccountPolicy {
305 #[clap(subcommand)]
306 commands: GroupAccountPolicyOpt,
307 },
308}
309
310#[derive(Clone, Debug, ValueEnum)]
311pub enum GraphType {
312 Graphviz,
313 Mermaid,
314 MermaidElk,
315}
316
317#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, ValueEnum)]
318pub enum ObjectType {
319 Group,
320 BuiltinGroup,
321 ServiceAccount,
322 Person,
323}
324
325#[derive(Debug, Args, Clone)]
326pub struct GraphCommonOpt {
327 #[arg(value_enum)]
328 pub graph_type: GraphType,
329 #[clap()]
330 pub filter: Vec<ObjectType>,
331}
332
333#[derive(Debug, Args, Clone)]
334pub struct AccountCommonOpt {
335 #[clap()]
336 account_id: String,
337}
338
339#[derive(Debug, Args, Clone)]
340pub struct AccountNamedOpt {
341 #[clap(flatten)]
342 aopts: AccountCommonOpt,
343}
344
345#[derive(Debug, Args, Clone)]
346pub struct AccountNamedExpireDateTimeOpt {
347 #[clap(flatten)]
348 aopts: AccountCommonOpt,
349 #[clap(name = "datetime", verbatim_doc_comment)]
350 datetime: String,
356}
357
358#[derive(Debug, Args, Clone)]
359pub struct AccountNamedValidDateTimeOpt {
360 #[clap(flatten)]
361 aopts: AccountCommonOpt,
362 #[clap(name = "datetime")]
363 datetime: String,
366}
367
368#[derive(Debug, Args, Clone)]
369pub struct AccountNamedTagOpt {
370 #[clap(flatten)]
371 aopts: AccountCommonOpt,
372 #[clap(name = "tag")]
373 tag: String,
374}
375
376#[derive(Debug, Args, Clone)]
377pub struct AccountNamedTagPkOpt {
378 #[clap(flatten)]
379 aopts: AccountCommonOpt,
380 #[clap(name = "tag")]
381 tag: String,
382 #[clap(name = "pubkey")]
383 pubkey: String,
384}
385
386#[derive(Debug, Args, Clone)]
387pub struct UseResetTokenOpt {
389 #[clap(name = "token")]
390 token: String,
391}
392
393#[derive(Debug, Args, Clone)]
394pub struct AccountCreateOpt {
395 #[clap(flatten)]
396 aopts: AccountCommonOpt,
397 #[clap(name = "display-name")]
398 display_name: String,
399}
400
401#[derive(Debug, Subcommand, Clone)]
402pub enum AccountCredential {
403 #[clap(name = "status")]
405 Status(AccountNamedOpt),
406 #[clap(name = "update")]
408 Update(AccountNamedOpt),
409 #[clap(name = "use-reset-token")]
411 UseResetToken(UseResetTokenOpt),
412 #[clap(name = "create-reset-token")]
415 CreateResetToken {
416 #[clap(flatten)]
417 aopts: AccountCommonOpt,
418
419 #[clap(long)]
422 ttl: Option<u32>,
423 },
424 #[clap(name = "send-reset-token")]
427 SendResetToken {
428 account_id: String,
429
430 #[clap(long)]
433 ttl: Option<u64>,
434
435 alternate_email: Option<String>,
438 },
439 #[clap(name = "softlock-reset")]
441 SoftlockReset {
442 account_id: String,
443 #[clap(name = "datetime", default_value = "now", verbatim_doc_comment)]
444 datetime: String,
448 }
449}
450
451#[derive(Debug, Subcommand, Clone)]
453pub enum AccountRadius {
454 #[clap(name = "show-secret")]
456 Show(AccountNamedOpt),
457 #[clap(name = "generate-secret")]
459 Generate(AccountNamedOpt),
460 #[clap(name = "delete-secret")]
461 DeleteSecret(AccountNamedOpt),
463}
464
465#[derive(Debug, Args, Clone)]
466pub struct AccountPosixOpt {
467 #[clap(flatten)]
468 aopts: AccountCommonOpt,
469 #[clap(long)]
470 gidnumber: Option<u32>,
471 #[clap(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
472 shell: Option<String>,
474}
475
476#[derive(Debug, Subcommand, Clone)]
477pub enum PersonPosix {
478 #[clap(name = "show")]
479 Show(AccountNamedOpt),
480 #[clap(name = "set")]
481 Set(AccountPosixOpt),
482 #[clap(name = "set-password")]
483 SetPassword(AccountNamedOpt),
484 #[clap(name = "reset-gidnumber")]
486 ResetGidnumber { account_id: String },
487}
488
489#[derive(Debug, Subcommand, Clone)]
490pub enum PersonApplicationOpt {
491 Create {
492 name: String,
493 application_uuid: Uuid,
494 label: String,
495 },
496 Delete {
497 name: String,
498 password_id: Uuid,
499 }
500}
501
502#[derive(Debug, Subcommand, Clone)]
503pub enum ServiceAccountPosix {
504 #[clap(name = "show")]
505 Show(AccountNamedOpt),
506 #[clap(name = "set")]
507 Set(AccountPosixOpt),
508 #[clap(name = "reset-gidnumber")]
510 ResetGidnumber { account_id: String },
511}
512
513#[derive(Debug, Args, Clone)]
514pub struct PersonUpdateOpt {
515 #[clap(flatten)]
516 aopts: AccountCommonOpt,
517 #[clap(long, short, help = "Set the legal name for the person. Giving an empty string clears the attribute.",
518 value_parser = clap::builder::StringValueParser::new())]
519 legalname: Option<String>,
520 #[clap(long, short, help = "Set the account name for the person.",
521 value_parser = clap::builder::NonEmptyStringValueParser::new())]
522 newname: Option<String>,
523 #[clap(long, short = 'i', help = "Set the display name for the person.",
524 value_parser = clap::builder::NonEmptyStringValueParser::new())]
525 displayname: Option<String>,
526 #[clap(
527 long,
528 short,
529 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
530 )]
531 mail: Option<Vec<String>>,
532}
533
534#[derive(Debug, Subcommand, Clone)]
535pub enum AccountSsh {
536 #[clap(name = "list-publickeys")]
537 List(AccountNamedOpt),
538 #[clap(name = "add-publickey")]
539 Add(AccountNamedTagPkOpt),
540 #[clap(name = "delete-publickey")]
541 Delete(AccountNamedTagOpt),
542}
543
544#[derive(Debug, Subcommand, Clone)]
545pub enum AccountValidity {
546 #[clap(name = "show")]
548 Show(AccountNamedOpt),
549 #[clap(name = "expire-at")]
551 ExpireAt(AccountNamedExpireDateTimeOpt),
552 #[clap(name = "begin-from")]
554 BeginFrom(AccountNamedValidDateTimeOpt),
555}
556
557#[derive(Debug, Subcommand, Clone)]
558pub enum AccountCertificate {
559 #[clap(name = "status")]
560 Status { account_id: String },
561 #[clap(name = "create")]
562 Create {
563 account_id: String,
564 certificate_path: PathBuf,
565 },
566}
567
568#[derive(Debug, Subcommand, Clone)]
569pub enum AccountUserAuthToken {
570 #[clap(name = "status")]
572 Status(AccountNamedOpt),
573 #[clap(name = "destroy")]
576 Destroy {
577 #[clap(flatten)]
578 aopts: AccountCommonOpt,
579
580 #[clap(name = "session-id")]
582 session_id: Uuid,
583 },
584}
585
586#[derive(Debug, Subcommand, Clone)]
587pub enum PersonOpt {
588 #[clap(name = "credential")]
590 Credential {
591 #[clap(subcommand)]
592 commands: AccountCredential,
593 },
594 #[clap(name = "radius")]
596 Radius {
597 #[clap(subcommand)]
598 commands: AccountRadius,
599 },
600 #[clap(name = "posix")]
602 Posix {
603 #[clap(subcommand)]
604 commands: PersonPosix,
605 },
606 #[clap(name = "session")]
608 Session {
609 #[clap(subcommand)]
610 commands: AccountUserAuthToken,
611 },
612 #[clap(name = "ssh")]
614 Ssh {
615 #[clap(subcommand)]
616 commands: AccountSsh,
617 },
618 #[clap(name = "list")]
620 List,
621 #[clap(name = "get")]
623 Get(AccountNamedOpt),
624 #[clap(name = "search")]
626 Search { account_id: String },
627 #[clap(name = "update")]
629 Update(PersonUpdateOpt),
630 #[clap(name = "create")]
632 Create(AccountCreateOpt),
633 #[clap(name = "delete")]
635 Delete(AccountNamedOpt),
636 #[clap(name = "validity")]
638 Validity {
639 #[clap(subcommand)]
640 commands: AccountValidity,
641 },
642
643 #[clap(name = "applications")]
645 Application {
646 #[clap(subcommand)]
647 commands: PersonApplicationOpt,
648 },
649
650 #[clap(name = "certificate", hide = true)]
651 Certificate {
652 #[clap(subcommand)]
653 commands: AccountCertificate,
654 },
655}
656
657#[derive(Debug, Subcommand, Clone)]
658pub enum ServiceAccountCredential {
659 #[clap(name = "status")]
661 Status(AccountNamedOpt),
662 #[clap(name = "generate")]
665 GeneratePw(AccountNamedOpt),
666}
667
668#[derive(Debug, Subcommand, Clone)]
669pub enum ServiceAccountApiToken {
670 #[clap(name = "status")]
672 Status(AccountNamedOpt),
673 #[clap(name = "generate", visible_aliases = &["create"])]
675 Generate {
676 #[clap(flatten)]
677 aopts: AccountCommonOpt,
678
679 #[clap(name = "label")]
682 label: String,
683 #[clap(name = "expiry")]
684 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
687 expiry: Option<String>,
688 #[clap(short = 'w', long = "readwrite")]
690 read_write: bool,
691
692 #[clap(short = 'c', long = "compact")]
698 compact: bool,
699 },
700 #[clap(name = "destroy")]
703 Destroy {
704 #[clap(flatten)]
705 aopts: AccountCommonOpt,
706
707 #[clap(name = "token-id")]
709 token_id: Uuid,
710 },
711}
712
713#[derive(Debug, Args, Clone)]
714pub struct ServiceAccountUpdateOpt {
715 #[clap(flatten)]
716 aopts: AccountCommonOpt,
717 #[clap(long, short, help = "Set the account name for the service account.",
718 value_parser = clap::builder::NonEmptyStringValueParser::new())]
719 newname: Option<String>,
720 #[clap(
721 long,
722 short = 'i',
723 help = "Set the display name for the service account.",
724 value_parser = clap::builder::NonEmptyStringValueParser::new()
725 )]
726 displayname: Option<String>,
727 #[clap(
728 long,
729 short = 'e',
730 help = "Set the entry manager for the service account.",
731 value_parser = clap::builder::NonEmptyStringValueParser::new()
732 )]
733 entry_managed_by: Option<String>,
734 #[clap(
735 long,
736 short,
737 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
738 )]
739 mail: Option<Vec<String>>,
740}
741
742#[derive(Debug, Subcommand, Clone)]
743pub enum ServiceAccountOpt {
744 #[clap(name = "credential")]
746 Credential {
747 #[clap(subcommand)]
748 commands: ServiceAccountCredential,
749 },
750 #[clap(name = "api-token")]
752 ApiToken {
753 #[clap(subcommand)]
754 commands: ServiceAccountApiToken,
755 },
756 #[clap(name = "posix")]
758 Posix {
759 #[clap(subcommand)]
760 commands: ServiceAccountPosix,
761 },
762 #[clap(name = "session")]
764 Session {
765 #[clap(subcommand)]
766 commands: AccountUserAuthToken,
767 },
768 #[clap(name = "ssh")]
770 Ssh {
771 #[clap(subcommand)]
772 commands: AccountSsh,
773 },
774 #[clap(name = "list")]
776 List,
777 #[clap(name = "get")]
779 Get(AccountNamedOpt),
780 #[clap(name = "create")]
782 Create {
783 #[clap(flatten)]
784 aopts: AccountCommonOpt,
785 #[clap(name = "display-name")]
786 display_name: String,
787 #[clap(name = "entry-managed-by")]
788 entry_managed_by: String,
789 },
790 #[clap(name = "update")]
792 Update(ServiceAccountUpdateOpt),
793 #[clap(name = "delete")]
795 Delete(AccountNamedOpt),
796 #[clap(name = "validity")]
798 Validity {
799 #[clap(subcommand)]
800 commands: AccountValidity,
801 },
802 #[clap(name = "into-person")]
806 IntoPerson(AccountNamedOpt),
807}
808
809#[derive(Debug, Subcommand, Clone)]
810pub enum RecycleOpt {
811 #[clap(name = "list")]
812 List,
814 #[clap(name = "get")]
815 Get(Named),
817 #[clap(name = "revive")]
818 Revive(Named),
820}
821
822#[derive(Debug, Args, Clone)]
823pub struct LoginOpt {}
824
825#[derive(Debug, Args, Clone)]
826pub struct LogoutOpt {
827 #[clap(short, long)]
828 local_only: bool,
830}
831
832#[derive(Debug, Subcommand, Clone)]
833pub enum SessionOpt {
834 #[clap(name = "list")]
835 List,
837 #[clap(name = "cleanup")]
838 Cleanup,
840}
841
842#[derive(Debug, Subcommand, Clone)]
843pub enum RawOpt {
844 #[clap(name = "search")]
845 Search {
846 filter: ScimFilter
847 },
848 #[clap(name = "create")]
849 Create {
850 file: PathBuf
851 },
852 #[clap(name = "update")]
853 Update {
854 file: PathBuf
855 },
856 #[clap(name = "delete")]
857 Delete {
858 id: String
859 },
860}
861
862#[derive(Debug, Subcommand, Clone)]
863pub enum SelfOpt {
864 #[clap(name = "identify-user")]
866 IdentifyUser,
867 Whoami,
869}
870
871#[derive(Debug, Args, Clone)]
872pub struct Oauth2SetDisplayname {
873 #[clap(flatten)]
874 nopt: Named,
875 #[clap(name = "displayname")]
876 displayname: String,
877}
878
879#[derive(Debug, Args, Clone)]
880pub struct Oauth2SetImplicitScopes {
881 #[clap(flatten)]
882 nopt: Named,
883 #[clap(name = "scopes")]
884 scopes: Vec<String>,
885}
886
887#[derive(Debug, Args, Clone)]
888pub struct Oauth2CreateScopeMapOpt {
889 #[clap(flatten)]
890 nopt: Named,
891 #[clap(name = "group")]
892 group: String,
893 #[clap(name = "scopes", required = true, num_args=1.. )]
894 scopes: Vec<String>,
895}
896
897#[derive(Debug, Args, Clone)]
898pub struct Oauth2DeleteScopeMapOpt {
899 #[clap(flatten)]
900 nopt: Named,
901 #[clap(name = "group")]
902 group: String,
903}
904
905#[derive(Debug, Clone, Copy, Eq, PartialEq)]
906pub enum Oauth2ClaimMapJoin {
907 Csv,
908 Ssv,
909 Array,
910}
911
912impl Oauth2ClaimMapJoin {
913 pub fn as_str(&self) -> &'static str {
914 match self {
915 Self::Csv => "csv",
916 Self::Ssv => "ssv",
917 Self::Array => "array",
918 }
919 }
920}
921
922impl ValueEnum for Oauth2ClaimMapJoin {
923 fn value_variants<'a>() -> &'a [Self] {
924 &[Self::Csv, Self::Ssv, Self::Array]
925 }
926
927 fn to_possible_value(&self) -> Option<PossibleValue> {
928 Some(self.as_str().into())
929 }
930}
931
932#[derive(Debug, Subcommand, Clone)]
933pub enum ApplicationOpt {
934 #[clap(name = "list")]
935 List,
937
938 #[clap(name = "get")]
939 Get {
941 #[clap(name = "name")]
942 name: String,
943 },
944
945 #[clap(name = "create")]
946 Create {
948 #[clap(name = "name")]
949 name: String,
950
951 #[clap(name = "displayname")]
952 displayname: String,
953
954 #[clap(name = "linked_group")]
955 linked_group: String,
956 },
957
958 #[clap(name = "delete")]
959 Delete {
961 #[clap(name = "name")]
962 name: String,
963 },
964}
965
966#[derive(Debug, Subcommand, Clone)]
967pub enum Oauth2Opt {
968 #[clap(name = "list")]
969 List,
971 #[clap(name = "get")]
972 Get(Named),
974 #[clap(name = "create")]
978 CreateBasic {
980 #[clap(name = "name")]
981 name: String,
982 #[clap(name = "displayname")]
983 displayname: String,
984 #[clap(name = "origin")]
985 origin: String,
986 },
987 #[clap(name = "create-public")]
988 CreatePublic {
994 #[clap(name = "name")]
995 name: String,
996 #[clap(name = "displayname")]
997 displayname: String,
998 #[clap(name = "origin")]
999 origin: String,
1000 },
1001 #[clap(name = "update-scope-map", visible_aliases=&["create-scope-map"])]
1002 UpdateScopeMap(Oauth2CreateScopeMapOpt),
1004 #[clap(name = "delete-scope-map")]
1005 DeleteScopeMap(Oauth2DeleteScopeMapOpt),
1007
1008 #[clap(name = "update-sup-scope-map", visible_aliases=&["create-sup-scope-map"])]
1009 UpdateSupScopeMap(Oauth2CreateScopeMapOpt),
1011 #[clap(name = "delete-sup-scope-map")]
1012 DeleteSupScopeMap(Oauth2DeleteScopeMapOpt),
1014
1015 #[clap(name = "update-claim-map", visible_aliases=&["create-claim-map"])]
1016 UpdateClaimMap {
1018 name: String,
1019 claim_name: String,
1020 group: String,
1021 values: Vec<String>,
1022 },
1023 #[clap(name = "update-claim-map-join")]
1024 UpdateClaimMapJoin {
1025 name: String,
1026 claim_name: String,
1027 join: Oauth2ClaimMapJoin,
1030 },
1031 #[clap(name = "delete-claim-map")]
1032 DeleteClaimMap {
1034 name: String,
1035 claim_name: String,
1036 group: String,
1037 },
1038
1039 #[clap(name = "reset-basic-secret")]
1040 ResetSecrets(Named),
1043 #[clap(name = "show-basic-secret")]
1044 ShowBasicSecret(Named),
1046 #[clap(name = "delete")]
1047 Delete(Named),
1049 #[clap(name = "set-displayname")]
1051 SetDisplayname(Oauth2SetDisplayname),
1052 #[clap(name = "set-name")]
1056 SetName {
1057 #[clap(flatten)]
1058 nopt: Named,
1059 #[clap(name = "newname")]
1060 name: String,
1061 },
1062
1063 #[clap(name = "set-landing-url")]
1066 SetLandingUrl {
1067 #[clap(flatten)]
1068 nopt: Named,
1069 #[clap(name = "landing-url")]
1070 url: Url,
1071 },
1072 #[clap(name = "set-image")]
1074 SetImage {
1075 #[clap(flatten)]
1076 nopt: Named,
1077 #[clap(name = "file-path")]
1078 path: PathBuf,
1080 #[clap(name = "image-type")]
1081 image_type: Option<ImageType>,
1083 },
1084 #[clap(name = "remove-image")]
1086 RemoveImage(Named),
1087
1088 #[clap(name = "set-refresh-token-expiry")]
1090 SetRefreshTokenExpiry {
1091 name: String,
1092 expiry: Option<u32>,
1093 },
1094
1095 #[clap(name = "add-redirect-url")]
1099 AddOrigin {
1100 name: String,
1101 #[clap(name = "url")]
1102 origin: Url,
1103 },
1104
1105 #[clap(name = "remove-redirect-url")]
1107 RemoveOrigin {
1108 name: String,
1109 #[clap(name = "url")]
1110 origin: Url,
1111 },
1112 #[clap(name = "enable-pkce")]
1113 EnablePkce(Named),
1115 #[clap(name = "warning-insecure-client-disable-pkce")]
1118 DisablePkce(Named),
1119 #[clap(name = "warning-enable-legacy-crypto")]
1120 EnableLegacyCrypto(Named),
1124 #[clap(name = "disable-legacy-crypto")]
1126 DisableLegacyCrypto(Named),
1127 #[clap(name = "enable-strict-redirect-url")]
1131 EnableStrictRedirectUri { name: String },
1132 #[clap(name = "disable-strict-redirect-url")]
1133 DisableStrictRedirectUri { name: String },
1134 #[clap(name = "enable-localhost-redirects")]
1135 EnablePublicLocalhost { name: String },
1137 #[clap(name = "disable-localhost-redirects")]
1139 DisablePublicLocalhost { name: String },
1140 #[clap(name = "prefer-short-username")]
1142 PreferShortUsername(Named),
1143 #[clap(name = "prefer-spn-username")]
1145 PreferSPNUsername(Named),
1146 #[cfg(feature = "dev-oauth2-device-flow")]
1147 DeviceFlowEnable(Named),
1149 #[cfg(feature = "dev-oauth2-device-flow")]
1150 DeviceFlowDisable(Named),
1152 #[clap(name = "rotate-cryptographic-keys")]
1158 RotateCryptographicKeys {
1159 name: String,
1160 #[clap(value_parser = parse_rfc3339)]
1161 rotate_at: OffsetDateTime,
1162 },
1163 #[clap(name = "revoke-cryptographic-key")]
1167 RevokeCryptographicKey { name: String, key_id: String },
1168 #[clap(name = "disable-consent-prompt")]
1172 DisableConsentPrompt(Named),
1173 #[clap(name = "enable-consent-prompt")]
1175 EnableConsentPrompt(Named),
1176}
1177
1178#[derive(Args, Debug, Clone)]
1179pub struct OptSetDomainDisplayname {
1180 #[clap(name = "new-display-name")]
1181 new_display_name: String,
1182}
1183
1184#[derive(Debug, Subcommand, Clone)]
1185pub enum PwBadlistOpt {
1186 #[clap[name = "show"]]
1187 Show,
1189 #[clap[name = "upload"]]
1190 Upload {
1194 #[clap(value_parser, required = true, num_args(1..))]
1195 paths: Vec<PathBuf>,
1196 #[clap(short = 'n', long)]
1198 dryrun: bool,
1199 },
1200 #[clap[name = "remove", hide = true]]
1201 Remove {
1204 #[clap(value_parser, required = true, num_args(1..))]
1205 paths: Vec<PathBuf>,
1206 },
1207}
1208
1209#[derive(Debug, Subcommand, Clone)]
1210pub enum DeniedNamesOpt {
1211 #[clap[name = "show"]]
1212 Show,
1214 #[clap[name = "append"]]
1215 Append {
1216 #[clap(value_parser, required = true, num_args(1..))]
1217 names: Vec<String>,
1218 },
1219 #[clap[name = "remove"]]
1220 Remove {
1222 #[clap(value_parser, required = true, num_args(1..))]
1223 names: Vec<String>,
1224 },
1225}
1226
1227#[derive(Debug, Subcommand, Clone)]
1228pub enum DomainOpt {
1229 #[clap[name = "set-displayname"]]
1230 SetDisplayname(OptSetDomainDisplayname),
1232 #[clap[name = "set-ldap-queryable-attrs"]]
1234 SetLdapMaxQueryableAttrs {
1235 #[clap(name = "maximum-queryable-attrs")]
1236 new_max_queryable_attrs: usize,
1237 },
1238 #[clap[name = "set-ldap-basedn"]]
1239 SetLdapBasedn {
1244 #[clap(name = "new-basedn")]
1245 new_basedn: String,
1246 },
1247 SetLdapAllowUnixPasswordBind {
1250 #[clap(name = "allow", action = clap::ArgAction::Set)]
1251 enable: bool,
1252 },
1253 SetAllowAccountRecovery {
1257 #[clap(name = "allow", action = clap::ArgAction::Set)]
1258 enable: bool,
1259 },
1260 SetAllowEasterEggs {
1264 #[clap(name = "allow", action = clap::ArgAction::Set)]
1265 enable: bool,
1266 },
1267 #[clap(name = "show")]
1268 Show,
1270 #[clap(name = "revoke-key")]
1271 RevokeKey { key_id: String },
1274 #[clap(name = "set-image")]
1276 SetImage {
1277 #[clap(name = "file-path")]
1278 path: PathBuf,
1279 #[clap(name = "image-type")]
1280 image_type: Option<ImageType>,
1281 },
1282 #[clap(name = "remove-image")]
1284 RemoveImage,
1285}
1286
1287#[derive(Debug, Subcommand, Clone)]
1288pub enum MessageOpt {
1289 #[clap(name = "list")]
1290 List,
1292
1293 #[clap(name = "get")]
1294 Get {
1296 message_id: Uuid
1297 },
1298
1299 #[clap(name = "mark-as-sent")]
1300 MarkAsSent {
1303 message_id: Uuid
1304 },
1305
1306 #[clap(name = "send-test-message")]
1307 SendTestMessage {
1308 to: String,
1310 }
1311}
1312
1313#[derive(Debug, Subcommand, Clone)]
1314pub enum SynchOpt {
1315 #[clap(name = "list")]
1316 List,
1318 #[clap(name = "get")]
1319 Get(Named),
1321 #[clap(name = "set-credential-portal")]
1322 SetCredentialPortal {
1325 #[clap()]
1326 account_id: String,
1327
1328 #[clap(name = "url")]
1329 url: Option<Url>,
1330 },
1331 #[clap(name = "create")]
1333 Create {
1334 #[clap()]
1335 account_id: String,
1336
1337 #[clap(name = "description",
1338 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1339 description: Option<String>,
1340 },
1341 #[clap(name = "generate-token")]
1343 GenerateToken {
1344 #[clap()]
1345 account_id: String,
1346 #[clap()]
1347 label: String,
1348 },
1349 #[clap(name = "destroy-token")]
1351 DestroyToken {
1352 #[clap()]
1353 account_id: String,
1354 },
1355 #[clap(name = "set-yield-attributes")]
1359 SetYieldAttributes {
1360 #[clap()]
1361 account_id: String,
1362
1363 #[clap(name = "attributes")]
1364 attrs: Vec<String>,
1365 },
1366 #[clap(name = "force-refresh")]
1370 ForceRefresh {
1371 #[clap()]
1372 account_id: String,
1373 },
1374 #[clap(name = "finalise")]
1380 Finalise {
1381 #[clap()]
1382 account_id: String,
1383 },
1384 #[clap(name = "terminate")]
1390 Terminate {
1391 #[clap()]
1392 account_id: String,
1393 },
1394}
1395
1396#[derive(Debug, Subcommand, Clone)]
1397pub enum AuthSessionExpiryOpt {
1398 #[clap[name = "get"]]
1399 Get,
1401 #[clap[name = "set"]]
1402 Set {
1404 #[clap(name = "expiry")]
1405 expiry: u32,
1406 },
1407}
1408
1409#[derive(Debug, Subcommand, Clone)]
1410pub enum PrivilegedSessionExpiryOpt {
1411 #[clap[name = "get"]]
1412 Get,
1414 #[clap[name = "set"]]
1415 Set {
1417 #[clap(name = "expiry")]
1418 expiry: u32,
1419 },
1420}
1421
1422#[derive(Args, Debug, Clone)]
1423pub struct ApiSchemaDownloadOpt {
1424 #[clap(name = "filename", env, default_value = "./kanidm-openapi.json")]
1426 filename: PathBuf,
1427 #[clap(short, long, env)]
1429 force: bool,
1430}
1431
1432#[derive(Debug, Subcommand, Clone)]
1433pub enum ApiOpt {
1434 #[clap(name = "download-schema")]
1436 DownloadSchema(ApiSchemaDownloadOpt),
1437}
1438
1439#[derive(Debug, Subcommand, Clone)]
1440pub enum SchemaClassOpt {
1441 List,
1443 Search {
1444 query: String,
1445 },
1446}
1447
1448#[derive(Debug, Subcommand, Clone)]
1449pub enum SchemaAttrOpt {
1450 List,
1452 Search {
1453 query: String,
1454 },
1455}
1456
1457#[derive(Debug, Subcommand, Clone)]
1458pub enum SchemaOpt {
1459 #[clap(name = "class")]
1461 Class {
1462 #[clap(subcommand)]
1463 commands: SchemaClassOpt,
1464 },
1465 #[clap(name = "attribute", visible_alias = "attr")]
1467 Attribute {
1468 #[clap(subcommand)]
1469 commands: SchemaAttrOpt,
1470 },
1471}
1472
1473#[derive(Debug, Subcommand, Clone)]
1474pub enum SystemOpt {
1475 #[clap(name = "pw-badlist")]
1476 PwBadlist {
1478 #[clap(subcommand)]
1479 commands: PwBadlistOpt,
1480 },
1481 #[clap(name = "denied-names")]
1482 DeniedNames {
1484 #[clap(subcommand)]
1485 commands: DeniedNamesOpt,
1486 },
1487 #[clap(name = "oauth2")]
1488 Oauth2 {
1490 #[clap(subcommand)]
1491 commands: Oauth2Opt,
1492 },
1493
1494 #[clap(name = "application")]
1495 Application {
1497 #[clap(subcommand)]
1498 commands: ApplicationOpt,
1499 },
1500
1501 #[clap(name = "domain")]
1502 Domain {
1504 #[clap(subcommand)]
1505 commands: DomainOpt,
1506 },
1507 #[clap(name = "sync")]
1508 Synch {
1510 #[clap(subcommand)]
1511 commands: SynchOpt,
1512 },
1513 #[clap(name = "message-queue", alias = "message")]
1514 Message {
1516 #[clap(subcommand)]
1517 commands: MessageOpt,
1518 },
1519 #[clap(name = "api")]
1520 Api {
1522 #[clap(subcommand)]
1523 commands: ApiOpt,
1524 },
1525}
1526
1527#[derive(Debug, Subcommand, Clone)]
1528#[clap(about = "Kanidm Client Utility")]
1529pub enum KanidmClientOpt {
1530 Login(LoginOpt),
1532 Reauth,
1534 Logout(LogoutOpt),
1536 Session {
1538 #[clap(subcommand)]
1539 commands: SessionOpt,
1540 },
1541 #[clap(name = "self")]
1542 CSelf {
1544 #[clap(subcommand)]
1545 commands: SelfOpt,
1546 },
1547 Person {
1549 #[clap(subcommand)]
1550 commands: PersonOpt,
1551 },
1552 Group {
1554 #[clap(subcommand)]
1555 commands: GroupOpt,
1556 },
1557 #[clap(name = "service-account")]
1559 ServiceAccount {
1560 #[clap(subcommand)]
1561 commands: ServiceAccountOpt,
1562 },
1563 #[clap(name = "graph")]
1565 Graph(GraphCommonOpt),
1566
1567 #[clap(hide = true)]
1569 Schema {
1570 #[clap(subcommand)]
1571 commands: SchemaOpt,
1572 },
1573
1574 System {
1576 #[clap(subcommand)]
1577 commands: SystemOpt,
1578 },
1579 #[clap(name = "recycle-bin")]
1580 Recycle {
1582 #[clap(subcommand)]
1583 commands: RecycleOpt,
1584 },
1585 #[clap(hide = true)]
1587 Raw {
1588 #[clap(subcommand)]
1589 commands: RawOpt,
1590 },
1591 Version,
1593}
1594
1595#[derive(Debug, clap::Parser, Clone)]
1596#[clap(about = "Kanidm Client Utility")]
1597pub struct KanidmClientParser {
1598 #[clap(subcommand)]
1599 pub commands: KanidmClientOpt,
1600
1601 #[clap(short, long, env = "KANIDM_DEBUG", global = true)]
1603 pub debug: bool,
1604 #[clap(short = 'I', long = "instance", env = "KANIDM_INSTANCE", global = true,
1606 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1607 pub instance: Option<String>,
1608 #[clap(short = 'H', long = "url", env = "KANIDM_URL", global = true,
1610 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1611 pub addr: Option<String>,
1612 #[clap(
1614 short = 'D',
1615 long = "name",
1616 env = "KANIDM_NAME",
1617 value_parser = clap::builder::NonEmptyStringValueParser::new(), global=true
1618 )]
1619 pub username: Option<String>,
1620 #[clap(
1622 value_parser,
1623 short = 'C',
1624 long = "ca",
1625 env = "KANIDM_CA_PATH",
1626 global = true
1627 )]
1628 pub ca_path: Option<PathBuf>,
1629 #[clap(short, long = "output", env = "KANIDM_OUTPUT", global = true, default_value=OutputMode::default())]
1631 output_mode: OutputMode,
1632 #[clap(
1634 long = "skip-hostname-verification",
1635 env = "KANIDM_SKIP_HOSTNAME_VERIFICATION",
1636 default_value_t = false,
1637 global = true
1638 )]
1639 skip_hostname_verification: bool,
1640 #[clap(
1642 long = "accept-invalid-certs",
1643 env = "KANIDM_ACCEPT_INVALID_CERTS",
1644 default_value_t = false,
1645 global = true
1646 )]
1647 accept_invalid_certs: bool,
1648 #[clap(
1650 short,
1651 long,
1652 env = "KANIDM_TOKEN_CACHE_PATH",
1653 hide = true,
1654 default_value = None,
1655 global=true,
1656 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1657 token_cache_path: Option<String>,
1658
1659 #[clap(
1660 short,
1661 long,
1662 env = "KANIDM_PASSWORD",
1663 hide = true,
1664 global = true,
1665 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1666 password: Option<String>,
1668}
1669
1670impl KanidmClientParser {
1671 fn get_token_cache_path(&self) -> String {
1672 match self.token_cache_path.clone() {
1673 None => CLIENT_TOKEN_CACHE.to_string(),
1674 Some(val) => val.clone(),
1675 }
1676 }
1677}