mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-11-04 08:05:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			298 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package controller_test
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"net/http"
 | 
						|
	"net/http/httptest"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
	"tinyauth/internal/config"
 | 
						|
	"tinyauth/internal/controller"
 | 
						|
	"tinyauth/internal/service"
 | 
						|
 | 
						|
	"github.com/gin-gonic/gin"
 | 
						|
	"github.com/pquerna/otp/totp"
 | 
						|
	"gotest.tools/v3/assert"
 | 
						|
)
 | 
						|
 | 
						|
var cookieValue string
 | 
						|
var totpSecret = "6WFZXPEZRK5MZHHYAFW4DAOUYQMCASBJ"
 | 
						|
 | 
						|
func setupUserController(t *testing.T, middlewares *[]gin.HandlerFunc) (*gin.Engine, *httptest.ResponseRecorder) {
 | 
						|
	// Setup
 | 
						|
	gin.SetMode(gin.TestMode)
 | 
						|
	router := gin.Default()
 | 
						|
 | 
						|
	if middlewares != nil {
 | 
						|
		for _, m := range *middlewares {
 | 
						|
			router.Use(m)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	group := router.Group("/api")
 | 
						|
	recorder := httptest.NewRecorder()
 | 
						|
 | 
						|
	// Database
 | 
						|
	databaseService := service.NewDatabaseService(service.DatabaseServiceConfig{
 | 
						|
		DatabasePath: "/tmp/tinyauth_test.db",
 | 
						|
	})
 | 
						|
 | 
						|
	assert.NilError(t, databaseService.Init())
 | 
						|
 | 
						|
	database := databaseService.GetDatabase()
 | 
						|
 | 
						|
	// Auth service
 | 
						|
	authService := service.NewAuthService(service.AuthServiceConfig{
 | 
						|
		Users: []config.User{
 | 
						|
			{
 | 
						|
				Username: "testuser",
 | 
						|
				Password: "$2a$10$ne6z693sTgzT3ePoQ05PgOecUHnBjM7sSNj6M.l5CLUP.f6NyCnt.", // test
 | 
						|
			},
 | 
						|
			{
 | 
						|
				Username:   "totpuser",
 | 
						|
				Password:   "$2a$10$ne6z693sTgzT3ePoQ05PgOecUHnBjM7sSNj6M.l5CLUP.f6NyCnt.", // test
 | 
						|
				TotpSecret: totpSecret,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		OauthWhitelist:    "",
 | 
						|
		SessionExpiry:     3600,
 | 
						|
		SecureCookie:      false,
 | 
						|
		CookieDomain:      "localhost",
 | 
						|
		LoginTimeout:      300,
 | 
						|
		LoginMaxRetries:   3,
 | 
						|
		SessionCookieName: "tinyauth-session",
 | 
						|
	}, nil, nil, database)
 | 
						|
 | 
						|
	// Controller
 | 
						|
	ctrl := controller.NewUserController(controller.UserControllerConfig{
 | 
						|
		CookieDomain: "localhost",
 | 
						|
	}, group, authService)
 | 
						|
	ctrl.SetupRoutes()
 | 
						|
 | 
						|
	return router, recorder
 | 
						|
}
 | 
						|
 | 
						|
func TestLoginHandler(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	router, recorder := setupUserController(t, nil)
 | 
						|
 | 
						|
	loginReq := controller.LoginRequest{
 | 
						|
		Username: "testuser",
 | 
						|
		Password: "test",
 | 
						|
	}
 | 
						|
 | 
						|
	loginReqJson, err := json.Marshal(loginReq)
 | 
						|
	assert.NilError(t, err)
 | 
						|
 | 
						|
	// Test
 | 
						|
	req := httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(loginReqJson)))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 200, recorder.Code)
 | 
						|
 | 
						|
	cookie := recorder.Result().Cookies()[0]
 | 
						|
 | 
						|
	assert.Equal(t, "tinyauth-session", cookie.Name)
 | 
						|
	assert.Assert(t, cookie.Value != "")
 | 
						|
 | 
						|
	cookieValue = cookie.Value
 | 
						|
 | 
						|
	// Test invalid credentials
 | 
						|
	loginReq = controller.LoginRequest{
 | 
						|
		Username: "testuser",
 | 
						|
		Password: "invalid",
 | 
						|
	}
 | 
						|
 | 
						|
	loginReqJson, err = json.Marshal(loginReq)
 | 
						|
	assert.NilError(t, err)
 | 
						|
 | 
						|
	recorder = httptest.NewRecorder()
 | 
						|
	req = httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(loginReqJson)))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 401, recorder.Code)
 | 
						|
 | 
						|
	// Test totp required
 | 
						|
	loginReq = controller.LoginRequest{
 | 
						|
		Username: "totpuser",
 | 
						|
		Password: "test",
 | 
						|
	}
 | 
						|
 | 
						|
	loginReqJson, err = json.Marshal(loginReq)
 | 
						|
	assert.NilError(t, err)
 | 
						|
 | 
						|
	recorder = httptest.NewRecorder()
 | 
						|
	req = httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(loginReqJson)))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 200, recorder.Code)
 | 
						|
 | 
						|
	loginResJson, err := json.Marshal(map[string]any{
 | 
						|
		"message":     "TOTP required",
 | 
						|
		"status":      200,
 | 
						|
		"totpPending": true,
 | 
						|
	})
 | 
						|
 | 
						|
	assert.NilError(t, err)
 | 
						|
	assert.Equal(t, string(loginResJson), recorder.Body.String())
 | 
						|
 | 
						|
	// Test invalid json
 | 
						|
	recorder = httptest.NewRecorder()
 | 
						|
	req = httptest.NewRequest("POST", "/api/user/login", strings.NewReader("{invalid json}"))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 400, recorder.Code)
 | 
						|
 | 
						|
	// Test rate limiting
 | 
						|
	loginReq = controller.LoginRequest{
 | 
						|
		Username: "testuser",
 | 
						|
		Password: "invalid",
 | 
						|
	}
 | 
						|
 | 
						|
	loginReqJson, err = json.Marshal(loginReq)
 | 
						|
	assert.NilError(t, err)
 | 
						|
 | 
						|
	for range 5 {
 | 
						|
		recorder = httptest.NewRecorder()
 | 
						|
		req = httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(loginReqJson)))
 | 
						|
		router.ServeHTTP(recorder, req)
 | 
						|
	}
 | 
						|
 | 
						|
	assert.Equal(t, 429, recorder.Code)
 | 
						|
}
 | 
						|
 | 
						|
