mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 04:35:40 +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)
|
|
}
|