Files
eventify_backend/CHANGELOG.md
Sicherhaven e0a491e8cb security: fix GoogleLoginView audience check + replace Clerk with direct GIS flow
- verify_oauth2_token now passes GOOGLE_CLIENT_ID as third arg (audience check)
- fail-closed: returns 503 if GOOGLE_CLIENT_ID env var is not set
- add GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID', '') to settings
- replace ClerkLoginViewTests with GoogleLoginViewTests (4 cases)
- update requirements-docker.txt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:31:18 +05:30

11 KiB
Raw Blame History

Changelog

All notable changes to the Eventify Backend are documented here. Format follows Keep a Changelog, versioning follows Semantic Versioning.


[1.10.0] — 2026-04-10

Security

  • GoogleLoginView audience-check fix (POST /api/user/google-login/) — CRITICAL security patch
    • verify_oauth2_token(token, google_requests.Request()) was called without the third audience argument, meaning any valid Google-signed ID token from any OAuth client was accepted — token spoofing from external apps was trivially possible
    • Fixed to verify_oauth2_token(token, google_requests.Request(), settings.GOOGLE_CLIENT_ID) — only tokens whose aud claim matches our registered Client ID are now accepted
    • Added fail-closed guard: if settings.GOOGLE_CLIENT_ID is empty the view returns HTTP 503 instead of silently accepting all tokens

Changed

  • Removed Clerk scaffolding — the @clerk/react broker approach added in a prior iteration has been replaced with direct Google Identity Services (GIS) ID-token flow on the frontend. Simpler architecture: one trust boundary instead of three.
    • Removed ClerkLoginView, _clerk_jwks_client, _get_clerk_jwks_client() from mobile_api/views/user.py
    • Removed path('user/clerk-login/', ...) from mobile_api/urls.py
    • Removed CLERK_JWKS_URL / CLERK_ISSUER / CLERK_SECRET_KEY from eventify/settings.py; replaced with GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID', '')
    • Removed PyJWT[crypto]>=2.8.0 and requests>=2.31.0 from requirements.txt + requirements-docker.txt (no longer needed; google-auth>=2.0.0 handles verification)

Added

  • Settings: GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID', '') in eventify/settings.py
  • Tests: mobile_api/tests.py::GoogleLoginViewTests — 4 cases: valid token creates user (audience arg verified), missing id_token → 400, ValueError (wrong sig / wrong aud) → 401, existing user reuses DRF token

Context

  • The consumer SPA (app.eventifyplus.com) now loads the Google Identity Services script dynamically and POSTs a Google ID token to the existing /api/user/google-login/ endpoint. Django is the sole session authority. localStorage.event_token / event_user are unchanged.
  • Deploy requirement: set GOOGLE_CLIENT_ID in the Django container .env before deploying — without it the view returns 503 (fail-closed by design).

[1.9.0] — 2026-04-07

Added

  • Lead Manager — new Lead model in admin_api for tracking Schedule-a-Call form submissions and sales inquiries
    • Fields: name, email, phone, event_type, message, status (new/contacted/qualified/converted/closed), source (schedule_call/website/manual), priority (low/medium/high), assigned_to (FK User), notes
    • Migration admin_api/0003_lead with indexes on status, priority, created_at, email
  • Consumer endpoint POST /api/leads/schedule-call/ — public (AllowAny, CSRF-exempt) endpoint for the Schedule a Call modal; creates Lead with status=new, source=schedule_call
  • Admin API endpoints (all IsAuthenticated):
    • GET /api/v1/leads/metrics/ — total, new today, counts per status
    • GET /api/v1/leads/ — paginated list with filters (status, priority, source, search, date_from, date_to)
    • GET /api/v1/leads/<id>/ — single lead detail
    • PATCH /api/v1/leads/<id>/update/ — update status, priority, assigned_to, notes
  • RBAC: leads added to ALL_MODULES, get_allowed_modules(), and StaffProfile.SCOPE_TO_MODULE

[1.8.3] — 2026-04-06

Fixed

  • TopEventsAPI now works without authenticationPOST /api/events/top-events/ had AllowAny permission but still called validate_token_and_get_user(), returning {"status":"error","message":"token and username required"} for unauthenticated requests
    • Removed validate_token_and_get_user() call entirely
    • Added event_status='published' filter (was is_top_event=True only)
    • Added event_type_name field resolution: e.event_type.event_type if e.event_type else ''model_to_dict() only returns the FK integer

[1.8.2] — 2026-04-06

Fixed

  • FeaturedEventsAPI now returns event_type_name stringmodel_to_dict() serialises the event_type FK as an integer ID; the hero slider frontend reads ev.event_type_name to display the category badge, which was always null
    • Added data_dict['event_type_name'] = e.event_type.event_type if e.event_type else '' after model_to_dict(e) to resolve the FK to its human-readable name (e.g. "Festivals")
    • No frontend changes required — fetchHeroSlides() already falls back to ev.event_type_name

[1.8.1] — 2026-04-06

Fixed

  • FeaturedEventsAPI now works without authenticationPOST /api/events/featured-events/ had AllowAny permission but still called validate_token_and_get_user(), causing the endpoint to return HTTP 200 + {"status":"error","message":"token and username required"} for unauthenticated requests (e.g. the desktop hero slider)
    • Removed the validate_token_and_get_user() call entirely — the endpoint is public by design and requires no token
    • Also tightened the queryset to event_status='published' (was is_featured=True only) to match ConsumerFeaturedEventsView behaviour and avoid returning draft/cancelled events
    • Root cause: host Nginx routes /api/eventify-backend container (port 3001), not eventify-django (port 8085); the validate_token_and_get_user gate in this container was silently blocking all hero slider requests

