3737
3838
3939class ScheduleMixin :
40+ MY_STARRED_ICS_TOKEN_SESSION_KEY = 'my_starred_ics_token'
41+
4042 @cached_property
4143 def version (self ):
4244 if version := self .kwargs .get ("version" ):
@@ -69,19 +71,27 @@ def dispatch(self, request, *args, **kwargs):
6971 return super ().dispatch (request , * args , ** kwargs )
7072
7173 @staticmethod
72- def generate_ics_token (user_id ):
73- """Generate a signed token with user ID and 15-day expiry"""
74+ def generate_ics_token (request , user_id ):
75+ """Generate a signed token with user ID and 15-day expiry, invalidating previous tokens"""
76+ # Clear any existing token from the session
77+ key = ScheduleMixin .MY_STARRED_ICS_TOKEN_SESSION_KEY
78+ if key in request .session :
79+ del request .session [key ]
80+
81+ # Generate new token
7482 expiry = timezone .now () + timedelta (days = 15 )
7583 value = {"user_id" : user_id , "exp" : int (expiry .timestamp ())}
76- return signing .dumps (value , salt = "my-starred-ics" )
84+ token = signing .dumps (value , salt = "my-starred-ics" )
85+
86+ # Store new token in session
87+ request .session [key ] = token
88+ return token
7789
7890 @staticmethod
7991 def parse_ics_token (token ):
8092 """Parse and validate the token, return user_id if valid"""
8193 try :
8294 value = signing .loads (token , salt = "my-starred-ics" , max_age = 15 * 24 * 60 * 60 )
83- if value ["exp" ] < int (timezone .now ().timestamp ()):
84- raise ValueError ("Token expired" )
8595 return value ["user_id" ]
8696 except (signing .BadSignature , signing .SignatureExpired , KeyError , ValueError ) as e :
8797 logger .warning ('Failed to parse ICS token: %s' , e )
@@ -97,7 +107,7 @@ def check_token_expiry(token):
97107 - True if token is valid and not expiring soon (>= 4 days)
98108 """
99109 try :
100- value = signing .loads (token , salt = "my-starred-ics" )
110+ value = signing .loads (token , salt = "my-starred-ics" , max_age = 15 * 24 * 60 * 60 )
101111 expiry_date = timezone .datetime .fromtimestamp (value ["exp" ], tz = timezone .utc )
102112 time_until_expiry = expiry_date - timezone .now ()
103113 return time_until_expiry >= timedelta (days = 4 )
@@ -357,7 +367,6 @@ class ChangelogView(EventPermissionRequired, TemplateView):
357367
358368class CalendarRedirectView (EventPermissionRequired , ScheduleMixin , TemplateView ):
359369 """Handles redirects for both Google Calendar and other calendar applications"""
360- MY_STARRED_ICS_TOKEN_SESSION_KEY = 'my_starred_ics_token'
361370 permission_required = "agenda.view_schedule"
362371
363372 def get (self , request , * args , ** kwargs ):
@@ -371,7 +380,8 @@ def get(self, request, *args, **kwargs):
371380 if is_my :
372381 # For starred sessions
373382 if not request .user .is_authenticated :
374- return HttpResponseRedirect (self .request .event .urls .login )
383+ login_url = f"{ self .request .event .urls .login } ?next={ request .get_full_path ()} "
384+ return HttpResponseRedirect (login_url )
375385
376386 # Check for existing valid token
377387 existing_token = request .session .get (self .MY_STARRED_ICS_TOKEN_SESSION_KEY )
@@ -380,14 +390,13 @@ def get(self, request, *args, **kwargs):
380390 # If we have an existing token, check if it's still valid and not expiring soon
381391 if existing_token :
382392 token_status = self .check_token_expiry (existing_token )
383- if token_status is True :
393+ if token_status is True : # Token is valid and has at least 4 days left
384394 token = existing_token
385395 generate_new_token = False
386396
387- # Generate new token if needed
397+ # Generate new token if needed (this will invalidate any existing token)
388398 if generate_new_token :
389- token = self .generate_ics_token (request .user .id )
390- request .session [self .MY_STARRED_ICS_TOKEN_SESSION_KEY ] = token
399+ token = self .generate_ics_token (request , request .user .id )
391400
392401 # Build tokenized URL for starred sessions
393402 ics_url = request .build_absolute_uri (
@@ -426,4 +435,4 @@ def get(self, request, *args, **kwargs):
426435 f'<body><p style="text-align: center; padding:2vw; font-family: Roboto,Helvetica Neue,HelveticaNeue,Helvetica,Arial,sans-serif;">Redirecting to: { webcal_url } </p><script>window.location.href="{ webcal_url } ";</script></body></html>' ,
427436 content_type = 'text/html'
428437 )
429- return response
438+ return response
0 commit comments