Fix virtual account filtering and permission inheritance
Two critical fixes for user account access:
1. **Permission inheritance for ALL permission types**
- Previously only checked READ permission inheritance
- Now checks ALL permission types (read, submit_expense, manage)
- Fixes issue where users with submit_expense on parent virtual accounts
couldn't see child expense accounts
2. **Virtual account filtering after permission check**
- Virtual accounts are now filtered AFTER permission inheritance logic
- This allows permission inheritance to work correctly for virtual parents
- Virtual accounts are still excluded from final results for users
3. **User-specific account filtering**
- Frontend now passes filter_by_user=true to only show permitted accounts
- Prevents users from seeing accounts they don't have access to
Flow now works correctly:
- Admin grants user submit_expense permission on virtual 'Expenses:Supplies'
- Permission inheritance checks ALL permission types (not just read)
- User sees all 'Expenses:Supplies:*' child accounts (Food, Kitchen, etc.)
- Virtual parent 'Expenses:Supplies' is filtered out from final results
- User only sees real expense accounts they can submit to
Fixes loading hang and empty account list in Add Expense dialog.
🤖 Generated with Claude Code (https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b97e899983
commit
0e6fe3e3cd
2 changed files with 24 additions and 6 deletions
|
|
@ -405,7 +405,7 @@ window.app = Vue.createApp({
|
||||||
try {
|
try {
|
||||||
const response = await LNbits.api.request(
|
const response = await LNbits.api.request(
|
||||||
'GET',
|
'GET',
|
||||||
'/castle/api/v1/accounts',
|
'/castle/api/v1/accounts?filter_by_user=true&exclude_virtual=true',
|
||||||
this.g.user.wallets[0].inkey
|
this.g.user.wallets[0].inkey
|
||||||
)
|
)
|
||||||
this.accounts = response.data
|
this.accounts = response.data
|
||||||
|
|
|
||||||
28
views_api.py
28
views_api.py
|
|
@ -130,21 +130,27 @@ async def api_get_currencies() -> list[str]:
|
||||||
@castle_api_router.get("/api/v1/accounts")
|
@castle_api_router.get("/api/v1/accounts")
|
||||||
async def api_get_accounts(
|
async def api_get_accounts(
|
||||||
filter_by_user: bool = False,
|
filter_by_user: bool = False,
|
||||||
|
exclude_virtual: bool = True,
|
||||||
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
) -> list[Account] | list[AccountWithPermissions]:
|
) -> list[Account] | list[AccountWithPermissions]:
|
||||||
"""
|
"""
|
||||||
Get all accounts in the chart of accounts.
|
Get all accounts in the chart of accounts.
|
||||||
|
|
||||||
- filter_by_user: If true, only return accounts the user has permissions for
|
- filter_by_user: If true, only return accounts the user has permissions for
|
||||||
|
- exclude_virtual: If true, exclude virtual parent accounts (default True)
|
||||||
- Returns AccountWithPermissions objects when filter_by_user=true, otherwise Account objects
|
- Returns AccountWithPermissions objects when filter_by_user=true, otherwise Account objects
|
||||||
"""
|
"""
|
||||||
all_accounts = await get_all_accounts()
|
all_accounts = await get_all_accounts()
|
||||||
|
|
||||||
if not filter_by_user:
|
if not filter_by_user:
|
||||||
# Return all accounts without filtering
|
# Filter out virtual accounts if requested (default behavior for user views)
|
||||||
|
if exclude_virtual:
|
||||||
|
all_accounts = [acc for acc in all_accounts if not acc.is_virtual]
|
||||||
|
# Return all accounts without filtering by permissions
|
||||||
return all_accounts
|
return all_accounts
|
||||||
|
|
||||||
# Filter by user permissions
|
# Filter by user permissions
|
||||||
|
# NOTE: Do NOT filter out virtual accounts yet - they're needed for inheritance logic
|
||||||
user_id = wallet.wallet.user
|
user_id = wallet.wallet.user
|
||||||
user_permissions = await get_user_permissions(user_id)
|
user_permissions = await get_user_permissions(user_id)
|
||||||
|
|
||||||
|
|
@ -160,10 +166,14 @@ async def api_get_accounts(
|
||||||
perm for perm in user_permissions if perm.account_id == account.id
|
perm for perm in user_permissions if perm.account_id == account.id
|
||||||
]
|
]
|
||||||
|
|
||||||
# Check if user has inherited permission from parent account
|
# Check if user has inherited permission from parent account (any permission type)
|
||||||
inherited_perms = await get_user_permissions_with_inheritance(
|
# Try each permission type to see if user has inherited access
|
||||||
user_id, account.name, PermissionType.READ
|
inherited_perms = []
|
||||||
)
|
for perm_type in [PermissionType.READ, PermissionType.SUBMIT_EXPENSE, PermissionType.MANAGE]:
|
||||||
|
perms = await get_user_permissions_with_inheritance(
|
||||||
|
user_id, account.name, perm_type
|
||||||
|
)
|
||||||
|
inherited_perms.extend(perms)
|
||||||
|
|
||||||
# Determine if account should be included
|
# Determine if account should be included
|
||||||
has_access = bool(account_perms) or bool(inherited_perms)
|
has_access = bool(account_perms) or bool(inherited_perms)
|
||||||
|
|
@ -197,6 +207,8 @@ async def api_get_accounts(
|
||||||
description=account.description,
|
description=account.description,
|
||||||
user_id=account.user_id,
|
user_id=account.user_id,
|
||||||
created_at=account.created_at,
|
created_at=account.created_at,
|
||||||
|
is_active=account.is_active,
|
||||||
|
is_virtual=account.is_virtual,
|
||||||
user_permissions=permission_types if permission_types else None,
|
user_permissions=permission_types if permission_types else None,
|
||||||
inherited_from=inherited_from,
|
inherited_from=inherited_from,
|
||||||
parent_account=parent_account,
|
parent_account=parent_account,
|
||||||
|
|
@ -205,6 +217,12 @@ async def api_get_accounts(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Filter out virtual accounts if requested (after permission inheritance logic)
|
||||||
|
if exclude_virtual:
|
||||||
|
accounts_with_permissions = [
|
||||||
|
acc for acc in accounts_with_permissions if not acc.is_virtual
|
||||||
|
]
|
||||||
|
|
||||||
return accounts_with_permissions
|
return accounts_with_permissions
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue