From 23fd877a9891b53bbfde0f08ff8669155b108ad0 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Mon, 21 Oct 2024 16:04:58 -0500 Subject: [PATCH 1/3] start simple session manager --- controllers/authentication.go | 26 +++++ routes.go | 3 + storage/templates/appsession.html | 12 ++ utils/session.go | 178 ++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 storage/templates/appsession.html create mode 100644 utils/session.go diff --git a/controllers/authentication.go b/controllers/authentication.go index a58b71d..4830a2d 100644 --- a/controllers/authentication.go +++ b/controllers/authentication.go @@ -468,6 +468,32 @@ func AppLogin(c *core.Context) *core.Response { return c.Response.Template("login.html", tmplData) } +// Show basic app login +func AppSession(c *core.Context) *core.Response { + + var session = new(utils.SessionUser) + + hassession := session.Init(c) + + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{} + + if hassession { + + return c.Response.Template("appsession.html", tmplData) + + } else { + + return c.Response.Template("login.html", tmplData) + + } + +} + // Show basic app sample func AppSample(c *core.Context) *core.Response { diff --git a/routes.go b/routes.go index 18c72be..610fbcb 100644 --- a/routes.go +++ b/routes.go @@ -41,4 +41,7 @@ func registerRoutes() { controller.Get("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) controller.Post("/applogin", controllers.AppLogin, hooks.CheckSessionCookie) + + controller.Get("/appsession", controllers.AppSession) + controller.Post("/appsession", controllers.AppSession) } diff --git a/storage/templates/appsession.html b/storage/templates/appsession.html new file mode 100644 index 0000000..32364f0 --- /dev/null +++ b/storage/templates/appsession.html @@ -0,0 +1,12 @@ + + + {{template "page_head" "Sample page test session vars"}} + +
+
+ {{template "page_card" .PageCard}} +
+
+ {{template "page_footer"}} + + \ No newline at end of file diff --git a/utils/session.go b/utils/session.go new file mode 100644 index 0000000..c792ba5 --- /dev/null +++ b/utils/session.go @@ -0,0 +1,178 @@ +// Copyright (c) 2024 Zeni Kim +// Use of this source code is governed by MIT-style +// license that can be found in the LICENSE file. + +package utils + +import ( + "crypto/md5" + "errors" + "fmt" + "sync" + "time" + + "encoding/json" + + "git.smarteching.com/goffee/core" + "git.smarteching.com/goffee/cup/models" + "gorm.io/gorm" +) + +type SessionUser struct { + mu sync.RWMutex + context *core.Context + userID uint + hashedSessionKey string + authenticated bool + sessionStart time.Time + values map[string]interface{} +} + +// start the struct +func (s *SessionUser) Init(c *core.Context) bool { + + // check session cookie + pass := true + token := "" + s.context = c + + payload := make(map[string]interface{}) + + usercookie, err := c.GetCookie() + if err != nil { + + } + + token = usercookie.Token + + if token == "" { + + pass = false + + } else { + + payload, err = c.GetJWT().DecodeToken(token) + + if err != nil { + pass = false + } else { + + userID := uint(c.CastToInt(payload["userID"])) + userAgent := c.GetUserAgent() + hashedCacheKey := CreateAuthTokenHashedCacheKey(userID, userAgent) + + cachedToken, err := c.GetCache().Get(hashedCacheKey) + + if err != nil { + pass = false + } else if cachedToken != token { + pass = false + } else { + var user models.User + res := c.GetGorm().Where("id = ?", userID).First(&user) + if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) { + pass = false + } + + // if have session start the struct + if pass { + + userAgent := c.GetUserAgent() + sessionKey := fmt.Sprintf("session%v_%v", userID, userAgent) + s.hashedSessionKey = fmt.Sprintf("%v", fmt.Sprintf("%x", md5.Sum([]byte(sessionKey)))) + value, err := c.GetCache().Get(s.hashedSessionKey) + + if err != nil { + s.values = make(map[string]interface{}) + s.authenticated = true + s.userID = userID + + if len(value) > 0 { + err := json.Unmarshal([]byte(value), &s.values) + if err != nil { + + } + } else { + + } + } + + return true + + } else { + + s.hashedSessionKey = "" + s.authenticated = false + s.userID = 0 + return false + } + } + } + } + + return false +} + +func (s *SessionUser) Set(key string, value interface{}) error { + s.mu.Lock() + s.values[key] = value + s.mu.Unlock() + return s.Save() +} + +func (s *SessionUser) Get(key string) (interface{}, bool) { + s.mu.RLock() + defer s.mu.RUnlock() + val, ok := s.values[key] + return val, ok +} + +func (s *SessionUser) Delete(key string) interface{} { + s.mu.RLock() + v, ok := s.values[key] + s.mu.RUnlock() + if ok { + s.mu.Lock() + delete(s.values, key) + s.mu.Unlock() + } + s.Save() + return v +} + +func (s *SessionUser) Flush() error { + s.mu.Lock() + s.values = make(map[string]interface{}) + s.mu.Unlock() + return s.Save() +} + +func (s *SessionUser) Save() error { + + var value string + + s.mu.RLock() + + if len(s.values) > 0 { + buf, err := json.Marshal(&s.values) + if err != nil { + s.mu.RUnlock() + return err + } + value = string(buf) + + } + + s.mu.RUnlock() + + if len(value) > 0 { + s.context.GetCache().Set(s.hashedSessionKey, value) + } + + return nil +} + +func (s *SessionUser) GetUserID() uint { + + return s.userID +} -- 2.39.2 From 3c1170bd8702aeacdea04bc61d1da9edc0c2de6e Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Mon, 21 Oct 2024 20:44:04 -0500 Subject: [PATCH 2/3] sessions review --- utils/session.go | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/utils/session.go b/utils/session.go index c792ba5..27e202d 100644 --- a/utils/session.go +++ b/utils/session.go @@ -5,7 +5,6 @@ package utils import ( - "crypto/md5" "errors" "fmt" "sync" @@ -37,7 +36,7 @@ func (s *SessionUser) Init(c *core.Context) bool { s.context = c payload := make(map[string]interface{}) - + // get cookie usercookie, err := c.GetCookie() if err != nil { @@ -54,13 +53,16 @@ func (s *SessionUser) Init(c *core.Context) bool { payload, err = c.GetJWT().DecodeToken(token) if err != nil { + pass = false + } else { userID := uint(c.CastToInt(payload["userID"])) userAgent := c.GetUserAgent() - hashedCacheKey := CreateAuthTokenHashedCacheKey(userID, userAgent) + // get data from redis + hashedCacheKey := CreateAuthTokenHashedCacheKey(userID, userAgent) cachedToken, err := c.GetCache().Get(hashedCacheKey) if err != nil { @@ -73,28 +75,19 @@ func (s *SessionUser) Init(c *core.Context) bool { if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) { pass = false } - // if have session start the struct if pass { - userAgent := c.GetUserAgent() - sessionKey := fmt.Sprintf("session%v_%v", userID, userAgent) - s.hashedSessionKey = fmt.Sprintf("%v", fmt.Sprintf("%x", md5.Sum([]byte(sessionKey)))) - value, err := c.GetCache().Get(s.hashedSessionKey) + sessionKey := fmt.Sprintf("sess_%v", userAgent) + s.hashedSessionKey = CreateAuthTokenHashedCacheKey(userID, sessionKey) - if err != nil { - s.values = make(map[string]interface{}) - s.authenticated = true - s.userID = userID + s.values = make(map[string]interface{}) + s.authenticated = true + s.userID = userID + value, _ := c.GetCache().Get(s.hashedSessionKey) - if len(value) > 0 { - err := json.Unmarshal([]byte(value), &s.values) - if err != nil { - - } - } else { - - } + if len(value) > 0 { + _ = json.Unmarshal([]byte(value), &s.values) } return true @@ -142,9 +135,9 @@ func (s *SessionUser) Delete(key string) interface{} { func (s *SessionUser) Flush() error { s.mu.Lock() - s.values = make(map[string]interface{}) + s.context.GetCache().Delete(s.hashedSessionKey) s.mu.Unlock() - return s.Save() + return nil } func (s *SessionUser) Save() error { @@ -160,15 +153,14 @@ func (s *SessionUser) Save() error { return err } value = string(buf) - } - s.mu.RUnlock() - if len(value) > 0 { s.context.GetCache().Set(s.hashedSessionKey, value) + } else { + s.context.GetCache().Delete(s.hashedSessionKey) } - + s.mu.RUnlock() return nil } -- 2.39.2 From ba49afe04a45b517b69ea9c4c3aa4f2bb19bbd12 Mon Sep 17 00:00:00 2001 From: Zeni Kim Date: Wed, 23 Oct 2024 07:09:27 -0500 Subject: [PATCH 3/3] reorder files --- controllers/authentication.go | 74 ++---------------------- controllers/sample.go | 103 ++++++++++++++++++++++++++++++++++ controllers/themedemo.go | 8 +-- 3 files changed, 113 insertions(+), 72 deletions(-) diff --git a/controllers/authentication.go b/controllers/authentication.go index 4830a2d..25c7b31 100644 --- a/controllers/authentication.go +++ b/controllers/authentication.go @@ -22,8 +22,6 @@ import ( "git.smarteching.com/goffee/cup/utils" "github.com/google/uuid" "gorm.io/gorm" - - "git.smarteching.com/goffee/core/template/components" ) func Signup(c *core.Context) *core.Response { @@ -223,6 +221,12 @@ func Signin(c *core.Context) *core.Response { userAgent := c.GetUserAgent() hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent) err = c.GetCache().Set(hashedCacheKey, token) + + // delete data from old sessions + sessionKey := fmt.Sprintf("sess_%v", userAgent) + hashedSessionKey := utils.CreateAuthTokenHashedCacheKey(user.ID, sessionKey) + _ = c.GetCache().Delete(hashedSessionKey) + if err != nil { c.GetLogger().Error(err.Error()) if TemplateEnable { @@ -448,69 +452,3 @@ func Signout(c *core.Context) *core.Response { "message": "signed out successfully", })) } - -// Show basic app login -func AppLogin(c *core.Context) *core.Response { - - // first, include all compoments - // first, include all compoments - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Card title", - CardBody: "Loerm ipsum at deim", - }, - } - return c.Response.Template("login.html", tmplData) -} - -// Show basic app login -func AppSession(c *core.Context) *core.Response { - - var session = new(utils.SessionUser) - - hassession := session.Init(c) - - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{} - - if hassession { - - return c.Response.Template("appsession.html", tmplData) - - } else { - - return c.Response.Template("login.html", tmplData) - - } - -} - -// Show basic app sample -func AppSample(c *core.Context) *core.Response { - - // first, include all compoments - type templateData struct { - PageCard components.PageCard - } - - // now fill data of the components - tmplData := templateData{ - PageCard: components.PageCard{ - CardTitle: "Protected page", - CardBody: "If you can see this page, your are loggedin", - }, - } - //fmt.Printf("Outside cookie user is: %s", user.Email) - - return c.Response.Template("app.html", tmplData) - -} diff --git a/controllers/sample.go b/controllers/sample.go index 1243d5d..bc06390 100644 --- a/controllers/sample.go +++ b/controllers/sample.go @@ -5,8 +5,11 @@ package controllers import ( + "fmt" + "git.smarteching.com/goffee/core" "git.smarteching.com/goffee/core/template/components" + "git.smarteching.com/goffee/cup/utils" ) // Show basic template @@ -28,3 +31,103 @@ func Sample(c *core.Context) *core.Response { return c.Response.Template("basic.html", tmplData) } + +// Show basic app login +func AppLogin(c *core.Context) *core.Response { + + // first, include all compoments + // first, include all compoments + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{ + PageCard: components.PageCard{ + CardTitle: "Card title", + CardBody: "Loerm ipsum at deim", + }, + } + return c.Response.Template("login.html", tmplData) +} + +// Show basic app login +func AppSession(c *core.Context) *core.Response { + + var session = new(utils.SessionUser) + + // true if session is active + hassession := session.Init(c) + + //session.Set("numberdos", 66) + + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{} + + if hassession { + + sesiondata := "" + cardtitle := fmt.Sprintf("Session user id: %v", session.GetUserID()) + numberdos, ok := session.Get("numberdos") + + if numberdos != nil { + numberdos = numberdos.(float64) + 10 + } else { + numberdos = 10 + } + + session.Set("numberdos", numberdos) + + if ok { + sesiondata = fmt.Sprintf("OK, Session numberdos has %v", numberdos) + } else { + sesiondata = fmt.Sprintf("No ok, session numberdos has %v", numberdos) + } + + // delete single + //session.Delete("numberdos") + + // delete all data + //session.Flush() + + tmplData = templateData{ + PageCard: components.PageCard{ + CardTitle: cardtitle, + CardBody: sesiondata, + }, + } + + return c.Response.Template("appsession.html", tmplData) + + } else { + + return c.Response.Template("login.html", tmplData) + + } + +} + +// Show basic app sample +func AppSample(c *core.Context) *core.Response { + + // first, include all compoments + type templateData struct { + PageCard components.PageCard + } + + // now fill data of the components + tmplData := templateData{ + PageCard: components.PageCard{ + CardTitle: "Protected page", + CardBody: "If you can see this page, your are loggedin", + }, + } + //fmt.Printf("Outside cookie user is: %s", user.Email) + + return c.Response.Template("app.html", tmplData) + +} diff --git a/controllers/themedemo.go b/controllers/themedemo.go index 1373052..22ba5a5 100644 --- a/controllers/themedemo.go +++ b/controllers/themedemo.go @@ -114,10 +114,10 @@ func Themeform(c *core.Context) *core.Response { optionc.Value = "buenosaires" optionc.Label = "Buenos Aires" allOptionsc = append(allOptionsc, optionc) - optionc.ID = "sogas" - optionc.Name = "sogas" - optionc.Value = "Sogamoso" - optionc.Label = "Sogamoso" + optionc.ID = "london" + optionc.Name = "london" + optionc.Value = "london" + optionc.Label = "London" //optionc.IsChecked = true allOptionsc = append(allOptionsc, optionc) -- 2.39.2