Skip to content

Commit

Permalink
feat(backend): update request and redirect logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Björn Urban committed Oct 30, 2023
1 parent d241a09 commit dff38f9
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 26 deletions.
11 changes: 7 additions & 4 deletions backend/cmd/kubevoyage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ func main() {
log.Fatalf(err.Error())
}

mux := setupServer(handler)
mux := setupServer(handler, app.DB)

log.Println("Starting server on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}
func setupServer(handle *handlers.Handler) http.Handler {
func setupServer(handle *handlers.Handler, db *gorm.DB) http.Handler {
mux := http.NewServeMux()

handler := cors.Default().Handler(mux)
Expand Down Expand Up @@ -109,7 +109,10 @@ func setupServer(handle *handlers.Handler) http.Handler {
})))

mux.Handle("/api/requests", logMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handlers.HandleRequests(w, r, db)
handle.HandleRequests(w, r)
})))
mux.Handle("/api/requests/update", logMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handle.HandleUpdateSiteState(w, r)
})))
mux.Handle("/api/register", logMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handle.HandleRegister(w, r)
Expand All @@ -124,7 +127,7 @@ func setupServer(handle *handlers.Handler) http.Handler {
handle.HandleRedirect(w, r)
})))
mux.Handle("/api/request", logMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handle.HandleRequestSite(w, r, db)
handle.HandleRequestSite(w, r)
})))

return handler
Expand Down
15 changes: 11 additions & 4 deletions backend/internal/handlers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,21 @@ func (h *Handler) HandleAuthenticate(w http.ResponseWriter, r *http.Request) {
var userSite models.UserSite
err = h.db.Joins("JOIN users ON users.id = user_sites.user_id").
Joins("JOIN sites ON sites.id = user_sites.site_id").
Where("users.email = ? AND sites.url = ? AND user_sites.state = ?", userEmail, siteURL, "authorized").
Where("users.email = ? AND sites.url = ?", userEmail, siteURL).
First(&userSite).Error

if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// Return 401 if the user is not authorized for the requested siteURL
w.WriteHeader(http.StatusUnauthorized)
http.Redirect(w, r, "api/request", http.StatusSeeOther)
return
}
h.logError(w, "Database error while checking user authorization", err, http.StatusInternalServerError)
return
}
if userSite.State == models.Requested || userSite.State == models.Declined {
w.WriteHeader(http.StatusUnauthorized)
}
w.WriteHeader(http.StatusOK)
}

