aboutsummaryrefslogtreecommitdiff
path: root/lockchain-core/src/traits.rs
blob: eb7c99bc3cdffe9318d37f232a299906443a3a0d (plain)
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
//! Common vault traits for plugin-crates
//!
//! The core of this crate has no functionality and is dependant
//! on other libraries to fill those holes. To make this easer
//! (and sometimes possible), we defined a few common behaviours
//! in traits to expand on in implementations specific to the
//! library.
//!
//! Each trait is documented in more detail and provides default
//! implementations with `unimplemented!` macros to make
//! compilation work without external crates but not calling
//! functions at runtime.

use crate::errors::VaultError;
use crate::init::Generator;
use crate::meta::{MetaDomain, VaultMetadata};
use crate::record::{EncryptedBody, Header, Payload, Record};
use serde::{de::DeserializeOwned, Serialize};
use crate::users::{Access, Token};

use base64;
use serde_json::{self, Error as SerdeError};
use std::error::Error;

/// A Body trait that can be implemented to hook into the generic Record
/// data module.
///
/// This allows working with both encrypted and cleartext data bodies.
pub trait Body: DeserializeOwned + Serialize + Send {
    ///Get the value of a field from this body
    fn get_field(&self, key: &str) -> Option<&Payload>;
    /// Set the value of a field
    fn set_field(&mut self, key: &str, value: Payload) -> Option<()>;
    /// Remove versioning and flatten the data tree to a single level.
    fn flatten(&mut self) -> Option<()>;
}

/// A simple trait that allows libraries to hook into the
/// `body()` and `record()` hooks for vault records.
pub trait LoadRecord<T: Body> {
    fn header() -> Header {
        unimplemented!()
    }

    fn body() -> T {
        unimplemented!()
    }
}

/// This is purely a marker trait for encryptable types
///
/// Indicates that a type should be handlable by an encryption
/// engine, also relying on the auto encoder functionality.
///
/// Additional functions might be added to this trait further down
/// the road but for now, it's really just a marker that you can easily
/// implement for any type that's also `AutoEncoder`
///
// TODO: Add documentation test code in again
pub trait Encryptable: AutoEncoder {}

/// A base trait that describes the basic functionality of
/// an encryption backend which handles encrypted files.
///
/// Encryption is never done directly on the bodies, only via
/// this scheduler type with the help of the [[Encryptable]] trait.
pub trait EncryptionHandler<T: Encryptable> {
    fn encrypt(&mut self, item: T) -> EncryptedBody;
    fn decrypt(&mut self, item: EncryptedBody) -> Option<T>;
}

/// An abstract file loading utility trait
///
/// Any type that implements `FileIO` also has to be
/// `AutoEncoder` in order to be storable. This trait implements
/// common file I/O operations, assuming that any type using it
/// will then provide the required utility functions.
pub trait FileIO: AutoEncoder {
    /// Load a type from a file path
    fn load(path: &str) -> Result<Self, Box<Error>> {
        use std::fs;
        fs::read_to_string(path)
            .and_then(|s| Self::decode(&s).map_err(|e| e.into()))
            .map_err(|e| e.into())
    }

    /// Store a type to a file path
    fn save(&self, path: &str) -> Result<(), Box<Error>> {
        use std::fs::OpenOptions;
        use std::io::Write;

        let mut file = OpenOptions::new().write(true).create(true).open(path)?;
        let content = self.encode()?;
        file.write_all(content.as_bytes())?;

        Ok(())
    }
}

