# Calendar Sync Permission - Quick Reference ## What Was Added A permission gating system for calendar sync features in the Django backend. ## Key Components ### 1. Database Field ```python # core/models.py - Added to Tenant model can_use_calendar_sync = models.BooleanField(default=False) ``` ### 2. Permission Check Factory ```python # core/permissions.py - Added to FEATURE_NAMES 'can_use_calendar_sync': 'Calendar Sync', ``` ### 3. OAuth Integration ```python # core/oauth_views.py - Check when purpose is 'calendar' if purpose == 'calendar': calendar_permission = HasFeaturePermission('can_use_calendar_sync') if not calendar_permission().has_permission(request, self): return Response({'error': 'Feature not available'}, status=403) ``` ### 4. Calendar Sync Views ```python # schedule/calendar_sync_views.py CalendarListView # GET /api/calendar/list/ CalendarSyncView # POST /api/calendar/sync/ CalendarDeleteView # DELETE /api/calendar/disconnect/ CalendarStatusView # GET /api/calendar/status/ ``` ## How to Use ### Enable for a Tenant ```bash # Via Django shell from core.models import Tenant tenant = Tenant.objects.get(schema_name='demo') tenant.can_use_calendar_sync = True tenant.save() ``` ### Use in ViewSet ```python from rest_framework import viewsets from core.permissions import HasFeaturePermission class MyViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated, HasFeaturePermission('can_use_calendar_sync')] ``` ### Use in APIView ```python from rest_framework.views import APIView class MyView(APIView): permission_classes = [CalendarSyncPermission] # CalendarSyncPermission = IsAuthenticated + has_feature check ``` ## API Endpoints | Method | Endpoint | Description | Permission | |--------|----------|-------------|-----------| | GET | /api/calendar/status/ | Check if calendar sync is available | Auth only | | GET | /api/calendar/list/ | List connected calendars | Calendar sync | | POST | /api/calendar/sync/ | Start calendar sync | Calendar sync | | DELETE | /api/calendar/disconnect/ | Disconnect a calendar | Calendar sync | | POST | /api/oauth/google/initiate/ | Start Google OAuth for calendar | Calendar sync (if purpose=calendar) | | POST | /api/oauth/microsoft/initiate/ | Start MS OAuth for calendar | Calendar sync (if purpose=calendar) | ## Testing ### Run tests ```bash cd /home/poduck/Desktop/smoothschedule2/smoothschedule docker compose -f docker-compose.local.yml exec django pytest schedule/tests/test_calendar_sync_permissions.py -v ``` ### Test endpoints manually ```bash # Check status (always works) curl http://lvh.me:8000/api/calendar/status/ -H "Authorization: Bearer " # List calendars (requires permission) curl http://lvh.me:8000/api/calendar/list/ -H "Authorization: Bearer " # Returns 403 if permission not granted ``` ## Files Modified | File | Changes | |------|---------| | core/models.py | Added can_use_calendar_sync field | | core/permissions.py | Added to FEATURE_NAMES | | core/oauth_views.py | Added permission check for calendar | ## Files Created | File | Purpose | |------|---------| | core/migrations/0016_tenant_can_use_calendar_sync.py | Database migration | | schedule/calendar_sync_views.py | Calendar sync API views | | schedule/calendar_sync_urls.py | URL routing | | schedule/tests/test_calendar_sync_permissions.py | Test suite | | CALENDAR_SYNC_INTEGRATION.md | Developer guide | ## Permission Check Pattern ``` Request to calendar endpoint ↓ Check: Is user authenticated? ├─ NO → 401 Unauthorized └─ YES ↓ Check: Does tenant have can_use_calendar_sync=True? ├─ NO → 403 Forbidden (upgrade message) └─ YES ↓ Process request ├─ Success → 200 OK └─ Error → 500 Server Error ``` ## Example: Full Permission Setup ```python # 1. Enable feature for tenant from core.models import Tenant tenant = Tenant.objects.get(schema_name='demo') tenant.can_use_calendar_sync = True tenant.save() # 2. User tries to access calendar endpoint # GET /api/calendar/list/ # → Check: tenant.has_feature('can_use_calendar_sync') # → True! → 200 OK with calendar list # 3. Without permission tenant.can_use_calendar_sync = False tenant.save() # GET /api/calendar/list/ # → Check: tenant.has_feature('can_use_calendar_sync') # → False! → 403 Forbidden with upgrade message ``` ## Related Documentation - **Full Guide:** `CALENDAR_SYNC_INTEGRATION.md` in smoothschedule/ folder - **Implementation Details:** `CALENDAR_SYNC_PERMISSION_IMPLEMENTATION.md` in project root - **Code:** `schedule/calendar_sync_views.py` (well-commented) - **Tests:** `schedule/tests/test_calendar_sync_permissions.py` ## Common Tasks ### Check if feature is enabled ```python tenant.has_feature('can_use_calendar_sync') # Returns bool ``` ### Get list of connected calendars ```python from core.models import OAuthCredential credentials = OAuthCredential.objects.filter( tenant=tenant, purpose='calendar', is_valid=True ) ``` ### Handle permission denied ```python from core.permissions import HasFeaturePermission permission = HasFeaturePermission('can_use_calendar_sync') if not permission().has_permission(request, view): # User doesn't have permission # Show upgrade prompt ``` ## Notes - Feature defaults to **False** for all tenants (opt-in) - Works alongside existing subscription plan system - Follows same pattern as SMS reminders, webhooks, etc. - Multi-tenant isolation built-in - OAuth tokens are encrypted at rest - All operations logged for audit trail