diff --git a/api/users.go b/api/users.go index f72338f..1f4aa48 100644 --- a/api/users.go +++ b/api/users.go @@ -29,6 +29,7 @@ import ( "net/http" "strconv" "strings" + "time" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" @@ -36,6 +37,7 @@ import ( "git.happydns.org/happyDomain/actions" "git.happydns.org/happyDomain/config" + "git.happydns.org/happyDomain/internal/session" "git.happydns.org/happyDomain/model" "git.happydns.org/happyDomain/storage" ) @@ -60,7 +62,9 @@ func declareUsersAuthRoutes(opts *config.Options, router *gin.RouterGroup) { router.DELETE("/session", clearSession) router.GET("/sessions", getSessions) + router.POST("/sessions", createSession) apiSessionsRoutes := router.Group("/session/:sid") + apiSessionsRoutes.PUT("", updateSession) apiSessionsRoutes.DELETE("", deleteSession) apiUserRoutes := router.Group("/users/:uid") @@ -786,6 +790,94 @@ func getSessions(c *gin.Context) { c.JSON(http.StatusOK, s) } +// createSession create a new session for the current user +// +// @Summary Create a new session for the current user. +// @Schemes +// @Description Create a new session for the current user. +// @Tags users +// @Accept json +// @Produce json +// @Security securitydefinitions.basic +// @Success 200 {object} happydns.Session +// @Failure 401 {object} happydns.Error "Authentication failure" +// @Router /sessions [post] +func createSession(c *gin.Context) { + var us happydns.Session + err := c.ShouldBindJSON(&us) + if err != nil { + log.Printf("%s sends invalid Session JSON: %s", c.ClientIP(), err.Error()) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Something is wrong in received data: %s", err.Error())}) + return + } + + myuser := c.MustGet("LoggedUser").(*happydns.User) + + sessid := session.NewSessionId() + mysession := &happydns.Session{ + Id: sessid, + IdUser: myuser.Id, + Description: us.Description, + IssuedAt: time.Now(), + ExpiresOn: time.Now().Add(24 * 365 * time.Hour), + } + + err = storage.MainStore.UpdateSession(mysession) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"id": sessid}) +} + +// updateSession update a session owned by the current user +// +// @Summary Update a session owned by the current user. +// @Schemes +// @Description Update a session owned by the current user. +// @Tags users +// @Accept json +// @Param sessionId path string true "Session identifier" +// @Produce json +// @Security securitydefinitions.basic +// @Success 200 {object} happydns.Session +// @Failure 401 {object} happydns.Error "Authentication failure" +// @Router /sessions/{sessionId} [put] +func updateSession(c *gin.Context) { + var us happydns.Session + err := c.ShouldBindJSON(&us) + if err != nil { + log.Printf("%s sends invalid Session JSON: %s", c.ClientIP(), err.Error()) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Something is wrong in received data: %s", err.Error())}) + return + } + + myuser := c.MustGet("LoggedUser").(*happydns.User) + + s, err := storage.MainStore.GetSession(c.Param("sid")) + if err != nil { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": err.Error()}) + return + } + + if !myuser.Id.Equals(s.IdUser) { + c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "You are not allowed to update this session."}) + return + } + + s.Description = us.Description + s.ExpiresOn = us.ExpiresOn + + err = storage.MainStore.UpdateSession(s) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + c.JSON(http.StatusOK, s) +} + // deleteSession delete a session owned by the current user // // @Summary Delete a session owned by the current user. diff --git a/internal/session/sessions.go b/internal/session/sessions.go index 97ad317..003d6a4 100644 --- a/internal/session/sessions.go +++ b/internal/session/sessions.go @@ -107,7 +107,7 @@ func (s *SessionStore) Save(r *http.Request, w http.ResponseWriter, session *ses s.storage.DeleteSession(session.ID) } else { if session.ID == "" { - session.ID = strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)), "=") + session.ID = NewSessionId() } encrypted, err := securecookie.EncodeMulti(session.Name(), session.ID, s.Codecs...) if err != nil { @@ -145,7 +145,25 @@ func (s *SessionStore) load(session *sessions.Session) error { return err } - return securecookie.DecodeMulti(session.Name(), mysession.Content, &session.Values, s.Codecs...) + err = securecookie.DecodeMulti(session.Name(), mysession.Content, &session.Values, s.Codecs...) + if err != nil { + return err + } + + if len(mysession.IdUser) > 0 { + session.Values["iduser"] = []byte(mysession.IdUser) + } + if len(mysession.Description) > 0 { + session.Values["description"] = mysession.Description + } + if _, ok := session.Values["created_on"].(time.Time); !ok && !mysession.IssuedAt.IsZero() { + session.Values["created_on"] = mysession.IssuedAt + } + if !mysession.ExpiresOn.IsZero() { + session.Values["expires_on"] = mysession.ExpiresOn + } + + return nil } // save writes encoded session.Values to a database record. @@ -200,3 +218,7 @@ func (s *SessionStore) save(session *sessions.Session, ua string) error { return s.storage.UpdateSession(mysession) } + +func NewSessionId() string { + return strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(64)), "=") +}