From ed1e6509ee2413a1718a9dc8eb98401dc1e5948d Mon Sep 17 00:00:00 2001 From: padreug Date: Tue, 11 Nov 2025 02:13:59 +0100 Subject: [PATCH] Add bulk grant permission API endpoint New Features: - BulkGrantPermission model: Grant same permission to multiple users - BulkGrantResult model: Detailed success/failure results - POST /api/v1/admin/permissions/bulk-grant endpoint This simplifies the admin workflow for granting the same account permission to multiple users at once (e.g., onboarding a team). The endpoint validates the account exists and is active, then grants the permission to each user, collecting successes and failures to return a detailed result. Related: UI-IMPROVEMENTS-PLAN.md Phase 1 --- models.py | 18 +++++++++++++++++ views_api.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/models.py b/models.py index c42d5d0..3507988 100644 --- a/models.py +++ b/models.py @@ -315,6 +315,24 @@ class CreateAccountPermission(BaseModel): notes: Optional[str] = None +class BulkGrantPermission(BaseModel): + """Bulk grant same permission to multiple users""" + user_ids: list[str] # List of user IDs to grant permission to + account_id: str # Account to grant permission on + permission_type: PermissionType # Type of permission to grant + expires_at: Optional[datetime] = None # Optional expiration + notes: Optional[str] = None # Notes for all permissions + + +class BulkGrantResult(BaseModel): + """Result of bulk grant operation""" + granted: list[AccountPermission] # Successfully granted permissions + failed: list[dict] # Failed grants with errors + total: int # Total attempted + success_count: int # Number of successful grants + failure_count: int # Number of failed grants + + class AccountWithPermissions(BaseModel): """Account with user-specific permission metadata""" id: str diff --git a/views_api.py b/views_api.py index 98e8f77..0dddb26 100644 --- a/views_api.py +++ b/views_api.py @@ -46,6 +46,8 @@ from .models import ( AccountWithPermissions, AssertionStatus, BalanceAssertion, + BulkGrantPermission, + BulkGrantResult, CastleSettings, CreateAccount, CreateAccountPermission, @@ -2967,6 +2969,60 @@ async def api_bulk_grant_permissions( return created_permissions +@castle_api_router.post("/api/v1/admin/permissions/bulk-grant", status_code=HTTPStatus.CREATED) +async def api_bulk_grant_permission_to_users( + data: "BulkGrantPermission", + wallet: WalletTypeInfo = Depends(require_admin_key), +) -> "BulkGrantResult": + """ + Grant the same permission to multiple users at once (admin only). + + This is a convenience endpoint that grants the same account permission + to multiple users in one operation. Useful for onboarding teams or + granting access to a shared expense account. + + Returns detailed results including successes and failures. + """ + from .models import BulkGrantResult + + granted = [] + failed = [] + + # Validate account exists and is active + account = await get_account(data.account_id) + if not account: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail=f"Account with ID '{data.account_id}' not found", + ) + + # Grant permission to each user + for user_id in data.user_ids: + try: + perm_data = CreateAccountPermission( + user_id=user_id, + account_id=data.account_id, + permission_type=data.permission_type, + expires_at=data.expires_at, + notes=data.notes, + ) + perm = await create_account_permission(perm_data, wallet.wallet.user) + granted.append(perm) + except Exception as e: + failed.append({ + "user_id": user_id, + "error": str(e), + }) + + return BulkGrantResult( + granted=granted, + failed=failed, + total=len(data.user_ids), + success_count=len(granted), + failure_count=len(failed), + ) + + # ===== USER PERMISSION ENDPOINTS =====