Skip to content

Commit 0d2dca0

Browse files
authored
Merge pull request #195 from thand-io/profile-nav
Added user nav for multiple sessions
2 parents 0ea099a + cc2560a commit 0d2dca0

6 files changed

Lines changed: 631 additions & 25 deletions

File tree

internal/daemon/server.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,15 @@ func (s *Server) setupRoutes(router *gin.Engine) {
422422
api.GET("/session/:provider", s.getSessionByProvider)
423423
api.POST("/sessions", s.postSession)
424424
api.DELETE("/session/:provider", s.deleteSession)
425+
api.DELETE("/sessions", s.deleteSessions)
425426

426427
} else if s.Config.IsServer() {
427428

429+
// Session management
430+
api.PUT("/sessions", s.putSession)
431+
api.DELETE("/session/:provider", s.deleteSession)
432+
api.DELETE("/sessions", s.deleteSessions)
433+
428434
// Register handlers
429435
api.POST("/preflight", func(c *gin.Context) {
430436
// Just a stub for now

internal/daemon/sessions.go

Lines changed: 239 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import (
44
"net/http"
55
"time"
66

7+
"github.com/gin-contrib/sessions"
78
"github.com/gin-gonic/gin"
89
"github.com/sirupsen/logrus"
910
"github.com/thand-io/agent/internal/common"
1011
"github.com/thand-io/agent/internal/models"
11-
"github.com/thand-io/agent/internal/sessions"
12+
sessionManager "github.com/thand-io/agent/internal/sessions"
1213
)
1314

1415
// postSession creates a new session
@@ -121,8 +122,8 @@ func (s *Server) postSession(c *gin.Context) {
121122
}).Debugln("Creating session")
122123

123124
// Now lets store the session in the users local session manager.
124-
sessionManager := sessions.GetSessionManager()
125-
err = sessionManager.AddSession(
125+
sessionMgr := sessionManager.GetSessionManager()
126+
err = sessionMgr.AddSession(
126127
loginServer,
127128
sessionCreateRequest.Provider,
128129
session,
@@ -146,12 +147,21 @@ func (s *Server) postSession(c *gin.Context) {
146147
// @Tags sessions
147148
// @Accept json
148149
// @Produce json
149-
// @Success 200 {object} sessions.LoginServer "List of sessions"
150+
// @Success 200 {object} models.SessionsResponse "List of sessions with default provider"
150151
// @Failure 400 {object} map[string]any "Bad request"
151152
// @Failure 500 {object} map[string]any "Internal server error"
152153
// @Router /sessions [get]
153154
func (s *Server) getSessions(c *gin.Context) {
154155

156+
// Get the default provider from cookie
157+
defaultProvider := ""
158+
defaultCookie := sessions.DefaultMany(c, ThandCookieName)
159+
if provider := defaultCookie.Get(ThandCookieAttributeActiveName); provider != nil {
160+
if providerStr, ok := provider.(string); ok {
161+
defaultProvider = providerStr
162+
}
163+
}
164+
155165
if s.Config.IsServer() {
156166

157167
remoteSessions, err := s.getUserSessions(c)
@@ -171,13 +181,14 @@ func (s *Server) getSessions(c *gin.Context) {
171181
}
172182
}
173183

174-
sessionsList := sessions.LoginServer{
175-
Version: "1",
176-
Timestamp: time.Now(),
177-
Sessions: foundSessions,
184+
sessionsResponse := models.SessionsResponse{
185+
Version: "1",
186+
Timestamp: time.Now(),
187+
Sessions: foundSessions,
188+
DefaultProvider: defaultProvider,
178189
}
179190

180-
c.JSON(http.StatusOK, sessionsList)
191+
c.JSON(http.StatusOK, sessionsResponse)
181192
return
182193

183194
} else if s.Config.IsAgent() {
@@ -188,16 +199,23 @@ func (s *Server) getSessions(c *gin.Context) {
188199
"loginServer": loginServer,
189200
}).Debugln("Fetching sessions")
190201

191-
sessionManager := sessions.GetSessionManager()
192-
sessionManager.Load(loginServer)
193-
sessionsList, err := sessionManager.GetLoginServer(loginServer)
202+
sessionMgr := sessionManager.GetSessionManager()
203+
sessionMgr.Load(loginServer)
204+
sessionsList, err := sessionMgr.GetLoginServer(loginServer)
194205

195206
if err != nil {
196207
s.getErrorPage(c, http.StatusInternalServerError, "Failed to list sessions", err)
197208
return
198209
}
199210

200-
c.JSON(http.StatusOK, sessionsList)
211+
sessionsResponse := models.SessionsResponse{
212+
Version: sessionsList.Version,
213+
Timestamp: sessionsList.Timestamp,
214+
Sessions: sessionsList.Sessions,
215+
DefaultProvider: defaultProvider,
216+
}
217+
218+
c.JSON(http.StatusOK, sessionsResponse)
201219
return
202220

203221
} else {
@@ -235,9 +253,9 @@ func (s *Server) getSessionByProvider(c *gin.Context) {
235253
"provider": provider,
236254
}).Debugln("Fetching session for provider")
237255

238-
sessionManager := sessions.GetSessionManager()
239-
sessionManager.Load(loginServer)
240-
session, err := sessionManager.GetSession(loginServer, provider)
256+
sessionMgr := sessionManager.GetSessionManager()
257+
sessionMgr.Load(loginServer)
258+
session, err := sessionMgr.GetSession(loginServer, provider)
241259

242260
if err != nil {
243261
s.getErrorPage(c, http.StatusInternalServerError, "Failed to get session", err)
@@ -254,6 +272,75 @@ func (s *Server) getSessionByProvider(c *gin.Context) {
254272
})
255273
}
256274

275+
// putSession sets the default session provider
276+
//
277+
// @Summary Set default session provider
278+
// @Description Update the default session provider for the user
279+
// @Tags sessions
280+
// @Accept json
281+
// @Produce json
282+
// @Param request body models.SessionSetDefaultRequest true "Provider selection request"
283+
// @Success 200 {object} map[string]any "Default provider updated successfully"
284+
// @Failure 400 {object} map[string]any "Bad request"
285+
// @Failure 404 {object} map[string]any "Provider session not found"
286+
// @Failure 500 {object} map[string]any "Internal server error"
287+
// @Router /sessions [put]
288+
func (s *Server) putSession(c *gin.Context) {
289+
290+
// This endpoint can only be called in server mode
291+
if !s.Config.IsServer() {
292+
s.getErrorPage(c, http.StatusBadRequest, "Setting default session can only be called in server mode")
293+
return
294+
}
295+
296+
// Parse the request body to get the provider name
297+
var requestBody models.SessionSetDefaultRequest
298+
if err := c.ShouldBindJSON(&requestBody); err != nil {
299+
s.getErrorPage(c, http.StatusBadRequest, "Failed to parse request body", err)
300+
return
301+
}
302+
303+
provider := requestBody.Provider
304+
305+
logrus.WithFields(logrus.Fields{
306+
"provider": provider,
307+
}).Debugln("Setting default session provider")
308+
309+
// Verify that the user has an active session for this provider
310+
remoteSessions, err := s.getUserSessions(c)
311+
if err != nil {
312+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to verify session", err)
313+
return
314+
}
315+
316+
session, exists := remoteSessions[provider]
317+
if !exists {
318+
s.getErrorPage(c, http.StatusNotFound, "No active session found for provider")
319+
return
320+
}
321+
322+
// Validate that the session is not expired
323+
if session.Expiry.Before(time.Now()) {
324+
s.getErrorPage(c, http.StatusBadRequest, "Session for this provider has expired")
325+
return
326+
}
327+
328+
// Update the default provider cookie
329+
defaultCookie := sessions.DefaultMany(c, ThandCookieName)
330+
defaultCookie.Set(ThandCookieAttributeActiveName, provider)
331+
err = defaultCookie.Save()
332+
333+
if err != nil {
334+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to save default provider", err)
335+
return
336+
}
337+
338+
c.JSON(http.StatusOK, gin.H{
339+
"message": "Default provider updated successfully",
340+
"provider": provider,
341+
})
342+
}
343+
257344
// deleteSession removes a session
258345
//
259346
// @Summary Delete session
@@ -274,15 +361,144 @@ func (s *Server) deleteSession(c *gin.Context) {
274361
return
275362
}
276363

277-
sessionManager := sessions.GetSessionManager()
278-
err := sessionManager.RemoveSession(s.Config.GetLoginServerHostname(), provider)
364+
logrus.WithFields(logrus.Fields{
365+
"provider": provider,
366+
}).Debugln("Deleting session for provider")
279367

280-
if err != nil {
281-
s.getErrorPage(c, http.StatusInternalServerError, "Failed to delete session", err)
368+
if s.Config.IsServer() {
369+
// Server mode: Clear all cookies for this provider
370+
371+
// Clear the provider-specific cookie
372+
providerCookie := sessions.DefaultMany(c, CreateCookieName(provider))
373+
providerCookie.Clear()
374+
err := providerCookie.Save()
375+
376+
if err != nil {
377+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to clear provider cookie", err)
378+
return
379+
}
380+
381+
// If this was the default provider, clear the default setting
382+
defaultCookie := sessions.DefaultMany(c, ThandCookieName)
383+
if activeProvider := defaultCookie.Get(ThandCookieAttributeActiveName); activeProvider != nil {
384+
if activeProviderStr, ok := activeProvider.(string); ok && activeProviderStr == provider {
385+
defaultCookie.Delete(ThandCookieAttributeActiveName)
386+
err = defaultCookie.Save()
387+
if err != nil {
388+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to clear default provider", err)
389+
return
390+
}
391+
}
392+
}
393+
394+
c.JSON(http.StatusOK, gin.H{
395+
"message": "Session deleted successfully",
396+
})
397+
return
398+
399+
} else if s.Config.IsAgent() {
400+
// Agent mode: Delete local session via session manager
401+
sessionMgr := sessionManager.GetSessionManager()
402+
err := sessionMgr.RemoveSession(s.Config.GetLoginServerHostname(), provider)
403+
404+
if err != nil {
405+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to delete session", err)
406+
return
407+
}
408+
409+
c.JSON(http.StatusOK, gin.H{
410+
"message": "Session deleted successfully",
411+
})
412+
return
413+
414+
} else {
415+
s.getErrorPage(c, http.StatusBadRequest, "Delete session can only be called in agent or server mode")
282416
return
283417
}
418+
}
284419

285-
c.JSON(http.StatusOK, gin.H{
286-
"message": "Session deleted successfully",
287-
})
420+
// deleteSessions removes all sessions
421+
//
422+
// @Summary Delete all sessions
423+
// @Description Remove all sessions for the user
424+
// @Tags sessions
425+
// @Accept json
426+
// @Produce json
427+
// @Success 200 {object} map[string]any "All sessions deleted successfully"
428+
// @Failure 400 {object} map[string]any "Bad request"
429+
// @Failure 500 {object} map[string]any "Internal server error"
430+
// @Router /sessions [delete]
431+
func (s *Server) deleteSessions(c *gin.Context) {
432+
433+
logrus.Debugln("Deleting all sessions")
434+
435+
if s.Config.IsServer() {
436+
// Server mode: Get all user sessions and clear all cookies
437+
438+
remoteSessions, err := s.getUserSessions(c)
439+
if err != nil {
440+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to get user sessions", err)
441+
return
442+
}
443+
444+
// Clear provider-specific cookies for each session
445+
for providerName := range remoteSessions {
446+
providerCookie := sessions.DefaultMany(c, CreateCookieName(providerName))
447+
providerCookie.Clear()
448+
err := providerCookie.Save()
449+
if err != nil {
450+
logrus.WithFields(logrus.Fields{
451+
"provider": providerName,
452+
"error": err,
453+
}).Warnln("Failed to clear provider cookie")
454+
}
455+
}
456+
457+
// Clear the default provider setting
458+
defaultCookie := sessions.DefaultMany(c, ThandCookieName)
459+
defaultCookie.Clear()
460+
err = defaultCookie.Save()
461+
if err != nil {
462+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to clear default cookie", err)
463+
return
464+
}
465+
466+
c.JSON(http.StatusOK, gin.H{
467+
"message": "All sessions deleted successfully",
468+
})
469+
return
470+
471+
} else if s.Config.IsAgent() {
472+
// Agent mode: Delete all local sessions via session manager
473+
loginServer := s.Config.GetLoginServerHostname()
474+
sessionMgr := sessionManager.GetSessionManager()
475+
sessionMgr.Load(loginServer)
476+
477+
// Get all sessions
478+
loginServerData, err := sessionMgr.GetLoginServer(loginServer)
479+
if err != nil {
480+
s.getErrorPage(c, http.StatusInternalServerError, "Failed to get sessions", err)
481+
return
482+
}
483+
484+
// Delete each session
485+
for providerName := range loginServerData.Sessions {
486+
err := sessionMgr.RemoveSession(loginServer, providerName)
487+
if err != nil {
488+
logrus.WithFields(logrus.Fields{
489+
"provider": providerName,
490+
"error": err,
491+
}).Warnln("Failed to delete session")
492+
}
493+
}
494+
495+
c.JSON(http.StatusOK, gin.H{
496+
"message": "All sessions deleted successfully",
497+
})
498+
return
499+
500+
} else {
501+
s.getErrorPage(c, http.StatusBadRequest, "Delete sessions can only be called in agent or server mode")
502+
return
503+
}
288504
}

0 commit comments

Comments
 (0)