mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 07:54:22 +00:00
Setup api auth middleware
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
|||||||
"novamd/internal/auth"
|
"novamd/internal/auth"
|
||||||
"novamd/internal/db"
|
"novamd/internal/db"
|
||||||
"novamd/internal/filesystem"
|
"novamd/internal/filesystem"
|
||||||
"novamd/internal/models"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
@@ -25,8 +24,20 @@ func SetupRoutes(r chi.Router, db *db.DB, fs *filesystem.FileSystem, authMiddlew
|
|||||||
r.Post("/auth/logout", Logout(sessionService))
|
r.Post("/auth/logout", Logout(sessionService))
|
||||||
r.Get("/auth/me", GetCurrentUser(db))
|
r.Get("/auth/me", GetCurrentUser(db))
|
||||||
|
|
||||||
// User routes
|
// Admin-only routes
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Use(authMiddleware.RequireRole("admin"))
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
// r.Get("/admin/users", ListUsers(db))
|
||||||
|
// r.Post("/admin/users", CreateUser(db))
|
||||||
|
// r.Delete("/admin/users/{userId}", DeleteUser(db))
|
||||||
|
})
|
||||||
|
|
||||||
|
// User routes - protected by resource ownership
|
||||||
r.Route("/users/{userId}", func(r chi.Router) {
|
r.Route("/users/{userId}", func(r chi.Router) {
|
||||||
|
r.Use(authMiddleware.RequireResourceOwnership)
|
||||||
|
|
||||||
r.Get("/", GetUser(db))
|
r.Get("/", GetUser(db))
|
||||||
|
|
||||||
// Workspace routes
|
// Workspace routes
|
||||||
@@ -37,6 +48,9 @@ func SetupRoutes(r chi.Router, db *db.DB, fs *filesystem.FileSystem, authMiddlew
|
|||||||
r.Put("/last", UpdateLastWorkspace(db))
|
r.Put("/last", UpdateLastWorkspace(db))
|
||||||
|
|
||||||
r.Route("/{workspaceId}", func(r chi.Router) {
|
r.Route("/{workspaceId}", func(r chi.Router) {
|
||||||
|
// Add workspace ownership check
|
||||||
|
r.Use(authMiddleware.RequireWorkspaceOwnership(db))
|
||||||
|
|
||||||
r.Get("/", GetWorkspace(db))
|
r.Get("/", GetWorkspace(db))
|
||||||
r.Put("/", UpdateWorkspace(db, fs))
|
r.Put("/", UpdateWorkspace(db, fs))
|
||||||
r.Delete("/", DeleteWorkspace(db))
|
r.Delete("/", DeleteWorkspace(db))
|
||||||
@@ -61,11 +75,5 @@ func SetupRoutes(r chi.Router, db *db.DB, fs *filesystem.FileSystem, authMiddlew
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Admin-only routes
|
|
||||||
r.Group(func(r chi.Router) {
|
|
||||||
r.Use(authMiddleware.RequireRole(string(models.RoleAdmin)))
|
|
||||||
// Admin-only endpoints
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"novamd/internal/db"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contextKey string
|
type contextKey string
|
||||||
@@ -92,6 +96,80 @@ func (m *Middleware) RequireRole(role string) func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequireResourceOwnership ensures users can only access their own resources
|
||||||
|
func (m *Middleware) RequireResourceOwnership(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Get requesting user from context (set by auth middleware)
|
||||||
|
claims, err := GetUserFromContext(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get requested user ID from URL
|
||||||
|
userIDStr := chi.URLParam(r, "userId")
|
||||||
|
requestedUserID, err := strconv.Atoi(userIDStr)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow if user is accessing their own resources or is an admin
|
||||||
|
if claims.UserID != requestedUserID && claims.Role != "admin" {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireWorkspaceOwnership ensures users can only access workspaces they own
|
||||||
|
type WorkspaceGetter interface {
|
||||||
|
GetWorkspaceByID(id int) (*Workspace, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Workspace struct {
|
||||||
|
ID int
|
||||||
|
UserID int
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireWorkspaceOwnership ensures users can only access workspaces they own
|
||||||
|
func (m *Middleware) RequireWorkspaceOwnership(db *db.DB) func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Get requesting user from context
|
||||||
|
claims, err := GetUserFromContext(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get workspace ID from URL
|
||||||
|
workspaceID, err := strconv.Atoi(chi.URLParam(r, "workspaceId"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid workspace ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get workspace from database
|
||||||
|
workspace, err := db.GetWorkspaceByID(workspaceID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Workspace not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user owns the workspace or is admin
|
||||||
|
if workspace.UserID != claims.UserID && claims.Role != "admin" {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserFromContext retrieves user claims from the request context
|
// GetUserFromContext retrieves user claims from the request context
|
||||||
func GetUserFromContext(ctx context.Context) (*UserClaims, error) {
|
func GetUserFromContext(ctx context.Context) (*UserClaims, error) {
|
||||||
claims, ok := ctx.Value(UserContextKey).(UserClaims)
|
claims, ok := ctx.Value(UserContextKey).(UserClaims)
|
||||||
|
|||||||
Reference in New Issue
Block a user