fix(connectors): gate folder listings for PAT access

This commit is contained in:
Anish Sarkar 2026-06-20 01:58:16 +05:30
parent 3a0cd8c8cf
commit cf840875c9
6 changed files with 40 additions and 19 deletions

View file

@ -27,7 +27,6 @@ from app.config import config
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
User,
get_async_session,
)
from app.services.composio_service import (
@ -36,12 +35,13 @@ from app.services.composio_service import (
TOOLKIT_TO_CONNECTOR_TYPE,
ComposioService,
)
from app.users import current_active_user, require_session_context
from app.users import get_auth_context, require_session_context
from app.utils.connector_naming import (
count_connectors_of_type,
get_base_name_for_type,
)
from app.utils.oauth_security import OAuthStateManager
from app.utils.rbac import check_search_space_access
logger = logging.getLogger(__name__)
@ -69,13 +69,16 @@ def get_state_manager() -> OAuthStateManager:
@router.get("/composio/toolkits")
async def list_composio_toolkits(user: User = Depends(current_active_user)):
async def list_composio_toolkits(
auth: AuthContext = Depends(require_session_context),
):
"""
List available Composio toolkits.
Returns:
JSON with list of available toolkits and their metadata.
"""
del auth
if not ComposioService.is_enabled():
raise HTTPException(
status_code=503,
@ -647,7 +650,7 @@ async def list_composio_drive_folders(
connector_id: int,
parent_id: str | None = None,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
auth: AuthContext = Depends(get_auth_context),
):
"""
List folders AND files in user's Google Drive via Composio.
@ -662,6 +665,7 @@ async def list_composio_drive_folders(
)
connector = None
user = auth.user
try:
result = await session.execute(
select(SearchSourceConnector).filter(
@ -679,6 +683,8 @@ async def list_composio_drive_folders(
detail="Composio Google Drive connector not found or access denied",
)
await check_search_space_access(session, auth, connector.search_space_id)
composio_connected_account_id = connector.config.get(
"composio_connected_account_id"
)

View file

@ -20,17 +20,17 @@ from app.config import config
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
User,
get_async_session,
)
from app.schemas.discord_auth_credentials import DiscordAuthCredentialsBase
from app.users import current_active_user, require_session_context
from app.users import get_auth_context, require_session_context
from app.utils.connector_naming import (
check_duplicate_connector,
extract_identifier_from_credentials,
generate_unique_connector_name,
)
from app.utils.oauth_security import OAuthStateManager, TokenEncryption
from app.utils.rbac import check_search_space_access
logger = logging.getLogger(__name__)
@ -615,7 +615,7 @@ def _compute_channel_permissions(
async def get_discord_channels(
connector_id: int,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
auth: AuthContext = Depends(get_auth_context),
):
"""
Get list of Discord text channels for a connector with permission info.
@ -633,6 +633,7 @@ async def get_discord_channels(
"""
from sqlalchemy import select
user = auth.user
try:
# Get connector and verify ownership
result = await session.execute(
@ -651,6 +652,8 @@ async def get_discord_channels(
detail="Discord connector not found or access denied",
)
await check_search_space_access(session, auth, connector.search_space_id)
# Get credentials and decrypt bot token
credentials = DiscordAuthCredentialsBase.from_dict(connector.config)
token_encryption = get_token_encryption()

View file

@ -27,16 +27,16 @@ from app.connectors.dropbox import DropboxClient, list_folder_contents
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
User,
get_async_session,
)
from app.users import current_active_user, require_session_context
from app.users import get_auth_context, require_session_context
from app.utils.connector_naming import (
check_duplicate_connector,
extract_identifier_from_credentials,
generate_unique_connector_name,
)
from app.utils.oauth_security import OAuthStateManager, TokenEncryption
from app.utils.rbac import check_search_space_access
logger = logging.getLogger(__name__)
router = APIRouter()
@ -411,10 +411,11 @@ async def list_dropbox_folders(
connector_id: int,
parent_path: str = "",
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
auth: AuthContext = Depends(get_auth_context),
):
"""List folders and files in user's Dropbox."""
connector = None
user = auth.user
try:
result = await session.execute(
select(SearchSourceConnector).filter(
@ -430,6 +431,8 @@ async def list_dropbox_folders(
status_code=404, detail="Dropbox connector not found or access denied"
)
await check_search_space_access(session, auth, connector.search_space_id)
dropbox_client = DropboxClient(session, connector_id)
items, error = await list_folder_contents(dropbox_client, path=parent_path)

View file

@ -34,10 +34,9 @@ from app.connectors.google_gmail_connector import fetch_google_user_email
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
User,
get_async_session,
)
from app.users import current_active_user, require_session_context
from app.users import get_auth_context, require_session_context
from app.utils.connector_naming import (
check_duplicate_connector,
generate_unique_connector_name,
@ -47,6 +46,7 @@ from app.utils.oauth_security import (
TokenEncryption,
generate_code_verifier,
)
from app.utils.rbac import check_search_space_access
# Relax token scope validation for Google OAuth
os.environ["OAUTHLIB_RELAX_TOKEN_SCOPE"] = "1"
@ -478,7 +478,7 @@ async def list_google_drive_folders(
connector_id: int,
parent_id: str | None = None,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
auth: AuthContext = Depends(get_auth_context),
):
"""
List folders AND files in user's Google Drive with hierarchical support.
@ -498,6 +498,7 @@ async def list_google_drive_folders(
]
}
"""
user = auth.user
try:
# Get connector and verify ownership
result = await session.execute(
@ -516,6 +517,8 @@ async def list_google_drive_folders(
detail="Google Drive connector not found or access denied",
)
await check_search_space_access(session, auth, connector.search_space_id)
# Initialize Drive client (credentials will be loaded on first API call)
drive_client = GoogleDriveClient(session, connector_id)

View file

@ -27,16 +27,16 @@ from app.connectors.onedrive import OneDriveClient, list_folder_contents
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
User,
get_async_session,
)
from app.users import current_active_user, require_session_context
from app.users import get_auth_context, require_session_context
from app.utils.connector_naming import (
check_duplicate_connector,
extract_identifier_from_credentials,
generate_unique_connector_name,
)
from app.utils.oauth_security import OAuthStateManager, TokenEncryption
from app.utils.rbac import check_search_space_access
logger = logging.getLogger(__name__)
router = APIRouter()
@ -418,10 +418,11 @@ async def list_onedrive_folders(
connector_id: int,
parent_id: str | None = None,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
auth: AuthContext = Depends(get_auth_context),
):
"""List folders and files in user's OneDrive."""
connector = None
user = auth.user
try:
result = await session.execute(
select(SearchSourceConnector).filter(
@ -437,6 +438,8 @@ async def list_onedrive_folders(
status_code=404, detail="OneDrive connector not found or access denied"
)
await check_search_space_access(session, auth, connector.search_space_id)
onedrive_client = OneDriveClient(session, connector_id)
items, error = await list_folder_contents(onedrive_client, parent_id=parent_id)

View file

@ -22,17 +22,17 @@ from app.config import config
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
User,
get_async_session,
)
from app.schemas.slack_auth_credentials import SlackAuthCredentialsBase
from app.users import current_active_user, require_session_context
from app.users import get_auth_context, require_session_context
from app.utils.connector_naming import (
check_duplicate_connector,
extract_identifier_from_credentials,
generate_unique_connector_name,
)
from app.utils.oauth_security import OAuthStateManager, TokenEncryption
from app.utils.rbac import check_search_space_access
logger = logging.getLogger(__name__)
@ -530,7 +530,7 @@ async def refresh_slack_token(
async def get_slack_channels(
connector_id: int,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
auth: AuthContext = Depends(get_auth_context),
) -> list[dict[str, Any]]:
"""
Get list of Slack channels with bot membership status.
@ -546,6 +546,7 @@ async def get_slack_channels(
Returns:
List of channels with id, name, is_private, and is_member fields
"""
user = auth.user
try:
# Get the connector and verify ownership
result = await session.execute(
@ -564,6 +565,8 @@ async def get_slack_channels(
detail="Slack connector not found or access denied",
)
await check_search_space_access(session, auth, connector.search_space_id)
# Get credentials and decrypt bot token
credentials = SlackAuthCredentialsBase.from_dict(connector.config)
token_encryption = get_token_encryption()