kanidm_cli/opt/
kanidm.rs

1use clap::{builder::PossibleValue, Args, Subcommand, ValueEnum};
2use kanidm_proto::internal::ImageType;
3use std::fmt;
4
5#[derive(Debug, Args)]
6pub struct Named {
7    pub name: String,
8    #[clap(flatten)]
9    pub copt: CommonOpt,
10}
11
12#[derive(Debug, Args)]
13pub struct DebugOpt {
14    /// Enable debugging of the kanidm tool
15    #[clap(short, long, env = "KANIDM_DEBUG")]
16    pub debug: bool,
17}
18
19#[derive(Debug, Clone, Copy)]
20/// The CLI output mode, either text or json, falls back to text if you ask for something other than text/json
21pub enum OutputMode {
22    Text,
23    Json,
24}
25
26impl std::str::FromStr for OutputMode {
27    type Err = String;
28    fn from_str(s: &str) -> Result<OutputMode, std::string::String> {
29        match s.to_lowercase().as_str() {
30            "text" => Ok(OutputMode::Text),
31            "json" => Ok(OutputMode::Json),
32            _ => Ok(OutputMode::Text),
33        }
34    }
35}
36
37impl OutputMode {
38    pub fn print_message<T>(self, input: T)
39    where
40        T: serde::Serialize + fmt::Debug + fmt::Display,
41    {
42        match self {
43            OutputMode::Json => {
44                println!(
45                    "{}",
46                    serde_json::to_string(&input).unwrap_or(format!("{:?}", input))
47                );
48            }
49            OutputMode::Text => {
50                println!("{}", input);
51            }
52        }
53    }
54}
55
56#[derive(Debug, Args, Clone)]
57pub struct CommonOpt {
58    /// Enable debugging of the kanidm tool
59    #[clap(short, long, env = "KANIDM_DEBUG")]
60    pub debug: bool,
61    /// Select the instance name you wish to connect to
62    #[clap(short = 'I', long = "instance", env = "KANIDM_INSTANCE",
63    value_parser = clap::builder::NonEmptyStringValueParser::new())]
64    pub instance: Option<String>,
65    /// The URL of the kanidm instance
66    #[clap(short = 'H', long = "url", env = "KANIDM_URL",
67    value_parser = clap::builder::NonEmptyStringValueParser::new())]
68    pub addr: Option<String>,
69    /// User which will initiate requests
70    #[clap(
71        short = 'D',
72        long = "name",
73        env = "KANIDM_NAME",
74        value_parser = clap::builder::NonEmptyStringValueParser::new()
75    )]
76    pub username: Option<String>,
77    /// Path to a CA certificate file
78    #[clap(value_parser, short = 'C', long = "ca", env = "KANIDM_CA_PATH")]
79    pub ca_path: Option<PathBuf>,
80    /// Log format (still in very early development)
81    #[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value = "text")]
82    output_mode: OutputMode,
83    /// Skip hostname verification
84    #[clap(
85        long = "skip-hostname-verification",
86        env = "KANIDM_SKIP_HOSTNAME_VERIFICATION",
87        default_value_t = false
88    )]
89    skip_hostname_verification: bool,
90    /// Path to a file to cache tokens in, defaults to ~/.cache/kanidm_tokens
91    #[clap(short, long, env = "KANIDM_TOKEN_CACHE_PATH", hide = true, default_value = None,
92    value_parser = clap::builder::NonEmptyStringValueParser::new())]
93    token_cache_path: Option<String>,
94}
95
96#[derive(Debug, Args)]
97pub struct GroupNamedMembers {
98    name: String,
99    #[clap(required = true, num_args(1..))]
100    members: Vec<String>,
101    #[clap(flatten)]
102    copt: CommonOpt,
103}
104
105#[derive(Debug, Args)]
106pub struct GroupPosixOpt {
107    name: String,
108    #[clap(long)]
109    gidnumber: Option<u32>,
110    #[clap(flatten)]
111    copt: CommonOpt,
112}
113
114#[derive(Debug, Subcommand)]
115pub enum GroupPosix {
116    /// Show details of a specific posix group
117    #[clap(name = "show")]
118    Show(Named),
119    /// Setup posix group properties, or alter them
120    #[clap(name = "set")]
121    Set(GroupPosixOpt),
122    /// Reset the gidnumber of this group to the generated default
123    #[clap(name = "reset-gidnumber")]
124    ResetGidnumber {
125        group_id: String,
126        #[clap(flatten)]
127        copt: CommonOpt,
128    },
129}
130
131#[derive(Debug, Clone, Copy, Eq, PartialEq)]
132pub enum AccountPolicyCredentialType {
133    Any,
134    Mfa,
135    Passkey,
136    AttestedPasskey,
137}
138
139impl AccountPolicyCredentialType {
140    pub fn as_str(&self) -> &'static str {
141        match self {
142            Self::Any => "any",
143            Self::Mfa => "mfa",
144            Self::Passkey => "passkey",
145            Self::AttestedPasskey => "attested_passkey",
146        }
147    }
148}
149
150impl ValueEnum for AccountPolicyCredentialType {
151    fn value_variants<'a>() -> &'a [Self] {
152        &[Self::Any, Self::Mfa, Self::Passkey, Self::AttestedPasskey]
153    }
154
155    fn to_possible_value(&self) -> Option<PossibleValue> {
156        Some(match self {
157            Self::Any => PossibleValue::new("any"),
158            Self::Mfa => PossibleValue::new("mfa"),
159            Self::Passkey => PossibleValue::new("passkey"),
160            Self::AttestedPasskey => PossibleValue::new("attested_passkey"),
161        })
162    }
163}
164
165#[derive(Debug, Subcommand)]
166pub enum GroupAccountPolicyOpt {
167    /// Enable account policy for this group
168    #[clap(name = "enable")]
169    Enable {
170        name: String,
171        #[clap(flatten)]
172        copt: CommonOpt,
173    },
174    /// Set the maximum time for session expiry in seconds.
175    #[clap(name = "auth-expiry")]
176    AuthSessionExpiry {
177        name: String,
178        expiry: u32,
179        #[clap(flatten)]
180        copt: CommonOpt,
181    },
182    /// Set the minimum credential class that members may authenticate with. Valid values
183    /// in order of weakest to strongest are: "any" "mfa" "passkey" "attested_passkey".
184    #[clap(name = "credential-type-minimum")]
185    CredentialTypeMinimum {
186        name: String,
187        #[clap(value_enum)]
188        value: AccountPolicyCredentialType,
189        #[clap(flatten)]
190        copt: CommonOpt,
191    },
192    /// Set the minimum character length of passwords for accounts.
193    #[clap(name = "password-minimum-length")]
194    PasswordMinimumLength {
195        name: String,
196        length: u32,
197        #[clap(flatten)]
198        copt: CommonOpt,
199    },
200
201
202    /// Set the maximum time for privilege session expiry in seconds.
203    #[clap(name = "privilege-expiry")]
204    PrivilegedSessionExpiry {
205        name: String,
206        expiry: u32,
207        #[clap(flatten)]
208        copt: CommonOpt,
209    },
210
211
212    /// The WebAuthn attestation CA list that should be enforced
213    /// on members of this group. Prevents use of passkeys that are
214    /// not in this list. To create this list, use `fido-mds-tool`
215    /// from <https://crates.io/crates/fido-mds-tool>
216    #[clap(name = "webauthn-attestation-ca-list")]
217    WebauthnAttestationCaList {
218        name: String,
219        attestation_ca_list_json_file: PathBuf,
220        #[clap(flatten)]
221        copt: CommonOpt,
222    },
223
224    /// Sets the maximum number of entries that may be returned in a
225    /// search operation.
226    #[clap(name = "limit-search-max-results")]
227    LimitSearchMaxResults {
228        name: String,
229        maximum: u32,
230        #[clap(flatten)]
231        copt: CommonOpt,
232    },
233    /// Sets the maximum number of entries that are examined during
234    /// a partially indexed search. This does not affect fully
235    /// indexed searches. If in doubt, set this to 1.5x limit-search-max-results
236    #[clap(name = "limit-search-max-filter-test")]
237    LimitSearchMaxFilterTest {
238        name: String,
239        maximum: u32,
240        #[clap(flatten)]
241        copt: CommonOpt,
242    },
243    /// Sets whether during login the primary password can be used
244    /// as a fallback if no posix password has been defined
245    #[clap(name = "allow-primary-cred-fallback")]
246    AllowPrimaryCredFallback {
247        name: String,
248        #[clap(name = "allow", action = clap::ArgAction::Set)]
249        allow: bool,
250        #[clap(flatten)]
251        copt: CommonOpt,
252    },
253
254    /// Reset the maximum time for session expiry to its default value
255    #[clap(name = "reset-auth-expiry")]
256    ResetAuthSessionExpiry {
257        name: String,
258        #[clap(flatten)]
259        copt: CommonOpt,
260    },
261    /// Reset the minimum character length of passwords to its default value.
262    #[clap(name = "reset-password-minimum-length")]
263    ResetPasswordMinimumLength {
264        name: String,
265        #[clap(flatten)]
266        copt: CommonOpt,
267    },
268    /// Reset the maximum time for privilege session expiry to its default value.
269    #[clap(name = "reset-privilege-expiry")]
270    ResetPrivilegedSessionExpiry {
271        name: String,
272        #[clap(flatten)]
273        copt: CommonOpt,
274    },
275    /// Reset the WebAuthn attestation CA list to its default value
276    /// allowing any passkey to be used by members of this group.
277    #[clap(name = "reset-webauthn-attestation-ca-list")]
278    ResetWebauthnAttestationCaList {
279        name: String,
280        #[clap(flatten)]
281        copt: CommonOpt,
282    },
283    /// Reset the searche maxmium results limit to its default value.
284    #[clap(name = "reset-limit-search-max-results")]
285    ResetLimitSearchMaxResults {
286        name: String,
287        #[clap(flatten)]
288        copt: CommonOpt,
289    },
290    /// Reset the max filter test limit to its default value.
291    #[clap(name = "reset-limit-search-max-filter-test")]
292    ResetLimitSearchMaxFilterTest {
293        name: String,
294        #[clap(flatten)]
295        copt: CommonOpt,
296    },
297
298}
299
300#[derive(Debug, Subcommand)]
301pub enum GroupOpt {
302    /// List all groups
303    #[clap(name = "list")]
304    List(CommonOpt),
305    /// View a specific group
306    #[clap(name = "get")]
307    Get(Named),
308    /// Search a group by name
309    #[clap(name = "search")]
310    Search {
311        /// The name of the group
312        name: String,
313        #[clap(flatten)]
314        copt: CommonOpt,
315    },
316    /// Create a new group
317    #[clap(name = "create")]
318    Create {
319        /// The name of the group
320        name: String,
321        /// Optional name/spn of a group that have entry manager rights over this group.
322        #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
323        entry_managed_by: Option<String>,
324        #[clap(flatten)]
325        copt: CommonOpt,
326    },
327    /// Delete a group
328    #[clap(name = "delete")]
329    Delete(Named),
330    /// List the members of a group
331    #[clap(name = "list-members")]
332    ListMembers(Named),
333    /// Set the exact list of members that this group should contain, removing any not listed in the
334    /// set operation.
335    #[clap(name = "set-members")]
336    SetMembers(GroupNamedMembers),
337    /// Set the exact list of mail addresses that this group is associated with. The first
338    /// mail address in the list is the `primary` and the remainder are aliases. Setting
339    /// an empty list will clear the mail attribute.
340    #[clap(name = "set-mail")]
341    SetMail {
342        #[clap(flatten)]
343        copt: CommonOpt,
344        name: String,
345        mail: Vec<String>,
346    },
347    /// Set a new entry-managed-by for this group.
348    #[clap(name = "set-entry-manager")]
349    SetEntryManagedBy {
350        /// The name of the group
351        name: String,
352        /// Optional name/spn of a group that have entry manager rights over this group.
353        entry_managed_by: String,
354        #[clap(flatten)]
355        copt: CommonOpt,
356    },
357    /// Rename an existing group
358    #[clap(name = "rename")]
359    Rename {
360        /// The name of the group
361        name: String,
362        /// The new name of the group
363        new_name: String,
364        #[clap(flatten)]
365        copt: CommonOpt,
366    },
367    /// Delete all members of a group.
368    #[clap(name = "purge-members")]
369    PurgeMembers(Named),
370    /// Add new members to a group
371    #[clap(name = "add-members")]
372    AddMembers(GroupNamedMembers),
373    /// Remove the named members from this group
374    #[clap(name = "remove-members")]
375    RemoveMembers(GroupNamedMembers),
376    /// Manage posix extensions for this group allowing groups to be used on unix/linux systems
377    #[clap(name = "posix")]
378    Posix {
379        #[clap(subcommand)]
380        commands: GroupPosix,
381    },
382    /// Manage the policies that apply to members of this group.
383    #[clap(name = "account-policy")]
384    AccountPolicy {
385        #[clap(subcommand)]
386        commands: GroupAccountPolicyOpt,
387    },
388}
389
390#[derive(Clone, Debug, ValueEnum)]
391pub enum GraphType {
392    Graphviz,
393    Mermaid,
394    MermaidElk,
395}
396
397#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, ValueEnum)]
398pub enum ObjectType {
399    Group,
400    BuiltinGroup,
401    ServiceAccount,
402    Person,
403}
404
405#[derive(Debug, Args)]
406pub struct GraphCommonOpt {
407    #[arg(value_enum)]
408    pub graph_type: GraphType,
409    #[clap()]
410    pub filter: Vec<ObjectType>,
411    #[clap(flatten)]
412    pub copt: CommonOpt,
413}
414
415#[derive(Debug, Args)]
416pub struct AccountCommonOpt {
417    #[clap()]
418    account_id: String,
419}
420
421#[derive(Debug, Args)]
422pub struct AccountNamedOpt {
423    #[clap(flatten)]
424    aopts: AccountCommonOpt,
425    #[clap(flatten)]
426    copt: CommonOpt,
427}
428
429#[derive(Debug, Args)]
430pub struct AccountNamedExpireDateTimeOpt {
431    #[clap(flatten)]
432    aopts: AccountCommonOpt,
433    #[clap(flatten)]
434    copt: CommonOpt,
435    #[clap(name = "datetime", verbatim_doc_comment)]
436    /// This accepts multiple options:
437    /// - An RFC3339 time of the format "YYYY-MM-DDTHH:MM:SS+TZ", "2020-09-25T11:22:02+10:00"
438    /// - One of "any", "clear" or "never" to remove account expiry.
439    /// - "epoch" to set the expiry to the UNIX epoch
440    /// - "now" to expire immediately (this will affect authentication with Kanidm, but external systems may not be aware of the change until next time it's validated, typically ~15 minutes)
441    datetime: String,
442}
443
444#[derive(Debug, Args)]
445pub struct AccountNamedValidDateTimeOpt {
446    #[clap(flatten)]
447    aopts: AccountCommonOpt,
448    #[clap(flatten)]
449    copt: CommonOpt,
450    #[clap(name = "datetime")]
451    /// An rfc3339 time of the format "YYYY-MM-DDTHH:MM:SS+TZ", "2020-09-25T11:22:02+10:00"
452    /// or the word "any", "clear" to remove valid from enforcement.
453    datetime: String,
454}
455
456#[derive(Debug, Args)]
457pub struct AccountNamedTagOpt {
458    #[clap(flatten)]
459    aopts: AccountCommonOpt,
460    #[clap(flatten)]
461    copt: CommonOpt,
462    #[clap(name = "tag")]
463    tag: String,
464}
465
466#[derive(Debug, Args)]
467pub struct AccountNamedTagPkOpt {
468    #[clap(flatten)]
469    aopts: AccountCommonOpt,
470    #[clap(flatten)]
471    copt: CommonOpt,
472    #[clap(name = "tag")]
473    tag: String,
474    #[clap(name = "pubkey")]
475    pubkey: String,
476}
477
478#[derive(Debug, Args)]
479/// Command-line options for account credential use-reset-token
480pub struct UseResetTokenOpt {
481    #[clap(flatten)]
482    copt: CommonOpt,
483    #[clap(name = "token")]
484    token: String,
485}
486
487#[derive(Debug, Args)]
488pub struct AccountCreateOpt {
489    #[clap(flatten)]
490    aopts: AccountCommonOpt,
491    #[clap(name = "display-name")]
492    display_name: String,
493    #[clap(flatten)]
494    copt: CommonOpt,
495}
496
497#[derive(Debug, Subcommand)]
498pub enum AccountCredential {
499    /// Show the status of this accounts credentials.
500    #[clap(name = "status")]
501    Status(AccountNamedOpt),
502    /// Interactively update/change the credentials for an account
503    #[clap(name = "update")]
504    Update(AccountNamedOpt),
505    /// Using a reset token, interactively reset credentials for a user
506    #[clap(name = "use-reset-token")]
507    UseResetToken(UseResetTokenOpt),
508    /// Create a reset token that can be given to another person so they can
509    /// recover or reset their account credentials.
510    #[clap(name = "create-reset-token")]
511    CreateResetToken {
512        #[clap(flatten)]
513        aopts: AccountCommonOpt,
514        #[clap(flatten)]
515        copt: CommonOpt,
516        /// Optionally set how many seconds the reset token should be valid for.
517        /// Default: 3600 seconds
518        ttl: Option<u32>,
519    },
520}
521
522/// RADIUS secret management
523#[derive(Debug, Subcommand)]
524pub enum AccountRadius {
525    /// Show the RADIUS secret for a user.
526    #[clap(name = "show-secret")]
527    Show(AccountNamedOpt),
528    /// Generate a randomized RADIUS secret for a user.
529    #[clap(name = "generate-secret")]
530    Generate(AccountNamedOpt),
531    #[clap(name = "delete-secret")]
532    /// Remove the configured RADIUS secret for the user.
533    DeleteSecret(AccountNamedOpt),
534}
535
536#[derive(Debug, Args)]
537pub struct AccountPosixOpt {
538    #[clap(flatten)]
539    aopts: AccountCommonOpt,
540    #[clap(long)]
541    gidnumber: Option<u32>,
542    #[clap(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
543    /// Set the user's login shell
544    shell: Option<String>,
545    #[clap(flatten)]
546    copt: CommonOpt,
547}
548
549#[derive(Debug, Subcommand)]
550pub enum PersonPosix {
551    #[clap(name = "show")]
552    Show(AccountNamedOpt),
553    #[clap(name = "set")]
554    Set(AccountPosixOpt),
555    #[clap(name = "set-password")]
556    SetPassword(AccountNamedOpt),
557    /// Reset the gidnumber of this person to the generated default
558    #[clap(name = "reset-gidnumber")]
559    ResetGidnumber {
560        account_id: String,
561        #[clap(flatten)]
562        copt: CommonOpt,
563    },
564}
565
566#[derive(Debug, Subcommand)]
567pub enum ServiceAccountPosix {
568    #[clap(name = "show")]
569    Show(AccountNamedOpt),
570    #[clap(name = "set")]
571    Set(AccountPosixOpt),
572    /// Reset the gidnumber of this service account to the generated default
573    #[clap(name = "reset-gidnumber")]
574    ResetGidnumber {
575        account_id: String,
576        #[clap(flatten)]
577        copt: CommonOpt,
578    },
579}
580
581#[derive(Debug, Args)]
582pub struct PersonUpdateOpt {
583    #[clap(flatten)]
584    aopts: AccountCommonOpt,
585    #[clap(long, short, help = "Set the legal name for the person.",
586    value_parser = clap::builder::NonEmptyStringValueParser::new())]
587    legalname: Option<String>,
588    #[clap(long, short, help = "Set the account name for the person.",
589    value_parser = clap::builder::NonEmptyStringValueParser::new())]
590    newname: Option<String>,
591    #[clap(long, short = 'i', help = "Set the display name for the person.",
592    value_parser = clap::builder::NonEmptyStringValueParser::new())]
593    displayname: Option<String>,
594    #[clap(
595        long,
596        short,
597        help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
598    )]
599    mail: Option<Vec<String>>,
600    #[clap(flatten)]
601    copt: CommonOpt,
602}
603
604#[derive(Debug, Subcommand)]
605pub enum AccountSsh {
606    #[clap(name = "list-publickeys")]
607    List(AccountNamedOpt),
608    #[clap(name = "add-publickey")]
609    Add(AccountNamedTagPkOpt),
610    #[clap(name = "delete-publickey")]
611    Delete(AccountNamedTagOpt),
612}
613
614#[derive(Debug, Subcommand)]
615pub enum AccountValidity {
616    /// Show an accounts validity window
617    #[clap(name = "show")]
618    Show(AccountNamedOpt),
619    /// Set an accounts expiry time
620    #[clap(name = "expire-at")]
621    ExpireAt(AccountNamedExpireDateTimeOpt),
622    /// Set an account valid from time
623    #[clap(name = "begin-from")]
624    BeginFrom(AccountNamedValidDateTimeOpt),
625}
626
627#[derive(Debug, Subcommand)]
628pub enum AccountCertificate {
629    #[clap(name = "status")]
630    Status {
631        account_id: String,
632        #[clap(flatten)]
633        copt: CommonOpt,
634    },
635    #[clap(name = "create")]
636    Create {
637        account_id: String,
638        certificate_path: PathBuf,
639        #[clap(flatten)]
640        copt: CommonOpt,
641    },
642}
643
644#[derive(Debug, Subcommand)]
645pub enum AccountUserAuthToken {
646    /// Show the status of logged in sessions associated to this account.
647    #[clap(name = "status")]
648    Status(AccountNamedOpt),
649    /// Destroy / revoke a session for this account. Access to the
650    /// session (user auth token) is NOT required, only the uuid of the session.
651    #[clap(name = "destroy")]
652    Destroy {
653        #[clap(flatten)]
654        aopts: AccountCommonOpt,
655        #[clap(flatten)]
656        copt: CommonOpt,
657        /// The UUID of the token to destroy.
658        #[clap(name = "session-id")]
659        session_id: Uuid,
660    },
661}
662
663#[derive(Debug, Subcommand)]
664pub enum PersonOpt {
665    /// Manage the credentials this person uses for authentication
666    #[clap(name = "credential")]
667    Credential {
668        #[clap(subcommand)]
669        commands: AccountCredential,
670    },
671    /// Manage radius access for this person
672    #[clap(name = "radius")]
673    Radius {
674        #[clap(subcommand)]
675        commands: AccountRadius,
676    },
677    /// Manage posix extensions for this person allowing access to unix/linux systems
678    #[clap(name = "posix")]
679    Posix {
680        #[clap(subcommand)]
681        commands: PersonPosix,
682    },
683    /// Manage sessions (user auth tokens) associated to this person.
684    #[clap(name = "session")]
685    Session {
686        #[clap(subcommand)]
687        commands: AccountUserAuthToken,
688    },
689    /// Manage ssh public key's associated to this person
690    #[clap(name = "ssh")]
691    Ssh {
692        #[clap(subcommand)]
693        commands: AccountSsh,
694    },
695    /// List all persons
696    #[clap(name = "list")]
697    List(CommonOpt),
698    /// View a specific person
699    #[clap(name = "get")]
700    Get(AccountNamedOpt),
701    /// Search persons by name
702    #[clap(name = "search")]
703    Search {
704        account_id: String,
705        #[clap(flatten)]
706        copt: CommonOpt,
707    },
708    /// Update a specific person's attributes
709    #[clap(name = "update")]
710    Update(PersonUpdateOpt),
711    /// Create a new person's account
712    #[clap(name = "create")]
713    Create(AccountCreateOpt),
714    /// Delete a person's account
715    #[clap(name = "delete")]
716    Delete(AccountNamedOpt),
717    /// Manage a person's account validity, such as expiry time (account lock/unlock)
718    #[clap(name = "validity")]
719    Validity {
720        #[clap(subcommand)]
721        commands: AccountValidity,
722    },
723    #[clap(name = "certificate", hide = true)]
724    Certificate {
725        #[clap(subcommand)]
726        commands: AccountCertificate,
727    },
728}
729
730#[derive(Debug, Subcommand)]
731pub enum ServiceAccountCredential {
732    /// Show the status of this accounts password
733    #[clap(name = "status")]
734    Status(AccountNamedOpt),
735    /// Reset and generate a new service account password. This password can NOT
736    /// be used with the LDAP interface.
737    #[clap(name = "generate")]
738    GeneratePw(AccountNamedOpt),
739}
740
741#[derive(Debug, Subcommand)]
742pub enum ServiceAccountApiToken {
743    /// Show the status of api tokens associated to this service account.
744    #[clap(name = "status")]
745    Status(AccountNamedOpt),
746    /// Generate a new api token for this service account.
747    #[clap(name = "generate")]
748    Generate {
749        #[clap(flatten)]
750        aopts: AccountCommonOpt,
751        #[clap(flatten)]
752        copt: CommonOpt,
753        /// A string describing the token. This is not used to identify the token, it is only
754        /// for human description of the tokens purpose.
755        #[clap(name = "label")]
756        label: String,
757        #[clap(name = "expiry")]
758        /// An optional rfc3339 time of the format "YYYY-MM-DDTHH:MM:SS+TZ", "2020-09-25T11:22:02+10:00".
759        /// After this time the api token will no longer be valid.
760        #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new())]
761        expiry: Option<String>,
762        #[clap(long = "rw")]
763        read_write: bool,
764    },
765    /// Destroy / revoke an api token from this service account. Access to the
766    /// token is NOT required, only the tag/uuid of the token.
767    #[clap(name = "destroy")]
768    Destroy {
769        #[clap(flatten)]
770        aopts: AccountCommonOpt,
771        #[clap(flatten)]
772        copt: CommonOpt,
773        /// The UUID of the token to destroy.
774        #[clap(name = "token-id")]
775        token_id: Uuid,
776    },
777}
778
779#[derive(Debug, Args)]
780pub struct ServiceAccountUpdateOpt {
781    #[clap(flatten)]
782    aopts: AccountCommonOpt,
783    #[clap(long, short, help = "Set the account name for the service account.",
784    value_parser = clap::builder::NonEmptyStringValueParser::new())]
785    newname: Option<String>,
786    #[clap(
787        long,
788        short = 'i',
789        help = "Set the display name for the service account.",
790        value_parser = clap::builder::NonEmptyStringValueParser::new()
791    )]
792    displayname: Option<String>,
793    #[clap(
794        long,
795        short = 'e',
796        help = "Set the entry manager for the service account.",
797        value_parser = clap::builder::NonEmptyStringValueParser::new()
798    )]
799    entry_managed_by: Option<String>,
800    #[clap(
801        long,
802        short,
803        help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'"
804    )]
805    mail: Option<Vec<String>>,
806    #[clap(flatten)]
807    copt: CommonOpt,
808}
809
810#[derive(Debug, Subcommand)]
811pub enum ServiceAccountOpt {
812    /// Manage the generated password of this service account.
813    #[clap(name = "credential")]
814    Credential {
815        #[clap(subcommand)]
816        commands: ServiceAccountCredential,
817    },
818    /// Manage api tokens associated to this service account.
819    #[clap(name = "api-token")]
820    ApiToken {
821        #[clap(subcommand)]
822        commands: ServiceAccountApiToken,
823    },
824    /// Manage posix extensions for this service account allowing access to unix/linux systems
825    #[clap(name = "posix")]
826    Posix {
827        #[clap(subcommand)]
828        commands: ServiceAccountPosix,
829    },
830    /// Manage sessions (user auth tokens) associated to this service account.
831    #[clap(name = "session")]
832    Session {
833        #[clap(subcommand)]
834        commands: AccountUserAuthToken,
835    },
836    /// Manage ssh public key's associated to this person
837    #[clap(name = "ssh")]
838    Ssh {
839        #[clap(subcommand)]
840        commands: AccountSsh,
841    },
842    /// List all service accounts
843    #[clap(name = "list")]
844    List(CommonOpt),
845    /// View a specific service account
846    #[clap(name = "get")]
847    Get(AccountNamedOpt),
848    /// Create a new service account
849    #[clap(name = "create")]
850    Create {
851        #[clap(flatten)]
852        aopts: AccountCommonOpt,
853        #[clap(name = "display-name")]
854        display_name: String,
855        #[clap(name = "entry-managed-by")]
856        entry_managed_by: String,
857        #[clap(flatten)]
858        copt: CommonOpt,
859    },
860    /// Update a specific service account's attributes
861    #[clap(name = "update")]
862    Update(ServiceAccountUpdateOpt),
863    /// Delete a service account
864    #[clap(name = "delete")]
865    Delete(AccountNamedOpt),
866    /// Manage a service account validity, such as expiry time (account lock/unlock)
867    #[clap(name = "validity")]
868    Validity {
869        #[clap(subcommand)]
870        commands: AccountValidity,
871    },
872    /// (Deprecated - due for removal in v1.1.0-15) - Convert a service account into a person. This is used during the alpha.9
873    /// to alpha.10 migration to "fix up" accounts that were not previously marked
874    /// as persons.
875    #[clap(name = "into-person")]
876    IntoPerson(AccountNamedOpt),
877}
878
879#[derive(Debug, Subcommand)]
880pub enum RecycleOpt {
881    #[clap(name = "list")]
882    /// List objects that are in the recycle bin
883    List(CommonOpt),
884    #[clap(name = "get")]
885    /// Display an object from the recycle bin
886    Get(Named),
887    #[clap(name = "revive")]
888    /// Revive a recycled object into a live (accessible) state - this is the opposite of "delete"
889    Revive(Named),
890}
891
892#[derive(Debug, Args)]
893pub struct LoginOpt {
894    #[clap(flatten)]
895    copt: CommonOpt,
896    #[clap(short, long, env = "KANIDM_PASSWORD", hide = true,
897    value_parser = clap::builder::NonEmptyStringValueParser::new())]
898    /// Supply a password to the login option
899    password: Option<String>,
900}
901
902#[derive(Debug, Args)]
903pub struct ReauthOpt {
904    #[clap(flatten)]
905    copt: CommonOpt,
906}
907
908#[derive(Debug, Args)]
909pub struct LogoutOpt {
910    #[clap(flatten)]
911    copt: CommonOpt,
912    #[clap(short, long)]
913    /// Do not send a logout request to the server - only remove the session token locally.
914    local_only: bool,
915}
916
917#[derive(Debug, Subcommand)]
918pub enum SessionOpt {
919    #[clap(name = "list")]
920    /// List current active sessions
921    List(CommonOpt),
922    #[clap(name = "cleanup")]
923    /// Remove sessions that have expired or are invalid.
924    Cleanup(CommonOpt),
925}
926
927#[derive(Debug, Args)]
928pub struct FilterOpt {
929    #[clap()]
930    filter: String,
931    #[clap(flatten)]
932    commonopts: CommonOpt,
933}
934
935#[derive(Debug, Args)]
936pub struct CreateOpt {
937    #[clap(value_parser)]
938    file: PathBuf,
939    #[clap(flatten)]
940    commonopts: CommonOpt,
941}
942
943#[derive(Debug, Args)]
944pub struct ModifyOpt {
945    #[clap(flatten)]
946    commonopts: CommonOpt,
947    #[clap()]
948    filter: String,
949    #[clap(value_parser)]
950    file: PathBuf,
951}
952
953#[derive(Debug, Subcommand)]
954pub enum RawOpt {
955    #[clap(name = "search")]
956    Search(FilterOpt),
957    #[clap(name = "create")]
958    Create(CreateOpt),
959    #[clap(name = "modify")]
960    Modify(ModifyOpt),
961    #[clap(name = "delete")]
962    Delete(FilterOpt),
963}
964
965#[derive(Debug, Subcommand)]
966pub enum SelfOpt {
967    /// Use the identify user feature
968    #[clap(name = "identify-user")]
969    IdentifyUser(CommonOpt),
970    /// Show the current authenticated user's identity
971    Whoami(CommonOpt),
972}
973
974#[derive(Debug, Args)]
975pub struct Oauth2SetDisplayname {
976    #[clap(flatten)]
977    nopt: Named,
978    #[clap(name = "displayname")]
979    displayname: String,
980}
981
982#[derive(Debug, Args)]
983pub struct Oauth2SetImplicitScopes {
984    #[clap(flatten)]
985    nopt: Named,
986    #[clap(name = "scopes")]
987    scopes: Vec<String>,
988}
989
990#[derive(Debug, Args)]
991pub struct Oauth2CreateScopeMapOpt {
992    #[clap(flatten)]
993    nopt: Named,
994    #[clap(name = "group")]
995    group: String,
996    #[clap(name = "scopes", required = true)]
997    scopes: Vec<String>,
998}
999
1000#[derive(Debug, Args)]
1001pub struct Oauth2DeleteScopeMapOpt {
1002    #[clap(flatten)]
1003    nopt: Named,
1004    #[clap(name = "group")]
1005    group: String,
1006}
1007
1008#[derive(Debug, Clone, Copy, Eq, PartialEq)]
1009pub enum Oauth2ClaimMapJoin {
1010    Csv,
1011    Ssv,
1012    Array,
1013}
1014
1015impl Oauth2ClaimMapJoin {
1016    pub fn as_str(&self) -> &'static str {
1017        match self {
1018            Self::Csv => "csv",
1019            Self::Ssv => "ssv",
1020            Self::Array => "array",
1021        }
1022    }
1023}
1024
1025impl ValueEnum for Oauth2ClaimMapJoin {
1026    fn value_variants<'a>() -> &'a [Self] {
1027        &[Self::Csv, Self::Ssv, Self::Array]
1028    }
1029
1030    fn to_possible_value(&self) -> Option<PossibleValue> {
1031        Some(match self {
1032            Self::Csv => PossibleValue::new("csv"),
1033            Self::Ssv => PossibleValue::new("ssv"),
1034            Self::Array => PossibleValue::new("array"),
1035        })
1036    }
1037}
1038
1039#[derive(Debug, Subcommand)]
1040pub enum Oauth2Opt {
1041    #[clap(name = "list")]
1042    /// List all configured oauth2 clients
1043    List(CommonOpt),
1044    #[clap(name = "get")]
1045    /// Display a selected oauth2 client
1046    Get(Named),
1047    // #[clap(name = "set")]
1048    // /// Set options for a selected oauth2 client
1049    // Set(),
1050    #[clap(name = "create")]
1051    /// Create a new oauth2 confidential client that is protected by basic auth.
1052    CreateBasic {
1053        #[clap(name = "name")]
1054        name: String,
1055        #[clap(name = "displayname")]
1056        displayname: String,
1057        #[clap(name = "origin")]
1058        origin: String,
1059        #[clap(flatten)]
1060        copt: CommonOpt,
1061    },
1062    #[clap(name = "create-public")]
1063    /// Create a new OAuth2 public client that requires PKCE. You should prefer
1064    /// using confidential client types if possible over public ones.
1065    ///
1066    /// Public clients have many limitations and can not access all API's of OAuth2. For
1067    /// example rfc7662 token introspection requires client authentication.
1068    CreatePublic {
1069        #[clap(name = "name")]
1070        name: String,
1071        #[clap(name = "displayname")]
1072        displayname: String,
1073        #[clap(name = "origin")]
1074        origin: String,
1075        #[clap(flatten)]
1076        copt: CommonOpt,
1077    },
1078    #[clap(name = "update-scope-map", visible_aliases=&["create-scope-map"])]
1079    /// Update or add a new mapping from a group to scopes that it provides to members
1080    UpdateScopeMap(Oauth2CreateScopeMapOpt),
1081    #[clap(name = "delete-scope-map")]
1082    /// Remove a mapping from groups to scopes
1083    DeleteScopeMap(Oauth2DeleteScopeMapOpt),
1084
1085    #[clap(name = "update-sup-scope-map", visible_aliases=&["create-sup-scope-map"])]
1086    /// Update or add a new mapping from a group to scopes that it provides to members
1087    UpdateSupScopeMap(Oauth2CreateScopeMapOpt),
1088    #[clap(name = "delete-sup-scope-map")]
1089    /// Remove a mapping from groups to scopes
1090    DeleteSupScopeMap(Oauth2DeleteScopeMapOpt),
1091
1092    #[clap(name = "update-claim-map", visible_aliases=&["create-claim-map"])]
1093    /// Update or add a new mapping from a group to custom claims that it provides to members
1094    UpdateClaimMap {
1095        #[clap(flatten)]
1096        copt: CommonOpt,
1097        name: String,
1098        claim_name: String,
1099        group: String,
1100        values: Vec<String>,
1101    },
1102    #[clap(name = "update-claim-map-join")]
1103    UpdateClaimMapJoin {
1104        #[clap(flatten)]
1105        copt: CommonOpt,
1106        name: String,
1107        claim_name: String,
1108        /// The join strategy. Valid values are csv (comma separated value), ssv (space
1109        /// separated value) and array.
1110        join: Oauth2ClaimMapJoin,
1111    },
1112    #[clap(name = "delete-claim-map")]
1113    /// Remove a mapping from groups to a custom claim
1114    DeleteClaimMap {
1115        #[clap(flatten)]
1116        copt: CommonOpt,
1117        name: String,
1118        claim_name: String,
1119        group: String,
1120    },
1121
1122    #[clap(name = "reset-secrets")]
1123    /// Reset the secrets associated to this client
1124    ResetSecrets(Named),
1125    #[clap(name = "show-basic-secret")]
1126    /// Show the associated basic secret for this client
1127    ShowBasicSecret(Named),
1128    #[clap(name = "delete")]
1129    /// Delete a oauth2 client
1130    Delete(Named),
1131    /// Set a new display name for a client
1132    #[clap(name = "set-displayname")]
1133    SetDisplayname(Oauth2SetDisplayname),
1134    /// Set a new name for this client. You may need to update
1135    /// your integrated applications after this so that they continue to
1136    /// function correctly.
1137    #[clap(name = "set-name")]
1138    SetName {
1139        #[clap(flatten)]
1140        nopt: Named,
1141        #[clap(name = "newname")]
1142        name: String,
1143    },
1144
1145    /// The landing URL is the default origin of the OAuth2 client. Additionally, this landing
1146    /// URL is the target when Kanidm redirects the user from the apps listing page.
1147    #[clap(name = "set-landing-url")]
1148    SetLandingUrl {
1149        #[clap(flatten)]
1150        nopt: Named,
1151        #[clap(name = "landing-url")]
1152        url: Url,
1153    },
1154    /// The image presented on the Kanidm Apps Listing page for an OAuth2 resource server.
1155    #[clap(name = "set-image")]
1156    SetImage {
1157        #[clap(flatten)]
1158        nopt: Named,
1159        #[clap(name = "file-path")]
1160        /// A local file path to an image to use as the icon for this OAuth2 client.
1161        path: PathBuf,
1162        #[clap(name = "image-type")]
1163        /// The type of image being uploaded.
1164        image_type: Option<ImageType>,
1165    },
1166    /// Removes the custom image previously set.
1167    #[clap(name = "remove-image")]
1168    RemoveImage(Named),
1169
1170    /// Add a supplemental URL as a redirection target. For example a phone app
1171    /// may use a redirect URL such as `app://my-cool-app` to trigger a native
1172    /// redirection event out of a browser.
1173    #[clap(name = "add-redirect-url")]
1174    AddOrigin {
1175        name: String,
1176        #[clap(name = "url")]
1177        origin: Url,
1178        #[clap(flatten)]
1179        copt: CommonOpt,
1180    },
1181
1182    /// Remove a supplemental redirect URL from the OAuth2 client configuration.
1183    #[clap(name = "remove-redirect-url")]
1184    RemoveOrigin {
1185        name: String,
1186        #[clap(name = "url")]
1187        origin: Url,
1188        #[clap(flatten)]
1189        copt: CommonOpt,
1190    },
1191    #[clap(name = "enable-pkce")]
1192    /// Enable PKCE on this oauth2 client. This defaults to being enabled.
1193    EnablePkce(Named),
1194    /// Disable PKCE on this oauth2 client to work around insecure clients that
1195    /// may not support it. You should request the client to enable PKCE!
1196    #[clap(name = "warning-insecure-client-disable-pkce")]
1197    DisablePkce(Named),
1198    #[clap(name = "warning-enable-legacy-crypto")]
1199    /// Enable legacy signing crypto on this oauth2 client. This defaults to being disabled.
1200    /// You only need to enable this for openid clients that do not support modern cryptographic
1201    /// operations.
1202    EnableLegacyCrypto(Named),
1203    /// Disable legacy signing crypto on this oauth2 client. This is the default.
1204    #[clap(name = "disable-legacy-crypto")]
1205    DisableLegacyCrypto(Named),
1206    /// Enable strict validation of redirect URLs. Previously redirect URLs only
1207    /// validated the origin of the URL matched. When enabled, redirect URLs must
1208    /// match exactly.
1209    #[clap(name = "enable-strict-redirect-url")]
1210    EnableStrictRedirectUri {
1211        name: String,
1212        #[clap(flatten)]
1213        copt: CommonOpt,
1214    },
1215    #[clap(name = "disable-strict-redirect-url")]
1216    DisableStrictRedirectUri {
1217        name: String,
1218        #[clap(flatten)]
1219        copt: CommonOpt,
1220    },
1221    #[clap(name = "enable-localhost-redirects")]
1222    /// Allow public clients to redirect to localhost.
1223    EnablePublicLocalhost {
1224        #[clap(flatten)]
1225        copt: CommonOpt,
1226        name: String,
1227    },
1228    /// Disable public clients redirecting to localhost.
1229    #[clap(name = "disable-localhost-redirects")]
1230    DisablePublicLocalhost {
1231        #[clap(flatten)]
1232        copt: CommonOpt,
1233        name: String,
1234    },
1235    /// Use the 'name' attribute instead of 'spn' for the preferred_username
1236    #[clap(name = "prefer-short-username")]
1237    PreferShortUsername(Named),
1238    /// Use the 'spn' attribute instead of 'name' for the preferred_username
1239    #[clap(name = "prefer-spn-username")]
1240    PreferSPNUsername(Named),
1241    #[cfg(feature = "dev-oauth2-device-flow")]
1242    /// Enable OAuth2 Device Flow authentication
1243    DeviceFlowEnable(Named),
1244    #[cfg(feature = "dev-oauth2-device-flow")]
1245    /// Disable OAuth2 Device Flow authentication
1246    DeviceFlowDisable(Named),
1247}
1248
1249#[derive(Args, Debug)]
1250pub struct OptSetDomainDisplayname {
1251    #[clap(flatten)]
1252    copt: CommonOpt,
1253    #[clap(name = "new-display-name")]
1254    new_display_name: String,
1255}
1256
1257#[derive(Debug, Subcommand)]
1258pub enum PwBadlistOpt {
1259    #[clap[name = "show"]]
1260    /// Show information about this system's password badlist
1261    Show(CommonOpt),
1262    #[clap[name = "upload"]]
1263    /// Upload an extra badlist, appending to the currently configured one.
1264    /// This badlist will be preprocessed to remove items that are already
1265    /// caught by "zxcvbn" at the configured level.
1266    Upload {
1267        #[clap(flatten)]
1268        copt: CommonOpt,
1269        #[clap(value_parser, required = true, num_args(1..))]
1270        paths: Vec<PathBuf>,
1271        /// Perform a dry run and display the list that would have been uploaded instead.
1272        #[clap(short = 'n', long)]
1273        dryrun: bool,
1274    },
1275    #[clap[name = "remove", hide = true]]
1276    /// Remove the content of these lists if present in the configured
1277    /// badlist.
1278    Remove {
1279        #[clap(flatten)]
1280        copt: CommonOpt,
1281        #[clap(value_parser, required = true, num_args(1..))]
1282        paths: Vec<PathBuf>,
1283    },
1284}
1285
1286#[derive(Debug, Subcommand)]
1287pub enum DeniedNamesOpt {
1288    #[clap[name = "show"]]
1289    /// Show information about this system's denied name list
1290    Show {
1291        #[clap(flatten)]
1292        copt: CommonOpt,
1293    },
1294    #[clap[name = "append"]]
1295    Append {
1296        #[clap(flatten)]
1297        copt: CommonOpt,
1298        #[clap(value_parser, required = true, num_args(1..))]
1299        names: Vec<String>,
1300    },
1301    #[clap[name = "remove"]]
1302    /// Remove a name from the denied name list.
1303    Remove {
1304        #[clap(flatten)]
1305        copt: CommonOpt,
1306        #[clap(value_parser, required = true, num_args(1..))]
1307        names: Vec<String>,
1308    },
1309}
1310
1311#[derive(Debug, Subcommand)]
1312pub enum DomainOpt {
1313    #[clap[name = "set-displayname"]]
1314    /// Set the domain display name
1315    SetDisplayname(OptSetDomainDisplayname),
1316    #[clap[name = "set-ldap-basedn"]]
1317    /// Change the basedn of this server. Takes effect after a server restart.
1318    /// Examples are `o=organisation` or `dc=domain,dc=name`. Must be a valid ldap
1319    /// dn containing only alphanumerics, and dn components must be org (o), domain (dc) or
1320    /// orgunit (ou).
1321    SetLdapBasedn {
1322        #[clap(flatten)]
1323        copt: CommonOpt,
1324        #[clap(name = "new-basedn")]
1325        new_basedn: String,
1326    },
1327    /// Enable or disable unix passwords being used to bind via LDAP. Unless you have a specific
1328    /// requirement for this, you should disable this.
1329    SetLdapAllowUnixPasswordBind {
1330        #[clap(flatten)]
1331        copt: CommonOpt,
1332        #[clap(name = "allow", action = clap::ArgAction::Set)]
1333        enable: bool,
1334    },
1335    /// Enable or disable easter eggs in the server. This includes seasonal icons, kanidm
1336    /// birthday surprises and other fun components. Defaults to false for production releases
1337    /// and true in development builds.
1338    SetAllowEasterEggs {
1339        #[clap(flatten)]
1340        copt: CommonOpt,
1341        #[clap(name = "allow", action = clap::ArgAction::Set)]
1342        enable: bool,
1343    },
1344    #[clap(name = "show")]
1345    /// Show information about this system's domain
1346    Show(CommonOpt),
1347    #[clap(name = "revoke-key")]
1348    /// Revoke a key by its key id. This will cause all user sessions to be
1349    /// invalidated (logged out).
1350    RevokeKey {
1351        #[clap(flatten)]
1352        copt: CommonOpt,
1353        key_id: String,
1354    },
1355    /// The image presented as the instance logo
1356    #[clap(name = "set-image")]
1357    SetImage {
1358        #[clap(flatten)]
1359        copt: CommonOpt,
1360        #[clap(name = "file-path")]
1361        path: PathBuf,
1362        #[clap(name = "image-type")]
1363        image_type: Option<ImageType>,
1364    },
1365    /// The remove the current instance logo, reverting to the default.
1366    #[clap(name = "remove-image")]
1367    RemoveImage {
1368        #[clap(flatten)]
1369        copt: CommonOpt,
1370    },
1371}
1372
1373#[derive(Debug, Subcommand)]
1374pub enum SynchOpt {
1375    #[clap(name = "list")]
1376    /// List all configured IDM sync accounts
1377    List(CommonOpt),
1378    #[clap(name = "get")]
1379    /// Display a selected IDM sync account
1380    Get(Named),
1381    #[clap(name = "set-credential-portal")]
1382    /// Set the url to the external credential portal. This will be displayed to synced users
1383    /// so that they can be redirected to update their credentials on this portal.
1384    SetCredentialPortal {
1385        #[clap()]
1386        account_id: String,
1387        #[clap(flatten)]
1388        copt: CommonOpt,
1389        #[clap(name = "url")]
1390        url: Option<Url>,
1391    },
1392    /// Create a new IDM sync account
1393    #[clap(name = "create")]
1394    Create {
1395        #[clap()]
1396        account_id: String,
1397        #[clap(flatten)]
1398        copt: CommonOpt,
1399        #[clap(name = "description",
1400        value_parser = clap::builder::NonEmptyStringValueParser::new())]
1401        description: Option<String>,
1402    },
1403    /// Generate a bearer token for an IDM sync account
1404    #[clap(name = "generate-token")]
1405    GenerateToken {
1406        #[clap()]
1407        account_id: String,
1408        #[clap()]
1409        label: String,
1410        #[clap(flatten)]
1411        copt: CommonOpt,
1412    },
1413    /// Destroy (revoke) the bearer token for an IDM sync account
1414    #[clap(name = "destroy-token")]
1415    DestroyToken {
1416        #[clap()]
1417        account_id: String,
1418        #[clap(flatten)]
1419        copt: CommonOpt,
1420    },
1421    /// Set the list of attributes that have their authority yielded from the sync account
1422    /// and are allowed to be modified by kanidm and users. Any attributes not listed in
1423    /// in this command will have their authority returned to the sync account.
1424    #[clap(name = "set-yield-attributes")]
1425    SetYieldAttributes {
1426        #[clap()]
1427        account_id: String,
1428        #[clap(flatten)]
1429        copt: CommonOpt,
1430        #[clap(name = "attributes")]
1431        attrs: Vec<String>,
1432    },
1433    /// Reset the sync cookie of this connector, so that on the next operation of the sync tool
1434    /// a full refresh of the provider is requested. Kanidm attributes that have been granted
1435    /// authority will *not* be lost or deleted.
1436    #[clap(name = "force-refresh")]
1437    ForceRefresh {
1438        #[clap()]
1439        account_id: String,
1440        #[clap(flatten)]
1441        copt: CommonOpt,
1442    },
1443    /// Finalise and remove this sync account. This will transfer all synchronised entries into
1444    /// the authority of Kanidm. This signals the end of a migration from an external IDM into
1445    /// Kanidm. ⚠️  This action can NOT be undone. Once complete, it is most likely
1446    /// that attempting to recreate a sync account from the same IDM will fail due to conflicting
1447    /// entries that Kanidm now owns.
1448    #[clap(name = "finalise")]
1449    Finalise {
1450        #[clap()]
1451        account_id: String,
1452        #[clap(flatten)]
1453        copt: CommonOpt,
1454    },
1455    /// Terminate and remove this sync account. This will DELETE all entries that were imported
1456    /// from the external IDM source. ⚠️  This action can NOT be undone, and will require you to
1457    /// recreate the sync account if you
1458    /// wish to re-import data. Recreating the sync account may fail until the recycle bin and
1459    /// and tombstones are purged.
1460    #[clap(name = "terminate")]
1461    Terminate {
1462        #[clap()]
1463        account_id: String,
1464        #[clap(flatten)]
1465        copt: CommonOpt,
1466    },
1467}
1468
1469#[derive(Debug, Subcommand)]
1470pub enum AuthSessionExpiryOpt {
1471    #[clap[name = "get"]]
1472    /// Show information about this system auth session expiry
1473    Get(CommonOpt),
1474    #[clap[name = "set"]]
1475    /// Sets the system auth session expiry in seconds
1476    Set {
1477        #[clap(flatten)]
1478        copt: CommonOpt,
1479        #[clap(name = "expiry")]
1480        expiry: u32,
1481    },
1482}
1483
1484#[derive(Debug, Subcommand)]
1485pub enum PrivilegedSessionExpiryOpt {
1486    #[clap[name = "get"]]
1487    /// Show information about this system privileged session expiry
1488    Get(CommonOpt),
1489    #[clap[name = "set"]]
1490    /// Sets the system auth privilege session expiry in seconds
1491    Set {
1492        #[clap(flatten)]
1493        copt: CommonOpt,
1494        #[clap(name = "expiry")]
1495        expiry: u32,
1496    },
1497}
1498
1499#[derive(Args, Debug)]
1500pub struct ApiSchemaDownloadOpt {
1501    #[clap(flatten)]
1502    copt: CommonOpt,
1503    /// Where to put the file, defaults to ./kanidm-openapi.json
1504    #[clap(name = "filename", env, default_value = "./kanidm-openapi.json")]
1505    filename: PathBuf,
1506    /// Force overwriting the file if it exists
1507    #[clap(short, long, env)]
1508    force: bool,
1509}
1510
1511#[derive(Debug, Subcommand)]
1512pub enum ApiOpt {
1513    /// Download the OpenAPI schema file
1514    #[clap(name = "download-schema")]
1515    DownloadSchema(ApiSchemaDownloadOpt),
1516}
1517
1518#[derive(Debug, Subcommand)]
1519pub enum SystemOpt {
1520    #[clap(name = "pw-badlist")]
1521    /// Configure and manage the password badlist entry
1522    PwBadlist {
1523        #[clap(subcommand)]
1524        commands: PwBadlistOpt,
1525    },
1526    #[clap(name = "denied-names")]
1527    /// Configure and manage denied names
1528    DeniedNames {
1529        #[clap(subcommand)]
1530        commands: DeniedNamesOpt,
1531    },
1532    #[clap(name = "oauth2")]
1533    /// Configure and display oauth2/oidc client configuration
1534    Oauth2 {
1535        #[clap(subcommand)]
1536        commands: Oauth2Opt,
1537    },
1538    #[clap(name = "domain")]
1539    /// Configure and display domain configuration
1540    Domain {
1541        #[clap(subcommand)]
1542        commands: DomainOpt,
1543    },
1544    #[clap(name = "sync")]
1545    /// Configure synchronisation from an external IDM system
1546    Synch {
1547        #[clap(subcommand)]
1548        commands: SynchOpt,
1549    },
1550    #[clap(name = "api")]
1551    /// API related things
1552    Api {
1553        #[clap(subcommand)]
1554        commands: ApiOpt,
1555    },
1556}
1557
1558#[derive(Debug, Subcommand)]
1559#[clap(about = "Kanidm Client Utility")]
1560pub enum KanidmClientOpt {
1561    /// Login to an account to use with future cli operations
1562    Login(LoginOpt),
1563    /// Reauthenticate to access privileged functions of this account for a short period.
1564    Reauth(ReauthOpt),
1565    /// Logout of an active cli session
1566    Logout(LogoutOpt),
1567    /// Manage active cli sessions
1568    Session {
1569        #[clap(subcommand)]
1570        commands: SessionOpt,
1571    },
1572    #[clap(name = "self")]
1573    /// Actions for the current authenticated account
1574    CSelf {
1575        #[clap(subcommand)]
1576        commands: SelfOpt,
1577    },
1578    /// Actions to manage and view person (user) accounts
1579    Person {
1580        #[clap(subcommand)]
1581        commands: PersonOpt,
1582    },
1583    /// Actions to manage groups
1584    Group {
1585        #[clap(subcommand)]
1586        commands: GroupOpt,
1587    },
1588    /// Actions to manage and view service accounts
1589    #[clap(name = "service-account")]
1590    ServiceAccount {
1591        #[clap(subcommand)]
1592        commands: ServiceAccountOpt,
1593    },
1594    /// Prints graphviz dot file of all groups
1595    #[clap(name = "graph")]
1596    Graph(GraphCommonOpt),
1597    /// System configuration operations
1598    System {
1599        #[clap(subcommand)]
1600        commands: SystemOpt,
1601    },
1602    #[clap(name = "recycle-bin")]
1603    /// Recycle Bin operations
1604    Recycle {
1605        #[clap(subcommand)]
1606        commands: RecycleOpt,
1607    },
1608    /// Unsafe - low level, raw database queries and operations.
1609    #[clap(hide = true)]
1610    Raw {
1611        #[clap(subcommand)]
1612        commands: RawOpt,
1613    },
1614    /// Print the program version and exit
1615    Version {},
1616}
1617
1618#[derive(Debug, clap::Parser)]
1619#[clap(about = "Kanidm Client Utility")]
1620pub struct KanidmClientParser {
1621    #[clap(subcommand)]
1622    pub commands: KanidmClientOpt,
1623}