1use clap::{builder::PossibleValue, Args, Subcommand, ValueEnum};
2use kanidm_proto::internal::ImageType;
3use std::fmt;
4use time::format_description::well_known::Rfc3339;
5use time::OffsetDateTime;
6
7fn parse_rfc3339(input: &str) -> Result<OffsetDateTime, time::error::Parse > {
8 if input == "now" {
9 Ok(OffsetDateTime::now_utc())
10 } else {
11 OffsetDateTime::parse(input, &Rfc3339)
12 }
13}
14
15#[derive(Debug, Args)]
16pub struct Named {
17 pub name: String,
18 #[clap(flatten)]
19 pub copt: CommonOpt,
20}
21
22#[derive(Debug, Args)]
23pub struct DebugOpt {
24 #[clap(short, long, env = "KANIDM_DEBUG")]
26 pub debug: bool,
27}
28
29#[derive(Debug, Clone, Copy)]
30pub enum OutputMode {
32 Text,
33 Json,
34}
35
36impl std::str::FromStr for OutputMode {
37 type Err = String;
38 fn from_str(s: &str) -> Result<OutputMode, std::string::String> {
39 match s.to_lowercase().as_str() {
40 "text" => Ok(OutputMode::Text),
41 "json" => Ok(OutputMode::Json),
42 _ => Ok(OutputMode::Text),
43 }
44 }
45}
46
47impl OutputMode {
48 pub fn print_message<T>(self, input: T)
49 where
50 T: serde::Serialize + fmt::Debug + fmt::Display,
51 {
52 match self {
53 OutputMode::Json => {
54 println!(
55 "{}",
56 serde_json::to_string(&input).unwrap_or(format!("{:?}", input))
57 );
58 }
59 OutputMode::Text => {
60 println!("{}", input);
61 }
62 }
63 }
64}
65
66#[derive(Debug, Args, Clone)]
67pub struct CommonOpt {
68 #[clap(short, long, env = "KANIDM_DEBUG")]
70 pub debug: bool,
71 #[clap(short = 'I', long = "instance", env = "KANIDM_INSTANCE",
73 value_parser = clap::builder::NonEmptyStringValueParser::new())]
74 pub instance: Option<String>,
75 #[clap(short = 'H', long = "url", env = "KANIDM_URL",
77 value_parser = clap::builder::NonEmptyStringValueParser::new())]
78 pub addr: Option<String>,
79 #[clap(
81 short = 'D',
82 long = "name",
83 env = "KANIDM_NAME",
84 value_parser = clap::builder::NonEmptyStringValueParser::new()
85 )]
86 pub username: Option<String>,
87 #[clap(value_parser, short = 'C', long = "ca", env = "KANIDM_CA_PATH")]
89 pub ca_path: Option<PathBuf>,
90 #[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value = "text")]
92 output_mode: OutputMode,
93 #[clap(
95 long = "skip-hostname-verification",
96 env = "KANIDM_SKIP_HOSTNAME_VERIFICATION",
97 default_value_t = false
98 )]
99 skip_hostname_verification: bool,
100 #[clap(
102 long = "accept-invalid-certs",
103 env = "KANIDM_ACCEPT_INVALID_CERTS",
104 default_value_t = false
105 )]
106 accept_invalid_certs: bool,
107 #[clap(short, long, env = "KANIDM_TOKEN_CACHE_PATH", hide = true, default_value = None,
109 value_parser = clap::builder::NonEmptyStringValueParser::new())]
110 token_cache_path: Option<String>,
111}
112
113#[derive(Debug, Args)]
114pub struct GroupNamedMembers {
115 name: String,
116 #[clap(required = true, num_args(1..))]
117 members: Vec<String>,
118 #[clap(flatten)]
119 copt: CommonOpt,
120}
121
122#[derive(Debug, Args)]
123pub struct GroupPosixOpt {
124 name: String,
125 #[clap(long)]
126 gidnumber: Option<u32>,
127 #[clap(flatten)]
128 copt: CommonOpt,
129}
130
131#[derive(Debug, Subcommand)]
132pub enum GroupPosix {
133 #[clap(name = "show")]
135 Show(Named),
136 #[clap(name = "set")]
138 Set(GroupPosixOpt),
139 #[clap(name = "reset-gidnumber")]
141 ResetGidnumber {
142 group_id: String,
143 #[clap(flatten)]
144 copt: CommonOpt,
145 },
146}
147
148#[derive(Debug, Clone, Copy, Eq, PartialEq)]
149pub enum AccountPolicyCredentialType {
150 Any,
151 Mfa,
152 Passkey,
153 AttestedPasskey,
154}
155
156impl AccountPolicyCredentialType {
157 pub fn as_str(&self) -> &'static str {
158 match self {
159 Self::Any => "any",
160 Self::Mfa => "mfa",
161 Self::Passkey => "passkey",
162 Self::AttestedPasskey => "attested_passkey",
163 }
164 }
165}
166
167impl ValueEnum for AccountPolicyCredentialType {
168 fn value_variants<'a>() -> &'a [Self] {
169 &[Self::Any, Self::Mfa, Self::Passkey, Self::AttestedPasskey]
170 }
171
172 fn to_possible_value(&self) -> Option<PossibleValue> {
173 Some(match self {
174 Self::Any => PossibleValue::new("any"),
175 Self::Mfa => PossibleValue::new("mfa"),
176 Self::Passkey => PossibleValue::new("passkey"),
177 Self::AttestedPasskey => PossibleValue::new("attested_passkey"),
178 })
179 }
180}
181
182#[derive(Debug, Subcommand)]
183pub enum GroupAccountPolicyOpt {
184 #[clap(name = "enable")]
186 Enable {
187 name: String,
188 #[clap(flatten)]
189 copt: CommonOpt,
190 },
191 #[clap(name = "auth-expiry")]
193 AuthSessionExpiry {
194 name: String,
195 expiry: u32,
196 #[clap(flatten)]
197 copt: CommonOpt,
198 },
199 #[clap(name = "credential-type-minimum")]
202 CredentialTypeMinimum {
203 name: String,
204 #[clap(value_enum)]
205 value: AccountPolicyCredentialType,
206 #[clap(flatten)]
207 copt: CommonOpt,
208 },
209 #[clap(name = "password-minimum-length")]
211 PasswordMinimumLength {
212 name: String,
213 length: u32,
214 #[clap(flatten)]
215 copt: CommonOpt,
216 },
217
218 #[clap(name = "privilege-expiry")]
220 PrivilegedSessionExpiry {
221 name: String,
222 expiry: u32,
223 #[clap(flatten)]
224 copt: CommonOpt,
225 },
226
227 #[clap(name = "webauthn-attestation-ca-list")]
232 WebauthnAttestationCaList {
233 name: String,
234 attestation_ca_list_json_file: PathBuf,
235 #[clap(flatten)]
236 copt: CommonOpt,
237 },
238
239 #[clap(name = "limit-search-max-results")]
242 LimitSearchMaxResults {
243 name: String,
244 maximum: u32,
245 #[clap(flatten)]
246 copt: CommonOpt,
247 },
248 #[clap(name = "limit-search-max-filter-test")]
252 LimitSearchMaxFilterTest {
253 name: String,
254 maximum: u32,
255 #[clap(flatten)]
256 copt: CommonOpt,
257 },
258 #[clap(name = "allow-primary-cred-fallback")]
261 AllowPrimaryCredFallback {
262 name: String,
263 #[clap(name = "allow", action = clap::ArgAction::Set)]
264 allow: bool,
265 #[clap(flatten)]
266 copt: CommonOpt,
267 },
268
269 #[clap(name = "reset-auth-expiry")]
271 ResetAuthSessionExpiry {
272 name: String,
273 #[clap(flatten)]
274 copt: CommonOpt,
275 },
276 #[clap(name = "reset-password-minimum-length")]
278 ResetPasswordMinimumLength {
279 name: String,
280 #[clap(flatten)]
281 copt: CommonOpt,
282 },
283 #[clap(name = "reset-privilege-expiry")]
285 ResetPrivilegedSessionExpiry {
286 name: String,
287 #[clap(flatten)]
288 copt: CommonOpt,
289 },
290 #[clap(name = "reset-webauthn-attestation-ca-list")]
293 ResetWebauthnAttestationCaList {
294 name: String,
295 #[clap(flatten)]
296 copt: CommonOpt,
297 },
298 #[clap(name = "reset-limit-search-max-results")]
300 ResetLimitSearchMaxResults {
301 name: String,
302 #[clap(flatten)]
303 copt: CommonOpt,
304 },
305 #[clap(name = "reset-limit-search-max-filter-test")]
307 ResetLimitSearchMaxFilterTest {
308 name: String,
309 #[clap(flatten)]
310 copt: CommonOpt,
311 },
312}
313
314#[derive(Debug, Subcommand)]
315pub enum GroupOpt {
316 #[clap(name = "list")]
318 List(CommonOpt),
319 #[clap(name = "get")]
321 Get(Named),
322 #[clap(name = "search")]
324 Search {
325 name: String,
327 #[clap(flatten)]
328 copt: CommonOpt,
329 },
330 #[clap(name = "create")]
332 Create {
333 name: String,
335 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
337 entry_managed_by: Option<String>,
338 #[clap(flatten)]
339 copt: CommonOpt,
340 },
341 #[clap(name = "delete")]
343 Delete(Named),
344 #[clap(name = "list-members")]
346 ListMembers(Named),
347 #[clap(name = "set-members")]
350 SetMembers(GroupNamedMembers),
351 #[clap(name = "set-mail")]
355 SetMail {
356 #[clap(flatten)]
357 copt: CommonOpt,
358 name: String,
359 mail: Vec<String>,
360 },
361 #[clap(name = "set-description")]
363 SetDescription {
364 #[clap(flatten)]
365 copt: CommonOpt,
366 name: String,
367 description: Option<String>,
368 },
369 #[clap(name = "set-entry-manager")]
371 SetEntryManagedBy {
372 name: String,
374 entry_managed_by: String,
376 #[clap(flatten)]
377 copt: CommonOpt,
378 },
379 #[clap(name = "rename")]
381 Rename {
382 name: String,
384 new_name: String,
386 #[clap(flatten)]
387 copt: CommonOpt,
388 },
389 #[clap(name = "purge-members")]
391 PurgeMembers(Named),
392 #[clap(name = "add-members")]
394 AddMembers(GroupNamedMembers),
395 #[clap(name = "remove-members")]
397 RemoveMembers(GroupNamedMembers),
398 #[clap(name = "posix")]
400 Posix {
401 #[clap(subcommand)]
402 commands: GroupPosix,
403 },
404 #[clap(name = "account-policy")]
406 AccountPolicy {
407 #[clap(subcommand)]
408 commands: GroupAccountPolicyOpt,
409 },
410}
411
412#[derive(Clone, Debug, ValueEnum)]
413pub enum GraphType {
414 Graphviz,
415 Mermaid,
416 MermaidElk,
417}
418
419#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, ValueEnum)]
420pub enum ObjectType {
421 Group,
422 BuiltinGroup,
423 ServiceAccount,
424 Person,
425}
426
427#[derive(Debug, Args)]
428pub struct GraphCommonOpt {
429 #[arg(value_enum)]
430 pub graph_type: GraphType,
431 #[clap()]
432 pub filter: Vec<ObjectType>,
433 #[clap(flatten)]
434 pub copt: CommonOpt,
435}
436
437#[derive(Debug, Args)]
438pub struct AccountCommonOpt {
439 #[clap()]
440 account_id: String,
441}
442
443#[derive(Debug, Args)]
444pub struct AccountNamedOpt {
445 #[clap(flatten)]
446 aopts: AccountCommonOpt,
447 #[clap(flatten)]
448 copt: CommonOpt,
449}
450
451#[derive(Debug, Args)]
452pub struct AccountNamedExpireDateTimeOpt {
453 #[clap(flatten)]
454 aopts: AccountCommonOpt,
455 #[clap(flatten)]
456 copt: CommonOpt,
457 #[clap(name = "datetime", verbatim_doc_comment)]
458 datetime: String,
464}
465
466#[derive(Debug, Args)]
467pub struct AccountNamedValidDateTimeOpt {
468 #[clap(flatten)]
469 aopts: AccountCommonOpt,
470 #[clap(flatten)]
471 copt: CommonOpt,
472 #[clap(name = "datetime")]
473 datetime: String,
476}
477
478#[derive(Debug, Args)]
479pub struct AccountNamedTagOpt {
480 #[clap(flatten)]
481 aopts: AccountCommonOpt,
482 #[clap(flatten)]
483 copt: CommonOpt,
484 #[clap(name = "tag")]
485 tag: String,
486}
487
488#[derive(Debug, Args)]
489pub struct AccountNamedTagPkOpt {
490 #[clap(flatten)]
491 aopts: AccountCommonOpt,
492 #[clap(flatten)]
493 copt: CommonOpt,
494 #[clap(name = "tag")]
495 tag: String,
496 #[clap(name = "pubkey")]
497 pubkey: String,
498}
499
500#[derive(Debug, Args)]
501pub struct UseResetTokenOpt {
503 #[clap(flatten)]
504 copt: CommonOpt,
505 #[clap(name = "token")]
506 token: String,
507}
508
509#[derive(Debug, Args)]
510pub struct AccountCreateOpt {
511 #[clap(flatten)]
512 aopts: AccountCommonOpt,
513 #[clap(name = "display-name")]
514 display_name: String,
515 #[clap(flatten)]
516 copt: CommonOpt,
517}
518
519#[derive(Debug, Subcommand)]
520pub enum AccountCredential {
521 #[clap(name = "status")]
523 Status(AccountNamedOpt),
524 #[clap(name = "update")]
526 Update(AccountNamedOpt),
527 #[clap(name = "use-reset-token")]
529 UseResetToken(UseResetTokenOpt),
530 #[clap(name = "create-reset-token")]
533 CreateResetToken {
534 #[clap(flatten)]
535 aopts: AccountCommonOpt,
536 #[clap(flatten)]
537 copt: CommonOpt,
538 ttl: Option<u32>,
541 },
542}
543
544#[derive(Debug, Subcommand)]
546pub enum AccountRadius {
547 #[clap(name = "show-secret")]
549 Show(AccountNamedOpt),
550 #[clap(name = "generate-secret")]
552 Generate(AccountNamedOpt),
553 #[clap(name = "delete-secret")]
554 DeleteSecret(AccountNamedOpt),
556}
557
558#[derive(Debug, Args)]
559pub struct AccountPosixOpt {
560 #[clap(flatten)]
561 aopts: AccountCommonOpt,
562 #[clap(long)]
563 gidnumber: Option<u32>,
564 #[clap(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
565 shell: Option<String>,
567 #[clap(flatten)]
568 copt: CommonOpt,
569}
570
571#[derive(Debug, Subcommand)]
572pub enum PersonPosix {
573 #[clap(name = "show")]
574 Show(AccountNamedOpt),
575 #[clap(name = "set")]
576 Set(AccountPosixOpt),
577 #[clap(name = "set-password")]
578 SetPassword(AccountNamedOpt),
579 #[clap(name = "reset-gidnumber")]
581 ResetGidnumber {
582 account_id: String,
583 #[clap(flatten)]
584 copt: CommonOpt,
585 },
586}
587
588#[derive(Debug, Subcommand)]
589pub enum ServiceAccountPosix {
590 #[clap(name = "show")]
591 Show(AccountNamedOpt),
592 #[clap(name = "set")]
593 Set(AccountPosixOpt),
594 #[clap(name = "reset-gidnumber")]
596 ResetGidnumber {
597 account_id: String,
598 #[clap(flatten)]
599 copt: CommonOpt,
600 },
601}
602
603#[derive(Debug, Args)]
604pub struct PersonUpdateOpt {
605 #[clap(flatten)]
606 aopts: AccountCommonOpt,
607 #[clap(long, short, help = "Set the legal name for the person.",
608 value_parser = clap::builder::NonEmptyStringValueParser::new())]
609 legalname: Option<String>,
610 #[clap(long, short, help = "Set the account name for the person.",
611 value_parser = clap::builder::NonEmptyStringValueParser::new())]
612 newname: Option<String>,
613 #[clap(long, short = 'i', help = "Set the display name for the person.",
614 value_parser = clap::builder::NonEmptyStringValueParser::new())]
615 displayname: Option<String>,
616 #[clap(
617 long,
618 short,
619 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
620 )]
621 mail: Option<Vec<String>>,
622 #[clap(flatten)]
623 copt: CommonOpt,
624}
625
626#[derive(Debug, Subcommand)]
627pub enum AccountSsh {
628 #[clap(name = "list-publickeys")]
629 List(AccountNamedOpt),
630 #[clap(name = "add-publickey")]
631 Add(AccountNamedTagPkOpt),
632 #[clap(name = "delete-publickey")]
633 Delete(AccountNamedTagOpt),
634}
635
636#[derive(Debug, Subcommand)]
637pub enum AccountValidity {
638 #[clap(name = "show")]
640 Show(AccountNamedOpt),
641 #[clap(name = "expire-at")]
643 ExpireAt(AccountNamedExpireDateTimeOpt),
644 #[clap(name = "begin-from")]
646 BeginFrom(AccountNamedValidDateTimeOpt),
647}
648
649#[derive(Debug, Subcommand)]
650pub enum AccountCertificate {
651 #[clap(name = "status")]
652 Status {
653 account_id: String,
654 #[clap(flatten)]
655 copt: CommonOpt,
656 },
657 #[clap(name = "create")]
658 Create {
659 account_id: String,
660 certificate_path: PathBuf,
661 #[clap(flatten)]
662 copt: CommonOpt,
663 },
664}
665
666#[derive(Debug, Subcommand)]
667pub enum AccountUserAuthToken {
668 #[clap(name = "status")]
670 Status(AccountNamedOpt),
671 #[clap(name = "destroy")]
674 Destroy {
675 #[clap(flatten)]
676 aopts: AccountCommonOpt,
677 #[clap(flatten)]
678 copt: CommonOpt,
679 #[clap(name = "session-id")]
681 session_id: Uuid,
682 },
683}
684
685#[derive(Debug, Subcommand)]
686pub enum PersonOpt {
687 #[clap(name = "credential")]
689 Credential {
690 #[clap(subcommand)]
691 commands: AccountCredential,
692 },
693 #[clap(name = "radius")]
695 Radius {
696 #[clap(subcommand)]
697 commands: AccountRadius,
698 },
699 #[clap(name = "posix")]
701 Posix {
702 #[clap(subcommand)]
703 commands: PersonPosix,
704 },
705 #[clap(name = "session")]
707 Session {
708 #[clap(subcommand)]
709 commands: AccountUserAuthToken,
710 },
711 #[clap(name = "ssh")]
713 Ssh {
714 #[clap(subcommand)]
715 commands: AccountSsh,
716 },
717 #[clap(name = "list")]
719 List(CommonOpt),
720 #[clap(name = "get")]
722 Get(AccountNamedOpt),
723 #[clap(name = "search")]
725 Search {
726 account_id: String,
727 #[clap(flatten)]
728 copt: CommonOpt,
729 },
730 #[clap(name = "update")]
732 Update(PersonUpdateOpt),
733 #[clap(name = "create")]
735 Create(AccountCreateOpt),
736 #[clap(name = "delete")]
738 Delete(AccountNamedOpt),
739 #[clap(name = "validity")]
741 Validity {
742 #[clap(subcommand)]
743 commands: AccountValidity,
744 },
745 #[clap(name = "certificate", hide = true)]
746 Certificate {
747 #[clap(subcommand)]
748 commands: AccountCertificate,
749 },
750}
751
752#[derive(Debug, Subcommand)]
753pub enum ServiceAccountCredential {
754 #[clap(name = "status")]
756 Status(AccountNamedOpt),
757 #[clap(name = "generate")]
760 GeneratePw(AccountNamedOpt),
761}
762
763#[derive(Debug, Subcommand)]
764pub enum ServiceAccountApiToken {
765 #[clap(name = "status")]
767 Status(AccountNamedOpt),
768 #[clap(name = "generate")]
770 Generate {
771 #[clap(flatten)]
772 aopts: AccountCommonOpt,
773 #[clap(flatten)]
774 copt: CommonOpt,
775 #[clap(name = "label")]
778 label: String,
779 #[clap(name = "expiry")]
780 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
783 expiry: Option<String>,
784 #[clap(long = "rw")]
785 read_write: bool,
786 },
787 #[clap(name = "destroy")]
790 Destroy {
791 #[clap(flatten)]
792 aopts: AccountCommonOpt,
793 #[clap(flatten)]
794 copt: CommonOpt,
795 #[clap(name = "token-id")]
797 token_id: Uuid,
798 },
799}
800
801#[derive(Debug, Args)]
802pub struct ServiceAccountUpdateOpt {
803 #[clap(flatten)]
804 aopts: AccountCommonOpt,
805 #[clap(long, short, help = "Set the account name for the service account.",
806 value_parser = clap::builder::NonEmptyStringValueParser::new())]
807 newname: Option<String>,
808 #[clap(
809 long,
810 short = 'i',
811 help = "Set the display name for the service account.",
812 value_parser = clap::builder::NonEmptyStringValueParser::new()
813 )]
814 displayname: Option<String>,
815 #[clap(
816 long,
817 short = 'e',
818 help = "Set the entry manager for the service account.",
819 value_parser = clap::builder::NonEmptyStringValueParser::new()
820 )]
821 entry_managed_by: Option<String>,
822 #[clap(
823 long,
824 short,
825 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
826 )]
827 mail: Option<Vec<String>>,
828 #[clap(flatten)]
829 copt: CommonOpt,
830}
831
832#[derive(Debug, Subcommand)]
833pub enum ServiceAccountOpt {
834 #[clap(name = "credential")]
836 Credential {
837 #[clap(subcommand)]
838 commands: ServiceAccountCredential,
839 },
840 #[clap(name = "api-token")]
842 ApiToken {
843 #[clap(subcommand)]
844 commands: ServiceAccountApiToken,
845 },
846 #[clap(name = "posix")]
848 Posix {
849 #[clap(subcommand)]
850 commands: ServiceAccountPosix,
851 },
852 #[clap(name = "session")]
854 Session {
855 #[clap(subcommand)]
856 commands: AccountUserAuthToken,
857 },
858 #[clap(name = "ssh")]
860 Ssh {
861 #[clap(subcommand)]
862 commands: AccountSsh,
863 },
864 #[clap(name = "list")]
866 List(CommonOpt),
867 #[clap(name = "get")]
869 Get(AccountNamedOpt),
870 #[clap(name = "create")]
872 Create {
873 #[clap(flatten)]
874 aopts: AccountCommonOpt,
875 #[clap(name = "display-name")]
876 display_name: String,
877 #[clap(name = "entry-managed-by")]
878 entry_managed_by: String,
879 #[clap(flatten)]
880 copt: CommonOpt,
881 },
882 #[clap(name = "update")]
884 Update(ServiceAccountUpdateOpt),
885 #[clap(name = "delete")]
887 Delete(AccountNamedOpt),
888 #[clap(name = "validity")]
890 Validity {
891 #[clap(subcommand)]
892 commands: AccountValidity,
893 },
894 #[clap(name = "into-person")]
898 IntoPerson(AccountNamedOpt),
899}
900
901#[derive(Debug, Subcommand)]
902pub enum RecycleOpt {
903 #[clap(name = "list")]
904 List(CommonOpt),
906 #[clap(name = "get")]
907 Get(Named),
909 #[clap(name = "revive")]
910 Revive(Named),
912}
913
914#[derive(Debug, Args)]
915pub struct LoginOpt {
916 #[clap(flatten)]
917 copt: CommonOpt,
918 #[clap(short, long, env = "KANIDM_PASSWORD", hide = true,
919 value_parser = clap::builder::NonEmptyStringValueParser::new())]
920 password: Option<String>,
922}
923
924#[derive(Debug, Args)]
925pub struct ReauthOpt {
926 #[clap(flatten)]
927 copt: CommonOpt,
928}
929
930#[derive(Debug, Args)]
931pub struct LogoutOpt {
932 #[clap(flatten)]
933 copt: CommonOpt,
934 #[clap(short, long)]
935 local_only: bool,
937}
938
939#[derive(Debug, Subcommand)]
940pub enum SessionOpt {
941 #[clap(name = "list")]
942 List(CommonOpt),
944 #[clap(name = "cleanup")]
945 Cleanup(CommonOpt),
947}
948
949#[derive(Debug, Args)]
950pub struct FilterOpt {
951 #[clap()]
952 filter: String,
953 #[clap(flatten)]
954 commonopts: CommonOpt,
955}
956
957#[derive(Debug, Args)]
958pub struct CreateOpt {
959 #[clap(value_parser)]
960 file: PathBuf,
961 #[clap(flatten)]
962 commonopts: CommonOpt,
963}
964
965#[derive(Debug, Args)]
966pub struct ModifyOpt {
967 #[clap(flatten)]
968 commonopts: CommonOpt,
969 #[clap()]
970 filter: String,
971 #[clap(value_parser)]
972 file: PathBuf,
973}
974
975#[derive(Debug, Subcommand)]
976pub enum RawOpt {
977 #[clap(name = "search")]
978 Search(FilterOpt),
979 #[clap(name = "create")]
980 Create(CreateOpt),
981 #[clap(name = "modify")]
982 Modify(ModifyOpt),
983 #[clap(name = "delete")]
984 Delete(FilterOpt),
985}
986
987#[derive(Debug, Subcommand)]
988pub enum SelfOpt {
989 #[clap(name = "identify-user")]
991 IdentifyUser(CommonOpt),
992 Whoami(CommonOpt),
994}
995
996#[derive(Debug, Args)]
997pub struct Oauth2SetDisplayname {
998 #[clap(flatten)]
999 nopt: Named,
1000 #[clap(name = "displayname")]
1001 displayname: String,
1002}
1003
1004#[derive(Debug, Args)]
1005pub struct Oauth2SetImplicitScopes {
1006 #[clap(flatten)]
1007 nopt: Named,
1008 #[clap(name = "scopes")]
1009 scopes: Vec<String>,
1010}
1011
1012#[derive(Debug, Args)]
1013pub struct Oauth2CreateScopeMapOpt {
1014 #[clap(flatten)]
1015 nopt: Named,
1016 #[clap(name = "group")]
1017 group: String,
1018 #[clap(name = "scopes", required = true)]
1019 scopes: Vec<String>,
1020}
1021
1022#[derive(Debug, Args)]
1023pub struct Oauth2DeleteScopeMapOpt {
1024 #[clap(flatten)]
1025 nopt: Named,
1026 #[clap(name = "group")]
1027 group: String,
1028}
1029
1030#[derive(Debug, Clone, Copy, Eq, PartialEq)]
1031pub enum Oauth2ClaimMapJoin {
1032 Csv,
1033 Ssv,
1034 Array,
1035}
1036
1037impl Oauth2ClaimMapJoin {
1038 pub fn as_str(&self) -> &'static str {
1039 match self {
1040 Self::Csv => "csv",
1041 Self::Ssv => "ssv",
1042 Self::Array => "array",
1043 }
1044 }
1045}
1046
1047impl ValueEnum for Oauth2ClaimMapJoin {
1048 fn value_variants<'a>() -> &'a [Self] {
1049 &[Self::Csv, Self::Ssv, Self::Array]
1050 }
1051
1052 fn to_possible_value(&self) -> Option<PossibleValue> {
1053 Some(match self {
1054 Self::Csv => PossibleValue::new("csv"),
1055 Self::Ssv => PossibleValue::new("ssv"),
1056 Self::Array => PossibleValue::new("array"),
1057 })
1058 }
1059}
1060
1061#[derive(Debug, Subcommand)]
1062pub enum Oauth2Opt {
1063 #[clap(name = "list")]
1064 List(CommonOpt),
1066 #[clap(name = "get")]
1067 Get(Named),
1069 #[clap(name = "create")]
1073 CreateBasic {
1075 #[clap(name = "name")]
1076 name: String,
1077 #[clap(name = "displayname")]
1078 displayname: String,
1079 #[clap(name = "origin")]
1080 origin: String,
1081 #[clap(flatten)]
1082 copt: CommonOpt,
1083 },
1084 #[clap(name = "create-public")]
1085 CreatePublic {
1091 #[clap(name = "name")]
1092 name: String,
1093 #[clap(name = "displayname")]
1094 displayname: String,
1095 #[clap(name = "origin")]
1096 origin: String,
1097 #[clap(flatten)]
1098 copt: CommonOpt,
1099 },
1100 #[clap(name = "update-scope-map", visible_aliases=&["create-scope-map"])]
1101 UpdateScopeMap(Oauth2CreateScopeMapOpt),
1103 #[clap(name = "delete-scope-map")]
1104 DeleteScopeMap(Oauth2DeleteScopeMapOpt),
1106
1107 #[clap(name = "update-sup-scope-map", visible_aliases=&["create-sup-scope-map"])]
1108 UpdateSupScopeMap(Oauth2CreateScopeMapOpt),
1110 #[clap(name = "delete-sup-scope-map")]
1111 DeleteSupScopeMap(Oauth2DeleteScopeMapOpt),
1113
1114 #[clap(name = "update-claim-map", visible_aliases=&["create-claim-map"])]
1115 UpdateClaimMap {
1117 #[clap(flatten)]
1118 copt: CommonOpt,
1119 name: String,
1120 claim_name: String,
1121 group: String,
1122 values: Vec<String>,
1123 },
1124 #[clap(name = "update-claim-map-join")]
1125 UpdateClaimMapJoin {
1126 #[clap(flatten)]
1127 copt: CommonOpt,
1128 name: String,
1129 claim_name: String,
1130 join: Oauth2ClaimMapJoin,
1133 },
1134 #[clap(name = "delete-claim-map")]
1135 DeleteClaimMap {
1137 #[clap(flatten)]
1138 copt: CommonOpt,
1139 name: String,
1140 claim_name: String,
1141 group: String,
1142 },
1143
1144 #[clap(name = "reset-basic-secret")]
1145 ResetSecrets(Named),
1148 #[clap(name = "show-basic-secret")]
1149 ShowBasicSecret(Named),
1151 #[clap(name = "delete")]
1152 Delete(Named),
1154 #[clap(name = "set-displayname")]
1156 SetDisplayname(Oauth2SetDisplayname),
1157 #[clap(name = "set-name")]
1161 SetName {
1162 #[clap(flatten)]
1163 nopt: Named,
1164 #[clap(name = "newname")]
1165 name: String,
1166 },
1167
1168 #[clap(name = "set-landing-url")]
1171 SetLandingUrl {
1172 #[clap(flatten)]
1173 nopt: Named,
1174 #[clap(name = "landing-url")]
1175 url: Url,
1176 },
1177 #[clap(name = "set-image")]
1179 SetImage {
1180 #[clap(flatten)]
1181 nopt: Named,
1182 #[clap(name = "file-path")]
1183 path: PathBuf,
1185 #[clap(name = "image-type")]
1186 image_type: Option<ImageType>,
1188 },
1189 #[clap(name = "remove-image")]
1191 RemoveImage(Named),
1192
1193 #[clap(name = "add-redirect-url")]
1197 AddOrigin {
1198 name: String,
1199 #[clap(name = "url")]
1200 origin: Url,
1201 #[clap(flatten)]
1202 copt: CommonOpt,
1203 },
1204
1205 #[clap(name = "remove-redirect-url")]
1207 RemoveOrigin {
1208 name: String,
1209 #[clap(name = "url")]
1210 origin: Url,
1211 #[clap(flatten)]
1212 copt: CommonOpt,
1213 },
1214 #[clap(name = "enable-pkce")]
1215 EnablePkce(Named),
1217 #[clap(name = "warning-insecure-client-disable-pkce")]
1220 DisablePkce(Named),
1221 #[clap(name = "warning-enable-legacy-crypto")]
1222 EnableLegacyCrypto(Named),
1226 #[clap(name = "disable-legacy-crypto")]
1228 DisableLegacyCrypto(Named),
1229 #[clap(name = "enable-strict-redirect-url")]
1233 EnableStrictRedirectUri {
1234 name: String,
1235 #[clap(flatten)]
1236 copt: CommonOpt,
1237 },
1238 #[clap(name = "disable-strict-redirect-url")]
1239 DisableStrictRedirectUri {
1240 name: String,
1241 #[clap(flatten)]
1242 copt: CommonOpt,
1243 },
1244 #[clap(name = "enable-localhost-redirects")]
1245 EnablePublicLocalhost {
1247 #[clap(flatten)]
1248 copt: CommonOpt,
1249 name: String,
1250 },
1251 #[clap(name = "disable-localhost-redirects")]
1253 DisablePublicLocalhost {
1254 #[clap(flatten)]
1255 copt: CommonOpt,
1256 name: String,
1257 },
1258 #[clap(name = "prefer-short-username")]
1260 PreferShortUsername(Named),
1261 #[clap(name = "prefer-spn-username")]
1263 PreferSPNUsername(Named),
1264 #[cfg(feature = "dev-oauth2-device-flow")]
1265 DeviceFlowEnable(Named),
1267 #[cfg(feature = "dev-oauth2-device-flow")]
1268 DeviceFlowDisable(Named),
1270 #[clap(name = "rotate-cryptographic-keys")]
1276 RotateCryptographicKeys {
1277 #[clap(flatten)]
1278 copt: CommonOpt,
1279 name: String,
1280 #[clap(value_parser = parse_rfc3339)]
1281 rotate_at: OffsetDateTime,
1282 },
1283 #[clap(name = "revoke-cryptographic-key")]
1287 RevokeCryptographicKey {
1288 #[clap(flatten)]
1289 copt: CommonOpt,
1290 name: String,
1291 key_id: String,
1292 },
1293}
1294
1295#[derive(Args, Debug)]
1296pub struct OptSetDomainDisplayname {
1297 #[clap(flatten)]
1298 copt: CommonOpt,
1299 #[clap(name = "new-display-name")]
1300 new_display_name: String,
1301}
1302
1303#[derive(Debug, Subcommand)]
1304pub enum PwBadlistOpt {
1305 #[clap[name = "show"]]
1306 Show(CommonOpt),
1308 #[clap[name = "upload"]]
1309 Upload {
1313 #[clap(flatten)]
1314 copt: CommonOpt,
1315 #[clap(value_parser, required = true, num_args(1..))]
1316 paths: Vec<PathBuf>,
1317 #[clap(short = 'n', long)]
1319 dryrun: bool,
1320 },
1321 #[clap[name = "remove", hide = true]]
1322 Remove {
1325 #[clap(flatten)]
1326 copt: CommonOpt,
1327 #[clap(value_parser, required = true, num_args(1..))]
1328 paths: Vec<PathBuf>,
1329 },
1330}
1331
1332#[derive(Debug, Subcommand)]
1333pub enum DeniedNamesOpt {
1334 #[clap[name = "show"]]
1335 Show {
1337 #[clap(flatten)]
1338 copt: CommonOpt,
1339 },
1340 #[clap[name = "append"]]
1341 Append {
1342 #[clap(flatten)]
1343 copt: CommonOpt,
1344 #[clap(value_parser, required = true, num_args(1..))]
1345 names: Vec<String>,
1346 },
1347 #[clap[name = "remove"]]
1348 Remove {
1350 #[clap(flatten)]
1351 copt: CommonOpt,
1352 #[clap(value_parser, required = true, num_args(1..))]
1353 names: Vec<String>,
1354 },
1355}
1356
1357#[derive(Debug, Subcommand)]
1358pub enum DomainOpt {
1359 #[clap[name = "set-displayname"]]
1360 SetDisplayname(OptSetDomainDisplayname),
1362 #[clap[name = "set-ldap-queryable-attrs"]]
1364 SetLdapMaxQueryableAttrs {
1365 #[clap(flatten)]
1366 copt: CommonOpt,
1367 #[clap(name = "maximum-queryable-attrs")]
1368 new_max_queryable_attrs: usize,
1369 },
1370 #[clap[name = "set-ldap-basedn"]]
1371 SetLdapBasedn {
1376 #[clap(flatten)]
1377 copt: CommonOpt,
1378 #[clap(name = "new-basedn")]
1379 new_basedn: String,
1380 },
1381 SetLdapAllowUnixPasswordBind {
1384 #[clap(flatten)]
1385 copt: CommonOpt,
1386 #[clap(name = "allow", action = clap::ArgAction::Set)]
1387 enable: bool,
1388 },
1389 SetAllowEasterEggs {
1393 #[clap(flatten)]
1394 copt: CommonOpt,
1395 #[clap(name = "allow", action = clap::ArgAction::Set)]
1396 enable: bool,
1397 },
1398 #[clap(name = "show")]
1399 Show(CommonOpt),
1401 #[clap(name = "revoke-key")]
1402 RevokeKey {
1405 #[clap(flatten)]
1406 copt: CommonOpt,
1407 key_id: String,
1408 },
1409 #[clap(name = "set-image")]
1411 SetImage {
1412 #[clap(flatten)]
1413 copt: CommonOpt,
1414 #[clap(name = "file-path")]
1415 path: PathBuf,
1416 #[clap(name = "image-type")]
1417 image_type: Option<ImageType>,
1418 },
1419 #[clap(name = "remove-image")]
1421 RemoveImage {
1422 #[clap(flatten)]
1423 copt: CommonOpt,
1424 },
1425}
1426
1427#[derive(Debug, Subcommand)]
1428pub enum SynchOpt {
1429 #[clap(name = "list")]
1430 List(CommonOpt),
1432 #[clap(name = "get")]
1433 Get(Named),
1435 #[clap(name = "set-credential-portal")]
1436 SetCredentialPortal {
1439 #[clap()]
1440 account_id: String,
1441 #[clap(flatten)]
1442 copt: CommonOpt,
1443 #[clap(name = "url")]
1444 url: Option<Url>,
1445 },
1446 #[clap(name = "create")]
1448 Create {
1449 #[clap()]
1450 account_id: String,
1451 #[clap(flatten)]
1452 copt: CommonOpt,
1453 #[clap(name = "description",
1454 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1455 description: Option<String>,
1456 },
1457 #[clap(name = "generate-token")]
1459 GenerateToken {
1460 #[clap()]
1461 account_id: String,
1462 #[clap()]
1463 label: String,
1464 #[clap(flatten)]
1465 copt: CommonOpt,
1466 },
1467 #[clap(name = "destroy-token")]
1469 DestroyToken {
1470 #[clap()]
1471 account_id: String,
1472 #[clap(flatten)]
1473 copt: CommonOpt,
1474 },
1475 #[clap(name = "set-yield-attributes")]
1479 SetYieldAttributes {
1480 #[clap()]
1481 account_id: String,
1482 #[clap(flatten)]
1483 copt: CommonOpt,
1484 #[clap(name = "attributes")]
1485 attrs: Vec<String>,
1486 },
1487 #[clap(name = "force-refresh")]
1491 ForceRefresh {
1492 #[clap()]
1493 account_id: String,
1494 #[clap(flatten)]
1495 copt: CommonOpt,
1496 },
1497 #[clap(name = "finalise")]
1503 Finalise {
1504 #[clap()]
1505 account_id: String,
1506 #[clap(flatten)]
1507 copt: CommonOpt,
1508 },
1509 #[clap(name = "terminate")]
1515 Terminate {
1516 #[clap()]
1517 account_id: String,
1518 #[clap(flatten)]
1519 copt: CommonOpt,
1520 },
1521}
1522
1523#[derive(Debug, Subcommand)]
1524pub enum AuthSessionExpiryOpt {
1525 #[clap[name = "get"]]
1526 Get(CommonOpt),
1528 #[clap[name = "set"]]
1529 Set {
1531 #[clap(flatten)]
1532 copt: CommonOpt,
1533 #[clap(name = "expiry")]
1534 expiry: u32,
1535 },
1536}
1537
1538#[derive(Debug, Subcommand)]
1539pub enum PrivilegedSessionExpiryOpt {
1540 #[clap[name = "get"]]
1541 Get(CommonOpt),
1543 #[clap[name = "set"]]
1544 Set {
1546 #[clap(flatten)]
1547 copt: CommonOpt,
1548 #[clap(name = "expiry")]
1549 expiry: u32,
1550 },
1551}
1552
1553#[derive(Args, Debug)]
1554pub struct ApiSchemaDownloadOpt {
1555 #[clap(flatten)]
1556 copt: CommonOpt,
1557 #[clap(name = "filename", env, default_value = "./kanidm-openapi.json")]
1559 filename: PathBuf,
1560 #[clap(short, long, env)]
1562 force: bool,
1563}
1564
1565#[derive(Debug, Subcommand)]
1566pub enum ApiOpt {
1567 #[clap(name = "download-schema")]
1569 DownloadSchema(ApiSchemaDownloadOpt),
1570}
1571
1572#[derive(Debug, Subcommand)]
1573pub enum SystemOpt {
1574 #[clap(name = "pw-badlist")]
1575 PwBadlist {
1577 #[clap(subcommand)]
1578 commands: PwBadlistOpt,
1579 },
1580 #[clap(name = "denied-names")]
1581 DeniedNames {
1583 #[clap(subcommand)]
1584 commands: DeniedNamesOpt,
1585 },
1586 #[clap(name = "oauth2")]
1587 Oauth2 {
1589 #[clap(subcommand)]
1590 commands: Oauth2Opt,
1591 },
1592 #[clap(name = "domain")]
1593 Domain {
1595 #[clap(subcommand)]
1596 commands: DomainOpt,
1597 },
1598 #[clap(name = "sync")]
1599 Synch {
1601 #[clap(subcommand)]
1602 commands: SynchOpt,
1603 },
1604 #[clap(name = "api")]
1605 Api {
1607 #[clap(subcommand)]
1608 commands: ApiOpt,
1609 },
1610}
1611
1612#[derive(Debug, Subcommand)]
1613#[clap(about = "Kanidm Client Utility")]
1614pub enum KanidmClientOpt {
1615 Login(LoginOpt),
1617 Reauth(ReauthOpt),
1619 Logout(LogoutOpt),
1621 Session {
1623 #[clap(subcommand)]
1624 commands: SessionOpt,
1625 },
1626 #[clap(name = "self")]
1627 CSelf {
1629 #[clap(subcommand)]
1630 commands: SelfOpt,
1631 },
1632 Person {
1634 #[clap(subcommand)]
1635 commands: PersonOpt,
1636 },
1637 Group {
1639 #[clap(subcommand)]
1640 commands: GroupOpt,
1641 },
1642 #[clap(name = "service-account")]
1644 ServiceAccount {
1645 #[clap(subcommand)]
1646 commands: ServiceAccountOpt,
1647 },
1648 #[clap(name = "graph")]
1650 Graph(GraphCommonOpt),
1651 System {
1653 #[clap(subcommand)]
1654 commands: SystemOpt,
1655 },
1656 #[clap(name = "recycle-bin")]
1657 Recycle {
1659 #[clap(subcommand)]
1660 commands: RecycleOpt,
1661 },
1662 #[clap(hide = true)]
1664 Raw {
1665 #[clap(subcommand)]
1666 commands: RawOpt,
1667 },
1668 Version {},
1670}
1671
1672#[derive(Debug, clap::Parser)]
1673#[clap(about = "Kanidm Client Utility")]
1674pub struct KanidmClientParser {
1675 #[clap(subcommand)]
1676 pub commands: KanidmClientOpt,
1677}