Skip to main content
Glama
test_nextcloud_impersonate.py7.69 kB
""" Manual test for Nextcloud impersonate API. This script tests using the Nextcloud impersonate app to allow admin users to act on behalf of other users. This is NOT the same as OAuth token exchange, but could serve as a workaround for background operations. Usage: # Start app container docker compose up -d app # Run the test uv run python tests/manual/test_nextcloud_impersonate.py """ import asyncio import logging import os import sys # Add parent directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../..")) import httpx # Setup logging logging.basicConfig( level=logging.INFO, format="%(levelname)-8s | %(name)-30s | %(message)s" ) logger = logging.getLogger(__name__) async def main(): """Test Nextcloud impersonate API""" # Configuration nextcloud_host = os.getenv("NEXTCLOUD_HOST", "http://localhost:8080") admin_user = os.getenv("NEXTCLOUD_USERNAME", "admin") admin_password = os.getenv("NEXTCLOUD_PASSWORD", "admin") target_user = "testuser" # We'll create this user logger.info("=" * 80) logger.info("Nextcloud Impersonate API Test") logger.info("=" * 80) logger.info(f"Nextcloud: {nextcloud_host}") logger.info(f"Admin user: {admin_user}") logger.info(f"Target user: {target_user}") logger.info("") async with httpx.AsyncClient() as client: # Step 1: Login as admin and get session logger.info("Step 1: Logging in as admin...") login_response = await client.post( f"{nextcloud_host}/login", data={ "user": admin_user, "password": admin_password, }, follow_redirects=True, ) if login_response.status_code != 200: logger.error(f"❌ Admin login failed: {login_response.status_code}") return 1 # Get requesttoken from response requesttoken = None for cookie in client.cookies.jar: if cookie.name == "nc_session": logger.info(f"✓ Admin logged in, session: {cookie.value[:20]}...") break logger.info("") # Step 2: Create test user if doesn't exist logger.info(f"Step 2: Creating test user '{target_user}'...") create_user_response = await client.post( f"{nextcloud_host}/ocs/v1.php/cloud/users", auth=(admin_user, admin_password), data={ "userid": target_user, "password": "testpassword123", }, headers={"OCS-APIRequest": "true"}, ) if create_user_response.status_code in (200, 400): # 400 if already exists logger.info("✓ Test user ready") else: logger.warning( f"User creation response: {create_user_response.status_code}" ) # Make sure user has logged in at least once (requirement for impersonation) logger.info(f" Performing initial login for {target_user}...") await client.post( f"{nextcloud_host}/login", data={ "user": target_user, "password": "testpassword123", }, follow_redirects=True, ) logger.info("✓ Test user has logged in") # Re-login as admin await client.post( f"{nextcloud_host}/login", data={ "user": admin_user, "password": admin_password, }, follow_redirects=True, ) logger.info("") # Step 3: Get CSRF token for impersonate request logger.info("Step 3: Getting CSRF token...") # Try to get token from settings page settings_response = await client.get( f"{nextcloud_host}/settings/users", follow_redirects=True, ) # Extract requesttoken from HTML import re token_match = re.search(r'data-requesttoken="([^"]+)"', settings_response.text) if token_match: requesttoken = token_match.group(1) logger.info(f"✓ CSRF token acquired: {requesttoken[:20]}...") else: logger.error("❌ Could not extract CSRF token from page") return 1 logger.info("") # Step 4: Call impersonate API logger.info(f"Step 4: Impersonating user '{target_user}'...") impersonate_response = await client.post( f"{nextcloud_host}/apps/impersonate/user", data={ "userId": target_user, }, headers={ "requesttoken": requesttoken, "Content-Type": "application/x-www-form-urlencoded", }, ) if impersonate_response.status_code != 200: logger.error(f"❌ Impersonate failed: {impersonate_response.status_code}") logger.error(f"Response: {impersonate_response.text}") return 1 logger.info("✓ Impersonation successful") logger.info("") # Step 5: Test API call as impersonated user logger.info("Step 5: Testing API call as impersonated user...") capabilities_response = await client.get( f"{nextcloud_host}/ocs/v2.php/cloud/capabilities", headers={"OCS-APIRequest": "true"}, ) if capabilities_response.status_code == 200: caps = capabilities_response.json() logger.info(f"✓ API call successful as {target_user}") logger.info( f" Version: {caps.get('ocs', {}).get('data', {}).get('version', {}).get('string')}" ) else: logger.error(f"❌ API call failed: {capabilities_response.status_code}") return 1 logger.info("") # Step 6: Get current user to verify impersonation logger.info("Step 6: Verifying current user...") user_response = await client.get( f"{nextcloud_host}/ocs/v2.php/cloud/user", headers={"OCS-APIRequest": "true"}, ) if user_response.status_code == 200: user_data = user_response.json() current_user = user_data.get("ocs", {}).get("data", {}).get("id") logger.info(f"✓ Current user: {current_user}") if current_user == target_user: logger.info(" ✓ Successfully impersonating target user!") else: logger.warning(f" ⚠ Expected {target_user}, got {current_user}") else: logger.error(f"❌ User check failed: {user_response.status_code}") logger.info("") logger.info("=" * 80) logger.info("✅ Impersonate API Test PASSED") logger.info("=" * 80) logger.info("") logger.info("Summary:") logger.info(" 1. Admin can impersonate other users via session-based API") logger.info(" 2. Impersonated session can access APIs as that user") logger.info(" 3. Requires admin credentials and CSRF token") logger.info("") logger.info("Limitations:") logger.info(" - Session-based (not stateless like OAuth)") logger.info(" - Requires admin credentials") logger.info(" - Target user must have logged in at least once") logger.info(" - Not suitable for distributed/background workers") logger.info("") logger.info("For background operations, consider:") logger.info(" - Use service account with appropriate permissions") logger.info(" - Or implement proper OAuth delegation (RFC 8693)") logger.info("") return 0 if __name__ == "__main__": exit_code = asyncio.run(main()) sys.exit(exit_code)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/No-Smoke/nextcloud-mcp-comprehensive'

If you have feedback or need assistance with the MCP directory API, please join our Discord server