kanidmd_lib/be/
dbvalue.rs

1use std::fmt;
2use std::time::Duration;
3
4use hashbrown::HashSet;
5use kanidm_proto::internal::ImageType;
6use serde::{Deserialize, Serialize};
7use serde_with::skip_serializing_none;
8use std::collections::{BTreeMap, BTreeSet};
9use url::Url;
10use uuid::Uuid;
11use webauthn_rs::prelude::{
12    AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
13    SecurityKey as SecurityKeyV4,
14};
15use webauthn_rs_core::proto::{COSEKey, UserVerificationPolicy};
16
17// Re-export this as though it was here.
18use crate::repl::cid::Cid;
19pub use kanidm_lib_crypto::DbPasswordV1;
20
21#[derive(Serialize, Deserialize, Debug, Ord, PartialOrd, PartialEq, Eq, Clone)]
22pub struct DbCidV1 {
23    #[serde(rename = "t")]
24    pub timestamp: Duration,
25    #[serde(rename = "s")]
26    pub server_id: Uuid,
27}
28
29impl From<Cid> for DbCidV1 {
30    fn from(Cid { s_uuid, ts }: Cid) -> Self {
31        DbCidV1 {
32            timestamp: ts,
33            server_id: s_uuid,
34        }
35    }
36}
37
38impl From<&Cid> for DbCidV1 {
39    fn from(&Cid { s_uuid, ts }: &Cid) -> Self {
40        DbCidV1 {
41            timestamp: ts,
42            server_id: s_uuid,
43        }
44    }
45}
46
47impl fmt::Display for DbCidV1 {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(f, "{:032}-{}", self.timestamp.as_nanos(), self.server_id)
50    }
51}
52
53#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
54pub enum DbValueIntentTokenStateV1 {
55    #[serde(rename = "v")]
56    Valid {
57        max_ttl: Duration,
58        #[serde(default)]
59        ext_cred_portal_can_view: bool,
60        #[serde(default)]
61        primary_can_edit: bool,
62        #[serde(default)]
63        passkeys_can_edit: bool,
64        #[serde(default)]
65        attested_passkeys_can_edit: bool,
66        #[serde(default)]
67        unixcred_can_edit: bool,
68        #[serde(default)]
69        sshpubkey_can_edit: bool,
70    },
71    #[serde(rename = "p")]
72    InProgress {
73        max_ttl: Duration,
74        session_id: Uuid,
75        session_ttl: Duration,
76        #[serde(default)]
77        ext_cred_portal_can_view: bool,
78        #[serde(default)]
79        primary_can_edit: bool,
80        #[serde(default)]
81        passkeys_can_edit: bool,
82        #[serde(default)]
83        attested_passkeys_can_edit: bool,
84        #[serde(default)]
85        unixcred_can_edit: bool,
86        #[serde(default)]
87        sshpubkey_can_edit: bool,
88    },
89    #[serde(rename = "c")]
90    Consumed { max_ttl: Duration },
91}
92
93#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
94pub enum DbTotpAlgoV1 {
95    S1,
96    S256,
97    S512,
98}
99
100#[derive(Serialize, Deserialize, PartialEq, Eq)]
101pub struct DbTotpV1 {
102    #[serde(rename = "l")]
103    pub label: String,
104    #[serde(rename = "k")]
105    pub key: Vec<u8>,
106    #[serde(rename = "s")]
107    pub step: u64,
108    #[serde(rename = "a")]
109    pub algo: DbTotpAlgoV1,
110    #[serde(rename = "d", default)]
111    pub digits: Option<u8>,
112}
113
114impl std::fmt::Debug for DbTotpV1 {
115    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116        f.debug_struct("DbTotpV1")
117            .field("label", &self.label)
118            .field("step", &self.step)
119            .field("algo", &self.algo)
120            .finish()
121    }
122}
123
124#[derive(Serialize, Deserialize, Debug)]
125pub struct DbWebauthnV1 {
126    #[serde(rename = "l")]
127    pub label: String,
128    #[serde(rename = "i")]
129    pub id: Vec<u8>,
130    #[serde(rename = "c")]
131    pub cred: COSEKey,
132    #[serde(rename = "t")]
133    pub counter: u32,
134    #[serde(rename = "v")]
135    pub verified: bool,
136    #[serde(rename = "p", default)]
137    pub registration_policy: UserVerificationPolicy,
138}
139
140#[derive(Serialize, Deserialize, PartialEq, Eq)]
141pub struct DbBackupCodeV1 {
142    pub code_set: HashSet<String>, // has to use std::HashSet for serde
143}
144
145impl std::fmt::Debug for DbBackupCodeV1 {
146    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
147        write!(f, "codes remaining: {}", self.code_set.len())
148    }
149}
150
151// We have to allow this as serde expects &T for the fn sig.
152#[allow(clippy::trivially_copy_pass_by_ref)]
153fn is_false(b: &bool) -> bool {
154    !b
155}
156
157#[skip_serializing_none]
158#[derive(Serialize, Deserialize, Debug)]
159#[serde(tag = "type_")]
160pub enum DbCred {
161    // These are the old v1 versions.
162    Pw {
163        password: Option<DbPasswordV1>,
164        webauthn: Option<Vec<DbWebauthnV1>>,
165        totp: Option<DbTotpV1>,
166        backup_code: Option<DbBackupCodeV1>,
167        claims: Vec<String>,
168        uuid: Uuid,
169    },
170    GPw {
171        password: Option<DbPasswordV1>,
172        webauthn: Option<Vec<DbWebauthnV1>>,
173        totp: Option<DbTotpV1>,
174        backup_code: Option<DbBackupCodeV1>,
175        claims: Vec<String>,
176        uuid: Uuid,
177    },
178    PwMfa {
179        password: Option<DbPasswordV1>,
180        webauthn: Option<Vec<DbWebauthnV1>>,
181        totp: Option<DbTotpV1>,
182        backup_code: Option<DbBackupCodeV1>,
183        claims: Vec<String>,
184        uuid: Uuid,
185    },
186    Wn {
187        password: Option<DbPasswordV1>,
188        webauthn: Option<Vec<DbWebauthnV1>>,
189        totp: Option<DbTotpV1>,
190        backup_code: Option<DbBackupCodeV1>,
191        claims: Vec<String>,
192        uuid: Uuid,
193    },
194
195    TmpWn {
196        webauthn: Vec<(String, PasskeyV4)>,
197        uuid: Uuid,
198    },
199
200    #[serde(rename = "V2PwMfa")]
201    V2PasswordMfa {
202        password: DbPasswordV1,
203        totp: Option<DbTotpV1>,
204        backup_code: Option<DbBackupCodeV1>,
205        webauthn: Vec<(String, SecurityKeyV4)>,
206        uuid: Uuid,
207    },
208
209    // New Formats!
210    #[serde(rename = "V2Pw")]
211    V2Password { password: DbPasswordV1, uuid: Uuid },
212    #[serde(rename = "V2GPw")]
213    V2GenPassword { password: DbPasswordV1, uuid: Uuid },
214    #[serde(rename = "V3PwMfa")]
215    V3PasswordMfa {
216        password: DbPasswordV1,
217        totp: Vec<(String, DbTotpV1)>,
218        backup_code: Option<DbBackupCodeV1>,
219        webauthn: Vec<(String, SecurityKeyV4)>,
220        uuid: Uuid,
221    },
222}
223
224impl DbCred {
225    fn uuid(&self) -> Uuid {
226        match self {
227            DbCred::Pw { uuid, .. }
228            | DbCred::GPw { uuid, .. }
229            | DbCred::PwMfa { uuid, .. }
230            | DbCred::Wn { uuid, .. }
231            | DbCred::TmpWn { uuid, .. }
232            | DbCred::V2PasswordMfa { uuid, .. }
233            | DbCred::V2Password { uuid, .. }
234            | DbCred::V2GenPassword { uuid, .. }
235            | DbCred::V3PasswordMfa { uuid, .. } => *uuid,
236        }
237    }
238}
239
240impl Eq for DbCred {}
241
242impl PartialEq for DbCred {
243    fn eq(&self, other: &Self) -> bool {
244        self.uuid() == other.uuid()
245    }
246}
247
248impl fmt::Display for DbCred {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        match self {
251            DbCred::Pw {
252                password,
253                webauthn,
254                totp,
255                backup_code,
256                claims,
257                uuid,
258            } => write!(
259                f,
260                "Pw (p {}, w {}, t {}, b {}, c {}, u {})",
261                password.is_some(),
262                webauthn.is_some(),
263                totp.is_some(),
264                backup_code.is_some(),
265                claims.len(),
266                uuid
267            ),
268            DbCred::GPw {
269                password,
270                webauthn,
271                totp,
272                backup_code,
273                claims,
274                uuid,
275            } => write!(
276                f,
277                "GPw (p {}, w {}, t {}, b {}, c {}, u {})",
278                password.is_some(),
279                webauthn.is_some(),
280                totp.is_some(),
281                backup_code.is_some(),
282                claims.len(),
283                uuid
284            ),
285            DbCred::PwMfa {
286                password,
287                webauthn,
288                totp,
289                backup_code,
290                claims,
291                uuid,
292            } => write!(
293                f,
294                "PwMfa (p {}, w {}, t {}, b {}, c {}, u {})",
295                password.is_some(),
296                webauthn.is_some(),
297                totp.is_some(),
298                backup_code.is_some(),
299                claims.len(),
300                uuid
301            ),
302            DbCred::Wn {
303                password,
304                webauthn,
305                totp,
306                backup_code,
307                claims,
308                uuid,
309            } => write!(
310                f,
311                "Wn (p {}, w {}, t {}, b {}, c {}, u {})",
312                password.is_some(),
313                webauthn.is_some(),
314                totp.is_some(),
315                backup_code.is_some(),
316                claims.len(),
317                uuid
318            ),
319            DbCred::TmpWn { webauthn, uuid } => {
320                write!(f, "TmpWn ( w {}, u {} )", webauthn.len(), uuid)
321            }
322            DbCred::V2Password { password: _, uuid } => write!(f, "V2Pw ( u {uuid} )"),
323            DbCred::V2GenPassword { password: _, uuid } => write!(f, "V2GPw ( u {uuid} )"),
324            DbCred::V2PasswordMfa {
325                password: _,
326                totp,
327                backup_code,
328                webauthn,
329                uuid,
330            } => write!(
331                f,
332                "V2PwMfa (p true, w {}, t {}, b {}, u {})",
333                webauthn.len(),
334                totp.is_some(),
335                backup_code.is_some(),
336                uuid
337            ),
338            DbCred::V3PasswordMfa {
339                password: _,
340                totp,
341                backup_code,
342                webauthn,
343                uuid,
344            } => write!(
345                f,
346                "V3PwMfa (p true, w {}, t {}, b {}, u {})",
347                webauthn.len(),
348                totp.len(),
349                backup_code.is_some(),
350                uuid
351            ),
352        }
353    }
354}
355
356#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
357pub struct DbValueCredV1 {
358    #[serde(rename = "t")]
359    pub tag: String,
360    #[serde(rename = "d")]
361    pub data: DbCred,
362}
363
364#[derive(Serialize, Deserialize, Debug)]
365pub enum DbValuePasskeyV1 {
366    V4 { u: Uuid, t: String, k: PasskeyV4 },
367}
368
369impl Eq for DbValuePasskeyV1 {}
370
371impl PartialEq for DbValuePasskeyV1 {
372    fn eq(&self, other: &Self) -> bool {
373        match (self, other) {
374            (
375                DbValuePasskeyV1::V4 {
376                    u: self_uuid,
377                    k: self_key,
378                    t: _,
379                },
380                DbValuePasskeyV1::V4 {
381                    u: other_uuid,
382                    k: other_key,
383                    t: _,
384                },
385            ) => self_uuid == other_uuid && self_key.cred_id() == other_key.cred_id(),
386        }
387    }
388}
389
390#[derive(Serialize, Deserialize, Debug)]
391pub enum DbValueAttestedPasskeyV1 {
392    V4 {
393        u: Uuid,
394        t: String,
395        k: AttestedPasskeyV4,
396    },
397}
398
399impl Eq for DbValueAttestedPasskeyV1 {}
400
401impl PartialEq for DbValueAttestedPasskeyV1 {
402    fn eq(&self, other: &Self) -> bool {
403        match (self, other) {
404            (
405                DbValueAttestedPasskeyV1::V4 {
406                    u: self_uuid,
407                    k: self_key,
408                    t: _,
409                },
410                DbValueAttestedPasskeyV1::V4 {
411                    u: other_uuid,
412                    k: other_key,
413                    t: _,
414                },
415            ) => self_uuid == other_uuid && self_key.cred_id() == other_key.cred_id(),
416        }
417    }
418}
419
420#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
421pub struct DbValueTaggedStringV1 {
422    #[serde(rename = "t")]
423    pub tag: String,
424    #[serde(rename = "d")]
425    pub data: String,
426}
427
428#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
429pub struct DbValueEmailAddressV1 {
430    pub d: String,
431    #[serde(skip_serializing_if = "is_false", default)]
432    pub p: bool,
433}
434
435#[derive(Serialize, Deserialize, Debug)]
436pub struct DbValuePhoneNumberV1 {
437    pub d: String,
438    #[serde(skip_serializing_if = "is_false", default)]
439    pub p: bool,
440}
441
442#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
443pub struct DbValueAddressV1 {
444    #[serde(rename = "f")]
445    pub formatted: String,
446    #[serde(rename = "s")]
447    pub street_address: String,
448    #[serde(rename = "l")]
449    pub locality: String,
450    #[serde(rename = "r")]
451    pub region: String,
452    #[serde(rename = "p")]
453    pub postal_code: String,
454    #[serde(rename = "c")]
455    pub country: String,
456}
457
458#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
459pub enum DbValueOauthClaimMapJoinV1 {
460    #[serde(rename = "c")]
461    CommaSeparatedValue,
462    #[serde(rename = "s")]
463    SpaceSeparatedValue,
464    #[serde(rename = "a")]
465    JsonArray,
466}
467
468#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
469pub enum DbValueOauthClaimMap {
470    V1 {
471        #[serde(rename = "n")]
472        name: String,
473        #[serde(rename = "j")]
474        join: DbValueOauthClaimMapJoinV1,
475        #[serde(rename = "d")]
476        values: BTreeMap<Uuid, BTreeSet<String>>,
477    },
478}
479
480#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
481pub struct DbValueOauthScopeMapV1 {
482    #[serde(rename = "u")]
483    pub refer: Uuid,
484    #[serde(rename = "m")]
485    pub data: Vec<String>,
486}
487
488#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
489pub enum DbValueAccessScopeV1 {
490    #[serde(rename = "i")]
491    IdentityOnly,
492    #[serde(rename = "r")]
493    #[default]
494    ReadOnly,
495    #[serde(rename = "w")]
496    ReadWrite,
497    #[serde(rename = "p")]
498    PrivilegeCapable,
499    #[serde(rename = "s")]
500    Synchronise,
501}
502
503#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
504#[allow(clippy::enum_variant_names)]
505pub enum DbValueIdentityId {
506    #[serde(rename = "v1i")]
507    V1Internal,
508    #[serde(rename = "v1u")]
509    V1Uuid(Uuid),
510    #[serde(rename = "v1s")]
511    V1Sync(Uuid),
512}
513
514#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
515pub enum DbValueSessionStateV1 {
516    #[serde(rename = "ea")]
517    ExpiresAt(String),
518    #[serde(rename = "nv")]
519    Never,
520    #[serde(rename = "ra")]
521    RevokedAt(DbCidV1),
522}
523
524#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
525pub enum DbValueAuthTypeV1 {
526    #[serde(rename = "an")]
527    Anonymous,
528    #[serde(rename = "po")]
529    Password,
530    #[serde(rename = "pg")]
531    GeneratedPassword,
532    #[serde(rename = "pt")]
533    PasswordTotp,
534    #[serde(rename = "pb")]
535    PasswordBackupCode,
536    #[serde(rename = "ps")]
537    PasswordSecurityKey,
538    #[serde(rename = "as")]
539    Passkey,
540    #[serde(rename = "ap")]
541    AttestedPasskey,
542}
543
544#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
545pub enum DbValueSession {
546    V1 {
547        #[serde(rename = "u")]
548        refer: Uuid,
549        #[serde(rename = "l")]
550        label: String,
551        #[serde(rename = "e")]
552        expiry: Option<String>,
553        #[serde(rename = "i")]
554        issued_at: String,
555        #[serde(rename = "b")]
556        issued_by: DbValueIdentityId,
557        #[serde(rename = "s", default)]
558        scope: DbValueAccessScopeV1,
559    },
560    V2 {
561        #[serde(rename = "u")]
562        refer: Uuid,
563        #[serde(rename = "l")]
564        label: String,
565        #[serde(rename = "e")]
566        expiry: Option<String>,
567        #[serde(rename = "i")]
568        issued_at: String,
569        #[serde(rename = "b")]
570        issued_by: DbValueIdentityId,
571        #[serde(rename = "c")]
572        cred_id: Uuid,
573        #[serde(rename = "s", default)]
574        scope: DbValueAccessScopeV1,
575    },
576    V3 {
577        #[serde(rename = "u")]
578        refer: Uuid,
579        #[serde(rename = "l")]
580        label: String,
581        #[serde(rename = "e")]
582        state: DbValueSessionStateV1,
583        #[serde(rename = "i")]
584        issued_at: String,
585        #[serde(rename = "b")]
586        issued_by: DbValueIdentityId,
587        #[serde(rename = "c")]
588        cred_id: Uuid,
589        #[serde(rename = "s", default)]
590        scope: DbValueAccessScopeV1,
591    },
592    V4 {
593        #[serde(rename = "u")]
594        refer: Uuid,
595        #[serde(rename = "l")]
596        label: String,
597        #[serde(rename = "e")]
598        state: DbValueSessionStateV1,
599        #[serde(rename = "i")]
600        issued_at: String,
601        #[serde(rename = "b")]
602        issued_by: DbValueIdentityId,
603        #[serde(rename = "c")]
604        cred_id: Uuid,
605        #[serde(rename = "s", default)]
606        scope: DbValueAccessScopeV1,
607        #[serde(rename = "t")]
608        type_: DbValueAuthTypeV1,
609    },
610}
611
612#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
613pub enum DbValueApiTokenScopeV1 {
614    #[serde(rename = "r")]
615    #[default]
616    ReadOnly,
617    #[serde(rename = "w")]
618    ReadWrite,
619    #[serde(rename = "s")]
620    Synchronise,
621}
622
623#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
624pub enum DbValueApiToken {
625    V1 {
626        #[serde(rename = "u")]
627        refer: Uuid,
628        #[serde(rename = "l")]
629        label: String,
630        #[serde(rename = "e")]
631        expiry: Option<String>,
632        #[serde(rename = "i")]
633        issued_at: String,
634        #[serde(rename = "b")]
635        issued_by: DbValueIdentityId,
636        #[serde(rename = "s", default)]
637        scope: DbValueApiTokenScopeV1,
638    },
639}
640
641#[skip_serializing_none]
642#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
643pub enum DbValueOauth2Session {
644    V1 {
645        #[serde(rename = "u")]
646        refer: Uuid,
647        #[serde(rename = "p")]
648        parent: Uuid,
649        #[serde(rename = "e")]
650        expiry: Option<String>,
651        #[serde(rename = "i")]
652        issued_at: String,
653        #[serde(rename = "r")]
654        rs_uuid: Uuid,
655    },
656    V2 {
657        #[serde(rename = "u")]
658        refer: Uuid,
659        #[serde(rename = "p")]
660        parent: Uuid,
661        #[serde(rename = "e")]
662        state: DbValueSessionStateV1,
663        #[serde(rename = "i")]
664        issued_at: String,
665        #[serde(rename = "r")]
666        rs_uuid: Uuid,
667    },
668    V3 {
669        #[serde(rename = "u")]
670        refer: Uuid,
671        #[serde(rename = "p")]
672        parent: Option<Uuid>,
673        #[serde(rename = "e")]
674        state: DbValueSessionStateV1,
675        #[serde(rename = "i")]
676        issued_at: String,
677        #[serde(rename = "r")]
678        rs_uuid: Uuid,
679    },
680}
681
682// Internal representation of an image
683#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
684pub enum DbValueImage {
685    V1 {
686        filename: String,
687        filetype: ImageType,
688        contents: Vec<u8>,
689    },
690}
691
692#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
693pub enum DbValueKeyUsage {
694    JwsEs256,
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: 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<Vec<u8>>),
805    #[serde(rename = "JR")]
806    JwsKeyRs256(Vec<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}