Add per-user folder-cipher mapping
This commit is contained in:
parent
f578019df6
commit
514a372bc8
|
@ -0,0 +1,32 @@
|
||||||
|
ALTER TABLE ciphers RENAME TO oldCiphers;
|
||||||
|
|
||||||
|
CREATE TABLE ciphers (
|
||||||
|
uuid TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
updated_at DATETIME NOT NULL,
|
||||||
|
user_uuid TEXT REFERENCES users (uuid), -- Make this optional
|
||||||
|
organization_uuid TEXT REFERENCES organizations (uuid), -- Add reference to orgs table
|
||||||
|
-- Remove folder_uuid
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
notes TEXT,
|
||||||
|
fields TEXT,
|
||||||
|
data TEXT NOT NULL,
|
||||||
|
favorite BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE folders_ciphers (
|
||||||
|
cipher_uuid TEXT NOT NULL REFERENCES ciphers (uuid),
|
||||||
|
folder_uuid TEXT NOT NULL REFERENCES folders (uuid),
|
||||||
|
|
||||||
|
PRIMARY KEY (cipher_uuid, folder_uuid)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO ciphers (uuid, created_at, updated_at, organization_uuid, type, name, notes, fields, data, favorite)
|
||||||
|
SELECT uuid, created_at, updated_at, organization_uuid, type, name, notes, fields, data, favorite FROM oldCiphers;
|
||||||
|
|
||||||
|
INSERT INTO folders_ciphers (cipher_uuid, folder_uuid)
|
||||||
|
SELECT uuid, folder_uuid FROM oldCiphers WHERE folder_uuid IS NOT NULL;
|
||||||
|
|
||||||
|
|
||||||
|
DROP TABLE oldCiphers;
|
|
@ -29,7 +29,7 @@ fn sync(headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
let folders_json: Vec<Value> = folders.iter().map(|c| c.to_json()).collect();
|
let folders_json: Vec<Value> = folders.iter().map(|c| c.to_json()).collect();
|
||||||
|
|
||||||
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
|
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
|
||||||
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &conn)).collect();
|
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
|
||||||
|
|
||||||
let domains_json = api::core::get_eq_domains(headers).unwrap().into_inner();
|
let domains_json = api::core::get_eq_domains(headers).unwrap().into_inner();
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ fn sync(headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
|
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
|
||||||
|
|
||||||
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &conn)).collect();
|
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"Data": ciphers_json,
|
"Data": ciphers_json,
|
||||||
|
@ -62,11 +62,11 @@ fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
None => err!("Cipher doesn't exist")
|
None => err!("Cipher doesn't exist")
|
||||||
};
|
};
|
||||||
|
|
||||||
if cipher.user_uuid != headers.user.uuid {
|
if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) {
|
||||||
err!("Cipher is not owned by user")
|
err!("Cipher is not owned by user")
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(cipher.to_json(&headers.host, &conn)))
|
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -109,12 +109,12 @@ fn post_ciphers(data: Json<CipherData>, headers: Headers, conn: DbConn) -> JsonR
|
||||||
|
|
||||||
let user_uuid = headers.user.uuid.clone();
|
let user_uuid = headers.user.uuid.clone();
|
||||||
let favorite = data.favorite.unwrap_or(false);
|
let favorite = data.favorite.unwrap_or(false);
|
||||||
let mut cipher = Cipher::new(user_uuid, data.type_, data.name.clone(), favorite);
|
let mut cipher = Cipher::new(Some(user_uuid), None, data.type_, data.name.clone(), favorite);
|
||||||
|
|
||||||
update_cipher_from_data(&mut cipher, data, &headers, &conn)?;
|
update_cipher_from_data(&mut cipher, data, &headers, &conn)?;
|
||||||
cipher.save(&conn);
|
cipher.save(&conn);
|
||||||
|
|
||||||
Ok(Json(cipher.to_json(&headers.host, &conn)))
|
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Headers, conn: &DbConn) -> EmptyResult {
|
fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Headers, conn: &DbConn) -> EmptyResult {
|
||||||
|
@ -174,7 +174,7 @@ fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Head
|
||||||
// Copy the type data and change the names to the correct case
|
// Copy the type data and change the names to the correct case
|
||||||
copy_values(&type_data, &mut values);
|
copy_values(&type_data, &mut values);
|
||||||
|
|
||||||
cipher.folder_uuid = data.folderId;
|
cipher.move_to_folder(data.folderId, &headers.user.uuid, &conn);
|
||||||
cipher.name = data.name;
|
cipher.name = data.name;
|
||||||
cipher.notes = data.notes;
|
cipher.notes = data.notes;
|
||||||
cipher.fields = uppercase_fields.map(|f| f.to_string());
|
cipher.fields = uppercase_fields.map(|f| f.to_string());
|
||||||
|
@ -247,11 +247,11 @@ fn post_ciphers_import(data: Json<ImportData>, headers: Headers, conn: DbConn) -
|
||||||
|
|
||||||
let user_uuid = headers.user.uuid.clone();
|
let user_uuid = headers.user.uuid.clone();
|
||||||
let favorite = cipher_data.favorite.unwrap_or(false);
|
let favorite = cipher_data.favorite.unwrap_or(false);
|
||||||
let mut cipher = Cipher::new(user_uuid, cipher_data.type_, cipher_data.name.clone(), favorite);
|
let mut cipher = Cipher::new(Some(user_uuid), None, cipher_data.type_, cipher_data.name.clone(), favorite);
|
||||||
|
|
||||||
if update_cipher_from_data(&mut cipher, cipher_data, &headers, &conn).is_err() { err!("Error creating cipher") }
|
if update_cipher_from_data(&mut cipher, cipher_data, &headers, &conn).is_err() { err!("Error creating cipher") }
|
||||||
|
|
||||||
cipher.folder_uuid = folder_uuid;
|
//cipher.folder_uuid = folder_uuid; // TODO: This needs to create new folder-cipher mapping
|
||||||
|
|
||||||
cipher.save(&conn);
|
cipher.save(&conn);
|
||||||
index += 1;
|
index += 1;
|
||||||
|
@ -274,8 +274,8 @@ fn put_cipher(uuid: String, data: Json<CipherData>, headers: Headers, conn: DbCo
|
||||||
None => err!("Cipher doesn't exist")
|
None => err!("Cipher doesn't exist")
|
||||||
};
|
};
|
||||||
|
|
||||||
if cipher.user_uuid != headers.user.uuid {
|
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||||
err!("Cipher is not owned by user")
|
err!("Cipher is not write accessible")
|
||||||
}
|
}
|
||||||
|
|
||||||
cipher.favorite = data.favorite.unwrap_or(false);
|
cipher.favorite = data.favorite.unwrap_or(false);
|
||||||
|
@ -283,7 +283,7 @@ fn put_cipher(uuid: String, data: Json<CipherData>, headers: Headers, conn: DbCo
|
||||||
update_cipher_from_data(&mut cipher, data, &headers, &conn)?;
|
update_cipher_from_data(&mut cipher, data, &headers, &conn)?;
|
||||||
cipher.save(&conn);
|
cipher.save(&conn);
|
||||||
|
|
||||||
Ok(Json(cipher.to_json(&headers.host, &conn)))
|
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -294,8 +294,8 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
|
||||||
None => err!("Cipher doesn't exist")
|
None => err!("Cipher doesn't exist")
|
||||||
};
|
};
|
||||||
|
|
||||||
if cipher.user_uuid != headers.user.uuid {
|
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||||
err!("Cipher is not owned by user")
|
err!("Cipher is not write accessible")
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut params = content_type.params();
|
let mut params = content_type.params();
|
||||||
|
@ -323,7 +323,7 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
|
||||||
attachment.save(&conn);
|
attachment.save(&conn);
|
||||||
}).expect("Error processing multipart data");
|
}).expect("Error processing multipart data");
|
||||||
|
|
||||||
Ok(Json(cipher.to_json(&headers.host, &conn)))
|
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
|
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")]
|
||||||
|
@ -347,8 +347,8 @@ fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn
|
||||||
None => err!("Cipher doesn't exist")
|
None => err!("Cipher doesn't exist")
|
||||||
};
|
};
|
||||||
|
|
||||||
if cipher.user_uuid != headers.user.uuid {
|
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||||
err!("Cipher is not owned by user")
|
err!("Cipher cannot be deleted by user")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete attachment
|
// Delete attachment
|
||||||
|
@ -399,7 +399,7 @@ fn move_cipher_selected(data: Json<Value>, headers: Headers, conn: DbConn) -> Em
|
||||||
if folder.user_uuid != headers.user.uuid {
|
if folder.user_uuid != headers.user.uuid {
|
||||||
err!("Folder is not owned by user")
|
err!("Folder is not owned by user")
|
||||||
}
|
}
|
||||||
Some(folder_id.to_string())
|
Some(folder.uuid)
|
||||||
}
|
}
|
||||||
None => err!("Folder doesn't exist")
|
None => err!("Folder doesn't exist")
|
||||||
}
|
}
|
||||||
|
@ -424,12 +424,12 @@ fn move_cipher_selected(data: Json<Value>, headers: Headers, conn: DbConn) -> Em
|
||||||
None => err!("Cipher doesn't exist")
|
None => err!("Cipher doesn't exist")
|
||||||
};
|
};
|
||||||
|
|
||||||
if cipher.user_uuid != headers.user.uuid {
|
if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) {
|
||||||
err!("Cipher is not owned by user")
|
err!("Cipher is not accessible by user")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move cipher
|
// Move cipher
|
||||||
cipher.folder_uuid = folder_id.clone();
|
cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn);
|
||||||
cipher.save(&conn);
|
cipher.save(&conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,8 +464,8 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn) -> Empty
|
||||||
None => err!("Cipher doesn't exist"),
|
None => err!("Cipher doesn't exist"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if cipher.user_uuid != headers.user.uuid {
|
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||||
err!("Cipher is not owned by user")
|
err!("Cipher can't be deleted by user")
|
||||||
}
|
}
|
||||||
|
|
||||||
_delete_cipher(cipher, conn);
|
_delete_cipher(cipher, conn);
|
||||||
|
|
|
@ -109,6 +109,7 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"Data":
|
"Data":
|
||||||
Collection::find_by_user_uuid(&headers.user.uuid, &conn)
|
Collection::find_by_user_uuid(&headers.user.uuid, &conn)
|
||||||
|
.expect("Error loading collections")
|
||||||
.iter()
|
.iter()
|
||||||
.map(|collection| {
|
.map(|collection| {
|
||||||
collection.to_json()
|
collection.to_json()
|
||||||
|
@ -121,9 +122,9 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
fn get_org_collections(org_id: String, headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_org_collections(org_id: String, headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"Data":
|
"Data":
|
||||||
Collection::find_by_user_uuid(&headers.user.uuid, &conn)
|
Collection::find_by_organization_and_user_uuid(&org_id, &headers.user.uuid, &conn)
|
||||||
|
.unwrap_or(vec![])
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|collection| { collection.org_uuid == org_id })
|
|
||||||
.map(|collection| {
|
.map(|collection| {
|
||||||
collection.to_json()
|
collection.to_json()
|
||||||
}).collect::<Value>(),
|
}).collect::<Value>(),
|
||||||
|
@ -230,7 +231,7 @@ struct OrgIdData {
|
||||||
#[get("/ciphers/organization-details?<data>")]
|
#[get("/ciphers/organization-details?<data>")]
|
||||||
fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> JsonResult {
|
fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
let ciphers = Cipher::find_by_org(&data.organizationId, &conn);
|
let ciphers = Cipher::find_by_org(&data.organizationId, &conn);
|
||||||
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &conn)).collect();
|
let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"Data": ciphers_json,
|
"Data": ciphers_json,
|
||||||
|
|
|
@ -97,7 +97,7 @@ fn login(connect_data: Form<ConnectData>, device_type: DeviceType, conn: DbConn)
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = User::find_by_uuid(&device.user_uuid, &conn).unwrap();
|
let user = User::find_by_uuid(&device.user_uuid, &conn).unwrap();
|
||||||
let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
|
let orgs = UserOrganization::find_by_user(&user.uuid, &conn).unwrap_or(vec![]);
|
||||||
|
|
||||||
let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
|
let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
|
||||||
device.save(&conn);
|
device.save(&conn);
|
||||||
|
|
|
@ -3,19 +3,19 @@ use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::User;
|
use super::{User, Organization, UserOrganization, FolderCipher};
|
||||||
|
|
||||||
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
|
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
|
||||||
#[table_name = "ciphers"]
|
#[table_name = "ciphers"]
|
||||||
#[belongs_to(User, foreign_key = "user_uuid")]
|
#[belongs_to(User, foreign_key = "user_uuid")]
|
||||||
|
#[belongs_to(Organization, foreign_key = "organization_uuid")]
|
||||||
#[primary_key(uuid)]
|
#[primary_key(uuid)]
|
||||||
pub struct Cipher {
|
pub struct Cipher {
|
||||||
pub uuid: String,
|
pub uuid: String,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
|
|
||||||
pub user_uuid: String,
|
pub user_uuid: Option<String>,
|
||||||
pub folder_uuid: Option<String>,
|
|
||||||
pub organization_uuid: Option<String>,
|
pub organization_uuid: Option<String>,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -36,7 +36,7 @@ pub struct Cipher {
|
||||||
|
|
||||||
/// Local methods
|
/// Local methods
|
||||||
impl Cipher {
|
impl Cipher {
|
||||||
pub fn new(user_uuid: String, type_: i32, name: String, favorite: bool) -> Self {
|
pub fn new(user_uuid: Option<String>, organization_uuid: Option<String>, type_: i32, name: String, favorite: bool) -> Self {
|
||||||
let now = Utc::now().naive_utc();
|
let now = Utc::now().naive_utc();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -45,8 +45,7 @@ impl Cipher {
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
|
|
||||||
user_uuid,
|
user_uuid,
|
||||||
folder_uuid: None,
|
organization_uuid,
|
||||||
organization_uuid: None,
|
|
||||||
|
|
||||||
type_,
|
type_,
|
||||||
favorite,
|
favorite,
|
||||||
|
@ -63,11 +62,11 @@ impl Cipher {
|
||||||
use diesel;
|
use diesel;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use db::DbConn;
|
use db::DbConn;
|
||||||
use db::schema::ciphers;
|
use db::schema::*;
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Cipher {
|
impl Cipher {
|
||||||
pub fn to_json(&self, host: &str, conn: &DbConn) -> JsonValue {
|
pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> JsonValue {
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use util::format_date;
|
use util::format_date;
|
||||||
use super::Attachment;
|
use super::Attachment;
|
||||||
|
@ -94,7 +93,7 @@ impl Cipher {
|
||||||
"Id": self.uuid,
|
"Id": self.uuid,
|
||||||
"Type": self.type_,
|
"Type": self.type_,
|
||||||
"RevisionDate": format_date(&self.updated_at),
|
"RevisionDate": format_date(&self.updated_at),
|
||||||
"FolderId": self.folder_uuid,
|
"FolderId": self.get_folder_uuid(&user_uuid, &conn),
|
||||||
"Favorite": self.favorite,
|
"Favorite": self.favorite,
|
||||||
"OrganizationId": self.organization_uuid,
|
"OrganizationId": self.organization_uuid,
|
||||||
"Attachments": attachments_json,
|
"Attachments": attachments_json,
|
||||||
|
@ -142,6 +141,84 @@ impl Cipher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> Result<(), &str> {
|
||||||
|
match self.get_folder_uuid(&user_uuid, &conn) {
|
||||||
|
None => {
|
||||||
|
match folder_uuid {
|
||||||
|
Some(new_folder) => {
|
||||||
|
let folder_cipher = FolderCipher::new(&new_folder, &self.uuid);
|
||||||
|
folder_cipher.save(&conn).or(Err("Couldn't save folder setting"))
|
||||||
|
},
|
||||||
|
None => Ok(()) //nothing to do
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(current_folder) => {
|
||||||
|
match folder_uuid {
|
||||||
|
Some(new_folder) => {
|
||||||
|
if current_folder == new_folder {
|
||||||
|
Ok(()) //nothing to do
|
||||||
|
} else {
|
||||||
|
match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) {
|
||||||
|
Some(current_folder) => {
|
||||||
|
current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
|
||||||
|
},
|
||||||
|
None => Ok(()) // Weird, but nothing to do
|
||||||
|
};
|
||||||
|
|
||||||
|
FolderCipher::new(&new_folder, &self.uuid)
|
||||||
|
.save(&conn).or(Err("Couldn't save folder setting"))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) {
|
||||||
|
Some(current_folder) => {
|
||||||
|
current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
|
||||||
|
},
|
||||||
|
None => Err("Couldn't move from previous folder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
|
||||||
|
match ciphers::table
|
||||||
|
.filter(ciphers::user_uuid.eq(user_uuid))
|
||||||
|
.filter(ciphers::uuid.eq(&self.uuid))
|
||||||
|
.first::<Self>(&**conn).ok() {
|
||||||
|
Some(_) => true, // cipher directly owned by user
|
||||||
|
None =>{
|
||||||
|
match self.organization_uuid {
|
||||||
|
Some(ref org_uuid) => {
|
||||||
|
match users_organizations::table
|
||||||
|
.filter(users_organizations::org_uuid.eq(org_uuid))
|
||||||
|
.filter(users_organizations::user_uuid.eq(user_uuid))
|
||||||
|
.filter(users_organizations::access_all.eq(true))
|
||||||
|
.first::<UserOrganization>(&**conn).ok() {
|
||||||
|
Some(_) => true,
|
||||||
|
None => false //TODO R/W access on collection
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => false // cipher not in organization and not owned by user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool {
|
||||||
|
// TODO also check for read-only access
|
||||||
|
self.is_write_accessible_to_user(user_uuid, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_folder_uuid(&self, user_uuid: &str, conn: &DbConn) -> Option<String> {
|
||||||
|
folders_ciphers::table.inner_join(folders::table)
|
||||||
|
.filter(folders::user_uuid.eq(&user_uuid))
|
||||||
|
.filter(folders_ciphers::cipher_uuid.eq(&self.uuid))
|
||||||
|
.select(folders_ciphers::folder_uuid)
|
||||||
|
.first::<String>(&**conn).ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
|
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||||
ciphers::table
|
ciphers::table
|
||||||
.filter(ciphers::uuid.eq(uuid))
|
.filter(ciphers::uuid.eq(uuid))
|
||||||
|
@ -161,8 +238,9 @@ impl Cipher {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
||||||
ciphers::table
|
folders_ciphers::table.inner_join(ciphers::table)
|
||||||
.filter(ciphers::folder_uuid.eq(folder_uuid))
|
.filter(folders_ciphers::folder_uuid.eq(folder_uuid))
|
||||||
|
.select(ciphers::all_columns)
|
||||||
.load::<Self>(&**conn).expect("Error loading ciphers")
|
.load::<Self>(&**conn).expect("Error loading ciphers")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,11 +66,19 @@ impl Collection {
|
||||||
.first::<Self>(&**conn).ok()
|
.first::<Self>(&**conn).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Option<Vec<Self>> {
|
||||||
users_collections::table.inner_join(collections::table)
|
users_collections::table.inner_join(collections::table)
|
||||||
.filter(users_collections::user_uuid.eq(user_uuid))
|
.filter(users_collections::user_uuid.eq(user_uuid))
|
||||||
.select(collections::all_columns)
|
.select(collections::all_columns)
|
||||||
.load::<Self>(&**conn).expect("Error loading user collections")
|
.load::<Self>(&**conn).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Vec<Self>> {
|
||||||
|
users_collections::table.inner_join(collections::table)
|
||||||
|
.filter(users_collections::user_uuid.eq(user_uuid))
|
||||||
|
.filter(collections::org_uuid.eq(org_uuid))
|
||||||
|
.select(collections::all_columns)
|
||||||
|
.load::<Self>(&**conn).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
|
pub fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::User;
|
use super::{User, Cipher};
|
||||||
|
|
||||||
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
|
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
|
||||||
#[table_name = "folders"]
|
#[table_name = "folders"]
|
||||||
|
@ -17,6 +17,16 @@ pub struct Folder {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
|
||||||
|
#[table_name = "folders_ciphers"]
|
||||||
|
#[belongs_to(Cipher, foreign_key = "cipher_uuid")]
|
||||||
|
#[belongs_to(Folder, foreign_key = "folder_uuid")]
|
||||||
|
#[primary_key(cipher_uuid, folder_uuid)]
|
||||||
|
pub struct FolderCipher {
|
||||||
|
pub cipher_uuid: String,
|
||||||
|
pub folder_uuid: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// Local methods
|
/// Local methods
|
||||||
impl Folder {
|
impl Folder {
|
||||||
pub fn new(user_uuid: String, name: String) -> Self {
|
pub fn new(user_uuid: String, name: String) -> Self {
|
||||||
|
@ -44,10 +54,19 @@ impl Folder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FolderCipher {
|
||||||
|
pub fn new(cipher_uuid: &str, folder_uuid: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
cipher_uuid: cipher_uuid.to_string(),
|
||||||
|
folder_uuid: folder_uuid.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
use diesel;
|
use diesel;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use db::DbConn;
|
use db::DbConn;
|
||||||
use db::schema::folders;
|
use db::schema::{folders, folders_ciphers};
|
||||||
|
|
||||||
/// Database methods
|
/// Database methods
|
||||||
impl Folder {
|
impl Folder {
|
||||||
|
@ -83,3 +102,25 @@ impl Folder {
|
||||||
.load::<Self>(&**conn).expect("Error loading folders")
|
.load::<Self>(&**conn).expect("Error loading folders")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FolderCipher {
|
||||||
|
pub fn save(&self, conn: &DbConn) -> QueryResult<()> {
|
||||||
|
diesel::replace_into(folders_ciphers::table)
|
||||||
|
.values(&*self)
|
||||||
|
.execute(&**conn).and(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
|
||||||
|
diesel::delete(folders_ciphers::table
|
||||||
|
.filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid))
|
||||||
|
.filter(folders_ciphers::folder_uuid.eq(self.folder_uuid))
|
||||||
|
).execute(&**conn).and(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option<Self> {
|
||||||
|
folders_ciphers::table
|
||||||
|
.filter(folders_ciphers::folder_uuid.eq(folder_uuid))
|
||||||
|
.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))
|
||||||
|
.first::<Self>(&**conn).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod organization;
|
||||||
pub use self::attachment::Attachment;
|
pub use self::attachment::Attachment;
|
||||||
pub use self::cipher::Cipher;
|
pub use self::cipher::Cipher;
|
||||||
pub use self::device::Device;
|
pub use self::device::Device;
|
||||||
pub use self::folder::Folder;
|
pub use self::folder::{Folder, FolderCipher};
|
||||||
pub use self::user::User;
|
pub use self::user::User;
|
||||||
pub use self::organization::Organization;
|
pub use self::organization::Organization;
|
||||||
pub use self::organization::{UserOrganization, UserOrgStatus, UserOrgType};
|
pub use self::organization::{UserOrganization, UserOrgStatus, UserOrgType};
|
||||||
|
|
|
@ -222,10 +222,10 @@ impl UserOrganization {
|
||||||
.first::<Self>(&**conn).ok()
|
.first::<Self>(&**conn).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Option<Vec<Self>> {
|
||||||
users_organizations::table
|
users_organizations::table
|
||||||
.filter(users_organizations::user_uuid.eq(user_uuid))
|
.filter(users_organizations::user_uuid.eq(user_uuid))
|
||||||
.load::<Self>(&**conn).expect("Error loading user organizations")
|
.load::<Self>(&**conn).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
|
||||||
|
|
|
@ -128,7 +128,7 @@ impl User {
|
||||||
pub fn to_json(&self, conn: &DbConn) -> JsonValue {
|
pub fn to_json(&self, conn: &DbConn) -> JsonValue {
|
||||||
use super::UserOrganization;
|
use super::UserOrganization;
|
||||||
|
|
||||||
let orgs = UserOrganization::find_by_user(&self.uuid, conn);
|
let orgs = UserOrganization::find_by_user(&self.uuid, conn).unwrap_or(vec![]);
|
||||||
let orgs_json: Vec<JsonValue> = orgs.iter().map(|c| c.to_json(&conn)).collect();
|
let orgs_json: Vec<JsonValue> = orgs.iter().map(|c| c.to_json(&conn)).collect();
|
||||||
|
|
||||||
json!({
|
json!({
|
||||||
|
|
|
@ -12,8 +12,7 @@ table! {
|
||||||
uuid -> Text,
|
uuid -> Text,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
user_uuid -> Text,
|
user_uuid -> Nullable<Text>,
|
||||||
folder_uuid -> Nullable<Text>,
|
|
||||||
organization_uuid -> Nullable<Text>,
|
organization_uuid -> Nullable<Text>,
|
||||||
#[sql_name = "type"]
|
#[sql_name = "type"]
|
||||||
type_ -> Integer,
|
type_ -> Integer,
|
||||||
|
@ -107,8 +106,14 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
folders_ciphers (cipher_uuid, folder_uuid) {
|
||||||
|
cipher_uuid -> Text,
|
||||||
|
folder_uuid -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
joinable!(attachments -> ciphers (cipher_uuid));
|
joinable!(attachments -> ciphers (cipher_uuid));
|
||||||
joinable!(ciphers -> folders (folder_uuid));
|
|
||||||
joinable!(ciphers -> users (user_uuid));
|
joinable!(ciphers -> users (user_uuid));
|
||||||
joinable!(collections -> organizations (org_uuid));
|
joinable!(collections -> organizations (org_uuid));
|
||||||
joinable!(devices -> users (user_uuid));
|
joinable!(devices -> users (user_uuid));
|
||||||
|
@ -117,6 +122,8 @@ joinable!(users_collections -> collections (collection_uuid));
|
||||||
joinable!(users_collections -> users (user_uuid));
|
joinable!(users_collections -> users (user_uuid));
|
||||||
joinable!(users_organizations -> organizations (org_uuid));
|
joinable!(users_organizations -> organizations (org_uuid));
|
||||||
joinable!(users_organizations -> users (user_uuid));
|
joinable!(users_organizations -> users (user_uuid));
|
||||||
|
joinable!(folders_ciphers -> ciphers (cipher_uuid));
|
||||||
|
joinable!(folders_ciphers -> folders (folder_uuid));
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
allow_tables_to_appear_in_same_query!(
|
||||||
attachments,
|
attachments,
|
||||||
|
@ -128,4 +135,5 @@ allow_tables_to_appear_in_same_query!(
|
||||||
users,
|
users,
|
||||||
users_collections,
|
users_collections,
|
||||||
users_organizations,
|
users_organizations,
|
||||||
|
folders_ciphers,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue