fix: fix controller tests

This commit is contained in:
Stavros
2026-05-04 20:07:03 +03:00
parent 004df2f852
commit c932817757
6 changed files with 154 additions and 117 deletions
+12 -8
View File
@@ -7,11 +7,11 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/stretchr/testify/assert"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/model"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
)
func TestContextController(t *testing.T) {
@@ -79,12 +79,16 @@ func TestContextController(t *testing.T) {
description: "Ensure user context returns when authorized",
middlewares: []gin.HandlerFunc{
func(c *gin.Context) {
c.Set("context", &config.UserContext{
Username: "johndoe",
Name: "John Doe",
Email: utils.CompileUserEmail("johndoe", controllerConfig.CookieDomain),
Provider: "local",
IsLoggedIn: true,
c.Set("context", &model.UserContext{
Authenticated: true,
Provider: model.ProviderLocal,
Local: &model.LocalContext{
BaseContext: model.BaseContext{
Username: "johndoe",
Name: "John Doe",
Email: utils.CompileUserEmail("johndoe", controllerConfig.CookieDomain),
},
},
})
},
},
+15 -11
View File
@@ -12,14 +12,14 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/go-querystring/query"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/model"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestOIDCController(t *testing.T) {
@@ -27,7 +27,7 @@ func TestOIDCController(t *testing.T) {
tempDir := t.TempDir()
oidcServiceCfg := service.OIDCServiceConfig{
Clients: map[string]config.OIDCClientConfig{
Clients: map[string]model.OIDCClientConfig{
"test": {
ClientID: "some-client-id",
ClientSecret: "some-client-secret",
@@ -44,12 +44,16 @@ func TestOIDCController(t *testing.T) {
controllerCfg := controller.OIDCControllerConfig{}
simpleCtx := func(c *gin.Context) {
c.Set("context", &config.UserContext{
Username: "test",
Name: "Test User",
Email: "test@example.com",
IsLoggedIn: true,
Provider: "local",
c.Set("context", &model.UserContext{
Authenticated: true,
Provider: model.ProviderLocal,
Local: &model.LocalContext{
BaseContext: model.BaseContext{
Username: "test",
Name: "Test User",
Email: "test@example.com",
},
},
})
c.Next()
}
@@ -848,7 +852,7 @@ func TestOIDCController(t *testing.T) {
},
}
app := bootstrap.NewBootstrapApp(config.Config{})
app := bootstrap.NewBootstrapApp(model.Config{})
db, err := app.SetupDatabase(path.Join(tempDir, "tinyauth.db"))
require.NoError(t, err)
+8 -8
View File
@@ -99,16 +99,12 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
return
}
if acls == nil {
acls = &model.App{}
}
tlog.App.Trace().Interface("acls", acls).Msg("ACLs for resource")
clientIP := c.ClientIP()
if controller.auth.IsBypassedIP(clientIP, acls) {
controller.setHeaders(c, *acls)
controller.setHeaders(c, acls)
c.JSON(200, gin.H{
"status": 200,
"message": "Authenticated",
@@ -126,7 +122,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
if !authEnabled {
tlog.App.Debug().Msg("Authentication disabled for resource, allowing access")
controller.setHeaders(c, *acls)
controller.setHeaders(c, acls)
c.JSON(200, gin.H{
"status": 200,
"message": "Authenticated",
@@ -267,7 +263,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
c.Header("Remote-Sub", utils.SanitizeHeader(userContext.OAuth.Sub))
}
controller.setHeaders(c, *acls)
controller.setHeaders(c, acls)
c.JSON(200, gin.H{
"status": 200,
@@ -300,9 +296,13 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
}
func (controller *ProxyController) setHeaders(c *gin.Context, acls model.App) {
func (controller *ProxyController) setHeaders(c *gin.Context, acls *model.App) {
c.Header("Authorization", c.Request.Header.Get("Authorization"))
if acls == nil {
return
}
headers := utils.ParseHeaders(acls.Response.Headers)
for key, value := range headers {
+35 -27
View File
@@ -6,14 +6,14 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/model"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestProxyController(t *testing.T) {
@@ -21,7 +21,7 @@ func TestProxyController(t *testing.T) {
tempDir := t.TempDir()
authServiceCfg := service.AuthServiceConfig{
Users: []config.User{
LocalUsers: []model.LocalUser{
{
Username: "testuser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
@@ -29,7 +29,7 @@ func TestProxyController(t *testing.T) {
{
Username: "totpuser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
TOTPSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
},
},
SessionExpiry: 10, // 10 seconds, useful for testing
@@ -43,28 +43,28 @@ func TestProxyController(t *testing.T) {
AppURL: "https://tinyauth.example.com",
}
acls := map[string]config.App{
acls := map[string]model.App{
"app_path_allow": {
Config: config.AppConfig{
Config: model.AppConfig{
Domain: "path-allow.example.com",
},
Path: config.AppPath{
Path: model.AppPath{
Allow: "/allowed",
},
},
"app_user_allow": {
Config: config.AppConfig{
Config: model.AppConfig{
Domain: "user-allow.example.com",
},
Users: config.AppUsers{
Users: model.AppUsers{
Allow: "testuser",
},
},
"ip_bypass": {
Config: config.AppConfig{
Config: model.AppConfig{
Domain: "ip-bypass.example.com",
},
IP: config.AppIP{
IP: model.AppIP{
Bypass: []string{"10.10.10.10"},
},
},
@@ -74,24 +74,32 @@ func TestProxyController(t *testing.T) {
Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Mobile Safari/537.36`
simpleCtx := func(c *gin.Context) {
c.Set("context", &config.UserContext{
Username: "testuser",
Name: "Testuser",
Email: "testuser@example.com",
IsLoggedIn: true,
Provider: "local",
c.Set("context", &model.UserContext{
Authenticated: true,
Provider: model.ProviderLocal,
Local: &model.LocalContext{
BaseContext: model.BaseContext{
Username: "testuser",
Name: "Testuser",
Email: "testuser@example.com",
},
},
})
c.Next()
}
simpleCtxTotp := func(c *gin.Context) {
c.Set("context", &config.UserContext{
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
IsLoggedIn: true,
Provider: "local",
TotpEnabled: true,
c.Set("context", &model.UserContext{
Authenticated: true,
Provider: model.ProviderLocal,
Local: &model.LocalContext{
BaseContext: model.BaseContext{
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
},
TOTPEnabled: true,
},
})
c.Next()
}
@@ -391,9 +399,9 @@ func TestProxyController(t *testing.T) {
},
}
oauthBrokerCfgs := make(map[string]config.OAuthServiceConfig)
oauthBrokerCfgs := make(map[string]model.OAuthServiceConfig)
app := bootstrap.NewBootstrapApp(config.Config{})
app := bootstrap.NewBootstrapApp(model.Config{})
db, err := app.SetupDatabase(path.Join(tempDir, "tinyauth.db"))
require.NoError(t, err)
+79 -58
View File
@@ -10,14 +10,14 @@ import (
"github.com/gin-gonic/gin"
"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/model"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUserController(t *testing.T) {
@@ -25,7 +25,7 @@ func TestUserController(t *testing.T) {
tempDir := t.TempDir()
authServiceCfg := service.AuthServiceConfig{
Users: []config.User{
LocalUsers: []model.LocalUser{
{
Username: "testuser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
@@ -33,12 +33,12 @@ func TestUserController(t *testing.T) {
{
Username: "totpuser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
TOTPSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
},
{
Username: "attruser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
Attributes: config.UserAttributes{
Attributes: model.UserAttributes{
Name: "Alice Smith",
Email: "alice@example.com",
},
@@ -46,8 +46,8 @@ func TestUserController(t *testing.T) {
{
Username: "attrtotpuser",
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
Attributes: config.UserAttributes{
TOTPSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
Attributes: model.UserAttributes{
Name: "Bob Jones",
Email: "bob@example.com",
},
@@ -61,7 +61,54 @@ func TestUserController(t *testing.T) {
}
userControllerCfg := controller.UserControllerConfig{
CookieDomain: "example.com",
CookieDomain: "example.com",
SessionCookieName: "tinyauth-session",
}
totpCtx := func(c *gin.Context) {
c.Set("context", &model.UserContext{
Authenticated: true,
Provider: model.ProviderLocal,
Local: &model.LocalContext{
BaseContext: model.BaseContext{
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
},
TOTPPending: true,
TOTPEnabled: true,
},
})
}
totpAttrCtx := func(c *gin.Context) {
c.Set("context", &model.UserContext{
Authenticated: true,
Provider: model.ProviderLocal,
Local: &model.LocalContext{
BaseContext: model.BaseContext{
Username: "attrtotpuser",
Name: "Bob Jones",
Email: "bob@example.com",
},
TOTPPending: true,
TOTPEnabled: true,
},
})
}
simpleCtx := func(c *gin.Context) {
c.Set("context", &model.UserContext{
Authenticated: true,
Provider: model.ProviderLocal,
Local: &model.LocalContext{
BaseContext: model.BaseContext{
Username: "testuser",
Name: "Test User",
Email: "testuser@example.com",
},
},
})
}
type testCase struct {
@@ -188,7 +235,9 @@ func TestUserController(t *testing.T) {
},
{
description: "Should be able to logout",
middlewares: []gin.HandlerFunc{},
middlewares: []gin.HandlerFunc{
simpleCtx,
},
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
// First login to get a session cookie
loginReq := controller.LoginRequest{
@@ -204,9 +253,10 @@ func TestUserController(t *testing.T) {
router.ServeHTTP(recorder, req)
assert.Equal(t, 200, recorder.Code)
assert.Len(t, recorder.Result().Cookies(), 1)
cookies := recorder.Result().Cookies()
assert.Len(t, cookies, 1)
cookie := recorder.Result().Cookies()[0]
cookie := cookies[0]
assert.Equal(t, "tinyauth-session", cookie.Name)
// Now logout using the session cookie
@@ -217,17 +267,20 @@ func TestUserController(t *testing.T) {
router.ServeHTTP(recorder, req)
assert.Equal(t, 200, recorder.Code)
assert.Len(t, recorder.Result().Cookies(), 1)
cookies = recorder.Result().Cookies()
assert.Len(t, cookies, 1)
logoutCookie := recorder.Result().Cookies()[0]
assert.Equal(t, "tinyauth-session", logoutCookie.Name)
assert.Equal(t, "", logoutCookie.Value)
assert.Equal(t, -1, logoutCookie.MaxAge) // MaxAge -1 means delete cookie
cookie = cookies[0]
assert.Equal(t, "tinyauth-session", cookie.Name)
assert.Equal(t, "", cookie.Value)
assert.Equal(t, -1, cookie.MaxAge) // MaxAge -1 means delete cookie
},
},
{
description: "Should be able to login with totp",
middlewares: []gin.HandlerFunc{},
middlewares: []gin.HandlerFunc{
totpCtx,
},
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
code, err := totp.GenerateCode("JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK", time.Now())
assert.NoError(t, err)
@@ -258,7 +311,9 @@ func TestUserController(t *testing.T) {
},
{
description: "Totp should rate limit on multiple invalid attempts",
middlewares: []gin.HandlerFunc{},
middlewares: []gin.HandlerFunc{
totpCtx,
},
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
for range 3 {
totpReq := controller.TotpRequest{
@@ -328,7 +383,9 @@ func TestUserController(t *testing.T) {
},
{
description: "TOTP completion uses name and email from user attributes",
middlewares: []gin.HandlerFunc{},
middlewares: []gin.HandlerFunc{
totpAttrCtx,
},
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
code, err := totp.GenerateCode("JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK", time.Now())
require.NoError(t, err)
@@ -349,9 +406,9 @@ func TestUserController(t *testing.T) {
},
}
oauthBrokerCfgs := make(map[string]config.OAuthServiceConfig)
oauthBrokerCfgs := make(map[string]model.OAuthServiceConfig)
app := bootstrap.NewBootstrapApp(config.Config{})
app := bootstrap.NewBootstrapApp(model.Config{})
db, err := app.SetupDatabase(path.Join(tempDir, "tinyauth.db"))
require.NoError(t, err)
@@ -379,33 +436,6 @@ func TestUserController(t *testing.T) {
authService.ClearRateLimitsTestingOnly()
}
setTotpMiddlewareOverrides := map[string]config.UserContext{
"Should be able to login with totp": {
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
Provider: "local",
TotpPending: true,
TotpEnabled: true,
},
"Totp should rate limit on multiple invalid attempts": {
Username: "totpuser",
Name: "Totpuser",
Email: "totpuser@example.com",
Provider: "local",
TotpPending: true,
TotpEnabled: true,
},
"TOTP completion uses name and email from user attributes": {
Username: "attrtotpuser",
Name: "Bob Jones",
Email: "bob@example.com",
Provider: "local",
TotpPending: true,
TotpEnabled: true,
},
}
for _, test := range tests {
beforeEach()
t.Run(test.description, func(t *testing.T) {
@@ -415,15 +445,6 @@ func TestUserController(t *testing.T) {
router.Use(middleware)
}
// Gin is stupid and doesn't allow setting a middleware after the groups
// so we need to do some stupid overrides here
if ctx, ok := setTotpMiddlewareOverrides[test.description]; ok {
ctx := ctx
router.Use(func(c *gin.Context) {
c.Set("context", &ctx)
})
}
group := router.Group("/api")
gin.SetMode(gin.TestMode)
@@ -8,14 +8,14 @@ import (
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tinyauthapp/tinyauth/internal/bootstrap"
"github.com/tinyauthapp/tinyauth/internal/config"
"github.com/tinyauthapp/tinyauth/internal/controller"
"github.com/tinyauthapp/tinyauth/internal/model"
"github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWellKnownController(t *testing.T) {
@@ -23,7 +23,7 @@ func TestWellKnownController(t *testing.T) {
tempDir := t.TempDir()
oidcServiceCfg := service.OIDCServiceConfig{
Clients: map[string]config.OIDCClientConfig{
Clients: map[string]model.OIDCClientConfig{
"test": {
ClientID: "some-client-id",
ClientSecret: "some-client-secret",
@@ -101,7 +101,7 @@ func TestWellKnownController(t *testing.T) {
},
}
app := bootstrap.NewBootstrapApp(config.Config{})
app := bootstrap.NewBootstrapApp(model.Config{})
db, err := app.SetupDatabase(path.Join(tempDir, "tinyauth.db"))
require.NoError(t, err)