kanidm_proto/internal/
mod.rs

1//! Kanidm internal elements
2//!
3//! Items defined in this module *may* change between releases without notice.
4
5use crate::constants::{
6    CONTENT_TYPE_GIF, CONTENT_TYPE_JPG, CONTENT_TYPE_PNG, CONTENT_TYPE_SVG, CONTENT_TYPE_WEBP,
7};
8use clap::ValueEnum;
9use serde::{Deserialize, Serialize};
10use std::fmt;
11use std::str::FromStr;
12use url::Url;
13use utoipa::ToSchema;
14use uuid::Uuid;
15
16use num_enum::TryFromPrimitive;
17
18mod credupdate;
19mod error;
20mod raw;
21mod token;
22
23pub use self::credupdate::*;
24pub use self::error::*;
25pub use self::raw::*;
26pub use self::token::*;
27
28pub const COOKIE_AUTH_SESSION_ID: &str = "auth-session-id";
29pub const COOKIE_BEARER_TOKEN: &str = "bearer";
30pub const COOKIE_CU_SESSION_TOKEN: &str = "cu-session-token";
31pub const COOKIE_USERNAME: &str = "username";
32pub const COOKIE_OAUTH2_REQ: &str = "o2-authreq";
33
34#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
35/// This is a description of a linked or connected application for a user. This is
36/// used in the UI to render applications on the dashboard for a user to access.
37pub enum AppLink {
38    Oauth2 {
39        name: String,
40        display_name: String,
41        redirect_url: Url,
42        // Whether this oauth2 resource has an image.
43        has_image: bool,
44    },
45}
46
47#[derive(
48    Debug, Serialize, Deserialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, ToSchema,
49)]
50#[serde(rename_all = "lowercase")]
51#[derive(TryFromPrimitive)]
52#[repr(u16)]
53pub enum UiHint {
54    ExperimentalFeatures = 0,
55    PosixAccount = 1,
56    CredentialUpdate = 2,
57    SynchronisedAccount = 3,
58}
59
60impl fmt::Display for UiHint {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        match self {
63            UiHint::PosixAccount => write!(f, "PosixAccount"),
64            UiHint::CredentialUpdate => write!(f, "CredentialUpdate"),
65            UiHint::ExperimentalFeatures => write!(f, "ExperimentalFeatures"),
66            UiHint::SynchronisedAccount => write!(f, "SynchronisedAccount"),
67        }
68    }
69}
70
71impl FromStr for UiHint {
72    type Err = ();
73
74    fn from_str(s: &str) -> Result<Self, Self::Err> {
75        match s {
76            "CredentialUpdate" => Ok(UiHint::CredentialUpdate),
77            "PosixAccount" => Ok(UiHint::PosixAccount),
78            "ExperimentalFeatures" => Ok(UiHint::ExperimentalFeatures),
79            "SynchronisedAccount" => Ok(UiHint::SynchronisedAccount),
80            _ => Err(()),
81        }
82    }
83}
84
85// State machine states and transitions for the identity verification system feature!
86#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, ToSchema)]
87pub enum IdentifyUserRequest {
88    Start,
89    SubmitCode { other_totp: u32 },
90    DisplayCode,
91}
92
93#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, ToSchema)]
94pub enum IdentifyUserResponse {
95    IdentityVerificationUnavailable,
96    IdentityVerificationAvailable,
97    ProvideCode { step: u32, totp: u32 },
98    WaitForCode,
99    Success,
100    CodeFailure,
101    InvalidUserId,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Ord, PartialOrd, ValueEnum)]
105#[serde(rename_all = "lowercase")]
106pub enum ImageType {
107    Png,
108    Jpg,
109    Gif,
110    Svg,
111    Webp,
112}
113
114impl TryFrom<&str> for ImageType {
115    type Error = &'static str;
116    /// ```
117    /// use kanidm_proto::internal::ImageType;
118    /// assert_eq!(ImageType::try_from("png").unwrap(), ImageType::Png);
119    /// assert!(ImageType::try_from("krabs").is_err());
120    /// ```
121    fn try_from(value: &str) -> Result<Self, &'static str> {
122        #[allow(clippy::panic)]
123        match value {
124            "png" => Ok(Self::Png),
125            "jpg" => Ok(Self::Jpg),
126            "jpeg" => Ok(Self::Jpg), // ugh I hate this
127            "gif" => Ok(Self::Gif),
128            "svg" => Ok(Self::Svg),
129            "webp" => Ok(Self::Webp),
130            _ => Err("Invalid image type!"),
131        }
132    }
133}
134
135impl ImageType {
136    pub fn try_from_content_type(content_type: &str) -> Result<Self, String> {
137        let content_type = content_type.to_lowercase();
138        match content_type.as_str() {
139            CONTENT_TYPE_JPG => Ok(ImageType::Jpg),
140            CONTENT_TYPE_PNG => Ok(ImageType::Png),
141            CONTENT_TYPE_GIF => Ok(ImageType::Gif),
142            CONTENT_TYPE_WEBP => Ok(ImageType::Webp),
143            CONTENT_TYPE_SVG => Ok(ImageType::Svg),
144            _ => Err(format!("Invalid content type: {}", content_type)),
145        }
146    }
147
148    pub fn as_content_type_str(&self) -> &'static str {
149        match &self {
150            ImageType::Jpg => CONTENT_TYPE_JPG,
151            ImageType::Png => CONTENT_TYPE_PNG,
152            ImageType::Gif => CONTENT_TYPE_GIF,
153            ImageType::Webp => CONTENT_TYPE_WEBP,
154            ImageType::Svg => CONTENT_TYPE_SVG,
155        }
156    }
157}
158
159#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, PartialOrd, Ord, Hash)]
160pub struct ImageValue {
161    pub filename: String,
162    pub filetype: ImageType,
163    pub contents: Vec<u8>,
164}
165
166impl TryFrom<&str> for ImageValue {
167    type Error = String;
168    fn try_from(s: &str) -> Result<Self, String> {
169        serde_json::from_str(s)
170            .map_err(|e| format!("Failed to decode ImageValue from {} - {:?}", s, e))
171    }
172}
173
174impl ImageValue {
175    pub fn new(filename: String, filetype: ImageType, contents: Vec<u8>) -> Self {
176        Self {
177            filename,
178            filetype,
179            contents,
180        }
181    }
182}
183
184#[repr(u32)]
185#[derive(Debug, Copy, Clone, Deserialize, Default, Eq, PartialEq)]
186#[serde(rename_all = "lowercase")]
187/// Filesystem type object, used for tuning database parameters.
188pub enum FsType {
189    Zfs = 65536,
190    #[default]
191    #[serde(other)]
192    /// The default setting, if not set to "zfs"
193    Generic = 4096,
194}
195
196impl FsType {
197    pub fn checkpoint_pages(&self) -> u32 {
198        match self {
199            FsType::Generic => 2048,
200            FsType::Zfs => 256,
201        }
202    }
203}
204
205impl TryFrom<&str> for FsType {
206    type Error = ();
207
208    fn try_from(s: &str) -> Result<Self, Self::Error> {
209        match s {
210            "zfs" => Ok(FsType::Zfs),
211            "generic" => Ok(FsType::Generic),
212            _ => Err(()),
213        }
214    }
215}
216
217#[derive(Debug, Serialize, Deserialize, Clone, Copy, ToSchema)]
218pub enum Oauth2ClaimMapJoin {
219    #[serde(rename = "csv")]
220    Csv,
221    #[serde(rename = "ssv")]
222    Ssv,
223    #[serde(rename = "array")]
224    Array,
225}
226
227#[derive(Debug, Serialize, Deserialize, Clone)]
228pub struct DomainInfo {
229    pub name: String,
230    pub displayname: String,
231    pub uuid: Uuid,
232    pub level: u32,
233}
234
235#[derive(Debug, Serialize, Deserialize, Clone)]
236pub struct DomainUpgradeCheckReport {
237    pub name: String,
238    pub uuid: Uuid,
239    pub current_level: u32,
240    pub upgrade_level: u32,
241    pub report_items: Vec<DomainUpgradeCheckItem>,
242}
243
244#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
245pub enum DomainUpgradeCheckStatus {
246    Pass6To7Gidnumber,
247    Fail6To7Gidnumber,
248
249    Pass7To8SecurityKeys,
250    Fail7To8SecurityKeys,
251
252    Pass7To8Oauth2StrictRedirectUri,
253    Fail7To8Oauth2StrictRedirectUri,
254}
255
256#[derive(Debug, Serialize, Deserialize, Clone)]
257pub struct DomainUpgradeCheckItem {
258    pub from_level: u32,
259    pub to_level: u32,
260    pub status: DomainUpgradeCheckStatus,
261    pub affected_entries: Vec<String>,
262}
263
264#[test]
265fn test_fstype_deser() {
266    assert_eq!(FsType::try_from("zfs"), Ok(FsType::Zfs));
267    assert_eq!(FsType::try_from("generic"), Ok(FsType::Generic));
268    assert_eq!(FsType::try_from(" "), Err(()));
269    assert_eq!(FsType::try_from("crab🦀"), Err(()));
270}