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 Ok(OffsetDateTime::now_utc())
12 } else {
13 OffsetDateTime::parse(input, &Rfc3339)
14 }
15}
16
17#[derive(Debug, Args, Clone)]
18pub struct Named {
19 pub name: String,
20}
21
22#[derive(Debug, Args, Clone)]
23pub struct DebugOpt {
24 #[clap(short, long, env = "KANIDM_DEBUG")]
26 pub debug: bool,
27}
28
29#[derive(Debug, Clone, Copy, Default)]
30pub enum OutputMode {
32 #[default]
33 Text,
34 Json,
35}
36
37impl From<OutputMode> for clap::builder::OsStr {
38 fn from(output_mode: OutputMode) -> Self {
39 match output_mode {
40 OutputMode::Text => "text".into(),
41 OutputMode::Json => "json".into(),
42 }
43 }
44}
45
46impl std::str::FromStr for OutputMode {
47 type Err = String;
48 fn from_str(s: &str) -> Result<OutputMode, std::string::String> {
49 match s.to_lowercase().as_str() {
50 "text" => Ok(OutputMode::Text),
51 "json" => Ok(OutputMode::Json),
52 _ => Ok(OutputMode::Text),
53 }
54 }
55}
56
57impl OutputMode {
58 pub fn print_message<T>(self, input: T)
59 where
60 T: serde::Serialize + fmt::Debug + fmt::Display,
61 {
62 match self {
63 OutputMode::Json => {
64 println!(
65 "{}",
66 serde_json::to_string(&input).unwrap_or(format!("{input:?}"))
67 );
68 }
69 OutputMode::Text => {
70 println!("{input}");
71 }
72 }
73 }
74}
75
76#[derive(Debug, Args, Clone)]
77pub struct GroupNamedMembers {
78 name: String,
79 #[clap(required = true, num_args(1..))]
80 members: Vec<String>,
81}
82
83#[derive(Debug, Args, Clone)]
84pub struct GroupPosixOpt {
85 name: String,
86 #[clap(long)]
87 gidnumber: Option<u32>,
88}
89
90#[derive(Debug, Subcommand, Clone)]
91pub enum GroupPosix {
92 #[clap(name = "show")]
94 Show(Named),
95 #[clap(name = "set")]
97 Set(GroupPosixOpt),
98 #[clap(name = "reset-gidnumber")]
100 ResetGidnumber { group_id: String },
101}
102
103#[derive(Debug, Clone, Copy, Eq, PartialEq)]
104pub enum AccountPolicyCredentialType {
105 Any,
106 Mfa,
107 Passkey,
108 AttestedPasskey,
109}
110
111impl AccountPolicyCredentialType {
112 pub fn as_str(&self) -> &'static str {
113 match self {
114 Self::Any => "any",
115 Self::Mfa => "mfa",
116 Self::Passkey => "passkey",
117 Self::AttestedPasskey => "attested_passkey",
118 }
119 }
120}
121
122impl ValueEnum for AccountPolicyCredentialType {
123 fn value_variants<'a>() -> &'a [Self] {
124 &[Self::Any, Self::Mfa, Self::Passkey, Self::AttestedPasskey]
125 }
126
127 fn to_possible_value(&self) -> Option<PossibleValue> {
128 Some(self.as_str().into())
129 }
130}
131
132#[derive(Debug, Subcommand, Clone)]
133pub enum GroupAccountPolicyOpt {
134 #[clap(name = "enable")]
136 Enable { name: String },
137 #[clap(name = "auth-expiry")]
139 AuthSessionExpiry { name: String, expiry: u32 },
140 #[clap(name = "credential-type-minimum")]
143 CredentialTypeMinimum {
144 name: String,
145 #[clap(value_enum)]
146 value: AccountPolicyCredentialType,
147 },
148 #[clap(name = "password-minimum-length")]
150 PasswordMinimumLength { name: String, length: u32 },
151
152 #[clap(name = "privilege-expiry")]
154 PrivilegedSessionExpiry { name: String, expiry: u32 },
155
156 #[clap(name = "webauthn-attestation-ca-list")]
161 WebauthnAttestationCaList {
162 name: String,
163 attestation_ca_list_json_file: PathBuf,
164 },
165
166 #[clap(name = "limit-search-max-results")]
169 LimitSearchMaxResults { name: String, maximum: u32 },
170 #[clap(name = "limit-search-max-filter-test")]
174 LimitSearchMaxFilterTest { name: String, maximum: u32 },
175 #[clap(name = "allow-primary-cred-fallback")]
178 AllowPrimaryCredFallback {
179 name: String,
180 #[clap(name = "allow", action = clap::ArgAction::Set)]
181 allow: bool,
182 },
183
184 #[clap(name = "reset-auth-expiry")]
186 ResetAuthSessionExpiry { name: String },
187 #[clap(name = "reset-password-minimum-length")]
189 ResetPasswordMinimumLength { name: String },
190 #[clap(name = "reset-privilege-expiry")]
192 ResetPrivilegedSessionExpiry { name: String },
193 #[clap(name = "reset-webauthn-attestation-ca-list")]
196 ResetWebauthnAttestationCaList { name: String },
197 #[clap(name = "reset-limit-search-max-results")]
199 ResetLimitSearchMaxResults { name: String },
200 #[clap(name = "reset-limit-search-max-filter-test")]
202 ResetLimitSearchMaxFilterTest { name: String },
203}
204
205#[derive(Debug, Subcommand, Clone)]
206pub enum GroupOpt {
207 #[clap(name = "list")]
209 List,
210 #[clap(name = "get")]
212 Get(Named),
213 #[clap(name = "search")]
215 Search {
216 name: String,
218 },
219 #[clap(name = "create")]
221 Create {
222 name: String,
224 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
226 entry_managed_by: Option<String>,
227 },
228 #[clap(name = "delete")]
230 Delete(Named),
231 #[clap(name = "list-members")]
233 ListMembers(Named),
234 #[clap(name = "set-members")]
237 SetMembers(GroupNamedMembers),
238 #[clap(name = "set-mail")]
242 SetMail { name: String, mail: Vec<String> },
243 #[clap(name = "set-description")]
245 SetDescription {
246 name: String,
247 description: Option<String>,
248 },
249 #[clap(name = "set-entry-manager")]
251 SetEntryManagedBy {
252 name: String,
254 entry_managed_by: String,
256 },
257 #[clap(name = "rename")]
259 Rename {
260 name: String,
262 new_name: String,
264 },
265 #[clap(name = "purge-members")]
267 PurgeMembers(Named),
268 #[clap(name = "add-members")]
270 AddMembers(GroupNamedMembers),
271 #[clap(name = "remove-members")]
273 RemoveMembers(GroupNamedMembers),
274 #[clap(name = "posix")]
276 Posix {
277 #[clap(subcommand)]
278 commands: GroupPosix,
279 },
280 #[clap(name = "account-policy")]
282 AccountPolicy {
283 #[clap(subcommand)]
284 commands: GroupAccountPolicyOpt,
285 },
286}
287
288#[derive(Clone, Debug, ValueEnum)]
289pub enum GraphType {
290 Graphviz,
291 Mermaid,
292 MermaidElk,
293}
294
295#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, ValueEnum)]
296pub enum ObjectType {
297 Group,
298 BuiltinGroup,
299 ServiceAccount,
300 Person,
301}
302
303#[derive(Debug, Args, Clone)]
304pub struct GraphCommonOpt {
305 #[arg(value_enum)]
306 pub graph_type: GraphType,
307 #[clap()]
308 pub filter: Vec<ObjectType>,
309}
310
311#[derive(Debug, Args, Clone)]
312pub struct AccountCommonOpt {
313 #[clap()]
314 account_id: String,
315}
316
317#[derive(Debug, Args, Clone)]
318pub struct AccountNamedOpt {
319 #[clap(flatten)]
320 aopts: AccountCommonOpt,
321}
322
323#[derive(Debug, Args, Clone)]
324pub struct AccountNamedExpireDateTimeOpt {
325 #[clap(flatten)]
326 aopts: AccountCommonOpt,
327 #[clap(name = "datetime", verbatim_doc_comment)]
328 datetime: String,
334}
335
336#[derive(Debug, Args, Clone)]
337pub struct AccountNamedValidDateTimeOpt {
338 #[clap(flatten)]
339 aopts: AccountCommonOpt,
340 #[clap(name = "datetime")]
341 datetime: String,
344}
345
346#[derive(Debug, Args, Clone)]
347pub struct AccountNamedTagOpt {
348 #[clap(flatten)]
349 aopts: AccountCommonOpt,
350 #[clap(name = "tag")]
351 tag: String,
352}
353
354#[derive(Debug, Args, Clone)]
355pub struct AccountNamedTagPkOpt {
356 #[clap(flatten)]
357 aopts: AccountCommonOpt,
358 #[clap(name = "tag")]
359 tag: String,
360 #[clap(name = "pubkey")]
361 pubkey: String,
362}
363
364#[derive(Debug, Args, Clone)]
365pub struct UseResetTokenOpt {
367 #[clap(name = "token")]
368 token: String,
369}
370
371#[derive(Debug, Args, Clone)]
372pub struct AccountCreateOpt {
373 #[clap(flatten)]
374 aopts: AccountCommonOpt,
375 #[clap(name = "display-name")]
376 display_name: String,
377}
378
379#[derive(Debug, Subcommand, Clone)]
380pub enum AccountCredential {
381 #[clap(name = "status")]
383 Status(AccountNamedOpt),
384 #[clap(name = "update")]
386 Update(AccountNamedOpt),
387 #[clap(name = "use-reset-token")]
389 UseResetToken(UseResetTokenOpt),
390 #[clap(name = "create-reset-token")]
393 CreateResetToken {
394 #[clap(flatten)]
395 aopts: AccountCommonOpt,
396
397 ttl: Option<u32>,
400 },
401}
402
403#[derive(Debug, Subcommand, Clone)]
405pub enum AccountRadius {
406 #[clap(name = "show-secret")]
408 Show(AccountNamedOpt),
409 #[clap(name = "generate-secret")]
411 Generate(AccountNamedOpt),
412 #[clap(name = "delete-secret")]
413 DeleteSecret(AccountNamedOpt),
415}
416
417#[derive(Debug, Args, Clone)]
418pub struct AccountPosixOpt {
419 #[clap(flatten)]
420 aopts: AccountCommonOpt,
421 #[clap(long)]
422 gidnumber: Option<u32>,
423 #[clap(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
424 shell: Option<String>,
426}
427
428#[derive(Debug, Subcommand, Clone)]
429pub enum PersonPosix {
430 #[clap(name = "show")]
431 Show(AccountNamedOpt),
432 #[clap(name = "set")]
433 Set(AccountPosixOpt),
434 #[clap(name = "set-password")]
435 SetPassword(AccountNamedOpt),
436 #[clap(name = "reset-gidnumber")]
438 ResetGidnumber { account_id: String },
439}
440
441#[derive(Debug, Subcommand, Clone)]
442pub enum ServiceAccountPosix {
443 #[clap(name = "show")]
444 Show(AccountNamedOpt),
445 #[clap(name = "set")]
446 Set(AccountPosixOpt),
447 #[clap(name = "reset-gidnumber")]
449 ResetGidnumber { account_id: String },
450}
451
452#[derive(Debug, Args, Clone)]
453pub struct PersonUpdateOpt {
454 #[clap(flatten)]
455 aopts: AccountCommonOpt,
456 #[clap(long, short, help = "Set the legal name for the person.",
457 value_parser = clap::builder::NonEmptyStringValueParser::new())]
458 legalname: Option<String>,
459 #[clap(long, short, help = "Set the account name for the person.",
460 value_parser = clap::builder::NonEmptyStringValueParser::new())]
461 newname: Option<String>,
462 #[clap(long, short = 'i', help = "Set the display name for the person.",
463 value_parser = clap::builder::NonEmptyStringValueParser::new())]
464 displayname: Option<String>,
465 #[clap(
466 long,
467 short,
468 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
469 )]
470 mail: Option<Vec<String>>,
471}
472
473#[derive(Debug, Subcommand, Clone)]
474pub enum AccountSsh {
475 #[clap(name = "list-publickeys")]
476 List(AccountNamedOpt),
477 #[clap(name = "add-publickey")]
478 Add(AccountNamedTagPkOpt),
479 #[clap(name = "delete-publickey")]
480 Delete(AccountNamedTagOpt),
481}
482
483#[derive(Debug, Subcommand, Clone)]
484pub enum AccountValidity {
485 #[clap(name = "show")]
487 Show(AccountNamedOpt),
488 #[clap(name = "expire-at")]
490 ExpireAt(AccountNamedExpireDateTimeOpt),
491 #[clap(name = "begin-from")]
493 BeginFrom(AccountNamedValidDateTimeOpt),
494}
495
496#[derive(Debug, Subcommand, Clone)]
497pub enum AccountCertificate {
498 #[clap(name = "status")]
499 Status { account_id: String },
500 #[clap(name = "create")]
501 Create {
502 account_id: String,
503 certificate_path: PathBuf,
504 },
505}
506
507#[derive(Debug, Subcommand, Clone)]
508pub enum AccountUserAuthToken {
509 #[clap(name = "status")]
511 Status(AccountNamedOpt),
512 #[clap(name = "destroy")]
515 Destroy {
516 #[clap(flatten)]
517 aopts: AccountCommonOpt,
518
519 #[clap(name = "session-id")]
521 session_id: Uuid,
522 },
523}
524
525#[derive(Debug, Subcommand, Clone)]
526pub enum PersonOpt {
527 #[clap(name = "credential")]
529 Credential {
530 #[clap(subcommand)]
531 commands: AccountCredential,
532 },
533 #[clap(name = "radius")]
535 Radius {
536 #[clap(subcommand)]
537 commands: AccountRadius,
538 },
539 #[clap(name = "posix")]
541 Posix {
542 #[clap(subcommand)]
543 commands: PersonPosix,
544 },
545 #[clap(name = "session")]
547 Session {
548 #[clap(subcommand)]
549 commands: AccountUserAuthToken,
550 },
551 #[clap(name = "ssh")]
553 Ssh {
554 #[clap(subcommand)]
555 commands: AccountSsh,
556 },
557 #[clap(name = "list")]
559 List,
560 #[clap(name = "get")]
562 Get(AccountNamedOpt),
563 #[clap(name = "search")]
565 Search { account_id: String },
566 #[clap(name = "update")]
568 Update(PersonUpdateOpt),
569 #[clap(name = "create")]
571 Create(AccountCreateOpt),
572 #[clap(name = "delete")]
574 Delete(AccountNamedOpt),
575 #[clap(name = "validity")]
577 Validity {
578 #[clap(subcommand)]
579 commands: AccountValidity,
580 },
581 #[clap(name = "certificate", hide = true)]
582 Certificate {
583 #[clap(subcommand)]
584 commands: AccountCertificate,
585 },
586}
587
588#[derive(Debug, Subcommand, Clone)]
589pub enum ServiceAccountCredential {
590 #[clap(name = "status")]
592 Status(AccountNamedOpt),
593 #[clap(name = "generate")]
596 GeneratePw(AccountNamedOpt),
597}
598
599#[derive(Debug, Subcommand, Clone)]
600pub enum ServiceAccountApiToken {
601 #[clap(name = "status")]
603 Status(AccountNamedOpt),
604 #[clap(name = "generate")]
606 Generate {
607 #[clap(flatten)]
608 aopts: AccountCommonOpt,
609
610 #[clap(name = "label")]
613 label: String,
614 #[clap(name = "expiry")]
615 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
618 expiry: Option<String>,
619 #[clap(short = 'w', long = "readwrite")]
621 read_write: bool,
622 },
623 #[clap(name = "destroy")]
626 Destroy {
627 #[clap(flatten)]
628 aopts: AccountCommonOpt,
629
630 #[clap(name = "token-id")]
632 token_id: Uuid,
633 },
634}
635
636#[derive(Debug, Args, Clone)]
637pub struct ServiceAccountUpdateOpt {
638 #[clap(flatten)]
639 aopts: AccountCommonOpt,
640 #[clap(long, short, help = "Set the account name for the service account.",
641 value_parser = clap::builder::NonEmptyStringValueParser::new())]
642 newname: Option<String>,
643 #[clap(
644 long,
645 short = 'i',
646 help = "Set the display name for the service account.",
647 value_parser = clap::builder::NonEmptyStringValueParser::new()
648 )]
649 displayname: Option<String>,
650 #[clap(
651 long,
652 short = 'e',
653 help = "Set the entry manager for the service account.",
654 value_parser = clap::builder::NonEmptyStringValueParser::new()
655 )]
656 entry_managed_by: Option<String>,
657 #[clap(
658 long,
659 short,
660 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
661 )]
662 mail: Option<Vec<String>>,
663}
664
665#[derive(Debug, Subcommand, Clone)]
666pub enum ServiceAccountOpt {
667 #[clap(name = "credential")]
669 Credential {
670 #[clap(subcommand)]
671 commands: ServiceAccountCredential,
672 },
673 #[clap(name = "api-token")]
675 ApiToken {
676 #[clap(subcommand)]
677 commands: ServiceAccountApiToken,
678 },
679 #[clap(name = "posix")]
681 Posix {
682 #[clap(subcommand)]
683 commands: ServiceAccountPosix,
684 },
685 #[clap(name = "session")]
687 Session {
688 #[clap(subcommand)]
689 commands: AccountUserAuthToken,
690 },
691 #[clap(name = "ssh")]
693 Ssh {
694 #[clap(subcommand)]
695 commands: AccountSsh,
696 },
697 #[clap(name = "list")]
699 List,
700 #[clap(name = "get")]
702 Get(AccountNamedOpt),
703 #[clap(name = "create")]
705 Create {
706 #[clap(flatten)]
707 aopts: AccountCommonOpt,
708 #[clap(name = "display-name")]
709 display_name: String,
710 #[clap(name = "entry-managed-by")]
711 entry_managed_by: String,
712 },
713 #[clap(name = "update")]
715 Update(ServiceAccountUpdateOpt),
716 #[clap(name = "delete")]
718 Delete(AccountNamedOpt),
719 #[clap(name = "validity")]
721 Validity {
722 #[clap(subcommand)]
723 commands: AccountValidity,
724 },
725 #[clap(name = "into-person")]
729 IntoPerson(AccountNamedOpt),
730}
731
732#[derive(Debug, Subcommand, Clone)]
733pub enum RecycleOpt {
734 #[clap(name = "list")]
735 List,
737 #[clap(name = "get")]
738 Get(Named),
740 #[clap(name = "revive")]
741 Revive(Named),
743}
744
745#[derive(Debug, Args, Clone)]
746pub struct LoginOpt {}
747
748#[derive(Debug, Args, Clone)]
749pub struct LogoutOpt {
750 #[clap(short, long)]
751 local_only: bool,
753}
754
755#[derive(Debug, Subcommand, Clone)]
756pub enum SessionOpt {
757 #[clap(name = "list")]
758 List,
760 #[clap(name = "cleanup")]
761 Cleanup,
763}
764
765#[derive(Debug, Subcommand, Clone)]
766pub enum RawOpt {
767 #[clap(name = "search")]
768 Search {
769 filter: ScimFilter
770 },
771 #[clap(name = "create")]
772 Create {
773 file: PathBuf
774 },
775 #[clap(name = "update")]
776 Update {
777 file: PathBuf
778 },
779 #[clap(name = "delete")]
780 Delete {
781 id: String
782 },
783}
784
785#[derive(Debug, Subcommand, Clone)]
786pub enum SelfOpt {
787 #[clap(name = "identify-user")]
789 IdentifyUser,
790 Whoami,
792}
793
794#[derive(Debug, Args, Clone)]
795pub struct Oauth2SetDisplayname {
796 #[clap(flatten)]
797 nopt: Named,
798 #[clap(name = "displayname")]
799 displayname: String,
800}
801
802#[derive(Debug, Args, Clone)]
803pub struct Oauth2SetImplicitScopes {
804 #[clap(flatten)]
805 nopt: Named,
806 #[clap(name = "scopes")]
807 scopes: Vec<String>,
808}
809
810#[derive(Debug, Args, Clone)]
811pub struct Oauth2CreateScopeMapOpt {
812 #[clap(flatten)]
813 nopt: Named,
814 #[clap(name = "group")]
815 group: String,
816 #[clap(name = "scopes", required = true, num_args=1.. )]
817 scopes: Vec<String>,
818}
819
820#[derive(Debug, Args, Clone)]
821pub struct Oauth2DeleteScopeMapOpt {
822 #[clap(flatten)]
823 nopt: Named,
824 #[clap(name = "group")]
825 group: String,
826}
827
828#[derive(Debug, Clone, Copy, Eq, PartialEq)]
829pub enum Oauth2ClaimMapJoin {
830 Csv,
831 Ssv,
832 Array,
833}
834
835impl Oauth2ClaimMapJoin {
836 pub fn as_str(&self) -> &'static str {
837 match self {
838 Self::Csv => "csv",
839 Self::Ssv => "ssv",
840 Self::Array => "array",
841 }
842 }
843}
844
845impl ValueEnum for Oauth2ClaimMapJoin {
846 fn value_variants<'a>() -> &'a [Self] {
847 &[Self::Csv, Self::Ssv, Self::Array]
848 }
849
850 fn to_possible_value(&self) -> Option<PossibleValue> {
851 Some(self.as_str().into())
852 }
853}
854
855#[derive(Debug, Subcommand, Clone)]
856pub enum Oauth2Opt {
857 #[clap(name = "list")]
858 List,
860 #[clap(name = "get")]
861 Get(Named),
863 #[clap(name = "create")]
867 CreateBasic {
869 #[clap(name = "name")]
870 name: String,
871 #[clap(name = "displayname")]
872 displayname: String,
873 #[clap(name = "origin")]
874 origin: String,
875 },
876 #[clap(name = "create-public")]
877 CreatePublic {
883 #[clap(name = "name")]
884 name: String,
885 #[clap(name = "displayname")]
886 displayname: String,
887 #[clap(name = "origin")]
888 origin: String,
889 },
890 #[clap(name = "update-scope-map", visible_aliases=&["create-scope-map"])]
891 UpdateScopeMap(Oauth2CreateScopeMapOpt),
893 #[clap(name = "delete-scope-map")]
894 DeleteScopeMap(Oauth2DeleteScopeMapOpt),
896
897 #[clap(name = "update-sup-scope-map", visible_aliases=&["create-sup-scope-map"])]
898 UpdateSupScopeMap(Oauth2CreateScopeMapOpt),
900 #[clap(name = "delete-sup-scope-map")]
901 DeleteSupScopeMap(Oauth2DeleteScopeMapOpt),
903
904 #[clap(name = "update-claim-map", visible_aliases=&["create-claim-map"])]
905 UpdateClaimMap {
907 name: String,
908 claim_name: String,
909 group: String,
910 values: Vec<String>,
911 },
912 #[clap(name = "update-claim-map-join")]
913 UpdateClaimMapJoin {
914 name: String,
915 claim_name: String,
916 join: Oauth2ClaimMapJoin,
919 },
920 #[clap(name = "delete-claim-map")]
921 DeleteClaimMap {
923 name: String,
924 claim_name: String,
925 group: String,
926 },
927
928 #[clap(name = "reset-basic-secret")]
929 ResetSecrets(Named),
932 #[clap(name = "show-basic-secret")]
933 ShowBasicSecret(Named),
935 #[clap(name = "delete")]
936 Delete(Named),
938 #[clap(name = "set-displayname")]
940 SetDisplayname(Oauth2SetDisplayname),
941 #[clap(name = "set-name")]
945 SetName {
946 #[clap(flatten)]
947 nopt: Named,
948 #[clap(name = "newname")]
949 name: String,
950 },
951
952 #[clap(name = "set-landing-url")]
955 SetLandingUrl {
956 #[clap(flatten)]
957 nopt: Named,
958 #[clap(name = "landing-url")]
959 url: Url,
960 },
961 #[clap(name = "set-image")]
963 SetImage {
964 #[clap(flatten)]
965 nopt: Named,
966 #[clap(name = "file-path")]
967 path: PathBuf,
969 #[clap(name = "image-type")]
970 image_type: Option<ImageType>,
972 },
973 #[clap(name = "remove-image")]
975 RemoveImage(Named),
976
977 #[clap(name = "add-redirect-url")]
981 AddOrigin {
982 name: String,
983 #[clap(name = "url")]
984 origin: Url,
985 },
986
987 #[clap(name = "remove-redirect-url")]
989 RemoveOrigin {
990 name: String,
991 #[clap(name = "url")]
992 origin: Url,
993 },
994 #[clap(name = "enable-pkce")]
995 EnablePkce(Named),
997 #[clap(name = "warning-insecure-client-disable-pkce")]
1000 DisablePkce(Named),
1001 #[clap(name = "warning-enable-legacy-crypto")]
1002 EnableLegacyCrypto(Named),
1006 #[clap(name = "disable-legacy-crypto")]
1008 DisableLegacyCrypto(Named),
1009 #[clap(name = "enable-strict-redirect-url")]
1013 EnableStrictRedirectUri { name: String },
1014 #[clap(name = "disable-strict-redirect-url")]
1015 DisableStrictRedirectUri { name: String },
1016 #[clap(name = "enable-localhost-redirects")]
1017 EnablePublicLocalhost { name: String },
1019 #[clap(name = "disable-localhost-redirects")]
1021 DisablePublicLocalhost { name: String },
1022 #[clap(name = "prefer-short-username")]
1024 PreferShortUsername(Named),
1025 #[clap(name = "prefer-spn-username")]
1027 PreferSPNUsername(Named),
1028 #[cfg(feature = "dev-oauth2-device-flow")]
1029 DeviceFlowEnable(Named),
1031 #[cfg(feature = "dev-oauth2-device-flow")]
1032 DeviceFlowDisable(Named),
1034 #[clap(name = "rotate-cryptographic-keys")]
1040 RotateCryptographicKeys {
1041 name: String,
1042 #[clap(value_parser = parse_rfc3339)]
1043 rotate_at: OffsetDateTime,
1044 },
1045 #[clap(name = "revoke-cryptographic-key")]
1049 RevokeCryptographicKey { name: String, key_id: String },
1050}
1051
1052#[derive(Args, Debug, Clone)]
1053pub struct OptSetDomainDisplayname {
1054 #[clap(name = "new-display-name")]
1055 new_display_name: String,
1056}
1057
1058#[derive(Debug, Subcommand, Clone)]
1059pub enum PwBadlistOpt {
1060 #[clap[name = "show"]]
1061 Show,
1063 #[clap[name = "upload"]]
1064 Upload {
1068 #[clap(value_parser, required = true, num_args(1..))]
1069 paths: Vec<PathBuf>,
1070 #[clap(short = 'n', long)]
1072 dryrun: bool,
1073 },
1074 #[clap[name = "remove", hide = true]]
1075 Remove {
1078 #[clap(value_parser, required = true, num_args(1..))]
1079 paths: Vec<PathBuf>,
1080 },
1081}
1082
1083#[derive(Debug, Subcommand, Clone)]
1084pub enum DeniedNamesOpt {
1085 #[clap[name = "show"]]
1086 Show,
1088 #[clap[name = "append"]]
1089 Append {
1090 #[clap(value_parser, required = true, num_args(1..))]
1091 names: Vec<String>,
1092 },
1093 #[clap[name = "remove"]]
1094 Remove {
1096 #[clap(value_parser, required = true, num_args(1..))]
1097 names: Vec<String>,
1098 },
1099}
1100
1101#[derive(Debug, Subcommand, Clone)]
1102pub enum DomainOpt {
1103 #[clap[name = "set-displayname"]]
1104 SetDisplayname(OptSetDomainDisplayname),
1106 #[clap[name = "set-ldap-queryable-attrs"]]
1108 SetLdapMaxQueryableAttrs {
1109 #[clap(name = "maximum-queryable-attrs")]
1110 new_max_queryable_attrs: usize,
1111 },
1112 #[clap[name = "set-ldap-basedn"]]
1113 SetLdapBasedn {
1118 #[clap(name = "new-basedn")]
1119 new_basedn: String,
1120 },
1121 SetLdapAllowUnixPasswordBind {
1124 #[clap(name = "allow", action = clap::ArgAction::Set)]
1125 enable: bool,
1126 },
1127 SetAllowEasterEggs {
1131 #[clap(name = "allow", action = clap::ArgAction::Set)]
1132 enable: bool,
1133 },
1134 #[clap(name = "show")]
1135 Show,
1137 #[clap(name = "revoke-key")]
1138 RevokeKey { key_id: String },
1141 #[clap(name = "set-image")]
1143 SetImage {
1144 #[clap(name = "file-path")]
1145 path: PathBuf,
1146 #[clap(name = "image-type")]
1147 image_type: Option<ImageType>,
1148 },
1149 #[clap(name = "remove-image")]
1151 RemoveImage,
1152}
1153
1154#[derive(Debug, Subcommand, Clone)]
1155pub enum MessageOpt {
1156 #[clap(name = "list")]
1157 List,
1159
1160 #[clap(name = "get")]
1161 Get {
1163 message_id: Uuid
1164 },
1165
1166 #[clap(name = "mark-as-sent")]
1167 MarkAsSent {
1170 message_id: Uuid
1171 },
1172
1173 #[clap(name = "send-test-message")]
1174 SendTestMessage {
1175 to: String,
1177 }
1178}
1179
1180#[derive(Debug, Subcommand, Clone)]
1181pub enum SynchOpt {
1182 #[clap(name = "list")]
1183 List,
1185 #[clap(name = "get")]
1186 Get(Named),
1188 #[clap(name = "set-credential-portal")]
1189 SetCredentialPortal {
1192 #[clap()]
1193 account_id: String,
1194
1195 #[clap(name = "url")]
1196 url: Option<Url>,
1197 },
1198 #[clap(name = "create")]
1200 Create {
1201 #[clap()]
1202 account_id: String,
1203
1204 #[clap(name = "description",
1205 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1206 description: Option<String>,
1207 },
1208 #[clap(name = "generate-token")]
1210 GenerateToken {
1211 #[clap()]
1212 account_id: String,
1213 #[clap()]
1214 label: String,
1215 },
1216 #[clap(name = "destroy-token")]
1218 DestroyToken {
1219 #[clap()]
1220 account_id: String,
1221 },
1222 #[clap(name = "set-yield-attributes")]
1226 SetYieldAttributes {
1227 #[clap()]
1228 account_id: String,
1229
1230 #[clap(name = "attributes")]
1231 attrs: Vec<String>,
1232 },
1233 #[clap(name = "force-refresh")]
1237 ForceRefresh {
1238 #[clap()]
1239 account_id: String,
1240 },
1241 #[clap(name = "finalise")]
1247 Finalise {
1248 #[clap()]
1249 account_id: String,
1250 },
1251 #[clap(name = "terminate")]
1257 Terminate {
1258 #[clap()]
1259 account_id: String,
1260 },
1261}
1262
1263#[derive(Debug, Subcommand, Clone)]
1264pub enum AuthSessionExpiryOpt {
1265 #[clap[name = "get"]]
1266 Get,
1268 #[clap[name = "set"]]
1269 Set {
1271 #[clap(name = "expiry")]
1272 expiry: u32,
1273 },
1274}
1275
1276#[derive(Debug, Subcommand, Clone)]
1277pub enum PrivilegedSessionExpiryOpt {
1278 #[clap[name = "get"]]
1279 Get,
1281 #[clap[name = "set"]]
1282 Set {
1284 #[clap(name = "expiry")]
1285 expiry: u32,
1286 },
1287}
1288
1289#[derive(Args, Debug, Clone)]
1290pub struct ApiSchemaDownloadOpt {
1291 #[clap(name = "filename", env, default_value = "./kanidm-openapi.json")]
1293 filename: PathBuf,
1294 #[clap(short, long, env)]
1296 force: bool,
1297}
1298
1299#[derive(Debug, Subcommand, Clone)]
1300pub enum ApiOpt {
1301 #[clap(name = "download-schema")]
1303 DownloadSchema(ApiSchemaDownloadOpt),
1304}
1305
1306#[derive(Debug, Subcommand, Clone)]
1307pub enum SchemaClassOpt {
1308 List,
1310 Search {
1311 query: String,
1312 },
1313}
1314
1315#[derive(Debug, Subcommand, Clone)]
1316pub enum SchemaAttrOpt {
1317 List,
1319 Search {
1320 query: String,
1321 },
1322}
1323
1324#[derive(Debug, Subcommand, Clone)]
1325pub enum SchemaOpt {
1326 #[clap(name = "class")]
1328 Class {
1329 #[clap(subcommand)]
1330 commands: SchemaClassOpt,
1331 },
1332 #[clap(name = "attribute", visible_alias = "attr")]
1334 Attribute {
1335 #[clap(subcommand)]
1336 commands: SchemaAttrOpt,
1337 },
1338}
1339
1340#[derive(Debug, Subcommand, Clone)]
1341pub enum SystemOpt {
1342 #[clap(name = "pw-badlist")]
1343 PwBadlist {
1345 #[clap(subcommand)]
1346 commands: PwBadlistOpt,
1347 },
1348 #[clap(name = "denied-names")]
1349 DeniedNames {
1351 #[clap(subcommand)]
1352 commands: DeniedNamesOpt,
1353 },
1354 #[clap(name = "oauth2")]
1355 Oauth2 {
1357 #[clap(subcommand)]
1358 commands: Oauth2Opt,
1359 },
1360 #[clap(name = "domain")]
1361 Domain {
1363 #[clap(subcommand)]
1364 commands: DomainOpt,
1365 },
1366 #[clap(name = "sync")]
1367 Synch {
1369 #[clap(subcommand)]
1370 commands: SynchOpt,
1371 },
1372 #[clap(name = "message-queue", alias = "message")]
1373 Message {
1375 #[clap(subcommand)]
1376 commands: MessageOpt,
1377 },
1378 #[clap(name = "api")]
1379 Api {
1381 #[clap(subcommand)]
1382 commands: ApiOpt,
1383 },
1384}
1385
1386#[derive(Debug, Subcommand, Clone)]
1387#[clap(about = "Kanidm Client Utility")]
1388pub enum KanidmClientOpt {
1389 Login(LoginOpt),
1391 Reauth {
1393 #[clap()]
1394 mode: kanidm_proto::cli::OpType,
1395 },
1396 Logout(LogoutOpt),
1398 Session {
1400 #[clap(subcommand)]
1401 commands: SessionOpt,
1402 },
1403 #[clap(name = "self")]
1404 CSelf {
1406 #[clap(subcommand)]
1407 commands: SelfOpt,
1408 },
1409 Person {
1411 #[clap(subcommand)]
1412 commands: PersonOpt,
1413 },
1414 Group {
1416 #[clap(subcommand)]
1417 commands: GroupOpt,
1418 },
1419 #[clap(name = "service-account")]
1421 ServiceAccount {
1422 #[clap(subcommand)]
1423 commands: ServiceAccountOpt,
1424 },
1425 #[clap(name = "graph")]
1427 Graph(GraphCommonOpt),
1428
1429 #[clap(hide = true)]
1431 Schema {
1432 #[clap(subcommand)]
1433 commands: SchemaOpt,
1434 },
1435
1436 System {
1438 #[clap(subcommand)]
1439 commands: SystemOpt,
1440 },
1441 #[clap(name = "recycle-bin")]
1442 Recycle {
1444 #[clap(subcommand)]
1445 commands: RecycleOpt,
1446 },
1447 #[clap(hide = true)]
1449 Raw {
1450 #[clap(subcommand)]
1451 commands: RawOpt,
1452 },
1453 Version,
1455}
1456
1457#[derive(Debug, clap::Parser, Clone)]
1458#[clap(about = "Kanidm Client Utility")]
1459pub struct KanidmClientParser {
1460 #[clap(subcommand)]
1461 pub commands: KanidmClientOpt,
1462
1463 #[clap(short, long, env = "KANIDM_DEBUG", global = true)]
1465 pub debug: bool,
1466 #[clap(short = 'I', long = "instance", env = "KANIDM_INSTANCE", global = true,
1468 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1469 pub instance: Option<String>,
1470 #[clap(short = 'H', long = "url", env = "KANIDM_URL", global = true,
1472 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1473 pub addr: Option<String>,
1474 #[clap(
1476 short = 'D',
1477 long = "name",
1478 env = "KANIDM_NAME",
1479 value_parser = clap::builder::NonEmptyStringValueParser::new(), global=true
1480 )]
1481 pub username: Option<String>,
1482 #[clap(
1484 value_parser,
1485 short = 'C',
1486 long = "ca",
1487 env = "KANIDM_CA_PATH",
1488 global = true
1489 )]
1490 pub ca_path: Option<PathBuf>,
1491 #[clap(short, long = "output", env = "KANIDM_OUTPUT", global = true, default_value=OutputMode::default())]
1493 output_mode: OutputMode,
1494 #[clap(
1496 long = "skip-hostname-verification",
1497 env = "KANIDM_SKIP_HOSTNAME_VERIFICATION",
1498 default_value_t = false,
1499 global = true
1500 )]
1501 skip_hostname_verification: bool,
1502 #[clap(
1504 long = "accept-invalid-certs",
1505 env = "KANIDM_ACCEPT_INVALID_CERTS",
1506 default_value_t = false,
1507 global = true
1508 )]
1509 accept_invalid_certs: bool,
1510 #[clap(
1512 short,
1513 long,
1514 env = "KANIDM_TOKEN_CACHE_PATH",
1515 hide = true,
1516 default_value = None,
1517 global=true,
1518 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1519 token_cache_path: Option<String>,
1520
1521 #[clap(
1522 short,
1523 long,
1524 env = "KANIDM_PASSWORD",
1525 hide = true,
1526 global = true,
1527 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1528 password: Option<String>,
1530}
1531
1532impl KanidmClientParser {
1533 fn get_token_cache_path(&self) -> String {
1534 match self.token_cache_path.clone() {
1535 None => CLIENT_TOKEN_CACHE.to_string(),
1536 Some(val) => val.clone(),
1537 }
1538 }
1539}