1
0
Fork 0
mirror of https://github.com/dani-garcia/vaultwarden.git synced 2025-08-24 11:43:19 +00:00

Merge pull request #493 from endyman/feature/initial_mysql_support

Initial support for mysql
This commit is contained in:
Daniel García 2019-06-01 23:33:06 +02:00 committed by GitHub
commit 1322b876e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 610 additions and 134 deletions

View file

@ -2,7 +2,6 @@ use std::ops::Deref;
use diesel::r2d2;
use diesel::r2d2::ConnectionManager;
use diesel::sqlite::SqliteConnection;
use diesel::{Connection as DieselConnection, ConnectionError};
use rocket::http::Status;
@ -17,16 +16,25 @@ use crate::error::Error;
use crate::CONFIG;
/// An alias to the database connection used
type Connection = SqliteConnection;
#[cfg(feature = "sqlite")]
type Connection = diesel::sqlite::SqliteConnection;
#[cfg(feature = "mysql")]
type Connection = diesel::mysql::MysqlConnection;
/// An alias to the type for a pool of Diesel SQLite connections.
/// An alias to the type for a pool of Diesel connections.
type Pool = r2d2::Pool<ConnectionManager<Connection>>;
/// Connection request guard type: a wrapper around an r2d2 pooled connection.
pub struct DbConn(pub r2d2::PooledConnection<ConnectionManager<Connection>>);
pub mod models;
#[cfg(feature = "sqlite")]
#[path = "schemas/sqlite/schema.rs"]
pub mod schema;
#[cfg(feature = "mysql")]
#[path = "schemas/mysql/schema.rs"]
pub mod schema;
/// Initializes a database pool.
pub fn init_pool() -> Pool {
@ -36,7 +44,9 @@ pub fn init_pool() -> Pool {
}
pub fn get_connection() -> Result<Connection, ConnectionError> {
Connection::establish(&CONFIG.database_url())
let url = CONFIG.database_url();
println!("{}", url.to_string());
Connection::establish(&url)
}
/// Creates a back-up of the database using sqlite3

View file

@ -12,7 +12,7 @@ pub struct Attachment {
pub cipher_uuid: String,
pub file_name: String,
pub file_size: i32,
pub key: Option<String>,
pub akey: Option<String>,
}
/// Local methods
@ -23,7 +23,7 @@ impl Attachment {
cipher_uuid,
file_name,
file_size,
key: None,
akey: None,
}
}
@ -43,7 +43,7 @@ impl Attachment {
"FileName": self.file_name,
"Size": self.file_size.to_string(),
"SizeName": display_size,
"Key": self.key,
"Key": self.akey,
"Object": "attachment"
})
}

View file

