tryBearerAuth validated the bearer token but never looked up the associated user or set it on the request context. This meant downstream handlers calling GetCurrentUser would get nil even with a valid token. Changes: - Add ContextWithUser/UserFromContext helpers in auth package - tryBearerAuth now looks up the user by token's UserID and sets it on the request context via auth.ContextWithUser - GetCurrentUser checks context first before falling back to session cookie - Add integration tests for bearer auth user context
161 lines
3.9 KiB
Go
161 lines
3.9 KiB
Go
package middleware_test
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/fx"
|
|
|
|
"git.eeqj.de/sneak/upaas/internal/config"
|
|
"git.eeqj.de/sneak/upaas/internal/database"
|
|
"git.eeqj.de/sneak/upaas/internal/globals"
|
|
"git.eeqj.de/sneak/upaas/internal/logger"
|
|
"git.eeqj.de/sneak/upaas/internal/middleware"
|
|
"git.eeqj.de/sneak/upaas/internal/models"
|
|
"git.eeqj.de/sneak/upaas/internal/service/auth"
|
|
)
|
|
|
|
// setupMiddleware creates a Middleware with a real SQLite database for
|
|
// integration testing.
|
|
func setupMiddleware(t *testing.T) (*middleware.Middleware, *auth.Service, *database.Database) {
|
|
t.Helper()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
globals.SetAppname("upaas-test")
|
|
globals.SetVersion("test")
|
|
|
|
globalsInst, err := globals.New(fx.Lifecycle(nil))
|
|
require.NoError(t, err)
|
|
|
|
loggerInst, err := logger.New(
|
|
fx.Lifecycle(nil),
|
|
logger.Params{Globals: globalsInst},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
cfg := &config.Config{
|
|
Port: 8080,
|
|
DataDir: tmpDir,
|
|
SessionSecret: "test-secret-key-at-least-32-chars!!",
|
|
}
|
|
_ = filepath.Join(tmpDir, "upaas.db")
|
|
|
|
dbInst, err := database.New(fx.Lifecycle(nil), database.Params{
|
|
Logger: loggerInst,
|
|
Config: cfg,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
authSvc, err := auth.New(fx.Lifecycle(nil), auth.ServiceParams{
|
|
Logger: loggerInst,
|
|
Config: cfg,
|
|
Database: dbInst,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
mw, err := middleware.New(fx.Lifecycle(nil), middleware.Params{
|
|
Logger: loggerInst,
|
|
Globals: globalsInst,
|
|
Config: cfg,
|
|
Auth: authSvc,
|
|
Database: dbInst,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return mw, authSvc, dbInst
|
|
}
|
|
|
|
func TestAPISessionAuth_BearerTokenSetsUserContext(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mw, authSvc, dbInst := setupMiddleware(t)
|
|
ctx := t.Context()
|
|
|
|
// Create a user.
|
|
user, err := authSvc.CreateUser(ctx, "testuser", "password123")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, user)
|
|
|
|
// Create an API token for the user.
|
|
rawToken, err := models.GenerateToken()
|
|
require.NoError(t, err)
|
|
|
|
tokenHash := database.HashAPIToken(rawToken)
|
|
apiToken := models.NewAPIToken(dbInst)
|
|
apiToken.UserID = user.ID
|
|
apiToken.Name = "test-token"
|
|
apiToken.TokenHash = tokenHash
|
|
|
|
err = apiToken.Save(ctx)
|
|
require.NoError(t, err)
|
|
|
|
// Build a handler behind APISessionAuth that checks user context.
|
|
var gotUser *models.User
|
|
|
|
var getUserErr error
|
|
|
|
handler := mw.APISessionAuth()(http.HandlerFunc(
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
gotUser, getUserErr = authSvc.GetCurrentUser(r.Context(), r)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
},
|
|
))
|
|
|
|
// Make request with bearer token.
|
|
req := httptest.NewRequest(http.MethodGet, "/api/test", nil)
|
|
req.Header.Set("Authorization", "Bearer "+rawToken)
|
|
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code)
|
|
require.NoError(t, getUserErr)
|
|
require.NotNil(t, gotUser, "GetCurrentUser should return the user for bearer auth")
|
|
assert.Equal(t, user.ID, gotUser.ID)
|
|
assert.Equal(t, "testuser", gotUser.Username)
|
|
}
|
|
|
|
func TestAPISessionAuth_NoBearerTokenReturns401(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mw, _, _ := setupMiddleware(t)
|
|
|
|
handler := mw.APISessionAuth()(http.HandlerFunc(
|
|
func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
},
|
|
))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/test", nil)
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, rec.Code)
|
|
}
|
|
|
|
func TestAPISessionAuth_InvalidBearerTokenReturns401(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mw, _, _ := setupMiddleware(t)
|
|
|
|
handler := mw.APISessionAuth()(http.HandlerFunc(
|
|
func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
},
|
|
))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/test", nil)
|
|
req.Header.Set("Authorization", "Bearer invalid-token")
|
|
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, rec.Code)
|
|
}
|