func TestLogoutHandler(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	router, recorder := setupUserController(t, nil)
 | 
						|
 | 
						|
	// Test
 | 
						|
	req := httptest.NewRequest("POST", "/api/user/logout", nil)
 | 
						|
 | 
						|
	req.AddCookie(&http.Cookie{
 | 
						|
		Name:  "tinyauth-session",
 | 
						|
		Value: cookieValue,
 | 
						|
	})
 | 
						|
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 200, recorder.Code)
 | 
						|
 | 
						|
	cookie := recorder.Result().Cookies()[0]
 | 
						|
 | 
						|
	assert.Equal(t, "tinyauth-session", cookie.Name)
 | 
						|
	assert.Equal(t, "", cookie.Value)
 | 
						|
	assert.Equal(t, -1, cookie.MaxAge)
 | 
						|
}
 | 
						|
 | 
						|
func TestTotpHandler(t *testing.T) {
 | 
						|
	// Setup
 | 
						|
	router, recorder := setupUserController(t, &[]gin.HandlerFunc{
 | 
						|
		func(c *gin.Context) {
 | 
						|
			c.Set("context", &config.UserContext{
 | 
						|
				Username:    "totpuser",
 | 
						|
				Name:        "totpuser",
 | 
						|
				Email:       "totpuser@example.com",
 | 
						|
				IsLoggedIn:  false,
 | 
						|
				OAuth:       false,
 | 
						|
				Provider:    "username",
 | 
						|
				TotpPending: true,
 | 
						|
				OAuthGroups: "",
 | 
						|
				TotpEnabled: true,
 | 
						|
			})
 | 
						|
			c.Next()
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	// Test
 | 
						|
	code, err := totp.GenerateCode(totpSecret, time.Now())
 | 
						|
 | 
						|
	assert.NilError(t, err)
 | 
						|
 | 
						|
	totpReq := controller.TotpRequest{
 | 
						|
		Code: code,
 | 
						|
	}
 | 
						|
 | 
						|
	totpReqJson, err := json.Marshal(totpReq)
 | 
						|
	assert.NilError(t, err)
 | 
						|
 | 
						|
	req := httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(totpReqJson)))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 200, recorder.Code)
 | 
						|
 | 
						|
	cookie := recorder.Result().Cookies()[0]
 | 
						|
 | 
						|
	assert.Equal(t, "tinyauth-session", cookie.Name)
 | 
						|
	assert.Assert(t, cookie.Value != "")
 | 
						|
 | 
						|
	// Test invalid json
 | 
						|
	recorder = httptest.NewRecorder()
 | 
						|
	req = httptest.NewRequest("POST", "/api/user/totp", strings.NewReader("{invalid json}"))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 400, recorder.Code)
 | 
						|
 | 
						|
	// Test rate limiting
 | 
						|
	totpReq = controller.TotpRequest{
 | 
						|
		Code: "000000",
 | 
						|
	}
 | 
						|
 | 
						|
	totpReqJson, err = json.Marshal(totpReq)
 | 
						|
	assert.NilError(t, err)
 | 
						|
 | 
						|
	for range 5 {
 | 
						|
		recorder = httptest.NewRecorder()
 | 
						|
		req = httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(totpReqJson)))
 | 
						|
		router.ServeHTTP(recorder, req)
 | 
						|
	}
 | 
						|
 | 
						|
	assert.Equal(t, 429, recorder.Code)
 | 
						|
 | 
						|
	// Test invalid code
 | 
						|
	router, recorder = setupUserController(t, &[]gin.HandlerFunc{
 | 
						|
		func(c *gin.Context) {
 | 
						|
			c.Set("context", &config.UserContext{
 | 
						|
				Username:    "totpuser",
 | 
						|
				Name:        "totpuser",
 | 
						|
				Email:       "totpuser@example.com",
 | 
						|
				IsLoggedIn:  false,
 | 
						|
				OAuth:       false,
 | 
						|
				Provider:    "username",
 | 
						|
				TotpPending: true,
 | 
						|
				OAuthGroups: "",
 | 
						|
				TotpEnabled: true,
 | 
						|
			})
 | 
						|
			c.Next()
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	req = httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(totpReqJson)))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 401, recorder.Code)
 | 
						|
 | 
						|
	// Test no totp pending
 | 
						|
	router, recorder = setupUserController(t, &[]gin.HandlerFunc{
 | 
						|
		func(c *gin.Context) {
 | 
						|
			c.Set("context", &config.UserContext{
 | 
						|
				Username:    "totpuser",
 | 
						|
				Name:        "totpuser",
 | 
						|
				Email:       "totpuser@example.com",
 | 
						|
				IsLoggedIn:  false,
 | 
						|
				OAuth:       false,
 | 
						|
				Provider:    "username",
 | 
						|
				TotpPending: false,
 | 
						|
				OAuthGroups: "",
 | 
						|
				TotpEnabled: false,
 | 
						|
			})
 | 
						|
			c.Next()
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	req = httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(totpReqJson)))
 | 
						|
	router.ServeHTTP(recorder, req)
 | 
						|
 | 
						|
	assert.Equal(t, 401, recorder.Code)
 | 
						|
}
 |