This commit is contained in:
Daniel García
2021-03-14 23:35:55 +01:00
parent 2969e87b52
commit 8da5b99482
20 changed files with 812 additions and 2 deletions
+1
View File
@@ -28,6 +28,7 @@
# RSA_KEY_FILENAME=data/rsa_key
# ICON_CACHE_FOLDER=data/icon_cache
# ATTACHMENTS_FOLDER=data/attachments
# SENDS_FOLDER=data/sends
## Templates data folder, by default uses embedded templates
## Check source code to see the format
@@ -0,0 +1 @@
DROP TABLE sends;
@@ -0,0 +1,25 @@
CREATE TABLE sends (
uuid CHAR(36) NOT NULL PRIMARY KEY,
user_uuid CHAR(36) REFERENCES users (uuid),
organization_uuid CHAR(36) REFERENCES organizations (uuid),
name TEXT NOT NULL,
notes TEXT,
atype INTEGER NOT NULL,
data TEXT NOT NULL,
key TEXT NOT NULL,
password_hash BLOB,
password_salt BLOB,
password_iter INTEGER,
max_access_count INTEGER,
access_count INTEGER NOT NULL,
creation_date DATETIME NOT NULL,
revision_date DATETIME NOT NULL,
expiration_date DATETIME,
deletion_date DATETIME NOT NULL,
disabled BOOLEAN NOT NULL
);
@@ -0,0 +1 @@
DROP TABLE sends;
@@ -0,0 +1,25 @@
CREATE TABLE sends (
uuid CHAR(36) NOT NULL PRIMARY KEY,
user_uuid CHAR(36) REFERENCES users (uuid),
organization_uuid CHAR(36) REFERENCES organizations (uuid),
name TEXT NOT NULL,
notes TEXT,
atype INTEGER NOT NULL,
data TEXT NOT NULL,
key TEXT NOT NULL,
password_hash BYTEA,
password_salt BYTEA,
password_iter INTEGER,
max_access_count INTEGER,
access_count INTEGER NOT NULL,
creation_date TIMESTAMP NOT NULL,
revision_date TIMESTAMP NOT NULL,
expiration_date TIMESTAMP,
deletion_date TIMESTAMP NOT NULL,
disabled BOOLEAN NOT NULL
);
@@ -0,0 +1 @@
DROP TABLE sends;
@@ -0,0 +1,25 @@
CREATE TABLE sends (
uuid TEXT NOT NULL PRIMARY KEY,
user_uuid TEXT REFERENCES users (uuid),
organization_uuid TEXT REFERENCES organizations (uuid),
name TEXT NOT NULL,
notes TEXT,
atype INTEGER NOT NULL,
data TEXT NOT NULL,
key TEXT NOT NULL,
password_hash BLOB,
password_salt BLOB,
password_iter INTEGER,
max_access_count INTEGER,
access_count INTEGER NOT NULL,
creation_date DATETIME NOT NULL,
revision_date DATETIME NOT NULL,
expiration_date DATETIME,
deletion_date DATETIME NOT NULL,
disabled BOOLEAN NOT NULL
);
+7
View File
@@ -104,6 +104,12 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
.collect();
let sends = Send::find_by_user(&headers.user.uuid, &conn);
let sends_json: Vec<Value> = sends
.iter()
.map(|s| s.to_json())
.collect();
let domains_json = if data.exclude_domains {
Value::Null
} else {
@@ -117,6 +123,7 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
"Policies": policies_json,
"Ciphers": ciphers_json,
"Domains": domains_json,
"Sends": sends_json,
"Object": "sync"
})))
}
+2
View File
@@ -3,6 +3,7 @@ mod ciphers;
mod folders;
mod organizations;
pub mod two_factor;
mod sends;
pub fn routes() -> Vec<Route> {
let mut mod_routes = routes![
@@ -20,6 +21,7 @@ pub fn routes() -> Vec<Route> {
routes.append(&mut folders::routes());
routes.append(&mut organizations::routes());
routes.append(&mut two_factor::routes());
routes.append(&mut sends::routes());
routes.append(&mut mod_routes);
routes
File diff suppressed because it is too large Load Diff
+4
View File
@@ -394,6 +394,10 @@ pub enum UpdateType {
LogOut = 11,
SyncSendCreate = 12,
SyncSendUpdate = 13,
SyncSendDelete = 14,
None = 100,
}
+6 -1
View File
@@ -10,7 +10,7 @@ pub fn routes() -> Vec<Route> {
// If addding more routes here, consider also adding them to
// crate::utils::LOGGED_ROUTES to make sure they appear in the log
if CONFIG.web_vault_enabled() {
routes![web_index, app_id, web_files, attachments, alive, static_files]
routes![web_index, app_id, web_files, attachments, sends, alive, static_files]
} else {
routes![attachments, alive, static_files]
}
@@ -60,6 +60,11 @@ fn attachments(uuid: String, file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new(&CONFIG.attachments_folder()).join(uuid).join(file)).ok()
}
#[get("/sends/<send_id>/<file_id>")]
fn sends(send_id: String, file_id: String) -> Option<NamedFile> {
NamedFile::open(Path::new(&CONFIG.sends_folder()).join(send_id).join(file_id)).ok()
}
#[get("/alive")]
fn alive() -> Json<String> {
use crate::util::format_date;
+2
View File
@@ -299,6 +299,8 @@ make_config! {
icon_cache_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "icon_cache");
/// Attachments folder
attachments_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "attachments");
/// Sends folder
sends_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "sends");
/// Templates folder
templates_folder: String, false, auto, |c| format!("{}/{}", c.data_folder, "templates");
/// Session JWT key
+2
View File
@@ -8,6 +8,7 @@ mod org_policy;
mod organization;
mod two_factor;
mod user;
mod send;
pub use self::attachment::Attachment;
pub use self::cipher::Cipher;
@@ -19,3 +20,4 @@ pub use self::org_policy::{OrgPolicy, OrgPolicyType};
pub use self::organization::{Organization, UserOrgStatus, UserOrgType, UserOrganization};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::user::{Invitation, User, UserStampException};
pub use self::send::{Send, SendType};
+235
View File
@@ -0,0 +1,235 @@
use chrono::{NaiveDateTime, Utc};
use serde_json::Value;
use super::{Organization, User};
db_object! {
#[derive(Identifiable, Queryable, Insertable, Associations, AsChangeset)]
#[table_name = "sends"]
#[changeset_options(treat_none_as_null="true")]
#[belongs_to(User, foreign_key = "user_uuid")]
#[belongs_to(Organization, foreign_key = "organization_uuid")]
#[primary_key(uuid)]
pub struct Send {
pub uuid: String,
pub user_uuid: Option<String>,
pub organization_uuid: Option<String>,
pub name: String,
pub notes: Option<String>,
pub atype: i32,
pub data: String,
pub key: String,
pub password_hash: Option<Vec<u8>>,
password_salt: Option<Vec<u8>>,
password_iter: Option<i32>,
pub max_access_count: Option<i32>,
pub access_count: i32,
pub creation_date: NaiveDateTime,
pub revision_date: NaiveDateTime,
pub expiration_date: Option<NaiveDateTime>,
pub deletion_date: NaiveDateTime,
pub disabled: bool,
}
}
#[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
pub enum SendType {
Text = 0,
File = 1,
}
impl Send {
pub fn new(atype: i32, name: String, data: String, key: String, deletion_date: NaiveDateTime) -> Self {
let now = Utc::now().naive_utc();
Self {
uuid: crate::util::get_uuid(),
user_uuid: None,
organization_uuid: None,
name,
notes: None,
atype,
data,
key,
password_hash: None,
password_salt: None,
password_iter: None,
max_access_count: None,
access_count: 0,
creation_date: now,
revision_date: now,
expiration_date: None,
deletion_date,
disabled: false,
}
}
pub fn set_password(&mut self, password: Option<&str>) {
const PASSWORD_ITER: i32 = 100_000;
if let Some(password) = password {
self.password_iter = Some(PASSWORD_ITER);
let salt = crate::crypto::get_random_64();
let hash = crate::crypto::hash_password(password.as_bytes(), &salt, PASSWORD_ITER as u32);
self.password_salt = Some(salt);
self.password_hash = Some(hash);
} else {
self.password_iter = None;
self.password_salt = None;
self.password_hash = None;
}
}
pub fn check_password(&self, password: &str) -> bool {
match (&self.password_hash, &self.password_salt, self.password_iter) {
(Some(hash), Some(salt), Some(iter)) => {
crate::crypto::verify_password_hash(password.as_bytes(), salt, hash, iter as u32)
}
_ => false,
}
}
pub fn to_json(&self) -> Value {
use crate::util::format_date;
use data_encoding::BASE64URL_NOPAD;
use uuid::Uuid;
let data: Value = serde_json::from_str(&self.data).unwrap_or_default();
json!({
"Id": self.uuid,
"AccessId": BASE64URL_NOPAD.encode(Uuid::parse_str(&self.uuid).unwrap_or_default().as_bytes()),
"Type": self.atype,
"Name": self.name,
"Notes": self.notes,
"Text": if self.atype == SendType::Text as i32 { Some(&data) } else { None },
"File": if self.atype == SendType::File as i32 { Some(&data) } else { None },
"Key": self.key,
"MaxAccessCount": self.max_access_count,
"AccessCount": self.access_count,
"Password": self.password_hash.as_deref().map(|h| BASE64URL_NOPAD.encode(h)),
"Disabled": self.disabled,
"RevisionDate": format_date(&self.revision_date),
"ExpirationDate": self.expiration_date.as_ref().map(format_date),
"DeletionDate": format_date(&self.deletion_date),
"Object": "send",
})
}
}
use crate::db::DbConn;
use crate::api::EmptyResult;
use crate::error::MapResult;
impl Send {
pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
// self.update_users_revision(conn);
self.revision_date = Utc::now().naive_utc();
db_run! { conn:
sqlite, mysql {
match diesel::replace_into(sends::table)
.values(SendDb::to_db(self))
.execute(conn)
{
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(sends::table)
.filter(sends::uuid.eq(&self.uuid))
.set(SendDb::to_db(self))
.execute(conn)
.map_res("Error saving send")
}
Err(e) => Err(e.into()),
}.map_res("Error saving send")
}
postgresql {
let value = SendDb::to_db(self);
diesel::insert_into(sends::table)
.values(&value)
.on_conflict(sends::uuid)
.do_update()
.set(&value)
.execute(conn)
.map_res("Error saving send")
}
}
}
pub fn delete(&self, conn: &DbConn) -> EmptyResult {
// self.update_users_revision(conn);
db_run! { conn: {
diesel::delete(sends::table.filter(sends::uuid.eq(&self.uuid)))
.execute(conn)
.map_res("Error deleting send")
}}
}
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
for send in Self::find_by_user(user_uuid, &conn) {
send.delete(&conn)?;
}
Ok(())
}
pub fn find_by_access_id(access_id: &str, conn: &DbConn) -> Option<Self> {
use data_encoding::BASE64URL_NOPAD;
use uuid::Uuid;
let uuid_vec = match BASE64URL_NOPAD.decode(access_id.as_bytes()) {
Ok(v) => v,
Err(_) => return None,
};
let uuid = match Uuid::from_slice(&uuid_vec) {
Ok(u) => u.to_string(),
Err(_) => return None,
};
Self::find_by_uuid(&uuid, conn)
}
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
db_run! {conn: {
sends::table
.filter(sends::uuid.eq(uuid))
.first::<SendDb>(conn)
.ok()
.from_db()
}}
}
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
db_run! {conn: {
sends::table
.filter(sends::user_uuid.eq(user_uuid))
.load::<SendDb>(conn).expect("Error loading sends").from_db()
}}
}
pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
db_run! {conn: {
sends::table
.filter(sends::organization_uuid.eq(org_uuid))
.load::<SendDb>(conn).expect("Error loading sends").from_db()
}}
}
}
+2 -1
View File
@@ -177,7 +177,7 @@ impl User {
}
}
use super::{Cipher, Device, Favorite, Folder, TwoFactor, UserOrgType, UserOrganization};
use super::{Cipher, Device, Favorite, Folder, Send, TwoFactor, UserOrgType, UserOrganization};
use crate::db::DbConn;
use crate::api::EmptyResult;
@@ -263,6 +263,7 @@ impl User {
}
}
Send::delete_all_by_user(&self.uuid, conn)?;
UserOrganization::delete_all_by_user(&self.uuid, conn)?;
Cipher::delete_all_by_user(&self.uuid, conn)?;
Favorite::delete_all_by_user(&self.uuid, conn)?;
+26
View File
@@ -102,6 +102,29 @@ table! {
}
}
table! {
sends (uuid) {
uuid -> Text,
user_uuid -> Nullable<Text>,
organization_uuid -> Nullable<Text>,
name -> Text,
notes -> Nullable<Text>,
atype -> Integer,
data -> Text,
key -> Text,
password_hash -> Nullable<Binary>,
password_salt -> Nullable<Binary>,
password_iter -> Nullable<Integer>,
max_access_count -> Nullable<Integer>,
access_count -> Integer,
creation_date -> Datetime,
revision_date -> Datetime,
expiration_date -> Nullable<Datetime>,
deletion_date -> Datetime,
disabled -> Bool,
}
}
table! {
twofactor (uuid) {
uuid -> Text,
@@ -176,6 +199,8 @@ joinable!(folders -> users (user_uuid));
joinable!(folders_ciphers -> ciphers (cipher_uuid));
joinable!(folders_ciphers -> folders (folder_uuid));
joinable!(org_policies -> organizations (org_uuid));
joinable!(sends -> organizations (organization_uuid));
joinable!(sends -> users (user_uuid));
joinable!(twofactor -> users (user_uuid));
joinable!(users_collections -> collections (collection_uuid));
joinable!(users_collections -> users (user_uuid));
@@ -193,6 +218,7 @@ allow_tables_to_appear_in_same_query!(
invitations,
org_policies,
organizations,
sends,
twofactor,
users,
users_collections,
+26
View File
@@ -102,6 +102,29 @@ table! {
}
}
table! {
sends (uuid) {
uuid -> Text,
user_uuid -> Nullable<Text>,
organization_uuid -> Nullable<Text>,
name -> Text,
notes -> Nullable<Text>,
atype -> Integer,
data -> Text,
key -> Text,
password_hash -> Nullable<Binary>,
password_salt -> Nullable<Binary>,
password_iter -> Nullable<Integer>,
max_access_count -> Nullable<Integer>,
access_count -> Integer,
creation_date -> Timestamp,
revision_date -> Timestamp,
expiration_date -> Nullable<Timestamp>,
deletion_date -> Timestamp,
disabled -> Bool,
}
}
table! {
twofactor (uuid) {
uuid -> Text,
@@ -176,6 +199,8 @@ joinable!(folders -> users (user_uuid));
joinable!(folders_ciphers -> ciphers (cipher_uuid));
joinable!(folders_ciphers -> folders (folder_uuid));
joinable!(org_policies -> organizations (org_uuid));
joinable!(sends -> organizations (organization_uuid));
joinable!(sends -> users (user_uuid));
joinable!(twofactor -> users (user_uuid));
joinable!(users_collections -> collections (collection_uuid));
joinable!(users_collections -> users (user_uuid));
@@ -193,6 +218,7 @@ allow_tables_to_appear_in_same_query!(
invitations,
org_policies,
organizations,
sends,
twofactor,
users,
users_collections,
+26
View File
@@ -102,6 +102,29 @@ table! {
}
}
table! {
sends (uuid) {
uuid -> Text,
user_uuid -> Nullable<Text>,
organization_uuid -> Nullable<Text>,
name -> Text,
notes -> Nullable<Text>,
atype -> Integer,
data -> Text,
key -> Text,
password_hash -> Nullable<Binary>,
password_salt -> Nullable<Binary>,
password_iter -> Nullable<Integer>,
max_access_count -> Nullable<Integer>,
access_count -> Integer,
creation_date -> Timestamp,
revision_date -> Timestamp,
expiration_date -> Nullable<Timestamp>,
deletion_date -> Timestamp,
disabled -> Bool,
}
}
table! {
twofactor (uuid) {
uuid -> Text,
@@ -176,6 +199,8 @@ joinable!(folders -> users (user_uuid));
joinable!(folders_ciphers -> ciphers (cipher_uuid));
joinable!(folders_ciphers -> folders (folder_uuid));
joinable!(org_policies -> organizations (org_uuid));
joinable!(sends -> organizations (organization_uuid));
joinable!(sends -> users (user_uuid));
joinable!(twofactor -> users (user_uuid));
joinable!(users_collections -> collections (collection_uuid));
joinable!(users_collections -> users (user_uuid));
@@ -193,6 +218,7 @@ allow_tables_to_appear_in_same_query!(
invitations,
org_policies,
organizations,
sends,
twofactor,
users,
users_collections,
+12
View File
@@ -220,6 +220,18 @@ macro_rules! err {
}};
}
#[macro_export]
macro_rules! err_code {
($msg:expr, $err_code: literal) => {{
error!("{}", $msg);
return Err(crate::error::Error::new($msg, $msg).with_code($err_code));
}};
($usr_msg:expr, $log_value:expr, $err_code: literal) => {{
error!("{}. {}", $usr_msg, $log_value);
return Err(crate::error::Error::new($usr_msg, $log_value).with_code($err_code));
}};
}
#[macro_export]
macro_rules! err_discard {
($msg:expr, $data:expr) => {{