introduce collection_id newtype

Stefan Melmuk 2 weeks ago
parent 16afe10898
commit 7f2db91f23
No known key found for this signature in database
GPG Key ID: 817020C608FE9C09

@ -368,7 +368,7 @@ pub async fn update_cipher_from_data(
cipher: &mut Cipher, cipher: &mut Cipher,
data: CipherData, data: CipherData,
headers: &Headers, headers: &Headers,
shared_to_collections: Option<Vec<String>>, shared_to_collections: Option<Vec<CollectionId>>,
conn: &mut DbConn, conn: &mut DbConn,
nt: &Notify<'_>, nt: &Notify<'_>,
ut: UpdateType, ut: UpdateType,
@ -710,7 +710,7 @@ async fn put_cipher_partial(
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct CollectionsAdminData { struct CollectionsAdminData {
#[serde(alias = "CollectionIds")] #[serde(alias = "CollectionIds")]
collection_ids: Vec<String>, collection_ids: Vec<CollectionId>,
} }
#[put("/ciphers/<uuid>/collections_v2", data = "<data>")] #[put("/ciphers/<uuid>/collections_v2", data = "<data>")]
@ -769,9 +769,9 @@ async fn post_collections_update(
err!("Cipher is not write accessible") err!("Cipher is not write accessible")
} }
let posted_collections = HashSet::<String>::from_iter(data.collection_ids); let posted_collections = HashSet::<CollectionId>::from_iter(data.collection_ids);
let current_collections = let current_collections =
HashSet::<String>::from_iter(cipher.get_collections(headers.user.uuid.to_string(), &mut conn).await); HashSet::<CollectionId>::from_iter(cipher.get_collections(headers.user.uuid.to_string(), &mut conn).await);
for collection in posted_collections.symmetric_difference(&current_collections) { for collection in posted_collections.symmetric_difference(&current_collections) {
match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await
@ -846,9 +846,10 @@ async fn post_collections_admin(
err!("Cipher is not write accessible") err!("Cipher is not write accessible")
} }
let posted_collections = HashSet::<String>::from_iter(data.collection_ids); let posted_collections = HashSet::<CollectionId>::from_iter(data.collection_ids);
let current_collections = let current_collections = HashSet::<CollectionId>::from_iter(
HashSet::<String>::from_iter(cipher.get_admin_collections(headers.user.uuid.to_string(), &mut conn).await); cipher.get_admin_collections(headers.user.uuid.to_string(), &mut conn).await,
);
for collection in posted_collections.symmetric_difference(&current_collections) { for collection in posted_collections.symmetric_difference(&current_collections) {
match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await
@ -900,7 +901,7 @@ struct ShareCipherData {
#[serde(alias = "Cipher")] #[serde(alias = "Cipher")]
cipher: CipherData, cipher: CipherData,
#[serde(alias = "CollectionIds")] #[serde(alias = "CollectionIds")]
collection_ids: Vec<String>, collection_ids: Vec<CollectionId>,
} }
#[post("/ciphers/<uuid>/share", data = "<data>")] #[post("/ciphers/<uuid>/share", data = "<data>")]
@ -933,7 +934,7 @@ async fn put_cipher_share(
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct ShareSelectedCipherData { struct ShareSelectedCipherData {
ciphers: Vec<CipherData>, ciphers: Vec<CipherData>,
collection_ids: Vec<String>, collection_ids: Vec<CollectionId>,
} }
#[put("/ciphers/share", data = "<data>")] #[put("/ciphers/share", data = "<data>")]
@ -1834,10 +1835,10 @@ pub struct CipherSyncData {
pub cipher_attachments: HashMap<String, Vec<Attachment>>, pub cipher_attachments: HashMap<String, Vec<Attachment>>,
pub cipher_folders: HashMap<String, String>, pub cipher_folders: HashMap<String, String>,
pub cipher_favorites: HashSet<String>, pub cipher_favorites: HashSet<String>,
pub cipher_collections: HashMap<String, Vec<String>>, pub cipher_collections: HashMap<String, Vec<CollectionId>>,
pub members: HashMap<OrganizationId, Membership>, pub members: HashMap<OrganizationId, Membership>,
pub user_collections: HashMap<String, CollectionUser>, pub user_collections: HashMap<CollectionId, CollectionUser>,
pub user_collections_groups: HashMap<String, CollectionGroup>, pub user_collections_groups: HashMap<CollectionId, CollectionGroup>,
pub user_group_full_access_for_organizations: HashSet<OrganizationId>, pub user_group_full_access_for_organizations: HashSet<OrganizationId>,
} }
@ -1878,7 +1879,7 @@ impl CipherSyncData {
// Generate a HashMap with the Cipher UUID as key and one or more Collection UUID's // Generate a HashMap with the Cipher UUID as key and one or more Collection UUID's
let user_cipher_collections = Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await; let user_cipher_collections = Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await;
let mut cipher_collections: HashMap<String, Vec<String>> = let mut cipher_collections: HashMap<String, Vec<CollectionId>> =
HashMap::with_capacity(user_cipher_collections.len()); HashMap::with_capacity(user_cipher_collections.len());
for (cipher, collection) in user_cipher_collections { for (cipher, collection) in user_cipher_collections {
cipher_collections.entry(cipher).or_default().push(collection); cipher_collections.entry(cipher).or_default().push(collection);
@ -1889,14 +1890,14 @@ impl CipherSyncData {
Membership::find_by_user(user_uuid, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect(); Membership::find_by_user(user_uuid, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect();
// Generate a HashMap with the User_Collections UUID as key and the CollectionUser record // Generate a HashMap with the User_Collections UUID as key and the CollectionUser record
let user_collections: HashMap<String, CollectionUser> = CollectionUser::find_by_user(user_uuid, conn) let user_collections: HashMap<CollectionId, CollectionUser> = CollectionUser::find_by_user(user_uuid, conn)
.await .await
.into_iter() .into_iter()
.map(|uc| (uc.collection_uuid.clone(), uc)) .map(|uc| (uc.collection_uuid.clone(), uc))
.collect(); .collect();
// Generate a HashMap with the collections_uuid as key and the CollectionGroup record // Generate a HashMap with the collections_uuid as key and the CollectionGroup record
let user_collections_groups: HashMap<String, CollectionGroup> = if CONFIG.org_groups_enabled() { let user_collections_groups: HashMap<CollectionId, CollectionGroup> = if CONFIG.org_groups_enabled() {
CollectionGroup::find_by_user(user_uuid, conn) CollectionGroup::find_by_user(user_uuid, conn)
.await .await
.into_iter() .into_iter()

@ -126,7 +126,7 @@ struct NewCollectionData {
name: String, name: String,
groups: Vec<NewCollectionObjectData>, groups: Vec<NewCollectionObjectData>,
users: Vec<NewCollectionMemberData>, users: Vec<NewCollectionMemberData>,
id: Option<String>, id: Option<CollectionId>,
external_id: Option<String>, external_id: Option<String>,
} }
@ -340,7 +340,7 @@ async fn get_org_collections_details(
}; };
// get all collection memberships for the current organization // get all collection memberships for the current organization
let coll_users = CollectionUser::find_by_organization(&org_id, &mut conn).await; let col_users = CollectionUser::find_by_organization(&org_id, &mut conn).await;
// check if current user has full access to the organization (either directly or via any group) // check if current user has full access to the organization (either directly or via any group)
let has_full_access_to_org = member.access_all let has_full_access_to_org = member.access_all
@ -355,7 +355,7 @@ async fn get_org_collections_details(
&& GroupUser::has_access_to_collection_by_member(&col.uuid, &member.uuid, &mut conn).await); && GroupUser::has_access_to_collection_by_member(&col.uuid, &member.uuid, &mut conn).await);
// get the users assigned directly to the given collection // get the users assigned directly to the given collection
let users: Vec<Value> = coll_users let users: Vec<Value> = col_users
.iter() .iter()
.filter(|collection_user| collection_user.collection_uuid == col.uuid) .filter(|collection_user| collection_user.collection_uuid == col.uuid)
.map(|collection_user| UserSelection::to_collection_user_details_read_only(collection_user).to_json()) .map(|collection_user| UserSelection::to_collection_user_details_read_only(collection_user).to_json())
@ -450,7 +450,7 @@ async fn post_organization_collections(
#[put("/organizations/<org_id>/collections/<col_id>", data = "<data>")] #[put("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
async fn put_organization_collection_update( async fn put_organization_collection_update(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: CollectionId,
headers: ManagerHeaders, headers: ManagerHeaders,
data: Json<NewCollectionData>, data: Json<NewCollectionData>,
conn: DbConn, conn: DbConn,
@ -461,7 +461,7 @@ async fn put_organization_collection_update(
#[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")] #[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")]
async fn post_organization_collection_update( async fn post_organization_collection_update(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: CollectionId,
headers: ManagerHeaders, headers: ManagerHeaders,
data: Json<NewCollectionData>, data: Json<NewCollectionData>,
mut conn: DbConn, mut conn: DbConn,
@ -472,7 +472,7 @@ async fn post_organization_collection_update(
err!("Can't find organization details") err!("Can't find organization details")
}; };
let Some(mut collection) = Collection::find_by_uuid_and_org(col_id, &org_id, &mut conn).await else { let Some(mut collection) = Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await else {
err!("Collection not found") err!("Collection not found")
}; };
@ -495,15 +495,13 @@ async fn post_organization_collection_update(
) )
.await; .await;
CollectionGroup::delete_all_by_collection(col_id, &mut conn).await?; CollectionGroup::delete_all_by_collection(&col_id, &mut conn).await?;
for group in data.groups { for group in data.groups {
CollectionGroup::new(String::from(col_id), group.id, group.read_only, group.hide_passwords) CollectionGroup::new(col_id.clone(), group.id, group.read_only, group.hide_passwords).save(&mut conn).await?;
.save(&mut conn)
.await?;
} }
CollectionUser::delete_all_by_collection(col_id, &mut conn).await?; CollectionUser::delete_all_by_collection(&col_id, &mut conn).await?;
for user in data.users { for user in data.users {
let Some(member) = Membership::find_by_uuid_and_org(&user.id, &org_id, &mut conn).await else { let Some(member) = Membership::find_by_uuid_and_org(&user.id, &org_id, &mut conn).await else {
@ -514,7 +512,7 @@ async fn post_organization_collection_update(
continue; continue;
} }
CollectionUser::save(&member.user_uuid, col_id, user.read_only, user.hide_passwords, &mut conn).await?; CollectionUser::save(&member.user_uuid, &col_id, user.read_only, user.hide_passwords, &mut conn).await?;
} }
Ok(Json(collection.to_json_details(&headers.user.uuid, None, &mut conn).await)) Ok(Json(collection.to_json_details(&headers.user.uuid, None, &mut conn).await))
@ -523,12 +521,12 @@ async fn post_organization_collection_update(
#[delete("/organizations/<org_id>/collections/<col_id>/user/<member_id>")] #[delete("/organizations/<org_id>/collections/<col_id>/user/<member_id>")]
async fn delete_organization_collection_user( async fn delete_organization_collection_user(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: CollectionId,
member_id: MembershipId, member_id: MembershipId,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
let Some(collection) = Collection::find_by_uuid_and_org(col_id, &org_id, &mut conn).await else { let Some(collection) = Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await else {
err!("Collection not found", "Collection does not exist or does not belong to this organization") err!("Collection not found", "Collection does not exist or does not belong to this organization")
}; };
@ -546,7 +544,7 @@ async fn delete_organization_collection_user(
#[post("/organizations/<org_id>/collections/<col_id>/delete-user/<member_id>")] #[post("/organizations/<org_id>/collections/<col_id>/delete-user/<member_id>")]
async fn post_organization_collection_delete_user( async fn post_organization_collection_delete_user(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: CollectionId,
member_id: MembershipId, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
@ -556,7 +554,7 @@ async fn post_organization_collection_delete_user(
async fn _delete_organization_collection( async fn _delete_organization_collection(
org_id: &OrganizationId, org_id: &OrganizationId,
col_id: &str, col_id: &CollectionId,
headers: &ManagerHeaders, headers: &ManagerHeaders,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -579,11 +577,11 @@ async fn _delete_organization_collection(
#[delete("/organizations/<org_id>/collections/<col_id>")] #[delete("/organizations/<org_id>/collections/<col_id>")]
async fn delete_organization_collection( async fn delete_organization_collection(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: CollectionId,
headers: ManagerHeaders, headers: ManagerHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
_delete_organization_collection(&org_id, col_id, &headers, &mut conn).await _delete_organization_collection(&org_id, &col_id, &headers, &mut conn).await
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -598,17 +596,17 @@ struct DeleteCollectionData {
#[post("/organizations/<org_id>/collections/<col_id>/delete")] #[post("/organizations/<org_id>/collections/<col_id>/delete")]
async fn post_organization_collection_delete( async fn post_organization_collection_delete(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: CollectionId,
headers: ManagerHeaders, headers: ManagerHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
_delete_organization_collection(&org_id, col_id, &headers, &mut conn).await _delete_organization_collection(&org_id, &col_id, &headers, &mut conn).await
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct BulkCollectionIds { struct BulkCollectionIds {
ids: Vec<String>, ids: Vec<CollectionId>,
} }
#[delete("/organizations/<org_id>/collections", data = "<data>")] #[delete("/organizations/<org_id>/collections", data = "<data>")]
@ -630,14 +628,14 @@ async fn bulk_delete_organization_collections(
Ok(()) Ok(())
} }
#[get("/organizations/<org_id>/collections/<coll_id>/details")] #[get("/organizations/<org_id>/collections/<col_id>/details")]
async fn get_org_collection_detail( async fn get_org_collection_detail(
org_id: OrganizationId, org_id: OrganizationId,
coll_id: &str, col_id: CollectionId,
headers: ManagerHeaders, headers: ManagerHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
match Collection::find_by_uuid_and_user(coll_id, headers.user.uuid.clone(), &mut conn).await { match Collection::find_by_uuid_and_user(&col_id, headers.user.uuid.clone(), &mut conn).await {
None => err!("Collection not found"), None => err!("Collection not found"),
Some(collection) => { Some(collection) => {
if collection.org_uuid != org_id { if collection.org_uuid != org_id {
@ -684,15 +682,15 @@ async fn get_org_collection_detail(
} }
} }
#[get("/organizations/<org_id>/collections/<coll_id>/users")] #[get("/organizations/<org_id>/collections/<col_id>/users")]
async fn get_collection_users( async fn get_collection_users(
org_id: OrganizationId, org_id: OrganizationId,
coll_id: &str, col_id: CollectionId,
_headers: ManagerHeaders, _headers: ManagerHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
// Get org and collection, check that collection is from org // Get org and collection, check that collection is from org
let Some(collection) = Collection::find_by_uuid_and_org(coll_id, &org_id, &mut conn).await else { let Some(collection) = Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await else {
err!("Collection not found in Organization") err!("Collection not found in Organization")
}; };
@ -709,21 +707,21 @@ async fn get_collection_users(
Ok(Json(json!(user_list))) Ok(Json(json!(user_list)))
} }
#[put("/organizations/<org_id>/collections/<coll_id>/users", data = "<data>")] #[put("/organizations/<org_id>/collections/<col_id>/users", data = "<data>")]
async fn put_collection_users( async fn put_collection_users(
org_id: OrganizationId, org_id: OrganizationId,
coll_id: &str, col_id: CollectionId,
data: Json<Vec<CollectionData>>, data: Json<Vec<MembershipData>>,
_headers: ManagerHeaders, _headers: ManagerHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
// Get org and collection, check that collection is from org // Get org and collection, check that collection is from org
if Collection::find_by_uuid_and_org(coll_id, &org_id, &mut conn).await.is_none() { if Collection::find_by_uuid_and_org(&col_id, &org_id, &mut conn).await.is_none() {
err!("Collection not found in Organization") err!("Collection not found in Organization")
} }
// Delete all the user-collections // Delete all the user-collections
CollectionUser::delete_all_by_collection(coll_id, &mut conn).await?; CollectionUser::delete_all_by_collection(&col_id, &mut conn).await?;
// And then add all the received ones (except if the user has access_all) // And then add all the received ones (except if the user has access_all)
for d in data.iter() { for d in data.iter() {
@ -735,7 +733,7 @@ async fn put_collection_users(
continue; continue;
} }
CollectionUser::save(&user.user_uuid, coll_id, d.read_only, d.hide_passwords, &mut conn).await?; CollectionUser::save(&user.user_uuid, &col_id, d.read_only, d.hide_passwords, &mut conn).await?;
} }
Ok(()) Ok(())
@ -841,6 +839,14 @@ async fn post_org_keys(
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct CollectionData { struct CollectionData {
id: CollectionId,
read_only: bool,
hide_passwords: bool,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct MembershipData {
id: MembershipId, id: MembershipId,
read_only: bool, read_only: bool,
hide_passwords: bool, hide_passwords: bool,
@ -1615,14 +1621,14 @@ async fn post_org_import(
// TODO: See if we can optimize the whole cipher adding/importing and prevent duplicate code and checks. // TODO: See if we can optimize the whole cipher adding/importing and prevent duplicate code and checks.
Cipher::validate_cipher_data(&data.ciphers)?; Cipher::validate_cipher_data(&data.ciphers)?;
let existing_collections: HashSet<Option<String>> = let existing_collections: HashSet<Option<CollectionId>> =
Collection::find_by_organization(&org_id, &mut conn).await.into_iter().map(|c| (Some(c.uuid))).collect(); Collection::find_by_organization(&org_id, &mut conn).await.into_iter().map(|c| Some(c.uuid)).collect();
let mut collections: Vec<String> = Vec::with_capacity(data.collections.len()); let mut collections: Vec<CollectionId> = Vec::with_capacity(data.collections.len());
for coll in data.collections { for col in data.collections {
let collection_uuid = if existing_collections.contains(&coll.id) { let collection_uuid = if existing_collections.contains(&col.id) {
coll.id.unwrap() col.id.unwrap()
} else { } else {
let new_collection = Collection::new(org_id.clone(), coll.name, coll.external_id); let new_collection = Collection::new(org_id.clone(), col.name, col.external_id);
new_collection.save(&mut conn).await?; new_collection.save(&mut conn).await?;
new_collection.uuid new_collection.uuid
}; };
@ -1649,10 +1655,10 @@ async fn post_org_import(
} }
// Assign the collections // Assign the collections
for (cipher_index, coll_index) in relations { for (cipher_index, col_index) in relations {
let cipher_id = &ciphers[cipher_index]; let cipher_id = &ciphers[cipher_index];
let coll_id = &collections[coll_index]; let col_id = &collections[col_index];
CollectionCipher::save(cipher_id, coll_id, &mut conn).await?; CollectionCipher::save(cipher_id, col_id, &mut conn).await?;
} }
let mut user = headers.user; let mut user = headers.user;
@ -1665,7 +1671,7 @@ async fn post_org_import(
struct BulkCollectionsData { struct BulkCollectionsData {
organization_id: OrganizationId, organization_id: OrganizationId,
cipher_ids: Vec<String>, cipher_ids: Vec<String>,
collection_ids: HashSet<String>, collection_ids: HashSet<CollectionId>,
remove_collections: bool, remove_collections: bool,
} }
@ -1683,7 +1689,7 @@ async fn post_bulk_collections(data: Json<BulkCollectionsData>, headers: Headers
// Get all the collection available to the user in one query // Get all the collection available to the user in one query
// Also filter based upon the provided collections // Also filter based upon the provided collections
let user_collections: HashMap<String, Collection> = let user_collections: HashMap<CollectionId, Collection> =
Collection::find_by_organization_and_user_uuid(&data.organization_id, &headers.user.uuid, &mut conn) Collection::find_by_organization_and_user_uuid(&data.organization_id, &headers.user.uuid, &mut conn)
.await .await
.into_iter() .into_iter()
@ -2352,7 +2358,7 @@ struct GroupRequest {
#[serde(default)] #[serde(default)]
access_all: bool, access_all: bool,
external_id: Option<String>, external_id: Option<String>,
collections: Vec<SelectionReadOnly>, collections: Vec<CollectionSelection>,
users: Vec<MembershipId>, users: Vec<MembershipId>,
} }
@ -2380,10 +2386,6 @@ struct SelectionReadOnly {
} }
impl SelectionReadOnly { impl SelectionReadOnly {
pub fn to_collection_group(&self, groups_uuid: String) -> CollectionGroup {
CollectionGroup::new(self.id.clone(), groups_uuid, self.read_only, self.hide_passwords)
}
pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> Self { pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> Self {
Self { Self {
id: collection_group.groups_uuid.clone(), id: collection_group.groups_uuid.clone(),
@ -2397,6 +2399,20 @@ impl SelectionReadOnly {
} }
} }
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct CollectionSelection {
id: CollectionId,
read_only: bool,
hide_passwords: bool,
}
impl CollectionSelection {
pub fn to_collection_group(&self, groups_uuid: String) -> CollectionGroup {
CollectionGroup::new(self.id.clone(), groups_uuid, self.read_only, self.hide_passwords)
}
}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct UserSelection { struct UserSelection {
@ -2496,7 +2512,7 @@ async fn put_group(
async fn add_update_group( async fn add_update_group(
mut group: Group, mut group: Group,
collections: Vec<SelectionReadOnly>, collections: Vec<CollectionSelection>,
members: Vec<MembershipId>, members: Vec<MembershipId>,
org_id: OrganizationId, org_id: OrganizationId,
headers: &AdminHeaders, headers: &AdminHeaders,
@ -2504,8 +2520,8 @@ async fn add_update_group(
) -> JsonResult { ) -> JsonResult {
group.save(conn).await?; group.save(conn).await?;
for selection_read_only_request in collections { for col_selection in collections {
let mut collection_group = selection_read_only_request.to_collection_group(group.uuid.clone()); let mut collection_group = col_selection.to_collection_group(group.uuid.clone());
collection_group.save(conn).await?; collection_group.save(conn).await?;
} }

@ -10,7 +10,7 @@ use rocket_ws::{Message, WebSocket};
use crate::{ use crate::{
auth::{ClientIp, WsAccessTokenHeader}, auth::{ClientIp, WsAccessTokenHeader},
db::{ db::{
models::{Cipher, Folder, Send as DbSend, User, UserId}, models::{Cipher, CollectionId, Folder, Send as DbSend, User, UserId},
DbConn, DbConn,
}, },
Error, CONFIG, Error, CONFIG,
@ -415,7 +415,7 @@ impl WebSocketUsers {
cipher: &Cipher, cipher: &Cipher,
user_uuids: &[UserId], user_uuids: &[UserId],
acting_device_uuid: &String, acting_device_uuid: &String,
collection_uuids: Option<Vec<String>>, collection_uuids: Option<Vec<CollectionId>>,
conn: &mut DbConn, conn: &mut DbConn,
) { ) {
// Skip any processing if both WebSockets and Push are not active // Skip any processing if both WebSockets and Push are not active
@ -428,7 +428,7 @@ impl WebSocketUsers {
let (user_uuid, collection_uuids, revision_date) = if let Some(collection_uuids) = collection_uuids { let (user_uuid, collection_uuids, revision_date) = if let Some(collection_uuids) = collection_uuids {
( (
Value::Nil, Value::Nil,
Value::Array(collection_uuids.into_iter().map(|v| v.into()).collect::<Vec<Value>>()), Value::Array(collection_uuids.into_iter().map(|v| v.to_string().into()).collect::<Vec<Value>>()),
serialize_date(Utc::now().naive_utc()), serialize_date(Utc::now().naive_utc()),
) )
} else { } else {

@ -14,7 +14,7 @@ use std::{
net::IpAddr, net::IpAddr,
}; };
use crate::db::models::{MembershipId, OrganizationId, UserId}; use crate::db::models::{CollectionId, MembershipId, OrganizationId, UserId};
use crate::{error::Error, CONFIG}; use crate::{error::Error, CONFIG};
const JWT_ALGORITHM: Algorithm = Algorithm::RS256; const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
@ -649,16 +649,16 @@ impl From<AdminHeaders> for Headers {
// col_id is usually the fourth path param ("/organizations/<org_id>/collections/<col_id>"), // col_id is usually the fourth path param ("/organizations/<org_id>/collections/<col_id>"),
// but there could be cases where it is a query value. // but there could be cases where it is a query value.
// First check the path, if this is not a valid uuid, try the query values. // First check the path, if this is not a valid uuid, try the query values.
fn get_col_id(request: &Request<'_>) -> Option<String> { fn get_col_id(request: &Request<'_>) -> Option<CollectionId> {
if let Some(Ok(col_id)) = request.param::<String>(3) { if let Some(Ok(col_id)) = request.param::<String>(3) {
if uuid::Uuid::parse_str(&col_id).is_ok() { if uuid::Uuid::parse_str(&col_id).is_ok() {
return Some(col_id); return Some(col_id.into());
} }
} }
if let Some(Ok(col_id)) = request.query_value::<String>("collectionId") { if let Some(Ok(col_id)) = request.query_value::<String>("collectionId") {
if uuid::Uuid::parse_str(&col_id).is_ok() { if uuid::Uuid::parse_str(&col_id).is_ok() {
return Some(col_id); return Some(col_id.into());
} }
} }
@ -763,11 +763,11 @@ impl From<ManagerHeadersLoose> for Headers {
impl ManagerHeaders { impl ManagerHeaders {
pub async fn from_loose( pub async fn from_loose(
h: ManagerHeadersLoose, h: ManagerHeadersLoose,
collections: &Vec<String>, collections: &Vec<CollectionId>,
conn: &mut DbConn, conn: &mut DbConn,
) -> Result<ManagerHeaders, Error> { ) -> Result<ManagerHeaders, Error> {
for col_id in collections { for col_id in collections {
if uuid::Uuid::parse_str(col_id).is_err() { if uuid::Uuid::parse_str(col_id.as_ref()).is_err() {
err!("Collection Id is malformed!"); err!("Collection Id is malformed!");
} }
if !Collection::can_access_collection(&h.membership, col_id, conn).await { if !Collection::can_access_collection(&h.membership, col_id, conn).await {

@ -4,8 +4,8 @@ use chrono::{NaiveDateTime, TimeDelta, Utc};
use serde_json::Value; use serde_json::Value;
use super::{ use super::{
Attachment, CollectionCipher, Favorite, FolderCipher, Group, Membership, MembershipStatus, MembershipType, Attachment, CollectionCipher, CollectionId, Favorite, FolderCipher, Group, Membership, MembershipStatus,
OrganizationId, User, UserId, MembershipType, OrganizationId, User, UserId,
}; };
use crate::api::core::{CipherData, CipherSyncData, CipherSyncType}; use crate::api::core::{CipherData, CipherSyncData, CipherSyncType};
@ -862,7 +862,7 @@ impl Cipher {
}} }}
} }
pub async fn get_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<String> { pub async fn get_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<CollectionId> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! {conn: { db_run! {conn: {
ciphers_collections::table ciphers_collections::table
@ -894,7 +894,7 @@ impl Cipher {
.and(collections_groups::read_only.eq(false))) .and(collections_groups::read_only.eq(false)))
) )
.select(ciphers_collections::collection_uuid) .select(ciphers_collections::collection_uuid)
.load::<String>(conn).unwrap_or_default() .load::<CollectionId>(conn).unwrap_or_default()
}} }}
} else { } else {
db_run! {conn: { db_run! {conn: {
@ -916,12 +916,12 @@ impl Cipher {
.and(users_collections::read_only.eq(false))) .and(users_collections::read_only.eq(false)))
) )
.select(ciphers_collections::collection_uuid) .select(ciphers_collections::collection_uuid)
.load::<String>(conn).unwrap_or_default() .load::<CollectionId>(conn).unwrap_or_default()
}} }}
} }
} }
pub async fn get_admin_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<String> { pub async fn get_admin_collections(&self, user_id: String, conn: &mut DbConn) -> Vec<CollectionId> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! {conn: { db_run! {conn: {
ciphers_collections::table ciphers_collections::table
@ -954,7 +954,7 @@ impl Cipher {
.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::<String>(conn).unwrap_or_default() .load::<CollectionId>(conn).unwrap_or_default()
}} }}
} else { } else {
db_run! {conn: { db_run! {conn: {
@ -977,14 +977,17 @@ impl Cipher {
.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::<String>(conn).unwrap_or_default() .load::<CollectionId>(conn).unwrap_or_default()
}} }}
} }
} }
/// Return a Vec with (cipher_uuid, collection_uuid) /// Return a Vec with (cipher_uuid, collection_uuid)
/// This is used during a full sync so we only need one query for all collections accessible. /// This is used during a full sync so we only need one query for all collections accessible.
pub async fn get_collections_with_cipher_by_user(user_id: String, conn: &mut DbConn) -> Vec<(String, String)> { pub async fn get_collections_with_cipher_by_user(
user_id: String,
conn: &mut DbConn,
) -> Vec<(String, CollectionId)> {
db_run! {conn: { db_run! {conn: {
ciphers_collections::table ciphers_collections::table
.inner_join(collections::table.on( .inner_join(collections::table.on(
@ -1018,7 +1021,7 @@ impl Cipher {
.or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group .or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group
.select(ciphers_collections::all_columns) .select(ciphers_collections::all_columns)
.distinct() .distinct()
.load::<(String, String)>(conn).unwrap_or_default() .load::<(String, CollectionId)>(conn).unwrap_or_default()
}} }}
} }
} }

@ -1,4 +1,10 @@
use rocket::request::FromParam;
use serde_json::Value; use serde_json::Value;
use std::{
borrow::Borrow,
fmt::{Display, Formatter},
ops::Deref,
};
use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User, UserId}; use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User, UserId};
use crate::CONFIG; use crate::CONFIG;
@ -8,7 +14,7 @@ db_object! {
#[diesel(table_name = collections)] #[diesel(table_name = collections)]
#[diesel(primary_key(uuid))] #[diesel(primary_key(uuid))]
pub struct Collection { pub struct Collection {
pub uuid: String, pub uuid: CollectionId,
pub org_uuid: OrganizationId, pub org_uuid: OrganizationId,
pub name: String, pub name: String,
pub external_id: Option<String>, pub external_id: Option<String>,
@ -19,7 +25,7 @@ db_object! {
#[diesel(primary_key(user_uuid, collection_uuid))] #[diesel(primary_key(user_uuid, collection_uuid))]
pub struct CollectionUser { pub struct CollectionUser {
pub user_uuid: UserId, pub user_uuid: UserId,
pub collection_uuid: String, pub collection_uuid: CollectionId,
pub read_only: bool, pub read_only: bool,
pub hide_passwords: bool, pub hide_passwords: bool,
} }
@ -29,7 +35,7 @@ db_object! {
#[diesel(primary_key(cipher_uuid, collection_uuid))] #[diesel(primary_key(cipher_uuid, collection_uuid))]
pub struct CollectionCipher { pub struct CollectionCipher {
pub cipher_uuid: String, pub cipher_uuid: String,
pub collection_uuid: String, pub collection_uuid: CollectionId,
} }
} }
@ -37,7 +43,7 @@ db_object! {
impl Collection { impl Collection {
pub fn new(org_uuid: OrganizationId, name: String, external_id: Option<String>) -> Self { pub fn new(org_uuid: OrganizationId, name: String, external_id: Option<String>) -> Self {
let mut new_model = Self { let mut new_model = Self {
uuid: crate::util::get_uuid(), uuid: CollectionId(crate::util::get_uuid()),
org_uuid, org_uuid,
name, name,
external_id: None, external_id: None,
@ -121,7 +127,7 @@ impl Collection {
json_object json_object
} }
pub async fn can_access_collection(member: &Membership, col_id: &str, conn: &mut DbConn) -> bool { pub async fn can_access_collection(member: &Membership, col_id: &CollectionId, conn: &mut DbConn) -> bool {
member.has_status(MembershipStatus::Confirmed) member.has_status(MembershipStatus::Confirmed)
&& (member.has_full_access() && (member.has_full_access()
|| CollectionUser::has_access_to_collection_by_user(col_id, &member.user_uuid, conn).await || CollectionUser::has_access_to_collection_by_user(col_id, &member.user_uuid, conn).await
@ -198,7 +204,7 @@ impl Collection {
} }
} }
pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &CollectionId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: { db_run! { conn: {
collections::table collections::table
.filter(collections::uuid.eq(uuid)) .filter(collections::uuid.eq(uuid))
@ -312,7 +318,11 @@ impl Collection {
}} }}
} }
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> { pub async fn find_by_uuid_and_org(
uuid: &CollectionId,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Self> {
db_run! { conn: { db_run! { conn: {
collections::table collections::table
.filter(collections::uuid.eq(uuid)) .filter(collections::uuid.eq(uuid))
@ -324,7 +334,7 @@ impl Collection {
}} }}
} }
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: UserId, conn: &mut DbConn) -> Option<Self> { pub async fn find_by_uuid_and_user(uuid: &CollectionId, user_uuid: UserId, conn: &mut DbConn) -> Option<Self> {
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
db_run! { conn: { db_run! { conn: {
collections::table collections::table
@ -534,7 +544,7 @@ impl CollectionUser {
pub async fn save( pub async fn save(
user_uuid: &UserId, user_uuid: &UserId,
collection_uuid: &str, collection_uuid: &CollectionId,
read_only: bool, read_only: bool,
hide_passwords: bool, hide_passwords: bool,
conn: &mut DbConn, conn: &mut DbConn,
@ -604,7 +614,7 @@ impl CollectionUser {
}} }}
} }
pub async fn find_by_collection(collection_uuid: &str, conn: &mut DbConn) -> Vec<Self> { pub async fn find_by_collection(collection_uuid: &CollectionId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
users_collections::table users_collections::table
.filter(users_collections::collection_uuid.eq(collection_uuid)) .filter(users_collections::collection_uuid.eq(collection_uuid))
@ -616,7 +626,7 @@ impl CollectionUser {
} }
pub async fn find_by_collection_swap_user_uuid_with_member_uuid( pub async fn find_by_collection_swap_user_uuid_with_member_uuid(
collection_uuid: &str, collection_uuid: &CollectionId,
conn: &mut DbConn, conn: &mut DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
@ -631,7 +641,7 @@ impl CollectionUser {
} }
pub async fn find_by_collection_and_user( pub async fn find_by_collection_and_user(
collection_uuid: &str, collection_uuid: &CollectionId,
user_uuid: &UserId, user_uuid: &UserId,
conn: &mut DbConn, conn: &mut DbConn,
) -> Option<Self> { ) -> Option<Self> {
@ -657,7 +667,7 @@ impl CollectionUser {
}} }}
} }
pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
for collection in CollectionUser::find_by_collection(collection_uuid, conn).await.iter() { for collection in CollectionUser::find_by_collection(collection_uuid, conn).await.iter() {
User::update_uuid_revision(&collection.user_uuid, conn).await; User::update_uuid_revision(&collection.user_uuid, conn).await;
} }
@ -689,14 +699,18 @@ impl CollectionUser {
}} }}
} }
pub async fn has_access_to_collection_by_user(col_id: &str, user_uuid: &UserId, conn: &mut DbConn) -> bool { pub async fn has_access_to_collection_by_user(
col_id: &CollectionId,
user_uuid: &UserId,
conn: &mut DbConn,
) -> bool {
Self::find_by_collection_and_user(col_id, user_uuid, conn).await.is_some() Self::find_by_collection_and_user(col_id, user_uuid, conn).await.is_some()
} }
} }
/// Database methods /// Database methods
impl CollectionCipher { impl CollectionCipher {
pub async fn save(cipher_uuid: &str, collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn save(cipher_uuid: &str, collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
Self::update_users_revision(collection_uuid, conn).await; Self::update_users_revision(collection_uuid, conn).await;
db_run! { conn: db_run! { conn:
@ -726,7 +740,7 @@ impl CollectionCipher {
} }
} }
pub async fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete(cipher_uuid: &str, collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
Self::update_users_revision(collection_uuid, conn).await; Self::update_users_revision(collection_uuid, conn).await;
db_run! { conn: { db_run! { conn: {
@ -748,7 +762,7 @@ impl CollectionCipher {
}} }}
} }
pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: { db_run! { 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)
@ -756,9 +770,57 @@ impl CollectionCipher {
}} }}
} }
pub async fn update_users_revision(collection_uuid: &str, conn: &mut DbConn) { pub async fn update_users_revision(collection_uuid: &CollectionId, conn: &mut DbConn) {
if let Some(collection) = Collection::find_by_uuid(collection_uuid, conn).await { if let Some(collection) = Collection::find_by_uuid(collection_uuid, conn).await {
collection.update_users_revision(conn).await; collection.update_users_revision(conn).await;
} }
} }
} }
#[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct CollectionId(String);
impl AsRef<str> for CollectionId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Deref for CollectionId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<str> for CollectionId {
fn borrow(&self) -> &str {
&self.0
}
}
impl Display for CollectionId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for CollectionId {
fn from(raw: String) -> Self {
Self(raw)
}
}
impl<'r> FromParam<'r> for CollectionId {
type Error = ();
#[inline(always)]
fn from_param(param: &'r str) -> Result<Self, Self::Error> {
if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) {
Ok(Self(param.to_string()))
} else {
Err(())
}
}
}

@ -1,4 +1,4 @@
use super::{Membership, MembershipId, OrganizationId, User, UserId}; use super::{CollectionId, Membership, MembershipId, OrganizationId, User, UserId};
use crate::api::EmptyResult; use crate::api::EmptyResult;
use crate::db::DbConn; use crate::db::DbConn;
use crate::error::MapResult; use crate::error::MapResult;
@ -23,7 +23,7 @@ db_object! {
#[diesel(table_name = collections_groups)] #[diesel(table_name = collections_groups)]
#[diesel(primary_key(collections_uuid, groups_uuid))] #[diesel(primary_key(collections_uuid, groups_uuid))]
pub struct CollectionGroup { pub struct CollectionGroup {
pub collections_uuid: String, pub collections_uuid: CollectionId,
pub groups_uuid: String, pub groups_uuid: String,
pub read_only: bool, pub read_only: bool,
pub hide_passwords: bool, pub hide_passwords: bool,
@ -113,7 +113,7 @@ impl Group {
} }
impl CollectionGroup { impl CollectionGroup {
pub fn new(collections_uuid: String, groups_uuid: String, read_only: bool, hide_passwords: bool) -> Self { pub fn new(collections_uuid: CollectionId, groups_uuid: String, read_only: bool, hide_passwords: bool) -> Self {
Self { Self {
collections_uuid, collections_uuid,
groups_uuid, groups_uuid,
@ -370,7 +370,7 @@ impl CollectionGroup {
}} }}
} }
pub async fn find_by_collection(collection_uuid: &str, conn: &mut DbConn) -> Vec<Self> { pub async fn find_by_collection(collection_uuid: &CollectionId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
collections_groups::table collections_groups::table
.filter(collections_groups::collections_uuid.eq(collection_uuid)) .filter(collections_groups::collections_uuid.eq(collection_uuid))
@ -410,7 +410,7 @@ impl CollectionGroup {
}} }}
} }
pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &mut DbConn) -> EmptyResult {
let collection_assigned_to_groups = CollectionGroup::find_by_collection(collection_uuid, conn).await; let collection_assigned_to_groups = CollectionGroup::find_by_collection(collection_uuid, conn).await;
for collection_assigned_to_group in collection_assigned_to_groups { 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; let group_users = GroupUser::find_by_group(&collection_assigned_to_group.groups_uuid, conn).await;
@ -496,7 +496,7 @@ impl GroupUser {
} }
pub async fn has_access_to_collection_by_member( pub async fn has_access_to_collection_by_member(
collection_uuid: &str, collection_uuid: &CollectionId,
member_uuid: &MembershipId, member_uuid: &MembershipId,
conn: &mut DbConn, conn: &mut DbConn,
) -> bool { ) -> bool {

@ -19,7 +19,7 @@ mod user;
pub use self::attachment::Attachment; pub use self::attachment::Attachment;
pub use self::auth_request::AuthRequest; pub use self::auth_request::AuthRequest;
pub use self::cipher::{Cipher, RepromptType}; pub use self::cipher::{Cipher, RepromptType};
pub use self::collection::{Collection, CollectionCipher, CollectionUser}; pub use self::collection::{Collection, CollectionCipher, CollectionId, CollectionUser};
pub use self::device::{Device, DeviceType}; pub use self::device::{Device, DeviceType};
pub use self::emergency_access::{EmergencyAccess, EmergencyAccessStatus, EmergencyAccessType}; pub use self::emergency_access::{EmergencyAccess, EmergencyAccessStatus, EmergencyAccessType};
pub use self::event::{Event, EventType}; pub use self::event::{Event, EventType};

@ -10,8 +10,10 @@ use std::{
ops::Deref, ops::Deref,
}; };
use super::{CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User, UserId}; use super::{
use crate::db::models::{Collection, CollectionGroup}; Collection, CollectionGroup, CollectionId, CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor,
User, UserId,
};
use crate::CONFIG; use crate::CONFIG;
db_object! { db_object! {
@ -474,7 +476,7 @@ impl Membership {
// If collections are to be included, only include them if the user does not have full access via a group or defined to the user it self // If collections are to be included, only include them if the user does not have full access via a group or defined to the user it self
let collections: Vec<Value> = if include_collections && !(full_access_group || self.has_full_access()) { let collections: Vec<Value> = if include_collections && !(full_access_group || self.has_full_access()) {
// Get all collections for the user here already to prevent more queries // Get all collections for the user here already to prevent more queries
let cu: HashMap<String, CollectionUser> = let cu: HashMap<CollectionId, CollectionUser> =
CollectionUser::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn) CollectionUser::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn)
.await .await
.into_iter() .into_iter()
@ -482,7 +484,7 @@ impl Membership {
.collect(); .collect();
// Get all collection groups for this user to prevent there inclusion // Get all collection groups for this user to prevent there inclusion
let cg: HashSet<String> = CollectionGroup::find_by_user(&self.user_uuid, conn) let cg: HashSet<CollectionId> = CollectionGroup::find_by_user(&self.user_uuid, conn)
.await .await
.into_iter() .into_iter()
.map(|cg| cg.collections_uuid) .map(|cg| cg.collections_uuid)
@ -961,7 +963,7 @@ impl Membership {
} }
pub async fn find_by_collection_and_org( pub async fn find_by_collection_and_org(
collection_uuid: &str, collection_uuid: &CollectionId,
org_uuid: &OrganizationId, org_uuid: &OrganizationId,
conn: &mut DbConn, conn: &mut DbConn,
) -> Vec<Self> { ) -> Vec<Self> {

Loading…
Cancel
Save