Browse Source

Remove "db_run!" macro calls where possible

Signed-off-by: BlackDex <black.dex@gmail.com>
pull/7200/head
BlackDex 2 weeks ago
parent
commit
95c413d58d
No known key found for this signature in database GPG Key ID: 58C80A2AA6C765E1
  1. 22
      src/db/models/archive.rs
  2. 52
      src/db/models/attachment.rs
  3. 32
      src/db/models/auth_request.rs
  4. 473
      src/db/models/cipher.rs
  5. 597
      src/db/models/collection.rs
  6. 58
      src/db/models/device.rs
  7. 79
      src/db/models/emergency_access.rs
  8. 47
      src/db/models/event.rs
  9. 41
      src/db/models/favorite.rs
  10. 50
      src/db/models/folder.rs
  11. 235
      src/db/models/group.rs
  12. 69
      src/db/models/org_policy.rs
  13. 287
      src/db/models/organization.rs
  14. 57
      src/db/models/send.rs
  15. 15
      src/db/models/sso_auth.rs
  16. 32
      src/db/models/two_factor.rs
  17. 31
      src/db/models/two_factor_duo_context.rs
  18. 31
      src/db/models/two_factor_incomplete.rs
  19. 86
      src/db/models/user.rs

22
src/db/models/archive.rs

@ -21,13 +21,15 @@ pub struct Archive {
impl Archive { impl Archive {
// Returns the date the specified cipher was archived // Returns the date the specified cipher was archived
pub async fn get_archived_at(cipher_uuid: &CipherId, user_uuid: &UserId, conn: &DbConn) -> Option<NaiveDateTime> { pub async fn get_archived_at(cipher_uuid: &CipherId, user_uuid: &UserId, conn: &DbConn) -> Option<NaiveDateTime> {
db_run! { conn: { conn.run(move |conn| {
archives::table archives::table
.filter(archives::cipher_uuid.eq(cipher_uuid)) .filter(archives::cipher_uuid.eq(cipher_uuid))
.filter(archives::user_uuid.eq(user_uuid)) .filter(archives::user_uuid.eq(user_uuid))
.select(archives::archived_at) .select(archives::archived_at)
.first::<NaiveDateTime>(conn).ok() .first::<NaiveDateTime>(conn)
}} .ok()
})
.await
} }
// Saves (inserts or updates) an archive record with the provided timestamp // Saves (inserts or updates) an archive record with the provided timestamp
@ -68,26 +70,26 @@ impl Archive {
// Deletes an archive record for a specific cipher // Deletes an archive record for a specific cipher
pub async fn delete_by_cipher(user_uuid: &UserId, cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult { pub async fn delete_by_cipher(user_uuid: &UserId, cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult {
User::update_uuid_revision(user_uuid, conn).await; User::update_uuid_revision(user_uuid, conn).await;
db_run! { conn: { conn.run(move |conn| {
diesel::delete( diesel::delete(
archives::table archives::table.filter(archives::user_uuid.eq(user_uuid)).filter(archives::cipher_uuid.eq(cipher_uuid)),
.filter(archives::user_uuid.eq(user_uuid))
.filter(archives::cipher_uuid.eq(cipher_uuid))
) )
.execute(conn) .execute(conn)
.map_res("Error deleting archive") .map_res("Error deleting archive")
}} })
.await
} }
/// Return a vec with (cipher_uuid, archived_at) /// Return a vec with (cipher_uuid, archived_at)
/// This is used during a full sync so we only need one query for all archive matches /// This is used during a full sync so we only need one query for all archive matches
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<(CipherId, NaiveDateTime)> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<(CipherId, NaiveDateTime)> {
db_run! { conn: { conn.run(move |conn| {
archives::table archives::table
.filter(archives::user_uuid.eq(user_uuid)) .filter(archives::user_uuid.eq(user_uuid))
.select((archives::cipher_uuid, archives::archived_at)) .select((archives::cipher_uuid, archives::archived_at))
.load::<(CipherId, NaiveDateTime)>(conn) .load::<(CipherId, NaiveDateTime)>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
} }

52
src/db/models/attachment.rs

@ -112,15 +112,15 @@ impl Attachment {
} }
pub async fn delete(&self, conn: &DbConn) -> EmptyResult { pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
crate::util::retry(|| crate::util::retry(
diesel::delete(attachments::table.filter(attachments::id.eq(&self.id))) || diesel::delete(attachments::table.filter(attachments::id.eq(&self.id))).execute(conn),
.execute(conn),
10, 10,
) )
.map(|_| ()) .map(|_| ())
.map_res("Error deleting attachment") .map_res("Error deleting attachment")
}}?; })
.await?;
let operator = CONFIG.opendal_operator_for_path_type(&PathType::Attachments)?; let operator = CONFIG.opendal_operator_for_path_type(&PathType::Attachments)?;
let file_path = self.get_file_path(); let file_path = self.get_file_path();
@ -144,25 +144,22 @@ impl Attachment {
} }
pub async fn find_by_id(id: &AttachmentId, conn: &DbConn) -> Option<Self> { pub async fn find_by_id(id: &AttachmentId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| attachments::table.filter(attachments::id.eq(id.to_lowercase())).first::<Self>(conn).ok())
attachments::table .await
.filter(attachments::id.eq(id.to_lowercase()))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
attachments::table attachments::table
.filter(attachments::cipher_uuid.eq(cipher_uuid)) .filter(attachments::cipher_uuid.eq(cipher_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading attachments") .expect("Error loading attachments")
}} })
.await
} }
pub async fn size_by_user(user_uuid: &UserId, conn: &DbConn) -> i64 { pub async fn size_by_user(user_uuid: &UserId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
let result: Option<BigDecimal> = attachments::table let result: Option<BigDecimal> = attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
.filter(ciphers::user_uuid.eq(user_uuid)) .filter(ciphers::user_uuid.eq(user_uuid))
@ -173,24 +170,26 @@ impl Attachment {
match result.map(|r| r.to_i64()) { match result.map(|r| r.to_i64()) {
Some(Some(r)) => r, Some(Some(r)) => r,
Some(None) => i64::MAX, Some(None) => i64::MAX,
None => 0 None => 0,
} }
}} })
.await
} }
pub async fn count_by_user(user_uuid: &UserId, conn: &DbConn) -> i64 { pub async fn count_by_user(user_uuid: &UserId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
attachments::table attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
.filter(ciphers::user_uuid.eq(user_uuid)) .filter(ciphers::user_uuid.eq(user_uuid))
.count() .count()
.first(conn) .first(conn)
.unwrap_or(0) .unwrap_or(0)
}} })
.await
} }
pub async fn size_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 { pub async fn size_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
let result: Option<BigDecimal> = attachments::table let result: Option<BigDecimal> = attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
.filter(ciphers::organization_uuid.eq(org_uuid)) .filter(ciphers::organization_uuid.eq(org_uuid))
@ -201,20 +200,22 @@ impl Attachment {
match result.map(|r| r.to_i64()) { match result.map(|r| r.to_i64()) {
Some(Some(r)) => r, Some(Some(r)) => r,
Some(None) => i64::MAX, Some(None) => i64::MAX,
None => 0 None => 0,
} }
}} })
.await
} }
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 { pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
attachments::table attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
.filter(ciphers::organization_uuid.eq(org_uuid)) .filter(ciphers::organization_uuid.eq(org_uuid))
.count() .count()
.first(conn) .first(conn)
.unwrap_or(0) .unwrap_or(0)
}} })
.await
} }
// This will return all attachments linked to the user or org // This will return all attachments linked to the user or org
@ -225,7 +226,7 @@ impl Attachment {
org_uuids: &Vec<OrganizationId>, org_uuids: &Vec<OrganizationId>,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
attachments::table attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
.filter(ciphers::user_uuid.eq(user_uuid)) .filter(ciphers::user_uuid.eq(user_uuid))
@ -233,7 +234,8 @@ impl Attachment {
.select(attachments::all_columns) .select(attachments::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading attachments") .expect("Error loading attachments")
}} })
.await
} }
} }

32
src/db/models/auth_request.rs