[1.8.0] — 2026-04-04

Added

  • BulkUserPublicInfoView (POST /api/user/bulk-public-info/)
    • Internal endpoint for the Node.js gamification server to resolve user details
    • Accepts { emails: [...] } (max 500), returns { users: { email: { display_name, district, eventify_id } } }
    • Used for leaderboard data bridge (syncing user names/districts into gamification DB)
    • CSRF-exempt, returns only public-safe fields (no passwords, tokens, or sensitive PII)

[1.7.0] — 2026-04-04

Added

  • Home District with 6-month cooldown
    • district_changed_at DateTimeField on User model (migration 0013_user_district_changed_at) — nullable, no backfill; NULL means "eligible to change immediately"
    • VALID_DISTRICTS constant (14 Kerala districts) in accounts/models.py for server-side validation
    • WebRegisterForm now accepts optional district field; stamps district_changed_at on valid selection during signup
    • UpdateProfileView enforces 183-day (~6 months) cooldown — rejects district changes within the window with a human-readable "Next change: {date}" error
    • district_changed_at included in all relevant API responses: LoginView, WebRegisterView, StatusView, UpdateProfileView
    • StatusView now also returns district field (was previously missing)

[1.6.2] — 2026-04-03

Security

  • Internal exceptions no longer exposed to API callers — all 15 except Exception as e blocks across mobile_api/views/user.py and mobile_api/views/events.py now log the real error via eventify_logger and return a generic "An unexpected server error occurred." to the caller
    • Affected views: RegisterView, WebRegisterView, LoginView, StatusView, LogoutView, UpdateProfileView, EventTypeAPI, EventListAPI, EventDetailAPI, EventImagesListAPI, EventsByDateAPI, DateSheetAPI, PincodeEventsAPI, FeaturedEventsAPI, TopEventsAPI
    • StatusView and UpdateProfileView were also missing log(...) calls entirely — added
    • from eventify_logger.services import log import added to events.py (was absent)

[1.6.1] — 2026-04-03

Added

  • eventify_id in StatusView response (/api/user/status/) — consumer app uses this to refresh the Eventify ID badge (EVT-XXXXXXXX) for sessions that pre-date the eventify_id login field
  • accounts migration 0012_user_eventify_id deployed to production containers — backfilled all existing users with unique Eventify IDs; previously the migration existed locally but had not been applied in production

[1.6.0] — 2026-04-02

Added

  • Unique Eventify ID system (EVT-XXXXXXXX format)
    • New eventify_id field on User model — CharField(max_length=12, unique=True, editable=False, db_index=True)
    • Charset ABCDEFGHJKLMNPQRSTUVWXYZ23456789 (no ambiguous characters I/O/0/1) giving ~1.78T combinations
    • Auto-generated on first save() via a 10-attempt retry loop using secrets.choice()
    • Migration 0012_user_eventify_id: add nullable → backfill all existing users → make non-null
  • eventify_id exposed in accounts/api.py_partner_user_to_dict() fields list
  • eventify_id exposed in partner/api.py_user_to_dict() fields list
  • eventify_id exposed in mobile_api/views/user.pyLoginView response (populates localStorage.event_user.eventify_id)
  • eventifyId exposed in admin_api/views.py_serialize_user() (camelCase for direct TypeScript compatibility)
  • Server-side search in UserListView now also filters on eventify_id__icontains
  • Synced migration 0011_user_allowed_modules_alter_user_id (pulled from server, was missing from local repo)

Changed

  • accounts/models.py: merged allowed_modules field + get_allowed_modules() + ALL_MODULES constant from server (previously only existed on server)

[1.5.0] — 2026-03-31

Added

  • allowed_modules TextField on User model — comma-separated module slug access control
  • get_allowed_modules() method on User — returns list of accessible modules based on role or explicit list
  • ALL_MODULES class constant listing all platform module slugs
  • Migration 0011_user_allowed_modules_alter_user_id

[1.4.0] — 2026-03-24

Added

  • Partner portal login/logout APIs (accounts/api.py) — PartnerLoginAPI, PartnerLogoutAPI, PartnerMeAPI
  • _partner_user_to_dict() serializer for partner-scoped user data
  • Partner CRUD, KYC review, and user management endpoints in partner/api.py

[1.3.0] — 2026-03-14

Changed

  • User id field changed from AutoField to BigAutoField (migration 0010_alter_user_id)

[1.2.0] — 2026-03-10

Added

  • partner ForeignKey on User model linking users to partners (migration 0009_user_partner)
  • Profile picture upload support (ImageField) with default.png fallback (migration 00060007)

[1.1.0] — 2026-02-28

Added

  • Location fields on User: pincode, district, state, country, place, latitude, longitude
  • Custom UserManager for programmatic user creation

[1.0.0] — 2026-03-01

Added

  • Initial Django project with custom User model extending AbstractUser
  • Role choices: admin, manager, staff, customer, partner, partner_manager, partner_staff, partner_customer
  • JWT authentication via djangorestframework-simplejwt
  • Admin API foundation: auth, dashboard metrics, partners, users, events
  • Docker + Gunicorn + PostgreSQL 16 production setup