1use clap::{builder::PossibleValue, Args, Subcommand, ValueEnum};
2use kanidm_proto::constants::CLIENT_TOKEN_CACHE;
3use kanidm_proto::internal::ImageType;
4use std::fmt;
5use time::format_description::well_known::Rfc3339;
6use time::OffsetDateTime;
7
8fn parse_rfc3339(input: &str) -> Result<OffsetDateTime, time::error::Parse> {
9 if input == "now" {
10 Ok(OffsetDateTime::now_utc())
11 } else {
12 OffsetDateTime::parse(input, &Rfc3339)
13 }
14}
15
16#[derive(Debug, Args, Clone)]
17pub struct Named {
18 pub name: String,
19}
20
21#[derive(Debug, Args, Clone)]
22pub struct DebugOpt {
23 #[clap(short, long, env = "KANIDM_DEBUG")]
25 pub debug: bool,
26}
27
28#[derive(Debug, Clone, Copy, Default)]
29pub enum OutputMode {
31 #[default]
32 Text,
33 Json,
34}
35
36impl From<OutputMode> for clap::builder::OsStr {
37 fn from(output_mode: OutputMode) -> Self {
38 match output_mode {
39 OutputMode::Text => "text".into(),
40 OutputMode::Json => "json".into(),
41 }
42 }
43}
44
45impl std::str::FromStr for OutputMode {
46 type Err = String;
47 fn from_str(s: &str) -> Result<OutputMode, std::string::String> {
48 match s.to_lowercase().as_str() {
49 "text" => Ok(OutputMode::Text),
50 "json" => Ok(OutputMode::Json),
51 _ => Ok(OutputMode::Text),
52 }
53 }
54}
55
56impl OutputMode {
57 pub fn print_message<T>(self, input: T)
58 where
59 T: serde::Serialize + fmt::Debug + fmt::Display,
60 {
61 match self {
62 OutputMode::Json => {
63 println!(
64 "{}",
65 serde_json::to_string(&input).unwrap_or(format!("{input:?}"))
66 );
67 }
68 OutputMode::Text => {
69 println!("{input}");
70 }
71 }
72 }
73}
74
75#[derive(Debug, Args, Clone)]
76pub struct GroupNamedMembers {
77 name: String,
78 #[clap(required = true, num_args(1..))]
79 members: Vec<String>,
80}
81
82#[derive(Debug, Args, Clone)]
83pub struct GroupPosixOpt {
84 name: String,
85 #[clap(long)]
86 gidnumber: Option<u32>,
87}
88
89#[derive(Debug, Subcommand, Clone)]
90pub enum GroupPosix {
91 #[clap(name = "show")]
93 Show(Named),
94 #[clap(name = "set")]
96 Set(GroupPosixOpt),
97 #[clap(name = "reset-gidnumber")]
99 ResetGidnumber { group_id: String },
100}
101
102#[derive(Debug, Clone, Copy, Eq, PartialEq)]
103pub enum AccountPolicyCredentialType {
104 Any,
105 Mfa,
106 Passkey,
107 AttestedPasskey,
108}
109
110impl AccountPolicyCredentialType {
111 pub fn as_str(&self) -> &'static str {
112 match self {
113 Self::Any => "any",
114 Self::Mfa => "mfa",
115 Self::Passkey => "passkey",
116 Self::AttestedPasskey => "attested_passkey",
117 }
118 }
119}
120
121impl ValueEnum for AccountPolicyCredentialType {
122 fn value_variants<'a>() -> &'a [Self] {
123 &[Self::Any, Self::Mfa, Self::Passkey, Self::AttestedPasskey]
124 }
125
126 fn to_possible_value(&self) -> Option<PossibleValue> {
127 Some(self.as_str().into())
128 }
129}
130
131#[derive(Debug, Subcommand, Clone)]
132pub enum GroupAccountPolicyOpt {
133 #[clap(name = "enable")]
135 Enable { name: String },
136 #[clap(name = "auth-expiry")]
138 AuthSessionExpiry { name: String, expiry: u32 },
139 #[clap(name = "credential-type-minimum")]
142 CredentialTypeMinimum {
143 name: String,
144 #[clap(value_enum)]
145 value: AccountPolicyCredentialType,
146 },
147 #[clap(name = "password-minimum-length")]
149 PasswordMinimumLength { name: String, length: u32 },
150
151 #[clap(name = "privilege-expiry")]
153 PrivilegedSessionExpiry { name: String, expiry: u32 },
154
155 #[clap(name = "webauthn-attestation-ca-list")]
160 WebauthnAttestationCaList {
161 name: String,
162 attestation_ca_list_json_file: PathBuf,
163 },
164
165 #[clap(name = "limit-search-max-results")]
168 LimitSearchMaxResults { name: String, maximum: u32 },
169 #[clap(name = "limit-search-max-filter-test")]
173 LimitSearchMaxFilterTest { name: String, maximum: u32 },
174 #[clap(name = "allow-primary-cred-fallback")]
177 AllowPrimaryCredFallback {
178 name: String,
179 #[clap(name = "allow", action = clap::ArgAction::Set)]
180 allow: bool,
181 },
182
183 #[clap(name = "reset-auth-expiry")]
185 ResetAuthSessionExpiry { name: String },
186 #[clap(name = "reset-password-minimum-length")]
188 ResetPasswordMinimumLength { name: String },
189 #[clap(name = "reset-privilege-expiry")]
191 ResetPrivilegedSessionExpiry { name: String },
192 #[clap(name = "reset-webauthn-attestation-ca-list")]
195 ResetWebauthnAttestationCaList { name: String },
196 #[clap(name = "reset-limit-search-max-results")]
198 ResetLimitSearchMaxResults { name: String },
199 #[clap(name = "reset-limit-search-max-filter-test")]
201 ResetLimitSearchMaxFilterTest { name: String },
202}
203
204#[derive(Debug, Subcommand, Clone)]
205pub enum GroupOpt {
206 #[clap(name = "list")]
208 List,
209 #[clap(name = "get")]
211 Get(Named),
212 #[clap(name = "search")]
214 Search {
215 name: String,
217 },
218 #[clap(name = "create")]
220 Create {
221 name: String,
223 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
225 entry_managed_by: Option<String>,
226 },
227 #[clap(name = "delete")]
229 Delete(Named),
230 #[clap(name = "list-members")]
232 ListMembers(Named),
233 #[clap(name = "set-members")]
236 SetMembers(GroupNamedMembers),
237 #[clap(name = "set-mail")]
241 SetMail { name: String, mail: Vec<String> },
242 #[clap(name = "set-description")]
244 SetDescription {
245 name: String,
246 description: Option<String>,
247 },
248 #[clap(name = "set-entry-manager")]
250 SetEntryManagedBy {
251 name: String,
253 entry_managed_by: String,
255 },
256 #[clap(name = "rename")]
258 Rename {
259 name: String,
261 new_name: String,
263 },
264 #[clap(name = "purge-members")]
266 PurgeMembers(Named),
267 #[clap(name = "add-members")]
269 AddMembers(GroupNamedMembers),
270 #[clap(name = "remove-members")]
272 RemoveMembers(GroupNamedMembers),
273 #[clap(name = "posix")]
275 Posix {
276 #[clap(subcommand)]
277 commands: GroupPosix,
278 },
279 #[clap(name = "account-policy")]
281 AccountPolicy {
282 #[clap(subcommand)]
283 commands: GroupAccountPolicyOpt,
284 },
285}
286
287#[derive(Clone, Debug, ValueEnum)]
288pub enum GraphType {
289 Graphviz,
290 Mermaid,
291 MermaidElk,
292}
293
294#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, ValueEnum)]
295pub enum ObjectType {
296 Group,
297 BuiltinGroup,
298 ServiceAccount,
299 Person,
300}
301
302#[derive(Debug, Args, Clone)]
303pub struct GraphCommonOpt {
304 #[arg(value_enum)]
305 pub graph_type: GraphType,
306 #[clap()]
307 pub filter: Vec<ObjectType>,
308}
309
310#[derive(Debug, Args, Clone)]
311pub struct AccountCommonOpt {
312 #[clap()]
313 account_id: String,
314}
315
316#[derive(Debug, Args, Clone)]
317pub struct AccountNamedOpt {
318 #[clap(flatten)]
319 aopts: AccountCommonOpt,
320}
321
322#[derive(Debug, Args, Clone)]
323pub struct AccountNamedExpireDateTimeOpt {
324 #[clap(flatten)]
325 aopts: AccountCommonOpt,
326 #[clap(name = "datetime", verbatim_doc_comment)]
327 datetime: String,
333}
334
335#[derive(Debug, Args, Clone)]
336pub struct AccountNamedValidDateTimeOpt {
337 #[clap(flatten)]
338 aopts: AccountCommonOpt,
339 #[clap(name = "datetime")]
340 datetime: String,
343}
344
345#[derive(Debug, Args, Clone)]
346pub struct AccountNamedTagOpt {
347 #[clap(flatten)]
348 aopts: AccountCommonOpt,
349 #[clap(name = "tag")]
350 tag: String,
351}
352
353#[derive(Debug, Args, Clone)]
354pub struct AccountNamedTagPkOpt {
355 #[clap(flatten)]
356 aopts: AccountCommonOpt,
357 #[clap(name = "tag")]
358 tag: String,
359 #[clap(name = "pubkey")]
360 pubkey: String,
361}
362
363#[derive(Debug, Args, Clone)]
364pub struct UseResetTokenOpt {
366 #[clap(name = "token")]
367 token: String,
368}
369
370#[derive(Debug, Args, Clone)]
371pub struct AccountCreateOpt {
372 #[clap(flatten)]
373 aopts: AccountCommonOpt,
374 #[clap(name = "display-name")]
375 display_name: String,
376}
377
378#[derive(Debug, Subcommand, Clone)]
379pub enum AccountCredential {
380 #[clap(name = "status")]
382 Status(AccountNamedOpt),
383 #[clap(name = "update")]
385 Update(AccountNamedOpt),
386 #[clap(name = "use-reset-token")]
388 UseResetToken(UseResetTokenOpt),
389 #[clap(name = "create-reset-token")]
392 CreateResetToken {
393 #[clap(flatten)]
394 aopts: AccountCommonOpt,
395
396 ttl: Option<u32>,
399 },
400}
401
402#[derive(Debug, Subcommand, Clone)]
404pub enum AccountRadius {
405 #[clap(name = "show-secret")]
407 Show(AccountNamedOpt),
408 #[clap(name = "generate-secret")]
410 Generate(AccountNamedOpt),
411 #[clap(name = "delete-secret")]
412 DeleteSecret(AccountNamedOpt),
414}
415
416#[derive(Debug, Args, Clone)]
417pub struct AccountPosixOpt {
418 #[clap(flatten)]
419 aopts: AccountCommonOpt,
420 #[clap(long)]
421 gidnumber: Option<u32>,
422 #[clap(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
423 shell: Option<String>,
425}
426
427#[derive(Debug, Subcommand, Clone)]
428pub enum PersonPosix {
429 #[clap(name = "show")]
430 Show(AccountNamedOpt),
431 #[clap(name = "set")]
432 Set(AccountPosixOpt),
433 #[clap(name = "set-password")]
434 SetPassword(AccountNamedOpt),
435 #[clap(name = "reset-gidnumber")]
437 ResetGidnumber { account_id: String },
438}
439
440#[derive(Debug, Subcommand, Clone)]
441pub enum ServiceAccountPosix {
442 #[clap(name = "show")]
443 Show(AccountNamedOpt),
444 #[clap(name = "set")]
445 Set(AccountPosixOpt),
446 #[clap(name = "reset-gidnumber")]
448 ResetGidnumber { account_id: String },
449}
450
451#[derive(Debug, Args, Clone)]
452pub struct PersonUpdateOpt {
453 #[clap(flatten)]
454 aopts: AccountCommonOpt,
455 #[clap(long, short, help = "Set the legal name for the person.",
456 value_parser = clap::builder::NonEmptyStringValueParser::new())]
457 legalname: Option<String>,
458 #[clap(long, short, help = "Set the account name for the person.",
459 value_parser = clap::builder::NonEmptyStringValueParser::new())]
460 newname: Option<String>,
461 #[clap(long, short = 'i', help = "Set the display name for the person.",
462 value_parser = clap::builder::NonEmptyStringValueParser::new())]
463 displayname: Option<String>,
464 #[clap(
465 long,
466 short,
467 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
468 )]
469 mail: Option<Vec<String>>,
470}
471
472#[derive(Debug, Subcommand, Clone)]
473pub enum AccountSsh {
474 #[clap(name = "list-publickeys")]
475 List(AccountNamedOpt),
476 #[clap(name = "add-publickey")]
477 Add(AccountNamedTagPkOpt),
478 #[clap(name = "delete-publickey")]
479 Delete(AccountNamedTagOpt),
480}
481
482#[derive(Debug, Subcommand, Clone)]
483pub enum AccountValidity {
484 #[clap(name = "show")]
486 Show(AccountNamedOpt),
487 #[clap(name = "expire-at")]
489 ExpireAt(AccountNamedExpireDateTimeOpt),
490 #[clap(name = "begin-from")]
492 BeginFrom(AccountNamedValidDateTimeOpt),
493}
494
495#[derive(Debug, Subcommand, Clone)]
496pub enum AccountCertificate {
497 #[clap(name = "status")]
498 Status { account_id: String },
499 #[clap(name = "create")]
500 Create {
501 account_id: String,
502 certificate_path: PathBuf,
503 },
504}
505
506#[derive(Debug, Subcommand, Clone)]
507pub enum AccountUserAuthToken {
508 #[clap(name = "status")]
510 Status(AccountNamedOpt),
511 #[clap(name = "destroy")]
514 Destroy {
515 #[clap(flatten)]
516 aopts: AccountCommonOpt,
517
518 #[clap(name = "session-id")]
520 session_id: Uuid,
521 },
522}
523
524#[derive(Debug, Subcommand, Clone)]
525pub enum PersonOpt {
526 #[clap(name = "credential")]
528 Credential {
529 #[clap(subcommand)]
530 commands: AccountCredential,
531 },
532 #[clap(name = "radius")]
534 Radius {
535 #[clap(subcommand)]
536 commands: AccountRadius,
537 },
538 #[clap(name = "posix")]
540 Posix {
541 #[clap(subcommand)]
542 commands: PersonPosix,
543 },
544 #[clap(name = "session")]
546 Session {
547 #[clap(subcommand)]
548 commands: AccountUserAuthToken,
549 },
550 #[clap(name = "ssh")]
552 Ssh {
553 #[clap(subcommand)]
554 commands: AccountSsh,
555 },
556 #[clap(name = "list")]
558 List,
559 #[clap(name = "get")]
561 Get(AccountNamedOpt),
562 #[clap(name = "search")]
564 Search { account_id: String },
565 #[clap(name = "update")]
567 Update(PersonUpdateOpt),
568 #[clap(name = "create")]
570 Create(AccountCreateOpt),
571 #[clap(name = "delete")]
573 Delete(AccountNamedOpt),
574 #[clap(name = "validity")]
576 Validity {
577 #[clap(subcommand)]
578 commands: AccountValidity,
579 },
580 #[clap(name = "certificate", hide = true)]
581 Certificate {
582 #[clap(subcommand)]
583 commands: AccountCertificate,
584 },
585}
586
587#[derive(Debug, Subcommand, Clone)]
588pub enum ServiceAccountCredential {
589 #[clap(name = "status")]
591 Status(AccountNamedOpt),
592 #[clap(name = "generate")]
595 GeneratePw(AccountNamedOpt),
596}
597
598#[derive(Debug, Subcommand, Clone)]
599pub enum ServiceAccountApiToken {
600 #[clap(name = "status")]
602 Status(AccountNamedOpt),
603 #[clap(name = "generate")]
605 Generate {
606 #[clap(flatten)]
607 aopts: AccountCommonOpt,
608
609 #[clap(name = "label")]
612 label: String,
613 #[clap(name = "expiry")]
614 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
617 expiry: Option<String>,
618 #[clap(long = "rw")]
619 read_write: bool,
620 },
621 #[clap(name = "destroy")]
624 Destroy {
625 #[clap(flatten)]
626 aopts: AccountCommonOpt,
627
628 #[clap(name = "token-id")]
630 token_id: Uuid,
631 },
632}
633
634#[derive(Debug, Args, Clone)]
635pub struct ServiceAccountUpdateOpt {
636 #[clap(flatten)]
637 aopts: AccountCommonOpt,
638 #[clap(long, short, help = "Set the account name for the service account.",
639 value_parser = clap::builder::NonEmptyStringValueParser::new())]
640 newname: Option<String>,
641 #[clap(
642 long,
643 short = 'i',
644 help = "Set the display name for the service account.",
645 value_parser = clap::builder::NonEmptyStringValueParser::new()
646 )]
647 displayname: Option<String>,
648 #[clap(
649 long,
650 short = 'e',
651 help = "Set the entry manager for the service account.",
652 value_parser = clap::builder::NonEmptyStringValueParser::new()
653 )]
654 entry_managed_by: Option<String>,
655 #[clap(
656 long,
657 short,
658 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
659 )]
660 mail: Option<Vec<String>>,
661}
662
663#[derive(Debug, Subcommand, Clone)]
664pub enum ServiceAccountOpt {
665 #[clap(name = "credential")]
667 Credential {
668 #[clap(subcommand)]
669 commands: ServiceAccountCredential,
670 },
671 #[clap(name = "api-token")]
673 ApiToken {
674 #[clap(subcommand)]
675 commands: ServiceAccountApiToken,
676 },
677 #[clap(name = "posix")]
679 Posix {
680 #[clap(subcommand)]
681 commands: ServiceAccountPosix,
682 },
683 #[clap(name = "session")]
685 Session {
686 #[clap(subcommand)]
687 commands: AccountUserAuthToken,
688 },
689 #[clap(name = "ssh")]
691 Ssh {
692 #[clap(subcommand)]
693 commands: AccountSsh,
694 },
695 #[clap(name = "list")]
697 List,
698 #[clap(name = "get")]
700 Get(AccountNamedOpt),
701 #[clap(name = "create")]
703 Create {
704 #[clap(flatten)]
705 aopts: AccountCommonOpt,
706 #[clap(name = "display-name")]
707 display_name: String,
708 #[clap(name = "entry-managed-by")]
709 entry_managed_by: String,
710 },
711 #[clap(name = "update")]
713 Update(ServiceAccountUpdateOpt),
714 #[clap(name = "delete")]
716 Delete(AccountNamedOpt),
717 #[clap(name = "validity")]
719 Validity {
720 #[clap(subcommand)]
721 commands: AccountValidity,
722 },
723 #[clap(name = "into-person")]
727 IntoPerson(AccountNamedOpt),
728}
729
730#[derive(Debug, Subcommand, Clone)]
731pub enum RecycleOpt {
732 #[clap(name = "list")]
733 List,
735 #[clap(name = "get")]
736 Get(Named),
738 #[clap(name = "revive")]
739 Revive(Named),
741}
742
743#[derive(Debug, Args, Clone)]
744pub struct LoginOpt {}
745
746#[derive(Debug, Args, Clone)]
747pub struct LogoutOpt {
748 #[clap(short, long)]
749 local_only: bool,
751}
752
753#[derive(Debug, Subcommand, Clone)]
754pub enum SessionOpt {
755 #[clap(name = "list")]
756 List,
758 #[clap(name = "cleanup")]
759 Cleanup,
761}
762
763#[derive(Debug, Args, Clone)]
764pub struct FilterOpt {
765 #[clap()]
766 filter: String,
767}
768
769#[derive(Debug, Args, Clone)]
770pub struct CreateOpt {
771 #[clap(value_parser)]
772 file: PathBuf,
773}
774
775#[derive(Debug, Args, Clone)]
776pub struct ModifyOpt {
777 #[clap()]
778 filter: String,
779 #[clap(value_parser)]
780 file: PathBuf,
781}
782
783#[derive(Debug, Subcommand, Clone)]
784pub enum RawOpt {
785 #[clap(name = "search")]
786 Search(FilterOpt),
787 #[clap(name = "create")]
788 Create(CreateOpt),
789 #[clap(name = "modify")]
790 Modify(ModifyOpt),
791 #[clap(name = "delete")]
792 Delete(FilterOpt),
793}
794
795#[derive(Debug, Subcommand, Clone)]
796pub enum SelfOpt {
797 #[clap(name = "identify-user")]
799 IdentifyUser,
800 Whoami,
802}
803
804#[derive(Debug, Args, Clone)]
805pub struct Oauth2SetDisplayname {
806 #[clap(flatten)]
807 nopt: Named,
808 #[clap(name = "displayname")]
809 displayname: String,
810}
811
812#[derive(Debug, Args, Clone)]
813pub struct Oauth2SetImplicitScopes {
814 #[clap(flatten)]
815 nopt: Named,
816 #[clap(name = "scopes")]
817 scopes: Vec<String>,
818}
819
820#[derive(Debug, Args, Clone)]
821pub struct Oauth2CreateScopeMapOpt {
822 #[clap(flatten)]
823 nopt: Named,
824 #[clap(name = "group")]
825 group: String,
826 #[clap(name = "scopes", required = true, num_args=1.. )]
827 scopes: Vec<String>,
828}
829
830#[derive(Debug, Args, Clone)]
831pub struct Oauth2DeleteScopeMapOpt {
832 #[clap(flatten)]
833 nopt: Named,
834 #[clap(name = "group")]
835 group: String,
836}
837
838#[derive(Debug, Clone, Copy, Eq, PartialEq)]
839pub enum Oauth2ClaimMapJoin {
840 Csv,
841 Ssv,
842 Array,
843}
844
845impl Oauth2ClaimMapJoin {
846 pub fn as_str(&self) -> &'static str {
847 match self {
848 Self::Csv => "csv",
849 Self::Ssv => "ssv",
850 Self::Array => "array",
851 }
852 }
853}
854
855impl ValueEnum for Oauth2ClaimMapJoin {
856 fn value_variants<'a>() -> &'a [Self] {
857 &[Self::Csv, Self::Ssv, Self::Array]
858 }
859
860 fn to_possible_value(&self) -> Option<PossibleValue> {
861 Some(self.as_str().into())
862 }
863}
864
865#[derive(Debug, Subcommand, Clone)]
866pub enum Oauth2Opt {
867 #[clap(name = "list")]
868 List,
870 #[clap(name = "get")]
871 Get(Named),
873 #[clap(name = "create")]
877 CreateBasic {
879 #[clap(name = "name")]
880 name: String,
881 #[clap(name = "displayname")]
882 displayname: String,
883 #[clap(name = "origin")]
884 origin: String,
885 },
886 #[clap(name = "create-public")]
887 CreatePublic {
893 #[clap(name = "name")]
894 name: String,
895 #[clap(name = "displayname")]
896 displayname: String,
897 #[clap(name = "origin")]
898 origin: String,
899 },
900 #[clap(name = "update-scope-map", visible_aliases=&["create-scope-map"])]
901 UpdateScopeMap(Oauth2CreateScopeMapOpt),
903 #[clap(name = "delete-scope-map")]
904 DeleteScopeMap(Oauth2DeleteScopeMapOpt),
906
907 #[clap(name = "update-sup-scope-map", visible_aliases=&["create-sup-scope-map"])]
908 UpdateSupScopeMap(Oauth2CreateScopeMapOpt),
910 #[clap(name = "delete-sup-scope-map")]
911 DeleteSupScopeMap(Oauth2DeleteScopeMapOpt),
913
914 #[clap(name = "update-claim-map", visible_aliases=&["create-claim-map"])]
915 UpdateClaimMap {
917 name: String,
918 claim_name: String,
919 group: String,
920 values: Vec<String>,
921 },
922 #[clap(name = "update-claim-map-join")]
923 UpdateClaimMapJoin {
924 name: String,
925 claim_name: String,
926 join: Oauth2ClaimMapJoin,
929 },
930 #[clap(name = "delete-claim-map")]
931 DeleteClaimMap {
933 name: String,
934 claim_name: String,
935 group: String,
936 },
937
938 #[clap(name = "reset-basic-secret")]
939 ResetSecrets(Named),
942 #[clap(name = "show-basic-secret")]
943 ShowBasicSecret(Named),
945 #[clap(name = "delete")]
946 Delete(Named),
948 #[clap(name = "set-displayname")]
950 SetDisplayname(Oauth2SetDisplayname),
951 #[clap(name = "set-name")]
955 SetName {
956 #[clap(flatten)]
957 nopt: Named,
958 #[clap(name = "newname")]
959 name: String,
960 },
961
962 #[clap(name = "set-landing-url")]
965 SetLandingUrl {
966 #[clap(flatten)]
967 nopt: Named,
968 #[clap(name = "landing-url")]
969 url: Url,
970 },
971 #[clap(name = "set-image")]
973 SetImage {
974 #[clap(flatten)]
975 nopt: Named,
976 #[clap(name = "file-path")]
977 path: PathBuf,
979 #[clap(name = "image-type")]
980 image_type: Option<ImageType>,
982 },
983 #[clap(name = "remove-image")]
985 RemoveImage(Named),
986
987 #[clap(name = "add-redirect-url")]
991 AddOrigin {
992 name: String,
993 #[clap(name = "url")]
994 origin: Url,
995 },
996
997 #[clap(name = "remove-redirect-url")]
999 RemoveOrigin {
1000 name: String,
1001 #[clap(name = "url")]
1002 origin: Url,
1003 },
1004 #[clap(name = "enable-pkce")]
1005 EnablePkce(Named),
1007 #[clap(name = "warning-insecure-client-disable-pkce")]
1010 DisablePkce(Named),
1011 #[clap(name = "warning-enable-legacy-crypto")]
1012 EnableLegacyCrypto(Named),
1016 #[clap(name = "disable-legacy-crypto")]
1018 DisableLegacyCrypto(Named),
1019 #[clap(name = "enable-strict-redirect-url")]
1023 EnableStrictRedirectUri { name: String },
1024 #[clap(name = "disable-strict-redirect-url")]
1025 DisableStrictRedirectUri { name: String },
1026 #[clap(name = "enable-localhost-redirects")]
1027 EnablePublicLocalhost { name: String },
1029 #[clap(name = "disable-localhost-redirects")]
1031 DisablePublicLocalhost { name: String },
1032 #[clap(name = "prefer-short-username")]
1034 PreferShortUsername(Named),
1035 #[clap(name = "prefer-spn-username")]
1037 PreferSPNUsername(Named),
1038 #[cfg(feature = "dev-oauth2-device-flow")]
1039 DeviceFlowEnable(Named),
1041 #[cfg(feature = "dev-oauth2-device-flow")]
1042 DeviceFlowDisable(Named),
1044 #[clap(name = "rotate-cryptographic-keys")]
1050 RotateCryptographicKeys {
1051 name: String,
1052 #[clap(value_parser = parse_rfc3339)]
1053 rotate_at: OffsetDateTime,
1054 },
1055 #[clap(name = "revoke-cryptographic-key")]
1059 RevokeCryptographicKey { name: String, key_id: String },
1060}
1061
1062#[derive(Args, Debug, Clone)]
1063pub struct OptSetDomainDisplayname {
1064 #[clap(name = "new-display-name")]
1065 new_display_name: String,
1066}
1067
1068#[derive(Debug, Subcommand, Clone)]
1069pub enum PwBadlistOpt {
1070 #[clap[name = "show"]]
1071 Show,
1073 #[clap[name = "upload"]]
1074 Upload {
1078 #[clap(value_parser, required = true, num_args(1..))]
1079 paths: Vec<PathBuf>,
1080 #[clap(short = 'n', long)]
1082 dryrun: bool,
1083 },
1084 #[clap[name = "remove", hide = true]]
1085 Remove {
1088 #[clap(value_parser, required = true, num_args(1..))]
1089 paths: Vec<PathBuf>,
1090 },
1091}
1092
1093#[derive(Debug, Subcommand, Clone)]
1094pub enum DeniedNamesOpt {
1095 #[clap[name = "show"]]
1096 Show,
1098 #[clap[name = "append"]]
1099 Append {
1100 #[clap(value_parser, required = true, num_args(1..))]
1101 names: Vec<String>,
1102 },
1103 #[clap[name = "remove"]]
1104 Remove {
1106 #[clap(value_parser, required = true, num_args(1..))]
1107 names: Vec<String>,
1108 },
1109}
1110
1111#[derive(Debug, Subcommand, Clone)]
1112pub enum DomainOpt {
1113 #[clap[name = "set-displayname"]]
1114 SetDisplayname(OptSetDomainDisplayname),
1116 #[clap[name = "set-ldap-queryable-attrs"]]
1118 SetLdapMaxQueryableAttrs {
1119 #[clap(name = "maximum-queryable-attrs")]
1120 new_max_queryable_attrs: usize,
1121 },
1122 #[clap[name = "set-ldap-basedn"]]
1123 SetLdapBasedn {
1128 #[clap(name = "new-basedn")]
1129 new_basedn: String,
1130 },
1131 SetLdapAllowUnixPasswordBind {
1134 #[clap(name = "allow", action = clap::ArgAction::Set)]
1135 enable: bool,
1136 },
1137 SetAllowEasterEggs {
1141 #[clap(name = "allow", action = clap::ArgAction::Set)]
1142 enable: bool,
1143 },
1144 #[clap(name = "show")]
1145 Show,
1147 #[clap(name = "revoke-key")]
1148 RevokeKey { key_id: String },
1151 #[clap(name = "set-image")]
1153 SetImage {
1154 #[clap(name = "file-path")]
1155 path: PathBuf,
1156 #[clap(name = "image-type")]
1157 image_type: Option<ImageType>,
1158 },
1159 #[clap(name = "remove-image")]
1161 RemoveImage,
1162}
1163
1164#[derive(Debug, Subcommand, Clone)]
1165pub enum SynchOpt {
1166 #[clap(name = "list")]
1167 List,
1169 #[clap(name = "get")]
1170 Get(Named),
1172 #[clap(name = "set-credential-portal")]
1173 SetCredentialPortal {
1176 #[clap()]
1177 account_id: String,
1178
1179 #[clap(name = "url")]
1180 url: Option<Url>,
1181 },
1182 #[clap(name = "create")]
1184 Create {
1185 #[clap()]
1186 account_id: String,
1187
1188 #[clap(name = "description",
1189 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1190 description: Option<String>,
1191 },
1192 #[clap(name = "generate-token")]
1194 GenerateToken {
1195 #[clap()]
1196 account_id: String,
1197 #[clap()]
1198 label: String,
1199 },
1200 #[clap(name = "destroy-token")]
1202 DestroyToken {
1203 #[clap()]
1204 account_id: String,
1205 },
1206 #[clap(name = "set-yield-attributes")]
1210 SetYieldAttributes {
1211 #[clap()]
1212 account_id: String,
1213
1214 #[clap(name = "attributes")]
1215 attrs: Vec<String>,
1216 },
1217 #[clap(name = "force-refresh")]
1221 ForceRefresh {
1222 #[clap()]
1223 account_id: String,
1224 },
1225 #[clap(name = "finalise")]
1231 Finalise {
1232 #[clap()]
1233 account_id: String,
1234 },
1235 #[clap(name = "terminate")]
1241 Terminate {
1242 #[clap()]
1243 account_id: String,
1244 },
1245}
1246
1247#[derive(Debug, Subcommand, Clone)]
1248pub enum AuthSessionExpiryOpt {
1249 #[clap[name = "get"]]
1250 Get,
1252 #[clap[name = "set"]]
1253 Set {
1255 #[clap(name = "expiry")]
1256 expiry: u32,
1257 },
1258}
1259
1260#[derive(Debug, Subcommand, Clone)]
1261pub enum PrivilegedSessionExpiryOpt {
1262 #[clap[name = "get"]]
1263 Get,
1265 #[clap[name = "set"]]
1266 Set {
1268 #[clap(name = "expiry")]
1269 expiry: u32,
1270 },
1271}
1272
1273#[derive(Args, Debug, Clone)]
1274pub struct ApiSchemaDownloadOpt {
1275 #[clap(name = "filename", env, default_value = "./kanidm-openapi.json")]
1277 filename: PathBuf,
1278 #[clap(short, long, env)]
1280 force: bool,
1281}
1282
1283#[derive(Debug, Subcommand, Clone)]
1284pub enum ApiOpt {
1285 #[clap(name = "download-schema")]
1287 DownloadSchema(ApiSchemaDownloadOpt),
1288}
1289
1290#[derive(Debug, Subcommand, Clone)]
1291pub enum SchemaClassOpt {
1292 List,
1294 Search {
1295 query: String,
1296 },
1297}
1298
1299#[derive(Debug, Subcommand, Clone)]
1300pub enum SchemaAttrOpt {
1301 List,
1303 Search {
1304 query: String,
1305 },
1306}
1307
1308#[derive(Debug, Subcommand, Clone)]
1309pub enum SchemaOpt {
1310 #[clap(name = "class")]
1312 Class {
1313 #[clap(subcommand)]
1314 commands: SchemaClassOpt,
1315 },
1316 #[clap(name = "attribute", visible_alias = "attr")]
1318 Attribute {
1319 #[clap(subcommand)]
1320 commands: SchemaAttrOpt,
1321 },
1322}
1323
1324#[derive(Debug, Subcommand, Clone)]
1325pub enum SystemOpt {
1326 #[clap(name = "pw-badlist")]
1327 PwBadlist {
1329 #[clap(subcommand)]
1330 commands: PwBadlistOpt,
1331 },
1332 #[clap(name = "denied-names")]
1333 DeniedNames {
1335 #[clap(subcommand)]
1336 commands: DeniedNamesOpt,
1337 },
1338 #[clap(name = "oauth2")]
1339 Oauth2 {
1341 #[clap(subcommand)]
1342 commands: Oauth2Opt,
1343 },
1344 #[clap(name = "domain")]
1345 Domain {
1347 #[clap(subcommand)]
1348 commands: DomainOpt,
1349 },
1350 #[clap(name = "sync")]
1351 Synch {
1353 #[clap(subcommand)]
1354 commands: SynchOpt,
1355 },
1356 #[clap(name = "api")]
1357 Api {
1359 #[clap(subcommand)]
1360 commands: ApiOpt,
1361 },
1362}
1363
1364#[derive(Debug, Subcommand, Clone)]
1365#[clap(about = "Kanidm Client Utility")]
1366pub enum KanidmClientOpt {
1367 Login(LoginOpt),
1369 Reauth {
1371 #[clap()]
1372 mode: kanidm_proto::cli::OpType,
1373 },
1374 Logout(LogoutOpt),
1376 Session {
1378 #[clap(subcommand)]
1379 commands: SessionOpt,
1380 },
1381 #[clap(name = "self")]
1382 CSelf {
1384 #[clap(subcommand)]
1385 commands: SelfOpt,
1386 },
1387 Person {
1389 #[clap(subcommand)]
1390 commands: PersonOpt,
1391 },
1392 Group {
1394 #[clap(subcommand)]
1395 commands: GroupOpt,
1396 },
1397 #[clap(name = "service-account")]
1399 ServiceAccount {
1400 #[clap(subcommand)]
1401 commands: ServiceAccountOpt,
1402 },
1403 #[clap(name = "graph")]
1405 Graph(GraphCommonOpt),
1406
1407 #[clap(hide = true)]
1409 Schema {
1410 #[clap(subcommand)]
1411 commands: SchemaOpt,
1412 },
1413
1414 System {
1416 #[clap(subcommand)]
1417 commands: SystemOpt,
1418 },
1419 #[clap(name = "recycle-bin")]
1420 Recycle {
1422 #[clap(subcommand)]
1423 commands: RecycleOpt,
1424 },
1425 #[clap(hide = true)]
1427 Raw {
1428 #[clap(subcommand)]
1429 commands: RawOpt,
1430 },
1431 Version,
1433}
1434
1435#[derive(Debug, clap::Parser, Clone)]
1436#[clap(about = "Kanidm Client Utility")]
1437pub struct KanidmClientParser {
1438 #[clap(subcommand)]
1439 pub commands: KanidmClientOpt,
1440
1441 #[clap(short, long, env = "KANIDM_DEBUG", global = true)]
1443 pub debug: bool,
1444 #[clap(short = 'I', long = "instance", env = "KANIDM_INSTANCE", global = true,
1446 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1447 pub instance: Option<String>,
1448 #[clap(short = 'H', long = "url", env = "KANIDM_URL", global = true,
1450 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1451 pub addr: Option<String>,
1452 #[clap(
1454 short = 'D',
1455 long = "name",
1456 env = "KANIDM_NAME",
1457 value_parser = clap::builder::NonEmptyStringValueParser::new(), global=true
1458 )]
1459 pub username: Option<String>,
1460 #[clap(
1462 value_parser,
1463 short = 'C',
1464 long = "ca",
1465 env = "KANIDM_CA_PATH",
1466 global = true
1467 )]
1468 pub ca_path: Option<PathBuf>,
1469 #[clap(short, long = "output", env = "KANIDM_OUTPUT", global = true, default_value=OutputMode::default())]
1471 output_mode: OutputMode,
1472 #[clap(
1474 long = "skip-hostname-verification",
1475 env = "KANIDM_SKIP_HOSTNAME_VERIFICATION",
1476 default_value_t = false,
1477 global = true
1478 )]
1479 skip_hostname_verification: bool,
1480 #[clap(
1482 long = "accept-invalid-certs",
1483 env = "KANIDM_ACCEPT_INVALID_CERTS",
1484 default_value_t = false,
1485 global = true
1486 )]
1487 accept_invalid_certs: bool,
1488 #[clap(
1490 short,
1491 long,
1492 env = "KANIDM_TOKEN_CACHE_PATH",
1493 hide = true,
1494 default_value = None,
1495 global=true,
1496 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1497 token_cache_path: Option<String>,
1498
1499 #[clap(
1500 short,
1501 long,
1502 env = "KANIDM_PASSWORD",
1503 hide = true,
1504 global = true,
1505 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1506 password: Option<String>,
1508}
1509
1510impl KanidmClientParser {
1511 fn get_token_cache_path(&self) -> String {
1512 match self.token_cache_path.clone() {
1513 None => CLIENT_TOKEN_CACHE.to_string(),
1514 Some(val) => val.clone(),
1515 }
1516 }
1517}