feat(calendar,addressbook): remove old sharing code
This commit is contained in:
@@ -3,8 +3,7 @@ use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::address_book_dto::{
|
||||
AddressBookDto, CreateAddressBookDto, ShareAddressBookDto, UnshareAddressBookDto,
|
||||
UpdateAddressBookDto,
|
||||
AddressBookDto, CreateAddressBookDto, UpdateAddressBookDto,
|
||||
};
|
||||
use crate::application::dtos::contact_dto::{
|
||||
ContactDto, ContactGroupDto, CreateContactDto, CreateContactGroupDto, CreateContactVCardDto,
|
||||
@@ -14,7 +13,6 @@ use crate::application::ports::authorization_ports::AuthorizationEngine;
|
||||
use crate::application::ports::carddav_ports::{
|
||||
AddressBookUseCase, ContactStoragePort, ContactUseCase,
|
||||
};
|
||||
use crate::application::ports::storage_ports::StorageUseCase;
|
||||
use crate::common::errors::DomainError;
|
||||
use crate::domain::entities::contact::{Address, AddressBook, Contact, ContactGroup, Email, Phone};
|
||||
use crate::domain::services::authorization::{Permission, Resource, Role, Subject};
|
||||
@@ -444,105 +442,6 @@ impl AddressBookUseCase for ContactService {
|
||||
.collect();
|
||||
Ok(dtos)
|
||||
}
|
||||
|
||||
async fn share_address_book(
|
||||
&self,
|
||||
dto: ShareAddressBookDto,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError> {
|
||||
let id = Uuid::parse_str(&dto.address_book_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
||||
|
||||
// AuthZ: caller must have Share on the address book. Only
|
||||
// Owner grants include Share today; matches the pre-Round-3
|
||||
// owner-only rule.
|
||||
self.require_address_book_perm(&id, &user_id, Permission::Share)
|
||||
.await?;
|
||||
|
||||
// Don't allow sharing with yourself. `authz.set_role` would
|
||||
// silently no-op via `ON CONFLICT UPDATE` but the earlier
|
||||
// service returned a validation error to help the client
|
||||
// catch a UX bug — preserve that behaviour.
|
||||
if dto.user_id == user_id.to_string() {
|
||||
return Err(DomainError::validation_error(
|
||||
"Cannot share an address book with yourself",
|
||||
));
|
||||
}
|
||||
|
||||
let target_user_id = Uuid::parse_str(&dto.user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid target user ID format"))?;
|
||||
let role = if dto.can_write {
|
||||
Role::Editor
|
||||
} else {
|
||||
Role::Viewer
|
||||
};
|
||||
self.authz
|
||||
.set_role(
|
||||
user_id,
|
||||
Subject::User(target_user_id),
|
||||
role,
|
||||
Resource::AddressBook(id),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unshare_address_book(
|
||||
&self,
|
||||
dto: UnshareAddressBookDto,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError> {
|
||||
let id = Uuid::parse_str(&dto.address_book_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
||||
|
||||
// AuthZ: caller must have Share on the address book (same
|
||||
// permission that gates share creation gates removal too).
|
||||
self.require_address_book_perm(&id, &user_id, Permission::Share)
|
||||
.await?;
|
||||
|
||||
let target_user_id = Uuid::parse_str(&dto.user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid target user ID format"))?;
|
||||
self.authz
|
||||
.clear_role(Subject::User(target_user_id), Resource::AddressBook(id))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_address_book_shares(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<(String, bool)>, DomainError> {
|
||||
let id = Uuid::parse_str(address_book_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
||||
|
||||
// AuthZ: caller must have Manage on the address book. Only
|
||||
// Owner grants include Manage — matches the pre-Round-3
|
||||
// owner-only rule for the shares listing.
|
||||
self.require_address_book_perm(&id, &user_id, Permission::Manage)
|
||||
.await?;
|
||||
|
||||
let grants = self
|
||||
.authz
|
||||
.list_grants_on_resource(Resource::AddressBook(id))
|
||||
.await?;
|
||||
// Translate the engine's `Grant` view into the legacy
|
||||
// `(user_id_str, can_write_bool)` tuple the handler still
|
||||
// consumes. Non-user subjects (groups / tokens) are dropped
|
||||
// from this listing — a phase-4 endpoint will surface them
|
||||
// properly.
|
||||
Ok(grants
|
||||
.into_iter()
|
||||
.filter_map(|g| {
|
||||
let Subject::User(uid) = g.subject else {
|
||||
return None;
|
||||
};
|
||||
let can_write = matches!(g.role, Role::Editor | Role::Contributor | Role::Owner);
|
||||
Some((uid.to_string(), can_write))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl ContactUseCase for ContactService {
|
||||
@@ -1171,385 +1070,3 @@ impl ContactUseCase for ContactService {
|
||||
Ok(vcards)
|
||||
}
|
||||
}
|
||||
|
||||
impl StorageUseCase for ContactService {
|
||||
async fn handle_request(
|
||||
&self,
|
||||
action: &str,
|
||||
params: serde_json::Value,
|
||||
) -> Result<serde_json::Value, DomainError> {
|
||||
match action {
|
||||
// Address Book operations
|
||||
"create_address_book" => {
|
||||
let dto: CreateAddressBookDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let result = self.create_address_book(dto).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"update_address_book" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let update: UpdateAddressBookDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let result = self.update_address_book(address_book_id, update).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"delete_address_book" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
self.delete_address_book(address_book_id, user_id).await?;
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
"get_address_book" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.get_address_book(address_book_id, user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"list_user_address_books" => {
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.list_user_address_books(user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"list_public_address_books" => {
|
||||
let result = self.list_public_address_books().await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"share_address_book" => {
|
||||
let dto: ShareAddressBookDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
self.share_address_book(dto, user_id).await?;
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
"unshare_address_book" => {
|
||||
let dto: UnshareAddressBookDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
self.unshare_address_book(dto, user_id).await?;
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
"get_address_book_shares" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self
|
||||
.get_address_book_shares(address_book_id, user_id)
|
||||
.await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
|
||||
// Contact operations
|
||||
"create_contact" => {
|
||||
let dto: CreateContactDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let result = self.create_contact(dto).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"create_contact_from_vcard" => {
|
||||
let dto: CreateContactVCardDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let result = self.create_contact_from_vcard(dto).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"update_contact" => {
|
||||
let contact_id = params["contact_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
||||
|
||||
let update: UpdateContactDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let result = self.update_contact(contact_id, update).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"delete_contact" => {
|
||||
let contact_id = params["contact_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
self.delete_contact(contact_id, user_id).await?;
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
"get_contact" => {
|
||||
let contact_id = params["contact_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.get_contact(contact_id, user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"list_contacts" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self
|
||||
.list_contacts(address_book_id, None, None, user_id)
|
||||
.await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"search_contacts" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let query = params["query"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing query parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self
|
||||
.search_contacts(address_book_id, query, user_id)
|
||||
.await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
|
||||
// Group operations
|
||||
"create_group" => {
|
||||
let dto: CreateContactGroupDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let result = self.create_group(dto).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"update_group" => {
|
||||
let group_id = params["group_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
||||
|
||||
let update: UpdateContactGroupDto = serde_json::from_value(params.clone())
|
||||
.map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let result = self.update_group(group_id, update).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"delete_group" => {
|
||||
let group_id = params["group_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
self.delete_group(group_id, user_id).await?;
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
"get_group" => {
|
||||
let group_id = params["group_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.get_group(group_id, user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"list_groups" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.list_groups(address_book_id, user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
|
||||
// Group membership operations
|
||||
"add_contact_to_group" => {
|
||||
let dto: GroupMembershipDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
self.add_contact_to_group(dto, user_id).await?;
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
"remove_contact_from_group" => {
|
||||
let dto: GroupMembershipDto =
|
||||
serde_json::from_value(params.clone()).map_err(|e| {
|
||||
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
self.remove_contact_from_group(dto, user_id).await?;
|
||||
Ok(serde_json::Value::Null)
|
||||
}
|
||||
"list_contacts_in_group" => {
|
||||
let group_id = params["group_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.list_contacts_in_group(group_id, user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"list_groups_for_contact" => {
|
||||
let contact_id = params["contact_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.list_groups_for_contact(contact_id, user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
|
||||
// vCard operations
|
||||
"get_contact_vcard" => {
|
||||
let contact_id = params["contact_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self.get_contact_vcard(contact_id, user_id).await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
"get_contacts_as_vcards" => {
|
||||
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
||||
DomainError::validation_error("Missing address_book_id parameter")
|
||||
})?;
|
||||
|
||||
let user_id = params["user_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
||||
let user_id = Uuid::parse_str(user_id)
|
||||
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
||||
|
||||
let result = self
|
||||
.get_contacts_as_vcards(address_book_id, user_id)
|
||||
.await?;
|
||||
Ok(serde_json::to_value(result).unwrap())
|
||||
}
|
||||
|
||||
_ => Err(DomainError::validation_error(format!(
|
||||
"Unknown action: {}",
|
||||
action
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user