# 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. - **Endpoint‑Level 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**: Standards‑compliant 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**: Session‑based admin authentication with bcrypt password hashing - **OAuth2**: JWT‑based 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 auto‑reload) 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 server‑side components. - **Client Credentials**: For machine‑to‑machine 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) | | `/.well‑known/openid‑configuration` | 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` | `your‑secret‑key‑here‑change‑me` | 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 ready‑to‑run 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 multi‑user 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.