kanidmd_lib/be/
dbvalue.rs

1use hashbrown::HashSet;
2use kanidm_proto::internal::ImageType;
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5use std::collections::{BTreeMap, BTreeSet};
6use std::fmt;
7use std::time::Duration;
8use url::Url;
9use uuid::Uuid;
10use webauthn_rs::prelude::{
11    AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
12    SecurityKey as SecurityKeyV4,
13};
14use webauthn_rs_core::proto::{COSEKey, UserVerificationPolicy};
15// Re-export this as though it was here.
16use crate::repl::cid::Cid;
17use crypto_glue::traits::Zeroizing;
18pub use kanidm_lib_crypto::DbPasswordV1;
19
20#[derive(Serialize, Deserialize, Debug, Ord, PartialOrd, PartialEq, Eq, Clone)]
21pub struct DbCidV1 {
22    #[serde(rename = "t")]
23    pub timestamp: Duration,
24    #[serde(rename = "s")]
25    pub server_id: Uuid,
26}
27
28impl From<Cid> for DbCidV1 {
29    fn from(Cid { s_uuid, ts }: Cid) -> Self {
30        DbCidV1 {
31            timestamp: ts,
32            server_id: s_uuid,
33        }
34    }
35}
36
37impl From<&Cid> for DbCidV1 {
38    fn from(&Cid { s_uuid, ts }: &Cid) -> Self {
39        DbCidV1 {
40            timestamp: ts,
41            server_id: s_uuid,
42        }
43    }
44}
45
46impl fmt::Display for DbCidV1 {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "{:032}-{}", self.timestamp.as_nanos(), self.server_id)
49    }
50}
51
52#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
53pub enum DbValueIntentTokenStateV1 {
54    #[serde(rename = "v")]
55    Valid {
56        max_ttl: Duration,
57        #[serde(default)]
58        ext_cred_portal_can_view: bool,
59        #[serde(default)]
60        primary_can_edit: bool,
61        #[serde(default)]
62        passkeys_can_edit: bool,
63        #[serde(default)]
64        attested_passkeys_can_edit: bool,
65        #[serde(default)]
66        unixcred_can_edit: bool,
67        #[serde(default)]
68        sshpubkey_can_edit: bool,
69    },
70    #[serde(rename = "p")]
71    InProgress {
72        max_ttl: Duration,
73        session_id: Uuid,
74        session_ttl: Duration,
75        #[serde(default)]
76        ext_cred_portal_can_view: bool,
77        #[serde(default)]
78        primary_can_edit: bool,
79        #[serde(default)]
80        passkeys_can_edit: bool,
81        #[serde(default)]
82        attested_passkeys_can_edit: bool,
83        #[serde(default)]
84        unixcred_can_edit: bool,
85        #[serde(default)]
86        sshpubkey_can_edit: bool,
87    },
88    #[serde(rename = "c")]
89    Consumed { max_ttl: Duration },
90}
91
92#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
93pub enum DbTotpAlgoV1 {
94    S1,
95    S256,
96    S512,
97}
98
99#[derive(Serialize, Deserialize, PartialEq, Eq)]
100pub struct DbTotpV1 {
101    #[serde(rename = "l")]
102    pub label: String,
103    #[serde(rename = "k")]
104    pub key: Vec<u8>,
105    #[serde(rename = "s")]
106    pub step: u64,
107    #[serde(rename = "a")]
108    pub algo: DbTotpAlgoV1,
109    #[serde(rename = "d", default)]
110    pub digits: Option<u8>,
111}
112
113impl std::fmt::Debug for DbTotpV1 {
114    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
115        f.debug_struct("DbTotpV1")
116            .field("label", &self.label)
117            .field("step", &self.step)
118            .field("algo", &self.algo)
119            .finish()
120    }
121}
122
123#[derive(Serialize, Deserialize, Debug)]
124pub struct DbWebauthnV1 {
125    #[serde(rename = "l")]
126    pub label: String,
127    #[serde(rename = "i")]
128    pub id: Vec<u8>,
129    #[serde(rename = "c")]
130    pub cred: COSEKey,
131    #[serde(rename = "t")]
132    pub counter: u32,
133    #[serde(rename = "v")]
134    pub verified: bool,
135    #[serde(rename = "p", default)]
136    pub registration_policy: UserVerificationPolicy,
137}
138
139#[derive(Serialize, Deserialize, PartialEq, Eq)]
140pub struct DbBackupCodeV1 {
141    pub code_set: HashSet<String>, // has to use std::HashSet for serde
142}
143
144impl std::fmt::Debug for DbBackupCodeV1 {
145    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
146        write!(f, "codes remaining: {}", self.code_set.len())
147    }
148}
149
150// Allow as this is used in serde, but compiler gets that wrong.
151#[allow(dead_code)]
152// We have to allow this as serde expects &T for the fn sig.
153#[allow(clippy::trivially_copy_pass_by_ref)]
154fn is_false(b: &bool) -> bool {
155    !b
156}
157
158#[skip_serializing_none]
159#[derive(Serialize, Deserialize, Debug)]
160#[serde(tag = "type_")]
161pub enum DbCred {
162    // These are the old v1 versions.
163    Pw {
164        password: Option<DbPasswordV1>,
165        webauthn: Option<Vec<DbWebauthnV1>>,
166        totp: Option<DbTotpV1>,
167        backup_code: Option<DbBackupCodeV1>,
168        claims: Vec<String>,
169        uuid: Uuid,
170    },
171    GPw {
172        password: Option<DbPasswordV1>,
173        webauthn: Option<Vec<DbWebauthnV1>>,
174        totp: Option<DbTotpV1>,
175        backup_code: Option<DbBackupCodeV1>,
176        claims: Vec<String>,
177        uuid: Uuid,
178    },
179    PwMfa {
180        password: Option<DbPasswordV1>,
181        webauthn: Option<Vec<DbWebauthnV1>>,
182        totp: Option<DbTotpV1>,
183        backup_code: Option<DbBackupCodeV1>,
184        claims: Vec<String>,
185        uuid: Uuid,
186    },
187    Wn {
188        password: Option<DbPasswordV1>,
189        webauthn: Option<Vec<DbWebauthnV1>>,
190        totp: Option<DbTotpV1>,
191        backup_code: Option<DbBackupCodeV1>,
192        claims: Vec<String>,
193        uuid: Uuid,
194    },
195
196    TmpWn {
197        webauthn: Vec<(String, PasskeyV4)>,
198        uuid: Uuid,
199    },
200
201    #[serde(rename = "V2PwMfa")]
202    V2PasswordMfa {
203        password: DbPasswordV1,
204        totp: Option<DbTotpV1>,
205        backup_code: Option<DbBackupCodeV1>,
206        webauthn: Vec<(String, SecurityKeyV4)>,
207        uuid: Uuid,
208    },
209
210    // New Formats!
211    #[serde(rename = "V2Pw")]
212    V2Password { password: DbPasswordV1, uuid: Uuid },
213    #[serde(rename = "V2GPw")]
214    V2GenPassword { password: DbPasswordV1, uuid: Uuid },
215    #[serde(rename = "V3PwMfa")]
216    V3PasswordMfa {
217        password: DbPasswordV1,
218        totp: Vec<(String, DbTotpV1)>,
219        backup_code: Option<DbBackupCodeV1>,
220        webauthn: Vec<(String, SecurityKeyV4)>,
221        uuid: Uuid,
222    },
223}
224
225impl DbCred {
226    fn uuid(&self) -> Uuid {
227        match self {
228            DbCred::Pw { uuid, .. }
229            | DbCred::GPw { uuid, .. }
230            | DbCred::PwMfa { uuid, .. }
231            | DbCred::Wn { uuid, .. }
232            | DbCred::TmpWn { uuid, .. }
233            | DbCred::V2PasswordMfa { uuid, .. }
234            | DbCred::V2Password { uuid, .. }
235            | DbCred::V2GenPassword { uuid, .. }
236            | DbCred::V3PasswordMfa { uuid, .. } => *uuid,
237        }
238    }
239}
240
241impl Eq for DbCred {}
242
243impl PartialEq for DbCred {
244    fn eq(&self, other: &Self) -> bool {
245        self.uuid() == other.uuid()
246    }
247}
248
249impl fmt::Display for DbCred {
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        match self {
252            DbCred::Pw {
253                password,
254                webauthn,
255                totp,
256                backup_code,
257                claims,
258                uuid,
259            } => write!(
260                f,
261                "Pw (p {}, w {}, t {}, b {}, c {}, u {})",
262                password.is_some(),
263                webauthn.is_some(),
264                totp.is_some(),
265                backup_code.is_some(),
266                claims.len(),
267                uuid
268            ),
269            DbCred::GPw {
270                password,
271                webauthn,
272                totp,
273                backup_code,
274                claims,
275                uuid,
276            } => write!(
277                f,
278                "GPw (p {}, w {}, t {}, b {}, c {}, u {})",
279                password.is_some(),
280                webauthn.is_some(),
281                totp.is_some(),
282                backup_code.is_some(),
283                claims.len(),
284                uuid
285            ),
286            DbCred::PwMfa {
287                password,
288                webauthn,
289                totp,
290                backup_code,
291                claims,
292                uuid,
293            } => write!(
294                f,
295                "PwMfa (p {}, w {}, t {}, b {}, c {}, u {})",
296                password.is_some(),
297                webauthn.is_some(),
298                totp.is_some(),
299                backup_code.is_some(),
300                claims.len(),
301                uuid
302            ),
303            DbCred::Wn {
304                password,
305                webauthn,
306                totp,
307                backup_code,
308                claims,
309                uuid,
310            } => write!(
311                f,
312                "Wn (p {}, w {}, t {}, b {}, c {}, u {})",
313                password.is_some(),
314                webauthn.is_some(),
315                totp.is_some(),
316                backup_code.is_some(),
317                claims.len(),
318                uuid
319            ),
320            DbCred::TmpWn { webauthn, uuid } => {
321                write!(f, "TmpWn ( w {}, u {} )", webauthn.len(), uuid)
322            }
323            DbCred::V2Password { password: _, uuid } => write!(f, "V2Pw ( u {uuid} )"),
324            DbCred::V2GenPassword { password: _, uuid } => write!(f, "V2GPw ( u {uuid} )"),
325            DbCred::V2PasswordMfa {
326                password: _,
327                totp,
328                backup_code,
329                webauthn,
330                uuid,
331            } => write!(
332                f,
333                "V2PwMfa (p true, w {}, t {}, b {}, u {})",
334                webauthn.len(),
335                totp.is_some(),
336                backup_code.is_some(),
337                uuid
338            ),
339            DbCred::V3PasswordMfa {
340                password: _,
341                totp,
342                backup_code,
343                webauthn,
344                uuid,
345            } => write!(
346                f,
347                "V3PwMfa (p true, w {}, t {}, b {}, u {})",
348                webauthn.len(),
349                totp.len(),
350                backup_code.is_some(),
351                uuid
352            ),
353        }
354    }
355}
356
357#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
358pub struct DbValueCredV1 {
359    #[serde(rename = "t")]
360    pub tag: String,
361    #[serde(rename = "d")]
362    pub data: DbCred,
363}
364
365#[derive(Serialize, Deserialize, Debug)]
366pub enum DbValuePasskeyV1 {
367    V4 { u: Uuid, t: String, k: PasskeyV4 },
368}
369
370impl Eq for DbValuePasskeyV1 {}
371
372impl PartialEq for DbValuePasskeyV1 {
373    fn eq(&self, other: &Self) -> bool {
374        match (self, other) {
375            (
376                DbValuePasskeyV1::V4 {
377                    u: self_uuid,
378                    k: self_key,
379                    t: _,
380                },
381                DbValuePasskeyV1::V4 {
382                    u: other_uuid,
383                    k: other_key,
384                    t: _,
385                },
386            ) => self_uuid == other_uuid && self_key.cred_id() == other_key.cred_id(),
387        }
388    }
389}
390
391#[derive(Serialize, Deserialize, Debug)]
392pub enum DbValueAttestedPasskeyV1 {
393    V4 {
394        u: Uuid,
395        t: String,
396        k: AttestedPasskeyV4,
397    },
398}
399
400impl Eq for DbValueAttestedPasskeyV1 {}
401
402impl PartialEq for DbValueAttestedPasskeyV1 {
403    fn eq(&self, other: &Self) -> bool {
404        match (self, other) {
405            (
406                DbValueAttestedPasskeyV1::V4 {
407                    u: self_uuid,
408                    k: self_key,
409                    t: _,
410                },
411                DbValueAttestedPasskeyV1::V4 {
412                    u: other_uuid,
413                    k: other_key,
414                    t: _,
415                },
416            ) => self_uuid == other_uuid && self_key.cred_id() == other_key.cred_id(),
417        }
418    }
419}
420
421#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
422pub struct DbValueTaggedStringV1 {
423    #[serde(rename = "t")]
424    pub tag: String,
425    #[serde(rename = "d")]
426    pub data: String,
427}
428
429#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
430pub struct DbValueAddressV1 {
431    #[serde(rename = "f")]
432    pub formatted: String,
433    #[serde(rename = "s")]
434    pub street_address: String,
435    #[serde(rename = "l")]
436    pub locality: String,
437    #[serde(rename = "r")]
438    pub region: String,
439    #[serde(rename = "p")]
440    pub postal_code: String,
441    #[serde(rename = "c")]
442    pub country: String,
443}
444
445#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
446pub enum DbValueOauthClaimMapJoinV1 {
447    #[serde(rename = "c")]
448    CommaSeparatedValue,
449    #[serde(rename = "s")]
450    SpaceSeparatedValue,
451    #[serde(rename = "a")]
452    JsonArray,
453}
454
455#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
456pub enum DbValueOauthClaimMap {
457    V1 {
458        #[serde(rename = "n")]
459        name: String,
460        #[serde(rename = "j")]
461        join: DbValueOauthClaimMapJoinV1,
462        #[serde(rename = "d")]
463        values: BTreeMap<Uuid, BTreeSet<String>>,
464    },
465}
466
467#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
468pub struct DbValueOauthScopeMapV1 {
469    #[serde(rename = "u")]
470    pub refer: Uuid,
471    #[serde(rename = "m")]
472    pub data: Vec<String>,
473}
474
475#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
476pub enum DbValueAccessScopeV1 {
477    #[serde(rename = "i")]
478    IdentityOnly,
479    #[serde(rename = "r")]
480    #[default]
481    ReadOnly,
482    #[serde(rename = "w")]
483    ReadWrite,
484    #[serde(rename = "p")]
485    PrivilegeCapable,
486    #[serde(rename = "s")]
487    Synchronise,
488}
489
490#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
491#[allow(clippy::enum_variant_names)]
492pub enum DbValueIdentityId {
493    #[serde(rename = "v1i")]
494    V1Internal,
495    #[serde(rename = "v1u")]
496    V1Uuid(Uuid),
497    #[serde(rename = "v1s")]
498    V1Sync(Uuid),
499}
500
501#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
502pub enum DbValueSessionStateV1 {
503    #[serde(rename = "ea")]
504    ExpiresAt(String),
505    #[serde(rename = "nv")]
506    Never,
507    #[serde(rename = "ra")]
508    RevokedAt(DbCidV1),
509}
510
511#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
512pub enum DbValueAuthTypeV1 {
513    #[serde(rename = "an")]
514    Anonymous,
515    #[serde(rename = "po")]
516    Password,
517    #[serde(rename = "pg")]
518    GeneratedPassword,
519    #[serde(rename = "pt")]
520    PasswordTotp,
521    #[serde(rename = "pb")]
522    PasswordBackupCode,
523    #[serde(rename = "ps")]
524    PasswordSecurityKey,
525    #[serde(rename = "as")]
526    Passkey,
527    #[serde(rename = "ap")]
528    AttestedPasskey,
529}
530
531#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
532pub enum DbValueSession {
533    V1 {
534        #[serde(rename = "u")]
535        refer: Uuid,
536        #[serde(rename = "l")]
537        label: String,
538        #[serde(rename = "e")]
539        expiry: Option<String>,
540        #[serde(rename = "i")]
541        issued_at: String,
542        #[serde(rename = "b")]
543        issued_by: DbValueIdentityId,
544        #[serde(rename = "s", default)]
545        scope: DbValueAccessScopeV1,
546    },
547    V2 {
548        #[serde(rename = "u")]
549        refer: Uuid,
550        #[serde(rename = "l")]
551        label: String,
552        #[serde(rename = "e")]
553        expiry: Option<String>,
554        #[serde(rename = "i")]
555        issued_at: String,
556        #[serde(rename = "b")]
557        issued_by: DbValueIdentityId,
558        #[serde(rename = "c")]
559        cred_id: Uuid,
560        #[serde(rename = "s", default)]
561        scope: DbValueAccessScopeV1,
562    },
563    V3 {
564        #[serde(rename = "u")]
565        refer: Uuid,
566        #[serde(rename = "l")]
567        label: String,
568        #[serde(rename = "e")]
569        state: DbValueSessionStateV1,
570        #[serde(rename = "i")]
571        issued_at: String,
572        #[serde(rename = "b")]
573        issued_by: DbValueIdentityId,
574        #[serde(rename = "c")]
575        cred_id: Uuid,
576        #[serde(rename = "s", default)]
577        scope: DbValueAccessScopeV1,
578    },
579    V4 {
580        #[serde(rename = "u")]
581        refer: Uuid,
582        #[serde(rename = "l")]
583        label: String,
584        #[serde(rename = "e")]
585        state: DbValueSessionStateV1,
586        #[serde(rename = "i")]
587        issued_at: String,
588        #[serde(rename = "b")]
589        issued_by: DbValueIdentityId,
590        #[serde(rename = "c")]
591        cred_id: Uuid,
592        #[serde(rename = "s", default)]
593        scope: DbValueAccessScopeV1,
594        #[serde(rename = "t")]
595        type_: DbValueAuthTypeV1,
596    },
597}
598
599#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
600pub enum DbValueApiTokenScopeV1 {
601    #[serde(rename = "r")]
602    #[default]
603    ReadOnly,
604    #[serde(rename = "w")]
605    ReadWrite,
606    #[serde(rename = "s")]
607    Synchronise,
608}
609
610#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
611pub enum DbValueApiToken {
612    V1 {
613        #[serde(rename = "u")]
614        refer: Uuid,
615        #[serde(rename = "l")]
616        label: String,
617        #[serde(rename = "e")]
618        expiry: Option<String>,
619        #[serde(rename = "i")]
620        issued_at: String,
621        #[serde(rename = "b")]
622        issued_by: DbValueIdentityId,
623        #[serde(rename = "s", default)]
624        scope: DbValueApiTokenScopeV1,
625    },
626}
627
628#[skip_serializing_none]
629#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
630pub enum DbValueOauth2Session {
631    V1 {
632        #[serde(rename = "u")]
633        refer: Uuid,
634        #[serde(rename = "p")]
635        parent: Uuid,
636        #[serde(rename = "e")]
637        expiry: Option<String>,
638        #[serde(rename = "i")]
639        issued_at: String,
640        #[serde(rename = "r")]
641        rs_uuid: Uuid,
642    },
643    V2 {
644        #[serde(rename = "u")]
645        refer: Uuid,
646        #[serde(rename = "p")]
647        parent: Uuid,
648        #[serde(rename = "e")]
649        state: DbValueSessionStateV1,
650        #[serde(rename = "i")]
651        issued_at: String,
652        #[serde(rename = "r")]
653        rs_uuid: Uuid,
654    },
655    V3 {
656        #[serde(rename = "u")]
657        refer: Uuid,
658        #[serde(rename = "p")]
659        parent: Option<Uuid>,
660        #[serde(rename = "e")]
661        state: DbValueSessionStateV1,
662        #[serde(rename = "i")]
663        issued_at: String,
664        #[serde(rename = "r")]
665        rs_uuid: Uuid,
666    },
667}
668
669// Internal representation of an image
670#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
671pub enum DbValueImage {
672    V1 {
673        filename: String,
674        filetype: ImageType,
675        contents: Vec<u8>,
676    },
677}
678
679#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
680pub enum DbValueKeyUsage {
681    JwsEs256,
682    JwsHs256,
683    JwsRs256,
684    JweA128GCM,
685}
686
687#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
688pub enum DbValueKeyStatus {
689    Valid,
690    Retained,
691    Revoked,
692}
693
694#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
695pub enum DbValueKeyInternal {
696    V1 {
697        id: String,
698        usage: DbValueKeyUsage,
699        valid_from: u64,
700        status: DbValueKeyStatus,
701        status_cid: DbCidV1,
702        der: Zeroizing<Vec<u8>>,
703    },
704}
705
706#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
707pub enum DbValueCertificate {
708    V1 { certificate_der: Vec<u8> },
709}
710
711#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
712pub enum DbValueApplicationPassword {
713    V1 {
714        #[serde(rename = "u")]
715        refer: Uuid,
716        #[serde(rename = "a")]
717        application_refer: Uuid,
718        #[serde(rename = "l")]
719        label: String,
720        #[serde(rename = "p")]
721        password: DbPasswordV1,
722    },
723}
724
725#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
726pub enum DbValueSetV2 {
727    #[serde(rename = "U8")]
728    Utf8(Vec<String>),
729    #[serde(rename = "I8")]
730    Iutf8(Vec<String>),
731    #[serde(rename = "N8")]
732    Iname(Vec<String>),
733    #[serde(rename = "UU")]
734    Uuid(Vec<Uuid>),
735    #[serde(rename = "BO")]
736    Bool(Vec<bool>),
737    #[serde(rename = "SY")]
738    SyntaxType(Vec<u16>),
739    #[serde(rename = "IN")]
740    IndexType(Vec<u16>),
741    #[serde(rename = "RF")]
742    Reference(Vec<Uuid>),
743    #[serde(rename = "JF")]
744    JsonFilter(Vec<String>),
745    #[serde(rename = "CR")]
746    Credential(Vec<DbValueCredV1>),
747    #[serde(rename = "RU")]
748    SecretValue(Vec<String>),
749    #[serde(rename = "SK")]
750    SshKey(Vec<DbValueTaggedStringV1>),
751    #[serde(rename = "SP")]
752    Spn(Vec<(String, String)>),
753    #[serde(rename = "UI")]
754    Uint32(Vec<u32>),
755    #[serde(rename = "CI")]
756    Cid(Vec<DbCidV1>),
757    #[serde(rename = "NU")]
758    NsUniqueId(Vec<String>),
759    #[serde(rename = "DT")]
760    DateTime(Vec<String>),
761    #[serde(rename = "EM")]
762    EmailAddress(String, Vec<String>),
763    #[serde(rename = "PN")]
764    PhoneNumber(String, Vec<String>),
765    #[serde(rename = "AD")]
766    Address(Vec<DbValueAddressV1>),
767    #[serde(rename = "UR")]
768    Url(Vec<Url>),
769    #[serde(rename = "OS")]
770    OauthScope(Vec<String>),
771    #[serde(rename = "OM")]
772    OauthScopeMap(Vec<DbValueOauthScopeMapV1>),
773    #[serde(rename = "OC")]
774    OauthClaimMap(Vec<DbValueOauthClaimMap>),
775    #[serde(rename = "E2")]
776    PrivateBinary(Vec<Vec<u8>>),
777    #[serde(rename = "PB")]
778    PublicBinary(Vec<(String, Vec<u8>)>),
779    #[serde(rename = "RS")]
780    RestrictedString(Vec<String>),
781    #[serde(rename = "IT")]
782    IntentToken(Vec<(String, DbValueIntentTokenStateV1)>),
783    #[serde(rename = "PK")]
784    Passkey(Vec<DbValuePasskeyV1>),
785    #[serde(rename = "DK")]
786    AttestedPasskey(Vec<DbValueAttestedPasskeyV1>),
787    #[serde(rename = "TE")]
788    TrustedDeviceEnrollment(Vec<Uuid>),
789    #[serde(rename = "AS")]
790    Session(Vec<DbValueSession>),
791    #[serde(rename = "JE")]
792    JwsKeyEs256(Vec<Zeroizing<Vec<u8>>>),
793    #[serde(rename = "JR")]
794    JwsKeyRs256(Vec<Zeroizing<Vec<u8>>>),
795    #[serde(rename = "OZ")]
796    Oauth2Session(Vec<DbValueOauth2Session>),
797    #[serde(rename = "UH")]
798    UiHint(Vec<u16>),
799    #[serde(rename = "TO")]
800    TotpSecret(Vec<(String, DbTotpV1)>),
801    #[serde(rename = "AT")]
802    ApiToken(Vec<DbValueApiToken>),
803    #[serde(rename = "SA")]
804    AuditLogString(Vec<(Cid, String)>),
805    #[serde(rename = "EK")]
806    EcKeyPrivate(Vec<u8>),
807    #[serde(rename = "IM")]
808    Image(Vec<DbValueImage>),
809    #[serde(rename = "CT")]
810    CredentialType(Vec<u16>),
811    #[serde(rename = "WC")]
812    WebauthnAttestationCaList { ca_list: AttestationCaList },
813    #[serde(rename = "KI")]
814    KeyInternal(Vec<DbValueKeyInternal>),
815    #[serde(rename = "HS")]
816    HexString(Vec<String>),
817    #[serde(rename = "X509")]
818    Certificate(Vec<DbValueCertificate>),
819    #[serde(rename = "AP")]
820    ApplicationPassword(Vec<DbValueApplicationPassword>),
821}
822
823impl DbValueSetV2 {
824    pub fn len(&self) -> usize {
825        match self {
826            DbValueSetV2::Utf8(set)
827            | DbValueSetV2::Iutf8(set)
828            | DbValueSetV2::HexString(set)
829            | DbValueSetV2::Iname(set) => set.len(),
830            DbValueSetV2::Uuid(set) => set.len(),
831            DbValueSetV2::Bool(set) => set.len(),
832            DbValueSetV2::SyntaxType(set) => set.len(),
833            DbValueSetV2::IndexType(set) => set.len(),
834            DbValueSetV2::Reference(set) => set.len(),
835            DbValueSetV2::JsonFilter(set) => set.len(),
836            DbValueSetV2::Credential(set) => set.len(),
837            DbValueSetV2::SecretValue(set) => set.len(),
838            DbValueSetV2::SshKey(set) => set.len(),
839            DbValueSetV2::Spn(set) => set.len(),
840            DbValueSetV2::Uint32(set) => set.len(),
841            DbValueSetV2::Cid(set) => set.len(),
842            DbValueSetV2::NsUniqueId(set) => set.len(),
843            DbValueSetV2::DateTime(set) => set.len(),
844            DbValueSetV2::EmailAddress(_primary, set) => set.len(),
845            DbValueSetV2::PhoneNumber(_primary, set) => set.len(),
846            DbValueSetV2::Address(set) => set.len(),
847            DbValueSetV2::Url(set) => set.len(),
848            DbValueSetV2::OauthClaimMap(set) => set.len(),
849            DbValueSetV2::OauthScope(set) => set.len(),
850            DbValueSetV2::OauthScopeMap(set) => set.len(),
851            DbValueSetV2::PrivateBinary(set) => set.len(),
852            DbValueSetV2::PublicBinary(set) => set.len(),
853            DbValueSetV2::RestrictedString(set) => set.len(),
854            DbValueSetV2::IntentToken(set) => set.len(),
855            DbValueSetV2::Passkey(set) => set.len(),
856            DbValueSetV2::AttestedPasskey(set) => set.len(),
857            DbValueSetV2::TrustedDeviceEnrollment(set) => set.len(),
858            DbValueSetV2::Session(set) => set.len(),
859            DbValueSetV2::ApiToken(set) => set.len(),
860            DbValueSetV2::Oauth2Session(set) => set.len(),
861            DbValueSetV2::JwsKeyEs256(set) => set.len(),
862            DbValueSetV2::JwsKeyRs256(set) => set.len(),
863            DbValueSetV2::UiHint(set) => set.len(),
864            DbValueSetV2::TotpSecret(set) => set.len(),
865            DbValueSetV2::AuditLogString(set) => set.len(),
866            DbValueSetV2::Image(set) => set.len(),
867            DbValueSetV2::EcKeyPrivate(_key) => 1, // here we have to hard code it because the Vec<u8>
868            // represents the bytes of  SINGLE(!) key
869            DbValueSetV2::CredentialType(set) => set.len(),
870            DbValueSetV2::WebauthnAttestationCaList { ca_list } => ca_list.len(),
871            DbValueSetV2::KeyInternal(set) => set.len(),
872            DbValueSetV2::Certificate(set) => set.len(),
873            DbValueSetV2::ApplicationPassword(set) => set.len(),
874        }
875    }
876
877    pub fn is_empty(&self) -> bool {
878        self.len() == 0
879    }
880}
881
882#[cfg(test)]
883mod tests {
884    use base64::{engine::general_purpose, Engine as _};
885    use serde::{Deserialize, Serialize};
886    use serde_with::skip_serializing_none;
887    use uuid::Uuid;
888
889    use super::{DbBackupCodeV1, DbCred, DbPasswordV1, DbTotpV1, DbWebauthnV1};
890
891    fn dbcred_type_default_pw() -> DbCredTypeV1 {
892        DbCredTypeV1::Pw
893    }
894
895    #[derive(Serialize, Deserialize, Debug)]
896    pub enum DbCredTypeV1 {
897        Pw,
898        GPw,
899        PwMfa,
900        // PwWn,
901        Wn,
902        // WnVer,
903        // PwWnVer,
904    }
905
906    #[skip_serializing_none]
907    #[derive(Serialize, Deserialize, Debug)]
908    pub struct DbCredV1 {
909        #[serde(default = "dbcred_type_default_pw")]
910        pub type_: DbCredTypeV1,
911        pub password: Option<DbPasswordV1>,
912        pub webauthn: Option<Vec<DbWebauthnV1>>,
913        pub totp: Option<DbTotpV1>,
914        pub backup_code: Option<DbBackupCodeV1>,
915        pub claims: Vec<String>,
916        pub uuid: Uuid,
917    }
918
919    #[test]
920    fn test_dbcred_pre_totp_decode() {
921        // This test exists to prove that the previous dbcredv1 format (without totp)
922        // can still decode into the updated dbcredv1 that does have a TOTP field.
923        /*
924        let dbcred = DbCredV1 {
925            password: Some(DbPasswordV1::PBKDF2(0, vec![0], vec![0])),
926            claims: vec![],
927            uuid: Uuid::new_v4(),
928        };
929        let data = serde_cbor::to_vec(&dbcred).unwrap();
930        let s = general_purpose::STANDARD.encode(data);
931        */
932        let s = "o2hwYXNzd29yZKFmUEJLREYygwCBAIEAZmNsYWltc4BkdXVpZFAjkHFm4q5M86UcNRi4hBjN";
933        let data = general_purpose::STANDARD.decode(s).unwrap();
934        let dbcred: DbCredV1 = serde_cbor::from_slice(data.as_slice()).unwrap();
935
936        // Test converting to the new enum format
937        let x = vec![dbcred];
938
939        let json = serde_json::to_string(&x).unwrap();
940        eprintln!("{json}");
941
942        let _e_dbcred: Vec<DbCred> = serde_json::from_str(&json).unwrap();
943
944        // assert_eq!(dbcred,e_dbcred);
945    }
946}