mockapi/README.md.backup2
2026-03-16 09:00:26 +00:00

573 lines
20 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Configurable Mock API with Admin Interface and OAuth2 Provider
A lightweight, configurable mock API application in Python that allows dynamic endpoint management via an admin interface. The API serves customizable responses stored in a SQLite database with template variable support. Includes a full OAuth2 provider for securing endpoints with token-based authentication.
## Features
- **Dynamic Endpoint Configuration**: Create, read, update, and delete API endpoints through a web-based admin interface.
- **Template Variable Support**: Response bodies can include Jinja2 template variables (e.g., `{{ user_id }}`, `{{ timestamp }}`) populated from path parameters, query strings, headers, request body, system variables, and endpoint defaults.
- **Dynamic Route Registration**: Endpoints are registered/unregistered at runtime without restarting the server.
- **Admin Interface**: Secure web UI with session-based authentication for managing endpoints.
- **OAuth2 Provider**: Full OAuth2 implementation supporting authorization code, client credentials, and refresh token grant types.
- **EndpointLevel OAuth Protection**: Individual endpoints can require OAuth2 tokens with configurable scopes.
- **Admin OAuth2 Management**: Web UI for managing OAuth clients, tokens, and users.
- **OpenID Connect Discovery**: Standardscompliant discovery endpoint.
- **Production Ready**: Uses Waitress WSGI server, SQLAlchemy async, and FastAPI with proper error handling and security measures.
## Technology Stack
- **Framework**: FastAPI (with automatic OpenAPI documentation)
- **Server**: Waitress (production WSGI server)
- **Database**: SQLite with SQLAlchemy 2.0 async ORM
- **Templating**: Jinja2 with sandboxed environment
- **Authentication**: Sessionbased admin authentication with bcrypt password hashing
- **OAuth2**: JWTbased tokens with configurable scopes, client validation, and token revocation
- **Frontend**: Bootstrap 5 (CDN) for admin UI
## Project Structure
```
mockapi/
├── app.py # FastAPI application factory & lifespan
├── config.py # Configuration (Pydantic Settings)
├── database.py # SQLAlchemy async database setup
├── dependencies.py # FastAPI dependencies
├── example_usage.py # Integration test & demonstration script
├── middleware/
│ └── auth_middleware.py # Admin authentication middleware
├── models/
│ ├── endpoint_model.py # Endpoint SQLAlchemy model
│ └── oauth_models.py # OAuth2 client, token, and user models
├── observers/
│ └── __init__.py # Observer pattern placeholder
├── repositories/
│ ├── endpoint_repository.py # Repository pattern for endpoints
│ └── oauth2/ # OAuth2 repositories
├── run.py # Development runner script (with auto-reload)
├── services/
│ ├── route_service.py # Dynamic route registration/management
│ └── template_service.py # Jinja2 template rendering
├── controllers/
│ ├── admin_controller.py # Admin UI routes
│ └── oauth2/ # OAuth2 controllers and services
├── schemas/
│ ├── endpoint_schema.py # Pydantic schemas for validation
│ └── oauth2/ # OAuth2 schemas
├── templates/ # Jinja2 HTML templates
│ ├── base.html # Base layout
│ └── admin/
│ ├── login.html # Login page
│ ├── dashboard.html # Admin dashboard
│ ├── endpoints.html # Endpoint list
│ ├── endpoint_form.html # Create/edit endpoint
│ └── oauth/ # OAuth2 management pages
├── static/
│ └── css/ # Static CSS (optional)
├── tests/ # Test suite
│ ├── test_admin.py # Admin authentication tests
│ ├── test_endpoint_repository.py
│ ├── test_route_manager_fix.py
│ ├── test_oauth2_controller.py
│ └── integration/ # Integration tests
├── utils/ # Utility modules
│ └── __init__.py
├── requirements.txt # Python dependencies
├── .env.example # Example environment variables
├── .env # Local environment variables (create from .env.example)
├── run_example.sh # Script to run the integration test
├── LICENSE # MIT License
└── README.md # This file
```
## Installation
1. **Navigate to project directory**:
```bash
cd ~/GitLab/customer-engineering/mockapi
```
2. **Create a virtual environment** (recommended):
```bash
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
3. **Install dependencies**:
```bash
pip install -r requirements.txt
```
4. **Configure environment variables**:
```bash
cp .env.example .env
# Edit .env with your settings
```
Example `.env`:
```ini
DATABASE_URL=sqlite+aiosqlite:///./mockapi.db
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123 # Change this in production!
SECRET_KEY=your-secret-key-here # Change this!
DEBUG=True # Set to False in production
```
5. **Initialize the database** (tables are created automatically on first run).
## Running the Application
### Development (with autoreload)
Make sure your virtual environment is activated:
```bash
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
```
Then run with auto-reload for development:
```bash
# Using run.py (convenience script)
python run.py
# Or directly with uvicorn
uvicorn app:app --reload --host 0.0.0.0 --port 8000
```
### Production (with Waitress)
For production deployment, use Waitress WSGI server with the provided WSGI adapter (a2wsgi):
```bash
waitress-serve --host=0.0.0.0 --port=8000 --threads=4 wsgi:wsgi_app
```
The server will start on `http://localhost:8000` (or your configured host/port).
**Note:** Waitress is a WSGI server, but FastAPI is an ASGI framework. The `wsgi.py` file uses `a2wsgi` to wrap the ASGI application into a WSGI-compatible interface. Routes are automatically refreshed from the database on server startup.
## Quick Start with cURL Examples
### 1. Create a Mock Endpoint via Admin API
First, log in to the admin interface (default credentials: `admin` / `admin123`):
```bash
# Simulate login and create session (use browser for UI, but you can also use curl)
curl -c cookies.txt -X POST http://localhost:8000/admin/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=admin123"
```
Then create a mock endpoint:
```bash
curl -b cookies.txt -X POST http://localhost:8000/admin/endpoints \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "route=/api/greeting/{name}&method=GET&response_body={\"message\": \"Hello, {{ name }}!\"}&response_code=200&content_type=application/json&is_active=true"
```
### 2. Call the Mock Endpoint
```bash
curl http://localhost:8000/api/greeting/World
```
**Response:**
```json
{ "message": "Hello, World!" }
```
### 3. Use Template Variables
Create an endpoint that uses multiple variable sources:
```bash
curl -b cookies.txt -X POST http://localhost:8000/admin/endpoints \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "route=/api/user/{id}&method=GET&response_body={\"id\": {{ id }}, \"name\": \"{{ query.name }}\", \"timestamp\": \"{{ timestamp }}\"}&response_code=200&content_type=application/json&is_active=true"
```
Then call it with query parameters:
```bash
curl "http://localhost:8000/api/user/123?name=John"
```
**Response:**
```json
{ "id": 123, "name": "John", "timestamp": "2026-03-16T06:14:12.345678" }
```
### 4. OAuth2 Client Credentials Flow
First, create an OAuth client via the admin UI or using the admin API. Then obtain a token:
```bash
# Get an access token using client credentials
curl -X POST http://localhost:8000/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=your_client_id&client_secret=your_client_secret&scope=api:read"
```
**Response:**
```json
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 1800,
"scope": "api:read"
}
```
### 5. Protect an Endpoint with OAuth2
Create an endpoint that requires OAuth2:
```bash
curl -b cookies.txt -X POST http://localhost:8000/admin/endpoints \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "route=/api/protected&method=GET&response_body={\"status\": \"authorized\"}&response_code=200&content_type=application/json&is_active=true&requires_oauth=true&oauth_scopes=[\"api:read\"]"
```
Call it with a valid token:
```bash
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
http://localhost:8000/api/protected
```
**Response:**
```json
{ "status": "authorized" }
```
> **Note**: The `requires_oauth` and `oauth_scopes` fields are not yet exposed in the admin UI. To set OAuth protection, update the endpoint directly in the database or use the repository API.
### 6. OAuth2 Authorization Code Flow
For interactive applications:
1. **Authorization request** (user redirects to):
```
http://localhost:8000/oauth/authorize?response_type=code&client_id=your_client_id&redirect_uri=http://localhost:8080/callback&scope=api:read&state=xyz123
```
2. **Exchange code for token**:
```bash
curl -X POST http://localhost:8000/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=AUTH_CODE_HERE&redirect_uri=http://localhost:8080/callback&client_id=your_client_id&client_secret=your_client_secret"
```
### 7. OAuth2 Token Introspection
```bash
curl -u client_id:client_secret -X POST http://localhost:8000/oauth/introspect \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
```
### 8. OpenID Connect Discovery
```bash
curl http://localhost:8000/.well-known/openid-configuration
```
## OAuth2 Authentication
The application includes a full OAuth2 provider implementing RFC 6749 and OpenID Connect Discovery.
### Supported Grant Types
- **Authorization Code**: For web applications with serverside components.
- **Client Credentials**: For machinetomachine communication.
- **Refresh Token**: To obtain new access tokens without user interaction.
### Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/oauth/authorize` | GET, POST | Authorization endpoint (interactive user consent) |
| `/oauth/token` | POST | Token issuance and refresh |
| `/oauth/userinfo` | GET | OpenID Connect user claims |
| `/oauth/introspect` | POST | Token introspection (RFC 7662) |
| `/oauth/revoke` | POST | Token revocation (RFC 7009) |
| `/.wellknown/openidconfiguration` | GET | OpenID Connect discovery document |
### Scope Management
Default scopes include:
- `openid` OpenID Connect support
- `profile` Basic profile information
- `email` Email address claim
- `api:read` Read access to protected endpoints
- `api:write` Write access to protected endpoints
### Admin OAuth2 Management
Access OAuth2 management via the admin interface:
- **Clients**: `http://localhost:8000/admin/oauth/clients` Register and manage OAuth clients
- **Tokens**: `http://localhost:8000/admin/oauth/tokens` View and revoke issued tokens
- **Users**: `http://localhost:8000/admin/oauth/users` Manage OAuth user records
## Production Deployment Considerations
### 1. **Environment Configuration**
- Set `DEBUG=False` in production
- Use strong, unique values for `ADMIN_PASSWORD` and `SECRET_KEY`
- Consider using a more robust database (PostgreSQL) by changing `DATABASE_URL`
- Store sensitive values in environment variables or a secrets manager
### 2. **Process Management**
Use a process manager like systemd (Linux) or Supervisor to keep the application running:
**Example systemd service (`/etc/systemd/system/mockapi.service`)**:
```ini
[Unit]
Description=Mock API Service
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/mockapi
Environment="PATH=/path/to/mockapi/venv/bin"
ExecStart=/path/to/mockapi/venv/bin/waitress-serve --host=0.0.0.0 --port=8000 wsgi:wsgi_app
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
### 3. **Reverse Proxy (Recommended)**
Use Nginx or Apache as a reverse proxy for SSL termination, load balancing, and static file serving:
**Example Nginx configuration**:
```nginx
server {
listen 80;
server_name api.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
### 4. **Database Backups**
For SQLite, regularly backup the `mockapi.db` file. For production, consider migrating to PostgreSQL.
## Usage
### 1. Access the Admin Interface
- Open `http://localhost:8000/admin/login`
- Log in with the credentials set in `.env` (default: `admin` / `admin123`)
### 2. Create a Mock Endpoint
1. Navigate to **Endpoints** → **Create New**.
2. Fill in the form:
- **Route**: `/api/greeting/{name}` (supports path parameters)
- **Method**: GET
- **Response Body**: `{ "message": "Hello, {{ name }}!" }`
- **Response Code**: 200
- **Content-Type**: `application/json`
- **Variables**: `{ "server": "mock-api" }` (optional defaults)
3. Click **Create**.
### 3. Call the Mock Endpoint
```bash
curl http://localhost:8000/api/greeting/World
```
Response:
```json
{ "message": "Hello, World!" }
```
### 4. Template Variables
The following variable sources are available in response templates:
| Source | Example variable | Usage in template |
|--------|------------------|-------------------|
| Path parameters | `{{ name }}` | `/users/{id}` → `{{ id }}` |
| Query parameters | `{{ query.page }}` | `?page=1` → `{{ page }}` |
| Request headers | `{{ header.authorization }}` | `Authorization: Bearer token` |
| Request body | `{{ body.user.email }}` | JSON request body |
| System variables | `{{ timestamp }}`, `{{ request_id }}` | Automatically injected |
| Endpoint defaults | `{{ server }}` | Defined in endpoint variables |
### 5. Admin Functions
- **List endpoints** with pagination and filtering
- **Edit** existing endpoints (changes take effect immediately)
- **Activate/deactivate** endpoints without deletion
- **Delete** endpoints (removes route)
- **Dashboard** with statistics (total endpoints, active routes, etc.)
- **OAuth2 management** clients, tokens, users
## Security Considerations
- **Admin authentication**: Uses bcrypt password hashing. Store a strong password hash in production.
- **Session management**: Signed cookies with configurable secret key.
- **Template sandboxing**: Jinja2 environment restricted with `SandboxedEnvironment` and `StrictUndefined`.
- **Request size limits**: Maximum body size of 1MB to prevent DoS.
- **Route validation**: Prevents path traversal (`..`) and other unsafe patterns.
- **SQL injection protection**: All queries use SQLAlchemy ORM.
- **OAuth2 security**: Client secret hashing, token revocation, scope validation, secure token storage.
## Configuration Options
See `config.py` for all available settings. Key environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `sqlite+aiosqlite:///./mockapi.db` | SQLAlchemy database URL |
| `ADMIN_USERNAME` | `admin` | Admin login username |
| `ADMIN_PASSWORD` | `admin123` | Admin login password (plaintext) |
| `SECRET_KEY` | `yoursecretkeyherechangeme` | Session signing secret |
| `DEBUG` | `False` | Enable debug mode (more logging, relaxed validation) |
| `OAUTH2_ISSUER` | `http://localhost:8000` | OAuth2 issuer URL for discovery |
| `OAUTH2_ACCESS_TOKEN_EXPIRE_MINUTES` | `30` | Access token lifetime |
| `OAUTH2_REFRESH_TOKEN_EXPIRE_DAYS` | `7` | Refresh token lifetime |
| `OAUTH2_SUPPORTED_SCOPES` | `["openid","profile","email","api:read","api:write"]` | Available OAuth2 scopes |
**Warning**: In production (`DEBUG=False`), the default `ADMIN_PASSWORD` and `SECRET_KEY` will cause validation errors. You must set unique values via environment variables.
## API Documentation
FastAPI automatically provides OpenAPI documentation at:
- Swagger UI: `http://localhost:8000/docs`
- ReDoc: `http://localhost:8000/redoc`
The root URL (/) automatically redirects to the Swagger documentation at /docs.
The dynamic mock endpoints are not listed in the OpenAPI schema (they are registered at runtime).
## Development & Testing
## API Testing with Bruno
A ready-to-use [Bruno](https://www.usebruno.com/) API collection is available in the `examples/` directory:
```bash
# Set up test OAuth client and view instructions
./examples/setup.sh
# Or import directly:
# examples/mockapi-collection.bru
```
The collection includes:
- Global variables for base URL and credentials
- Pre-configured requests for all endpoints
- OAuth2 flow examples (client credentials, authorization code)
- Admin authentication
- Mock endpoint creation and testing
- Protected endpoint examples
See [examples/README.md](examples/README.md) for detailed usage instructions.
### Running Tests
Run tests with pytest:
```bash
pytest tests/
```
The test suite includes:
- Unit tests for repository and service layers
- Integration tests for admin authentication
- Template rendering tests
- OAuth2 unit and integration tests (21+ tests)
### Example Integration Test
A readytorun integration test demonstrates the core functionality:
```bash
# Make the script executable (Linux/macOS)
chmod +x run_example.sh
# Run the example
./run_example.sh
```
Or directly with Python:
```bash
python example_usage.py
```
The example script will:
1. Start the FastAPI app (via TestClient)
2. Log in as admin
3. Create a mock endpoint with template variables
4. Call the endpoint and verify the response
5. Report success or failure
This is a great way to verify that the API is working correctly after installation.
## Troubleshooting
### Common Issues
1. **"no such table: endpoints" error**
- The database hasn't been initialized
- Restart the application - tables are created on first startup
- Or run `python -c "from database import init_db; import asyncio; asyncio.run(init_db())"`
2. **Login fails even with correct credentials**
- Check that `DEBUG=True` is set in `.env` (or provide unique credentials)
- The default credentials only work when `DEBUG=True`
- In production, you must set unique `ADMIN_PASSWORD` and `SECRET_KEY`
3. **Routes not being registered**
- Check that the endpoint is marked as active (`is_active=True`)
- Refresh the page - routes are registered immediately after creation
- Check application logs for errors
4. **Template variables not rendering**
- Ensure you're using double curly braces: `{{ variable }}`
- Check variable names match the context (use path_, query_, header_ prefixes as needed)
- View the rendered template in the admin edit form preview
5. **OAuth2 token validation fails**
- Verify the token hasn't expired
- Check that the client is active
- Confirm the token has required scopes for the endpoint
- Ensure the token hasn't been revoked
### Logging
Enable debug logging by setting `DEBUG=True` in `.env`. Check the console output for detailed error messages.
## Limitations & Future Enhancements
- **Current limitations**:
- SQLite only (but can be extended to PostgreSQL via `DATABASE_URL`)
- Single admin user (no multiuser support)
- No request logging/history
- OAuth2 user authentication uses placeholder user IDs (integration with external identity providers pending)
- **Possible extensions**:
- Import/export endpoints as JSON/YAML
- Request logging and analytics
- WebSocket notifications for admin actions
- Multiple admin users with roles
- Rate limiting per endpoint
- CORS configuration
- PKCE support for public OAuth2 clients
- Integration with external identity providers (SAML, LDAP)
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Acknowledgments
- Built with [FastAPI](https://fastapi.tiangolo.com/), [SQLAlchemy](https://www.sqlalchemy.org/), and [Jinja2](https://jinja.palletsprojects.com/).
- Admin UI uses [Bootstrap 5](https://getbootstrap.com/) via CDN.
- OAuth2 implementation follows RFC 6749, RFC 7662, and OpenID Connect standards.