package handlers import ( "context" "crypto/hmac" "crypto/sha256" "encoding/base64" "errors" "golias/models" "net/http" "time" ) // LogRequests logs all HTTP requests with method, path and duration func LogRequests(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() // Wrap response writer to capture status code next.ServeHTTP(w, r) duration := time.Since(start) log.Debug("request completed", "method", r.Method, "path", r.URL.RequestURI(), "duration", duration.String(), ) }) } func GetSession(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // TODO: move cookieName := "session_token" sessionCookie, err := r.Cookie(cookieName) if err != nil { msg := "auth failed; failed to get session token from cookies" log.Debug(msg, "error", err) next.ServeHTTP(w, r) return } cookieValueB, err := base64.URLEncoding. DecodeString(sessionCookie.Value) if err != nil { msg := "auth failed; failed to decode b64 cookie" log.Debug(msg, "error", err) next.ServeHTTP(w, r) return } cookieValue := string(cookieValueB) if len(cookieValue) < sha256.Size { log.Warn("small cookie", "size", len(cookieValue)) next.ServeHTTP(w, r) return } // Split apart the signature and original cookie value. signature := cookieValue[:sha256.Size] sessionToken := cookieValue[sha256.Size:] //verify signature mac := hmac.New(sha256.New, []byte(cfg.CookieSecret)) mac.Write([]byte(cookieName)) mac.Write([]byte(sessionToken)) expectedSignature := mac.Sum(nil) if !hmac.Equal([]byte(signature), expectedSignature) { log.Debug("cookie with an invalid sign") next.ServeHTTP(w, r) return } userSession, err := cacheGetSession(sessionToken) log.Debug("userSession from cache", "us", userSession) if err != nil { msg := "auth failed; session does not exists" err = errors.New(msg) log.Debug(msg, "error", err) next.ServeHTTP(w, r) return } if userSession.IsExpired() { memcache.RemoveKey(sessionToken) msg := "session is expired" log.Debug(msg, "error", err, "token", sessionToken) next.ServeHTTP(w, r) return } ctx := context.WithValue(r.Context(), models.CtxUsernameKey, userSession.Username) ctx = context.WithValue(ctx, models.CtxSessionKey, userSession) if err := cacheSetSession(sessionToken, userSession); err != nil { msg := "failed to marshal user session" log.Warn(msg, "error", err) next.ServeHTTP(w, r) return } next.ServeHTTP(w, r.WithContext(ctx)) }) }