@ -114,31 +114,28 @@ impl AuthRequest {
} }
pub async fn find_by_uuid(uuid: &AuthRequestId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &AuthRequestId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| auth_requests::table.filter(auth_requests::uuid.eq(uuid)).first::<Self>(conn).ok()).await
auth_requests::table
.filter(auth_requests::uuid.eq(uuid))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_uuid_and_user(uuid: &AuthRequestId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_user(uuid: &AuthRequestId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
auth_requests::table auth_requests::table
.filter(auth_requests::uuid.eq(uuid)) .filter(auth_requests::uuid.eq(uuid))
.filter(auth_requests::user_uuid.eq(user_uuid)) .filter(auth_requests::user_uuid.eq(user_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
auth_requests::table auth_requests::table
.filter(auth_requests::user_uuid.eq(user_uuid)) .filter(auth_requests::user_uuid.eq(user_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading auth_requests") .expect("Error loading auth_requests")
}} })
.await
} }
pub async fn find_by_user_and_requested_device( pub async fn find_by_user_and_requested_device(
@ -146,7 +143,7 @@ impl AuthRequest {
device_uuid: &DeviceId, device_uuid: &DeviceId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
auth_requests::table auth_requests::table
.filter(auth_requests::user_uuid.eq(user_uuid)) .filter(auth_requests::user_uuid.eq(user_uuid))
.filter(auth_requests::request_device_identifier.eq(device_uuid)) .filter(auth_requests::request_device_identifier.eq(device_uuid))
@ -154,24 +151,27 @@ impl AuthRequest {
.order_by(auth_requests::creation_date.desc()) .order_by(auth_requests::creation_date.desc())
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_created_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> { pub async fn find_created_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
auth_requests::table auth_requests::table
.filter(auth_requests::creation_date.lt(dt)) .filter(auth_requests::creation_date.lt(dt))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading auth_requests") .expect("Error loading auth_requests")
}} })
.await
} }
pub async fn delete(&self, conn: &DbConn) -> EmptyResult { pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(auth_requests::table.filter(auth_requests::uuid.eq(&self.uuid))) diesel::delete(auth_requests::table.filter(auth_requests::uuid.eq(&self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting auth request") .map_res("Error deleting auth request")
}} })
.await
} }
pub fn check_access_code(&self, access_code: &str) -> bool { pub fn check_access_code(&self, access_code: &str) -> bool {

473
src/db/models/cipher.rs

@ -481,11 +481,12 @@ impl Cipher {
Attachment::delete_all_by_cipher(&self.uuid, conn).await?; Attachment::delete_all_by_cipher(&self.uuid, conn).await?;
Favorite::delete_all_by_cipher(&self.uuid, conn).await?; Favorite::delete_all_by_cipher(&self.uuid, conn).await?;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid))) diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting cipher") .map_res("Error deleting cipher")
}} })
.await
} }
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
@ -668,51 +669,51 @@ impl Cipher {
} }
async fn get_user_collections_access_flags(&self, user_uuid: &UserId, conn: &DbConn) -> Vec<(bool, bool, bool)> { async fn get_user_collections_access_flags(&self, user_uuid: &UserId, conn: &DbConn) -> Vec<(bool, bool, bool)> {
db_run! { conn: { conn.run(move |conn| {
// Check whether this cipher is in any collections accessible to the // Check whether this cipher is in any collections accessible to the
// user. If so, retrieve the access flags for each collection. // user. If so, retrieve the access flags for each collection.
ciphers::table ciphers::table
.filter(ciphers::uuid.eq(&self.uuid)) .filter(ciphers::uuid.eq(&self.uuid))
.inner_join(ciphers_collections::table.on( .inner_join(ciphers_collections::table.on(ciphers::uuid.eq(ciphers_collections::cipher_uuid)))
ciphers::uuid.eq(ciphers_collections::cipher_uuid) .inner_join(
)) users_collections::table.on(ciphers_collections::collection_uuid
.inner_join(users_collections::table.on( .eq(users_collections::collection_uuid)
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) .and(users_collections::user_uuid.eq(user_uuid))),
.and(users_collections::user_uuid.eq(user_uuid)) )
))
.select((users_collections::read_only, users_collections::hide_passwords, users_collections::manage)) .select((users_collections::read_only, users_collections::hide_passwords, users_collections::manage))
.load::<(bool, bool, bool)>(conn) .load::<(bool, bool, bool)>(conn)
.expect("Error getting user access restrictions") .expect("Error getting user access restrictions")
}} })
.await
} }
async fn get_group_collections_access_flags(&self, user_uuid: &UserId, conn: &DbConn) -> Vec<(bool, bool, bool)> { async fn get_group_collections_access_flags(&self, user_uuid: &UserId, conn: &DbConn) -> Vec<(bool, bool, bool)> {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
return Vec::new(); return Vec::new();
} }
db_run! { conn: { conn.run(move |conn| {
ciphers::table ciphers::table
.filter(ciphers::uuid.eq(&self.uuid)) .filter(ciphers::uuid.eq(&self.uuid))
.inner_join(ciphers_collections::table.on( .inner_join(ciphers_collections::table.on(ciphers::uuid.eq(ciphers_collections::cipher_uuid)))
ciphers::uuid.eq(ciphers_collections::cipher_uuid) .inner_join(
)) collections_groups::table
.inner_join(collections_groups::table.on( .on(collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)),
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid) )
)) .inner_join(groups_users::table.on(groups_users::groups_uuid.eq(collections_groups::groups_uuid)))
.inner_join(groups_users::table.on( .inner_join(
groups_users::groups_uuid.eq(collections_groups::groups_uuid) users_organizations::table.on(users_organizations::uuid.eq(groups_users::users_organizations_uuid)),
)) )
.inner_join(users_organizations::table.on( .inner_join(
users_organizations::uuid.eq(groups_users::users_organizations_uuid) groups::table.on(groups::uuid
)) .eq(collections_groups::groups_uuid)
.inner_join(groups::table.on(groups::uuid.eq(collections_groups::groups_uuid) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) )
))
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.select((collections_groups::read_only, collections_groups::hide_passwords, collections_groups::manage)) .select((collections_groups::read_only, collections_groups::hide_passwords, collections_groups::manage))
.load::<(bool, bool, bool)>(conn) .load::<(bool, bool, bool)>(conn)
.expect("Error getting group access restrictions") .expect("Error getting group access restrictions")
}} })
.await
} }
pub async fn is_write_accessible_to_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn is_write_accessible_to_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool {
@ -761,7 +762,7 @@ impl Cipher {
} }
pub async fn get_folder_uuid(&self, user_uuid: &UserId, conn: &DbConn) -> Option<FolderId> { pub async fn get_folder_uuid(&self, user_uuid: &UserId, conn: &DbConn) -> Option<FolderId> {
db_run! { conn: { conn.run(move |conn| {
folders_ciphers::table folders_ciphers::table
.inner_join(folders::table) .inner_join(folders::table)
.filter(folders::user_uuid.eq(&user_uuid)) .filter(folders::user_uuid.eq(&user_uuid))
@ -769,16 +770,12 @@ impl Cipher {
.select(folders_ciphers::folder_uuid) .select(folders_ciphers::folder_uuid)
.first::<FolderId>(conn) .first::<FolderId>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_uuid(uuid: &CipherId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &CipherId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| ciphers::table.filter(ciphers::uuid.eq(uuid)).first::<Self>(conn).ok()).await
ciphers::table
.filter(ciphers::uuid.eq(uuid))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_uuid_and_org( pub async fn find_by_uuid_and_org(
@ -786,13 +783,14 @@ impl Cipher {
org_uuid: &OrganizationId, org_uuid: &OrganizationId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
ciphers::table ciphers::table
.filter(ciphers::uuid.eq(cipher_uuid)) .filter(ciphers::uuid.eq(cipher_uuid))
.filter(ciphers::organization_uuid.eq(org_uuid)) .filter(ciphers::organization_uuid.eq(org_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
// Find all ciphers accessible or visible to the specified user. // Find all ciphers accessible or visible to the specified user.
@ -814,32 +812,35 @@ impl Cipher {
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! { conn: { conn.run(move |conn| {
let mut query = ciphers::table let mut query = ciphers::table
.left_join(ciphers_collections::table.on( .left_join(ciphers_collections::table.on(ciphers::uuid.eq(ciphers_collections::cipher_uuid)))
ciphers::uuid.eq(ciphers_collections::cipher_uuid) .left_join(
)) users_organizations::table.on(ciphers::organization_uuid
.left_join(users_organizations::table.on( .eq(users_organizations::org_uuid.nullable())
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
.and(users_organizations::user_uuid.eq(user_uuid)) .and(users_organizations::user_uuid.eq(user_uuid))
.and(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) .and(users_organizations::status.eq(MembershipStatus::Confirmed as i32))),
)) )
.left_join(users_collections::table.on( .left_join(
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) users_collections::table.on(ciphers_collections::collection_uuid
.eq(users_collections::collection_uuid)
// Ensure that users_collections::user_uuid is NULL for unconfirmed users. // Ensure that users_collections::user_uuid is NULL for unconfirmed users.
.and(users_organizations::user_uuid.eq(users_collections::user_uuid)) .and(users_organizations::user_uuid.eq(users_collections::user_uuid))),
)) )
.left_join(groups_users::table.on( .left_join(
groups_users::users_organizations_uuid.eq(users_organizations::uuid) groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
)) )
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .left_join(
groups::table.on(groups::uuid
.eq(groups_users::groups_uuid)
// Ensure that group and membership belong to the same org // Ensure that group and membership belong to the same org
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
)) )
.left_join(collections_groups::table.on( .left_join(
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid) collections_groups::table.on(collections_groups::collections_uuid
.and(collections_groups::groups_uuid.eq(groups::uuid)) .eq(ciphers_collections::collection_uuid)
)) .and(collections_groups::groups_uuid.eq(groups::uuid))),
)
.filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner .filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
.or_filter(users_organizations::access_all.eq(true)) // access_all in org .or_filter(users_organizations::access_all.eq(true)) // access_all in org
.or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection .or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
@ -849,39 +850,34 @@ impl Cipher {
if !visible_only { if !visible_only {
query = query.or_filter( query = query.or_filter(
users_organizations::atype.le(MembershipType::Admin as i32) // Org admin/owner users_organizations::atype.le(MembershipType::Admin as i32), // Org admin/owner
); );
} }
// Only filter for one specific cipher // Only filter for one specific cipher
if !cipher_uuids.is_empty() { if !cipher_uuids.is_empty() {
query = query.filter( query = query.filter(ciphers::uuid.eq_any(cipher_uuids));
ciphers::uuid.eq_any(cipher_uuids)
);
} }
query query.select(ciphers::all_columns).distinct().load::<Self>(conn).expect("Error loading ciphers")
.select(ciphers::all_columns) })
.distinct() .await
.load::<Self>(conn)
.expect("Error loading ciphers")
}}
} else { } else {
db_run! { conn: { conn.run(move |conn| {
let mut query = ciphers::table let mut query = ciphers::table
.left_join(ciphers_collections::table.on( .left_join(ciphers_collections::table.on(ciphers::uuid.eq(ciphers_collections::cipher_uuid)))
ciphers::uuid.eq(ciphers_collections::cipher_uuid) .left_join(
)) users_organizations::table.on(ciphers::organization_uuid
.left_join(users_organizations::table.on( .eq(users_organizations::org_uuid.nullable())
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
.and(users_organizations::user_uuid.eq(user_uuid)) .and(users_organizations::user_uuid.eq(user_uuid))
.and(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) .and(users_organizations::status.eq(MembershipStatus::Confirmed as i32))),
)) )
.left_join(users_collections::table.on( .left_join(
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) users_collections::table.on(ciphers_collections::collection_uuid
.eq(users_collections::collection_uuid)
// Ensure that users_collections::user_uuid is NULL for unconfirmed users. // Ensure that users_collections::user_uuid is NULL for unconfirmed users.
.and(users_organizations::user_uuid.eq(users_collections::user_uuid)) .and(users_organizations::user_uuid.eq(users_collections::user_uuid))),
)) )
.filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner .filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
.or_filter(users_organizations::access_all.eq(true)) // access_all in org .or_filter(users_organizations::access_all.eq(true)) // access_all in org
.or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection .or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
@ -889,23 +885,18 @@ impl Cipher {
if !visible_only { if !visible_only {
query = query.or_filter( query = query.or_filter(
users_organizations::atype.le(MembershipType::Admin as i32) // Org admin/owner users_organizations::atype.le(MembershipType::Admin as i32), // Org admin/owner
); );
} }
// Only filter for one specific cipher // Only filter for one specific cipher
if !cipher_uuids.is_empty() { if !cipher_uuids.is_empty() {
query = query.filter( query = query.filter(ciphers::uuid.eq_any(cipher_uuids));
ciphers::uuid.eq_any(cipher_uuids)
);
} }
query query.select(ciphers::all_columns).distinct().load::<Self>(conn).expect("Error loading ciphers")
.select(ciphers::all_columns) })
.distinct() .await
.load::<Self>(conn)
.expect("Error loading ciphers")
}}
} }
} }
@ -928,193 +919,208 @@ impl Cipher {
// Find all ciphers directly owned by the specified user. // Find all ciphers directly owned by the specified user.
pub async fn find_owned_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_owned_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
ciphers::table ciphers::table
.filter( .filter(ciphers::user_uuid.eq(user_uuid).and(ciphers::organization_uuid.is_null()))
ciphers::user_uuid.eq(user_uuid)
.and(ciphers::organization_uuid.is_null())
)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading ciphers") .expect("Error loading ciphers")
}} })
.await
} }
pub async fn count_owned_by_user(user_uuid: &UserId, conn: &DbConn) -> i64 { pub async fn count_owned_by_user(user_uuid: &UserId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
ciphers::table ciphers::table.filter(ciphers::user_uuid.eq(user_uuid)).count().first::<i64>(conn).ok().unwrap_or(0)
.filter(ciphers::user_uuid.eq(user_uuid)) })
.count() .await
.first::<i64>(conn)
.ok()
.unwrap_or(0)
}}
} }
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
ciphers::table ciphers::table
.filter(ciphers::organization_uuid.eq(org_uuid)) .filter(ciphers::organization_uuid.eq(org_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading ciphers") .expect("Error loading ciphers")
}} })
.await
} }
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 { pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
ciphers::table ciphers::table.filter(ciphers::organization_uuid.eq(org_uuid)).count().first::<i64>(conn).ok().unwrap_or(0)
.filter(ciphers::organization_uuid.eq(org_uuid)) })
.count() .await
.first::<i64>(conn)
.ok()
.unwrap_or(0)
}}
} }
pub async fn find_by_folder(folder_uuid: &FolderId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_folder(folder_uuid: &FolderId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
folders_ciphers::table.inner_join(ciphers::table) folders_ciphers::table
.inner_join(ciphers::table)
.filter(folders_ciphers::folder_uuid.eq(folder_uuid)) .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
.select(ciphers::all_columns) .select(ciphers::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading ciphers") .expect("Error loading ciphers")
}} })
.await
} }
/// Find all ciphers that were deleted before the specified datetime. /// Find all ciphers that were deleted before the specified datetime.
pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> { pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
ciphers::table ciphers::table.filter(ciphers::deleted_at.lt(dt)).load::<Self>(conn).expect("Error loading ciphers")
.filter(ciphers::deleted_at.lt(dt)) })
.load::<Self>(conn) .await
.expect("Error loading ciphers")
}}
} }
pub async fn get_collections(&self, user_uuid: UserId, conn: &DbConn) -> Vec<CollectionId> { pub async fn get_collections(&self, user_uuid: UserId, conn: &DbConn) -> Vec<CollectionId> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! { conn: { conn.run(move |conn| {
ciphers_collections::table ciphers_collections::table
.filter(ciphers_collections::cipher_uuid.eq(&self.uuid)) .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
.inner_join(collections::table.on( .inner_join(collections::table.on(collections::uuid.eq(ciphers_collections::collection_uuid)))
collections::uuid.eq(ciphers_collections::collection_uuid) .left_join(
)) users_organizations::table.on(users_organizations::org_uuid
.left_join(users_organizations::table.on( .eq(collections::org_uuid)
users_organizations::org_uuid.eq(collections::org_uuid) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
.and(users_organizations::user_uuid.eq(user_uuid.clone())) )
)) .left_join(
.left_join(users_collections::table.on( users_collections::table.on(users_collections::collection_uuid
users_collections::collection_uuid.eq(ciphers_collections::collection_uuid) .eq(ciphers_collections::collection_uuid)
.and(users_collections::user_uuid.eq(user_uuid.clone())) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
)) )
.left_join(groups_users::table.on( .left_join(
groups_users::users_organizations_uuid.eq(users_organizations::uuid) groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
)) )
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .left_join(
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) groups::table.on(groups::uuid
)) .eq(groups_users::groups_uuid)
.left_join(collections_groups::table.on( .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid) )
.and(collections_groups::groups_uuid.eq(groups::uuid)) .left_join(
)) collections_groups::table.on(collections_groups::collections_uuid
.filter(users_organizations::access_all.eq(true) // User has access all .eq(ciphers_collections::collection_uuid)
.or(users_collections::user_uuid.eq(user_uuid) // User has access to collection .and(collections_groups::groups_uuid.eq(groups::uuid))),
)
.filter(
users_organizations::access_all
.eq(true) // User has access all
.or(users_collections::user_uuid
.eq(user_uuid) // User has access to collection
.and(users_collections::read_only.eq(false))) .and(users_collections::read_only.eq(false)))
.or(groups::access_all.eq(true)) // Access via groups .or(groups::access_all.eq(true)) // Access via groups
.or(collections_groups::collections_uuid.is_not_null() // Access via groups .or(collections_groups::collections_uuid
.and(collections_groups::read_only.eq(false))) .is_not_null() // Access via groups
.and(collections_groups::read_only.eq(false))),
) )
.select(ciphers_collections::collection_uuid) .select(ciphers_collections::collection_uuid)
.load::<CollectionId>(conn) .load::<CollectionId>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} else { } else {
db_run! { conn: { conn.run(move |conn| {
ciphers_collections::table ciphers_collections::table
.filter(ciphers_collections::cipher_uuid.eq(&self.uuid)) .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
.inner_join(collections::table.on( .inner_join(collections::table.on(collections::uuid.eq(ciphers_collections::collection_uuid)))
collections::uuid.eq(ciphers_collections::collection_uuid) .inner_join(
)) users_organizations::table.on(users_organizations::org_uuid
.inner_join(users_organizations::table.on( .eq(collections::org_uuid)
users_organizations::org_uuid.eq(collections::org_uuid) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
.and(users_organizations::user_uuid.eq(user_uuid.clone())) )
)) .left_join(
.left_join(users_collections::table.on( users_collections::table.on(users_collections::collection_uuid
users_collections::collection_uuid.eq(ciphers_collections::collection_uuid) .eq(ciphers_collections::collection_uuid)
.and(users_collections::user_uuid.eq(user_uuid.clone())) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
)) )
.filter(users_organizations::access_all.eq(true) // User has access all .filter(
.or(users_collections::user_uuid.eq(user_uuid) // User has access to collection users_organizations::access_all
.and(users_collections::read_only.eq(false))) .eq(true) // User has access all
.or(users_collections::user_uuid
.eq(user_uuid) // User has access to collection
.and(users_collections::read_only.eq(false))),
) )
.select(ciphers_collections::collection_uuid) .select(ciphers_collections::collection_uuid)
.load::<CollectionId>(conn) .load::<CollectionId>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
} }
pub async fn get_admin_collections(&self, user_uuid: UserId, conn: &DbConn) -> Vec<CollectionId> { pub async fn get_admin_collections(&self, user_uuid: UserId, conn: &DbConn) -> Vec<CollectionId> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! { conn: { conn.run(move |conn| {
ciphers_collections::table ciphers_collections::table
.filter(ciphers_collections::cipher_uuid.eq(&self.uuid)) .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
.inner_join(collections::table.on( .inner_join(collections::table.on(collections::uuid.eq(ciphers_collections::collection_uuid)))
collections::uuid.eq(ciphers_collections::collection_uuid) .left_join(
)) users_organizations::table.on(users_organizations::org_uuid
.left_join(users_organizations::table.on( .eq(collections::org_uuid)
users_organizations::org_uuid.eq(collections::org_uuid) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
.and(users_organizations::user_uuid.eq(user_uuid.clone())) )
)) .left_join(
.left_join(users_collections::table.on( users_collections::table.on(users_collections::collection_uuid
users_collections::collection_uuid.eq(ciphers_collections::collection_uuid) .eq(ciphers_collections::collection_uuid)
.and(users_collections::user_uuid.eq(user_uuid.clone())) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
)) )
.left_join(groups_users::table.on( .left_join(
groups_users::users_organizations_uuid.eq(users_organizations::uuid) groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
)) )
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .left_join(
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) groups::table.on(groups::uuid
)) .eq(groups_users::groups_uuid)
.left_join(collections_groups::table.on( .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid) )
.and(collections_groups::groups_uuid.eq(groups::uuid)) .left_join(
)) collections_groups::table.on(collections_groups::collections_uuid
.filter(users_organizations::access_all.eq(true) // User has access all .eq(ciphers_collections::collection_uuid)
.or(users_collections::user_uuid.eq(user_uuid) // User has access to collection .and(collections_groups::groups_uuid.eq(groups::uuid))),
)
.filter(
users_organizations::access_all
.eq(true) // User has access all
.or(users_collections::user_uuid
.eq(user_uuid) // User has access to collection
.and(users_collections::read_only.eq(false))) .and(users_collections::read_only.eq(false)))
.or(groups::access_all.eq(true)) // Access via groups .or(groups::access_all.eq(true)) // Access via groups
.or(collections_groups::collections_uuid.is_not_null() // Access via groups .or(collections_groups::collections_uuid
.is_not_null() // Access via groups
.and(collections_groups::read_only.eq(false))) .and(collections_groups::read_only.eq(false)))
.or(users_organizations::atype.le(MembershipType::Admin as i32)) // User is admin or owner .or(users_organizations::atype.le(MembershipType::Admin as i32)), // User is admin or owner
) )
.select(ciphers_collections::collection_uuid) .select(ciphers_collections::collection_uuid)
.load::<CollectionId>(conn) .load::<CollectionId>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} else { } else {
db_run! { conn: { conn.run(move |conn| {
ciphers_collections::table ciphers_collections::table
.filter(ciphers_collections::cipher_uuid.eq(&self.uuid)) .filter(ciphers_collections::cipher_uuid.eq(&self.uuid))
.inner_join(collections::table.on( .inner_join(collections::table.on(collections::uuid.eq(ciphers_collections::collection_uuid)))
collections::uuid.eq(ciphers_collections::collection_uuid) .inner_join(
)) users_organizations::table.on(users_organizations::org_uuid
.inner_join(users_organizations::table.on( .eq(collections::org_uuid)
users_organizations::org_uuid.eq(collections::org_uuid) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
.and(users_organizations::user_uuid.eq(user_uuid.clone())) )
)) .left_join(
.left_join(users_collections::table.on( users_collections::table.on(users_collections::collection_uuid
users_collections::collection_uuid.eq(ciphers_collections::collection_uuid) .eq(ciphers_collections::collection_uuid)
.and(users_collections::user_uuid.eq(user_uuid.clone())) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
)) )
.filter(users_organizations::access_all.eq(true) // User has access all .filter(
.or(users_collections::user_uuid.eq(user_uuid) // User has access to collection users_organizations::access_all
.eq(true) // User has access all
.or(users_collections::user_uuid
.eq(user_uuid) // User has access to collection
.and(users_collections::read_only.eq(false))) .and(users_collections::read_only.eq(false)))
.or(users_organizations::atype.le(MembershipType::Admin as i32)) // User is admin or owner .or(users_organizations::atype.le(MembershipType::Admin as i32)), // User is admin or owner
) )
.select(ciphers_collections::collection_uuid) .select(ciphers_collections::collection_uuid)
.load::<CollectionId>(conn) .load::<CollectionId>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
} }
@ -1124,32 +1130,30 @@ impl Cipher {
user_uuid: UserId, user_uuid: UserId,
conn: &DbConn, conn: &DbConn,
) -> Vec<(CipherId, CollectionId)> { ) -> Vec<(CipherId, CollectionId)> {
db_run! { conn: { conn.run(move |conn| {
ciphers_collections::table ciphers_collections::table
.inner_join(collections::table.on( .inner_join(collections::table.on(collections::uuid.eq(ciphers_collections::collection_uuid)))
collections::uuid.eq(ciphers_collections::collection_uuid) .inner_join(
)) users_organizations::table.on(users_organizations::org_uuid
.inner_join(users_organizations::table.on( .eq(collections::org_uuid)
users_organizations::org_uuid.eq(collections::org_uuid).and( .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
users_organizations::user_uuid.eq(user_uuid.clone())
) )
)) .left_join(
.left_join(users_collections::table.on( users_collections::table.on(users_collections::collection_uuid
users_collections::collection_uuid.eq(ciphers_collections::collection_uuid).and( .eq(ciphers_collections::collection_uuid)
users_collections::user_uuid.eq(user_uuid.clone()) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
) )
)) .left_join(groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)))
.left_join(groups_users::table.on( .left_join(
groups_users::users_organizations_uuid.eq(users_organizations::uuid) groups::table.on(groups::uuid
)) .eq(groups_users::groups_uuid)
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) )
)) .left_join(
.left_join(collections_groups::table.on( collections_groups::table.on(collections_groups::collections_uuid
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid).and( .eq(ciphers_collections::collection_uuid)
collections_groups::groups_uuid.eq(groups::uuid) .and(collections_groups::groups_uuid.eq(groups::uuid))),
) )
))
.or_filter(users_collections::user_uuid.eq(user_uuid)) // User has access to collection .or_filter(users_collections::user_uuid.eq(user_uuid)) // User has access to collection
.or_filter(users_organizations::access_all.eq(true)) // User has access all .or_filter(users_organizations::access_all.eq(true)) // User has access all
.or_filter(users_organizations::atype.le(MembershipType::Admin as i32)) // User is admin or owner .or_filter(users_organizations::atype.le(MembershipType::Admin as i32)) // User is admin or owner
@ -1159,7 +1163,8 @@ impl Cipher {
.distinct() .distinct()
.load::<(CipherId, CollectionId)>(conn) .load::<(CipherId, CollectionId)>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
} }

597
src/db/models/collection.rs

@ -197,11 +197,12 @@ impl Collection {
CollectionUser::delete_all_by_collection(&self.uuid, conn).await?; CollectionUser::delete_all_by_collection(&self.uuid, conn).await?;
CollectionGroup::delete_all_by_collection(&self.uuid, &self.org_uuid, conn).await?; CollectionGroup::delete_all_by_collection(&self.uuid, &self.org_uuid, conn).await?;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(collections::table.filter(collections::uuid.eq(self.uuid))) diesel::delete(collections::table.filter(collections::uuid.eq(self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting collection") .map_res("Error deleting collection")
}} })
.await
} }
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
@ -218,84 +219,84 @@ impl Collection {
} }
pub async fn find_by_uuid(uuid: &CollectionId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &CollectionId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| collections::table.filter(collections::uuid.eq(uuid)).first::<Self>(conn).ok()).await
collections::table
.filter(collections::uuid.eq(uuid))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_user_uuid(user_uuid: UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user_uuid(user_uuid: UserId, conn: &DbConn) -> Vec<Self> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.left_join(users_collections::table.on( .left_join(
users_collections::collection_uuid.eq(collections::uuid).and( users_collections::table.on(users_collections::collection_uuid
users_collections::user_uuid.eq(user_uuid.clone()) .eq(collections::uuid)
) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
)) )
.left_join(users_organizations::table.on( .left_join(
collections::org_uuid.eq(users_organizations::org_uuid).and( users_organizations::table.on(collections::org_uuid
users_organizations::user_uuid.eq(user_uuid.clone()) .eq(users_organizations::org_uuid)
) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
)) )
.left_join(groups_users::table.on( .left_join(
groups_users::users_organizations_uuid.eq(users_organizations::uuid) groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
)) )
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .left_join(
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) groups::table.on(groups::uuid
)) .eq(groups_users::groups_uuid)
.left_join(collections_groups::table.on( .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and( )
collections_groups::collections_uuid.eq(collections::uuid) .left_join(
) collections_groups::table.on(collections_groups::groups_uuid
)) .eq(groups_users::groups_uuid)
.filter( .and(collections_groups::collections_uuid.eq(collections::uuid))),
users_organizations::status.eq(MembershipStatus::Confirmed as i32) )
) .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.filter( .filter(
users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection users_collections::user_uuid
users_organizations::access_all.eq(true) // access_all in Organization .eq(user_uuid)
).or( .or(
groups::access_all.eq(true) // access_all in groups // Directly accessed collection
).or( // access via groups users_organizations::access_all.eq(true), // access_all in Organization
groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( )
collections_groups::collections_uuid.is_not_null() .or(
) groups::access_all.eq(true), // access_all in groups
) )
.or(
// access via groups
groups_users::users_organizations_uuid
.eq(users_organizations::uuid)
.and(collections_groups::collections_uuid.is_not_null()),
),
) )
.select(collections::all_columns) .select(collections::all_columns)
.distinct() .distinct()
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading collections") .expect("Error loading collections")
}} })
.await
} else { } else {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.left_join(users_collections::table.on( .left_join(
users_collections::collection_uuid.eq(collections::uuid).and( users_collections::table.on(users_collections::collection_uuid
users_collections::user_uuid.eq(user_uuid.clone()) .eq(collections::uuid)
.and(users_collections::user_uuid.eq(user_uuid.clone()))),
) )
)) .left_join(
.left_join(users_organizations::table.on( users_organizations::table.on(collections::org_uuid
collections::org_uuid.eq(users_organizations::org_uuid).and( .eq(users_organizations::org_uuid)
users_organizations::user_uuid.eq(user_uuid.clone()) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
) )
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.filter(users_collections::user_uuid.eq(user_uuid).or(
// Directly accessed collection
users_organizations::access_all.eq(true), // access_all in Organization
)) ))
.filter(
users_organizations::status.eq(MembershipStatus::Confirmed as i32)
)
.filter(
users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection
users_organizations::access_all.eq(true) // access_all in Organization
)
)
.select(collections::all_columns) .select(collections::all_columns)
.distinct() .distinct()
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading collections") .expect("Error loading collections")
}} })
.await
} }
} }
@ -312,256 +313,311 @@ impl Collection {
} }
pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.filter(collections::org_uuid.eq(org_uuid)) .filter(collections::org_uuid.eq(org_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading collections") .expect("Error loading collections")
}} })
.await
} }
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 { pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table.filter(collections::org_uuid.eq(org_uuid)).count().first::<i64>(conn).ok().unwrap_or(0)
.filter(collections::org_uuid.eq(org_uuid)) })
.count() .await
.first::<i64>(conn)
.ok()
.unwrap_or(0)
}}
} }
pub async fn find_by_uuid_and_org(uuid: &CollectionId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_org(uuid: &CollectionId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.filter(collections::uuid.eq(uuid)) .filter(collections::uuid.eq(uuid))
.filter(collections::org_uuid.eq(org_uuid)) .filter(collections::org_uuid.eq(org_uuid))
.select(collections::all_columns) .select(collections::all_columns)
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_uuid_and_user(uuid: &CollectionId, user_uuid: UserId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_user(uuid: &CollectionId, user_uuid: UserId, conn: &DbConn) -> Option<Self> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.left_join(users_collections::table.on( .left_join(
users_collections::collection_uuid.eq(collections::uuid).and( users_collections::table.on(users_collections::collection_uuid
users_collections::user_uuid.eq(user_uuid.clone()) .eq(collections::uuid)
.and(users_collections::user_uuid.eq(user_uuid.clone()))),
) )
)) .left_join(
.left_join(users_organizations::table.on( users_organizations::table.on(collections::org_uuid
collections::org_uuid.eq(users_organizations::org_uuid).and( .eq(users_organizations::org_uuid)
users_organizations::user_uuid.eq(user_uuid) .and(users_organizations::user_uuid.eq(user_uuid))),
) )
)) .left_join(
.left_join(groups_users::table.on( groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
groups_users::users_organizations_uuid.eq(users_organizations::uuid) )
)) .left_join(
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) groups::table.on(groups::uuid
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) .eq(groups_users::groups_uuid)
)) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
.left_join(collections_groups::table.on( )
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and( .left_join(
collections_groups::collections_uuid.eq(collections::uuid) collections_groups::table.on(collections_groups::groups_uuid
.eq(groups_users::groups_uuid)
.and(collections_groups::collections_uuid.eq(collections::uuid))),
) )
))
.filter(collections::uuid.eq(uuid)) .filter(collections::uuid.eq(uuid))
.filter( .filter(
users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection users_collections::collection_uuid
users_organizations::access_all.eq(true).or( // access_all in Organization .eq(uuid)
users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner .or(
)).or( // Directly accessed collection
groups::access_all.eq(true) // access_all in groups users_organizations::access_all.eq(true).or(
).or( // access via groups // access_all in Organization
groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( users_organizations::atype.le(MembershipType::Admin as i32), // Org admin or owner
collections_groups::collections_uuid.is_not_null() ),
)
.or(
groups::access_all.eq(true), // access_all in groups
)
.or(
// access via groups
groups_users::users_organizations_uuid
.eq(users_organizations::uuid)
.and(collections_groups::collections_uuid.is_not_null()),
),
) )
) .select(collections::all_columns)
).select(collections::all_columns)
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} else { } else {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.left_join(users_collections::table.on( .left_join(
users_collections::collection_uuid.eq(collections::uuid).and( users_collections::table.on(users_collections::collection_uuid
users_collections::user_uuid.eq(user_uuid.clone()) .eq(collections::uuid)
.and(users_collections::user_uuid.eq(user_uuid.clone()))),
) )
)) .left_join(
.left_join(users_organizations::table.on( users_organizations::table.on(collections::org_uuid
collections::org_uuid.eq(users_organizations::org_uuid).and( .eq(users_organizations::org_uuid)
users_organizations::user_uuid.eq(user_uuid) .and(users_organizations::user_uuid.eq(user_uuid))),
) )
))
.filter(collections::uuid.eq(uuid)) .filter(collections::uuid.eq(uuid))
.filter( .filter(users_collections::collection_uuid.eq(uuid).or(
users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection // Directly accessed collection
users_organizations::access_all.eq(true).or( // access_all in Organization users_organizations::access_all.eq(true).or(
users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner // access_all in Organization
users_organizations::atype.le(MembershipType::Admin as i32), // Org admin or owner
),
)) ))
).select(collections::all_columns) .select(collections::all_columns)
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
} }
pub async fn is_writable_by_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn is_writable_by_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool {
let user_uuid = user_uuid.to_string(); let user_uuid = user_uuid.to_string();
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.filter(collections::uuid.eq(&self.uuid)) .filter(collections::uuid.eq(&self.uuid))
.inner_join(users_organizations::table.on( .inner_join(
collections::org_uuid.eq(users_organizations::org_uuid) users_organizations::table.on(collections::org_uuid
.and(users_organizations::user_uuid.eq(user_uuid.clone())) .eq(users_organizations::org_uuid)
)) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
.left_join(users_collections::table.on( )
users_collections::collection_uuid.eq(collections::uuid) .left_join(
.and(users_collections::user_uuid.eq(user_uuid)) users_collections::table.on(users_collections::collection_uuid
)) .eq(collections::uuid)
.left_join(groups_users::table.on( .and(users_collections::user_uuid.eq(user_uuid))),
groups_users::users_organizations_uuid.eq(users_organizations::uuid) )
)) .left_join(
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) )
)) .left_join(
.left_join(collections_groups::table.on( groups::table.on(groups::uuid
collections_groups::groups_uuid.eq(groups_users::groups_uuid) .eq(groups_users::groups_uuid)
.and(collections_groups::collections_uuid.eq(collections::uuid)) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
)) )
.filter(users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner .left_join(
collections_groups::table.on(collections_groups::groups_uuid
.eq(groups_users::groups_uuid)
.and(collections_groups::collections_uuid.eq(collections::uuid))),
)
.filter(
users_organizations::atype
.le(MembershipType::Admin as i32) // Org admin or owner
.or(users_organizations::access_all.eq(true)) // access_all via membership .or(users_organizations::access_all.eq(true)) // access_all via membership
.or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection .or(users_collections::collection_uuid
.eq(&self.uuid) // write access given to collection
.and(users_collections::read_only.eq(false))) .and(users_collections::read_only.eq(false)))
.or(groups::access_all.eq(true)) // access_all via group .or(groups::access_all.eq(true)) // access_all via group
.or(collections_groups::collections_uuid.is_not_null() // write access given via group .or(collections_groups::collections_uuid
.and(collections_groups::read_only.eq(false))) .is_not_null() // write access given via group
.and(collections_groups::read_only.eq(false))),
) )
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok() .ok()
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} else { } else {
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.filter(collections::uuid.eq(&self.uuid)) .filter(collections::uuid.eq(&self.uuid))
.inner_join(users_organizations::table.on( .inner_join(
collections::org_uuid.eq(users_organizations::org_uuid) users_organizations::table.on(collections::org_uuid
.and(users_organizations::user_uuid.eq(user_uuid.clone())) .eq(users_organizations::org_uuid)
)) .and(users_organizations::user_uuid.eq(user_uuid.clone()))),
.left_join(users_collections::table.on( )
users_collections::collection_uuid.eq(collections::uuid) .left_join(
.and(users_collections::user_uuid.eq(user_uuid)) users_collections::table.on(users_collections::collection_uuid
)) .eq(collections::uuid)
.filter(users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner .and(users_collections::user_uuid.eq(user_uuid))),
)
.filter(
users_organizations::atype
.le(MembershipType::Admin as i32) // Org admin or owner
.or(users_organizations::access_all.eq(true)) // access_all via membership .or(users_organizations::access_all.eq(true)) // access_all via membership
.or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection .or(users_collections::collection_uuid
.and(users_collections::read_only.eq(false))) .eq(&self.uuid) // write access given to collection
.and(users_collections::read_only.eq(false))),
) )
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok() .ok()
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} }
} }
pub async fn hide_passwords_for_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn hide_passwords_for_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool {
let user_uuid = user_uuid.to_string(); let user_uuid = user_uuid.to_string();
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.left_join(users_collections::table.on( .left_join(
users_collections::collection_uuid.eq(collections::uuid).and( users_collections::table.on(users_collections::collection_uuid
users_collections::user_uuid.eq(user_uuid.clone()) .eq(collections::uuid)
) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
)) )
.left_join(users_organizations::table.on( .left_join(
collections::org_uuid.eq(users_organizations::org_uuid).and( users_organizations::table.on(collections::org_uuid
users_organizations::user_uuid.eq(user_uuid) .eq(users_organizations::org_uuid)
.and(users_organizations::user_uuid.eq(user_uuid))),
)
.left_join(groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)))
.left_join(
groups::table.on(groups::uuid
.eq(groups_users::groups_uuid)
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
)
.left_join(
collections_groups::table.on(collections_groups::groups_uuid
.eq(groups_users::groups_uuid)
.and(collections_groups::collections_uuid.eq(collections::uuid))),
) )
))
.left_join(groups_users::table.on(
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
))
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
))
.left_join(collections_groups::table.on(
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
collections_groups::collections_uuid.eq(collections::uuid)
)
))
.filter(collections::uuid.eq(&self.uuid)) .filter(collections::uuid.eq(&self.uuid))
.filter( .filter(
users_collections::collection_uuid.eq(&self.uuid).and(users_collections::hide_passwords.eq(true)).or(// Directly accessed collection users_collections::collection_uuid
users_organizations::access_all.eq(true).or( // access_all in Organization .eq(&self.uuid)
users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner .and(users_collections::hide_passwords.eq(true))
)).or( .or(
groups::access_all.eq(true) // access_all in groups // Directly accessed collection
).or( // access via groups users_organizations::access_all.eq(true).or(
// access_all in Organization
users_organizations::atype.le(MembershipType::Admin as i32), // Org admin or owner
),
)
.or(
groups::access_all.eq(true), // access_all in groups
)
.or(
// access via groups
groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( groups_users::users_organizations_uuid.eq(users_organizations::uuid).and(
collections_groups::collections_uuid.is_not_null().and( collections_groups::collections_uuid
collections_groups::hide_passwords.eq(true)) .is_not_null()
) .and(collections_groups::hide_passwords.eq(true)),
) ),
),
) )
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok() .ok()
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} }
pub async fn is_coll_manageable_by_user(uuid: &CollectionId, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn is_coll_manageable_by_user(uuid: &CollectionId, user_uuid: &UserId, conn: &DbConn) -> bool {
let uuid = uuid.to_string(); let uuid = uuid.to_string();
let user_uuid = user_uuid.to_string(); let user_uuid = user_uuid.to_string();
db_run! { conn: { conn.run(move |conn| {
collections::table collections::table
.left_join(users_collections::table.on( .left_join(
users_collections::collection_uuid.eq(collections::uuid).and( users_collections::table.on(users_collections::collection_uuid
users_collections::user_uuid.eq(user_uuid.clone()) .eq(collections::uuid)
) .and(users_collections::user_uuid.eq(user_uuid.clone()))),
)) )
.left_join(users_organizations::table.on( .left_join(
collections::org_uuid.eq(users_organizations::org_uuid).and( users_organizations::table.on(collections::org_uuid
users_organizations::user_uuid.eq(user_uuid) .eq(users_organizations::org_uuid)
) .and(users_organizations::user_uuid.eq(user_uuid))),
)) )
.left_join(groups_users::table.on( .left_join(groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)))
groups_users::users_organizations_uuid.eq(users_organizations::uuid) .left_join(
)) groups::table.on(groups::uuid
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .eq(groups_users::groups_uuid)
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
)) )
.left_join(collections_groups::table.on( .left_join(
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and( collections_groups::table.on(collections_groups::groups_uuid
collections_groups::collections_uuid.eq(collections::uuid) .eq(groups_users::groups_uuid)
.and(collections_groups::collections_uuid.eq(collections::uuid))),
) )
))
.filter(collections::uuid.eq(&uuid)) .filter(collections::uuid.eq(&uuid))
.filter( .filter(
users_collections::collection_uuid.eq(&uuid).and(users_collections::manage.eq(true)).or(// Directly accessed collection users_collections::collection_uuid
users_organizations::access_all.eq(true).or( // access_all in Organization .eq(&uuid)
users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner .and(users_collections::manage.eq(true))
)).or( .or(
groups::access_all.eq(true) // access_all in groups // Directly accessed collection
).or( // access via groups users_organizations::access_all.eq(true).or(
// access_all in Organization
users_organizations::atype.le(MembershipType::Admin as i32), // Org admin or owner
),
)
.or(
groups::access_all.eq(true), // access_all in groups
)
.or(
// access via groups
groups_users::users_organizations_uuid.eq(users_organizations::uuid).and( groups_users::users_organizations_uuid.eq(users_organizations::uuid).and(
collections_groups::collections_uuid.is_not_null().and( collections_groups::collections_uuid
collections_groups::manage.eq(true)) .is_not_null()
) .and(collections_groups::manage.eq(true)),
) ),
),
) )
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok() .ok()
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} }
pub async fn is_manageable_by_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn is_manageable_by_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool {
@ -576,7 +632,7 @@ impl CollectionUser {
user_uuid: &UserId, user_uuid: &UserId,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_collections::table users_collections::table
.filter(users_collections::user_uuid.eq(user_uuid)) .filter(users_collections::user_uuid.eq(user_uuid))
.inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid)))
@ -584,23 +640,34 @@ impl CollectionUser {
.select(users_collections::all_columns) .select(users_collections::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading users_collections") .expect("Error loading users_collections")
}} })
.await
} }
pub async fn find_by_organization_swap_user_uuid_with_member_uuid( pub async fn find_by_organization_swap_user_uuid_with_member_uuid(
org_uuid: &OrganizationId, org_uuid: &OrganizationId,
conn: &DbConn, conn: &DbConn,
) -> Vec<CollectionMembership> { ) -> Vec<CollectionMembership> {
let col_users = db_run! { conn: { let col_users = conn
.run(move |conn| {
users_collections::table users_collections::table
.inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid)))
.filter(collections::org_uuid.eq(org_uuid)) .filter(collections::org_uuid.eq(org_uuid))
.inner_join(users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid))) .inner_join(
users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid)),
)
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.select((users_organizations::uuid, users_collections::collection_uuid, users_collections::read_only, users_collections::hide_passwords, users_collections::manage)) .select((
users_organizations::uuid,
users_collections::collection_uuid,
users_collections::read_only,
users_collections::hide_passwords,
users_collections::manage,
))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading users_collections") .expect("Error loading users_collections")
}}; })
.await;
col_users.into_iter().map(Into::into).collect() col_users.into_iter().map(Into::into).collect()
} }
@ -670,7 +737,7 @@ impl CollectionUser {
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
User::update_uuid_revision(&self.user_uuid, conn).await; User::update_uuid_revision(&self.user_uuid, conn).await;
db_run! { conn: { conn.run(move |conn| {
diesel::delete( diesel::delete(
users_collections::table users_collections::table
.filter(users_collections::user_uuid.eq(&self.user_uuid)) .filter(users_collections::user_uuid.eq(&self.user_uuid))
@ -678,17 +745,19 @@ impl CollectionUser {
) )
.execute(conn) .execute(conn)
.map_res("Error removing user from collection") .map_res("Error removing user from collection")
}} })
.await
} }
pub async fn find_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_collections::table users_collections::table
.filter(users_collections::collection_uuid.eq(collection_uuid)) .filter(users_collections::collection_uuid.eq(collection_uuid))
.select(users_collections::all_columns) .select(users_collections::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading users_collections") .expect("Error loading users_collections")
}} })
.await
} }
pub async fn find_by_org_and_coll_swap_user_uuid_with_member_uuid( pub async fn find_by_org_and_coll_swap_user_uuid_with_member_uuid(
@ -696,15 +765,25 @@ impl CollectionUser {
collection_uuid: &CollectionId, collection_uuid: &CollectionId,
conn: &DbConn, conn: &DbConn,
) -> Vec<CollectionMembership> { ) -> Vec<CollectionMembership> {
let col_users = db_run! { conn: { let col_users = conn
.run(move |conn| {
users_collections::table users_collections::table
.filter(users_collections::collection_uuid.eq(collection_uuid)) .filter(users_collections::collection_uuid.eq(collection_uuid))
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.inner_join(users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid))) .inner_join(
.select((users_organizations::uuid, users_collections::collection_uuid, users_collections::read_only, users_collections::hide_passwords, users_collections::manage)) users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid)),
)
.select((
users_organizations::uuid,
users_collections::collection_uuid,
users_collections::read_only,
users_collections::hide_passwords,
users_collections::manage,
))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading users_collections") .expect("Error loading users_collections")
}}; })
.await;
col_users.into_iter().map(Into::into).collect() col_users.into_iter().map(Into::into).collect()
} }
@ -713,24 +792,26 @@ impl CollectionUser {
user_uuid: &UserId, user_uuid: &UserId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
users_collections::table users_collections::table
.filter(users_collections::collection_uuid.eq(collection_uuid)) .filter(users_collections::collection_uuid.eq(collection_uuid))
.filter(users_collections::user_uuid.eq(user_uuid)) .filter(users_collections::user_uuid.eq(user_uuid))
.select(users_collections::all_columns) .select(users_collections::all_columns)
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_collections::table users_collections::table
.filter(users_collections::user_uuid.eq(user_uuid)) .filter(users_collections::user_uuid.eq(user_uuid))
.select(users_collections::all_columns) .select(users_collections::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading users_collections") .expect("Error loading users_collections")
}} })
.await
} }
pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> EmptyResult {
@ -738,11 +819,12 @@ impl CollectionUser {
User::update_uuid_revision(&collection.user_uuid, conn).await; User::update_uuid_revision(&collection.user_uuid, conn).await;
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(users_collections::table.filter(users_collections::collection_uuid.eq(collection_uuid))) diesel::delete(users_collections::table.filter(users_collections::collection_uuid.eq(collection_uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting users from collection") .map_res("Error deleting users from collection")
}} })
.await
} }
pub async fn delete_all_by_user_and_org( pub async fn delete_all_by_user_and_org(
@ -752,17 +834,21 @@ impl CollectionUser {
) -> EmptyResult { ) -> EmptyResult {
let collectionusers = Self::find_by_organization_and_user_uuid(org_uuid, user_uuid, conn).await; let collectionusers = Self::find_by_organization_and_user_uuid(org_uuid, user_uuid, conn).await;
db_run! { conn: { conn.run(move |conn| {
for user in collectionusers { for user in collectionusers {
let _: () = diesel::delete(users_collections::table.filter( let _: () = diesel::delete(
users_collections::user_uuid.eq(user_uuid) users_collections::table.filter(
.and(users_collections::collection_uuid.eq(user.collection_uuid)) users_collections::user_uuid
)) .eq(user_uuid)
.and(users_collections::collection_uuid.eq(user.collection_uuid)),
),
)
.execute(conn) .execute(conn)
.map_res("Error removing user from collections")?; .map_res("Error removing user from collections")?;
} }
Ok(()) Ok(())
}} })
.await
} }
pub async fn has_access_to_collection_by_user(col_id: &CollectionId, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn has_access_to_collection_by_user(col_id: &CollectionId, user_uuid: &UserId, conn: &DbConn) -> bool {
@ -805,7 +891,7 @@ impl CollectionCipher {
pub async fn delete(cipher_uuid: &CipherId, collection_uuid: &CollectionId, conn: &DbConn) -> EmptyResult { pub async fn delete(cipher_uuid: &CipherId, collection_uuid: &CollectionId, conn: &DbConn) -> EmptyResult {
Self::update_users_revision(collection_uuid, conn).await; Self::update_users_revision(collection_uuid, conn).await;
db_run! { conn: { conn.run(move |conn| {
diesel::delete( diesel::delete(
ciphers_collections::table ciphers_collections::table
.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid)) .filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))
@ -813,23 +899,26 @@ impl CollectionCipher {
) )
.execute(conn) .execute(conn)
.map_res("Error deleting cipher from collection") .map_res("Error deleting cipher from collection")
}} })
.await
} }
pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))) diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing cipher from collections") .map_res("Error removing cipher from collections")
}} })
.await
} }
pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(ciphers_collections::table.filter(ciphers_collections::collection_uuid.eq(collection_uuid))) diesel::delete(ciphers_collections::table.filter(ciphers_collections::collection_uuid.eq(collection_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing ciphers from collection") .map_res("Error removing ciphers from collection")
}} })
.await
} }
pub async fn update_users_revision(collection_uuid: &CollectionId, conn: &DbConn) { pub async fn update_users_revision(collection_uuid: &CollectionId, conn: &DbConn) {

58
src/db/models/device.rs

@ -169,21 +169,23 @@ impl Device {
} }
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(devices::table.filter(devices::user_uuid.eq(user_uuid))) diesel::delete(devices::table.filter(devices::user_uuid.eq(user_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing devices for user") .map_res("Error removing devices for user")
}} })
.await
} }
pub async fn find_by_uuid_and_user(uuid: &DeviceId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_user(uuid: &DeviceId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
devices::table devices::table
.filter(devices::uuid.eq(uuid)) .filter(devices::uuid.eq(uuid))
.filter(devices::user_uuid.eq(user_uuid)) .filter(devices::user_uuid.eq(user_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_with_auth_request_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<DeviceWithAuthRequest> { pub async fn find_with_auth_request_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<DeviceWithAuthRequest> {
@ -197,71 +199,65 @@ impl Device {
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
devices::table devices::table.filter(devices::user_uuid.eq(user_uuid)).load::<Self>(conn).expect("Error loading devices")
.filter(devices::user_uuid.eq(user_uuid)) })
.load::<Self>(conn) .await
.expect("Error loading devices")
}}
} }
pub async fn find_by_uuid(uuid: &DeviceId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &DeviceId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| devices::table.filter(devices::uuid.eq(uuid)).first::<Self>(conn).ok()).await
devices::table
.filter(devices::uuid.eq(uuid))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn clear_push_token_by_uuid(uuid: &DeviceId, conn: &DbConn) -> EmptyResult { pub async fn clear_push_token_by_uuid(uuid: &DeviceId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::update(devices::table) diesel::update(devices::table)
.filter(devices::uuid.eq(uuid)) .filter(devices::uuid.eq(uuid))
.set(devices::push_token.eq::<Option<String>>(None)) .set(devices::push_token.eq::<Option<String>>(None))
.execute(conn) .execute(conn)
.map_res("Error removing push token") .map_res("Error removing push token")
}} })
.await
} }
pub async fn find_by_refresh_token(refresh_token: &str, conn: &DbConn) -> Option<Self> { pub async fn find_by_refresh_token(refresh_token: &str, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| devices::table.filter(devices::refresh_token.eq(refresh_token)).first::<Self>(conn).ok())
devices::table .await
.filter(devices::refresh_token.eq(refresh_token))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_latest_active_by_user(user_uuid: &UserId, conn: &DbConn) -> Option<Self> { pub async fn find_latest_active_by_user(user_uuid: &UserId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
devices::table devices::table
.filter(devices::user_uuid.eq(user_uuid)) .filter(devices::user_uuid.eq(user_uuid))
.order(devices::updated_at.desc()) .order(devices::updated_at.desc())
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_push_devices_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_push_devices_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
devices::table devices::table
.filter(devices::user_uuid.eq(user_uuid)) .filter(devices::user_uuid.eq(user_uuid))
.filter(devices::push_token.is_not_null()) .filter(devices::push_token.is_not_null())
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading push devices") .expect("Error loading push devices")
}} })
.await
} }
pub async fn check_user_has_push_device(user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn check_user_has_push_device(user_uuid: &UserId, conn: &DbConn) -> bool {
db_run! { conn: { conn.run(move |conn| {
devices::table devices::table
.filter(devices::user_uuid.eq(user_uuid)) .filter(devices::user_uuid.eq(user_uuid))
.filter(devices::push_token.is_not_null()) .filter(devices::push_token.is_not_null())
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok() .ok()
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} }
pub async fn rotate_refresh_tokens_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn rotate_refresh_tokens_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {

79
src/db/models/emergency_access.rs

@ -186,28 +186,36 @@ impl EmergencyAccess {
self.status = status; self.status = status;
date.clone_into(&mut self.updated_at); date.clone_into(&mut self.updated_at);
db_run! { conn: { conn.run(move |conn| {
crate::util::retry(|| { crate::util::retry(
|| {
diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid))) diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid)))
.set((emergency_access::status.eq(status), emergency_access::updated_at.eq(date))) .set((emergency_access::status.eq(status), emergency_access::updated_at.eq(date)))
.execute(conn) .execute(conn)
}, 10) },
10,
)
.map_res("Error updating emergency access status") .map_res("Error updating emergency access status")
}} })
.await
} }
pub async fn update_last_notification_date_and_save(&mut self, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { pub async fn update_last_notification_date_and_save(&mut self, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult {
self.last_notification_at = Some(date.to_owned()); self.last_notification_at = Some(date.to_owned());
date.clone_into(&mut self.updated_at); date.clone_into(&mut self.updated_at);
db_run! { conn: { conn.run(move |conn| {
crate::util::retry(|| { crate::util::retry(
|| {
diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid))) diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid)))
.set((emergency_access::last_notification_at.eq(date), emergency_access::updated_at.eq(date))) .set((emergency_access::last_notification_at.eq(date), emergency_access::updated_at.eq(date)))
.execute(conn) .execute(conn)
}, 10) },
10,
)
.map_res("Error updating emergency access status") .map_res("Error updating emergency access status")
}} })
.await
} }
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
@ -230,11 +238,12 @@ impl EmergencyAccess {
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
User::update_uuid_revision(&self.grantor_uuid, conn).await; User::update_uuid_revision(&self.grantor_uuid, conn).await;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(emergency_access::table.filter(emergency_access::uuid.eq(self.uuid))) diesel::delete(emergency_access::table.filter(emergency_access::uuid.eq(self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing user from emergency access") .map_res("Error removing user from emergency access")
}} })
.await
} }
pub async fn find_by_grantor_uuid_and_grantee_uuid_or_email( pub async fn find_by_grantor_uuid_and_grantee_uuid_or_email(
@ -243,23 +252,25 @@ impl EmergencyAccess {
email: &str, email: &str,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::grantor_uuid.eq(grantor_uuid)) .filter(emergency_access::grantor_uuid.eq(grantor_uuid))
.filter(emergency_access::grantee_uuid.eq(grantee_uuid).or(emergency_access::email.eq(email))) .filter(emergency_access::grantee_uuid.eq(grantee_uuid).or(emergency_access::email.eq(email)))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_all_recoveries_initiated(conn: &DbConn) -> Vec<Self> { pub async fn find_all_recoveries_initiated(conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::status.eq(EmergencyAccessStatus::RecoveryInitiated as i32)) .filter(emergency_access::status.eq(EmergencyAccessStatus::RecoveryInitiated as i32))
.filter(emergency_access::recovery_initiated_at.is_not_null()) .filter(emergency_access::recovery_initiated_at.is_not_null())
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading emergency_access") .expect("Error loading emergency_access")
}} })
.await
} }
pub async fn find_by_uuid_and_grantor_uuid( pub async fn find_by_uuid_and_grantor_uuid(
@ -267,13 +278,14 @@ impl EmergencyAccess {
grantor_uuid: &UserId, grantor_uuid: &UserId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::uuid.eq(uuid)) .filter(emergency_access::uuid.eq(uuid))
.filter(emergency_access::grantor_uuid.eq(grantor_uuid)) .filter(emergency_access::grantor_uuid.eq(grantor_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_uuid_and_grantee_uuid( pub async fn find_by_uuid_and_grantee_uuid(
@ -281,13 +293,14 @@ impl EmergencyAccess {
grantee_uuid: &UserId, grantee_uuid: &UserId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::uuid.eq(uuid)) .filter(emergency_access::uuid.eq(uuid))
.filter(emergency_access::grantee_uuid.eq(grantee_uuid)) .filter(emergency_access::grantee_uuid.eq(grantee_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_uuid_and_grantee_email( pub async fn find_by_uuid_and_grantee_email(
@ -295,61 +308,67 @@ impl EmergencyAccess {
grantee_email: &str, grantee_email: &str,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::uuid.eq(uuid)) .filter(emergency_access::uuid.eq(uuid))
.filter(emergency_access::email.eq(grantee_email)) .filter(emergency_access::email.eq(grantee_email))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_all_by_grantee_uuid(grantee_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_all_by_grantee_uuid(grantee_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::grantee_uuid.eq(grantee_uuid)) .filter(emergency_access::grantee_uuid.eq(grantee_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading emergency_access") .expect("Error loading emergency_access")
}} })
.await
} }
pub async fn find_invited_by_grantee_email(grantee_email: &str, conn: &DbConn) -> Option<Self> { pub async fn find_invited_by_grantee_email(grantee_email: &str, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::email.eq(grantee_email)) .filter(emergency_access::email.eq(grantee_email))
.filter(emergency_access::status.eq(EmergencyAccessStatus::Invited as i32)) .filter(emergency_access::status.eq(EmergencyAccessStatus::Invited as i32))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_all_invited_by_grantee_email(grantee_email: &str, conn: &DbConn) -> Vec<Self> { pub async fn find_all_invited_by_grantee_email(grantee_email: &str, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::email.eq(grantee_email)) .filter(emergency_access::email.eq(grantee_email))
.filter(emergency_access::status.eq(EmergencyAccessStatus::Invited as i32)) .filter(emergency_access::status.eq(EmergencyAccessStatus::Invited as i32))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading emergency_access") .expect("Error loading emergency_access")
}} })
.await
} }
pub async fn find_all_by_grantor_uuid(grantor_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_all_by_grantor_uuid(grantor_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::grantor_uuid.eq(grantor_uuid)) .filter(emergency_access::grantor_uuid.eq(grantor_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading emergency_access") .expect("Error loading emergency_access")
}} })
.await
} }
pub async fn find_all_confirmed_by_grantor_uuid(grantor_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_all_confirmed_by_grantor_uuid(grantor_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
emergency_access::table emergency_access::table
.filter(emergency_access::grantor_uuid.eq(grantor_uuid)) .filter(emergency_access::grantor_uuid.eq(grantor_uuid))
.filter(emergency_access::status.ge(EmergencyAccessStatus::Confirmed as i32)) .filter(emergency_access::status.ge(EmergencyAccessStatus::Confirmed as i32))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading emergency_access") .expect("Error loading emergency_access")
}} })
.await
} }
pub async fn accept_invite(&mut self, grantee_uuid: &UserId, grantee_email: &str, conn: &DbConn) -> EmptyResult { pub async fn accept_invite(&mut self, grantee_uuid: &UserId, grantee_email: &str, conn: &DbConn) -> EmptyResult {

47
src/db/models/event.rs

@ -256,11 +256,10 @@ impl Event {
} }
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(event::table.filter(event::uuid.eq(self.uuid))) diesel::delete(event::table.filter(event::uuid.eq(self.uuid))).execute(conn).map_res("Error deleting event")
.execute(conn) })
.map_res("Error deleting event") .await
}}
} }
/// ############## /// ##############
@ -271,7 +270,7 @@ impl Event {
end: &NaiveDateTime, end: &NaiveDateTime,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
event::table event::table
.filter(event::org_uuid.eq(org_uuid)) .filter(event::org_uuid.eq(org_uuid))
.filter(event::event_date.between(start, end)) .filter(event::event_date.between(start, end))
@ -279,18 +278,15 @@ impl Event {
.limit(Self::PAGE_SIZE) .limit(Self::PAGE_SIZE)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error filtering events") .expect("Error filtering events")
}} })
.await
} }
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 { pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
event::table event::table.filter(event::org_uuid.eq(org_uuid)).count().first::<i64>(conn).ok().unwrap_or(0)
.filter(event::org_uuid.eq(org_uuid)) })
.count() .await
.first::<i64>(conn)
.ok()
.unwrap_or(0)
}}
} }
pub async fn find_by_org_and_member( pub async fn find_by_org_and_member(
@ -300,18 +296,23 @@ impl Event {
end: &NaiveDateTime, end: &NaiveDateTime,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
event::table event::table
.inner_join(users_organizations::table.on(users_organizations::uuid.eq(member_uuid))) .inner_join(users_organizations::table.on(users_organizations::uuid.eq(member_uuid)))
.filter(event::org_uuid.eq(org_uuid)) .filter(event::org_uuid.eq(org_uuid))
.filter(event::event_date.between(start, end)) .filter(event::event_date.between(start, end))
.filter(event::user_uuid.eq(users_organizations::user_uuid.nullable()).or(event::act_user_uuid.eq(users_organizations::user_uuid.nullable()))) .filter(
event::user_uuid
.eq(users_organizations::user_uuid.nullable())
.or(event::act_user_uuid.eq(users_organizations::user_uuid.nullable())),
)
.select(event::all_columns) .select(event::all_columns)
.order_by(event::event_date.desc()) .order_by(event::event_date.desc())
.limit(Self::PAGE_SIZE) .limit(Self::PAGE_SIZE)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error filtering events") .expect("Error filtering events")
}} })
.await
} }
pub async fn find_by_cipher_uuid( pub async fn find_by_cipher_uuid(
@ -320,7 +321,7 @@ impl Event {
end: &NaiveDateTime, end: &NaiveDateTime,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
event::table event::table
.filter(event::cipher_uuid.eq(cipher_uuid)) .filter(event::cipher_uuid.eq(cipher_uuid))
.filter(event::event_date.between(start, end)) .filter(event::event_date.between(start, end))
@ -328,17 +329,19 @@ impl Event {
.limit(Self::PAGE_SIZE) .limit(Self::PAGE_SIZE)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error filtering events") .expect("Error filtering events")
}} })
.await
} }
pub async fn clean_events(conn: &DbConn) -> EmptyResult { pub async fn clean_events(conn: &DbConn) -> EmptyResult {
if let Some(days_to_retain) = CONFIG.events_days_retain() { if let Some(days_to_retain) = CONFIG.events_days_retain() {
let dt = Utc::now().naive_utc() - TimeDelta::try_days(days_to_retain).unwrap(); let dt = Utc::now().naive_utc() - TimeDelta::try_days(days_to_retain).unwrap();
db_run! { conn: { conn.run(move |conn| {
diesel::delete(event::table.filter(event::event_date.lt(dt))) diesel::delete(event::table.filter(event::event_date.lt(dt)))
.execute(conn) .execute(conn)
.map_res("Error cleaning old events") .map_res("Error cleaning old events")
}} })
.await
} else { } else {
Ok(()) Ok(())
} }

41
src/db/models/favorite.rs

@ -19,16 +19,15 @@ pub struct Favorite {
impl Favorite { impl Favorite {
// Returns whether the specified cipher is a favorite of the specified user. // Returns whether the specified cipher is a favorite of the specified user.
pub async fn is_favorite(cipher_uuid: &CipherId, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn is_favorite(cipher_uuid: &CipherId, user_uuid: &UserId, conn: &DbConn) -> bool {
db_run! { conn: { conn.run(move |conn| {
let query = favorites::table let query = favorites::table
.filter(favorites::cipher_uuid.eq(cipher_uuid)) .filter(favorites::cipher_uuid.eq(cipher_uuid))
.filter(favorites::user_uuid.eq(user_uuid)) .filter(favorites::user_uuid.eq(user_uuid))
.count(); .count();
query.first::<i64>(conn) query.first::<i64>(conn).ok().unwrap_or(0) != 0
.ok() })
.unwrap_or(0) != 0 .await
}}
} }
// Sets whether the specified cipher is a favorite of the specified user. // Sets whether the specified cipher is a favorite of the specified user.
@ -42,27 +41,26 @@ impl Favorite {
match (old, new) { match (old, new) {
(false, true) => { (false, true) => {
User::update_uuid_revision(user_uuid, conn).await; User::update_uuid_revision(user_uuid, conn).await;
db_run! { conn: { conn.run(move |conn| {
diesel::insert_into(favorites::table) diesel::insert_into(favorites::table)
.values(( .values((favorites::user_uuid.eq(user_uuid), favorites::cipher_uuid.eq(cipher_uuid)))
favorites::user_uuid.eq(user_uuid),
favorites::cipher_uuid.eq(cipher_uuid),
))
.execute(conn) .execute(conn)
.map_res("Error adding favorite") .map_res("Error adding favorite")
}} })
.await
} }
(true, false) => { (true, false) => {
User::update_uuid_revision(user_uuid, conn).await; User::update_uuid_revision(user_uuid, conn).await;
db_run! { conn: { conn.run(move |conn| {
diesel::delete( diesel::delete(
favorites::table favorites::table
.filter(favorites::user_uuid.eq(user_uuid)) .filter(favorites::user_uuid.eq(user_uuid))
.filter(favorites::cipher_uuid.eq(cipher_uuid)) .filter(favorites::cipher_uuid.eq(cipher_uuid)),
) )
.execute(conn) .execute(conn)
.map_res("Error removing favorite") .map_res("Error removing favorite")
}} })
.await
} }
// Otherwise, the favorite status is already what it should be. // Otherwise, the favorite status is already what it should be.
_ => Ok(()), _ => Ok(()),
@ -71,31 +69,34 @@ impl Favorite {
// Delete all favorite entries associated with the specified cipher. // Delete all favorite entries associated with the specified cipher.
pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid))) diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing favorites by cipher") .map_res("Error removing favorites by cipher")
}} })
.await
} }
// Delete all favorite entries associated with the specified user. // Delete all favorite entries associated with the specified user.
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid))) diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing favorites by user") .map_res("Error removing favorites by user")
}} })
.await
} }
/// Return a vec with (cipher_uuid) this will only contain favorite flagged ciphers /// Return a vec with (cipher_uuid) this will only contain favorite flagged ciphers
/// This is used during a full sync so we only need one query for all favorite cipher matches. /// This is used during a full sync so we only need one query for all favorite cipher matches.
pub async fn get_all_cipher_uuid_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<CipherId> { pub async fn get_all_cipher_uuid_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<CipherId> {
db_run! { conn: { conn.run(move |conn| {
favorites::table favorites::table
.filter(favorites::user_uuid.eq(user_uuid)) .filter(favorites::user_uuid.eq(user_uuid))
.select(favorites::cipher_uuid) .select(favorites::cipher_uuid)
.load::<CipherId>(conn) .load::<CipherId>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
} }

50
src/db/models/folder.rs

@ -110,11 +110,12 @@ impl Folder {
User::update_uuid_revision(&self.user_uuid, conn).await; User::update_uuid_revision(&self.user_uuid, conn).await;
FolderCipher::delete_all_by_folder(&self.uuid, conn).await?; FolderCipher::delete_all_by_folder(&self.uuid, conn).await?;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(folders::table.filter(folders::uuid.eq(&self.uuid))) diesel::delete(folders::table.filter(folders::uuid.eq(&self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting folder") .map_res("Error deleting folder")
}} })
.await
} }
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
@ -125,22 +126,21 @@ impl Folder {
} }
pub async fn find_by_uuid_and_user(uuid: &FolderId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_user(uuid: &FolderId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
folders::table folders::table
.filter(folders::uuid.eq(uuid)) .filter(folders::uuid.eq(uuid))
.filter(folders::user_uuid.eq(user_uuid)) .filter(folders::user_uuid.eq(user_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
folders::table folders::table.filter(folders::user_uuid.eq(user_uuid)).load::<Self>(conn).expect("Error loading folders")
.filter(folders::user_uuid.eq(user_uuid)) })
.load::<Self>(conn) .await
.expect("Error loading folders")
}}
} }
} }
@ -168,7 +168,7 @@ impl FolderCipher {
} }
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete( diesel::delete(
folders_ciphers::table folders_ciphers::table
.filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid)) .filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid))
@ -176,23 +176,26 @@ impl FolderCipher {
) )
.execute(conn) .execute(conn)
.map_res("Error removing cipher from folder") .map_res("Error removing cipher from folder")
}} })
.await
} }
pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(folders_ciphers::table.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))) diesel::delete(folders_ciphers::table.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing cipher from folders") .map_res("Error removing cipher from folders")
}} })
.await
} }
pub async fn delete_all_by_folder(folder_uuid: &FolderId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_folder(folder_uuid: &FolderId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(folders_ciphers::table.filter(folders_ciphers::folder_uuid.eq(folder_uuid))) diesel::delete(folders_ciphers::table.filter(folders_ciphers::folder_uuid.eq(folder_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing ciphers from folder") .map_res("Error removing ciphers from folder")
}} })
.await
} }
pub async fn find_by_folder_and_cipher( pub async fn find_by_folder_and_cipher(
@ -200,35 +203,38 @@ impl FolderCipher {
cipher_uuid: &CipherId, cipher_uuid: &CipherId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
folders_ciphers::table folders_ciphers::table
.filter(folders_ciphers::folder_uuid.eq(folder_uuid)) .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid)) .filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_folder(folder_uuid: &FolderId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_folder(folder_uuid: &FolderId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
folders_ciphers::table folders_ciphers::table
.filter(folders_ciphers::folder_uuid.eq(folder_uuid)) .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading folders") .expect("Error loading folders")
}} })
.await
} }
/// Return a vec with (cipher_uuid, folder_uuid) /// Return a vec with (cipher_uuid, folder_uuid)
/// This is used during a full sync so we only need one query for all folder matches. /// This is used during a full sync so we only need one query for all folder matches.
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<(CipherId, FolderId)> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<(CipherId, FolderId)> {
db_run! { conn: { conn.run(move |conn| {
folders_ciphers::table folders_ciphers::table
.inner_join(folders::table) .inner_join(folders::table)
.filter(folders::user_uuid.eq(user_uuid)) .filter(folders::user_uuid.eq(user_uuid))
.select(folders_ciphers::all_columns) .select(folders_ciphers::all_columns)
.load::<(CipherId, FolderId)>(conn) .load::<(CipherId, FolderId)>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
} }

235
src/db/models/group.rs

@ -203,33 +203,31 @@ impl Group {
} }
pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
groups::table groups::table
.filter(groups::organizations_uuid.eq(org_uuid)) .filter(groups::organizations_uuid.eq(org_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading groups") .expect("Error loading groups")
}} })
.await
} }
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 { pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
groups::table groups::table.filter(groups::organizations_uuid.eq(org_uuid)).count().first::<i64>(conn).ok().unwrap_or(0)
.filter(groups::organizations_uuid.eq(org_uuid)) })
.count() .await
.first::<i64>(conn)
.ok()
.unwrap_or(0)
}}
} }
pub async fn find_by_uuid_and_org(uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_org(uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
groups::table groups::table
.filter(groups::uuid.eq(uuid)) .filter(groups::uuid.eq(uuid))
.filter(groups::organizations_uuid.eq(org_uuid)) .filter(groups::organizations_uuid.eq(org_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_external_id_and_org( pub async fn find_by_external_id_and_org(
@ -237,60 +235,64 @@ impl Group {
org_uuid: &OrganizationId, org_uuid: &OrganizationId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
groups::table groups::table
.filter(groups::external_id.eq(external_id)) .filter(groups::external_id.eq(external_id))
.filter(groups::organizations_uuid.eq(org_uuid)) .filter(groups::organizations_uuid.eq(org_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
//Returns all organizations the user has full access to //Returns all organizations the user has full access to
pub async fn get_orgs_by_user_with_full_access(user_uuid: &UserId, conn: &DbConn) -> Vec<OrganizationId> { pub async fn get_orgs_by_user_with_full_access(user_uuid: &UserId, conn: &DbConn) -> Vec<OrganizationId> {
db_run! { conn: { conn.run(move |conn| {
groups_users::table groups_users::table
.inner_join(users_organizations::table.on( .inner_join(
users_organizations::uuid.eq(groups_users::users_organizations_uuid) users_organizations::table.on(users_organizations::uuid.eq(groups_users::users_organizations_uuid)),
)) )
.inner_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .inner_join(
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) groups::table.on(groups::uuid
)) .eq(groups_users::groups_uuid)
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
)
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(groups::access_all.eq(true)) .filter(groups::access_all.eq(true))
.select(groups::organizations_uuid) .select(groups::organizations_uuid)
.distinct() .distinct()
.load::<OrganizationId>(conn) .load::<OrganizationId>(conn)
.expect("Error loading organization group full access information for user") .expect("Error loading organization group full access information for user")
}} })
.await
} }
pub async fn is_in_full_access_group(user_uuid: &UserId, org_uuid: &OrganizationId, conn: &DbConn) -> bool { pub async fn is_in_full_access_group(user_uuid: &UserId, org_uuid: &OrganizationId, conn: &DbConn) -> bool {
db_run! { conn: { conn.run(move |conn| {
groups::table groups::table
.inner_join(groups_users::table.on( .inner_join(groups_users::table.on(groups_users::groups_uuid.eq(groups::uuid)))
groups_users::groups_uuid.eq(groups::uuid) .inner_join(
)) users_organizations::table.on(users_organizations::uuid.eq(groups_users::users_organizations_uuid)),
.inner_join(users_organizations::table.on( )
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
))
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(groups::organizations_uuid.eq(org_uuid)) .filter(groups::organizations_uuid.eq(org_uuid))
.filter(groups::access_all.eq(true)) .filter(groups::access_all.eq(true))
.select(groups::access_all) .select(groups::access_all)
.first::<bool>(conn) .first::<bool>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
pub async fn delete(&self, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete(&self, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
CollectionGroup::delete_all_by_group(&self.uuid, org_uuid, conn).await?; CollectionGroup::delete_all_by_group(&self.uuid, org_uuid, conn).await?;
GroupUser::delete_all_by_group(&self.uuid, org_uuid, conn).await?; GroupUser::delete_all_by_group(&self.uuid, org_uuid, conn).await?;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(groups::table.filter(groups::uuid.eq(&self.uuid))) diesel::delete(groups::table.filter(groups::uuid.eq(&self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting group") .map_res("Error deleting group")
}} })
.await
} }
pub async fn update_revision(uuid: &GroupId, conn: &DbConn) { pub async fn update_revision(uuid: &GroupId, conn: &DbConn) {
@ -300,14 +302,18 @@ impl Group {
} }
async fn update_revision_impl(uuid: &GroupId, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { async fn update_revision_impl(uuid: &GroupId, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
crate::util::retry(|| { crate::util::retry(
|| {
diesel::update(groups::table.filter(groups::uuid.eq(uuid))) diesel::update(groups::table.filter(groups::uuid.eq(uuid)))
.set(groups::revision_date.eq(date)) .set(groups::revision_date.eq(date))
.execute(conn) .execute(conn)
}, 10) },
10,
)
.map_res("Error updating group revision") .map_res("Error updating group revision")
}} })
.await
} }
} }
@ -372,60 +378,63 @@ impl CollectionGroup {
} }
pub async fn find_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
collections_groups::table collections_groups::table
.inner_join(groups::table.on( .inner_join(groups::table.on(groups::uuid.eq(collections_groups::groups_uuid)))
groups::uuid.eq(collections_groups::groups_uuid) .inner_join(
)) collections::table.on(collections::uuid
.inner_join(collections::table.on( .eq(collections_groups::collections_uuid)
collections::uuid.eq(collections_groups::collections_uuid) .and(collections::org_uuid.eq(groups::organizations_uuid))),
.and(collections::org_uuid.eq(groups::organizations_uuid)) )
))
.filter(collections_groups::groups_uuid.eq(group_uuid)) .filter(collections_groups::groups_uuid.eq(group_uuid))
.filter(collections::org_uuid.eq(org_uuid)) .filter(collections::org_uuid.eq(org_uuid))
.select(collections_groups::all_columns) .select(collections_groups::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading collection groups") .expect("Error loading collection groups")
}} })
.await
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
collections_groups::table collections_groups::table
.inner_join(groups_users::table.on( .inner_join(groups_users::table.on(groups_users::groups_uuid.eq(collections_groups::groups_uuid)))
groups_users::groups_uuid.eq(collections_groups::groups_uuid) .inner_join(
)) users_organizations::table.on(users_organizations::uuid.eq(groups_users::users_organizations_uuid)),
.inner_join(users_organizations::table.on( )
users_organizations::uuid.eq(groups_users::users_organizations_uuid) .inner_join(
)) groups::table.on(groups::uuid
.inner_join(groups::table.on(groups::uuid.eq(collections_groups::groups_uuid) .eq(collections_groups::groups_uuid)
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
)) )
.inner_join(collections::table.on( .inner_join(
collections::uuid.eq(collections_groups::collections_uuid) collections::table.on(collections::uuid
.and(collections::org_uuid.eq(groups::organizations_uuid)) .eq(collections_groups::collections_uuid)
)) .and(collections::org_uuid.eq(groups::organizations_uuid))),
)
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.select(collections_groups::all_columns) .select(collections_groups::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading user collection groups") .expect("Error loading user collection groups")
}} })
.await
} }
pub async fn find_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
collections_groups::table collections_groups::table
.filter(collections_groups::collections_uuid.eq(collection_uuid)) .filter(collections_groups::collections_uuid.eq(collection_uuid))
.inner_join(collections::table.on( .inner_join(collections::table.on(collections::uuid.eq(collections_groups::collections_uuid)))
collections::uuid.eq(collections_groups::collections_uuid) .inner_join(
)) groups::table.on(groups::uuid
.inner_join(groups::table.on(groups::uuid.eq(collections_groups::groups_uuid) .eq(collections_groups::groups_uuid)
.and(groups::organizations_uuid.eq(collections::org_uuid)) .and(groups::organizations_uuid.eq(collections::org_uuid))),
)) )
.select(collections_groups::all_columns) .select(collections_groups::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading collection groups") .expect("Error loading collection groups")
}} })
.await
} }
pub async fn delete(&self, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete(&self, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
@ -434,13 +443,14 @@ impl CollectionGroup {
group_user.update_user_revision(conn).await; group_user.update_user_revision(conn).await;
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(collections_groups::table) diesel::delete(collections_groups::table)
.filter(collections_groups::collections_uuid.eq(&self.collections_uuid)) .filter(collections_groups::collections_uuid.eq(&self.collections_uuid))
.filter(collections_groups::groups_uuid.eq(&self.groups_uuid)) .filter(collections_groups::groups_uuid.eq(&self.groups_uuid))
.execute(conn) .execute(conn)
.map_res("Error deleting collection group") .map_res("Error deleting collection group")
}} })
.await
} }
pub async fn delete_all_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
@ -449,12 +459,13 @@ impl CollectionGroup {
group_user.update_user_revision(conn).await; group_user.update_user_revision(conn).await;
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(collections_groups::table) diesel::delete(collections_groups::table)
.filter(collections_groups::groups_uuid.eq(group_uuid)) .filter(collections_groups::groups_uuid.eq(group_uuid))
.execute(conn) .execute(conn)
.map_res("Error deleting collection group") .map_res("Error deleting collection group")
}} })
.await
} }
pub async fn delete_all_by_collection( pub async fn delete_all_by_collection(
@ -470,12 +481,13 @@ impl CollectionGroup {
} }
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(collections_groups::table) diesel::delete(collections_groups::table)
.filter(collections_groups::collections_uuid.eq(collection_uuid)) .filter(collections_groups::collections_uuid.eq(collection_uuid))
.execute(conn) .execute(conn)
.map_res("Error deleting collection group") .map_res("Error deleting collection group")
}} })
.await
} }
} }
@ -527,30 +539,31 @@ impl GroupUser {
} }
pub async fn find_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
groups_users::table groups_users::table
.inner_join(groups::table.on( .inner_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
groups::uuid.eq(groups_users::groups_uuid) .inner_join(
)) users_organizations::table.on(users_organizations::uuid
.inner_join(users_organizations::table.on( .eq(groups_users::users_organizations_uuid)
users_organizations::uuid.eq(groups_users::users_organizations_uuid) .and(users_organizations::org_uuid.eq(groups::organizations_uuid))),
.and(users_organizations::org_uuid.eq(groups::organizations_uuid)) )
))
.filter(groups_users::groups_uuid.eq(group_uuid)) .filter(groups_users::groups_uuid.eq(group_uuid))
.filter(groups::organizations_uuid.eq(org_uuid)) .filter(groups::organizations_uuid.eq(org_uuid))
.select(groups_users::all_columns) .select(groups_users::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading group users") .expect("Error loading group users")
}} })
.await
} }
pub async fn find_by_member(member_uuid: &MembershipId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_member(member_uuid: &MembershipId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
groups_users::table groups_users::table
.filter(groups_users::users_organizations_uuid.eq(member_uuid)) .filter(groups_users::users_organizations_uuid.eq(member_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading groups for user") .expect("Error loading groups for user")
}} })
.await
} }
pub async fn has_access_to_collection_by_member( pub async fn has_access_to_collection_by_member(
@ -558,24 +571,23 @@ impl GroupUser {
member_uuid: &MembershipId, member_uuid: &MembershipId,
conn: &DbConn, conn: &DbConn,
) -> bool { ) -> bool {
db_run! { conn: { conn.run(move |conn| {
groups_users::table groups_users::table
.inner_join(collections_groups::table.on( .inner_join(collections_groups::table.on(collections_groups::groups_uuid.eq(groups_users::groups_uuid)))
collections_groups::groups_uuid.eq(groups_users::groups_uuid) .inner_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
)) .inner_join(
.inner_join(groups::table.on( collections::table.on(collections::uuid
groups::uuid.eq(groups_users::groups_uuid) .eq(collections_groups::collections_uuid)
)) .and(collections::org_uuid.eq(groups::organizations_uuid))),
.inner_join(collections::table.on( )
collections::uuid.eq(collections_groups::collections_uuid)
.and(collections::org_uuid.eq(groups::organizations_uuid))
))
.filter(collections_groups::collections_uuid.eq(collection_uuid)) .filter(collections_groups::collections_uuid.eq(collection_uuid))
.filter(groups_users::users_organizations_uuid.eq(member_uuid)) .filter(groups_users::users_organizations_uuid.eq(member_uuid))
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} }
pub async fn has_full_access_by_member( pub async fn has_full_access_by_member(
@ -583,18 +595,18 @@ impl GroupUser {
member_uuid: &MembershipId, member_uuid: &MembershipId,
conn: &DbConn, conn: &DbConn,
) -> bool { ) -> bool {
db_run! { conn: { conn.run(move |conn| {
groups_users::table groups_users::table
.inner_join(groups::table.on( .inner_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
groups::uuid.eq(groups_users::groups_uuid)
))
.filter(groups::organizations_uuid.eq(org_uuid)) .filter(groups::organizations_uuid.eq(org_uuid))
.filter(groups::access_all.eq(true)) .filter(groups::access_all.eq(true))
.filter(groups_users::users_organizations_uuid.eq(member_uuid)) .filter(groups_users::users_organizations_uuid.eq(member_uuid))
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} }
pub async fn update_user_revision(&self, conn: &DbConn) { pub async fn update_user_revision(&self, conn: &DbConn) {
@ -614,13 +626,14 @@ impl GroupUser {
None => warn!("Member could not be found!"), None => warn!("Member could not be found!"),
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(groups_users::table) diesel::delete(groups_users::table)
.filter(groups_users::groups_uuid.eq(group_uuid)) .filter(groups_users::groups_uuid.eq(group_uuid))
.filter(groups_users::users_organizations_uuid.eq(member_uuid)) .filter(groups_users::users_organizations_uuid.eq(member_uuid))
.execute(conn) .execute(conn)
.map_res("Error deleting group users") .map_res("Error deleting group users")
}} })
.await
} }
pub async fn delete_all_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
@ -629,12 +642,13 @@ impl GroupUser {
group_user.update_user_revision(conn).await; group_user.update_user_revision(conn).await;
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(groups_users::table) diesel::delete(groups_users::table)
.filter(groups_users::groups_uuid.eq(group_uuid)) .filter(groups_users::groups_uuid.eq(group_uuid))
.execute(conn) .execute(conn)
.map_res("Error deleting group users") .map_res("Error deleting group users")
}} })
.await
} }
pub async fn delete_all_by_member(member_uuid: &MembershipId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_member(member_uuid: &MembershipId, conn: &DbConn) -> EmptyResult {
@ -643,12 +657,13 @@ impl GroupUser {
None => warn!("Member could not be found!"), None => warn!("Member could not be found!"),
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(groups_users::table) diesel::delete(groups_users::table)
.filter(groups_users::users_organizations_uuid.eq(member_uuid)) .filter(groups_users::users_organizations_uuid.eq(member_uuid))
.execute(conn) .execute(conn)
.map_res("Error deleting user groups") .map_res("Error deleting user groups")
}} })
.await
} }
} }