@ -24,7 +24,7 @@ pub struct Cipher {
Card = 3,
Identity = 4
*/
pub type_: i32,
pub atype: i32,
pub name: String,
pub notes: Option<String>,
pub fields: Option<String>,
@ -37,7 +37,7 @@ pub struct Cipher {
/// Local methods
impl Cipher {
pub fn new(type_: i32, name: String) -> Self {
pub fn new(atype: i32, name: String) -> Self {
let now = Utc::now().naive_utc();
Self {
@ -48,7 +48,7 @@ impl Cipher {
user_uuid: None,
organization_uuid: None,
type_,
atype,
favorite: false,
name,
@ -94,7 +94,7 @@ impl Cipher {
// TODO: ******* Backwards compat start **********
// To remove backwards compatibility, just remove this entire section
// and remove the compat code from ciphers::update_cipher_from_data
if self.type_ == 1 && data_json["Uris"].is_array() {
if self.atype == 1 && data_json["Uris"].is_array() {
let uri = data_json["Uris"][0]["Uri"].clone();
data_json["Uri"] = uri;
}
@ -102,7 +102,7 @@ impl Cipher {
let mut json_object = json!({
"Id": self.uuid,
"Type": self.type_,
"Type": self.atype,
"RevisionDate": format_date(&self.updated_at),
"FolderId": self.get_folder_uuid(&user_uuid, &conn),
"Favorite": self.favorite,
@ -123,7 +123,7 @@ impl Cipher {
"PasswordHistory": password_history_json,
});
let key = match self.type_ {
let key = match self.atype {
1 => "Login",
2 => "SecureNote",
3 => "Card",
@ -237,7 +237,7 @@ impl Cipher {
// Cipher owner
users_organizations::access_all.eq(true).or(
// access_all in Organization
users_organizations::type_.le(UserOrgType::Admin as i32).or(
users_organizations::atype.le(UserOrgType::Admin as i32).or(
// Org admin or owner
users_collections::user_uuid.eq(user_uuid).and(
users_collections::read_only.eq(false), //R/W access to collection
@ -268,7 +268,7 @@ impl Cipher {
// Cipher owner
users_organizations::access_all.eq(true).or(
// access_all in Organization
users_organizations::type_.le(UserOrgType::Admin as i32).or(
users_organizations::atype.le(UserOrgType::Admin as i32).or(
// Org admin or owner
users_collections::user_uuid.eq(user_uuid), // Access to Collection
),
@ -315,7 +315,7 @@ impl Cipher {
))
.filter(ciphers::user_uuid.eq(user_uuid).or( // Cipher owner
users_organizations::access_all.eq(true).or( // access_all in Organization
users_organizations::type_.le(UserOrgType::Admin as i32).or( // Org admin or owner
users_organizations::atype.le(UserOrgType::Admin as i32).or( // Org admin or owner
users_collections::user_uuid.eq(user_uuid).and( // Access to Collection
users_organizations::status.eq(UserOrgStatus::Confirmed as i32)
)
@ -365,7 +365,7 @@ impl Cipher {
.filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
.filter(users_collections::user_uuid.eq(user_id).or( // User has access to collection
users_organizations::access_all.eq(true).or( // User has access all
users_organizations::type_.le(UserOrgType::Admin as i32) // User is admin or owner
users_organizations::atype.le(UserOrgType::Admin as i32) // User is admin or owner
)
))
.select(ciphers_collections::collection_uuid)

View file

@ -146,7 +146,7 @@ impl Collection {
.filter(
users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection
users_organizations::access_all.eq(true).or( // access_all in Organization
users_organizations::type_.le(UserOrgType::Admin as i32) // Org admin or owner
users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner
)
)
).select(collections::all_columns)

View file

@ -15,7 +15,7 @@ pub struct Device {
pub name: String,
/// https://github.com/bitwarden/core/tree/master/src/Core/Enums
pub type_: i32,
pub atype: i32,
pub push_token: Option<String>,
pub refresh_token: String,
@ -25,7 +25,7 @@ pub struct Device {
/// Local methods
impl Device {
pub fn new(uuid: String, user_uuid: String, name: String, type_: i32) -> Self {
pub fn new(uuid: String, user_uuid: String, name: String, atype: i32) -> Self {
let now = Utc::now().naive_utc();
Self {
@ -35,7 +35,7 @@ impl Device {
user_uuid,
name,
type_,
atype,
push_token: None,
refresh_token: String::new(),
@ -70,10 +70,10 @@ impl Device {
let time_now = Utc::now().naive_utc();
self.updated_at = time_now;
let orgowner: Vec<_> = orgs.iter().filter(|o| o.type_ == 0).map(|o| o.org_uuid.clone()).collect();
let orgadmin: Vec<_> = orgs.iter().filter(|o| o.type_ == 1).map(|o| o.org_uuid.clone()).collect();
let orguser: Vec<_> = orgs.iter().filter(|o| o.type_ == 2).map(|o| o.org_uuid.clone()).collect();
let orgmanager: Vec<_> = orgs.iter().filter(|o| o.type_ == 3).map(|o| o.org_uuid.clone()).collect();
let orgowner: Vec<_> = orgs.iter().filter(|o| o.atype == 0).map(|o| o.org_uuid.clone()).collect();
let orgadmin: Vec<_> = orgs.iter().filter(|o| o.atype == 1).map(|o| o.org_uuid.clone()).collect();
let orguser: Vec<_> = orgs.iter().filter(|o| o.atype == 2).map(|o| o.org_uuid.clone()).collect();
let orgmanager: Vec<_> = orgs.iter().filter(|o| o.atype == 3).map(|o| o.org_uuid.clone()).collect();
// Create the JWT claims struct, to send to the client

View file

@ -21,9 +21,9 @@ pub struct UserOrganization {
pub org_uuid: String,
pub access_all: bool,
pub key: String,
pub akey: String,
pub status: i32,
pub type_: i32,
pub atype: i32,
}
pub enum UserOrgStatus {
@ -196,9 +196,9 @@ impl UserOrganization {
org_uuid,
access_all: false,
key: String::new(),
akey: String::new(),
status: UserOrgStatus::Accepted as i32,
type_: UserOrgType::User as i32,
atype: UserOrgType::User as i32,
}
}
}
@ -266,9 +266,9 @@ impl UserOrganization {
"MaxStorageGb": 10, // The value doesn't matter, we don't check server-side
// These are per user
"Key": self.key,
"Key": self.akey,
"Status": self.status,
"Type": self.type_,
"Type": self.atype,
"Enabled": true,
"Object": "profileOrganization",
@ -285,7 +285,7 @@ impl UserOrganization {
"Email": user.email,
"Status": self.status,
"Type": self.type_,
"Type": self.atype,
"AccessAll": self.access_all,
"Object": "organizationUserUserDetails",
@ -315,7 +315,7 @@ impl UserOrganization {
"UserId": self.user_uuid,
"Status": self.status,
"Type": self.type_,
"Type": self.atype,
"AccessAll": self.access_all,
"Collections": coll_uuids,
@ -357,7 +357,7 @@ impl UserOrganization {
}
pub fn has_full_access(self) -> bool {
self.access_all || self.type_ >= UserOrgType::Admin
self.access_all || self.atype >= UserOrgType::Admin
}
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
@ -405,10 +405,10 @@ impl UserOrganization {
.expect("Error loading user organizations")
}
pub fn find_by_org_and_type(org_uuid: &str, type_: i32, conn: &DbConn) -> Vec<Self> {
pub fn find_by_org_and_type(org_uuid: &str, atype: i32, conn: &DbConn) -> Vec<Self> {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::type_.eq(type_))
.filter(users_organizations::atype.eq(atype))
.load::<Self>(&**conn)
.expect("Error loading user organizations")
}

View file

@ -9,7 +9,7 @@ use super::User;
pub struct TwoFactor {
pub uuid: String,
pub user_uuid: String,
pub type_: i32,
pub atype: i32,
pub enabled: bool,
pub data: String,
}
@ -32,11 +32,11 @@ pub enum TwoFactorType {
/// Local methods
impl TwoFactor {
pub fn new(user_uuid: String, type_: TwoFactorType, data: String) -> Self {
pub fn new(user_uuid: String, atype: TwoFactorType, data: String) -> Self {
Self {
uuid: crate::util::get_uuid(),
user_uuid,
type_: type_ as i32,
atype: atype as i32,
enabled: true,
data,
}
@ -53,7 +53,7 @@ impl TwoFactor {
pub fn to_json_list(&self) -> Value {
json!({
"Enabled": self.enabled,
"Type": self.type_,
"Type": self.atype,
"Object": "twoFactorProvider"
})
}
@ -85,15 +85,15 @@ impl TwoFactor {
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
twofactor::table
.filter(twofactor::user_uuid.eq(user_uuid))
.filter(twofactor::type_.lt(1000)) // Filter implementation types
.filter(twofactor::atype.lt(1000)) // Filter implementation types
.load::<Self>(&**conn)
.expect("Error loading twofactor")
}
pub fn find_by_user_and_type(user_uuid: &str, type_: i32, conn: &DbConn) -> Option<Self> {
pub fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &DbConn) -> Option<Self> {
twofactor::table
.filter(twofactor::user_uuid.eq(user_uuid))
.filter(twofactor::type_.eq(type_))
.filter(twofactor::atype.eq(atype))
.first::<Self>(&**conn)
.ok()
}

View file

@ -20,7 +20,7 @@ pub struct User {
pub password_iterations: i32,
pub password_hint: Option<String>,
pub key: String,
pub akey: String,
pub private_key: Option<String>,
pub public_key: Option<String>,
@ -58,7 +58,7 @@ impl User {
updated_at: now,
name: email.clone(),
email,
key: String::new(),
akey: String::new(),
password_hash: Vec::new(),
salt: crypto::get_random_64(),
@ -140,7 +140,7 @@ impl User {
"MasterPasswordHint": self.password_hint,
"Culture": "en-US",
"TwoFactorEnabled": twofactor_enabled,
"Key": self.key,
"Key": self.akey,
"PrivateKey": self.private_key,
"SecurityStamp": self.security_stamp,
"Organizations": orgs_json,
@ -163,7 +163,7 @@ impl User {
pub fn delete(self, conn: &DbConn) -> EmptyResult {
for user_org in UserOrganization::find_by_user(&self.uuid, &*conn) {
if user_org.type_ == UserOrgType::Owner {
if user_org.atype == UserOrgType::Owner {
let owner_type = UserOrgType::Owner as i32;
if UserOrganization::find_by_org_and_type(&user_org.org_uuid, owner_type, &conn).len() <= 1 {
err!("Can't delete last owner")

View file

@ -0,0 +1,172 @@
table! {
attachments (id) {
id -> Varchar,
cipher_uuid -> Varchar,
file_name -> Text,
file_size -> Integer,
akey -> Nullable<Text>,
}
}
table! {
ciphers (uuid) {
uuid -> Varchar,
created_at -> Datetime,
updated_at -> Datetime,
user_uuid -> Nullable<Varchar>,
organization_uuid -> Nullable<Varchar>,
atype -> Integer,
name -> Text,
notes -> Nullable<Text>,
fields -> Nullable<Text>,
data -> Text,
favorite -> Bool,
password_history -> Nullable<Text>,
}
}
table! {
ciphers_collections (cipher_uuid, collection_uuid) {
cipher_uuid -> Varchar,
collection_uuid -> Varchar,
}
}
table! {
collections (uuid) {
uuid -> Varchar,
org_uuid -> Varchar,
name -> Text,
}
}
table! {
devices (uuid) {
uuid -> Varchar,
created_at -> Datetime,
updated_at -> Datetime,
user_uuid -> Varchar,
name -> Text,
atype -> Integer,
push_token -> Nullable<Text>,
refresh_token -> Text,
twofactor_remember -> Nullable<Text>,
}
}
table! {
folders (uuid) {
uuid -> Varchar,
created_at -> Datetime,
updated_at -> Datetime,
user_uuid -> Varchar,
name -> Text,
}
}
table! {
folders_ciphers (cipher_uuid, folder_uuid) {
cipher_uuid -> Varchar,
folder_uuid -> Varchar,
}
}
table! {
invitations (email) {
email -> Varchar,
}
}
table! {
organizations (uuid) {
uuid -> Varchar,
name -> Text,
billing_email -> Text,
}
}
table! {
twofactor (uuid) {
uuid -> Varchar,
user_uuid -> Varchar,
atype -> Integer,
enabled -> Bool,
data -> Text,
}
}
table! {
users (uuid) {
uuid -> Varchar,
created_at -> Datetime,
updated_at -> Datetime,
email -> Varchar,
name -> Text,
password_hash -> Blob,
salt -> Blob,
password_iterations -> Integer,
password_hint -> Nullable<Text>,
akey -> Text,
private_key -> Nullable<Text>,
public_key -> Nullable<Text>,
totp_secret -> Nullable<Text>,
totp_recover -> Nullable<Text>,
security_stamp -> Text,
equivalent_domains -> Text,
excluded_globals -> Text,
client_kdf_type -> Integer,
client_kdf_iter -> Integer,
}
}
table! {
users_collections (user_uuid, collection_uuid) {
user_uuid -> Varchar,
collection_uuid -> Varchar,
read_only -> Bool,
}
}
table! {
users_organizations (uuid) {
uuid -> Varchar,
user_uuid -> Varchar,
org_uuid -> Varchar,
access_all -> Bool,
akey -> Text,
status -> Integer,
atype -> Integer,
}
}
joinable!(attachments -> ciphers (cipher_uuid));
joinable!(ciphers -> organizations (organization_uuid));
joinable!(ciphers -> users (user_uuid));
joinable!(ciphers_collections -> ciphers (cipher_uuid));
joinable!(ciphers_collections -> collections (collection_uuid));
joinable!(collections -> organizations (org_uuid));
joinable!(devices -> users (user_uuid));
joinable!(folders -> users (user_uuid));
joinable!(folders_ciphers -> ciphers (cipher_uuid));
joinable!(folders_ciphers -> folders (folder_uuid));
joinable!(twofactor -> users (user_uuid));
joinable!(users_collections -> collections (collection_uuid));
joinable!(users_collections -> users (user_uuid));
joinable!(users_organizations -> organizations (org_uuid));
joinable!(users_organizations -> users (user_uuid));
allow_tables_to_appear_in_same_query!(
attachments,
ciphers,
ciphers_collections,
collections,
devices,
folders,
folders_ciphers,
invitations,
organizations,
twofactor,
users,
users_collections,
users_organizations,
);

View file

@ -4,7 +4,7 @@ table! {
cipher_uuid -> Text,
file_name -> Text,
file_size -> Integer,
key -> Nullable<Text>,
akey -> Nullable<Text>,
}
}
@ -15,8 +15,7 @@ table! {
updated_at -> Timestamp,
user_uuid -> Nullable<Text>,
organization_uuid -> Nullable<Text>,
#[sql_name = "type"]
type_ -> Integer,
atype -> Integer,
name -> Text,
notes -> Nullable<Text>,
fields -> Nullable<Text>,
@ -48,8 +47,7 @@ table! {
updated_at -> Timestamp,
user_uuid -> Text,
name -> Text,
#[sql_name = "type"]
type_ -> Integer,
atype -> Integer,
push_token -> Nullable<Text>,
refresh_token -> Text,
twofactor_remember -> Nullable<Text>,
@ -91,8 +89,7 @@ table! {
twofactor (uuid) {
uuid -> Text,
user_uuid -> Text,
#[sql_name = "type"]
type_ -> Integer,
atype -> Integer,
enabled -> Bool,
data -> Text,
}
@ -109,7 +106,7 @@ table! {
salt -> Binary,
password_iterations -> Integer,
password_hint -> Nullable<Text>,
key -> Text,
akey -> Text,
private_key -> Nullable<Text>,
public_key -> Nullable<Text>,
totp_secret -> Nullable<Text>,
@ -136,10 +133,9 @@ table! {
user_uuid -> Text,
org_uuid -> Text,
access_all -> Bool,
key -> Text,
akey -> Text,
status -> Integer,
#[sql_name = "type"]
type_ -> Integer,
atype -> Integer,
}
}
@ -173,4 +169,4 @@ allow_tables_to_appear_in_same_query!(
users,
users_collections,
users_organizations,
);
);