Browse Source

Db CRUD operations and API endpoints implemented

pull/2667/head
MFijak 3 years ago
committed by Maximilian Fijak
parent
commit
5a530414de
  1. 296
      src/api/core/organizations.rs
  2. 1
      src/api/mod.rs
  3. 355
      src/db/models/group.rs
  4. 2
      src/db/models/mod.rs
  5. 3
      src/db/models/organization.rs

296
src/api/core/organizations.rs

@ -6,7 +6,7 @@ use serde_json::Value;
use crate::{ use crate::{
api::{ api::{
core::{CipherSyncData, CipherSyncType}, core::{CipherSyncData, CipherSyncType},
EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, Notify, NumberOrString, PasswordData, UpdateType, EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, JsonVec, Notify, NumberOrString, PasswordData, UpdateType,
}, },
auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders},
db::{models::*, DbConn}, db::{models::*, DbConn},
@ -61,6 +61,21 @@ pub fn routes() -> Vec<Route> {
import, import,
post_org_keys, post_org_keys,
bulk_public_keys, bulk_public_keys,
get_groups,
post_groups,
get_group,
put_group,
post_group,
get_group_details,
delete_group,
post_delete_group,
get_group_users,
put_group_users,
get_user_groups,
post_user_groups,
put_user_groups,
delete_group_user,
post_delete_group_user,
] ]
} }
@ -1484,3 +1499,282 @@ async fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Header
Ok(()) Ok(())
} }
#[get("/organizations/<org_id>/groups")]
async fn get_groups(org_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
let groups = Group::find_by_organization(&org_id, &conn).await
.iter()
.map(Group::to_json)
.collect::<Value>();
Ok(Json(json!({
"Data": groups,
"Object": "list",
"ContinuationToken": null,
})))
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct GroupRequest {
Name: String,
AccessAll: Option<bool>,
ExternalId: String,
Collections: Vec<SelectionReadOnly>
}
impl GroupRequest {
pub fn to_group(&self, organizations_uuid: &str) -> Result<Group, String> {
let access_all_value = match self.AccessAll {
Some(value) => value,
_ => return Err(String::from("Could not convert GroupRequest to Group, because AccessAll has no value!"))
};
Ok(Group::new(
organizations_uuid.to_owned(),
self.Name.clone(),
access_all_value,
self.ExternalId.clone()
))
}
pub fn update_group(&self, mut group: Group) -> Result<Group, String> {
let access_all_value = match self.AccessAll {
Some(value) => value,
_ => return Err(String::from("Could not update group, because AccessAll has no value!"))
};
group.name = self.Name.clone();
group.access_all = access_all_value;
group.external_id = self.ExternalId.clone();
Ok(group)
}
}
#[derive(Deserialize, Serialize)]
#[allow(non_snake_case)]
struct SelectionReadOnly {
Id: String,
ReadOnly: bool,
HidePasswords: bool
}
impl SelectionReadOnly {
pub fn to_collection_group (&self, groups_uuid: String) -> CollectionGroup {
CollectionGroup::new (
self.Id.clone(),
groups_uuid.clone(),
self.ReadOnly,
self.HidePasswords
)
}
pub fn to_selection_read_only (collection_group: &CollectionGroup) -> SelectionReadOnly {
SelectionReadOnly {
Id: collection_group.collections_uuid.clone(),
ReadOnly: collection_group.read_only,
HidePasswords: collection_group.hide_passwords
}
}
pub fn to_json (&self) -> Value {
json!(self)
}
}
#[post("/organizations/<_org_id>/groups/<group_id>", data = "<data>")]
async fn post_group(_org_id: String, group_id: String, data: JsonUpcase<GroupRequest>, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
put_group(_org_id, group_id, data, _headers, conn).await
}
#[post("/organizations/<org_id>/groups", data = "<data>")]
async fn post_groups(org_id: String, _headers: AdminHeaders, data: JsonUpcase<GroupRequest>, conn: DbConn) -> JsonResult {
let group_request = data.into_inner().data;
let group = match group_request.to_group(&org_id) {
Ok(group) => group,
Err(err) => err!(&err)
};
add_update_group(group, group_request.Collections, &conn).await
}
#[put("/organizations/<_org_id>/groups/<group_id>", data = "<data>")]
async fn put_group(_org_id: String, group_id: String, data: JsonUpcase<GroupRequest>, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
let group = match Group::find_by_uuid(&group_id, &conn).await {
Some(group) => group,
None => err!("Group not found")
};
let group_request = data.into_inner().data;
let updated_group = match group_request.update_group(group) {
Ok(group) => group,
Err(err) => err!(&err)
};
CollectionGroup::delete_all_by_group(&group_id, &conn).await?;
add_update_group(updated_group, group_request.Collections, &conn).await
}
async fn add_update_group(mut group: Group, collections: Vec<SelectionReadOnly>, conn: &DbConn) -> JsonResult {
group.save(&conn).await?;
for selection_read_only_request in collections {
let mut collection_group = selection_read_only_request.to_collection_group(group.uuid.clone());
collection_group.save(&conn).await?;
}
Ok(Json(json!({
"Id": group.uuid,
"OrganizationId": group.organizations_uuid,
"Name": group.name,
"AccessAll": group.access_all,
"ExternalId": group.external_id
})))
}
#[get("/organizations/<_org_id>/groups/<group_id>/details")]
async fn get_group_details(_org_id: String, group_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
let group = match Group::find_by_uuid(&group_id, &conn).await {
Some(group) => group,
_ => err!("Group could not be found!")
};
let collection_groups = CollectionGroup::find_by_group(&group_id, &conn).await
.iter()
.map(|entry| SelectionReadOnly::to_selection_read_only(entry).to_json())
.collect::<Value>();
Ok(Json(json!({
"Id": group.uuid,
"OrganizationId": group.organizations_uuid,
"Name": group.name,
"AccessAll": group.access_all,
"ExternalId": group.external_id,
"Collections": collection_groups
})))
}
#[post("/organizations/<org_id>/groups/<group_id>/delete")]
async fn post_delete_group(org_id: String, group_id: String, _headers: AdminHeaders, conn: DbConn) -> EmptyResult {
delete_group(org_id, group_id, _headers, conn).await
}
#[delete("/organizations/<_org_id>/groups/<group_id>")]
async fn delete_group(_org_id: String, group_id: String, _headers: AdminHeaders, conn: DbConn) -> EmptyResult {
let group = match Group::find_by_uuid(&group_id, &conn).await {
Some(group) => group,
_ => err!("Group not found")
};
group.delete(&conn).await
}
#[get("/organizations/<_org_id>/groups/<group_id>")]
async fn get_group(_org_id: String, group_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
let group = match Group::find_by_uuid(&group_id, &conn).await {
Some(group) => group,
_ => err!("Group not found")
};
Ok(Json(group.to_json()))
}
#[get("/organizations/<_org_id>/groups/<group_id>/users")]
async fn get_group_users(_org_id: String, group_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
match Group::find_by_uuid(&group_id, &conn).await {
Some(_) => { /* Do nothing */ },
_ => err!("Group could not be found!")
};
let group_users: Vec<String> = GroupUser::find_by_group(&group_id, &conn).await
.iter()
.map(|entry| entry.users_organizations_uuid.clone())
.collect();
Ok(Json(json!(group_users)))
}
#[put("/organizations/<_org_id>/groups/<group_id>/users", data = "<data>")]
async fn put_group_users(_org_id: String, group_id: String, _headers: AdminHeaders, data: JsonVec<String>, conn: DbConn) -> EmptyResult {
match Group::find_by_uuid(&group_id, &conn).await {
Some(_) => { /* Do nothing */ },
_ => err!("Group could not be found!")
};
GroupUser::delete_all_by_group(&group_id, &conn).await?;
let assigned_user_ids = data.into_inner();
for assigned_user_id in assigned_user_ids {
let mut user_entry = GroupUser::new(group_id.clone(), assigned_user_id);
user_entry.save(&conn).await?;
}
Ok(())
}
#[get("/organizations/<_org_id>/users/<user_id>/groups")]
async fn get_user_groups(_org_id: String, user_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
match UserOrganization::find_by_uuid(&user_id, &conn).await {
Some(_) => { /* Do nothing */ },
_ => err!("User could not be found!")
};
let user_groups: Vec<String> = GroupUser::find_by_user(&user_id, &conn).await
.iter()
.map(|entry| entry.groups_uuid.clone())
.collect();
Ok(Json(json!(user_groups)))
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct OrganizationUserUpdateGroupsRequest {
GroupIds: Vec<String>
}
#[post("/organizations/<_org_id>/users/<user_id>/groups", data ="<data>")]
async fn post_user_groups(_org_id: String, user_id: String, data: JsonUpcase<OrganizationUserUpdateGroupsRequest>, _headers: AdminHeaders, conn: DbConn) -> EmptyResult {
put_user_groups(_org_id, user_id, data, _headers, conn).await
}
#[put("/organizations/<_org_id>/users/<user_id>/groups", data ="<data>")]
async fn put_user_groups(_org_id: String, user_id: String, data: JsonUpcase<OrganizationUserUpdateGroupsRequest>, _headers: AdminHeaders, conn: DbConn) -> EmptyResult {
match UserOrganization::find_by_uuid(&user_id, &conn).await {
Some(_) => { /* Do nothing */ },
_ => err!("User could not be found!")
};
GroupUser::delete_all_by_user(&user_id, &conn).await?;
let assigned_group_ids = data.into_inner().data;
for assigned_group_id in assigned_group_ids.GroupIds {
let mut group_user = GroupUser::new(assigned_group_id.clone(), user_id.clone());
group_user.save(&conn).await?;
}
Ok(())
}
#[post("/organizations/<org_id>/groups/<group_id>/delete-user/<user_id>")]
async fn post_delete_group_user(org_id: String, group_id: String, user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
delete_group_user(org_id, group_id, user_id, headers, conn).await
}
#[delete("/organizations/<_org_id>/groups/<group_id>/users/<user_id>")]
async fn delete_group_user(_org_id: String, group_id: String, user_id: String, _headers: AdminHeaders, conn: DbConn) -> EmptyResult {
match UserOrganization::find_by_uuid(&user_id, &conn).await {
Some(_) => { /* Do nothing */ },
_ => err!("User could not be found!")
};
match Group::find_by_uuid(&group_id, &conn).await {
Some(_) => { /* Do nothing */ },
_ => err!("Group could not be found!")
};
GroupUser::delete_by_group_id_and_user_id(&group_id, &user_id, &conn).await
}

1
src/api/mod.rs

@ -30,6 +30,7 @@ pub type EmptyResult = ApiResult<()>;
type JsonUpcase<T> = Json<util::UpCase<T>>; type JsonUpcase<T> = Json<util::UpCase<T>>;
type JsonUpcaseVec<T> = Json<Vec<util::UpCase<T>>>; type JsonUpcaseVec<T> = Json<Vec<util::UpCase<T>>>;
type JsonVec<T> = Json<Vec<T>>;
// Common structs representing JSON data received // Common structs representing JSON data received
#[derive(Deserialize)] #[derive(Deserialize)]

355
src/db/models/group.rs

@ -0,0 +1,355 @@
use serde_json::Value;
use chrono::{NaiveDateTime, Utc};
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,
pub external_id: String,
pub creation_date: NaiveDateTime,
pub revision_date: NaiveDateTime,
}
#[derive(Identifiable, Queryable, Insertable)]
#[table_name = "collection_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: String) -> Self {
let now = Utc::now().naive_utc();
Self {
uuid: crate::util::get_uuid(),
organizations_uuid: organizations_uuid,
name: name,
access_all: access_all,
external_id: external_id,
creation_date: now,
revision_date: now
}
}
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)
})
}
}
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: groups_uuid,
users_organizations_uuid: users_organizations_uuid
}
}
}
use crate::db::DbConn;
use crate::api::EmptyResult;
use crate::error::MapResult;
/// 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()
}}
}
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 {
Group::update_revision(&self.groups_uuid, &conn).await;
db_run! { conn:
sqlite, mysql {
match diesel::replace_into(collection_groups::table)
.values((
collection_groups::collections_uuid.eq(collections_uuid),
collection_groups::groups_uuid.eq(groups_uuid),
collection_groups::read_only.eq(read_only),
collection_groups::hide_passwords.eq(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(collection_groups::table)
.filter(collection_groups::collections_uuid.eq(self.collections_uuid))
.filter(collection_groups::groups_uuid.eq(self.groups_uuid))
.set((
collection_groups::collections_uuid.eq(self.collections_uuid),
collection_groups::groups_uuid.eq(self.groups_uuid),
collection_groups::read_only.eq(self.read_only),
collection_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(collection_groups::table)
.values((
collection_groups::collections_uuid.eq(&self.collections_uuid),
collection_groups::groups_uuid.eq(&self.groups_uuid),
collection_groups::read_only.eq(self.read_only),
collection_groups::hide_passwords.eq(self.hide_passwords),
))
.on_conflict((collection_groups::collections_uuid, collection_groups::groups_uuid))
.do_update()
.set((
collection_groups::read_only.eq(self.read_only),
collection_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: {
collection_groups::table
.filter(collection_groups::groups_uuid.eq(group_uuid))
.load::<CollectionGroupDb>(conn)
.expect("Error loading collection groups")
.from_db()
}}
}
pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(collection_groups::table)
.filter(collection_groups::collections_uuid.eq(&self.collections_uuid))
.filter(collection_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 {
db_run! { conn: {
diesel::delete(collection_groups::table)
.filter(collection_groups::groups_uuid.eq(group_uuid))
.execute(conn)
.map_res("Error deleting collection group")
}}
}
}
impl GroupUser {
pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
Group::update_revision(&self.groups_uuid, &conn).await;
db_run! { conn:
sqlite, mysql {
match diesel::replace_into(groups_users::table)
.values((
groups_users::users_organizations_uuid.eq(users_organizations_uuid),
groups_users::groups_uuid.eq(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 delete_by_group_id_and_user_id(group_uuid: &str, user_uuid: &str, conn: &DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(groups_users::table)
.filter(groups_users::groups_uuid.eq(group_uuid))
.filter(groups_users::users_organizations_uuid.eq(user_uuid))
.execute(conn)
.map_res("Error deleting group users")
}}
}
pub async fn delete_all_by_group(group_uuid: &str, conn: &DbConn) -> EmptyResult {
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 {
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")
}}
}
}

2
src/db/models/mod.rs

@ -11,6 +11,7 @@ mod send;
mod two_factor; mod two_factor;
mod two_factor_incomplete; mod two_factor_incomplete;
mod user; mod user;
mod group;
pub use self::attachment::Attachment; pub use self::attachment::Attachment;
pub use self::cipher::Cipher; pub use self::cipher::Cipher;
@ -25,3 +26,4 @@ pub use self::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::two_factor_incomplete::TwoFactorIncomplete; pub use self::two_factor_incomplete::TwoFactorIncomplete;
pub use self::user::{Invitation, User, UserStampException}; pub use self::user::{Invitation, User, UserStampException};
pub use self::group::{Group, CollectionGroup, GroupUser};

3
src/db/models/organization.rs

@ -2,7 +2,7 @@ use num_traits::FromPrimitive;
use serde_json::Value; use serde_json::Value;
use std::cmp::Ordering; use std::cmp::Ordering;
use super::{CollectionUser, OrgPolicy, OrgPolicyType, User}; use super::{CollectionUser, OrgPolicy, OrgPolicyType, User, GroupUser};
db_object! { db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -415,6 +415,7 @@ impl UserOrganization {
User::update_uuid_revision(&self.user_uuid, conn).await; User::update_uuid_revision(&self.user_uuid, conn).await;
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_user(&self.uuid, conn).await?;
db_run! { conn: { db_run! { 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)))

Loading…
Cancel
Save