69
src/db/models/org_policy.rs

@ -151,37 +151,38 @@ impl OrgPolicy {
} }
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(org_policies::table.filter(org_policies::uuid.eq(self.uuid))) diesel::delete(org_policies::table.filter(org_policies::uuid.eq(self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting org_policy") .map_res("Error deleting org_policy")
}} })
.await
} }
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
org_policies::table org_policies::table
.filter(org_policies::org_uuid.eq(org_uuid)) .filter(org_policies::org_uuid.eq(org_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading org_policy") .expect("Error loading org_policy")
}} })
.await
} }
pub async fn find_confirmed_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_confirmed_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
org_policies::table org_policies::table
.inner_join( .inner_join(
users_organizations::table.on( users_organizations::table.on(users_organizations::org_uuid
users_organizations::org_uuid.eq(org_policies::org_uuid) .eq(org_policies::org_uuid)
.and(users_organizations::user_uuid.eq(user_uuid))) .and(users_organizations::user_uuid.eq(user_uuid))),
)
.filter(
users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.select(org_policies::all_columns) .select(org_policies::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading org_policy") .expect("Error loading org_policy")
}} })
.await
} }
pub async fn find_by_org_and_type( pub async fn find_by_org_and_type(
@ -189,21 +190,23 @@ impl OrgPolicy {
policy_type: OrgPolicyType, policy_type: OrgPolicyType,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
org_policies::table org_policies::table
.filter(org_policies::org_uuid.eq(org_uuid)) .filter(org_policies::org_uuid.eq(org_uuid))
.filter(org_policies::atype.eq(policy_type as i32)) .filter(org_policies::atype.eq(policy_type as i32))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid))) diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting org_policy") .map_res("Error deleting org_policy")
}} })
.await
} }
pub async fn find_accepted_and_confirmed_by_user_and_active_policy( pub async fn find_accepted_and_confirmed_by_user_and_active_policy(
@ -211,25 +214,22 @@ impl OrgPolicy {
policy_type: OrgPolicyType, policy_type: OrgPolicyType,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
org_policies::table org_policies::table
.inner_join( .inner_join(
users_organizations::table.on( users_organizations::table.on(users_organizations::org_uuid
users_organizations::org_uuid.eq(org_policies::org_uuid) .eq(org_policies::org_uuid)
.and(users_organizations::user_uuid.eq(user_uuid))) .and(users_organizations::user_uuid.eq(user_uuid))),
)
.filter(
users_organizations::status.eq(MembershipStatus::Accepted as i32)
)
.or_filter(
users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.filter(users_organizations::status.eq(MembershipStatus::Accepted as i32))
.or_filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.filter(org_policies::atype.eq(policy_type as i32)) .filter(org_policies::atype.eq(policy_type as i32))
.filter(org_policies::enabled.eq(true)) .filter(org_policies::enabled.eq(true))
.select(org_policies::all_columns) .select(org_policies::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading org_policy") .expect("Error loading org_policy")
}} })
.await
} }
pub async fn find_confirmed_by_user_and_active_policy( pub async fn find_confirmed_by_user_and_active_policy(
@ -237,22 +237,21 @@ impl OrgPolicy {
policy_type: OrgPolicyType, policy_type: OrgPolicyType,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
org_policies::table org_policies::table
.inner_join( .inner_join(
users_organizations::table.on( users_organizations::table.on(users_organizations::org_uuid
users_organizations::org_uuid.eq(org_policies::org_uuid) .eq(org_policies::org_uuid)
.and(users_organizations::user_uuid.eq(user_uuid))) .and(users_organizations::user_uuid.eq(user_uuid))),
)
.filter(
users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.filter(org_policies::atype.eq(policy_type as i32)) .filter(org_policies::atype.eq(policy_type as i32))
.filter(org_policies::enabled.eq(true)) .filter(org_policies::enabled.eq(true))
.select(org_policies::all_columns) .select(org_policies::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading org_policy") .expect("Error loading org_policy")
}} })
.await
} }
/// Returns true if the user belongs to an org that has enabled the specified policy type, /// Returns true if the user belongs to an org that has enabled the specified policy type,

