func authMiddleware(ctx *gin.Context) {
	logger := tracelog.NewLogger().WithName("AuthMiddleware")
	accounts, err := auth.GetUserAccounts(ctx)
	if err != nil {
		response.Error(ctx, apierrors.InternalError(err))
	}
	if len(*accounts) == 0 {
		response.Error(ctx, apierrors.NewAPIError("no user found", "", http.StatusUnauthorized))
	}
	var user string
	session := sessions.Default(ctx)
	sessionUser := session.Get("user")
	if sessionUser == nil {  
		logger.V(1).Info("Basic auth authentication")
		authHeader := string(ctx.GetHeader("Authorization"))
		if authHeader != "" {
			headerParts := strings.Split(authHeader, " ")
			if len(headerParts) < 2 {
				response.Error(ctx, apierrors.NewInternalError("Authorization header format was not expected"))
				ctx.Abort()
				return
			}
			creds, err := base64.StdEncoding.DecodeString(headerParts[1])
			if err != nil {
				response.Error(ctx, apierrors.NewInternalError("Couldn't decode auth header"))
				ctx.Abort()
				return
			}
			user = strings.Split(string(creds), ":")[0]
			if user == "" {
				response.Error(ctx, apierrors.NewInternalError("Couldn't extract user from the auth header"))
				ctx.Abort()
				return
			}
		}
		gin.BasicAuth(*accounts)(ctx)
	} else {
		logger.V(1).Info("Session authentication")
		var ok bool
		user, ok = sessionUser.(string)
		if !ok {
			response.Error(ctx, apierrors.NewInternalError("Couldn't parse user from session"))
			ctx.Abort()
			return
		}
		userStillExists := false
		for checkUser := range *accounts {
			if checkUser == user {
				userStillExists = true
				break
			}
		}
		if !userStillExists {
			session.Clear()
			session.Options(sessions.Options{MaxAge: -1})
			err := session.Save()
			if err != nil {
				response.Error(ctx, apierrors.NewInternalError("Couldn't save the session"))
				ctx.Abort()
				return
			}
			response.Error(ctx, apierrors.NewAPIError("User no longer exists. Session expired.", "", http.StatusUnauthorized))
			ctx.Abort()
			return
		}
	}
	id := fmt.Sprintf("%d", rand.Intn(10000))  
	newCtx := ctx.Request.Context()
	newCtx = requestctx.ContextWithUser(newCtx, user)
	newCtx = requestctx.ContextWithID(newCtx, id)
	ctx.Request = ctx.Request.WithContext(newCtx)
}
authMiddleware authenticates the user either using the session or if one
doesn't exist, it authenticates with basic auth.
package server
import (
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/epinio/epinio/helpers/termui"
	"github.com/epinio/epinio/helpers/tracelog"
	apiv1 "github.com/epinio/epinio/internal/api/v1"
	"github.com/epinio/epinio/internal/api/v1/response"
	"github.com/epinio/epinio/internal/auth"
	"github.com/epinio/epinio/internal/cli/server/requestctx"
	"github.com/epinio/epinio/internal/filesystem"
	"github.com/epinio/epinio/internal/web"
	apierrors "github.com/epinio/epinio/pkg/api/core/v1/errors"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
	"github.com/go-logr/logr"
	"github.com/mattn/go-colorable"
)
func Start(wg *sync.WaitGroup, port int, _ *termui.UI, logger logr.Logger) (*http.Server, string, error) {
	gin.DefaultWriter = colorable.NewColorableStdout()
	var assetsDir http.FileSystem
	if os.Getenv("LOCAL_FILESYSTEM") == "true" {
		assetsDir = http.Dir(path.Join(".", "assets", "embedded-web-files", "assets"))
	} else {
		assetsDir = filesystem.Assets()
	}
	listener, err := net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(port))
	if err != nil {
		return nil, "", err
	}
	gin.SetMode(gin.ReleaseMode)
	router := gin.New()
	router.HandleMethodNotAllowed = true
	router.Use(gin.Recovery())
	router.GET("/ready", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{})
	})
	if os.Getenv("SESSION_KEY") == "" {
		return nil, "", errors.New("SESSION_KEY environment variable not defined")
	}
	store := cookie.NewStore([]byte(os.Getenv("SESSION_KEY")))
	store.Options(sessions.Options{MaxAge: 60 * 60 * 24})  
	router.Use(sessions.Sessions("epinio-session", store))
	ginLogger := gin.LoggerWithFormatter(Formatter)
	authenticatedGroup := router.Group("", ginLogger, authMiddleware, sessionMiddleware)
	web.Lemon(authenticatedGroup)
	assets := authenticatedGroup.Group("/assets")
	assets.StaticFS("/", assetsDir)
	api := authenticatedGroup.Group(apiv1.Root)
	apiv1.Lemon(api)
	srv := &http.Server{
		Handler: router,
	}
	go func() {
		defer wg.Done()  
		if err := srv.Serve(listener); err != http.ErrServerClosed {
			log.Fatalf("Epinio server failed to start: %v", err)
		}
	}()
	elements := strings.Split(listener.Addr().String(), ":")
	listeningPort := elements[len(elements)-1]
	return srv, listeningPort, nil
}
func Formatter(params gin.LogFormatterParams) string {
	user := requestctx.User(params.Request.Context())
	return fmt.Sprintf("Method: %s | Path: %s | User: %s | IP: %s | Timestamp: %s | Latency: %s | Status: %d | Message: %s \n",
		params.Method, params.Path, user, params.ClientIP, params.TimeStamp.Format(time.RFC3339), params.Latency, params.StatusCode, params.ErrorMessage)
}