1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! Common record representation inside of vaults.
//!
//! A record is a collection of data stored in a vault. It's
//! structured into a publicly known, unencrypted header and
//! a securely saved, encrypted body.
//!
//! While the `lockchain-server` never has access to the body data,
//! the header is stored and cached for make search requests faster.
//!
//! **No secret information should ever be stored in the header**

use chrono::{DateTime, Local};
use std::collections::BTreeMap;
use traits::{AutoEncoder, Body};

/// An enum that wraps around all possible data types to store
/// as the value of a vault record.
///
/// This doesn't include metadata attached to a field, just the
/// data representation itself (i.e. text, number or sub data-tree)
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum Payload {
    /// A simple (variable) text
    Text(String),
    /// A boolean (true, false)
    Boolean(bool),
    /// A 64bit, signed number
    Number(i64),
    /// A tree of String names, mapped to payloads
    BTreeMap(BTreeMap<String, Payload>),
    /// A list of various payloads
    List(Vec<Payload>),
}

/// The public header of a record
///
/// A header consists of always-available fields that
/// are hard-defined in the lockchain file format as well
/// as custom fields that can be declared by each application
/// specifically.
///
/// You should never rely on the presence of custom fields as
/// older version of the software might not support them or
/// know about them!
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub struct Header {
    /// The name of this record
    pub name: String,
    /// The primary category the record is in
    pub category: String,
    /// A collection of custom tags
    pub tags: Vec<String>,
    /// Custom fields to query by. **Do not store secure data in fields!
    pub fields: BTreeMap<String, Payload>,
    /// Timestamp when the record was created
    pub date_created: DateTime<Local>,
    /// Timestamp when the record was last updated
    pub date_updated: DateTime<Local>,
}

/// Represents a whole record in memory
///
/// The body field can be `None` if it hasn't been cached
/// yet. Calling `body()` will either resolve the data from disk
/// or still return `None` if the current setting doesn't support
/// body loading (such as the  `lockchain-server` which has no
/// cryptocraphy subsystem)
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub struct Record<T: Body> {
    pub header: Header,
    #[serde(bound(deserialize = "T: Body"))]
    pub body: Option<T>,
}

impl<T: Body> Record<T> {
    /// Create a new Record, generically for a backend in question
    pub fn new(name: &str, category: &str, tags: Vec<&str>) -> Self {
        Record {
            header: Header {
                name: name.to_owned(),
                category: category.to_owned(),
                tags: tags.into_iter().map(|s| s.to_owned()).collect(),
                fields: BTreeMap::new(),
                date_created: Local::now(),
                date_updated: Local::now(),
            },
            body: None,
        }
    }

    /// Attempt to set a key to a certain value
    pub fn add_data(&mut self, key: &str, value: Payload) -> Option<()> {
        (self.body.as_mut()?).set_field(key, value);
        Some(())
    }

    /// Attempt to read out the value of a certain key
    pub fn get_data(&self, key: &str) -> Option<&Payload> {
        (self.body.as_ref()?).get_field(key)
    }
}

impl<T: Body> AutoEncoder for Record<T> {}

#[derive(Serialize, Deserialize)]
pub struct EncryptedBody {
    pub data: String,
}

impl Body for EncryptedBody {
    fn get_field(&self, _: &str) -> Option<&Payload> {
        None
    }
    fn set_field(&mut self, _: &str, _: Payload) -> Option<()> {
        None
    }
    fn flatten(&mut self) -> Option<()> {
        None
    }
}