287
src/db/models/organization.rs

@ -384,43 +384,30 @@ impl Organization {
Group::delete_all_by_organization(&self.uuid, conn).await?; Group::delete_all_by_organization(&self.uuid, conn).await?;
OrganizationApiKey::delete_all_by_organization(&self.uuid, conn).await?; OrganizationApiKey::delete_all_by_organization(&self.uuid, conn).await?;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid))) diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error saving organization") .map_res("Error saving organization")
}} })
.await
} }
pub async fn find_by_uuid(uuid: &OrganizationId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| organizations::table.filter(organizations::uuid.eq(uuid)).first::<Self>(conn).ok()).await
organizations::table
.filter(organizations::uuid.eq(uuid))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_name(name: &str, conn: &DbConn) -> Option<Self> { pub async fn find_by_name(name: &str, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| organizations::table.filter(organizations::name.eq(name)).first::<Self>(conn).ok()).await
organizations::table
.filter(organizations::name.eq(name))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn get_all(conn: &DbConn) -> Vec<Self> { pub async fn get_all(conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| organizations::table.load::<Self>(conn).expect("Error loading organizations")).await
organizations::table
.load::<Self>(conn)
.expect("Error loading organizations")
}}
} }
pub async fn find_main_org_user_email(user_email: &str, conn: &DbConn) -> Option<Self> { pub async fn find_main_org_user_email(user_email: &str, conn: &DbConn) -> Option<Self> {
let lower_mail = user_email.to_lowercase(); let lower_mail = user_email.to_lowercase();
db_run! { conn: { conn.run(move |conn| {
organizations::table organizations::table
.inner_join(users_organizations::table.on(users_organizations::org_uuid.eq(organizations::uuid))) .inner_join(users_organizations::table.on(users_organizations::org_uuid.eq(organizations::uuid)))
.inner_join(users::table.on(users::uuid.eq(users_organizations::user_uuid))) .inner_join(users::table.on(users::uuid.eq(users_organizations::user_uuid)))
@ -430,13 +417,14 @@ impl Organization {
.select(organizations::all_columns) .select(organizations::all_columns)
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_org_user_email(user_email: &str, conn: &DbConn) -> Vec<Self> { pub async fn find_org_user_email(user_email: &str, conn: &DbConn) -> Vec<Self> {
let lower_mail = user_email.to_lowercase(); let lower_mail = user_email.to_lowercase();
db_run! { conn: { conn.run(move |conn| {
organizations::table organizations::table
.inner_join(users_organizations::table.on(users_organizations::org_uuid.eq(organizations::uuid))) .inner_join(users_organizations::table.on(users_organizations::org_uuid.eq(organizations::uuid)))
.inner_join(users::table.on(users::uuid.eq(users_organizations::user_uuid))) .inner_join(users::table.on(users::uuid.eq(users_organizations::user_uuid)))
@ -446,7 +434,8 @@ impl Organization {
.select(organizations::all_columns) .select(organizations::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading user orgs") .expect("Error loading user orgs")
}} })
.await
} }
} }
@ -786,11 +775,12 @@ impl Membership {
CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_uuid, conn).await?; CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_uuid, conn).await?;
GroupUser::delete_all_by_member(&self.uuid, conn).await?; GroupUser::delete_all_by_member(&self.uuid, conn).await?;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(users_organizations::table.filter(users_organizations::uuid.eq(self.uuid))) diesel::delete(users_organizations::table.filter(users_organizations::uuid.eq(self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing user from organization") .map_res("Error removing user from organization")
}} })
.await
} }
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
@ -830,64 +820,67 @@ impl Membership {
} }
pub async fn find_by_uuid(uuid: &MembershipId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &MembershipId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table.filter(users_organizations::uuid.eq(uuid)).first::<Self>(conn).ok()
.filter(users_organizations::uuid.eq(uuid)) })
.first::<Self>(conn) .await
.ok()
}}
} }
pub async fn find_by_uuid_and_org(uuid: &MembershipId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_org(uuid: &MembershipId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::uuid.eq(uuid)) .filter(users_organizations::uuid.eq(uuid))
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_confirmed_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_confirmed_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.load::<Self>(conn) .load::<Self>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
pub async fn find_invited_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_invited_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(MembershipStatus::Invited as i32)) .filter(users_organizations::status.eq(MembershipStatus::Invited as i32))
.load::<Self>(conn) .load::<Self>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
// Should be used only when email are disabled. // Should be used only when email are disabled.
// In Organizations::send_invite status is set to Accepted only if the user has a password. // In Organizations::send_invite status is set to Accepted only if the user has a password.
pub async fn accept_user_invitations(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn accept_user_invitations(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::update(users_organizations::table) diesel::update(users_organizations::table)
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(MembershipStatus::Invited as i32)) .filter(users_organizations::status.eq(MembershipStatus::Invited as i32))
.set(users_organizations::status.eq(MembershipStatus::Accepted as i32)) .set(users_organizations::status.eq(MembershipStatus::Accepted as i32))
.execute(conn) .execute(conn)
.map_res("Error confirming invitations") .map_res("Error confirming invitations")
}} })
.await
} }
pub async fn find_any_state_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_any_state_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
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) .load::<Self>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
pub async fn count_accepted_and_confirmed_by_user( pub async fn count_accepted_and_confirmed_by_user(
@ -895,70 +888,83 @@ impl Membership {
excluded_org: &OrganizationId, excluded_org: &OrganizationId,
conn: &DbConn, conn: &DbConn,
) -> i64 { ) -> i64 {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::org_uuid.ne(excluded_org)) .filter(users_organizations::org_uuid.ne(excluded_org))
.filter(users_organizations::status.eq(MembershipStatus::Accepted as i32).or(users_organizations::status.eq(MembershipStatus::Confirmed as i32))) .filter(
users_organizations::status
.eq(MembershipStatus::Accepted as i32)
.or(users_organizations::status.eq(MembershipStatus::Confirmed as i32)),
)
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.unwrap_or(0) .unwrap_or(0)
}} })
.await
} }
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading user organizations") .expect("Error loading user organizations")
}} })
.await
} }
pub async fn find_confirmed_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_confirmed_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.load::<Self>(conn) .load::<Self>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
// Get all users which are either owner or admin, or a manager which can manage/access all // Get all users which are either owner or admin, or a manager which can manage/access all
pub async fn find_confirmed_and_manage_all_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_confirmed_and_manage_all_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.filter( .filter(
users_organizations::atype.eq_any(vec![MembershipType::Owner as i32, MembershipType::Admin as i32]) users_organizations::atype
.or(users_organizations::atype.eq(MembershipType::Manager as i32).and(users_organizations::access_all.eq(true))) .eq_any(vec![MembershipType::Owner as i32, MembershipType::Admin as i32])
.or(users_organizations::atype
.eq(MembershipType::Manager as i32)
.and(users_organizations::access_all.eq(true))),
) )
.load::<Self>(conn) .load::<Self>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 { pub async fn count_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> i64 {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok() .ok()
.unwrap_or(0) .unwrap_or(0)
}} })
.await
} }
pub async fn find_by_org_and_type(org_uuid: &OrganizationId, atype: MembershipType, conn: &DbConn) -> Vec<Self> { pub async fn find_by_org_and_type(org_uuid: &OrganizationId, atype: MembershipType, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::atype.eq(atype as i32)) .filter(users_organizations::atype.eq(atype as i32))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading user organizations") .expect("Error loading user organizations")
}} })
.await
} }
pub async fn count_confirmed_by_org_and_type( pub async fn count_confirmed_by_org_and_type(
@ -966,7 +972,7 @@ impl Membership {
atype: MembershipType, atype: MembershipType,
conn: &DbConn, conn: &DbConn,
) -> i64 { ) -> i64 {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::atype.eq(atype as i32)) .filter(users_organizations::atype.eq(atype as i32))
@ -974,17 +980,19 @@ impl Membership {
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.unwrap_or(0) .unwrap_or(0)
}} })
.await
} }
pub async fn find_by_user_and_org(user_uuid: &UserId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> { pub async fn find_by_user_and_org(user_uuid: &UserId, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_confirmed_by_user_and_org( pub async fn find_confirmed_by_user_and_org(
@ -992,78 +1000,76 @@ impl Membership {
org_uuid: &OrganizationId, org_uuid: &OrganizationId,
conn: &DbConn, conn: &DbConn,
) -> Option<Self> { ) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter( .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
users_organizations::status.eq(MembershipStatus::Confirmed as i32)
)
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
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) .load::<Self>(conn)
.expect("Error loading user organizations") .expect("Error loading user organizations")
}} })
.await
} }
pub async fn get_orgs_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<OrganizationId> { pub async fn get_orgs_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<OrganizationId> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.select(users_organizations::org_uuid) .select(users_organizations::org_uuid)
.load::<OrganizationId>(conn) .load::<OrganizationId>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
pub async fn find_by_user_and_policy(user_uuid: &UserId, policy_type: OrgPolicyType, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user_and_policy(user_uuid: &UserId, policy_type: OrgPolicyType, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.inner_join( .inner_join(
org_policies::table.on( org_policies::table.on(org_policies::org_uuid
org_policies::org_uuid.eq(users_organizations::org_uuid) .eq(users_organizations::org_uuid)
.and(users_organizations::user_uuid.eq(user_uuid)) .and(users_organizations::user_uuid.eq(user_uuid))
.and(org_policies::atype.eq(policy_type as i32)) .and(org_policies::atype.eq(policy_type as i32))
.and(org_policies::enabled.eq(true))) .and(org_policies::enabled.eq(true))),
)
.filter(
users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.unwrap_or_default() .unwrap_or_default()
}} })
.await
} }
pub async fn find_by_cipher_and_org(cipher_uuid: &CipherId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_cipher_and_org(cipher_uuid: &CipherId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.left_join(users_collections::table.on( .left_join(users_collections::table.on(users_collections::user_uuid.eq(users_organizations::user_uuid)))
users_collections::user_uuid.eq(users_organizations::user_uuid) .left_join(
)) ciphers_collections::table.on(ciphers_collections::collection_uuid
.left_join(ciphers_collections::table.on( .eq(users_collections::collection_uuid)
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid).and( .and(ciphers_collections::cipher_uuid.eq(&cipher_uuid))),
ciphers_collections::cipher_uuid.eq(&cipher_uuid)
) )
.filter(users_organizations::access_all.eq(true).or(
// AccessAll..
ciphers_collections::cipher_uuid.eq(&cipher_uuid), // ..or access to collection with cipher
)) ))
.filter(
users_organizations::access_all.eq(true).or( // AccessAll..
ciphers_collections::cipher_uuid.eq(&cipher_uuid) // ..or access to collection with cipher
)
)
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.distinct() .distinct()
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading user organizations") .expect("Error loading user organizations")
}} })
.await
} }
pub async fn find_by_cipher_and_org_with_group( pub async fn find_by_cipher_and_org_with_group(
@ -1071,45 +1077,54 @@ impl Membership {
org_uuid: &OrganizationId, org_uuid: &OrganizationId,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.inner_join(groups_users::table.on( .inner_join(
groups_users::users_organizations_uuid.eq(users_organizations::uuid) groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid)),
)) )
.left_join(collections_groups::table.on( .left_join(collections_groups::table.on(collections_groups::groups_uuid.eq(groups_users::groups_uuid)))
collections_groups::groups_uuid.eq(groups_users::groups_uuid) .left_join(
)) groups::table.on(groups::uuid
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid) .eq(groups_users::groups_uuid)
.and(groups::organizations_uuid.eq(users_organizations::org_uuid)) .and(groups::organizations_uuid.eq(users_organizations::org_uuid))),
))
.left_join(ciphers_collections::table.on(
ciphers_collections::collection_uuid.eq(collections_groups::collections_uuid).and(ciphers_collections::cipher_uuid.eq(&cipher_uuid))
))
.filter(
groups::access_all.eq(true).or( // AccessAll via groups
ciphers_collections::cipher_uuid.eq(&cipher_uuid) // ..or access to collection via group
) )
.left_join(
ciphers_collections::table.on(ciphers_collections::collection_uuid
.eq(collections_groups::collections_uuid)
.and(ciphers_collections::cipher_uuid.eq(&cipher_uuid))),
) )
.filter(groups::access_all.eq(true).or(
// AccessAll via groups
ciphers_collections::cipher_uuid.eq(&cipher_uuid), // ..or access to collection via group
))
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.distinct() .distinct()
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading user organizations with groups") .expect("Error loading user organizations with groups")
}} })
.await
} }
pub async fn user_has_ge_admin_access_to_cipher(user_uuid: &UserId, cipher_uuid: &CipherId, conn: &DbConn) -> bool { pub async fn user_has_ge_admin_access_to_cipher(user_uuid: &UserId, cipher_uuid: &CipherId, conn: &DbConn) -> bool {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.inner_join(ciphers::table.on(ciphers::uuid.eq(cipher_uuid).and(ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())))) .inner_join(
ciphers::table.on(ciphers::uuid
.eq(cipher_uuid)
.and(ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()))),
)
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::atype.eq_any(vec![MembershipType::Owner as i32, MembershipType::Admin as i32])) .filter(
users_organizations::atype.eq_any(vec![MembershipType::Owner as i32, MembershipType::Admin as i32]),
)
.count() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok() .ok()
.unwrap_or(0) != 0 .unwrap_or(0)
}} != 0
})
.await
} }
pub async fn find_by_collection_and_org( pub async fn find_by_collection_and_org(
@ -1117,44 +1132,41 @@ impl Membership {
org_uuid: &OrganizationId, org_uuid: &OrganizationId,
conn: &DbConn, conn: &DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.left_join(users_collections::table.on( .left_join(users_collections::table.on(users_collections::user_uuid.eq(users_organizations::user_uuid)))
users_collections::user_uuid.eq(users_organizations::user_uuid) .filter(users_organizations::access_all.eq(true).or(
// AccessAll..
users_collections::collection_uuid.eq(&collection_uuid), // ..or access to collection with cipher
)) ))
.filter(
users_organizations::access_all.eq(true).or( // AccessAll..
users_collections::collection_uuid.eq(&collection_uuid) // ..or access to collection with cipher
)
)
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading user organizations") .expect("Error loading user organizations")
}} })
.await
} }
pub async fn find_by_external_id_and_org(ext_id: &str, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> { pub async fn find_by_external_id_and_org(ext_id: &str, org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter( .filter(users_organizations::external_id.eq(ext_id).and(users_organizations::org_uuid.eq(org_uuid)))
users_organizations::external_id.eq(ext_id)
.and(users_organizations::org_uuid.eq(org_uuid))
)
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_main_user_org(user_uuid: &str, conn: &DbConn) -> Option<Self> { pub async fn find_main_user_org(user_uuid: &str, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.ne(MembershipStatus::Revoked as i32)) .filter(users_organizations::status.ne(MembershipStatus::Revoked as i32))
.order(users_organizations::atype.asc()) .order(users_organizations::atype.asc())
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
} }
@ -1192,20 +1204,19 @@ impl OrganizationApiKey {
} }
pub async fn find_by_org_uuid(org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> { pub async fn find_by_org_uuid(org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
organization_api_key::table organization_api_key::table.filter(organization_api_key::org_uuid.eq(org_uuid)).first::<Self>(conn).ok()
.filter(organization_api_key::org_uuid.eq(org_uuid)) })
.first::<Self>(conn) .await
.ok()
}}
} }
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(organization_api_key::table.filter(organization_api_key::org_uuid.eq(org_uuid))) diesel::delete(organization_api_key::table.filter(organization_api_key::org_uuid.eq(org_uuid)))
.execute(conn) .execute(conn)
.map_res("Error removing organization api key from organization") .map_res("Error removing organization api key from organization")
}} })
.await
} }
} }

