Skip to main content

kaniop_operator/
crd.rs

1use kaniop_k8s_util::types::get_first_cloned;
2
3use kanidm_proto::{
4    constants::{ATTR_GIDNUMBER, ATTR_LOGINSHELL},
5    v1::Entry,
6};
7
8#[cfg(feature = "schemars")]
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12/// Configuration for automatic secret rotation.
13///
14/// When enabled, the operator will automatically rotate secrets based on the configured period.
15/// This is useful for security compliance and reducing the impact of credential leakage.
16#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
17#[cfg_attr(feature = "schemars", derive(JsonSchema))]
18#[serde(rename_all = "camelCase")]
19pub struct SecretRotation {
20    /// Enable automatic secret rotation. Defaults to false (opt-in).
21    #[serde(default)]
22    pub enabled: bool,
23
24    /// Rotation period in days. Secrets will be rotated when they are older than this period.
25    /// Defaults to 90 days.
26    #[serde(default = "default_rotation_period_days")]
27    pub period_days: u32,
28}
29
30impl Default for SecretRotation {
31    fn default() -> Self {
32        Self {
33            enabled: false,
34            period_days: default_rotation_period_days(),
35        }
36    }
37}
38
39/// Default rotation period of 90 days.
40///
41/// This value aligns with common security best practices and compliance frameworks
42/// (e.g., PCI-DSS, SOC 2) that recommend rotating credentials every 90 days.
43fn default_rotation_period_days() -> u32 {
44    90
45}
46
47/// Checks if a given value is equal to its type's default value.
48pub fn is_default<T: Default + PartialEq>(value: &T) -> bool {
49    #[cfg(feature = "examples-gen")]
50    {
51        // When generating examples, never skip fields so users can see all available options
52        let _ = value; // Suppress unused variable warning
53        false
54    }
55    #[cfg(not(feature = "examples-gen"))]
56    {
57        value == &T::default()
58    }
59}
60
61/// KanidmRef is a reference to a Kanidm object in the same cluster. It is used to specify where
62/// the object is stored.
63#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
64#[cfg_attr(feature = "schemars", derive(JsonSchema))]
65#[schemars(extend("x-kubernetes-validations" = [{"message": "Value is immutable", "rule": "self == oldSelf"}]))]
66#[serde(rename_all = "camelCase")]
67pub struct KanidmRef {
68    pub name: String,
69
70    /// For cross-namespace resources. Reference Kanidm namespace. If omitted, the namespace of the
71    /// resource will be used.
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub namespace: Option<String>,
74}
75
76/// Kanidm has features that enable its accounts and groups to be consumed on POSIX-like machines,
77/// such as Linux, FreeBSD or others. Both service accounts and person accounts can be used on POSIX
78/// systems.
79///
80/// The attributes defined here are set by the operator. If you want to manage those attributes
81/// from the database, do not set them here.
82/// Additionally, if you unset them here, they will be kept in the database.
83#[derive(Serialize, Deserialize, Clone, Debug, Default)]
84#[cfg_attr(feature = "schemars", derive(JsonSchema))]
85#[serde(rename_all = "camelCase")]
86pub struct KanidmAccountPosixAttributes {
87    /// The group ID number (GID) for the person account. In Kanidm there is no difference between
88    /// a UID and a GID number.
89    ///
90    /// If omitted, Kanidm will generate it automatically.
91    ///
92    /// More info:
93    /// https://kanidm.github.io/kanidm/stable/accounts/posix_accounts_and_groups.html#uid-and-gid-numbers
94    pub gidnumber: Option<u32>,
95    /// The login shell for the person account.
96    ///
97    /// This sets the default shell that will be used when the user logs in via SSH or other
98    /// mechanisms that require a shell. Common values include /bin/bash, /bin/zsh, /bin/sh.
99    pub loginshell: Option<String>,
100}
101
102impl PartialEq for KanidmAccountPosixAttributes {
103    /// Compare attributes defined in the first object with the second object values.
104    /// If the second object has more attributes defined, they will be ignored.
105    fn eq(&self, other: &Self) -> bool {
106        (self.gidnumber.is_none() || self.gidnumber == other.gidnumber)
107            && (self.loginshell.is_none() || self.loginshell == other.loginshell)
108    }
109}
110
111impl From<Entry> for KanidmAccountPosixAttributes {
112    fn from(entry: Entry) -> Self {
113        KanidmAccountPosixAttributes {
114            gidnumber: get_first_cloned(&entry, ATTR_GIDNUMBER).and_then(|s| s.parse::<u32>().ok()),
115            loginshell: get_first_cloned(&entry, ATTR_LOGINSHELL),
116        }
117    }
118}