Expand Down Expand Up @@ -310,13 +313,17 @@ func (h *Handler) getRedirectUrl(r *http.Request, w http.ResponseWriter) (string
if siteURL == "" {
surl, err := h.getRedirectFromCookie(r, w, false)
if err != nil {
fmt.Errorf("Redirect URL missing from both header and URL parameter")
return "", fmt.Errorf("Redirect URL missing from both header and URL parameter")
}
siteURL = surl
}
}
}
return siteURL, nil
if siteURL == "" {
return "", fmt.Errorf("Redirect URL missing from both header and URL parameter")
} else {
return siteURL, nil
}
}
func printHeaders(r *http.Request) {
for name, values := range r.Header {
Expand Down
94 changes: 77 additions & 17 deletions backend/internal/handlers/requests.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
package handlers

import (
"encoding/json"
"errors"
"fmt"
"github.com/B-Urb/KubeVoyage/internal/models"
"gorm.io/gorm"
"log"
"net/http"
"time"
)

type UserSiteResponse struct {
User string `json:"user"`
Site string `json:"site"`
State string `json:"state"`
}

func HandleRequests(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
func (h *Handler) HandleRequests(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
sendJSONError(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

var results []UserSiteResponse
err := db.Table("user_sites").
userEmail, err := h.getUserEmailFromToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
isAdmin, err := IsUserAdmin(h.db, userEmail)
if !isAdmin {
http.Error(w, "Only Admins can view this", http.StatusUnauthorized)
return
}
var results []models.UserSiteResponse
err = h.db.Table("user_sites").
Select("users.email as user, sites.url as site, user_sites.state as state").
Joins("JOIN users ON users.id = user_sites.user_id").
Joins("JOIN sites ON sites.id = user_sites.site_id").
Expand All @@ -37,39 +42,94 @@ func HandleRequests(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
sendJSONResponse(w, results, http.StatusOK)
}

func (h *Handler) HandleRequestSite(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
func (h *Handler) HandleRequestSite(w http.ResponseWriter, r *http.Request) {
userEmail, err := h.getUserEmailFromToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}

// Query the User table to get the unique ID associated with the email
var user models.User
if err := db.Where("email = ?", userEmail).First(&user).Error; err != nil {
if err := h.db.Where("email = ?", userEmail).First(&user).Error; err != nil {
http.Error(w, "User not found", http.StatusNotFound)
return
}

if user.Role == "admin" {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
siteURL := r.URL.Query().Get("redirect")

// Check if site already exists
var site models.Site
if err := db.Where("url = ?", siteURL).First(&site).Error; errors.Is(err, gorm.ErrRecordNotFound) {
if err := h.db.Where("url = ?", siteURL).First(&site).Error; errors.Is(err, gorm.ErrRecordNotFound) {
// If not, create a new site entry
site = models.Site{URL: siteURL}
db.Create(&site)
h.db.Create(&site)
}

// Create a new UserSite entry with state "requested"
userSite := models.UserSite{
UserID: user.ID, // Use the ID from the user query
SiteID: site.ID,
State: "requested",
State: models.Requested,
}
db.Create(&userSite)
h.db.Create(&userSite)

w.Write([]byte("Request submitted"))
}
func (h *Handler) HandleUpdateSiteState(w http.ResponseWriter, r *http.Request) {
type RequestBody struct {
UserEmail string `json:"userEmail"`
SiteURL string `json:"siteURL"`
NewState string `json:"newState"`
}
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var body RequestBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
state := models.State(body.NewState)
if !state.IsValid() {
http.Error(w, "Invalid state value", http.StatusBadRequest)
return
}
userEmail, err := h.getUserEmailFromToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
isAdmin, err := IsUserAdmin(h.db, userEmail)
if !isAdmin {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
var userID uint
if err := h.db.Model(&models.User{}).Where("email = ?", body.UserEmail).Select("id").First(&userID).Error; err != nil {
http.Error(w, fmt.Errorf("failed to find user: %w", err).Error(), http.StatusBadRequest)
return
}

// 2. Find the site ID
var siteID uint
if err := h.db.Model(&models.Site{}).Where("url = ?", body.SiteURL).Select("id").First(&siteID).Error; err != nil {
http.Error(w, fmt.Errorf("failed to find site: %w", err).Error(), http.StatusBadRequest)
return
}

// 3. Update the UserSite record
if err := h.db.Model(&models.UserSite{}).Where("user_id = ? AND site_id = ?", userID, siteID).Update("state", state).Error; err != nil {
http.Error(w, fmt.Errorf("failed to find and update request: %w", err).Error(), http.StatusBadRequest)
return
}

w.Write([]byte("State updated successfully"))
}

func HandleLogout(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Expand Down
19 changes: 19 additions & 0 deletions backend/internal/handlers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package handlers

import (
"encoding/json"
"errors"
"fmt"
"github.com/B-Urb/KubeVoyage/internal/models"
"gorm.io/gorm"
"log"
"net/http"
"net/url"
Expand Down Expand Up @@ -69,3 +72,19 @@ func sendJSONResponse(w http.ResponseWriter, data interface{}, statusCode int) {
w.WriteHeader(statusCode)
json.NewEncoder(w).Encode(data)
}

func IsUserAdmin(db *gorm.DB, email string) (bool, error) {
var user models.User

// Find the user by email
result := db.Where("email = ?", email).First(&user)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return false, errors.New("user not found")
}
if result.Error != nil {
return false, result.Error
}

// Check if the user's role is "admin"
return user.Role == "admin", nil
}
18 changes: 17 additions & 1 deletion backend/internal/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,27 @@ type Site struct {
type UserSite struct {
UserID uint `gorm:"primaryKey"`
SiteID uint `gorm:"primaryKey"`
State string
State State
}

type UserSiteResponse struct {
User string `json:"user"`
Site string `json:"site"`
State string `json:"state"`
}

type State string

const (
Requested State = "requested"
Authorized State = "authorized"
Declined State = "declined"
)

func (s State) IsValid() bool {
switch s {
case Requested, Authorized, Declined:
return true
}
return false
}

0 comments on commit dff38f9

Please sign in to comment.