57
src/db/models/send.rs

@ -236,11 +236,10 @@ impl Send {
operator.delete_with(&self.uuid).recursive(true).await.ok(); operator.delete_with(&self.uuid).recursive(true).await.ok();
} }
db_run! { conn: { conn.run(move |conn| {
diesel::delete(sends::table.filter(sends::uuid.eq(&self.uuid))) diesel::delete(sends::table.filter(sends::uuid.eq(&self.uuid))).execute(conn).map_res("Error deleting send")
.execute(conn) })
.map_res("Error deleting send") .await
}}
} }
/// Purge all sends that are past their deletion date. /// Purge all sends that are past their deletion date.
@ -282,31 +281,21 @@ impl Send {
} }
pub async fn find_by_uuid(uuid: &SendId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &SendId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| sends::table.filter(sends::uuid.eq(uuid)).first::<Self>(conn).ok()).await
sends::table
.filter(sends::uuid.eq(uuid))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_uuid_and_user(uuid: &SendId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid_and_user(uuid: &SendId, user_uuid: &UserId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
sends::table sends::table.filter(sends::uuid.eq(uuid)).filter(sends::user_uuid.eq(user_uuid)).first::<Self>(conn).ok()
.filter(sends::uuid.eq(uuid)) })
.filter(sends::user_uuid.eq(user_uuid)) .await
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
sends::table sends::table.filter(sends::user_uuid.eq(user_uuid)).load::<Self>(conn).expect("Error loading sends")
.filter(sends::user_uuid.eq(user_uuid)) })
.load::<Self>(conn) .await
.expect("Error loading sends")
}}
} }
pub async fn size_by_user(user_uuid: &UserId, conn: &DbConn) -> Option<i64> { pub async fn size_by_user(user_uuid: &UserId, conn: &DbConn) -> Option<i64> {
@ -331,22 +320,18 @@ impl Send {
} }
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
sends::table sends::table.filter(sends::organization_uuid.eq(org_uuid)).load::<Self>(conn).expect("Error loading sends")
.filter(sends::organization_uuid.eq(org_uuid)) })
.load::<Self>(conn) .await
.expect("Error loading sends")
}}
} }
pub async fn find_by_past_deletion_date(conn: &DbConn) -> Vec<Self> { pub async fn find_by_past_deletion_date(conn: &DbConn) -> Vec<Self> {
let now = Utc::now().naive_utc(); let now = Utc::now().naive_utc();
db_run! { conn: { conn.run(move |conn| {
sends::table sends::table.filter(sends::deletion_date.lt(now)).load::<Self>(conn).expect("Error loading sends")
.filter(sends::deletion_date.lt(now)) })
.load::<Self>(conn) .await
.expect("Error loading sends")
}}
} }
} }

