|
|
|
@@ -19,6 +19,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
from app.core.db import get_db
|
|
|
|
|
from app.core.storage import get_storage_backend
|
|
|
|
|
from app.core.visibility import apply_visibility_filter, check_single_entity_access
|
|
|
|
|
from app.deps import require_permission
|
|
|
|
|
from app.plugins.builtins.mail.models import (
|
|
|
|
|
ContactPgpKey,
|
|
|
|
@@ -139,7 +140,8 @@ async def _resolve_attachment_paths(attachment_ids: list[str]) -> list[dict]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _get_account(
|
|
|
|
|
db: AsyncSession, account_id: uuid.UUID, tenant_id: uuid.UUID, user_id: uuid.UUID
|
|
|
|
|
db: AsyncSession, account_id: uuid.UUID, tenant_id: uuid.UUID, user_id: uuid.UUID,
|
|
|
|
|
is_system_admin: bool = False,
|
|
|
|
|
) -> MailAccount:
|
|
|
|
|
account = (
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -150,21 +152,12 @@ async def _get_account(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not account:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail account not found", "code": "not_found"})
|
|
|
|
|
if account.user_id == user_id:
|
|
|
|
|
return account
|
|
|
|
|
delegate = (
|
|
|
|
|
await db.execute(
|
|
|
|
|
select(MailAccountDelegate).where(
|
|
|
|
|
and_(
|
|
|
|
|
MailAccountDelegate.account_id == account_id,
|
|
|
|
|
MailAccountDelegate.delegate_user_id == user_id,
|
|
|
|
|
has_access = await check_single_entity_access(
|
|
|
|
|
db, "mail_account", account_id, user_id, tenant_id, "read", is_system_admin
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if delegate:
|
|
|
|
|
return account
|
|
|
|
|
if not has_access:
|
|
|
|
|
raise HTTPException(403, detail={"detail": "No access to this account", "code": "forbidden"})
|
|
|
|
|
return account
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _check_send_permission(
|
|
|
|
@@ -229,20 +222,12 @@ async def list_accounts(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
accounts = (
|
|
|
|
|
(
|
|
|
|
|
await db.execute(
|
|
|
|
|
select(MailAccount).where(
|
|
|
|
|
and_(
|
|
|
|
|
MailAccount.tenant_id == tenant_id,
|
|
|
|
|
or_(MailAccount.user_id == user_id, MailAccount.is_shared),
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.scalars()
|
|
|
|
|
.all()
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
query = select(MailAccount).where(MailAccount.tenant_id == tenant_id)
|
|
|
|
|
query = await apply_visibility_filter(
|
|
|
|
|
db, query, "mail_account", MailAccount, user_id, tenant_id, is_system_admin
|
|
|
|
|
)
|
|
|
|
|
accounts = (await db.execute(query)).scalars().all()
|
|
|
|
|
return [account_to_response(a) for a in accounts]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -254,6 +239,7 @@ async def create_account(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
account = await create_mail_account(
|
|
|
|
|
db, tenant_id=tenant_id, user_id=user_id, data=data.model_dump()
|
|
|
|
|
)
|
|
|
|
@@ -288,8 +274,9 @@ async def update_account(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await update_mail_account(db, account, data.model_dump(exclude_unset=True))
|
|
|
|
|
return account_to_response(account)
|
|
|
|
|
|
|
|
|
@@ -302,9 +289,13 @@ async def delete_account(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
if account.user_id != user_id:
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
has_admin = await check_single_entity_access(
|
|
|
|
|
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
|
|
|
|
)
|
|
|
|
|
if not has_admin:
|
|
|
|
|
raise HTTPException(403, detail={"detail": "Only owner can delete", "code": "forbidden"})
|
|
|
|
|
await db.delete(account)
|
|
|
|
|
|
|
|
|
@@ -318,9 +309,13 @@ async def assign_shared_users(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
if account.user_id != user_id:
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
has_admin = await check_single_entity_access(
|
|
|
|
|
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
|
|
|
|
)
|
|
|
|
|
if not has_admin:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
403, detail={"detail": "Only owner can assign users", "code": "forbidden"}
|
|
|
|
|
)
|
|
|
|
@@ -360,9 +355,13 @@ async def create_delegate(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
if account.user_id != user_id:
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
has_admin = await check_single_entity_access(
|
|
|
|
|
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
|
|
|
|
)
|
|
|
|
|
if not has_admin:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
403, detail={"detail": "Only owner can create delegates", "code": "forbidden"}
|
|
|
|
|
)
|
|
|
|
@@ -401,9 +400,13 @@ async def create_send_permission(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
if account.user_id != user_id:
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
has_admin = await check_single_entity_access(
|
|
|
|
|
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
|
|
|
|
)
|
|
|
|
|
if not has_admin:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
403, detail={"detail": "Only owner can grant send permissions", "code": "forbidden"}
|
|
|
|
|
)
|
|
|
|
@@ -434,8 +437,9 @@ async def test_connection(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
password = await get_account_password(account)
|
|
|
|
|
try:
|
|
|
|
|
import aioimaplib
|
|
|
|
@@ -470,8 +474,9 @@ async def trigger_imap_sync(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
result = await imap_sync_account(db, acc_id, tenant_id)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
@@ -487,8 +492,9 @@ async def list_folders(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
folders = (
|
|
|
|
|
(
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -511,8 +517,9 @@ async def create_folder(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id")
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
parent_id = _parse_uuid(data.parent_id, "parent_id") if data.parent_id else None
|
|
|
|
|
folder = MailFolder(
|
|
|
|
|
tenant_id=tenant_id,
|
|
|
|
@@ -544,6 +551,7 @@ async def update_folder(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
f_id = _parse_uuid(folder_id, "folder_id")
|
|
|
|
|
folder = (
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -552,7 +560,7 @@ async def update_folder(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not folder:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Folder not found", "code": "not_found"})
|
|
|
|
|
await _get_account(db, folder.account_id, tenant_id, user_id)
|
|
|
|
|
await _get_account(db, folder.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
if data.name:
|
|
|
|
|
folder.name = data.name
|
|
|
|
|
await db.flush()
|
|
|
|
@@ -567,6 +575,7 @@ async def delete_folder(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
f_id = _parse_uuid(folder_id, "folder_id")
|
|
|
|
|
folder = (
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -575,7 +584,7 @@ async def delete_folder(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not folder:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Folder not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "full")
|
|
|
|
|
try:
|
|
|
|
|
await imap_delete_folder(db, f_id, tenant_id)
|
|
|
|
@@ -605,6 +614,7 @@ async def empty_folder(
|
|
|
|
|
"""
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
f_id = _parse_uuid(folder_id, "folder_id")
|
|
|
|
|
folder = (
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -613,7 +623,7 @@ async def empty_folder(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not folder:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Folder not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "delete")
|
|
|
|
|
|
|
|
|
|
# Check if this folder IS the Trash folder
|
|
|
|
@@ -705,6 +715,7 @@ async def sync_folder(
|
|
|
|
|
"""Sync a single folder from IMAP server immediately."""
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
f_id = _parse_uuid(folder_id, "folder_id")
|
|
|
|
|
folder = (
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -713,7 +724,7 @@ async def sync_folder(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not folder:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Folder not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, folder.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
result = await mail_services.imap_sync_folder(db, f_id, tenant_id)
|
|
|
|
|
await db.flush()
|
|
|
|
|
return result
|
|
|
|
@@ -735,6 +746,7 @@ async def upload_attachment(
|
|
|
|
|
"""
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
|
|
|
|
|
# Read file content and check size
|
|
|
|
|
content = await file.read()
|
|
|
|
@@ -777,8 +789,9 @@ async def send_mail(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_send_permission(db, account, user_id)
|
|
|
|
|
signature = None
|
|
|
|
|
if data.signature_id:
|
|
|
|
@@ -899,6 +912,7 @@ async def create_template(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
template = MailTemplate(
|
|
|
|
|
tenant_id=tenant_id,
|
|
|
|
|
user_id=user_id,
|
|
|
|
@@ -958,6 +972,7 @@ async def create_signature(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id") if data.account_id else None
|
|
|
|
|
sig = MailSignature(
|
|
|
|
|
tenant_id=tenant_id,
|
|
|
|
@@ -1054,8 +1069,9 @@ async def configure_vacation(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id")
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
vacation_data = {
|
|
|
|
|
"is_enabled": data.is_enabled,
|
|
|
|
|
"subject": data.subject,
|
|
|
|
@@ -1083,8 +1099,9 @@ async def test_vacation_dedup(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(account_id, "account_id")
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
should_send_1 = await should_send_vacation_reply(db, acc_id, sender, tenant_id)
|
|
|
|
|
if should_send_1:
|
|
|
|
|
await log_vacation_sent(db, acc_id, sender, tenant_id)
|
|
|
|
@@ -1107,6 +1124,7 @@ async def import_pgp_key(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
key_id, public_key_armored = import_pgp_private_key(data.private_key_armored, data.passphrase)
|
|
|
|
|
encrypted_private = encrypt_password(data.private_key_armored)
|
|
|
|
|
pgp_key = PgpKey(
|
|
|
|
@@ -1127,6 +1145,7 @@ async def list_pgp_keys(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
keys = (
|
|
|
|
|
(
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -1178,6 +1197,7 @@ async def create_label(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
label = MailLabel(tenant_id=tenant_id, name=data.name, color=data.color, user_id=user_id)
|
|
|
|
|
db.add(label)
|
|
|
|
|
await db.flush()
|
|
|
|
@@ -1265,13 +1285,14 @@ async def reply_mail(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
original = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not original:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
|
await _check_send_permission(db, account, user_id)
|
|
|
|
|
signature = None
|
|
|
|
@@ -1311,13 +1332,14 @@ async def forward_mail_endpoint(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
original = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not original:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, original.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
|
await _check_send_permission(db, account, user_id)
|
|
|
|
|
signature = None
|
|
|
|
@@ -1358,13 +1380,14 @@ async def update_flags(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
mail = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
|
if data.is_seen is not None:
|
|
|
|
|
mail.is_seen = data.is_seen
|
|
|
|
@@ -1397,6 +1420,7 @@ async def download_attachment(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
a_id = _parse_uuid(att_id, "att_id")
|
|
|
|
|
mail = (
|
|
|
|
@@ -1404,7 +1428,7 @@ async def download_attachment(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
|
attachment = (
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -1442,13 +1466,14 @@ async def link_mail(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
mail = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
|
if data.contact_id:
|
|
|
|
|
mail.contact_id = _parse_uuid(data.contact_id, "contact_id")
|
|
|
|
@@ -1468,13 +1493,14 @@ async def create_event_from_mail(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
mail = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
|
try:
|
|
|
|
|
from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract
|
|
|
|
@@ -1517,6 +1543,7 @@ async def assign_label(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
l_id = _parse_uuid(data.label_id, "label_id")
|
|
|
|
|
mail = (
|
|
|
|
@@ -1524,7 +1551,7 @@ async def assign_label(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
|
label = (
|
|
|
|
|
await db.execute(
|
|
|
|
@@ -1558,8 +1585,9 @@ async def create_draft(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
acc_id = _parse_uuid(data.account_id, "account_id")
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
|
mail = await save_draft(db, acc_id, tenant_id, user_id, data.model_dump())
|
|
|
|
|
return mail_to_response(mail)
|
|
|
|
@@ -1574,13 +1602,14 @@ async def update_draft_route(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
mail = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
|
mail = await update_draft(db, m_id, tenant_id, data.model_dump())
|
|
|
|
|
return mail_to_response(mail)
|
|
|
|
@@ -1597,13 +1626,14 @@ async def delete_mail(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
mail = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "delete")
|
|
|
|
|
|
|
|
|
|
# Find the Trash folder for this account
|
|
|
|
@@ -1700,6 +1730,7 @@ async def move_mail(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
target_f_id = _parse_uuid(data.target_folder_id, "target_folder_id")
|
|
|
|
|
mail = (
|
|
|
|
@@ -1707,7 +1738,7 @@ async def move_mail(
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "write")
|
|
|
|
|
# Verify target folder exists and belongs to same tenant
|
|
|
|
|
target_folder = (
|
|
|
|
@@ -1790,13 +1821,14 @@ async def get_mail(
|
|
|
|
|
):
|
|
|
|
|
tenant_id = uuid.UUID(current_user["tenant_id"])
|
|
|
|
|
user_id = uuid.UUID(current_user["user_id"])
|
|
|
|
|
is_system_admin = current_user.get("is_system_admin", False)
|
|
|
|
|
m_id = _parse_uuid(mail_id, "mail_id")
|
|
|
|
|
mail = (
|
|
|
|
|
await db.execute(select(Mail).where(and_(Mail.id == m_id, Mail.tenant_id == tenant_id)))
|
|
|
|
|
).scalar_one_or_none()
|
|
|
|
|
if not mail:
|
|
|
|
|
raise HTTPException(404, detail={"detail": "Mail not found", "code": "not_found"})
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id)
|
|
|
|
|
account = await _get_account(db, mail.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
|
|
|
|
await _check_delegate_access(db, account, user_id, "read")
|
|
|
|
|
attachments = (
|
|
|
|
|
(await db.execute(select(MailAttachment).where(MailAttachment.mail_id == mail.id)))
|
|
|
|
|