# WebDAV Server with aiohttp A comprehensive, production-ready WebDAV server implementation with full RFC 4918 compliance, Windows Explorer compatibility, and enterprise-grade features. ## Features ### Core WebDAV Protocol - ✅ **Full RFC 4918 Compliance**: All standard WebDAV methods implemented - ✅ **Windows Explorer Integration**: Seamless compatibility with Windows WebDAV Mini-Redirector - ✅ **Multiple Authentication Methods**: Basic, Digest, and Token-based authentication - ✅ **Resource Locking**: Exclusive and shared locks with timeout management - ✅ **Custom Properties**: Full property management (PROPFIND/PROPPATCH) - ✅ **Collection Operations**: MKCOL, COPY, MOVE with depth support ### Enterprise Features - 🔒 **Multi-User Support**: Isolated user directories with permissions - 💾 **SQLite Database**: Robust backend for users, locks, and properties - 🌐 **Web Management Interface**: Admin dashboard and user portal - 🚀 **High Performance**: Async I/O with aiohttp and aiofiles - 🔐 **Security Focused**: Input validation, path traversal prevention, SQL injection protection - 📊 **Monitoring Ready**: Structured logging and metrics collection - 🐳 **Docker Support**: Container-ready with docker-compose - 📈 **Production Ready**: Gunicorn integration, health checks, graceful shutdown ## Quick Start ### Prerequisites - Python 3.8 or higher - pip (Python package manager) - Optional: Redis for caching - Optional: Docker for containerized deployment ### Installation 1. **Clone or create the project directory:** ```bash mkdir webdav-server cd webdav-server ``` 2. **Install dependencies:** ```bash pip install -r requirements.txt ``` 3. **Configure environment:** ```bash cp .env.example .env # Edit .env with your configuration nano .env ``` 4. **Generate a secure secret key:** ```bash python -c "import secrets; print(secrets.token_hex(32))" # Copy the output to JWT_SECRET_KEY in .env ``` 5. **Run the server:** ```bash python main.py ``` The server will start on `http://0.0.0.0:8080` by default. ### First Run On first run, the server automatically creates: - A default admin user: `admin` / `admin123` - The WebDAV root directory structure - SQLite database with all required tables **⚠️ Important**: Change the default admin password immediately! ## Configuration ### Environment Variables All configuration is done through the `.env` file. Key settings: #### Server Configuration ```env HOST=0.0.0.0 # Listen address PORT=8080 # Listen port SSL_ENABLED=false # Enable HTTPS ``` #### Authentication ```env AUTH_METHODS=basic,digest # Supported auth methods JWT_SECRET_KEY=... # Secret for sessions (required) SESSION_TIMEOUT=3600 # Session duration in seconds ``` #### WebDAV Settings ```env WEBDAV_ROOT=./webdav # Root directory for files MAX_FILE_SIZE=104857600 # Max file size (100MB) MAX_PROPFIND_DEPTH=3 # Depth limit for PROPFIND LOCK_TIMEOUT_DEFAULT=3600 # Default lock timeout ``` See `.env.example` for complete configuration options. ## Usage ### Connecting with Windows Explorer #### Method 1: Map Network Drive 1. Open File Explorer 2. Right-click "This PC" → "Map network drive" 3. Choose a drive letter 4. Enter the WebDAV URL: ``` http://your-server:8080/ ``` 5. Check "Connect using different credentials" 6. Enter your username and password 7. Click "Finish" #### Method 2: Add Network Location 1. Open File Explorer 2. Right-click "This PC" → "Add a network location" 3. Choose "Choose a custom network location" 4. Enter the WebDAV URL: ``` http://your-server:8080/ ``` 5. Enter credentials when prompted #### Windows with SSL (Recommended) For HTTPS connections: ``` https://your-server:8443/ ``` **Note**: Windows requires port 443 for HTTPS WebDAV by default. To use other ports, modify Windows registry: ``` HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters BasicAuthLevel = 2 ``` ### Connecting with macOS Finder 1. Open Finder 2. Press `Cmd+K` (Go → Connect to Server) 3. Enter the WebDAV URL: ``` http://your-server:8080/ ``` 4. Click "Connect" 5. Enter your credentials ### Connecting with Linux (davfs2) 1. Install davfs2: ```bash sudo apt-get install davfs2 # Ubuntu/Debian ``` 2. Mount the WebDAV share: ```bash sudo mount -t davfs http://your-server:8080/ /mnt/webdav ``` 3. Enter credentials when prompted ### Command Line Tools #### Using curl **Upload a file:** ```bash curl -u username:password -T file.txt http://localhost:8080/file.txt ``` **Download a file:** ```bash curl -u username:password http://localhost:8080/file.txt -o file.txt ``` **Create a directory:** ```bash curl -u username:password -X MKCOL http://localhost:8080/newdir/ ``` **Delete a resource:** ```bash curl -u username:password -X DELETE http://localhost:8080/file.txt ``` #### Using cadaver ```bash cadaver http://localhost:8080/ # Enter credentials dav:/> ls dav:/> put localfile.txt dav:/> get remotefile.txt dav:/> mkcol newfolder ``` ## User Management ### Creating Users Programmatically ```python import asyncio from main import Database async def create_user(): db = Database('./webdav.db') user_id = await db.create_user( username='john', password='SecurePass123!', email='john@example.com', role='user' ) print(f"Created user with ID: {user_id}") asyncio.run(create_user()) ``` ### User Roles - **admin**: Full access to all features and user management - **user**: Standard user with access to their own directory - **readonly**: Read-only access (future implementation) ### Directory Structure ``` webdav/ ├── users/ │ ├── admin/ # Admin user directory │ ├── john/ # John's private directory │ └── jane/ # Jane's private directory ├── shared/ # Shared between all users (optional) └── public/ # Public access (optional) ``` ## Production Deployment ### Using Gunicorn Create `gunicorn_config.py`: ```python import multiprocessing bind = "0.0.0.0:8080" workers = multiprocessing.cpu_count() * 2 + 1 worker_class = "aiohttp.GunicornWebWorker" keepalive = 30 timeout = 60 accesslog = "./logs/access.log" errorlog = "./logs/error.log" loglevel = "info" ``` Run with Gunicorn: ```bash gunicorn main:init_app --config gunicorn_config.py ``` ### Using Docker Create `Dockerfile`: ```dockerfile FROM python:3.11-slim WORKDIR /app # Install dependencies COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy application COPY . . # Create directories RUN mkdir -p /app/webdav /app/logs # Expose port EXPOSE 8080 # Run application CMD ["python", "main.py"] ``` Create `docker-compose.yml`: ```yaml version: '3.8' services: webdav: build: . ports: - "8080:8080" volumes: - ./webdav:/app/webdav - ./logs:/app/logs - ./webdav.db:/app/webdav.db environment: - HOST=0.0.0.0 - PORT=8080 - DB_PATH=/app/webdav.db - WEBDAV_ROOT=/app/webdav restart: unless-stopped # Optional: Redis for caching redis: image: redis:alpine ports: - "6379:6379" restart: unless-stopped ``` Build and run: ```bash docker-compose up -d ``` ### Nginx Reverse Proxy Create `/etc/nginx/sites-available/webdav`: ```nginx server { listen 80; server_name webdav.example.com; # Redirect to HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name webdav.example.com; ssl_certificate /etc/letsencrypt/live/webdav.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/webdav.example.com/privkey.pem; # WebDAV specific settings client_max_body_size 100M; client_body_buffer_size 128k; location / { proxy_pass http://localhost:8080; 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; # WebDAV headers proxy_set_header Destination $http_destination; proxy_set_header Depth $http_depth; # Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } } ``` Enable and reload: ```bash sudo ln -s /etc/nginx/sites-available/webdav /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ## Security Best Practices ### SSL/TLS Configuration 1. **Always use HTTPS in production** 2. Generate SSL certificates with Let's Encrypt: ```bash sudo certbot certonly --nginx -d webdav.example.com ``` 3. Update `.env`: ```env SSL_ENABLED=true SSL_CERT_PATH=/etc/letsencrypt/live/webdav.example.com/fullchain.pem SSL_KEY_PATH=/etc/letsencrypt/live/webdav.example.com/privkey.pem ``` ### Authentication - Use **Digest authentication** over Basic when possible - Enforce strong passwords (min length, complexity) - Enable rate limiting to prevent brute force attacks - Implement account lockout after failed attempts ### Firewall Rules ```bash # Allow only necessary ports sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable ``` ### Regular Updates ```bash # Update dependencies pip install --upgrade -r requirements.txt # Backup database before updates cp webdav.db webdav.db.backup ``` ## Monitoring and Logging ### Log Files Logs are stored in `./logs/` directory: - `webdav.log` - Application logs - `access.log` - Access logs (requests) - `error.log` - Error logs ### Log Format JSON-structured logs for easy parsing: ```json { "timestamp": "2025-01-15T10:30:45Z", "level": "INFO", "user": "john", "method": "PROPFIND", "path": "/documents/", "status": 207, "duration_ms": 45 } ``` ### Health Check Access the health check endpoint: ```bash curl http://localhost:8080/health ``` ## Troubleshooting ### Windows Explorer Connection Issues **Problem**: "The network folder is no longer available" **Solutions**: 1. Increase Windows WebClient timeout: ``` Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters FileSizeLimitInBytes = DWORD: 0xFFFFFFFF ``` 2. Enable Basic Authentication over HTTP (insecure - use only for testing): ``` Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters BasicAuthLevel = 2 ``` 3. Restart WebClient service: ```powershell net stop webclient net start webclient ``` **Problem**: "The specified server cannot perform the requested operation" **Solution**: Ensure the URL ends with a slash and doesn't include port 80: - ❌ `http://server:80/path` - ✅ `http://server/path/` ### macOS Finder Issues **Problem**: Connection refused or timeout **Solutions**: 1. Use HTTP URL with explicit protocol: ``` http://server:8080/ ``` 2. Check firewall settings on server 3. Try connecting via IP address instead of hostname ### Performance Issues **Problem**: Slow PROPFIND responses **Solutions**: 1. Reduce `MAX_PROPFIND_DEPTH` in `.env`: ```env MAX_PROPFIND_DEPTH=1 ``` 2. Enable caching with Redis: ```env CACHE_ENABLED=true REDIS_HOST=localhost ``` 3. Increase worker processes: ```env WORKERS=8 ``` ### Database Locked Errors **Problem**: "database is locked" errors **Solutions**: 1. Enable WAL mode (automatic in code) 2. Ensure only one process accesses the database 3. Use separate databases for multiple instances ## API Documentation ### WebDAV Methods #### PROPFIND - Property Discovery ```http PROPFIND /documents/ HTTP/1.1 Depth: 1 Content-Type: application/xml ``` #### PROPPATCH - Property Modification ```http PROPPATCH /file.txt HTTP/1.1 Content-Type: application/xml My Document ``` #### LOCK - Resource Locking ```http LOCK /file.txt HTTP/1.1 Timeout: Second-3600 Content-Type: application/xml mailto:john@example.com ``` ## Development ### Running Tests ```bash # Install test dependencies pip install pytest pytest-asyncio pytest-aiohttp pytest-cov # Run tests pytest tests/ -v # Run with coverage pytest tests/ --cov=. --cov-report=html ``` ### Code Style ```bash # Format code black main.py # Lint code flake8 main.py # Type checking mypy main.py ``` ### Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Add tests 5. Submit a pull request ## License MIT License - See LICENSE file for details ## Support For issues, questions, or contributions: - GitHub Issues: [Create an issue] - Documentation: [Wiki] - Community: [Discussions] ## Changelog ### Version 1.0.0 (2025-01-15) - Initial release - Full RFC 4918 compliance - Windows Explorer compatibility - Multi-user support with SQLite backend - Basic and Digest authentication - Resource locking - Custom properties - Production-ready with Gunicorn support ## Acknowledgments - RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV) - aiohttp - Async HTTP client/server framework - Python community --- **Made with ❤️ for the WebDAV community**