mcp-servers/USER_MANAGEMENT_SETUP.md

351 lines
9.2 KiB
Markdown
Raw Permalink Normal View History

2026-03-31 15:33:27 -04:00
# 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