github.com/kyleu/dbaudit@v0.0.2-0.20240321155047-ff2f2c940496/app/controller/cutil/session.go (about) 1 // Package cutil - Content managed by Project Forge, see [projectforge.md] for details. 2 package cutil 3 4 import ( 5 "bytes" 6 "context" 7 "io" 8 "net/http" 9 "slices" 10 "strings" 11 12 "github.com/mileusna/useragent" 13 14 "github.com/kyleu/dbaudit/app" 15 "github.com/kyleu/dbaudit/app/controller/csession" 16 "github.com/kyleu/dbaudit/app/lib/telemetry" 17 "github.com/kyleu/dbaudit/app/lib/telemetry/httpmetrics" 18 "github.com/kyleu/dbaudit/app/lib/user" 19 "github.com/kyleu/dbaudit/app/util" 20 ) 21 22 var ( 23 initialIcons = []string{"searchbox"} 24 MaxBodySize = int64(1024 * 1024 * 128) // 128MB 25 ) 26 27 func LoadPageState(as *app.State, w http.ResponseWriter, r *http.Request, key string, logger util.Logger) *PageState { 28 parentCtx, logger := httpmetrics.ExtractHeaders(r, logger) 29 ctx, span, logger := telemetry.StartSpan(parentCtx, "http:"+key, logger) 30 span.Attribute("path", r.URL.Path) 31 if !telemetry.SkipControllerMetrics { 32 httpmetrics.InjectHTTP(200, r, span) 33 } 34 session, flashes, prof, accts := loadSession(ctx, as, w, r, logger) 35 params := ParamSetFromRequest(r) 36 ua := useragent.Parse(r.Header.Get("User-Agent")) 37 os := strings.ToLower(ua.OS) 38 browser := strings.ToLower(ua.Name) 39 platform := "unknown" 40 switch { 41 case ua.Desktop: 42 platform = "desktop" 43 case ua.Tablet: 44 platform = "tablet" 45 case ua.Mobile: 46 platform = "mobile" 47 case ua.Bot: 48 platform = "bot" 49 } 50 span.Attribute("browser", browser) 51 span.Attribute("os", os) 52 53 isAuthed, _ := user.Check("/", accts) 54 isAdmin, _ := user.Check("/admin", accts) 55 b, _ := io.ReadAll(http.MaxBytesReader(w, r.Body, MaxBodySize)) 56 r.Body = io.NopCloser(bytes.NewBuffer(b)) 57 58 return &PageState{ 59 Action: key, Method: r.Method, URI: r.URL, Flashes: flashes, Session: session, 60 OS: os, OSVersion: ua.OSVersion, Browser: browser, BrowserVersion: ua.Version, Platform: platform, 61 Profile: prof, Accounts: accts, Authed: isAuthed, Admin: isAdmin, Params: params, 62 Icons: slices.Clone(initialIcons), Started: util.TimeCurrent(), Logger: logger, Context: ctx, Span: span, RequestBody: b, 63 } 64 } 65 66 func loadSession(_ context.Context, _ *app.State, w http.ResponseWriter, r *http.Request, logger util.Logger) (util.ValueMap, []string, *user.Profile, user.Accounts) { 67 c, _ := r.Cookie(util.AppKey) 68 if c == nil || c.Value == "" { 69 return util.ValueMap{}, nil, user.DefaultProfile.Clone(), nil 70 } 71 72 dec, err := util.DecryptMessage(nil, c.Value, logger) 73 if err != nil { 74 logger.Warnf("error decrypting session: %+v", err) 75 } 76 session, err := util.FromJSONMap([]byte(dec)) 77 if err != nil { 78 session = util.ValueMap{} 79 } 80 81 flashes := util.StringSplitAndTrim(session.GetStringOpt(csession.WebFlashKey), ";") 82 if len(flashes) > 0 { 83 delete(session, csession.WebFlashKey) 84 err := csession.SaveSession(w, session, logger) 85 if err != nil { 86 logger.Warnf("can't save session: %+v", err) 87 } 88 } 89 90 prof, err := loadProfile(session) 91 if err != nil { 92 logger.Warnf("can't load profile: %+v", err) 93 } 94 95 var accts user.Accounts 96 authX, ok := session[csession.WebAuthKey] 97 if ok { 98 authS, ok := authX.(string) 99 if ok { 100 accts = user.AccountsFromString(authS) 101 } 102 } 103 104 return session, flashes, prof, accts 105 } 106 107 func loadProfile(session util.ValueMap) (*user.Profile, error) { 108 x, ok := session["profile"] 109 if !ok { 110 return user.DefaultProfile.Clone(), nil 111 } 112 s, ok := x.(string) 113 if !ok { 114 m, ok := x.(map[string]any) 115 if !ok { 116 return user.DefaultProfile.Clone(), nil 117 } 118 s = util.ToJSON(m) 119 } 120 p := &user.Profile{} 121 err := util.FromJSON([]byte(s), p) 122 if err != nil { 123 return nil, err 124 } 125 if p.Name == "" { 126 p.Name = user.DefaultProfile.Name 127 } 128 return p, nil 129 }