diff options
Diffstat (limited to 'src/db/models/group.rs')
-rw-r--r-- | src/db/models/group.rs | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/src/db/models/group.rs b/src/db/models/group.rs new file mode 100644 index 00000000..eea4bcd2 --- /dev/null +++ b/src/db/models/group.rs @@ -0,0 +1,501 @@ +use chrono::{NaiveDateTime, Utc}; +use serde_json::Value; + +db_object! { + #[derive(Identifiable, Queryable, Insertable, AsChangeset)] + #[table_name = "groups"] + #[primary_key(uuid)] + pub struct Group { + pub uuid: String, + pub organizations_uuid: String, + pub name: String, + pub access_all: bool, + external_id: Option<String>, + pub creation_date: NaiveDateTime, + pub revision_date: NaiveDateTime, + } + + #[derive(Identifiable, Queryable, Insertable)] + #[table_name = "collections_groups"] + #[primary_key(collections_uuid, groups_uuid)] + pub struct CollectionGroup { + pub collections_uuid: String, + pub groups_uuid: String, + pub read_only: bool, + pub hide_passwords: bool, + } + + #[derive(Identifiable, Queryable, Insertable)] + #[table_name = "groups_users"] + #[primary_key(groups_uuid, users_organizations_uuid)] + pub struct GroupUser { + pub groups_uuid: String, + pub users_organizations_uuid: String + } +} + +/// Local methods +impl Group { + pub fn new(organizations_uuid: String, name: String, access_all: bool, external_id: Option<String>) -> Self { + let now = Utc::now().naive_utc(); + + let mut new_model = Self { + uuid: crate::util::get_uuid(), + organizations_uuid, + name, + access_all, + external_id: None, + creation_date: now, + revision_date: now, + }; + + new_model.set_external_id(external_id); + + new_model + } + + pub fn to_json(&self) -> Value { + use crate::util::format_date; + + json!({ + "Id": self.uuid, + "OrganizationId": self.organizations_uuid, + "Name": self.name, + "AccessAll": self.access_all, + "ExternalId": self.external_id, + "CreationDate": format_date(&self.creation_date), + "RevisionDate": format_date(&self.revision_date) + }) + } + + pub fn set_external_id(&mut self, external_id: Option<String>) { + //Check if external id is empty. We don't want to have + //empty strings in the database + match external_id { + Some(external_id) => { + if external_id.is_empty() { + self.external_id = None; + } else { + self.external_id = Some(external_id) + } + } + None => self.external_id = None, + } + } + + pub fn get_external_id(&self) -> Option<String> { + self.external_id.clone() + } +} + +impl CollectionGroup { + pub fn new(collections_uuid: String, groups_uuid: String, read_only: bool, hide_passwords: bool) -> Self { + Self { + collections_uuid, + groups_uuid, + read_only, + hide_passwords, + } + } +} + +impl GroupUser { + pub fn new(groups_uuid: String, users_organizations_uuid: String) -> Self { + Self { + groups_uuid, + users_organizations_uuid, + } + } +} + +use crate::db::DbConn; + +use crate::api::EmptyResult; +use crate::error::MapResult; + +use super::{User, UserOrganization}; + +/// Database methods +impl Group { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + self.revision_date = Utc::now().naive_utc(); + + db_run! { conn: + sqlite, mysql { + match diesel::replace_into(groups::table) + .values(GroupDb::to_db(self)) + .execute(conn) + { + Ok(_) => Ok(()), + // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. + Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { + diesel::update(groups::table) + .filter(groups::uuid.eq(&self.uuid)) + .set(GroupDb::to_db(self)) + .execute(conn) + .map_res("Error saving group") + } + Err(e) => Err(e.into()), + }.map_res("Error saving group") + } + postgresql { + let value = GroupDb::to_db(self); + diesel::insert_into(groups::table) + .values(&value) + .on_conflict(groups::uuid) + .do_update() + .set(&value) + .execute(conn) + .map_res("Error saving group") + } + } + } + + pub async fn find_by_organization(organizations_uuid: &str, conn: &DbConn) -> Vec<Self> { + db_run! { conn: { + groups::table + .filter(groups::organizations_uuid.eq(organizations_uuid)) + .load::<GroupDb>(conn) + .expect("Error loading groups") + .from_db() + }} + } + + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { + db_run! { conn: { + groups::table + .filter(groups::uuid.eq(uuid)) + .first::<GroupDb>(conn) + .ok() + .from_db() + }} + } + + //Returns all organizations the user has full access to + pub async fn gather_user_organizations_full_access(user_uuid: &str, conn: &DbConn) -> Vec<String> { + db_run! { conn: { + groups_users::table + .inner_join(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) + )) + .filter(users_organizations::user_uuid.eq(user_uuid)) + .filter(groups::access_all.eq(true)) + .select(groups::organizations_uuid) + .distinct() + .load::<String>(conn) + .expect("Error loading organization group full access information for user") + }} + } + + pub async fn is_in_full_access_group(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> bool { + db_run! { conn: { + groups::table + .inner_join(groups_users::table.on( + groups_users::groups_uuid.eq(groups::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(groups::organizations_uuid.eq(org_uuid)) + .filter(groups::access_all.eq(true)) + .select(groups::access_all) + .first::<bool>(conn) + .unwrap_or_default() + }} + } + + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { + CollectionGroup::delete_all_by_group(&self.uuid, conn).await?; + GroupUser::delete_all_by_group(&self.uuid, conn).await?; + + db_run! { conn: { + diesel::delete(groups::table.filter(groups::uuid.eq(&self.uuid))) + .execute(conn) + .map_res("Error deleting group") + }} + } + + pub async fn update_revision(uuid: &str, conn: &DbConn) { + if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await { + warn!("Failed to update revision for {}: {:#?}", uuid, e); + } + } + + async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { + db_run! {conn: { + crate::util::retry(|| { + diesel::update(groups::table.filter(groups::uuid.eq(uuid))) + .set(groups::revision_date.eq(date)) + .execute(conn) + }, 10) + .map_res("Error updating group revision") + }} + } +} + +impl CollectionGroup { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + let group_users = GroupUser::find_by_group(&self.groups_uuid, conn).await; + for group_user in group_users { + group_user.update_user_revision(conn).await; + } + + db_run! { conn: + sqlite, mysql { + match diesel::replace_into(collections_groups::table) + .values(( + collections_groups::collections_uuid.eq(&self.collections_uuid), + collections_groups::groups_uuid.eq(&self.groups_uuid), + collections_groups::read_only.eq(&self.read_only), + collections_groups::hide_passwords.eq(&self.hide_passwords), + )) + .execute(conn) + { + Ok(_) => Ok(()), + // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. + Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { + diesel::update(collections_groups::table) + .filter(collections_groups::collections_uuid.eq(&self.collections_uuid)) + .filter(collections_groups::groups_uuid.eq(&self.groups_uuid)) + .set(( + collections_groups::collections_uuid.eq(&self.collections_uuid), + collections_groups::groups_uuid.eq(&self.groups_uuid), + collections_groups::read_only.eq(&self.read_only), + collections_groups::hide_passwords.eq(&self.hide_passwords), + )) + .execute(conn) + .map_res("Error adding group to collection") + } + Err(e) => Err(e.into()), + }.map_res("Error adding group to collection") + } + postgresql { + diesel::insert_into(collections_groups::table) + .values(( + collections_groups::collections_uuid.eq(&self.collections_uuid), + collections_groups::groups_uuid.eq(&self.groups_uuid), + collections_groups::read_only.eq(self.read_only), + collections_groups::hide_passwords.eq(self.hide_passwords), + )) + .on_conflict((collections_groups::collections_uuid, collections_groups::groups_uuid)) + .do_update() + .set(( + collections_groups::read_only.eq(self.read_only), + collections_groups::hide_passwords.eq(self.hide_passwords), + )) + .execute(conn) + .map_res("Error adding group to collection") + } + } + } + + pub async fn find_by_group(group_uuid: &str, conn: &DbConn) -> Vec<Self> { + db_run! { conn: { + collections_groups::table + .filter(collections_groups::groups_uuid.eq(group_uuid)) + .load::<CollectionGroupDb>(conn) + .expect("Error loading collection groups") + .from_db() + }} + } + + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { + db_run! { conn: { + collections_groups::table + .inner_join(groups_users::table.on( + groups_users::groups_uuid.eq(collections_groups::groups_uuid) + )) + .inner_join(users_organizations::table.on( + users_organizations::uuid.eq(groups_users::users_organizations_uuid) + )) + .filter(users_organizations::user_uuid.eq(user_uuid)) + .select(collections_groups::all_columns) + .load::<CollectionGroupDb>(conn) + .expect("Error loading user collection groups") + .from_db() + }} + } + + pub async fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec<Self> { + db_run! { conn: { + collections_groups::table + .filter(collections_groups::collections_uuid.eq(collection_uuid)) + .select(collections_groups::all_columns) + .load::<CollectionGroupDb>(conn) + .expect("Error loading collection groups") + .from_db() + }} + } + + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { + let group_users = GroupUser::find_by_group(&self.groups_uuid, conn).await; + for group_user in group_users { + group_user.update_user_revision(conn).await; + } + + db_run! { conn: { + diesel::delete(collections_groups::table) + .filter(collections_groups::collections_uuid.eq(&self.collections_uuid)) + .filter(collections_groups::groups_uuid.eq(&self.groups_uuid)) + .execute(conn) + .map_res("Error deleting collection group") + }} + } + + pub async fn delete_all_by_group(group_uuid: &str, conn: &DbConn) -> EmptyResult { + let group_users = GroupUser::find_by_group(group_uuid, conn).await; + for group_user in group_users { + group_user.update_user_revision(conn).await; + } + + db_run! { conn: { + diesel::delete(collections_groups::table) + .filter(collections_groups::groups_uuid.eq(group_uuid)) + .execute(conn) + .map_res("Error deleting collection group") + }} + } + + pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { + let collection_assigned_to_groups = CollectionGroup::find_by_collection(collection_uuid, conn).await; + for collection_assigned_to_group in collection_assigned_to_groups { + let group_users = GroupUser::find_by_group(&collection_assigned_to_group.groups_uuid, conn).await; + for group_user in group_users { + group_user.update_user_revision(conn).await; + } + } + + db_run! { conn: { + diesel::delete(collections_groups::table) + .filter(collections_groups::collections_uuid.eq(collection_uuid)) + .execute(conn) + .map_res("Error deleting collection group") + }} + } +} + +impl GroupUser { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + self.update_user_revision(conn).await; + + db_run! { conn: + sqlite, mysql { + match diesel::replace_into(groups_users::table) + .values(( + groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid), + groups_users::groups_uuid.eq(&self.groups_uuid), + )) + .execute(conn) + { + Ok(_) => Ok(()), + // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. + Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { + diesel::update(groups_users::table) + .filter(groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid)) + .filter(groups_users::groups_uuid.eq(&self.groups_uuid)) + .set(( + groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid), + groups_users::groups_uuid.eq(&self.groups_uuid), + )) + .execute(conn) + .map_res("Error adding user to group") + } + Err(e) => Err(e.into()), + }.map_res("Error adding user to group") + } + postgresql { + diesel::insert_into(groups_users::table) + .values(( + groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid), + groups_users::groups_uuid.eq(&self.groups_uuid), + )) + .on_conflict((groups_users::users_organizations_uuid, groups_users::groups_uuid)) + .do_update() + .set(( + groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid), + groups_users::groups_uuid.eq(&self.groups_uuid), + )) + .execute(conn) + .map_res("Error adding user to group") + } + } + } + + pub async fn find_by_group(group_uuid: &str, conn: &DbConn) -> Vec<Self> { + db_run! { conn: { + groups_users::table + .filter(groups_users::groups_uuid.eq(group_uuid)) + .load::<GroupUserDb>(conn) + .expect("Error loading group users") + .from_db() + }} + } + + pub async fn find_by_user(users_organizations_uuid: &str, conn: &DbConn) -> Vec<Self> { + db_run! { conn: { + groups_users::table + .filter(groups_users::users_organizations_uuid.eq(users_organizations_uuid)) + .load::<GroupUserDb>(conn) + .expect("Error loading groups for user") + .from_db() + }} + } + + pub async fn update_user_revision(&self, conn: &DbConn) { + match UserOrganization::find_by_uuid(&self.users_organizations_uuid, conn).await { + Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, + None => warn!("User could not be found!"), + } + } + + pub async fn delete_by_group_id_and_user_id( + group_uuid: &str, + users_organizations_uuid: &str, + conn: &DbConn, + ) -> EmptyResult { + match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { + Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, + None => warn!("User could not be found!"), + }; + + db_run! { conn: { + diesel::delete(groups_users::table) + .filter(groups_users::groups_uuid.eq(group_uuid)) + .filter(groups_users::users_organizations_uuid.eq(users_organizations_uuid)) + .execute(conn) + .map_res("Error deleting group users") + }} + } + + pub async fn delete_all_by_group(group_uuid: &str, conn: &DbConn) -> EmptyResult { + let group_users = GroupUser::find_by_group(group_uuid, conn).await; + for group_user in group_users { + group_user.update_user_revision(conn).await; + } + + db_run! { conn: { + diesel::delete(groups_users::table) + .filter(groups_users::groups_uuid.eq(group_uuid)) + .execute(conn) + .map_res("Error deleting group users") + }} + } + + pub async fn delete_all_by_user(users_organizations_uuid: &str, conn: &DbConn) -> EmptyResult { + match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { + Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, + None => warn!("User could not be found!"), + } + + db_run! { conn: { + diesel::delete(groups_users::table) + .filter(groups_users::users_organizations_uuid.eq(users_organizations_uuid)) + .execute(conn) + .map_res("Error deleting user groups") + }} + } +} |