/// A comprehensive trait describing a generic vault
///
/// It describes the workflow around a vault, no matter how it
/// is implemented or what backing storage it's using.
///
/// A vault is, at it's core, a collection of records and a collcetion
/// of metadata items, including users, keys and autherisation info. Both
/// the userstore and keystore are implemented via the metadata
/// system, which is a simplification of a record.
///
/// The vault API **is stateful** which means that a user needs to be
/// authenticated to aquire a token which is then used to verify all
/// future transactions. In case the vault is in-memory only, the
/// authentication will need to be backed by some persistence layer
/// (i.e. lockchain-files)
///
pub trait Vault<T>: Send + LoadRecord<T>
where
    T: Body,
{
    /// Consumes a vault generator to construct a vault
    fn new(_: Generator) -> Result<Box<Self>, VaultError>;
    /// Load and open an existing vault
    fn load(name: &str, location: &str) -> Result<Box<Self>, VaultError>;
    /// Unlock the vault for a specific user
    fn authenticate(&mut self, username: &str, secret: &str) -> Token;
    /// End a specific user session
    fn deauthenticate(&mut self, username: &str, _: Token);
    /// Create a new user with a list of initial access rights
    ///
    /// **Important Note** A backend can make no guarantee for the safety
    /// of it's persistence. This means that a client library author is
    /// responsible for encrypting all required secrets **before** submitting
    /// them to a vault backend!
    fn create_user(
        &mut self,
        token: Token,
        username: &str,
        secret: Vec<u8>,
        access: Vec<Access>,
    ) -> Result<(), ()>;
    /// Delete a user
    fn delete_user(&mut self, token: Token, username: &str);
    // / Modify user data, if authenticated as said user
    // fn modify_user(&mut self, token: Token, username: &str) -> Option<&mut User>;

    /// Get basic vault metadata
    fn metadata(&self) -> VaultMetadata;
    /// Fetch metadata headers for all records
    fn fetch(&mut self);
    /// Pull a specific record from the backend
    fn pull(&mut self, name: &str);
    /// Sync all changes back to the backend
    ///
    /// Ultimately it's up to the backend to decide
    /// how changes are synced.
    /// It's free to ignore any sync requests
    /// but they can still be made for backends
    /// which explicitly promise sync requests compliance
    fn sync(&mut self);

    /// Get a complete record from this vault
    fn get_record(&self, name: &str) -> Option<&Record<T>>;
    /// Probe if a record is contained
    fn contains(&self, name: &str) -> bool;
    /// Add a new record to this vault
    fn add_record(&mut self, key: &str, category: &str, tags: Vec<&str>);
    /// Delete a record from this vault
    fn delete_record(&mut self, record: &str) -> Option<Record<T>>;

    /// Add data to an existing record, overwriting existing fields
    fn add_data(&mut self, record: &str, key: &str, data: Payload) -> Option<()>;
    /// Get the (latest) value of a specific record data field
    fn get_data(&self, record: &str, key: &str) -> Option<&Payload>;

    /// Adds a domain space to the metadata store inside the vault
    fn meta_add_domain(&mut self, domain: &str) -> Option<()>;
    /// Returns all records from a meta domain
    fn meta_pull_domain(&self, domain: &str) -> Option<&MetaDomain>;
    /// Entirely replace a meta domain in the store
    fn meta_push_domain(&mut self, domain: MetaDomain) -> Option<()>;
    /// Set the value of a field inside a domain. Field names **must not** collide
    fn meta_set(&mut self, domain: &str, name: &str, data: Payload) -> Option<()>;
    /// Get the value of a (unique) field inside a domain
    fn meta_get(&mut self, domain: &str, name: &str) -> Option<Payload>;
    /// Check if a metadomain exists, regardless of data or depth
    fn meta_exists(&self, domain: &str) -> bool;
}

/// Auto-implement this trait to serialise types to json
pub trait AutoEncoder: Serialize + DeserializeOwned {
    fn encode(&self) -> Result<String, SerdeError> {
        serde_json::to_string(self)
    }

    fn decode(s: &str) -> Result<Self, SerdeError> {
        serde_json::from_str(s)
    }
}

/// Include this trait to monkey-patch base64 functions onto String types
pub trait Base64AutoEncoder {
    fn to_base64(&self) -> String;
    fn from_base64(base64: &str) -> String;
}

impl Base64AutoEncoder for String {
    /// Automatically encode this string to base64
    fn to_base64(&self) -> String {
        base64::encode(self.as_bytes())
    }

    /// Craft a string from an existing base64 string slice
    fn from_base64(base64: &str) -> String {
        String::from_utf8(base64::decode(base64).unwrap()).unwrap()
    }
}

impl Base64AutoEncoder for Vec<u8> {
    /// Automatically encode this string to base64
    fn to_base64(&self) -> String {
        base64::encode(self)
    }

    /// Craft a string from an existing base64 string slice
    fn from_base64(base64: &str) -> String {
        String::from_utf8(base64::decode(base64).unwrap()).unwrap()
    }
}