15
src/db/models/sso_auth.rs

@ -109,13 +109,14 @@ impl SsoAuth {
pub async fn find(state: &OIDCState, conn: &DbConn) -> Option<Self> { pub async fn find(state: &OIDCState, conn: &DbConn) -> Option<Self> {
let oldest = Utc::now().naive_utc() - *SSO_AUTH_EXPIRATION; let oldest = Utc::now().naive_utc() - *SSO_AUTH_EXPIRATION;
db_run! { conn: { conn.run(move |conn| {
sso_auth::table sso_auth::table
.filter(sso_auth::state.eq(state)) .filter(sso_auth::state.eq(state))
.filter(sso_auth::created_at.ge(oldest)) .filter(sso_auth::created_at.ge(oldest))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_code(code: &OIDCCode, conn: &DbConn) -> Option<Self> { pub async fn find_by_code(code: &OIDCCode, conn: &DbConn) -> Option<Self> {
@ -130,22 +131,24 @@ impl SsoAuth {
} }
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
db_run! {conn: { conn.run(move |conn| {
diesel::delete(sso_auth::table.filter(sso_auth::state.eq(self.state))) diesel::delete(sso_auth::table.filter(sso_auth::state.eq(self.state)))
.execute(conn) .execute(conn)
.map_res("Error deleting sso_auth") .map_res("Error deleting sso_auth")
}} })
.await
} }
pub async fn delete_expired(pool: DbPool) -> EmptyResult { pub async fn delete_expired(pool: DbPool) -> EmptyResult {
debug!("Purging expired sso_auth"); debug!("Purging expired sso_auth");
if let Ok(conn) = pool.get().await { if let Ok(conn) = pool.get().await {
let oldest = Utc::now().naive_utc() - *SSO_AUTH_EXPIRATION; let oldest = Utc::now().naive_utc() - *SSO_AUTH_EXPIRATION;
db_run! { conn: { conn.run(move |conn| {
diesel::delete(sso_auth::table.filter(sso_auth::created_at.lt(oldest))) diesel::delete(sso_auth::table.filter(sso_auth::created_at.lt(oldest)))
.execute(conn) .execute(conn)
.map_res("Error deleting expired SSO nonce") .map_res("Error deleting expired SSO nonce")
}} })
.await
} else { } else {
err!("Failed to get DB connection while purging expired sso_auth") err!("Failed to get DB connection while purging expired sso_auth")
} }

32
src/db/models/two_factor.rs

@ -118,39 +118,43 @@ impl TwoFactor {
} }
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(twofactor::table.filter(twofactor::uuid.eq(self.uuid))) diesel::delete(twofactor::table.filter(twofactor::uuid.eq(self.uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting twofactor") .map_res("Error deleting twofactor")
}} })
.await
} }
pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> { pub async fn find_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
twofactor::table twofactor::table
.filter(twofactor::user_uuid.eq(user_uuid)) .filter(twofactor::user_uuid.eq(user_uuid))
.filter(twofactor::atype.lt(1000)) // Filter implementation types .filter(twofactor::atype.lt(1000)) // Filter implementation types
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading twofactor") .expect("Error loading twofactor")
}} })
.await
} }
pub async fn find_by_user_and_type(user_uuid: &UserId, atype: i32, conn: &DbConn) -> Option<Self> { pub async fn find_by_user_and_type(user_uuid: &UserId, atype: i32, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
twofactor::table twofactor::table
.filter(twofactor::user_uuid.eq(user_uuid)) .filter(twofactor::user_uuid.eq(user_uuid))
.filter(twofactor::atype.eq(atype)) .filter(twofactor::atype.eq(atype))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid))) diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting twofactors") .map_res("Error deleting twofactors")
}} })
.await
} }
pub async fn migrate_u2f_to_webauthn(conn: &DbConn) -> EmptyResult { pub async fn migrate_u2f_to_webauthn(conn: &DbConn) -> EmptyResult {
@ -158,12 +162,14 @@ impl TwoFactor {
use webauthn_rs::prelude::{COSEEC2Key, COSEKey, COSEKeyType, ECDSACurve}; use webauthn_rs::prelude::{COSEEC2Key, COSEKey, COSEKeyType, ECDSACurve};
use webauthn_rs_proto::{COSEAlgorithm, UserVerificationPolicy}; use webauthn_rs_proto::{COSEAlgorithm, UserVerificationPolicy};
let u2f_factors = db_run! { conn: { let u2f_factors = conn
.run(move |conn| {
twofactor::table twofactor::table
.filter(twofactor::atype.eq(TwoFactorType::U2f as i32)) .filter(twofactor::atype.eq(TwoFactorType::U2f as i32))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading twofactor") .expect("Error loading twofactor")
}}; })
.await;
for mut u2f in u2f_factors { for mut u2f in u2f_factors {
let mut regs: Vec<U2FRegistration> = serde_json::from_str(&u2f.data)?; let mut regs: Vec<U2FRegistration> = serde_json::from_str(&u2f.data)?;
@ -230,12 +236,14 @@ impl TwoFactor {
} }
pub async fn migrate_credential_to_passkey(conn: &DbConn) -> EmptyResult { pub async fn migrate_credential_to_passkey(conn: &DbConn) -> EmptyResult {
let webauthn_factors = db_run! { conn: { let webauthn_factors = conn
.run(move |conn| {
twofactor::table twofactor::table
.filter(twofactor::atype.eq(TwoFactorType::Webauthn as i32)) .filter(twofactor::atype.eq(TwoFactorType::Webauthn as i32))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading twofactor") .expect("Error loading twofactor")
}}; })
.await;
for webauthn_factor in webauthn_factors { for webauthn_factor in webauthn_factors {
// assume that a failure to parse into the old struct, means that it was already converted // assume that a failure to parse into the old struct, means that it was already converted

31
src/db/models/two_factor_duo_context.rs

@ -19,12 +19,10 @@ pub struct TwoFactorDuoContext {
impl TwoFactorDuoContext { impl TwoFactorDuoContext {
pub async fn find_by_state(state: &str, conn: &DbConn) -> Option<Self> { pub async fn find_by_state(state: &str, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
twofactor_duo_ctx::table twofactor_duo_ctx::table.filter(twofactor_duo_ctx::state.eq(state)).first::<Self>(conn).ok()
.filter(twofactor_duo_ctx::state.eq(state)) })
.first::<Self>(conn) .await
.ok()
}}
} }
pub async fn save(state: &str, user_email: &str, nonce: &str, ttl: i64, conn: &DbConn) -> EmptyResult { pub async fn save(state: &str, user_email: &str, nonce: &str, ttl: i64, conn: &DbConn) -> EmptyResult {
@ -36,37 +34,38 @@ impl TwoFactorDuoContext {
let exp = Utc::now().timestamp() + ttl; let exp = Utc::now().timestamp() + ttl;
db_run! { conn: { conn.run(move |conn| {
diesel::insert_into(twofactor_duo_ctx::table) diesel::insert_into(twofactor_duo_ctx::table)
.values(( .values((
twofactor_duo_ctx::state.eq(state), twofactor_duo_ctx::state.eq(state),
twofactor_duo_ctx::user_email.eq(user_email), twofactor_duo_ctx::user_email.eq(user_email),
twofactor_duo_ctx::nonce.eq(nonce), twofactor_duo_ctx::nonce.eq(nonce),
twofactor_duo_ctx::exp.eq(exp) twofactor_duo_ctx::exp.eq(exp),
)) ))
.execute(conn) .execute(conn)
.map_res("Error saving context to twofactor_duo_ctx") .map_res("Error saving context to twofactor_duo_ctx")
}} })
.await
} }
pub async fn find_expired(conn: &DbConn) -> Vec<Self> { pub async fn find_expired(conn: &DbConn) -> Vec<Self> {
let now = Utc::now().timestamp(); let now = Utc::now().timestamp();
db_run! { conn: { conn.run(move |conn| {
twofactor_duo_ctx::table twofactor_duo_ctx::table
.filter(twofactor_duo_ctx::exp.lt(now)) .filter(twofactor_duo_ctx::exp.lt(now))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error finding expired contexts in twofactor_duo_ctx") .expect("Error finding expired contexts in twofactor_duo_ctx")
}} })
.await
} }
pub async fn delete(&self, conn: &DbConn) -> EmptyResult { pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete( diesel::delete(twofactor_duo_ctx::table.filter(twofactor_duo_ctx::state.eq(&self.state)))
twofactor_duo_ctx::table
.filter(twofactor_duo_ctx::state.eq(&self.state)))
.execute(conn) .execute(conn)
.map_res("Error deleting from twofactor_duo_ctx") .map_res("Error deleting from twofactor_duo_ctx")
}} })
.await
} }
pub async fn purge_expired_duo_contexts(conn: &DbConn) { pub async fn purge_expired_duo_contexts(conn: &DbConn) {

31
src/db/models/two_factor_incomplete.rs

@ -49,7 +49,7 @@ impl TwoFactorIncomplete {
return Ok(()); return Ok(());
} }
db_run! { conn: { conn.run(move |conn| {
diesel::insert_into(twofactor_incomplete::table) diesel::insert_into(twofactor_incomplete::table)
.values(( .values((
twofactor_incomplete::user_uuid.eq(user_uuid), twofactor_incomplete::user_uuid.eq(user_uuid),
@ -61,7 +61,8 @@ impl TwoFactorIncomplete {
)) ))
.execute(conn) .execute(conn)
.map_res("Error adding twofactor_incomplete record") .map_res("Error adding twofactor_incomplete record")
}} })
.await
} }
pub async fn mark_complete(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> EmptyResult { pub async fn mark_complete(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> EmptyResult {
@ -73,22 +74,24 @@ impl TwoFactorIncomplete {
} }
pub async fn find_by_user_and_device(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> Option<Self> { pub async fn find_by_user_and_device(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| {
twofactor_incomplete::table twofactor_incomplete::table
.filter(twofactor_incomplete::user_uuid.eq(user_uuid)) .filter(twofactor_incomplete::user_uuid.eq(user_uuid))
.filter(twofactor_incomplete::device_uuid.eq(device_uuid)) .filter(twofactor_incomplete::device_uuid.eq(device_uuid))
.first::<Self>(conn) .first::<Self>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_logins_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> { pub async fn find_logins_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
db_run! { conn: { conn.run(move |conn| {
twofactor_incomplete::table twofactor_incomplete::table
.filter(twofactor_incomplete::login_time.lt(dt)) .filter(twofactor_incomplete::login_time.lt(dt))
.load::<Self>(conn) .load::<Self>(conn)
.expect("Error loading twofactor_incomplete") .expect("Error loading twofactor_incomplete")
}} })
.await
} }
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
@ -96,20 +99,24 @@ impl TwoFactorIncomplete {
} }
pub async fn delete_by_user_and_device(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> EmptyResult { pub async fn delete_by_user_and_device(user_uuid: &UserId, device_uuid: &DeviceId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(twofactor_incomplete::table diesel::delete(
twofactor_incomplete::table
.filter(twofactor_incomplete::user_uuid.eq(user_uuid)) .filter(twofactor_incomplete::user_uuid.eq(user_uuid))
.filter(twofactor_incomplete::device_uuid.eq(device_uuid))) .filter(twofactor_incomplete::device_uuid.eq(device_uuid)),
)
.execute(conn) .execute(conn)
.map_res("Error in twofactor_incomplete::delete_by_user_and_device()") .map_res("Error in twofactor_incomplete::delete_by_user_and_device()")
}} })
.await
} }
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(twofactor_incomplete::table.filter(twofactor_incomplete::user_uuid.eq(user_uuid))) diesel::delete(twofactor_incomplete::table.filter(twofactor_incomplete::user_uuid.eq(user_uuid)))
.execute(conn) .execute(conn)
.map_res("Error in twofactor_incomplete::delete_all_by_user()") .map_res("Error in twofactor_incomplete::delete_all_by_user()")
}} })
.await
} }
} }

