diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 90e30f7..9274a98 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -122,6 +122,7 @@ jobs: run: npx semantic-release --debug helm-release: needs: build-docker + if: ${{ github.ref }} == 'master' # depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions # see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token permissions: diff --git a/backend/cmd/kubevoyage/main.go b/backend/cmd/kubevoyage/main.go index e393be9..5e770ea 100644 --- a/backend/cmd/kubevoyage/main.go +++ b/backend/cmd/kubevoyage/main.go @@ -127,6 +127,12 @@ func setupServer(handle *handlers.Handler) http.Handler { mux.Handle("/api/request", logMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handle.HandleRequestSite(w, r) }))) + mux.Handle("/api/logout", logMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handle.HandleLogout(w, r) + }))) + mux.Handle("/api/validate-session", logMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handle.HandleValidateSession(w, r) + }))) return handler } diff --git a/backend/go.mod b/backend/go.mod index 027603f..d3d9afe 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,14 +4,14 @@ go 1.21 require ( github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/gorilla/sessions v1.2.2 + github.com/gorilla/sessions v1.3.0 github.com/rs/cors v1.11.0 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.22.0 - gorm.io/driver/mysql v1.5.6 - gorm.io/driver/postgres v1.5.7 - gorm.io/driver/sqlite v1.5.5 - gorm.io/gorm v1.25.10 + golang.org/x/crypto v0.25.0 + gorm.io/driver/mysql v1.5.7 + gorm.io/driver/postgres v1.5.9 + gorm.io/driver/sqlite v1.5.6 + gorm.io/gorm v1.25.11 ) require ( @@ -20,13 +20,15 @@ require ( github.com/gorilla/securecookie v1.1.2 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.4.3 // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/text v0.16.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 5577113..d1d87f2 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -12,12 +12,18 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg= +github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -28,6 +34,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= @@ -43,8 +51,20 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -53,10 +73,18 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= +gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= +gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= diff --git a/backend/internal/handlers/auth.go b/backend/internal/handlers/auth.go index 02830e4..5b5ce17 100644 --- a/backend/internal/handlers/auth.go +++ b/backend/internal/handlers/auth.go @@ -32,7 +32,8 @@ type TokenInfo struct { user string } -var store = sessions.NewCookieStore([]byte("your-very-secret-key")) +var secret, _ = util.GetEnvOrDefault("JWT_SECRET_KEY", "kubevoyage") +var store = sessions.NewCookieStore([]byte(secret)) var oneTimeStore = make(map[string]TokenInfo) func NewHandler(db *gorm.DB) *Handler { @@ -85,18 +86,6 @@ func (h *Handler) HandleLogin(w http.ResponseWriter, r *http.Request) { return } - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "user": inputUser.Email, - "exp": time.Now().Add(24 * time.Hour).Unix(), - }) - - // Sign and get the complete encoded token as a string using the secret - _, err = token.SignedString(h.JWTKey) - if err != nil { - sendJSONError(w, "Internal Server Error", http.StatusInternalServerError) - return - } - session, _ := store.Get(r, "session-cook") tld, err := extractMainDomain(r.Host) if err != nil { @@ -105,7 +94,7 @@ func (h *Handler) HandleLogin(w http.ResponseWriter, r *http.Request) { } session.Options = &sessions.Options{ Path: "/", // Available across the entire domain - MaxAge: 3600, // Expires after 1 hour + MaxAge: 3600 * 24 * 7, // Expires after 1 week TODO: make configurable HttpOnly: true, // Not accessible via JavaScript Secure: true, // Only sent over HTTPS SameSite: http.SameSiteNoneMode, // Controls cross-site request behavior @@ -113,18 +102,24 @@ func (h *Handler) HandleLogin(w http.ResponseWriter, r *http.Request) { } session.Values["authenticated"] = true session.Values["user"] = inputUser.Email - session.Save(r, w) + if err := session.Save(r, w); err != nil { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } var domain string - siteURL, siteUrlErr := h.getRedirectUrl(r, w) + siteURL, siteUrlErr := h.getRedirectUrl(r) if siteUrlErr != nil { // If there was an error getting the redirect URL, use the request's host as the domain log.Println("Site URl could not be determined: " + siteURL) domain = r.Host } else { // If the redirect URL was obtained successfully, extract the main domain - h.setRedirectCookie(siteURL, r, w) - var err error + err := h.setRedirectCookie(siteURL, r, w) + if err != nil { + slog.Error("Failed to set redirect cookie", "error", err) + } + domain, err = extractMainDomain(r.Host) if err != nil { sendJSONError(w, "Invalid Redirect URL", http.StatusBadRequest) @@ -132,20 +127,10 @@ func (h *Handler) HandleLogin(w http.ResponseWriter, r *http.Request) { } } if err != nil { - slog.Error("could not extract Main Domain", err) + slog.Error("could not extract Main Domain", "error", err) return } - slog.Info("Domain: ", domain) - // Set the token as a cookie - //http.SetCookie(w, &http.Cookie{ - // Name: "X-Auth-Token", - // Value: tokenString, - // Expires: time.Now().Add(24 * time.Hour), - // Secure: true, // Set this to true if using HTTPS - // SameSite: http.SameSiteNoneMode, // Set this to true if using HTTPS - // Domain: r.Host, // Adjust to your domain - // Path: "/", - //}) + slog.Info("Domain: ", "value", domain) response := LoginResponse{ Success: true, @@ -204,7 +189,7 @@ func (h *Handler) HandleRegister(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleRedirect(w http.ResponseWriter, r *http.Request) { //FIXME: Not unchecked redirecting with parameter - siteURL, err := h.getRedirectFromCookie(r, w, true) + siteURL, err := h.getRedirectFromCookie(r, true) if err != nil { } @@ -212,19 +197,20 @@ func (h *Handler) HandleRedirect(w http.ResponseWriter, r *http.Request) { siteURL = r.Host } - redirect := r.Header.Get("X-Auth-Site") - log.Println(redirect) - log.Println(siteURL) http.Redirect(w, r, siteURL, http.StatusSeeOther) } func (h *Handler) HandleAuthenticate(w http.ResponseWriter, r *http.Request) { // 1. Extract the user's email from the session or JWT token. - siteURL, err := h.getRedirectUrl(r, w) + siteURL, err := h.getRedirectUrl(r) if err != nil { - log.Println(err.Error()) - //h.logError(w, err.Error(), nil, http.StatusBadRequest) - //return + slog.Error("Error retrieving redirect url", "error", err) + } + if siteURL == "" { + siteURL, err = h.getRedirectFromCookie(r, false) + if err != nil { + slog.Error("Error retrieving redirect url", "error", err) + } } tld, err := extractMainDomain(siteURL) session, err := store.Get(r, "session-cook") @@ -258,14 +244,20 @@ func (h *Handler) HandleAuthenticate(w http.ResponseWriter, r *http.Request) { } // If the user cannot be read from the cookie, redirect to /login with the site URL as a parameter - h.setRedirectCookie(siteURL, r, w) //Fixme: improve domain handling + err = h.setRedirectCookie(siteURL, r, w) //Fixme: improve domain handling + if err != nil { + slog.Error("failed to set redirect cookie", "error", err) + } http.Redirect(w, r, "/login?redirect="+strings.TrimSuffix(siteURL, "/")+"&token="+oneTimeToken, http.StatusSeeOther) return } if tokenAuthenticated { session.Values["authenticated"] = true session.Values["user"] = oneTimeStore[token].user - session.Save(r, w) + err = session.Save(r, w) + if err != nil { + slog.Error("Failed to save session", "error", err) + } delete(oneTimeStore, token) } slog.Debug("Incoming session is authenticated") @@ -310,6 +302,69 @@ func (h *Handler) HandleAuthenticate(w http.ResponseWriter, r *http.Request) { } w.WriteHeader(http.StatusOK) } +func (h *Handler) HandleLogout(w http.ResponseWriter, r *http.Request) { + session, err := store.Get(r, "session-cook") + if err != nil { + sendJSONError(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + // Clear session values + session.Values["authenticated"] = false + + // Expire the cookie + session.Options.MaxAge = -1 + tld, err := extractMainDomain(r.Host) + session.Options.Domain = tld + + // Save the session + err = session.Save(r, w) + if err != nil { + sendJSONError(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + response := map[string]interface{}{ + "success": true, + "message": "Logout successful", + } + sendJSONResponse(w, response, http.StatusOK) +} + +func (h *Handler) HandleValidateSession(w http.ResponseWriter, r *http.Request) { + session, err := store.Get(r, "session-cook") + if err != nil { + sendJSONError(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + authenticated, ok := session.Values["authenticated"].(bool) + if !ok || !authenticated { + sendJSONError(w, "Unauthorized", http.StatusUnauthorized) + return + } + + user, ok := session.Values["user"].(string) + if !ok || user == "" { + sendJSONError(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Optionally, you can check if the user still exists in the database + var dbUser models.User + result := h.db.Where("email = ?", user).First(&dbUser) + if result.Error != nil { + sendJSONError(w, "User not found", http.StatusUnauthorized) + return + } + + response := map[string]interface{}{ + "success": true, + "message": "Session is valid", + "user": user, + } + sendJSONResponse(w, response, http.StatusOK) +} func (h *Handler) logError(w http.ResponseWriter, message string, err error, statusCode int) { logMessage := message @@ -320,11 +375,25 @@ func (h *Handler) logError(w http.ResponseWriter, message string, err error, sta http.Error(w, message, statusCode) } +func (h *Handler) getUserFromSession(r *http.Request) (string, error) { + session, err := store.Get(r, "session-cook") + if err != nil { + slog.Debug("Error retrieving user from session", "error", err) + return "", err + } + user, success := session.Values["user"].(string) + if success == false { + slog.Debug("Error retrieving user from session", "error", err) + return "", err + } + return user, nil +} + func (h *Handler) getUserEmailFromToken(r *http.Request) (string, error) { - cookie, err := r.Cookie("X-Auth-Token") + cookie, err := r.Cookie("session-cook") if err != nil { - slog.Error("Authentication Cookie missing", err) - return "", fmt.Errorf("Authentication cookie missing") + slog.Error("Authentication Cookie missing", "error", err) + return "", fmt.Errorf("authentication cookie missing") } tokenStr := cookie.Value @@ -335,12 +404,12 @@ func (h *Handler) getUserEmailFromToken(r *http.Request) (string, error) { }) if err != nil { - return "", fmt.Errorf("Invalid token") + return "", fmt.Errorf("invalid token") } userEmail, ok := (*claims)["user"].(string) if !ok { - return "", fmt.Errorf("Invalid token claims") + return "", fmt.Errorf("invalid token claims") } return userEmail, nil @@ -365,7 +434,7 @@ func (h *Handler) setRedirectCookie(redirectUrl string, r *http.Request, w http. }) return nil } -func (h *Handler) getRedirectFromCookie(r *http.Request, w http.ResponseWriter, clear bool) (string, error) { +func (h *Handler) getRedirectFromCookie(r *http.Request, clear bool) (string, error) { cookie, err := r.Cookie("X-Auth-Site") if err != nil { if errors.Is(err, http.ErrNoCookie) { @@ -374,36 +443,15 @@ func (h *Handler) getRedirectFromCookie(r *http.Request, w http.ResponseWriter, } return "", err } - - // Clear the cookie once it's read - //http.SetCookie(w, &http.Cookie{ - // Name: "X-Auth-Site", - // Value: "", - // Expires: time.Unix(0, 0), - // Path: "/", - //}) - + if clear { + } return cookie.Value, nil } -func (h *Handler) getRedirectUrl(r *http.Request, w http.ResponseWriter) (string, error) { +func (h *Handler) getRedirectUrl(r *http.Request) (string, error) { // Extract the redirect parameter from the request to get the site URL. - - siteURL := r.Header.Get("X-Forwarded-Uri") - if siteURL == "" { - siteURL = r.Header.Get("X-Auth-Site") - if siteURL == "" { - siteURL = r.URL.Query().Get("redirect") - if siteURL == "" { - surl, err := h.getRedirectFromCookie(r, w, false) - if err != nil { - return "", fmt.Errorf("Redirect URL missing from both header and URL parameter") - } - siteURL = surl - } - } - } - if siteURL == "" { - return "", fmt.Errorf("Redirect URL missing from both header and URL parameter") + siteURL := r.URL.Query().Get("redirect") + if siteURL == "" || siteURL == "null" { + return "", fmt.Errorf("redirect URL missing from both header and URL parameter") } else { return siteURL, nil } @@ -412,34 +460,10 @@ func (h *Handler) getRedirectUrl(r *http.Request, w http.ResponseWriter) (string // generateSessionID generates a secure, random session ID. func generateSessionID() string { size := 32 // This size can be adjusted according to your security needs - bytes := make([]byte, size) - _, err := rand.Read(bytes) + randomBytes := make([]byte, size) + _, err := rand.Read(randomBytes) if err != nil { log.Fatalf("Failed to generate random session ID: %v", err) } - return hex.EncodeToString(bytes) -} -func logCookies(r *http.Request) { - cookies := r.Cookies() // Step 1: Retrieve all cookies - var cookieStrings []string - - for _, cookie := range cookies { - // Step 2: Format each cookie as a string - cookieStr := fmt.Sprintf("%s=%s", cookie.Name, cookie.Value) - cookieStrings = append(cookieStrings, cookieStr) - } - - // Join all cookie strings into a single string - allCookies := strings.Join(cookieStrings, "; ") - - // Step 3: Log the complete cookie string - slog.Info("Cookies:", allCookies) -} -func printHeaders(r *http.Request) { - for name, values := range r.Header { - // Loop over all values for the name. - for _, value := range values { - fmt.Printf("%s: %s\n", name, value) - } - } + return hex.EncodeToString(randomBytes) } diff --git a/backend/internal/handlers/requests.go b/backend/internal/handlers/requests.go index 28ffa62..703bfef 100644 --- a/backend/internal/handlers/requests.go +++ b/backend/internal/handlers/requests.go @@ -8,7 +8,6 @@ import ( "gorm.io/gorm" "log" "net/http" - "time" ) func (h *Handler) HandleRequests(w http.ResponseWriter, r *http.Request) { @@ -16,7 +15,7 @@ func (h *Handler) HandleRequests(w http.ResponseWriter, r *http.Request) { sendJSONError(w, "Method not allowed", http.StatusMethodNotAllowed) return } - userEmail, err := h.getUserEmailFromToken(r) + userEmail, err := h.getUserFromSession(r) if err != nil { http.Error(w, err.Error(), http.StatusUnauthorized) return @@ -43,7 +42,9 @@ func (h *Handler) HandleRequests(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleRequestSite(w http.ResponseWriter, r *http.Request) { - userEmail, err := h.getUserEmailFromToken(r) + var redirect models.Redirect + + userEmail, err := h.getUserFromSession(r) if err != nil { http.Error(w, err.Error(), http.StatusUnauthorized) return @@ -55,21 +56,18 @@ func (h *Handler) HandleRequestSite(w http.ResponseWriter, r *http.Request) { http.Error(w, "User not found", http.StatusNotFound) return } - if user.Role == "admin" { - http.Error(w, err.Error(), http.StatusUnauthorized) - return - } - siteURL, err := h.getRedirectUrl(r, w) + // Parse the request body + err = json.NewDecoder(r.Body).Decode(&redirect) if err != nil { - http.Error(w, "Request URL not found", http.StatusNotFound) + sendJSONError(w, "Bad Request", http.StatusBadRequest) return } // Check if site already exists var site models.Site - if err := h.db.Where("url = ?", siteURL).First(&site).Error; errors.Is(err, gorm.ErrRecordNotFound) { + if err := h.db.Where("url = ?", redirect.Redirect).First(&site).Error; errors.Is(err, gorm.ErrRecordNotFound) { // If not, create a new site entry - site = models.Site{URL: siteURL} + site = models.Site{URL: redirect.Redirect} h.db.Create(&site) } @@ -103,7 +101,7 @@ func (h *Handler) HandleUpdateSiteState(w http.ResponseWriter, r *http.Request) http.Error(w, "Invalid state value", http.StatusBadRequest) return } - userEmail, err := h.getUserEmailFromToken(r) + userEmail, err := h.getUserFromSession(r) if err != nil { http.Error(w, err.Error(), http.StatusUnauthorized) return @@ -134,17 +132,3 @@ func (h *Handler) HandleUpdateSiteState(w http.ResponseWriter, r *http.Request) w.Write([]byte("State updated successfully")) } - -func HandleLogout(w http.ResponseWriter, r *http.Request) { - http.SetCookie(w, &http.Cookie{ - Name: "auth_token", - Value: "", - Expires: time.Unix(0, 0), - HttpOnly: true, - Secure: true, // Set this to true if using HTTPS - Domain: "", // Adjust to your domain - Path: "/", - }) - - w.Write([]byte("Logged out successfully")) -} diff --git a/backend/internal/handlers/utils.go b/backend/internal/handlers/utils.go index 12025e9..682434c 100644 --- a/backend/internal/handlers/utils.go +++ b/backend/internal/handlers/utils.go @@ -76,6 +76,9 @@ func sendJSONResponse(w http.ResponseWriter, data interface{}, statusCode int) { func IsUserAdmin(db *gorm.DB, email string) (bool, error) { var user models.User + if email == "" { + return false, errors.New("user is empty") + } // Find the user by email result := db.Where("email = ?", email).First(&user) if errors.Is(result.Error, gorm.ErrRecordNotFound) { diff --git a/backend/internal/models/models.go b/backend/internal/models/models.go index 11fe063..a5f37a1 100644 --- a/backend/internal/models/models.go +++ b/backend/internal/models/models.go @@ -39,3 +39,7 @@ func (s State) IsValid() bool { } return false } + +type Redirect struct { + Redirect string +} diff --git a/deploy/charts/Chart.yaml b/deploy/charts/Chart.yaml index c6100a1..aa91330 100644 --- a/deploy/charts/Chart.yaml +++ b/deploy/charts/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v2 name: kubevoyage description: A Helm chart for Kubernetes auth proxy -version: 0.7.0 \ No newline at end of file +version: 0.9.0 \ No newline at end of file diff --git a/deploy/charts/values.yaml b/deploy/charts/values.yaml index ba8ee3d..69d6839 100644 --- a/deploy/charts/values.yaml +++ b/deploy/charts/values.yaml @@ -44,4 +44,4 @@ database: port: user: password: - name: \ No newline at end of file + name: diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3b86103..27ad9fd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -144,10 +144,11 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "25.0.7", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", - "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", + "version": "25.0.8", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz", + "integrity": "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==", "dev": true, + "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", @@ -238,208 +239,224 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", + "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", + "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", - "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", + "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", + "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", + "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", + "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", + "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", + "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", + "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", + "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", + "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", + "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", + "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", + "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", + "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", + "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1323,10 +1340,11 @@ } }, "node_modules/rollup": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", + "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.5" }, @@ -1338,22 +1356,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.17.2", - "@rollup/rollup-android-arm64": "4.17.2", - "@rollup/rollup-darwin-arm64": "4.17.2", - "@rollup/rollup-darwin-x64": "4.17.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", - "@rollup/rollup-linux-arm64-gnu": "4.17.2", - "@rollup/rollup-linux-arm64-musl": "4.17.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", - "@rollup/rollup-linux-s390x-gnu": "4.17.2", - "@rollup/rollup-linux-x64-gnu": "4.17.2", - "@rollup/rollup-linux-x64-musl": "4.17.2", - "@rollup/rollup-win32-arm64-msvc": "4.17.2", - "@rollup/rollup-win32-ia32-msvc": "4.17.2", - "@rollup/rollup-win32-x64-msvc": "4.17.2", + "@rollup/rollup-android-arm-eabi": "4.18.1", + "@rollup/rollup-android-arm64": "4.18.1", + "@rollup/rollup-darwin-arm64": "4.18.1", + "@rollup/rollup-darwin-x64": "4.18.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", + "@rollup/rollup-linux-arm-musleabihf": "4.18.1", + "@rollup/rollup-linux-arm64-gnu": "4.18.1", + "@rollup/rollup-linux-arm64-musl": "4.18.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", + "@rollup/rollup-linux-riscv64-gnu": "4.18.1", + "@rollup/rollup-linux-s390x-gnu": "4.18.1", + "@rollup/rollup-linux-x64-gnu": "4.18.1", + "@rollup/rollup-linux-x64-musl": "4.18.1", + "@rollup/rollup-win32-arm64-msvc": "4.18.1", + "@rollup/rollup-win32-ia32-msvc": "4.18.1", + "@rollup/rollup-win32-x64-msvc": "4.18.1", "fsevents": "~2.3.2" } }, @@ -1400,10 +1418,11 @@ } }, "node_modules/rollup-plugin-svelte": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.2.0.tgz", - "integrity": "sha512-Qvo5VNFQZtaI+sHSjcCIFDP+olfKVyslAoJIkL3DxuhUpNY5Ys0+hhxUY3kuEKt9BXFgkFJiiic/XRb07zdSbg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.2.2.tgz", + "integrity": "sha512-hgnIblTRewaBEVQD6N0Q43o+y6q1TmDRhBjaEzQCi50bs8TXqjc+d1zFZyE8tsfgcfNHZQzclh4RxlFUB85H8Q==", "dev": true, + "license": "MIT", "dependencies": { "@rollup/pluginutils": "^4.1.0", "resolve.exports": "^2.0.0" @@ -1421,6 +1440,7 @@ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", "dev": true, + "license": "MIT", "dependencies": { "estree-walker": "^2.0.1", "picomatch": "^2.2.2" @@ -1588,10 +1608,11 @@ } }, "node_modules/svelte": { - "version": "4.2.15", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.15.tgz", - "integrity": "sha512-j9KJSccHgLeRERPlhMKrCXpk2TqL2m5Z+k+OBTQhZOhIdCCd3WfqV+ylPWeipEwq17P/ekiSFWwrVQv93i3bsg==", + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", + "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", @@ -1613,15 +1634,16 @@ } }, "node_modules/svelte-routing": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-2.12.0.tgz", - "integrity": "sha512-6i4Mncy4P2b7gD7+BOT9JzQvrfGfGXqFra8VXYU5//bpn6AzJ0PLEhH1E/KwY2AxleOiS/8Nm37MGuic2kn15A==" + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-2.13.0.tgz", + "integrity": "sha512-/NTxqTwLc7Dq306hARJrH2HLXOBtKd7hu8nxgoFDlK0AC4SOKnzisiX/9m8Uksei1QAWtlAEdF91YphNM8iDMg==" }, "node_modules/svelte/node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } @@ -1631,6 +1653,7 @@ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*" } diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 472acc5..a95c9a6 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -1,9 +1,37 @@ @@ -14,15 +42,22 @@ diff --git a/frontend/src/Login.svelte b/frontend/src/Login.svelte index 55d9681..38a3d2b 100644 --- a/frontend/src/Login.svelte +++ b/frontend/src/Login.svelte @@ -1,5 +1,6 @@