1use clap::{builder::PossibleValue, Args, Subcommand, ValueEnum};
2use kanidm_proto::constants::CLIENT_TOKEN_CACHE;
3use kanidm_proto::internal::ImageType;
4use kanidm_proto::scim_v1::ScimFilter;
5use std::fmt;
6use time::format_description::well_known::Rfc3339;
7use time::OffsetDateTime;
8
9fn parse_rfc3339(input: &str) -> Result<OffsetDateTime, time::error::Parse> {
10 if input == "now" {
11 #[allow(clippy::disallowed_methods)]
12 Ok(OffsetDateTime::now_utc())
14 } else {
15 OffsetDateTime::parse(input, &Rfc3339)
16 }
17}
18
19#[derive(Debug, Args, Clone)]
20pub struct Named {
21 pub name: String,
22}
23
24#[derive(Debug, Args, Clone)]
25pub struct DebugOpt {
26 #[clap(short, long, env = "KANIDM_DEBUG")]
28 pub debug: bool,
29}
30
31#[derive(Debug, Clone, Copy, Default)]
32pub enum OutputMode {
34 #[default]
35 Text,
36 Json,
37}
38
39impl From<OutputMode> for clap::builder::OsStr {
40 fn from(output_mode: OutputMode) -> Self {
41 match output_mode {
42 OutputMode::Text => "text".into(),
43 OutputMode::Json => "json".into(),
44 }
45 }
46}
47
48impl std::str::FromStr for OutputMode {
49 type Err = String;
50 fn from_str(s: &str) -> Result<OutputMode, std::string::String> {
51 match s.to_lowercase().as_str() {
52 "text" => Ok(OutputMode::Text),
53 "json" => Ok(OutputMode::Json),
54 _ => Ok(OutputMode::Text),
55 }
56 }
57}
58
59impl OutputMode {
60 pub fn print_message<T>(self, input: T)
61 where
62 T: serde::Serialize + fmt::Debug + fmt::Display,
63 {
64 match self {
65 OutputMode::Json => {
66 println!(
67 "{}",
68 serde_json::to_string(&input).unwrap_or(format!("{input:?}"))
69 );
70 }
71 OutputMode::Text => {
72 println!("{input}");
73 }
74 }
75 }
76}
77
78#[derive(Debug, Args, Clone)]
79pub struct GroupNamedMembers {
80 name: String,
81 #[clap(required = true, num_args(1..))]
82 members: Vec<String>,
83}
84
85#[derive(Debug, Args, Clone)]
86pub struct GroupPosixOpt {
87 name: String,
88 #[clap(long)]
89 gidnumber: Option<u32>,
90}
91
92#[derive(Debug, Subcommand, Clone)]
93pub enum GroupPosix {
94 #[clap(name = "show")]
96 Show(Named),
97 #[clap(name = "set")]
99 Set(GroupPosixOpt),
100 #[clap(name = "reset-gidnumber")]
102 ResetGidnumber { group_id: String },
103}
104
105#[derive(Debug, Clone, Copy, Eq, PartialEq)]
106pub enum AccountPolicyCredentialType {
107 Any,
108 Mfa,
109 Passkey,
110 AttestedPasskey,
111}
112
113impl AccountPolicyCredentialType {
114 pub fn as_str(&self) -> &'static str {
115 match self {
116 Self::Any => "any",
117 Self::Mfa => "mfa",
118 Self::Passkey => "passkey",
119 Self::AttestedPasskey => "attested_passkey",
120 }
121 }
122}
123
124impl ValueEnum for AccountPolicyCredentialType {
125 fn value_variants<'a>() -> &'a [Self] {
126 &[Self::Any, Self::Mfa, Self::Passkey, Self::AttestedPasskey]
127 }
128
129 fn to_possible_value(&self) -> Option<PossibleValue> {
130 Some(self.as_str().into())
131 }
132}
133
134#[derive(Debug, Subcommand, Clone)]
135pub enum GroupAccountPolicyOpt {
136 #[clap(name = "enable")]
138 Enable { name: String },
139 #[clap(name = "auth-expiry")]
141 AuthSessionExpiry { name: String, expiry: u32 },
142 #[clap(name = "credential-type-minimum")]
145 CredentialTypeMinimum {
146 name: String,
147 #[clap(value_enum)]
148 value: AccountPolicyCredentialType,
149 },
150 #[clap(name = "password-minimum-length")]
152 PasswordMinimumLength { name: String, length: u32 },
153
154 #[clap(name = "privilege-expiry")]
156 PrivilegedSessionExpiry { name: String, expiry: u32 },
157
158 #[clap(name = "webauthn-attestation-ca-list")]
163 WebauthnAttestationCaList {
164 name: String,
165 attestation_ca_list_json_file: PathBuf,
166 },
167
168 #[clap(name = "limit-search-max-results")]
171 LimitSearchMaxResults { name: String, maximum: u32 },
172 #[clap(name = "limit-search-max-filter-test")]
176 LimitSearchMaxFilterTest { name: String, maximum: u32 },
177 #[clap(name = "allow-primary-cred-fallback")]
180 AllowPrimaryCredFallback {
181 name: String,
182 #[clap(name = "allow", action = clap::ArgAction::Set)]
183 allow: bool,
184 },
185
186 #[clap(name = "reset-auth-expiry")]
188 ResetAuthSessionExpiry { name: String },
189 #[clap(name = "reset-password-minimum-length")]
191 ResetPasswordMinimumLength { name: String },
192 #[clap(name = "reset-privilege-expiry")]
194 ResetPrivilegedSessionExpiry { name: String },
195 #[clap(name = "reset-webauthn-attestation-ca-list")]
198 ResetWebauthnAttestationCaList { name: String },
199 #[clap(name = "reset-limit-search-max-results")]
201 ResetLimitSearchMaxResults { name: String },
202 #[clap(name = "reset-limit-search-max-filter-test")]
204 ResetLimitSearchMaxFilterTest { name: String },
205}
206
207#[derive(Debug, Subcommand, Clone)]
208pub enum GroupOpt {
209 #[clap(name = "list")]
211 List,
212 #[clap(name = "get")]
214 Get(Named),
215 #[clap(name = "search")]
217 Search {
218 name: String,
220 },
221 #[clap(name = "create")]
223 Create {
224 name: String,
226 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
228 entry_managed_by: Option<String>,
229 },
230 #[clap(name = "delete")]
232 Delete(Named),
233 #[clap(name = "list-members")]
235 ListMembers(Named),
236 #[clap(name = "set-members")]
239 SetMembers(GroupNamedMembers),
240 #[clap(name = "set-mail")]
244 SetMail { name: String, mail: Vec<String> },
245 #[clap(name = "set-description")]
247 SetDescription {
248 name: String,
249 description: Option<String>,
250 },
251 #[clap(name = "set-entry-manager")]
253 SetEntryManagedBy {
254 name: String,
256 entry_managed_by: String,
258 },
259 #[clap(name = "rename")]
261 Rename {
262 name: String,
264 new_name: String,
266 },
267 #[clap(name = "purge-members")]
269 PurgeMembers(Named),
270 #[clap(name = "add-members")]
272 AddMembers(GroupNamedMembers),
273 #[clap(name = "remove-members")]
275 RemoveMembers(GroupNamedMembers),
276 #[clap(name = "posix")]
278 Posix {
279 #[clap(subcommand)]
280 commands: GroupPosix,
281 },
282 #[clap(name = "account-policy")]
284 AccountPolicy {
285 #[clap(subcommand)]
286 commands: GroupAccountPolicyOpt,
287 },
288}
289
290#[derive(Clone, Debug, ValueEnum)]
291pub enum GraphType {
292 Graphviz,
293 Mermaid,
294 MermaidElk,
295}
296
297#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, ValueEnum)]
298pub enum ObjectType {
299 Group,
300 BuiltinGroup,
301 ServiceAccount,
302 Person,
303}
304
305#[derive(Debug, Args, Clone)]
306pub struct GraphCommonOpt {
307 #[arg(value_enum)]
308 pub graph_type: GraphType,
309 #[clap()]
310 pub filter: Vec<ObjectType>,
311}
312
313#[derive(Debug, Args, Clone)]
314pub struct AccountCommonOpt {
315 #[clap()]
316 account_id: String,
317}
318
319#[derive(Debug, Args, Clone)]
320pub struct AccountNamedOpt {
321 #[clap(flatten)]
322 aopts: AccountCommonOpt,
323}
324
325#[derive(Debug, Args, Clone)]
326pub struct AccountNamedExpireDateTimeOpt {
327 #[clap(flatten)]
328 aopts: AccountCommonOpt,
329 #[clap(name = "datetime", verbatim_doc_comment)]
330 datetime: String,
336}
337
338#[derive(Debug, Args, Clone)]
339pub struct AccountNamedValidDateTimeOpt {
340 #[clap(flatten)]
341 aopts: AccountCommonOpt,
342 #[clap(name = "datetime")]
343 datetime: String,
346}
347
348#[derive(Debug, Args, Clone)]
349pub struct AccountNamedTagOpt {
350 #[clap(flatten)]
351 aopts: AccountCommonOpt,
352 #[clap(name = "tag")]
353 tag: String,
354}
355
356#[derive(Debug, Args, Clone)]
357pub struct AccountNamedTagPkOpt {
358 #[clap(flatten)]
359 aopts: AccountCommonOpt,
360 #[clap(name = "tag")]
361 tag: String,
362 #[clap(name = "pubkey")]
363 pubkey: String,
364}
365
366#[derive(Debug, Args, Clone)]
367pub struct UseResetTokenOpt {
369 #[clap(name = "token")]
370 token: String,
371}
372
373#[derive(Debug, Args, Clone)]
374pub struct AccountCreateOpt {
375 #[clap(flatten)]
376 aopts: AccountCommonOpt,
377 #[clap(name = "display-name")]
378 display_name: String,
379}
380
381#[derive(Debug, Subcommand, Clone)]
382pub enum AccountCredential {
383 #[clap(name = "status")]
385 Status(AccountNamedOpt),
386 #[clap(name = "update")]
388 Update(AccountNamedOpt),
389 #[clap(name = "use-reset-token")]
391 UseResetToken(UseResetTokenOpt),
392 #[clap(name = "create-reset-token")]
395 CreateResetToken {
396 #[clap(flatten)]
397 aopts: AccountCommonOpt,
398
399 ttl: Option<u32>,
402 },
403 #[clap(name = "softlock-reset")]
405 SoftlockReset {
406 account_id: String,
407 #[clap(name = "datetime", default_value = "now", verbatim_doc_comment)]
408 datetime: String,
412 }
413}
414
415#[derive(Debug, Subcommand, Clone)]
417pub enum AccountRadius {
418 #[clap(name = "show-secret")]
420 Show(AccountNamedOpt),
421 #[clap(name = "generate-secret")]
423 Generate(AccountNamedOpt),
424 #[clap(name = "delete-secret")]
425 DeleteSecret(AccountNamedOpt),
427}
428
429#[derive(Debug, Args, Clone)]
430pub struct AccountPosixOpt {
431 #[clap(flatten)]
432 aopts: AccountCommonOpt,
433 #[clap(long)]
434 gidnumber: Option<u32>,
435 #[clap(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
436 shell: Option<String>,
438}
439
440#[derive(Debug, Subcommand, Clone)]
441pub enum PersonPosix {
442 #[clap(name = "show")]
443 Show(AccountNamedOpt),
444 #[clap(name = "set")]
445 Set(AccountPosixOpt),
446 #[clap(name = "set-password")]
447 SetPassword(AccountNamedOpt),
448 #[clap(name = "reset-gidnumber")]
450 ResetGidnumber { account_id: String },
451}
452
453#[derive(Debug, Subcommand, Clone)]
454pub enum ServiceAccountPosix {
455 #[clap(name = "show")]
456 Show(AccountNamedOpt),
457 #[clap(name = "set")]
458 Set(AccountPosixOpt),
459 #[clap(name = "reset-gidnumber")]
461 ResetGidnumber { account_id: String },
462}
463
464#[derive(Debug, Args, Clone)]
465pub struct PersonUpdateOpt {
466 #[clap(flatten)]
467 aopts: AccountCommonOpt,
468 #[clap(long, short, help = "Set the legal name for the person.",
469 value_parser = clap::builder::NonEmptyStringValueParser::new())]
470 legalname: Option<String>,
471 #[clap(long, short, help = "Set the account name for the person.",
472 value_parser = clap::builder::NonEmptyStringValueParser::new())]
473 newname: Option<String>,
474 #[clap(long, short = 'i', help = "Set the display name for the person.",
475 value_parser = clap::builder::NonEmptyStringValueParser::new())]
476 displayname: Option<String>,
477 #[clap(
478 long,
479 short,
480 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
481 )]
482 mail: Option<Vec<String>>,
483}
484
485#[derive(Debug, Subcommand, Clone)]
486pub enum AccountSsh {
487 #[clap(name = "list-publickeys")]
488 List(AccountNamedOpt),
489 #[clap(name = "add-publickey")]
490 Add(AccountNamedTagPkOpt),
491 #[clap(name = "delete-publickey")]
492 Delete(AccountNamedTagOpt),
493}
494
495#[derive(Debug, Subcommand, Clone)]
496pub enum AccountValidity {
497 #[clap(name = "show")]
499 Show(AccountNamedOpt),
500 #[clap(name = "expire-at")]
502 ExpireAt(AccountNamedExpireDateTimeOpt),
503 #[clap(name = "begin-from")]
505 BeginFrom(AccountNamedValidDateTimeOpt),
506}
507
508#[derive(Debug, Subcommand, Clone)]
509pub enum AccountCertificate {
510 #[clap(name = "status")]
511 Status { account_id: String },
512 #[clap(name = "create")]
513 Create {
514 account_id: String,
515 certificate_path: PathBuf,
516 },
517}
518
519#[derive(Debug, Subcommand, Clone)]
520pub enum AccountUserAuthToken {
521 #[clap(name = "status")]
523 Status(AccountNamedOpt),
524 #[clap(name = "destroy")]
527 Destroy {
528 #[clap(flatten)]
529 aopts: AccountCommonOpt,
530
531 #[clap(name = "session-id")]
533 session_id: Uuid,
534 },
535}
536
537#[derive(Debug, Subcommand, Clone)]
538pub enum PersonOpt {
539 #[clap(name = "credential")]
541 Credential {
542 #[clap(subcommand)]
543 commands: AccountCredential,
544 },
545 #[clap(name = "radius")]
547 Radius {
548 #[clap(subcommand)]
549 commands: AccountRadius,
550 },
551 #[clap(name = "posix")]
553 Posix {
554 #[clap(subcommand)]
555 commands: PersonPosix,
556 },
557 #[clap(name = "session")]
559 Session {
560 #[clap(subcommand)]
561 commands: AccountUserAuthToken,
562 },
563 #[clap(name = "ssh")]
565 Ssh {
566 #[clap(subcommand)]
567 commands: AccountSsh,
568 },
569 #[clap(name = "list")]
571 List,
572 #[clap(name = "get")]
574 Get(AccountNamedOpt),
575 #[clap(name = "search")]
577 Search { account_id: String },
578 #[clap(name = "update")]
580 Update(PersonUpdateOpt),
581 #[clap(name = "create")]
583 Create(AccountCreateOpt),
584 #[clap(name = "delete")]
586 Delete(AccountNamedOpt),
587 #[clap(name = "validity")]
589 Validity {
590 #[clap(subcommand)]
591 commands: AccountValidity,
592 },
593 #[clap(name = "certificate", hide = true)]
594 Certificate {
595 #[clap(subcommand)]
596 commands: AccountCertificate,
597 },
598}
599
600#[derive(Debug, Subcommand, Clone)]
601pub enum ServiceAccountCredential {
602 #[clap(name = "status")]
604 Status(AccountNamedOpt),
605 #[clap(name = "generate")]
608 GeneratePw(AccountNamedOpt),
609}
610
611#[derive(Debug, Subcommand, Clone)]
612pub enum ServiceAccountApiToken {
613 #[clap(name = "status")]
615 Status(AccountNamedOpt),
616 #[clap(name = "generate")]
618 Generate {
619 #[clap(flatten)]
620 aopts: AccountCommonOpt,
621
622 #[clap(name = "label")]
625 label: String,
626 #[clap(name = "expiry")]
627 #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
630 expiry: Option<String>,
631 #[clap(short = 'w', long = "readwrite")]
633 read_write: bool,
634
635 #[clap(short = 'c', long = "compact")]
641 compact: bool,
642 },
643 #[clap(name = "destroy")]
646 Destroy {
647 #[clap(flatten)]
648 aopts: AccountCommonOpt,
649
650 #[clap(name = "token-id")]
652 token_id: Uuid,
653 },
654}
655
656#[derive(Debug, Args, Clone)]
657pub struct ServiceAccountUpdateOpt {
658 #[clap(flatten)]
659 aopts: AccountCommonOpt,
660 #[clap(long, short, help = "Set the account name for the service account.",
661 value_parser = clap::builder::NonEmptyStringValueParser::new())]
662 newname: Option<String>,
663 #[clap(
664 long,
665 short = 'i',
666 help = "Set the display name for the service account.",
667 value_parser = clap::builder::NonEmptyStringValueParser::new()
668 )]
669 displayname: Option<String>,
670 #[clap(
671 long,
672 short = 'e',
673 help = "Set the entry manager for the service account.",
674 value_parser = clap::builder::NonEmptyStringValueParser::new()
675 )]
676 entry_managed_by: Option<String>,
677 #[clap(
678 long,
679 short,
680 help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
681 )]
682 mail: Option<Vec<String>>,
683}
684
685#[derive(Debug, Subcommand, Clone)]
686pub enum ServiceAccountOpt {
687 #[clap(name = "credential")]
689 Credential {
690 #[clap(subcommand)]
691 commands: ServiceAccountCredential,
692 },
693 #[clap(name = "api-token")]
695 ApiToken {
696 #[clap(subcommand)]
697 commands: ServiceAccountApiToken,
698 },
699 #[clap(name = "posix")]
701 Posix {
702 #[clap(subcommand)]
703 commands: ServiceAccountPosix,
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,
720 #[clap(name = "get")]
722 Get(AccountNamedOpt),
723 #[clap(name = "create")]
725 Create {
726 #[clap(flatten)]
727 aopts: AccountCommonOpt,
728 #[clap(name = "display-name")]
729 display_name: String,
730 #[clap(name = "entry-managed-by")]
731 entry_managed_by: String,
732 },
733 #[clap(name = "update")]
735 Update(ServiceAccountUpdateOpt),
736 #[clap(name = "delete")]
738 Delete(AccountNamedOpt),
739 #[clap(name = "validity")]
741 Validity {
742 #[clap(subcommand)]
743 commands: AccountValidity,
744 },
745 #[clap(name = "into-person")]
749 IntoPerson(AccountNamedOpt),
750}
751
752#[derive(Debug, Subcommand, Clone)]
753pub enum RecycleOpt {
754 #[clap(name = "list")]
755 List,
757 #[clap(name = "get")]
758 Get(Named),
760 #[clap(name = "revive")]
761 Revive(Named),
763}
764
765#[derive(Debug, Args, Clone)]
766pub struct LoginOpt {}
767
768#[derive(Debug, Args, Clone)]
769pub struct LogoutOpt {
770 #[clap(short, long)]
771 local_only: bool,
773}
774
775#[derive(Debug, Subcommand, Clone)]
776pub enum SessionOpt {
777 #[clap(name = "list")]
778 List,
780 #[clap(name = "cleanup")]
781 Cleanup,
783}
784
785#[derive(Debug, Subcommand, Clone)]
786pub enum RawOpt {
787 #[clap(name = "search")]
788 Search {
789 filter: ScimFilter
790 },
791 #[clap(name = "create")]
792 Create {
793 file: PathBuf
794 },
795 #[clap(name = "update")]
796 Update {
797 file: PathBuf
798 },
799 #[clap(name = "delete")]
800 Delete {
801 id: String
802 },
803}
804
805#[derive(Debug, Subcommand, Clone)]
806pub enum SelfOpt {
807 #[clap(name = "identify-user")]
809 IdentifyUser,
810 Whoami,
812}
813
814#[derive(Debug, Args, Clone)]
815pub struct Oauth2SetDisplayname {
816 #[clap(flatten)]
817 nopt: Named,
818 #[clap(name = "displayname")]
819 displayname: String,
820}
821
822#[derive(Debug, Args, Clone)]
823pub struct Oauth2SetImplicitScopes {
824 #[clap(flatten)]
825 nopt: Named,
826 #[clap(name = "scopes")]
827 scopes: Vec<String>,
828}
829
830#[derive(Debug, Args, Clone)]
831pub struct Oauth2CreateScopeMapOpt {
832 #[clap(flatten)]
833 nopt: Named,
834 #[clap(name = "group")]
835 group: String,
836 #[clap(name = "scopes", required = true, num_args=1.. )]
837 scopes: Vec<String>,
838}
839
840#[derive(Debug, Args, Clone)]
841pub struct Oauth2DeleteScopeMapOpt {
842 #[clap(flatten)]
843 nopt: Named,
844 #[clap(name = "group")]
845 group: String,
846}
847
848#[derive(Debug, Clone, Copy, Eq, PartialEq)]
849pub enum Oauth2ClaimMapJoin {
850 Csv,
851 Ssv,
852 Array,
853}
854
855impl Oauth2ClaimMapJoin {
856 pub fn as_str(&self) -> &'static str {
857 match self {
858 Self::Csv => "csv",
859 Self::Ssv => "ssv",
860 Self::Array => "array",
861 }
862 }
863}
864
865impl ValueEnum for Oauth2ClaimMapJoin {
866 fn value_variants<'a>() -> &'a [Self] {
867 &[Self::Csv, Self::Ssv, Self::Array]
868 }
869
870 fn to_possible_value(&self) -> Option<PossibleValue> {
871 Some(self.as_str().into())
872 }
873}
874
875#[derive(Debug, Subcommand, Clone)]
876pub enum Oauth2Opt {
877 #[clap(name = "list")]
878 List,
880 #[clap(name = "get")]
881 Get(Named),
883 #[clap(name = "create")]
887 CreateBasic {
889 #[clap(name = "name")]
890 name: String,
891 #[clap(name = "displayname")]
892 displayname: String,
893 #[clap(name = "origin")]
894 origin: String,
895 },
896 #[clap(name = "create-public")]
897 CreatePublic {
903 #[clap(name = "name")]
904 name: String,
905 #[clap(name = "displayname")]
906 displayname: String,
907 #[clap(name = "origin")]
908 origin: String,
909 },
910 #[clap(name = "update-scope-map", visible_aliases=&["create-scope-map"])]
911 UpdateScopeMap(Oauth2CreateScopeMapOpt),
913 #[clap(name = "delete-scope-map")]
914 DeleteScopeMap(Oauth2DeleteScopeMapOpt),
916
917 #[clap(name = "update-sup-scope-map", visible_aliases=&["create-sup-scope-map"])]
918 UpdateSupScopeMap(Oauth2CreateScopeMapOpt),
920 #[clap(name = "delete-sup-scope-map")]
921 DeleteSupScopeMap(Oauth2DeleteScopeMapOpt),
923
924 #[clap(name = "update-claim-map", visible_aliases=&["create-claim-map"])]
925 UpdateClaimMap {
927 name: String,
928 claim_name: String,
929 group: String,
930 values: Vec<String>,
931 },
932 #[clap(name = "update-claim-map-join")]
933 UpdateClaimMapJoin {
934 name: String,
935 claim_name: String,
936 join: Oauth2ClaimMapJoin,
939 },
940 #[clap(name = "delete-claim-map")]
941 DeleteClaimMap {
943 name: String,
944 claim_name: String,
945 group: String,
946 },
947
948 #[clap(name = "reset-basic-secret")]
949 ResetSecrets(Named),
952 #[clap(name = "show-basic-secret")]
953 ShowBasicSecret(Named),
955 #[clap(name = "delete")]
956 Delete(Named),
958 #[clap(name = "set-displayname")]
960 SetDisplayname(Oauth2SetDisplayname),
961 #[clap(name = "set-name")]
965 SetName {
966 #[clap(flatten)]
967 nopt: Named,
968 #[clap(name = "newname")]
969 name: String,
970 },
971
972 #[clap(name = "set-landing-url")]
975 SetLandingUrl {
976 #[clap(flatten)]
977 nopt: Named,
978 #[clap(name = "landing-url")]
979 url: Url,
980 },
981 #[clap(name = "set-image")]
983 SetImage {
984 #[clap(flatten)]
985 nopt: Named,
986 #[clap(name = "file-path")]
987 path: PathBuf,
989 #[clap(name = "image-type")]
990 image_type: Option<ImageType>,
992 },
993 #[clap(name = "remove-image")]
995 RemoveImage(Named),
996
997 #[clap(name = "add-redirect-url")]
1001 AddOrigin {
1002 name: String,
1003 #[clap(name = "url")]
1004 origin: Url,
1005 },
1006
1007 #[clap(name = "remove-redirect-url")]
1009 RemoveOrigin {
1010 name: String,
1011 #[clap(name = "url")]
1012 origin: Url,
1013 },
1014 #[clap(name = "enable-pkce")]
1015 EnablePkce(Named),
1017 #[clap(name = "warning-insecure-client-disable-pkce")]
1020 DisablePkce(Named),
1021 #[clap(name = "warning-enable-legacy-crypto")]
1022 EnableLegacyCrypto(Named),
1026 #[clap(name = "disable-legacy-crypto")]
1028 DisableLegacyCrypto(Named),
1029 #[clap(name = "enable-strict-redirect-url")]
1033 EnableStrictRedirectUri { name: String },
1034 #[clap(name = "disable-strict-redirect-url")]
1035 DisableStrictRedirectUri { name: String },
1036 #[clap(name = "enable-localhost-redirects")]
1037 EnablePublicLocalhost { name: String },
1039 #[clap(name = "disable-localhost-redirects")]
1041 DisablePublicLocalhost { name: String },
1042 #[clap(name = "prefer-short-username")]
1044 PreferShortUsername(Named),
1045 #[clap(name = "prefer-spn-username")]
1047 PreferSPNUsername(Named),
1048 #[cfg(feature = "dev-oauth2-device-flow")]
1049 DeviceFlowEnable(Named),
1051 #[cfg(feature = "dev-oauth2-device-flow")]
1052 DeviceFlowDisable(Named),
1054 #[clap(name = "rotate-cryptographic-keys")]
1060 RotateCryptographicKeys {
1061 name: String,
1062 #[clap(value_parser = parse_rfc3339)]
1063 rotate_at: OffsetDateTime,
1064 },
1065 #[clap(name = "revoke-cryptographic-key")]
1069 RevokeCryptographicKey { name: String, key_id: String },
1070 #[clap(name = "disable-consent-prompt")]
1074 DisableConsentPrompt(Named),
1075 #[clap(name = "enable-consent-prompt")]
1077 EnableConsentPrompt(Named),
1078}
1079
1080#[derive(Args, Debug, Clone)]
1081pub struct OptSetDomainDisplayname {
1082 #[clap(name = "new-display-name")]
1083 new_display_name: String,
1084}
1085
1086#[derive(Debug, Subcommand, Clone)]
1087pub enum PwBadlistOpt {
1088 #[clap[name = "show"]]
1089 Show,
1091 #[clap[name = "upload"]]
1092 Upload {
1096 #[clap(value_parser, required = true, num_args(1..))]
1097 paths: Vec<PathBuf>,
1098 #[clap(short = 'n', long)]
1100 dryrun: bool,
1101 },
1102 #[clap[name = "remove", hide = true]]
1103 Remove {
1106 #[clap(value_parser, required = true, num_args(1..))]
1107 paths: Vec<PathBuf>,
1108 },
1109}
1110
1111#[derive(Debug, Subcommand, Clone)]
1112pub enum DeniedNamesOpt {
1113 #[clap[name = "show"]]
1114 Show,
1116 #[clap[name = "append"]]
1117 Append {
1118 #[clap(value_parser, required = true, num_args(1..))]
1119 names: Vec<String>,
1120 },
1121 #[clap[name = "remove"]]
1122 Remove {
1124 #[clap(value_parser, required = true, num_args(1..))]
1125 names: Vec<String>,
1126 },
1127}
1128
1129#[derive(Debug, Subcommand, Clone)]
1130pub enum DomainOpt {
1131 #[clap[name = "set-displayname"]]
1132 SetDisplayname(OptSetDomainDisplayname),
1134 #[clap[name = "set-ldap-queryable-attrs"]]
1136 SetLdapMaxQueryableAttrs {
1137 #[clap(name = "maximum-queryable-attrs")]
1138 new_max_queryable_attrs: usize,
1139 },
1140 #[clap[name = "set-ldap-basedn"]]
1141 SetLdapBasedn {
1146 #[clap(name = "new-basedn")]
1147 new_basedn: String,
1148 },
1149 SetLdapAllowUnixPasswordBind {
1152 #[clap(name = "allow", action = clap::ArgAction::Set)]
1153 enable: bool,
1154 },
1155 SetAllowEasterEggs {
1159 #[clap(name = "allow", action = clap::ArgAction::Set)]
1160 enable: bool,
1161 },
1162 #[clap(name = "show")]
1163 Show,
1165 #[clap(name = "revoke-key")]
1166 RevokeKey { key_id: String },
1169 #[clap(name = "set-image")]
1171 SetImage {
1172 #[clap(name = "file-path")]
1173 path: PathBuf,
1174 #[clap(name = "image-type")]
1175 image_type: Option<ImageType>,
1176 },
1177 #[clap(name = "remove-image")]
1179 RemoveImage,
1180}
1181
1182#[derive(Debug, Subcommand, Clone)]
1183pub enum MessageOpt {
1184 #[clap(name = "list")]
1185 List,
1187
1188 #[clap(name = "get")]
1189 Get {
1191 message_id: Uuid
1192 },
1193
1194 #[clap(name = "mark-as-sent")]
1195 MarkAsSent {
1198 message_id: Uuid
1199 },
1200
1201 #[clap(name = "send-test-message")]
1202 SendTestMessage {
1203 to: String,
1205 }
1206}
1207
1208#[derive(Debug, Subcommand, Clone)]
1209pub enum SynchOpt {
1210 #[clap(name = "list")]
1211 List,
1213 #[clap(name = "get")]
1214 Get(Named),
1216 #[clap(name = "set-credential-portal")]
1217 SetCredentialPortal {
1220 #[clap()]
1221 account_id: String,
1222
1223 #[clap(name = "url")]
1224 url: Option<Url>,
1225 },
1226 #[clap(name = "create")]
1228 Create {
1229 #[clap()]
1230 account_id: String,
1231
1232 #[clap(name = "description",
1233 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1234 description: Option<String>,
1235 },
1236 #[clap(name = "generate-token")]
1238 GenerateToken {
1239 #[clap()]
1240 account_id: String,
1241 #[clap()]
1242 label: String,
1243 },
1244 #[clap(name = "destroy-token")]
1246 DestroyToken {
1247 #[clap()]
1248 account_id: String,
1249 },
1250 #[clap(name = "set-yield-attributes")]
1254 SetYieldAttributes {
1255 #[clap()]
1256 account_id: String,
1257
1258 #[clap(name = "attributes")]
1259 attrs: Vec<String>,
1260 },
1261 #[clap(name = "force-refresh")]
1265 ForceRefresh {
1266 #[clap()]
1267 account_id: String,
1268 },
1269 #[clap(name = "finalise")]
1275 Finalise {
1276 #[clap()]
1277 account_id: String,
1278 },
1279 #[clap(name = "terminate")]
1285 Terminate {
1286 #[clap()]
1287 account_id: String,
1288 },
1289}
1290
1291#[derive(Debug, Subcommand, Clone)]
1292pub enum AuthSessionExpiryOpt {
1293 #[clap[name = "get"]]
1294 Get,
1296 #[clap[name = "set"]]
1297 Set {
1299 #[clap(name = "expiry")]
1300 expiry: u32,
1301 },
1302}
1303
1304#[derive(Debug, Subcommand, Clone)]
1305pub enum PrivilegedSessionExpiryOpt {
1306 #[clap[name = "get"]]
1307 Get,
1309 #[clap[name = "set"]]
1310 Set {
1312 #[clap(name = "expiry")]
1313 expiry: u32,
1314 },
1315}
1316
1317#[derive(Args, Debug, Clone)]
1318pub struct ApiSchemaDownloadOpt {
1319 #[clap(name = "filename", env, default_value = "./kanidm-openapi.json")]
1321 filename: PathBuf,
1322 #[clap(short, long, env)]
1324 force: bool,
1325}
1326
1327#[derive(Debug, Subcommand, Clone)]
1328pub enum ApiOpt {
1329 #[clap(name = "download-schema")]
1331 DownloadSchema(ApiSchemaDownloadOpt),
1332}
1333
1334#[derive(Debug, Subcommand, Clone)]
1335pub enum SchemaClassOpt {
1336 List,
1338 Search {
1339 query: String,
1340 },
1341}
1342
1343#[derive(Debug, Subcommand, Clone)]
1344pub enum SchemaAttrOpt {
1345 List,
1347 Search {
1348 query: String,
1349 },
1350}
1351
1352#[derive(Debug, Subcommand, Clone)]
1353pub enum SchemaOpt {
1354 #[clap(name = "class")]
1356 Class {
1357 #[clap(subcommand)]
1358 commands: SchemaClassOpt,
1359 },
1360 #[clap(name = "attribute", visible_alias = "attr")]
1362 Attribute {
1363 #[clap(subcommand)]
1364 commands: SchemaAttrOpt,
1365 },
1366}
1367
1368#[derive(Debug, Subcommand, Clone)]
1369pub enum SystemOpt {
1370 #[clap(name = "pw-badlist")]
1371 PwBadlist {
1373 #[clap(subcommand)]
1374 commands: PwBadlistOpt,
1375 },
1376 #[clap(name = "denied-names")]
1377 DeniedNames {
1379 #[clap(subcommand)]
1380 commands: DeniedNamesOpt,
1381 },
1382 #[clap(name = "oauth2")]
1383 Oauth2 {
1385 #[clap(subcommand)]
1386 commands: Oauth2Opt,
1387 },
1388 #[clap(name = "domain")]
1389 Domain {
1391 #[clap(subcommand)]
1392 commands: DomainOpt,
1393 },
1394 #[clap(name = "sync")]
1395 Synch {
1397 #[clap(subcommand)]
1398 commands: SynchOpt,
1399 },
1400 #[clap(name = "message-queue", alias = "message")]
1401 Message {
1403 #[clap(subcommand)]
1404 commands: MessageOpt,
1405 },
1406 #[clap(name = "api")]
1407 Api {
1409 #[clap(subcommand)]
1410 commands: ApiOpt,
1411 },
1412}
1413
1414#[derive(Debug, Subcommand, Clone)]
1415#[clap(about = "Kanidm Client Utility")]
1416pub enum KanidmClientOpt {
1417 Login(LoginOpt),
1419 Reauth,
1421 Logout(LogoutOpt),
1423 Session {
1425 #[clap(subcommand)]
1426 commands: SessionOpt,
1427 },
1428 #[clap(name = "self")]
1429 CSelf {
1431 #[clap(subcommand)]
1432 commands: SelfOpt,
1433 },
1434 Person {
1436 #[clap(subcommand)]
1437 commands: PersonOpt,
1438 },
1439 Group {
1441 #[clap(subcommand)]
1442 commands: GroupOpt,
1443 },
1444 #[clap(name = "service-account")]
1446 ServiceAccount {
1447 #[clap(subcommand)]
1448 commands: ServiceAccountOpt,
1449 },
1450 #[clap(name = "graph")]
1452 Graph(GraphCommonOpt),
1453
1454 #[clap(hide = true)]
1456 Schema {
1457 #[clap(subcommand)]
1458 commands: SchemaOpt,
1459 },
1460
1461 System {
1463 #[clap(subcommand)]
1464 commands: SystemOpt,
1465 },
1466 #[clap(name = "recycle-bin")]
1467 Recycle {
1469 #[clap(subcommand)]
1470 commands: RecycleOpt,
1471 },
1472 #[clap(hide = true)]
1474 Raw {
1475 #[clap(subcommand)]
1476 commands: RawOpt,
1477 },
1478 Version,
1480}
1481
1482#[derive(Debug, clap::Parser, Clone)]
1483#[clap(about = "Kanidm Client Utility")]
1484pub struct KanidmClientParser {
1485 #[clap(subcommand)]
1486 pub commands: KanidmClientOpt,
1487
1488 #[clap(short, long, env = "KANIDM_DEBUG", global = true)]
1490 pub debug: bool,
1491 #[clap(short = 'I', long = "instance", env = "KANIDM_INSTANCE", global = true,
1493 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1494 pub instance: Option<String>,
1495 #[clap(short = 'H', long = "url", env = "KANIDM_URL", global = true,
1497 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1498 pub addr: Option<String>,
1499 #[clap(
1501 short = 'D',
1502 long = "name",
1503 env = "KANIDM_NAME",
1504 value_parser = clap::builder::NonEmptyStringValueParser::new(), global=true
1505 )]
1506 pub username: Option<String>,
1507 #[clap(
1509 value_parser,
1510 short = 'C',
1511 long = "ca",
1512 env = "KANIDM_CA_PATH",
1513 global = true
1514 )]
1515 pub ca_path: Option<PathBuf>,
1516 #[clap(short, long = "output", env = "KANIDM_OUTPUT", global = true, default_value=OutputMode::default())]
1518 output_mode: OutputMode,
1519 #[clap(
1521 long = "skip-hostname-verification",
1522 env = "KANIDM_SKIP_HOSTNAME_VERIFICATION",
1523 default_value_t = false,
1524 global = true
1525 )]
1526 skip_hostname_verification: bool,
1527 #[clap(
1529 long = "accept-invalid-certs",
1530 env = "KANIDM_ACCEPT_INVALID_CERTS",
1531 default_value_t = false,
1532 global = true
1533 )]
1534 accept_invalid_certs: bool,
1535 #[clap(
1537 short,
1538 long,
1539 env = "KANIDM_TOKEN_CACHE_PATH",
1540 hide = true,
1541 default_value = None,
1542 global=true,
1543 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1544 token_cache_path: Option<String>,
1545
1546 #[clap(
1547 short,
1548 long,
1549 env = "KANIDM_PASSWORD",
1550 hide = true,
1551 global = true,
1552 value_parser = clap::builder::NonEmptyStringValueParser::new())]
1553 password: Option<String>,
1555}
1556
1557impl KanidmClientParser {
1558 fn get_token_cache_path(&self) -> String {
1559 match self.token_cache_path.clone() {
1560 None => CLIENT_TOKEN_CACHE.to_string(),
1561 Some(val) => val.clone(),
1562 }
1563 }
1564}