From 895dbd81225931b2e972f618855d2fca2d8b9f16 Mon Sep 17 00:00:00 2001 From: zgaetano Date: Tue, 31 Mar 2026 15:29:29 -0400 Subject: [PATCH] Add mcp-gateway/USER_MANAGEMENT_SETUP.md --- mcp-gateway/USER_MANAGEMENT_SETUP.md | 350 +++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 mcp-gateway/USER_MANAGEMENT_SETUP.md diff --git a/mcp-gateway/USER_MANAGEMENT_SETUP.md b/mcp-gateway/USER_MANAGEMENT_SETUP.md new file mode 100644 index 0000000..b3969c1 --- /dev/null +++ b/mcp-gateway/USER_MANAGEMENT_SETUP.md @@ -0,0 +1,350 @@ +# User Management System Setup Guide + +This guide explains how to integrate the user management system into your MCP Gateway, enabling per-user API key generation and MCP access control. + +## Overview + +The user management system provides: + +- **User Creation & Management**: Create and manage users, enable/disable accounts +- **API Key Generation**: Generate unique API keys with optional TTL (time-to-live) +- **MCP Access Control**: Allow/block specific MCPs for each user (e.g., some users access only ERPNext, others access Wave + TrueNAS) +- **Persistent Storage**: User data stored in JSON file, survives container restarts +- **Web Dashboard**: Intuitive UI for managing users and API keys + +## Files Created + +1. **user_management.py** - Core user/API key management logic +2. **user_routes.py** - REST API endpoints for user management +3. **user_dashboard_ui.py** - Web dashboard with Vue.js UI +4. **gateway_proxy_user_integration.py** - Integration instructions + +## Installation Steps + +### Step 1: Copy Files + +The files are already created in `gateway-proxy/`: +- `user_management.py` +- `user_routes.py` +- `user_dashboard_ui.py` + +### Step 2: Update gateway_proxy.py + +Add the import at line 33 (after existing imports): + +```python +from user_management import user_manager +from user_routes import ( + create_user, list_users, get_user, delete_user, toggle_user, + generate_api_key, revoke_api_key, set_mcp_access +) +from user_dashboard_ui import user_management_dashboard +``` + +### Step 3: Update validate_bearer_token() Function + +Replace the `validate_bearer_token()` function (around line 254) with: + +```python +def validate_bearer_token(request: Request) -> dict | None: + auth_header = request.headers.get("Authorization", "") + if not auth_header.startswith("Bearer "): + return None + token = auth_header[7:] + + # Check static API key first + if STATIC_API_KEY and token == STATIC_API_KEY: + return {"client_id": "static", "scope": "mcp:tools", "user": "static"} + + # Check user API key + user_info = user_manager.validate_api_key(token) + if user_info: + return { + "client_id": "api-key", + "scope": "mcp:tools", + "user": user_info['username'], + "user_id": user_info['user_id'], + "mcp_allowed": user_info['mcp_allowed'], + "mcp_blocked": user_info['mcp_blocked'] + } + + # Check OAuth token + token_hash = _hash(token) + info = ACCESS_TOKENS.get(token_hash) + if not info: + return None + if info["expires_at"] < time.time(): + del ACCESS_TOKENS[token_hash] + return None + return info +``` + +### Step 4: Add MCP Access Control to handle_mcp() + +In the `handle_mcp()` function, after retrieving `token_info`, add this check: + +```python +# Check MCP access control +if token_info and 'user' in token_info and token_info.get('client_id') == 'api-key': + mcp_allowed = token_info.get('mcp_allowed', []) + mcp_blocked = token_info.get('mcp_blocked', []) + + # Check if user can access any backend (basic check) + if not mcp_allowed and not mcp_blocked: + # User has no restrictions, allow all + pass + elif mcp_blocked and len(mcp_blocked) == len(BACKENDS): + return JSONResponse( + {'error': 'access_denied', 'message': 'All MCPs are blocked for your user'}, + status_code=403 + ) +``` + +### Step 5: Update Routes List + +Add these routes to the routes list before creating the app (around line 1012): + +```python +routes = [ + # ... existing well-known and OAuth routes ... + + # MCP endpoint + Route("/mcp", handle_mcp, methods=["GET", "HEAD", "POST", "DELETE"]), + + # Monitoring + Route("/health", health, methods=["GET"]), + Route("/status", status, methods=["GET"]), + + # Dashboard + Route("/dashboard", dashboard, methods=["GET"]), + Route("/dashboard/status", dashboard_status, methods=["GET"]), + + # User Management - Enhanced Dashboard + Route("/admin", user_management_dashboard, methods=["GET"]), + + # User Management API + Route("/users", create_user, methods=["POST"]), + Route("/users", list_users, methods=["GET"]), + Route("/users/{username}", get_user, methods=["GET"]), + Route("/users/{username}", delete_user, methods=["DELETE"]), + Route("/users/{username}/enable", toggle_user, methods=["PATCH"]), + Route("/users/{username}/keys", generate_api_key, methods=["POST"]), + Route("/users/{username}/mcp-access", set_mcp_access, methods=["PUT"]), + Route("/keys/revoke", revoke_api_key, methods=["POST"]), +] +``` + +### Step 6: Update docker-compose.yml + +Ensure the gateway service has a data volume for persistent storage: + +```yaml +services: + mcp-gateway: + image: mcp-gateway:latest + build: + context: ./gateway-proxy + dockerfile: Dockerfile + container_name: mcp-gateway + ports: + - "4444:4444" + environment: + # ... existing environment variables ... + USERS_DB_PATH: /data/users.json + volumes: + - ./data:/data + - ./gateway-proxy:/app + depends_on: + # ... existing dependencies ... +``` + +### Step 7: Create Data Directory + +```bash +mkdir -p /sessions/vigilant-elegant-ramanujan/mnt/MCP\ Servers/mcp-gateway/data +``` + +### Step 8: Restart Gateway + +```bash +docker-compose down +docker-compose up -d mcp-gateway +``` + +## Usage + +### Access the Dashboard + +Open your browser to: + +``` +http://10.0.0.25:4444/admin +``` + +### Create a User via API + +```bash +curl -X POST http://10.0.0.25:4444/users \ + -H "Content-Type: application/json" \ + -d '{ + "username": "alice", + "email": "alice@example.com", + "description": "Engineering team" + }' +``` + +### Generate an API Key + +```bash +curl -X POST http://10.0.0.25:4444/users/alice/keys \ + -H "Content-Type: application/json" \ + -d '{ + "key_name": "production-key", + "ttl_days": 90 + }' +``` + +Response: +```json +{ + "status": "created", + "username": "alice", + "api_key": "mcpgw_...", + "note": "Save this key immediately — it will not be shown again" +} +``` + +### Configure MCP Access + +```bash +curl -X PUT http://10.0.0.25:4444/users/alice/mcp-access \ + -H "Content-Type: application/json" \ + -d '{ + "allowed_mcps": ["erpnext", "wave"], + "blocked_mcps": [] + }' +``` + +This allows alice to access only ERPNext and Wave Finance MCPs. + +### Use the API Key + +```bash +curl -X POST http://10.0.0.25:4444/mcp \ + -H "Authorization: Bearer mcpgw_..." \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "2024-11-05", + "capabilities": {}, + "clientInfo": {"name": "test-client", "version": "1.0"} + } + }' +``` + +## Access Control Examples + +### Example 1: Limited Access User + +Create a user with access to only ERPNext: + +```bash +# Create user +curl -X POST http://10.0.0.25:4444/users \ + -H "Content-Type: application/json" \ + -d '{"username": "erp-user", "description": "ERP access only"}' + +# Generate key +curl -X POST http://10.0.0.25:4444/users/erp-user/keys \ + -H "Content-Type: application/json" \ + -d '{"key_name": "erp-only"}' + +# Restrict to ERPNext +curl -X PUT http://10.0.0.25:4444/users/erp-user/mcp-access \ + -H "Content-Type: application/json" \ + -d '{"allowed_mcps": ["erpnext"]}' +``` + +### Example 2: Block Specific MCPs + +Create a user with access to everything except Home Assistant: + +```bash +curl -X PUT http://10.0.0.25:4444/users/alice/mcp-access \ + -H "Content-Type: application/json" \ + -d '{"blocked_mcps": ["homeassistant"]}' +``` + +### Example 3: Revoke an API Key + +```bash +curl -X POST http://10.0.0.25:4444/keys/revoke \ + -H "Content-Type: application/json" \ + -d '{"api_key": "mcpgw_..."}' +``` + +## Database Schema + +User data is stored in `/data/users.json` with the following structure: + +```json +{ + "users": { + "alice": { + "user_id": "abc123...", + "email": "alice@example.com", + "description": "Engineering team", + "created_at": "2026-03-31T12:00:00.000000", + "enabled": true, + "api_keys": ["hash1", "hash2"], + "mcp_allowed": ["erpnext", "wave"], + "mcp_blocked": [], + "metadata": {} + } + }, + "api_keys": { + "hash1": { + "user_id": "abc123...", + "username": "alice", + "key_name": "prod-key", + "created_at": "2026-03-31T12:00:00.000000", + "expires_at": null, + "revoked": false + } + } +} +``` + +## Security Notes + +1. **API Keys are Hashed**: Only SHA256 hashes are stored, never the actual key +2. **One-Time Display**: Keys are shown only once during generation +3. **TTL Support**: Keys can expire after N days +4. **Revocation**: Keys can be revoked at any time +5. **Per-User Access Control**: Each user has independent MCP restrictions + +## Troubleshooting + +### Users/Keys Not Persisting After Restart + +Check that: +1. `/data` directory exists and is mounted in docker-compose.yml +2. `/data` directory has write permissions +3. `USERS_DB_PATH=/data/users.json` is set in environment + +### API Key Not Working + +1. Verify key hasn't been revoked: Check in dashboard +2. Verify key hasn't expired: Check `expires_at` timestamp +3. Verify user is enabled: Check `enabled` field in dashboard +4. Check MCP access: Verify the user has access to the MCP they're trying to use + +## Next Steps + +- Configure additional users for different teams/roles +- Set up automated key rotation policies +- Monitor API key usage via the dashboard +- Integrate with your CI/CD for automated deployments