86
src/db/models/user.rs

@ -342,11 +342,10 @@ impl User {
TwoFactorIncomplete::delete_all_by_user(&self.uuid, conn).await?; TwoFactorIncomplete::delete_all_by_user(&self.uuid, conn).await?;
Invitation::take(&self.email, conn).await; // Delete invitation if any Invitation::take(&self.email, conn).await; // Delete invitation if any
db_run! { conn: { conn.run(move |conn| {
diesel::delete(users::table.filter(users::uuid.eq(self.uuid))) diesel::delete(users::table.filter(users::uuid.eq(self.uuid))).execute(conn).map_res("Error deleting user")
.execute(conn) })
.map_res("Error deleting user") .await
}}
} }
pub async fn update_uuid_revision(uuid: &UserId, conn: &DbConn) { pub async fn update_uuid_revision(uuid: &UserId, conn: &DbConn) {
@ -358,14 +357,11 @@ impl User {
pub async fn update_all_revisions(conn: &DbConn) -> EmptyResult { pub async fn update_all_revisions(conn: &DbConn) -> EmptyResult {
let updated_at = Utc::now().naive_utc(); let updated_at = Utc::now().naive_utc();
db_run! { conn: { conn.run(move |conn| {
retry(|| { retry(|| diesel::update(users::table).set(users::updated_at.eq(updated_at)).execute(conn), 10)
diesel::update(users::table)
.set(users::updated_at.eq(updated_at))
.execute(conn)
}, 10)
.map_res("Error updating revision date for all users") .map_res("Error updating revision date for all users")
}} })
.await
} }
pub async fn update_revision(&mut self, conn: &DbConn) -> EmptyResult { pub async fn update_revision(&mut self, conn: &DbConn) -> EmptyResult {
@ -375,51 +371,48 @@ impl User {
} }
async fn update_revision_impl(uuid: &UserId, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { async fn update_revision_impl(uuid: &UserId, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
retry(|| { retry(
|| {
diesel::update(users::table.filter(users::uuid.eq(uuid))) diesel::update(users::table.filter(users::uuid.eq(uuid)))
.set(users::updated_at.eq(date)) .set(users::updated_at.eq(date))
.execute(conn) .execute(conn)
}, 10) },
10,
)
.map_res("Error updating user revision") .map_res("Error updating user revision")
}} })
.await
} }
pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> { pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
let lower_mail = mail.to_lowercase(); let lower_mail = mail.to_lowercase();
db_run! { conn: { conn.run(move |conn| users::table.filter(users::email.eq(lower_mail)).first::<Self>(conn).ok()).await
users::table
.filter(users::email.eq(lower_mail))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_uuid(uuid: &UserId, conn: &DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &UserId, conn: &DbConn) -> Option<Self> {
db_run! { conn: { conn.run(move |conn| users::table.filter(users::uuid.eq(uuid)).first::<Self>(conn).ok()).await
users::table
.filter(users::uuid.eq(uuid))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn find_by_device_for_email2fa(device_uuid: &DeviceId, conn: &DbConn) -> Option<Self> { pub async fn find_by_device_for_email2fa(device_uuid: &DeviceId, conn: &DbConn) -> Option<Self> {
if let Some(user_uuid) = db_run! ( conn: { if let Some(user_uuid) = conn
.run(move |conn| {
twofactor_incomplete::table twofactor_incomplete::table
.filter(twofactor_incomplete::device_uuid.eq(device_uuid)) .filter(twofactor_incomplete::device_uuid.eq(device_uuid))
.order_by(twofactor_incomplete::login_time.desc()) .order_by(twofactor_incomplete::login_time.desc())
.select(twofactor_incomplete::user_uuid) .select(twofactor_incomplete::user_uuid)
.first::<UserId>(conn) .first::<UserId>(conn)
.ok() .ok()
}) { })
.await
{
return Self::find_by_uuid(&user_uuid, conn).await; return Self::find_by_uuid(&user_uuid, conn).await;
} }
None None
} }
pub async fn get_all(conn: &DbConn) -> Vec<(Self, Option<SsoUser>)> { pub async fn get_all(conn: &DbConn) -> Vec<(Self, Option<SsoUser>)> {
db_run! { conn: { conn.run(move |conn| {
users::table users::table
.left_join(sso_users::table) .left_join(sso_users::table)
.select(<(Self, Option<SsoUser>)>::as_select()) .select(<(Self, Option<SsoUser>)>::as_select())
@ -427,7 +420,8 @@ impl User {
.expect("Error loading groups for user") .expect("Error loading groups for user")
.into_iter() .into_iter()
.collect() .collect()
}} })
.await
} }
pub async fn last_active(&self, conn: &DbConn) -> Option<NaiveDateTime> { pub async fn last_active(&self, conn: &DbConn) -> Option<NaiveDateTime> {
@ -472,21 +466,18 @@ impl Invitation {
} }
pub async fn delete(self, conn: &DbConn) -> EmptyResult { pub async fn delete(self, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(invitations::table.filter(invitations::email.eq(self.email))) diesel::delete(invitations::table.filter(invitations::email.eq(self.email)))
.execute(conn) .execute(conn)
.map_res("Error deleting invitation") .map_res("Error deleting invitation")
}} })
.await
} }
pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> { pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
let lower_mail = mail.to_lowercase(); let lower_mail = mail.to_lowercase();
db_run! { conn: { conn.run(move |conn| invitations::table.filter(invitations::email.eq(lower_mail)).first::<Self>(conn).ok())
invitations::table .await
.filter(invitations::email.eq(lower_mail))
.first::<Self>(conn)
.ok()
}}
} }
pub async fn take(mail: &str, conn: &DbConn) -> bool { pub async fn take(mail: &str, conn: &DbConn) -> bool {
@ -536,34 +527,37 @@ impl SsoUser {
} }
pub async fn find_by_identifier(identifier: &str, conn: &DbConn) -> Option<(User, Self)> { pub async fn find_by_identifier(identifier: &str, conn: &DbConn) -> Option<(User, Self)> {
db_run! { conn: { conn.run(move |conn| {
users::table users::table
.inner_join(sso_users::table) .inner_join(sso_users::table)
.select(<(User, Self)>::as_select()) .select(<(User, Self)>::as_select())
.filter(sso_users::identifier.eq(identifier)) .filter(sso_users::identifier.eq(identifier))
.first::<(User, Self)>(conn) .first::<(User, Self)>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<(User, Option<Self>)> { pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<(User, Option<Self>)> {
let lower_mail = mail.to_lowercase(); let lower_mail = mail.to_lowercase();
db_run! { conn: { conn.run(move |conn| {
users::table users::table
.left_join(sso_users::table) .left_join(sso_users::table)
.select(<(User, Option<Self>)>::as_select()) .select(<(User, Option<Self>)>::as_select())
.filter(users::email.eq(lower_mail)) .filter(users::email.eq(lower_mail))
.first::<(User, Option<Self>)>(conn) .first::<(User, Option<Self>)>(conn)
.ok() .ok()
}} })
.await
} }
pub async fn delete(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { pub async fn delete(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
db_run! { conn: { conn.run(move |conn| {
diesel::delete(sso_users::table.filter(sso_users::user_uuid.eq(user_uuid))) diesel::delete(sso_users::table.filter(sso_users::user_uuid.eq(user_uuid)))
.execute(conn) .execute(conn)
.map_res("Error deleting sso user") .map_res("Error deleting sso user")
}} })
.await
} }
} }

Loading…
Cancel
Save