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