mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-05-20 19:20:14 +00:00
Merge branch 'main' into feat/tailscale
This commit is contained in:
@@ -208,7 +208,12 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
||||
name = user.Name
|
||||
} else {
|
||||
controller.log.App.Debug().Msg("No name from OAuth provider, generating from email")
|
||||
name = fmt.Sprintf("%s (%s)", utils.Capitalize(strings.Split(user.Email, "@")[0]), strings.Split(user.Email, "@")[1])
|
||||
parts := strings.SplitN(user.Email, "@", 2)
|
||||
if len(parts) == 2 {
|
||||
name = fmt.Sprintf("%s (%s)", utils.Capitalize(parts[0]), parts[1])
|
||||
} else {
|
||||
name = utils.Capitalize(user.Email)
|
||||
}
|
||||
}
|
||||
|
||||
var username string
|
||||
|
||||
@@ -146,7 +146,7 @@ func (controller *OIDCController) Authorize(c *gin.Context) {
|
||||
client, ok := controller.oidc.GetClient(req.ClientID)
|
||||
|
||||
if !ok {
|
||||
controller.authorizeError(c, err, "Client not found", "The client ID is invalid", "", "", "")
|
||||
controller.authorizeError(c, fmt.Errorf("client not found: %s", req.ClientID), "Client not found", "The client ID is invalid", "", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ func (controller *OIDCController) Token(c *gin.Context) {
|
||||
entry, err := controller.oidc.GetCodeEntry(c, controller.oidc.Hash(req.Code), client.ClientID)
|
||||
if err != nil {
|
||||
if err := controller.oidc.DeleteTokenByCodeHash(c, controller.oidc.Hash(req.Code)); err != nil {
|
||||
controller.log.App.Error().Err(err).Msg("Failed to delete code")
|
||||
controller.log.App.Error().Err(err).Msg("Failed to revoke tokens for replayed code")
|
||||
}
|
||||
if errors.Is(err, service.ErrCodeNotFound) {
|
||||
controller.log.App.Warn().Msg("Code not found")
|
||||
|
||||
@@ -15,10 +15,9 @@ import (
|
||||
"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/controller"
|
||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
|
||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||
"github.com/tinyauthapp/tinyauth/internal/test"
|
||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||
@@ -839,16 +838,11 @@ func TestOIDCController(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app := bootstrap.NewBootstrapApp(cfg)
|
||||
|
||||
err := app.SetupDatabase()
|
||||
require.NoError(t, err)
|
||||
|
||||
queries := repository.New(app.GetDB())
|
||||
store := memory.New()
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
oidcService, err := service.NewOIDCService(log, cfg, runtime, queries, context.TODO(), wg)
|
||||
oidcService, err := service.NewOIDCService(log, cfg, runtime, store, context.TODO(), wg)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -869,8 +863,4 @@ func TestOIDCController(t *testing.T) {
|
||||
test.run(t, router, recorder)
|
||||
})
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
app.GetDB().Close()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package controller
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -51,10 +52,11 @@ type ProxyContext struct {
|
||||
}
|
||||
|
||||
type ProxyController struct {
|
||||
log *logger.Logger
|
||||
runtime model.RuntimeConfig
|
||||
acls *service.AccessControlsService
|
||||
auth *service.AuthService
|
||||
log *logger.Logger
|
||||
runtime model.RuntimeConfig
|
||||
acls *service.AccessControlsService
|
||||
auth *service.AuthService
|
||||
policyEngine *service.PolicyEngine
|
||||
}
|
||||
|
||||
func NewProxyController(
|
||||
@@ -63,12 +65,14 @@ func NewProxyController(
|
||||
router *gin.RouterGroup,
|
||||
acls *service.AccessControlsService,
|
||||
auth *service.AuthService,
|
||||
policyEngine *service.PolicyEngine,
|
||||
) *ProxyController {
|
||||
controller := &ProxyController{
|
||||
log: log,
|
||||
runtime: runtime,
|
||||
acls: acls,
|
||||
auth: auth,
|
||||
log: log,
|
||||
runtime: runtime,
|
||||
acls: acls,
|
||||
auth: auth,
|
||||
policyEngine: policyEngine,
|
||||
}
|
||||
|
||||
proxyGroup := router.Group("/auth")
|
||||
@@ -101,7 +105,13 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
||||
|
||||
clientIP := c.ClientIP()
|
||||
|
||||
if controller.auth.IsBypassedIP(clientIP, acls) {
|
||||
aclsCtx := &service.ACLContext{
|
||||
ACLs: acls,
|
||||
IP: net.ParseIP(clientIP),
|
||||
Path: proxyCtx.Path,
|
||||
}
|
||||
|
||||
if controller.policyEngine.Evaluate(service.RuleIPBypassed, aclsCtx) {
|
||||
controller.setHeaders(c, acls)
|
||||
c.JSON(200, gin.H{
|
||||
"status": 200,
|
||||
@@ -110,15 +120,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
authEnabled, err := controller.auth.IsAuthEnabled(proxyCtx.Path, acls)
|
||||
|
||||
if err != nil {
|
||||
controller.log.App.Error().Err(err).Msg("Failed to determine if authentication is enabled for resource")
|
||||
controller.handleError(c, proxyCtx)
|
||||
return
|
||||
}
|
||||
|
||||
if !authEnabled {
|
||||
if controller.policyEngine.Evaluate(service.RuleAuthEnabled, aclsCtx) {
|
||||
controller.log.App.Debug().Msg("Authentication is disabled for this resource, allowing access without authentication")
|
||||
controller.setHeaders(c, acls)
|
||||
c.JSON(200, gin.H{
|
||||
@@ -128,7 +130,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !controller.auth.CheckIP(clientIP, acls) {
|
||||
if !controller.policyEngine.Evaluate(service.RuleIPAllowed, aclsCtx) {
|
||||
queries, err := query.Values(UnauthorizedQuery{
|
||||
Resource: strings.Split(proxyCtx.Host, ".")[0],
|
||||
IP: clientIP,
|
||||
@@ -144,9 +146,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
||||
|
||||
if !controller.useBrowserResponse(proxyCtx) {
|
||||
c.Header("x-tinyauth-location", redirectURL)
|
||||
c.JSON(401, gin.H{
|
||||
"status": 401,
|
||||
"message": "Unauthorized",
|
||||
c.JSON(403, gin.H{
|
||||
"status": 403,
|
||||
"message": "Forbidden",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -164,10 +166,10 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if userContext.Authenticated {
|
||||
userAllowed := controller.auth.IsUserAllowed(c, *userContext, acls)
|
||||
aclsCtx.UserContext = userContext
|
||||
|
||||
if !userAllowed {
|
||||
if userContext.Authenticated {
|
||||
if !controller.policyEngine.Evaluate(service.RuleUserAllowed, aclsCtx) {
|
||||
controller.log.App.Warn().Str("user", userContext.GetUsername()).Str("resource", strings.Split(proxyCtx.Host, ".")[0]).Msg("User is not allowed to access resource")
|
||||
|
||||
queries, err := query.Values(UnauthorizedQuery{
|
||||
@@ -205,9 +207,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
||||
var groupOK bool
|
||||
|
||||
if userContext.IsOAuth() {
|
||||
groupOK = controller.auth.IsInOAuthGroup(c, *userContext, acls)
|
||||
groupOK = controller.policyEngine.Evaluate(service.RuleOAuthGroup, aclsCtx)
|
||||
} else {
|
||||
groupOK = controller.auth.IsInLDAPGroup(c, *userContext, acls)
|
||||
groupOK = controller.policyEngine.Evaluate(service.RuleLDAPGroup, aclsCtx)
|
||||
}
|
||||
|
||||
if !groupOK {
|
||||
|
||||
@@ -9,10 +9,9 @@ import (
|
||||
"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/controller"
|
||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
|
||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||
"github.com/tinyauthapp/tinyauth/internal/test"
|
||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||
@@ -24,33 +23,6 @@ func TestProxyController(t *testing.T) {
|
||||
|
||||
cfg, runtime := test.CreateTestConfigs(t)
|
||||
|
||||
acls := map[string]model.App{
|
||||
"app_path_allow": {
|
||||
Config: model.AppConfig{
|
||||
Domain: "path-allow.example.com",
|
||||
},
|
||||
Path: model.AppPath{
|
||||
Allow: "/allowed",
|
||||
},
|
||||
},
|
||||
"app_user_allow": {
|
||||
Config: model.AppConfig{
|
||||
Domain: "user-allow.example.com",
|
||||
},
|
||||
Users: model.AppUsers{
|
||||
Allow: "testuser",
|
||||
},
|
||||
},
|
||||
"ip_bypass": {
|
||||
Config: model.AppConfig{
|
||||
Domain: "ip-bypass.example.com",
|
||||
},
|
||||
IP: model.AppIP{
|
||||
Bypass: []string{"10.10.10.10"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const browserUserAgent = `
|
||||
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`
|
||||
|
||||
@@ -379,19 +351,37 @@ func TestProxyController(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app := bootstrap.NewBootstrapApp(cfg)
|
||||
|
||||
err := app.SetupDatabase()
|
||||
require.NoError(t, err)
|
||||
|
||||
queries := repository.New(app.GetDB())
|
||||
store := memory.New()
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
ctx := context.TODO()
|
||||
|
||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
||||
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, queries, broker, nil)
|
||||
aclsService := service.NewAccessControlsService(log, nil, acls)
|
||||
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil)
|
||||
aclsService := service.NewAccessControlsService(log, cfg, nil)
|
||||
|
||||
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
||||
require.NoError(t, err)
|
||||
|
||||
policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{
|
||||
Log: log,
|
||||
})
|
||||
policyEngine.RegisterRule(service.RuleOAuthGroup, &service.OAuthGroupRule{
|
||||
Log: log,
|
||||
})
|
||||
policyEngine.RegisterRule(service.RuleLDAPGroup, &service.LDAPGroupRule{
|
||||
Log: log,
|
||||
})
|
||||
policyEngine.RegisterRule(service.RuleAuthEnabled, &service.AuthEnabledRule{
|
||||
Log: log,
|
||||
})
|
||||
policyEngine.RegisterRule(service.RuleIPAllowed, &service.IPAllowedRule{
|
||||
Log: log,
|
||||
Config: cfg,
|
||||
})
|
||||
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
|
||||
Log: log,
|
||||
})
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
@@ -406,13 +396,9 @@ func TestProxyController(t *testing.T) {
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
controller.NewProxyController(log, runtime, group, aclsService, authService)
|
||||
controller.NewProxyController(log, runtime, group, aclsService, authService, policyEngine)
|
||||
|
||||
test.run(t, router, recorder)
|
||||
})
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
app.GetDB().Close()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func (controller *ResourcesController) resourcesHandler(c *gin.Context) {
|
||||
if controller.config.Resources.Path == "" {
|
||||
c.JSON(404, gin.H{
|
||||
"status": 404,
|
||||
"message": "Resources not found",
|
||||
"message": "Resource not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -190,6 +190,9 @@ func (controller *UserController) loginHandler(c *gin.Context) {
|
||||
|
||||
if search.Type == model.UserLDAP {
|
||||
sessionCookie.Provider = "ldap"
|
||||
if search.Email != "" {
|
||||
sessionCookie.Email = search.Email
|
||||
}
|
||||
}
|
||||
|
||||
cookie, err := controller.auth.CreateSession(c, sessionCookie)
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
"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/controller"
|
||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
|
||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||
"github.com/tinyauthapp/tinyauth/internal/test"
|
||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||
@@ -73,12 +73,7 @@ func TestUserController(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
app := bootstrap.NewBootstrapApp(cfg)
|
||||
|
||||
err := app.SetupDatabase()
|
||||
require.NoError(t, err)
|
||||
|
||||
queries := repository.New(app.GetDB())
|
||||
store := memory.New()
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
@@ -254,7 +249,7 @@ func TestUserController(t *testing.T) {
|
||||
totpCtx,
|
||||
},
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||
_, err := queries.CreateSession(context.TODO(), repository.CreateSessionParams{
|
||||
_, err := store.CreateSession(context.TODO(), repository.CreateSessionParams{
|
||||
UUID: "test-totp-login-uuid",
|
||||
Username: "test",
|
||||
Email: "test@example.com",
|
||||
@@ -378,7 +373,7 @@ func TestUserController(t *testing.T) {
|
||||
totpAttrCtx,
|
||||
},
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||
_, err := queries.CreateSession(context.TODO(), repository.CreateSessionParams{
|
||||
_, err := store.CreateSession(context.TODO(), repository.CreateSessionParams{
|
||||
UUID: "test-totp-login-attributes-uuid",
|
||||
Username: "test",
|
||||
Email: "test@example.com",
|
||||
@@ -420,7 +415,7 @@ func TestUserController(t *testing.T) {
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
||||
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, queries, broker, nil)
|
||||
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil)
|
||||
|
||||
beforeEach := func() {
|
||||
// Clear failed login attempts before each test
|
||||
@@ -446,8 +441,4 @@ func TestUserController(t *testing.T) {
|
||||
test.run(t, router, recorder)
|
||||
})
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
app.GetDB().Close()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,9 +11,8 @@ import (
|
||||
"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/controller"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
|
||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||
"github.com/tinyauthapp/tinyauth/internal/test"
|
||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||
@@ -92,14 +91,9 @@ func TestWellKnownController(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
app := bootstrap.NewBootstrapApp(cfg)
|
||||
store := memory.New()
|
||||
|
||||
err := app.SetupDatabase()
|
||||
require.NoError(t, err)
|
||||
|
||||
queries := repository.New(app.GetDB())
|
||||
|
||||
oidcService, err := service.NewOIDCService(log, cfg, runtime, queries, ctx, wg)
|
||||
oidcService, err := service.NewOIDCService(log, cfg, runtime, store, ctx, wg)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -114,8 +108,4 @@ func TestWellKnownController(t *testing.T) {
|
||||
test.run(t, router, recorder)
|
||||
})
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
app.GetDB().Close()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user