mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-06-14 15:30:16 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8426db306d | |||
| 4b323d07ab | |||
| 568809a9dc | |||
| e4dc3ca2e4 | |||
| f8b85e3bc7 | |||
| 7cd3719734 | |||
| c51ec3c7f6 |
@@ -21,6 +21,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298
|
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298
|
||||||
github.com/weppos/publicsuffix-go v0.50.3
|
github.com/weppos/publicsuffix-go v0.50.3
|
||||||
|
go.uber.org/dig v1.19.0
|
||||||
golang.org/x/crypto v0.52.0
|
golang.org/x/crypto v0.52.0
|
||||||
golang.org/x/oauth2 v0.36.0
|
golang.org/x/oauth2 v0.36.0
|
||||||
golang.org/x/tools v0.45.0
|
golang.org/x/tools v0.45.0
|
||||||
|
|||||||
@@ -485,6 +485,8 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09
|
|||||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||||
|
go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=
|
||||||
|
go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
||||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/steveiliop56/ding"
|
"github.com/steveiliop56/ding"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||||
@@ -31,10 +32,23 @@ import (
|
|||||||
// 2. HTTP server listeners - ding.RingNormal
|
// 2. HTTP server listeners - ding.RingNormal
|
||||||
// 3. Networking layers, user and label providers (e.g. ailscale service, kubernetes service) - ding.RingMajor
|
// 3. Networking layers, user and label providers (e.g. ailscale service, kubernetes service) - ding.RingMajor
|
||||||
// 4. Database connection - ding.RingCritical
|
// 4. Database connection - ding.RingCritical
|
||||||
|
|
||||||
|
type Services struct {
|
||||||
|
accessControlService *service.AccessControlsService
|
||||||
|
authService *service.AuthService
|
||||||
|
dockerService *service.DockerService
|
||||||
|
kubernetesService *service.KubernetesService
|
||||||
|
ldapService *service.LdapService
|
||||||
|
oauthBrokerService *service.OAuthBrokerService
|
||||||
|
oidcService *service.OIDCService
|
||||||
|
tailscaleService *service.TailscaleService
|
||||||
|
policyEngine *service.PolicyEngine
|
||||||
|
}
|
||||||
|
|
||||||
type BootstrapApp struct {
|
type BootstrapApp struct {
|
||||||
config model.Config
|
config model.Config
|
||||||
runtime model.RuntimeConfig
|
runtime model.RuntimeConfig
|
||||||
services service.Services
|
services Services
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
@@ -43,9 +57,7 @@ type BootstrapApp struct {
|
|||||||
db *sql.DB
|
db *sql.DB
|
||||||
ding *ding.Ding
|
ding *ding.Ding
|
||||||
listeners []Listener
|
listeners []Listener
|
||||||
deps struct {
|
dig *dig.Container
|
||||||
service *service.ServiceDependencies
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBootstrapApp(config model.Config) *BootstrapApp {
|
func NewBootstrapApp(config model.Config) *BootstrapApp {
|
||||||
@@ -60,7 +72,11 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
app.ctx = ctx
|
app.ctx = ctx
|
||||||
app.cancel = cancel
|
app.cancel = cancel
|
||||||
|
|
||||||
// Create a ding instance
|
// create the dig container
|
||||||
|
c := dig.New()
|
||||||
|
app.dig = c
|
||||||
|
|
||||||
|
// create a ding instance
|
||||||
dg := ding.New(ctx)
|
dg := ding.New(ctx)
|
||||||
app.ding = dg
|
app.ding = dg
|
||||||
|
|
||||||
@@ -147,12 +163,6 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
app.runtime.OAuthProviders[id] = provider
|
app.runtime.OAuthProviders[id] = provider
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup oidc clients
|
|
||||||
for id, client := range app.config.OIDC.Clients {
|
|
||||||
client.ID = id
|
|
||||||
app.runtime.OIDCClients = append(app.runtime.OIDCClients, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cookie domain
|
// cookie domain
|
||||||
cookieDomainResolver := utils.GetCookieDomain
|
cookieDomainResolver := utils.GetCookieDomain
|
||||||
|
|
||||||
@@ -201,6 +211,33 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
// store
|
// store
|
||||||
app.queries = store
|
app.queries = store
|
||||||
|
|
||||||
|
// provide basic utilities to container
|
||||||
|
type utilityProvider struct {
|
||||||
|
dig.Out
|
||||||
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
Runtime *model.RuntimeConfig
|
||||||
|
Ding *ding.Ding
|
||||||
|
Ctx context.Context
|
||||||
|
Queries repository.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(func() utilityProvider {
|
||||||
|
return utilityProvider{
|
||||||
|
Log: app.log,
|
||||||
|
Config: &app.config,
|
||||||
|
Runtime: &app.runtime,
|
||||||
|
Ding: app.ding,
|
||||||
|
Ctx: app.ctx,
|
||||||
|
Queries: app.queries,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide utilities to container: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// services
|
// services
|
||||||
err = app.setupServices()
|
err = app.setupServices()
|
||||||
|
|
||||||
@@ -223,7 +260,7 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
return configuredProviders[i].Name < configuredProviders[j].Name
|
return configuredProviders[i].Name < configuredProviders[j].Name
|
||||||
})
|
})
|
||||||
|
|
||||||
if app.services.AuthService.LocalAuthConfigured() {
|
if app.services.authService.LocalAuthConfigured() {
|
||||||
configuredProviders = append(configuredProviders, model.Provider{
|
configuredProviders = append(configuredProviders, model.Provider{
|
||||||
Name: "Local",
|
Name: "Local",
|
||||||
ID: "local",
|
ID: "local",
|
||||||
@@ -231,7 +268,7 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.services.AuthService.LDAPAuthConfigured() {
|
if app.services.authService.LDAPAuthConfigured() {
|
||||||
configuredProviders = append(configuredProviders, model.Provider{
|
configuredProviders = append(configuredProviders, model.Provider{
|
||||||
Name: "LDAP",
|
Name: "LDAP",
|
||||||
ID: "ldap",
|
ID: "ldap",
|
||||||
@@ -250,8 +287,8 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
app.runtime.ConfiguredProviders = configuredProviders
|
app.runtime.ConfiguredProviders = configuredProviders
|
||||||
|
|
||||||
// throw in tailscale if it's configured just before setting up the controllers
|
// throw in tailscale if it's configured just before setting up the controllers
|
||||||
if app.services.TailscaleService != nil {
|
if app.services.tailscaleService != nil {
|
||||||
app.runtime.TrustedDomains = append(app.runtime.TrustedDomains, "https://"+app.services.TailscaleService.GetHostname())
|
app.runtime.TrustedDomains = append(app.runtime.TrustedDomains, "https://"+app.services.tailscaleService.GetHostname())
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup router
|
// setup router
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/controller"
|
"github.com/tinyauthapp/tinyauth/internal/controller"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/middleware"
|
"github.com/tinyauthapp/tinyauth/internal/middleware"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -40,31 +41,119 @@ func (app *BootstrapApp) setupRouter() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contextMiddleware := middleware.NewContextMiddleware(app.log, app.runtime, app.services.AuthService, app.services.OAuthBrokerService, app.services.TailscaleService)
|
err := app.dig.Provide(middleware.NewContextMiddleware)
|
||||||
engine.Use(contextMiddleware.Middleware())
|
|
||||||
|
|
||||||
uiMiddleware, err := middleware.NewUIMiddleware()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize UI middleware: %w", err)
|
return fmt.Errorf("failed to provide context middleware: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.Use(uiMiddleware.Middleware())
|
err = app.dig.Provide(middleware.NewUIMiddleware)
|
||||||
|
|
||||||
zerologMiddleware := middleware.NewZerologMiddleware(app.log)
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide ui middleware: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
engine.Use(zerologMiddleware.Middleware())
|
err = app.dig.Provide(middleware.NewZerologMiddleware)
|
||||||
|
|
||||||
apiRouter := engine.Group("/api")
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide zerolog middleware: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
controller.NewContextController(app.log, app.config, app.runtime, apiRouter)
|
type middlewareInput struct {
|
||||||
controller.NewOAuthController(app.log, app.config, app.runtime, apiRouter, app.services.AuthService)
|
dig.In
|
||||||
controller.NewOIDCController(app.log, app.services.OIDCService, app.runtime, apiRouter, &engine.RouterGroup)
|
|
||||||
controller.NewProxyController(app.log, app.runtime, apiRouter, app.services.AccessControlService, app.services.AuthService, app.services.PolicyEngine)
|
ContextMiddleware *middleware.ContextMiddleware
|
||||||
controller.NewUserController(app.log, app.runtime, apiRouter, app.services.AuthService)
|
UIMiddleware *middleware.UIMiddleware
|
||||||
controller.NewResourcesController(app.config, &engine.RouterGroup)
|
ZerologMiddleware *middleware.ZerologMiddleware
|
||||||
controller.NewHealthController(apiRouter)
|
}
|
||||||
controller.NewWellKnownController(app.services.OIDCService, &engine.RouterGroup)
|
|
||||||
|
err = app.dig.Invoke(func(mi middlewareInput) {
|
||||||
|
engine.Use(mi.ContextMiddleware.Middleware())
|
||||||
|
engine.Use(mi.UIMiddleware.Middleware())
|
||||||
|
engine.Use(mi.ZerologMiddleware.Middleware())
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to invoke middleware: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(func() *gin.RouterGroup {
|
||||||
|
return &engine.RouterGroup
|
||||||
|
}, dig.Name("mainRouterGroup"))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide main router group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(func() *gin.RouterGroup {
|
||||||
|
return engine.Group("/api")
|
||||||
|
}, dig.Name("apiRouterGroup"))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide api router group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewContextController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide context controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewOAuthController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide oauth controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewOIDCController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide oidc controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewProxyController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide proxy controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewUserController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide user controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewResourcesController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide resources controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewHealthController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide health controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(controller.NewWellKnownController)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide well-known controller: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type controllerInput struct {
|
||||||
|
dig.In
|
||||||
|
|
||||||
|
ContextController *controller.ContextController
|
||||||
|
OAuthController *controller.OAuthController
|
||||||
|
OIDCController *controller.OIDCController
|
||||||
|
ProxyController *controller.ProxyController
|
||||||
|
UserController *controller.UserController
|
||||||
|
ResourcesController *controller.ResourcesController
|
||||||
|
HealthController *controller.HealthController
|
||||||
|
WellKnownController *controller.WellKnownController
|
||||||
|
}
|
||||||
|
|
||||||
|
// force dig to build all controllers and register their routes
|
||||||
|
err = app.dig.Invoke(func(ci controllerInput) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to invoke controllers: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
app.router = engine
|
app.router = engine
|
||||||
return nil
|
return nil
|
||||||
@@ -99,7 +188,7 @@ func (app *BootstrapApp) calculateListenerPolicy() []Listener {
|
|||||||
l := []Listener{}
|
l := []Listener{}
|
||||||
|
|
||||||
if !app.config.Server.ConcurrentListenersEnabled {
|
if !app.config.Server.ConcurrentListenersEnabled {
|
||||||
if app.services.TailscaleService != nil {
|
if app.services.tailscaleService != nil {
|
||||||
l = append(l, ListenerTailscale)
|
l = append(l, ListenerTailscale)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
@@ -117,7 +206,7 @@ func (app *BootstrapApp) calculateListenerPolicy() []Listener {
|
|||||||
l = append(l, ListenerUnix)
|
l = append(l, ListenerUnix)
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.services.TailscaleService != nil {
|
if app.services.tailscaleService != nil {
|
||||||
l = append(l, ListenerTailscale)
|
l = append(l, ListenerTailscale)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,9 +275,9 @@ func (app *BootstrapApp) serveUnix(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (app *BootstrapApp) serveTailscale(ctx context.Context) error {
|
func (app *BootstrapApp) serveTailscale(ctx context.Context) error {
|
||||||
app.log.App.Info().Msgf("Starting Tailscale server on %s", fmt.Sprintf("https://%s", app.services.TailscaleService.GetHostname()))
|
app.log.App.Info().Msgf("Starting Tailscale server on %s", fmt.Sprintf("https://%s", app.services.tailscaleService.GetHostname()))
|
||||||
|
|
||||||
listener, err := app.services.TailscaleService.CreateListener()
|
listener, err := app.services.tailscaleService.CreateListener()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create tailscale listener: %w", err)
|
return fmt.Errorf("failed to create tailscale listener: %w", err)
|
||||||
|
|||||||
@@ -5,66 +5,84 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *BootstrapApp) setupServices() error {
|
func (app *BootstrapApp) setupServices() error {
|
||||||
app.deps.service = &service.ServiceDependencies{
|
err := app.setupPolicyEngine()
|
||||||
Log: app.log,
|
|
||||||
StaticConfig: &app.config,
|
|
||||||
RuntimeConfig: &app.runtime,
|
|
||||||
Ctx: app.ctx,
|
|
||||||
Ding: app.ding,
|
|
||||||
Services: &app.services,
|
|
||||||
Queries: &app.queries,
|
|
||||||
}
|
|
||||||
|
|
||||||
ldap, err := service.NewLdapService(app.deps.service)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.log.App.Warn().Err(err).Msg("Failed to initialize LDAP connection, will continue without it")
|
return fmt.Errorf("failed to setup policy engine: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.services.LDAPService = ldap
|
|
||||||
|
|
||||||
labelProvider, err := app.getLabelProvider()
|
labelProvider, err := app.getLabelProvider()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize label provider: %w", err)
|
return fmt.Errorf("failed to get label provider: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.deps.service.LabelProvider = labelProvider
|
err = app.dig.Provide(func() service.LabelProvider {
|
||||||
|
return labelProvider
|
||||||
tailscaleService, err := service.NewTailscaleService(app.deps.service)
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.log.App.Warn().Err(err).Msg("Failed to initialize Tailscale connection, will continue without it")
|
return fmt.Errorf("failed to provide label provider: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.services.TailscaleService = tailscaleService
|
err = app.dig.Provide(service.NewLdapService)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide ldap service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
accessControlsService := service.NewAccessControlsService(app.deps.service)
|
err = app.dig.Provide(service.NewTailscaleService)
|
||||||
app.services.AccessControlService = accessControlsService
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide tailscale service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = app.setupPolicyEngine()
|
err = app.dig.Provide(service.NewAccessControlsService)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide access controls service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(service.NewOAuthBrokerService)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide oauth broker service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(service.NewAuthService)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide auth service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Provide(service.NewOIDCService)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to provide oidc service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type svcInput struct {
|
||||||
|
dig.In
|
||||||
|
|
||||||
|
AccessControlService *service.AccessControlsService
|
||||||
|
AuthService *service.AuthService
|
||||||
|
LDAPService *service.LdapService
|
||||||
|
OAuthBrokerService *service.OAuthBrokerService
|
||||||
|
OIDCService *service.OIDCService
|
||||||
|
TailscaleService *service.TailscaleService
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.dig.Invoke(func(i svcInput) error {
|
||||||
|
app.services.accessControlService = i.AccessControlService
|
||||||
|
app.services.authService = i.AuthService
|
||||||
|
app.services.ldapService = i.LDAPService
|
||||||
|
app.services.oauthBrokerService = i.OAuthBrokerService
|
||||||
|
app.services.tailscaleService = i.TailscaleService
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize policy engine: %w", err)
|
return fmt.Errorf("failed to invoke services: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
oauthBrokerService := service.NewOAuthBrokerService(app.deps.service)
|
|
||||||
app.services.OAuthBrokerService = oauthBrokerService
|
|
||||||
|
|
||||||
authService := service.NewAuthService(app.deps.service)
|
|
||||||
app.services.AuthService = authService
|
|
||||||
|
|
||||||
oidcService, err := service.NewOIDCService(app.deps.service)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to initialize oidc service: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.services.OIDCService = oidcService
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,66 +99,93 @@ func (app *BootstrapApp) getLabelProvider() (service.LabelProvider, error) {
|
|||||||
if useKubernetes {
|
if useKubernetes {
|
||||||
app.log.App.Debug().Msg("Using Kubernetes label provider")
|
app.log.App.Debug().Msg("Using Kubernetes label provider")
|
||||||
|
|
||||||
kubernetesService, err := service.NewKubernetesService(app.deps.service)
|
err := app.dig.Provide(service.NewKubernetesService)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize kubernetes service: %w", err)
|
return nil, fmt.Errorf("failed to provide kubernetes service: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.services.KubernetesService = kubernetesService
|
err = app.dig.Invoke(func(k *service.KubernetesService) error {
|
||||||
return kubernetesService, nil
|
app.services.kubernetesService = k
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to invoke kubernetes service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kubernetes will fail to initialize with an error if it cannot connect to the cluster
|
||||||
|
// but just to be safe, we check if the service is nil and log a warning if it is
|
||||||
|
if app.services.kubernetesService == nil {
|
||||||
|
if app.config.LabelProvider == "kubernetes" {
|
||||||
|
app.log.App.Warn().Msg("Kubernetes label provider selected but Kubernetes is not available, will continue without it")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.services.kubernetesService, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
app.log.App.Debug().Msg("Using Docker label provider")
|
app.log.App.Debug().Msg("Using Docker label provider")
|
||||||
|
|
||||||
dockerService, err := service.NewDockerService(app.deps.service)
|
err := app.dig.Provide(service.NewDockerService)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize docker service: %w", err)
|
return nil, fmt.Errorf("failed to provide docker service: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dockerService == nil {
|
err = app.dig.Invoke(func(d *service.DockerService) error {
|
||||||
|
app.services.dockerService = d
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to invoke docker service: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.services.dockerService == nil {
|
||||||
if app.config.LabelProvider == "docker" {
|
if app.config.LabelProvider == "docker" {
|
||||||
app.log.App.Warn().Msg("Docker label provider selected but Docker is not available, will continue without it")
|
app.log.App.Warn().Msg("Docker label provider selected but Docker is not available, will continue without it")
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
app.services.DockerService = dockerService
|
return app.services.dockerService, nil
|
||||||
return dockerService, nil
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid label provider: %s", app.config.LabelProvider)
|
return nil, fmt.Errorf("invalid label provider: %s", app.config.LabelProvider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *BootstrapApp) setupPolicyEngine() error {
|
func (app *BootstrapApp) setupPolicyEngine() error {
|
||||||
policyEngine, err := service.NewPolicyEngine(app.deps.service)
|
err := app.dig.Provide(service.NewPolicyEngine)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize policy engine: %w", err)
|
return fmt.Errorf("failed to create policy engine: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{
|
err = app.dig.Invoke(func(policyEngine *service.PolicyEngine) error {
|
||||||
Log: app.log,
|
policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{
|
||||||
})
|
Log: app.log,
|
||||||
policyEngine.RegisterRule(service.RuleOAuthGroup, &service.OAuthGroupRule{
|
})
|
||||||
Log: app.log,
|
policyEngine.RegisterRule(service.RuleOAuthGroup, &service.OAuthGroupRule{
|
||||||
})
|
Log: app.log,
|
||||||
policyEngine.RegisterRule(service.RuleLDAPGroup, &service.LDAPGroupRule{
|
})
|
||||||
Log: app.log,
|
policyEngine.RegisterRule(service.RuleLDAPGroup, &service.LDAPGroupRule{
|
||||||
})
|
Log: app.log,
|
||||||
policyEngine.RegisterRule(service.RuleAuthEnabled, &service.AuthEnabledRule{
|
})
|
||||||
Log: app.log,
|
policyEngine.RegisterRule(service.RuleAuthEnabled, &service.AuthEnabledRule{
|
||||||
})
|
Log: app.log,
|
||||||
policyEngine.RegisterRule(service.RuleIPAllowed, &service.IPAllowedRule{
|
})
|
||||||
Log: app.log,
|
policyEngine.RegisterRule(service.RuleIPAllowed, &service.IPAllowedRule{
|
||||||
Config: app.config,
|
Log: app.log,
|
||||||
})
|
Config: app.config,
|
||||||
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
|
})
|
||||||
Log: app.log,
|
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
|
||||||
Config: app.config,
|
Log: app.log,
|
||||||
|
Config: app.config,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
app.services.PolicyEngine = policyEngine
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -71,29 +72,33 @@ type AppContextResponse struct {
|
|||||||
App ACRApp `json:"app"`
|
App ACRApp `json:"app"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContextController struct {
|
type ContextControllerInput struct {
|
||||||
log *logger.Logger
|
dig.In
|
||||||
config model.Config
|
|
||||||
runtime model.RuntimeConfig
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
Runtime *model.RuntimeConfig
|
||||||
|
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContextController(
|
type ContextController struct {
|
||||||
log *logger.Logger,
|
log *logger.Logger
|
||||||
config model.Config,
|
config *model.Config
|
||||||
runtimeConfig model.RuntimeConfig,
|
runtime *model.RuntimeConfig
|
||||||
router *gin.RouterGroup,
|
}
|
||||||
) *ContextController {
|
|
||||||
|
func NewContextController(i ContextControllerInput) *ContextController {
|
||||||
controller := &ContextController{
|
controller := &ContextController{
|
||||||
log: log,
|
log: i.Log,
|
||||||
config: config,
|
config: i.Config,
|
||||||
runtime: runtimeConfig,
|
runtime: i.Runtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.UI.WarningsEnabled {
|
if !i.Config.UI.WarningsEnabled {
|
||||||
log.App.Warn().Msg("UI warnings are disabled. This may lead to security issues if you are not careful. Make sure to enable warnings in production environments.")
|
i.Log.App.Warn().Msg("UI warnings are disabled. This may lead to security issues if you are not careful. Make sure to enable warnings in production environments.")
|
||||||
}
|
}
|
||||||
|
|
||||||
contextGroup := router.Group("/context")
|
contextGroup := i.RouterGroup.Group("/context")
|
||||||
contextGroup.GET("/user", controller.userContextHandler)
|
contextGroup.GET("/user", controller.userContextHandler)
|
||||||
contextGroup.GET("/app", controller.appContextHandler)
|
contextGroup.GET("/app", controller.appContextHandler)
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,12 @@ func TestContextController(t *testing.T) {
|
|||||||
group := router.Group("/api")
|
group := router.Group("/api")
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
controller.NewContextController(log, cfg, runtime, group)
|
controller.NewContextController(controller.ContextControllerInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
Runtime: &runtime,
|
||||||
|
RouterGroup: group,
|
||||||
|
})
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import "github.com/gin-gonic/gin"
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
)
|
||||||
|
|
||||||
type HealthController struct {
|
type HealthController struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHealthController(router *gin.RouterGroup) *HealthController {
|
type HealthControllerInput struct {
|
||||||
|
dig.In
|
||||||
|
|
||||||
|
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHealthController(i HealthControllerInput) *HealthController {
|
||||||
controller := &HealthController{}
|
controller := &HealthController{}
|
||||||
|
|
||||||
router.GET("/healthz", controller.healthHandler)
|
i.RouterGroup.GET("/healthz", controller.healthHandler)
|
||||||
router.HEAD("/healthz", controller.healthHandler)
|
i.RouterGroup.HEAD("/healthz", controller.healthHandler)
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ func TestHealthController(t *testing.T) {
|
|||||||
group := router.Group("/api")
|
group := router.Group("/api")
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
controller.NewHealthController(group)
|
controller.NewHealthController(controller.HealthControllerInput{
|
||||||
|
RouterGroup: group,
|
||||||
|
})
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/go-querystring/query"
|
"github.com/google/go-querystring/query"
|
||||||
@@ -22,26 +23,30 @@ type OAuthRequest struct {
|
|||||||
|
|
||||||
type OAuthController struct {
|
type OAuthController struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
config model.Config
|
config *model.Config
|
||||||
runtime model.RuntimeConfig
|
runtime *model.RuntimeConfig
|
||||||
auth *service.AuthService
|
auth *service.AuthService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOAuthController(
|
type OAuthControllerInput struct {
|
||||||
log *logger.Logger,
|
dig.In
|
||||||
config model.Config,
|
|
||||||
runtimeConfig model.RuntimeConfig,
|
Log *logger.Logger
|
||||||
router *gin.RouterGroup,
|
Config *model.Config
|
||||||
auth *service.AuthService,
|
RuntimeConfig *model.RuntimeConfig
|
||||||
) *OAuthController {
|
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
||||||
|
AuthService *service.AuthService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOAuthController(i OAuthControllerInput) *OAuthController {
|
||||||
controller := &OAuthController{
|
controller := &OAuthController{
|
||||||
log: log,
|
log: i.Log,
|
||||||
config: config,
|
config: i.Config,
|
||||||
runtime: runtimeConfig,
|
runtime: i.RuntimeConfig,
|
||||||
auth: auth,
|
auth: i.AuthService,
|
||||||
}
|
}
|
||||||
|
|
||||||
oauthGroup := router.Group("/oauth")
|
oauthGroup := i.RouterGroup.Group("/oauth")
|
||||||
oauthGroup.GET("/url/:provider", controller.oauthURLHandler)
|
oauthGroup.GET("/url/:provider", controller.oauthURLHandler)
|
||||||
oauthGroup.GET("/callback/:provider", controller.oauthCallbackHandler)
|
oauthGroup.GET("/callback/:provider", controller.oauthCallbackHandler)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
"github.com/google/go-querystring/query"
|
"github.com/google/go-querystring/query"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
@@ -30,7 +31,7 @@ type authorizeErrorParams struct {
|
|||||||
type OIDCController struct {
|
type OIDCController struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
oidc *service.OIDCService
|
oidc *service.OIDCService
|
||||||
runtime model.RuntimeConfig
|
runtime *model.RuntimeConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorizeCallback struct {
|
type AuthorizeCallback struct {
|
||||||
@@ -78,22 +79,27 @@ type AuthorizeCompleteRequest struct {
|
|||||||
Ticket string `json:"ticket" binding:"required"`
|
Ticket string `json:"ticket" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOIDCController(
|
type OIDCControllerInput struct {
|
||||||
log *logger.Logger,
|
dig.In
|
||||||
oidcService *service.OIDCService,
|
|
||||||
runtimeConfig model.RuntimeConfig,
|
Log *logger.Logger
|
||||||
router *gin.RouterGroup,
|
OIDCService *service.OIDCService
|
||||||
mainRouter *gin.RouterGroup) *OIDCController {
|
RuntimeConfig *model.RuntimeConfig
|
||||||
|
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
||||||
|
MainRouter *gin.RouterGroup `name:"mainRouterGroup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOIDCController(i OIDCControllerInput) *OIDCController {
|
||||||
controller := &OIDCController{
|
controller := &OIDCController{
|
||||||
log: log,
|
log: i.Log,
|
||||||
oidc: oidcService,
|
oidc: i.OIDCService,
|
||||||
runtime: runtimeConfig,
|
runtime: i.RuntimeConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
mainRouter.POST("/authorize", controller.authorize)
|
i.MainRouter.POST("/authorize", controller.authorize)
|
||||||
mainRouter.GET("/authorize", controller.authorize)
|
i.MainRouter.GET("/authorize", controller.authorize)
|
||||||
|
|
||||||
oidcGroup := router.Group("/oidc")
|
oidcGroup := i.RouterGroup.Group("/oidc")
|
||||||
oidcGroup.POST("/authorize-complete", controller.authorizeComplete)
|
oidcGroup.POST("/authorize-complete", controller.authorizeComplete)
|
||||||
oidcGroup.POST("/token", controller.Token)
|
oidcGroup.POST("/token", controller.Token)
|
||||||
oidcGroup.GET("/userinfo", controller.Userinfo)
|
oidcGroup.GET("/userinfo", controller.Userinfo)
|
||||||
|
|||||||
@@ -35,7 +35,13 @@ func TestOIDCController(t *testing.T) {
|
|||||||
|
|
||||||
store := memory.New()
|
store := memory.New()
|
||||||
|
|
||||||
oidcService, err := service.NewOIDCService(log, cfg, runtime, store, dg)
|
oidcService, err := service.NewOIDCService(service.OIDCServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Queries: store,
|
||||||
|
Ding: dg,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Middleware that injects an authenticated local user into the gin context,
|
// Middleware that injects an authenticated local user into the gin context,
|
||||||
@@ -831,7 +837,13 @@ func TestOIDCController(t *testing.T) {
|
|||||||
svc = nil
|
svc = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.NewOIDCController(log, svc, runtime, group, &router.RouterGroup)
|
controller.NewOIDCController(controller.OIDCControllerInput{
|
||||||
|
Log: log,
|
||||||
|
OIDCService: svc,
|
||||||
|
RuntimeConfig: &runtime,
|
||||||
|
RouterGroup: group,
|
||||||
|
MainRouter: &router.RouterGroup,
|
||||||
|
})
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/go-querystring/query"
|
"github.com/google/go-querystring/query"
|
||||||
@@ -53,29 +54,33 @@ type ProxyContext struct {
|
|||||||
|
|
||||||
type ProxyController struct {
|
type ProxyController struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
runtime model.RuntimeConfig
|
runtime *model.RuntimeConfig
|
||||||
acls *service.AccessControlsService
|
acls *service.AccessControlsService
|
||||||
auth *service.AuthService
|
auth *service.AuthService
|
||||||
policyEngine *service.PolicyEngine
|
policyEngine *service.PolicyEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxyController(
|
type ProxyControllerInput struct {
|
||||||
log *logger.Logger,
|
dig.In
|
||||||
runtime model.RuntimeConfig,
|
|
||||||
router *gin.RouterGroup,
|
Log *logger.Logger
|
||||||
acls *service.AccessControlsService,
|
RuntimeConfig *model.RuntimeConfig
|
||||||
auth *service.AuthService,
|
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
||||||
policyEngine *service.PolicyEngine,
|
ACLsService *service.AccessControlsService
|
||||||
) *ProxyController {
|
AuthService *service.AuthService
|
||||||
|
PolicyEngine *service.PolicyEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyController(i ProxyControllerInput) *ProxyController {
|
||||||
controller := &ProxyController{
|
controller := &ProxyController{
|
||||||
log: log,
|
log: i.Log,
|
||||||
runtime: runtime,
|
runtime: i.RuntimeConfig,
|
||||||
acls: acls,
|
acls: i.ACLsService,
|
||||||
auth: auth,
|
auth: i.AuthService,
|
||||||
policyEngine: policyEngine,
|
policyEngine: i.PolicyEngine,
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyGroup := router.Group("/auth")
|
proxyGroup := i.RouterGroup.Group("/auth")
|
||||||
proxyGroup.Any("/:proxy", controller.proxyHandler)
|
proxyGroup.Any("/:proxy", controller.proxyHandler)
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
|
|||||||
@@ -369,10 +369,21 @@ func TestProxyController(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
dg := ding.New(ctx)
|
dg := ding.New(ctx)
|
||||||
|
|
||||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
broker := service.NewOAuthBrokerService(service.OAuthBrokerServiceInput{
|
||||||
aclsService := service.NewAccessControlsService(log, cfg, nil)
|
Log: log,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Ctx: ctx,
|
||||||
|
})
|
||||||
|
aclsService := service.NewAccessControlsService(service.AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
LabelProvider: nil,
|
||||||
|
})
|
||||||
|
|
||||||
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
policyEngine, err := service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{
|
policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{
|
||||||
@@ -395,7 +406,18 @@ func TestProxyController(t *testing.T) {
|
|||||||
Log: log,
|
Log: log,
|
||||||
})
|
})
|
||||||
|
|
||||||
authService := service.NewAuthService(log, cfg, runtime, ctx, dg, nil, store, broker, nil, policyEngine)
|
authService := service.NewAuthService(service.AuthServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Ctx: ctx,
|
||||||
|
Ding: dg,
|
||||||
|
LDAP: nil,
|
||||||
|
Queries: store,
|
||||||
|
OAuthBroker: broker,
|
||||||
|
Tailscale: nil,
|
||||||
|
PolicyEngine: policyEngine,
|
||||||
|
})
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.description, func(t *testing.T) {
|
t.Run(test.description, func(t *testing.T) {
|
||||||
@@ -410,7 +432,14 @@ func TestProxyController(t *testing.T) {
|
|||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
controller.NewProxyController(log, runtime, group, aclsService, authService, policyEngine)
|
controller.NewProxyController(controller.ProxyControllerInput{
|
||||||
|
Log: log,
|
||||||
|
RuntimeConfig: &runtime,
|
||||||
|
RouterGroup: group,
|
||||||
|
ACLsService: aclsService,
|
||||||
|
AuthService: authService,
|
||||||
|
PolicyEngine: policyEngine,
|
||||||
|
})
|
||||||
|
|
||||||
test.run(t, router, recorder)
|
test.run(t, router, recorder)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,25 +5,30 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResourcesController struct {
|
type ResourcesController struct {
|
||||||
config model.Config
|
config *model.Config
|
||||||
fileServer http.Handler
|
fileServer http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResourcesController(
|
type ResourcesControllerInput struct {
|
||||||
config model.Config,
|
dig.In
|
||||||
router *gin.RouterGroup,
|
|
||||||
) *ResourcesController {
|
RouterGroup *gin.RouterGroup `name:"mainRouterGroup"`
|
||||||
fileServer := http.StripPrefix("/resources", http.FileServer(http.Dir(config.Resources.Path)))
|
Config *model.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResourcesController(i ResourcesControllerInput) *ResourcesController {
|
||||||
|
fileServer := http.StripPrefix("/resources", http.FileServer(http.Dir(i.Config.Resources.Path)))
|
||||||
|
|
||||||
controller := &ResourcesController{
|
controller := &ResourcesController{
|
||||||
config: config,
|
config: i.Config,
|
||||||
fileServer: fileServer,
|
fileServer: fileServer,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.GET("/resources/*resource", controller.resourcesHandler)
|
i.RouterGroup.GET("/resources/*resource", controller.resourcesHandler)
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,10 @@ func TestResourcesController(t *testing.T) {
|
|||||||
group := router.Group("/")
|
group := router.Group("/")
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
controller.NewResourcesController(cfg, group)
|
controller.NewResourcesController(controller.ResourcesControllerInput{
|
||||||
|
RouterGroup: group,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
test.run(t, router, recorder)
|
test.run(t, router, recorder)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pquerna/otp/totp"
|
"github.com/pquerna/otp/totp"
|
||||||
@@ -27,23 +28,27 @@ type TotpRequest struct {
|
|||||||
|
|
||||||
type UserController struct {
|
type UserController struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
runtime model.RuntimeConfig
|
runtime *model.RuntimeConfig
|
||||||
auth *service.AuthService
|
auth *service.AuthService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserController(
|
type UserControllerInput struct {
|
||||||
log *logger.Logger,
|
dig.In
|
||||||
runtimeConfig model.RuntimeConfig,
|
|
||||||
router *gin.RouterGroup,
|
Log *logger.Logger
|
||||||
auth *service.AuthService,
|
RuntimeConfig *model.RuntimeConfig
|
||||||
) *UserController {
|
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
|
||||||
|
AuthService *service.AuthService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserController(i UserControllerInput) *UserController {
|
||||||
controller := &UserController{
|
controller := &UserController{
|
||||||
log: log,
|
log: i.Log,
|
||||||
runtime: runtimeConfig,
|
runtime: i.RuntimeConfig,
|
||||||
auth: auth,
|
auth: i.AuthService,
|
||||||
}
|
}
|
||||||
|
|
||||||
userGroup := router.Group("/user")
|
userGroup := i.RouterGroup.Group("/user")
|
||||||
userGroup.POST("/login", controller.loginHandler)
|
userGroup.POST("/login", controller.loginHandler)
|
||||||
userGroup.POST("/logout", controller.logoutHandler)
|
userGroup.POST("/logout", controller.logoutHandler)
|
||||||
userGroup.POST("/totp", controller.totpHandler)
|
userGroup.POST("/totp", controller.totpHandler)
|
||||||
|
|||||||
@@ -414,11 +414,29 @@ func TestUserController(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
dg := ding.New(ctx)
|
dg := ding.New(ctx)
|
||||||
|
|
||||||
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
policyEngine, err := service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
broker := service.NewOAuthBrokerService(service.OAuthBrokerServiceInput{
|
||||||
authService := service.NewAuthService(log, cfg, runtime, ctx, dg, nil, store, broker, nil, policyEngine)
|
Log: log,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Ctx: ctx,
|
||||||
|
})
|
||||||
|
authService := service.NewAuthService(service.AuthServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Ctx: ctx,
|
||||||
|
Ding: dg,
|
||||||
|
LDAP: nil,
|
||||||
|
Queries: store,
|
||||||
|
OAuthBroker: broker,
|
||||||
|
Tailscale: nil,
|
||||||
|
PolicyEngine: policyEngine,
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach := func() {
|
beforeEach := func() {
|
||||||
// Clear failed login attempts before each test
|
// Clear failed login attempts before each test
|
||||||
@@ -437,7 +455,12 @@ func TestUserController(t *testing.T) {
|
|||||||
group := router.Group("/api")
|
group := router.Group("/api")
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
controller.NewUserController(log, runtime, group, authService)
|
controller.NewUserController(controller.UserControllerInput{
|
||||||
|
Log: log,
|
||||||
|
RuntimeConfig: &runtime,
|
||||||
|
RouterGroup: group,
|
||||||
|
AuthService: authService,
|
||||||
|
})
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenIDConnectConfiguration struct {
|
type OpenIDConnectConfiguration struct {
|
||||||
@@ -30,13 +31,20 @@ type WellKnownController struct {
|
|||||||
oidc *service.OIDCService
|
oidc *service.OIDCService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWellKnownController(oidc *service.OIDCService, router *gin.RouterGroup) *WellKnownController {
|
type WellKnownControllerInput struct {
|
||||||
|
dig.In
|
||||||
|
|
||||||
|
OIDCService *service.OIDCService
|
||||||
|
RouterGroup *gin.RouterGroup `name:"mainRouterGroup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWellKnownController(i WellKnownControllerInput) *WellKnownController {
|
||||||
controller := &WellKnownController{
|
controller := &WellKnownController{
|
||||||
oidc: oidc,
|
oidc: i.OIDCService,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.GET("/.well-known/openid-configuration", controller.OpenIDConnectConfiguration)
|
i.RouterGroup.GET("/.well-known/openid-configuration", controller.OpenIDConnectConfiguration)
|
||||||
router.GET("/.well-known/jwks.json", controller.JWKS)
|
i.RouterGroup.GET("/.well-known/jwks.json", controller.JWKS)
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,13 @@ func TestWellKnownController(t *testing.T) {
|
|||||||
|
|
||||||
store := memory.New()
|
store := memory.New()
|
||||||
|
|
||||||
oidcService, err := service.NewOIDCService(log, cfg, runtime, store, dg)
|
oidcService, err := service.NewOIDCService(service.OIDCServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Queries: store,
|
||||||
|
Ding: dg,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@@ -103,7 +109,10 @@ func TestWellKnownController(t *testing.T) {
|
|||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
controller.NewWellKnownController(oidcService, &router.RouterGroup)
|
controller.NewWellKnownController(controller.WellKnownControllerInput{
|
||||||
|
OIDCService: oidcService,
|
||||||
|
RouterGroup: &router.RouterGroup,
|
||||||
|
})
|
||||||
|
|
||||||
test.run(t, router, recorder)
|
test.run(t, router, recorder)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -37,25 +38,29 @@ var (
|
|||||||
|
|
||||||
type ContextMiddleware struct {
|
type ContextMiddleware struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
runtime model.RuntimeConfig
|
runtime *model.RuntimeConfig
|
||||||
auth *service.AuthService
|
auth *service.AuthService
|
||||||
broker *service.OAuthBrokerService
|
broker *service.OAuthBrokerService
|
||||||
tailscale *service.TailscaleService
|
tailscale *service.TailscaleService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContextMiddleware(
|
type ContextMiddlewareInput struct {
|
||||||
log *logger.Logger,
|
dig.In
|
||||||
runtime model.RuntimeConfig,
|
|
||||||
auth *service.AuthService,
|
Log *logger.Logger
|
||||||
broker *service.OAuthBrokerService,
|
RuntimeConfig *model.RuntimeConfig
|
||||||
tailscale *service.TailscaleService,
|
AuthService *service.AuthService
|
||||||
) *ContextMiddleware {
|
BrokerService *service.OAuthBrokerService
|
||||||
|
TailscaleService *service.TailscaleService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContextMiddleware(i ContextMiddlewareInput) *ContextMiddleware {
|
||||||
return &ContextMiddleware{
|
return &ContextMiddleware{
|
||||||
log: log,
|
log: i.Log,
|
||||||
runtime: runtime,
|
runtime: i.RuntimeConfig,
|
||||||
auth: auth,
|
auth: i.AuthService,
|
||||||
broker: broker,
|
broker: i.BrokerService,
|
||||||
tailscale: tailscale,
|
tailscale: i.TailscaleService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -254,13 +254,37 @@ func TestContextMiddleware(t *testing.T) {
|
|||||||
|
|
||||||
store := memory.New()
|
store := memory.New()
|
||||||
|
|
||||||
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
policyEngine, err := service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
broker := service.NewOAuthBrokerService(service.OAuthBrokerServiceInput{
|
||||||
authService := service.NewAuthService(log, cfg, runtime, ctx, dg, nil, store, broker, nil, policyEngine)
|
Log: log,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Ctx: ctx,
|
||||||
|
})
|
||||||
|
authService := service.NewAuthService(service.AuthServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Ctx: ctx,
|
||||||
|
Ding: dg,
|
||||||
|
LDAP: nil,
|
||||||
|
Queries: store,
|
||||||
|
OAuthBroker: broker,
|
||||||
|
Tailscale: nil,
|
||||||
|
PolicyEngine: policyEngine,
|
||||||
|
})
|
||||||
|
|
||||||
contextMiddleware := middleware.NewContextMiddleware(log, runtime, authService, broker, nil)
|
contextMiddleware := middleware.NewContextMiddleware(middleware.ContextMiddlewareInput{
|
||||||
|
Log: log,
|
||||||
|
RuntimeConfig: &runtime,
|
||||||
|
AuthService: authService,
|
||||||
|
BrokerService: broker,
|
||||||
|
TailscaleService: nil,
|
||||||
|
})
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
authService.ClearLoginAttempts()
|
authService.ClearLoginAttempts()
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/assets"
|
"github.com/tinyauthapp/tinyauth/internal/assets"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -18,7 +19,12 @@ type UIMiddleware struct {
|
|||||||
uiFileServer http.Handler
|
uiFileServer http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUIMiddleware() (*UIMiddleware, error) {
|
// for future use if we need to inject dependencies into the middleware
|
||||||
|
type UIMiddlewareInput struct {
|
||||||
|
dig.In
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUIMiddleware(_ UIMiddlewareInput) (*UIMiddleware, error) {
|
||||||
m := &UIMiddleware{}
|
m := &UIMiddleware{}
|
||||||
|
|
||||||
ui, err := fs.Sub(assets.FrontendAssets, "dist")
|
ui, err := fs.Sub(assets.FrontendAssets, "dist")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
// See context middleware for explanation of why we have to do this
|
// See context middleware for explanation of why we have to do this
|
||||||
@@ -21,9 +22,15 @@ type ZerologMiddleware struct {
|
|||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewZerologMiddleware(log *logger.Logger) *ZerologMiddleware {
|
type ZerologMiddlewareInput struct {
|
||||||
|
dig.In
|
||||||
|
|
||||||
|
Log *logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZerologMiddleware(i ZerologMiddlewareInput) *ZerologMiddleware {
|
||||||
return &ZerologMiddleware{
|
return &ZerologMiddleware{
|
||||||
log: log,
|
log: i.Log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ type RuntimeConfig struct {
|
|||||||
OAuthProviders map[string]OAuthServiceConfig
|
OAuthProviders map[string]OAuthServiceConfig
|
||||||
OAuthWhitelist []string
|
OAuthWhitelist []string
|
||||||
ConfiguredProviders []Provider
|
ConfiguredProviders []Provider
|
||||||
OIDCClients []OIDCClientConfig
|
|
||||||
TrustedDomains []string
|
TrustedDomains []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LabelProvider interface {
|
type LabelProvider interface {
|
||||||
@@ -14,17 +15,23 @@ type LabelProvider interface {
|
|||||||
type AccessControlsService struct {
|
type AccessControlsService struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
config *model.Config
|
config *model.Config
|
||||||
labelProvider *LabelProvider
|
labelProvider LabelProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccessControlsService(
|
type AccessControlServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) *AccessControlsService {
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
LabelProvider LabelProvider `optional:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccessControlsService(i AccessControlServiceInput) *AccessControlsService {
|
||||||
|
|
||||||
return &AccessControlsService{
|
return &AccessControlsService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
config: deps.StaticConfig,
|
config: i.Config,
|
||||||
labelProvider: &deps.LabelProvider,
|
labelProvider: i.LabelProvider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,8 +63,8 @@ func (service *AccessControlsService) GetAccessControls(domain string) (*model.A
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we have a label provider configured, try to get ACLs from it
|
// If we have a label provider configured, try to get ACLs from it
|
||||||
if service.labelProvider != nil && *service.labelProvider != nil {
|
if service.labelProvider != nil {
|
||||||
return (*service.labelProvider).GetLabels(domain)
|
return service.labelProvider.GetLabels(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// no labels
|
// no labels
|
||||||
|
|||||||
@@ -87,7 +87,11 @@ func TestLookupStaticACLs(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
svc := NewAccessControlsService(log, model.Config{Apps: tt.apps}, nil)
|
svc := NewAccessControlsService(AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &model.Config{Apps: tt.apps},
|
||||||
|
LabelProvider: nil,
|
||||||
|
})
|
||||||
got := svc.lookupStaticACLs(tt.domain)
|
got := svc.lookupStaticACLs(tt.domain)
|
||||||
if tt.expectNil {
|
if tt.expectNil {
|
||||||
assert.Nil(t, got)
|
assert.Nil(t, got)
|
||||||
@@ -112,7 +116,11 @@ func TestGetAccessControls(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
svc := NewAccessControlsService(log, config, nil)
|
svc := NewAccessControlsService(AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &config,
|
||||||
|
LabelProvider: nil,
|
||||||
|
})
|
||||||
|
|
||||||
got, err := svc.GetAccessControls("foo.example.com")
|
got, err := svc.GetAccessControls("foo.example.com")
|
||||||
|
|
||||||
@@ -123,7 +131,11 @@ func TestGetAccessControls(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("returns nil when no static match and no label provider", func(t *testing.T) {
|
t.Run("returns nil when no static match and no label provider", func(t *testing.T) {
|
||||||
svc := NewAccessControlsService(log, model.Config{}, nil)
|
svc := NewAccessControlsService(AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &model.Config{},
|
||||||
|
LabelProvider: nil,
|
||||||
|
})
|
||||||
|
|
||||||
got, err := svc.GetAccessControls("unknown.example.com")
|
got, err := svc.GetAccessControls("unknown.example.com")
|
||||||
|
|
||||||
@@ -133,7 +145,11 @@ func TestGetAccessControls(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("returns nil when label provider pointer wraps a nil interface", func(t *testing.T) {
|
t.Run("returns nil when label provider pointer wraps a nil interface", func(t *testing.T) {
|
||||||
var provider LabelProvider
|
var provider LabelProvider
|
||||||
svc := NewAccessControlsService(log, model.Config{}, &provider)
|
svc := NewAccessControlsService(AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &model.Config{},
|
||||||
|
LabelProvider: provider, // nil provider
|
||||||
|
})
|
||||||
|
|
||||||
got, err := svc.GetAccessControls("unknown.example.com")
|
got, err := svc.GetAccessControls("unknown.example.com")
|
||||||
|
|
||||||
@@ -152,7 +168,11 @@ func TestGetAccessControls(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
var provider LabelProvider = mock
|
var provider LabelProvider = mock
|
||||||
svc := NewAccessControlsService(log, model.Config{}, &provider)
|
svc := NewAccessControlsService(AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &model.Config{},
|
||||||
|
LabelProvider: provider,
|
||||||
|
})
|
||||||
|
|
||||||
got, err := svc.GetAccessControls("dynamic.example.com")
|
got, err := svc.GetAccessControls("dynamic.example.com")
|
||||||
|
|
||||||
@@ -170,7 +190,11 @@ func TestGetAccessControls(t *testing.T) {
|
|||||||
"foo": {Config: model.AppConfig{Domain: "foo.example.com"}},
|
"foo": {Config: model.AppConfig{Domain: "foo.example.com"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
svc := NewAccessControlsService(log, config, &provider)
|
svc := NewAccessControlsService(AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &config,
|
||||||
|
LabelProvider: provider,
|
||||||
|
})
|
||||||
|
|
||||||
got, err := svc.GetAccessControls("foo.example.com")
|
got, err := svc.GetAccessControls("foo.example.com")
|
||||||
|
|
||||||
@@ -188,7 +212,11 @@ func TestGetAccessControls(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
var provider LabelProvider = mock
|
var provider LabelProvider = mock
|
||||||
svc := NewAccessControlsService(log, model.Config{}, &provider)
|
svc := NewAccessControlsService(AccessControlServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &model.Config{},
|
||||||
|
LabelProvider: provider,
|
||||||
|
})
|
||||||
|
|
||||||
got, err := svc.GetAccessControls("dynamic.example.com")
|
got, err := svc.GetAccessControls("dynamic.example.com")
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@@ -82,19 +83,32 @@ type AuthService struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthService(
|
type AuthServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) *AuthService {
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
Runtime *model.RuntimeConfig
|
||||||
|
Ctx context.Context
|
||||||
|
Ding *ding.Ding
|
||||||
|
LDAP *LdapService `optional:"true"`
|
||||||
|
Queries repository.Store
|
||||||
|
OAuthBroker *OAuthBrokerService
|
||||||
|
Tailscale *TailscaleService `optional:"true"`
|
||||||
|
PolicyEngine *PolicyEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthService(i AuthServiceInput) *AuthService {
|
||||||
service := &AuthService{
|
service := &AuthService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
runtime: deps.RuntimeConfig,
|
runtime: i.Runtime,
|
||||||
ctx: deps.Ctx,
|
ctx: i.Ctx,
|
||||||
config: deps.StaticConfig,
|
config: i.Config,
|
||||||
ldap: deps.Services.LDAPService,
|
ldap: i.LDAP,
|
||||||
queries: *deps.Queries,
|
queries: i.Queries,
|
||||||
oauthBroker: deps.Services.OAuthBrokerService,
|
oauthBroker: i.OAuthBroker,
|
||||||
tailscale: deps.Services.TailscaleService,
|
tailscale: i.Tailscale,
|
||||||
policyEngine: deps.Services.PolicyEngine,
|
policyEngine: i.PolicyEngine,
|
||||||
}
|
}
|
||||||
|
|
||||||
// caches setup
|
// caches setup
|
||||||
@@ -106,7 +120,7 @@ func NewAuthService(
|
|||||||
service.caches.login = loginCache
|
service.caches.login = loginCache
|
||||||
service.caches.ldap = ldapCache
|
service.caches.ldap = ldapCache
|
||||||
|
|
||||||
deps.Ding.Go(func(ctx context.Context) {
|
i.Ding.Go(func(ctx context.Context) {
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
)
|
)
|
||||||
@@ -12,9 +13,22 @@ func TestIsEmailWhitelistedUsesProviderSpecificList(t *testing.T) {
|
|||||||
log := logger.NewLogger().WithTestConfig()
|
log := logger.NewLogger().WithTestConfig()
|
||||||
log.Init()
|
log.Init()
|
||||||
|
|
||||||
|
policyEngine, err := NewPolicyEngine(PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &model.Config{
|
||||||
|
Auth: model.AuthConfig{
|
||||||
|
ACLs: model.ACLsConfig{
|
||||||
|
Policy: string(PolicyAllow),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
auth := &AuthService{
|
auth := &AuthService{
|
||||||
log: log,
|
log: log,
|
||||||
runtime: model.RuntimeConfig{
|
runtime: &model.RuntimeConfig{
|
||||||
OAuthWhitelist: []string{"global@example.com"},
|
OAuthWhitelist: []string{"global@example.com"},
|
||||||
OAuthProviders: map[string]model.OAuthServiceConfig{
|
OAuthProviders: map[string]model.OAuthServiceConfig{
|
||||||
"github": {
|
"github": {
|
||||||
@@ -28,6 +42,7 @@ func TestIsEmailWhitelistedUsesProviderSpecificList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
policyEngine: policyEngine,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.True(t, auth.IsEmailWhitelisted("github", "github@example.com"))
|
assert.True(t, auth.IsEmailWhitelisted("github", "github@example.com"))
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/decoders"
|
"github.com/tinyauthapp/tinyauth/internal/utils/decoders"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
container "github.com/docker/docker/api/types/container"
|
container "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
@@ -21,34 +22,40 @@ type DockerService struct {
|
|||||||
isConnected bool
|
isConnected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDockerService(
|
type DockerServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) (*DockerService, error) {
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Ctx context.Context
|
||||||
|
Ding *ding.Ding
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDockerService(i DockerServiceInput) (*DockerService, error) {
|
||||||
|
|
||||||
client, err := client.NewClientWithOpts(client.FromEnv)
|
client, err := client.NewClientWithOpts(client.FromEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client.NegotiateAPIVersion(deps.Ctx)
|
client.NegotiateAPIVersion(i.Ctx)
|
||||||
|
|
||||||
_, err = client.Ping(deps.Ctx)
|
_, err = client.Ping(i.Ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
deps.Log.App.Debug().Err(err).Msg("Docker not connected")
|
i.Log.App.Debug().Err(err).Msg("Docker not connected")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
service := &DockerService{
|
service := &DockerService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
client: client,
|
client: client,
|
||||||
context: deps.Ctx,
|
context: i.Ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
service.isConnected = true
|
service.isConnected = true
|
||||||
service.log.App.Debug().Msg("Docker connected successfully")
|
service.log.App.Debug().Msg("Docker connected successfully")
|
||||||
|
|
||||||
deps.Ding.Go(service.watchAndClose, ding.RingMajor)
|
i.Ding.Go(service.watchAndClose, ding.RingMajor)
|
||||||
|
|
||||||
return service, nil
|
return service, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/decoders"
|
"github.com/tinyauthapp/tinyauth/internal/utils/decoders"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
@@ -48,9 +49,15 @@ type KubernetesService struct {
|
|||||||
appNameIndex map[string]ingressAppKey
|
appNameIndex map[string]ingressAppKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKubernetesService(
|
type KubernetesServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) (*KubernetesService, error) {
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Ctx context.Context
|
||||||
|
Ding *ding.Ding
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKubernetesService(i KubernetesServiceInput) (*KubernetesService, error) {
|
||||||
cfg, err := rest.InClusterConfig()
|
cfg, err := rest.InClusterConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get in-cluster kubernetes config: %w", err)
|
return nil, fmt.Errorf("failed to get in-cluster kubernetes config: %w", err)
|
||||||
@@ -67,31 +74,31 @@ func NewKubernetesService(
|
|||||||
Resource: "ingresses",
|
Resource: "ingresses",
|
||||||
}
|
}
|
||||||
|
|
||||||
accessCtx, accessCancel := context.WithTimeout(deps.Ctx, 5*time.Second)
|
accessCtx, accessCancel := context.WithTimeout(i.Ctx, 5*time.Second)
|
||||||
defer accessCancel()
|
defer accessCancel()
|
||||||
|
|
||||||
_, err = client.Resource(gvr).List(accessCtx, metav1.ListOptions{Limit: 1})
|
_, err = client.Resource(gvr).List(accessCtx, metav1.ListOptions{Limit: 1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
deps.Log.App.Warn().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Failed to access Ingress API, Kubernetes label provider will be disabled")
|
i.Log.App.Warn().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Failed to access Ingress API, Kubernetes label provider will be disabled")
|
||||||
return nil, fmt.Errorf("failed to access ingress api: %w", err)
|
return nil, fmt.Errorf("failed to access ingress api: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.Log.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Successfully accessed Ingress API, starting watcher")
|
i.Log.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Successfully accessed Ingress API, starting watcher")
|
||||||
|
|
||||||
service := &KubernetesService{
|
service := &KubernetesService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
client: client,
|
client: client,
|
||||||
ingressApps: make(map[ingressKey][]ingressApp),
|
ingressApps: make(map[ingressKey][]ingressApp),
|
||||||
domainIndex: make(map[string]ingressAppKey),
|
domainIndex: make(map[string]ingressAppKey),
|
||||||
appNameIndex: make(map[string]ingressAppKey),
|
appNameIndex: make(map[string]ingressAppKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.Ding.Go(func(ctx context.Context) {
|
i.Ding.Go(func(ctx context.Context) {
|
||||||
service.watchGVR(gvr, ctx)
|
service.watchGVR(gvr, ctx)
|
||||||
}, ding.RingMajor)
|
}, ding.RingMajor)
|
||||||
|
|
||||||
service.started = true
|
service.started = true
|
||||||
deps.Log.App.Debug().Msg("Kubernetes label provider started successfully")
|
i.Log.App.Debug().Msg("Kubernetes label provider started successfully")
|
||||||
|
|
||||||
return service, nil
|
return service, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,42 +13,48 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LdapService struct {
|
type LdapService struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
config *model.Config
|
config *model.Config
|
||||||
|
|
||||||
conn *ldapgo.Conn
|
conn *ldapgo.Conn
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
cert *tls.Certificate
|
cert *tls.Certificate
|
||||||
ldapBindPw string
|
bindPw string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLdapService(
|
type LdapServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) (*LdapService, error) {
|
|
||||||
if deps.StaticConfig.LDAP.Address == "" {
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
Ding *ding.Ding
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLdapService(i LdapServiceInput) (*LdapService, error) {
|
||||||
|
if i.Config.LDAP.Address == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapBindPw := utils.GetSecret(deps.StaticConfig.LDAP.BindPassword, deps.StaticConfig.LDAP.BindPasswordFile)
|
|
||||||
|
|
||||||
ldap := &LdapService{
|
ldap := &LdapService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
config: deps.StaticConfig,
|
config: i.Config,
|
||||||
ldapBindPw: ldapBindPw,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ldap.bindPw = utils.GetSecret(i.Config.LDAP.BindPassword, i.Config.LDAP.BindPasswordFile)
|
||||||
|
|
||||||
// Check whether authentication with client certificate is possible
|
// Check whether authentication with client certificate is possible
|
||||||
if deps.StaticConfig.LDAP.AuthCert != "" && deps.StaticConfig.LDAP.AuthKey != "" {
|
if i.Config.LDAP.AuthCert != "" && i.Config.LDAP.AuthKey != "" {
|
||||||
cert, err := tls.LoadX509KeyPair(deps.StaticConfig.LDAP.AuthCert, deps.StaticConfig.LDAP.AuthKey)
|
cert, err := tls.LoadX509KeyPair(i.Config.LDAP.AuthCert, i.Config.LDAP.AuthKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize LDAP with mTLS authentication: %w", err)
|
return nil, fmt.Errorf("failed to initialize LDAP with mTLS authentication: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ldap.log.App.Info().Msg("LDAP mTLS authentication configured successfully")
|
i.Log.App.Info().Msg("LDAP mTLS authentication configured successfully")
|
||||||
|
|
||||||
ldap.cert = &cert
|
ldap.cert = &cert
|
||||||
|
|
||||||
@@ -70,7 +76,7 @@ func NewLdapService(
|
|||||||
return nil, fmt.Errorf("failed to connect to ldap server: %w", err)
|
return nil, fmt.Errorf("failed to connect to ldap server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.Ding.Go(func(ctx context.Context) {
|
i.Ding.Go(func(ctx context.Context) {
|
||||||
ldap.log.App.Debug().Msg("Starting LDAP connection heartbeat routine")
|
ldap.log.App.Debug().Msg("Starting LDAP connection heartbeat routine")
|
||||||
|
|
||||||
ticker := time.NewTicker(5 * time.Minute)
|
ticker := time.NewTicker(5 * time.Minute)
|
||||||
@@ -215,7 +221,7 @@ func (ldap *LdapService) BindService(rebind bool) error {
|
|||||||
if ldap.cert != nil {
|
if ldap.cert != nil {
|
||||||
return ldap.conn.ExternalBind()
|
return ldap.conn.ExternalBind()
|
||||||
}
|
}
|
||||||
return ldap.conn.Bind(ldap.config.LDAP.BindDN, ldap.config.LDAP.BindPassword)
|
return ldap.conn.Bind(ldap.config.LDAP.BindDN, ldap.bindPw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ldap *LdapService) Bind(userDN string, password string) error {
|
func (ldap *LdapService) Bind(userDN string, password string) error {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
|
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
@@ -32,21 +33,27 @@ var presets = map[string]func(config model.OAuthServiceConfig, ctx context.Conte
|
|||||||
"google": newGoogleOAuthService,
|
"google": newGoogleOAuthService,
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOAuthBrokerService(
|
type OAuthBrokerServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) *OAuthBrokerService {
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Runtime *model.RuntimeConfig
|
||||||
|
Ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOAuthBrokerService(i OAuthBrokerServiceInput) *OAuthBrokerService {
|
||||||
service := &OAuthBrokerService{
|
service := &OAuthBrokerService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
services: make(map[string]OAuthServiceImpl),
|
services: make(map[string]OAuthServiceImpl),
|
||||||
configs: deps.RuntimeConfig.OAuthProviders,
|
configs: i.Runtime.OAuthProviders,
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, cfg := range service.configs {
|
for name, cfg := range service.configs {
|
||||||
if presetFunc, exists := presets[name]; exists {
|
if presetFunc, exists := presets[name]; exists {
|
||||||
service.services[name] = presetFunc(cfg, deps.Ctx)
|
service.services[name] = presetFunc(cfg, i.Ctx)
|
||||||
service.log.App.Debug().Str("service", name).Msg("Loaded OAuth service from preset")
|
service.log.App.Debug().Str("service", name).Msg("Loaded OAuth service from preset")
|
||||||
} else {
|
} else {
|
||||||
service.services[name] = NewOAuthService(cfg, name, deps.Ctx)
|
service.services[name] = NewOAuthService(cfg, name, i.Ctx)
|
||||||
service.log.App.Debug().Str("service", name).Msg("Loaded OAuth service from custom config")
|
service.log.App.Debug().Str("service", name).Msg("Loaded OAuth service from custom config")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -149,16 +151,24 @@ type OIDCService struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOIDCService(
|
type OIDCServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) (*OIDCService, error) {
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
Runtime *model.RuntimeConfig
|
||||||
|
Queries repository.Store
|
||||||
|
Ding *ding.Ding
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
|
||||||
// If not configured, skip init
|
// If not configured, skip init
|
||||||
if len(deps.RuntimeConfig.OIDCClients) == 0 {
|
if len(i.Config.OIDC.Clients) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure issuer is https
|
// Ensure issuer is https
|
||||||
uissuer, err := url.Parse(deps.RuntimeConfig.AppURL)
|
uissuer, err := url.Parse(i.Runtime.AppURL)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse app url: %w", err)
|
return nil, fmt.Errorf("failed to parse app url: %w", err)
|
||||||
@@ -171,14 +181,14 @@ func NewOIDCService(
|
|||||||
issuer := fmt.Sprintf("%s://%s", uissuer.Scheme, uissuer.Host)
|
issuer := fmt.Sprintf("%s://%s", uissuer.Scheme, uissuer.Host)
|
||||||
|
|
||||||
// Create/load private and public keys
|
// Create/load private and public keys
|
||||||
if strings.TrimSpace(deps.StaticConfig.OIDC.PrivateKeyPath) == "" ||
|
if strings.TrimSpace(i.Config.OIDC.PrivateKeyPath) == "" ||
|
||||||
strings.TrimSpace(deps.StaticConfig.OIDC.PublicKeyPath) == "" {
|
strings.TrimSpace(i.Config.OIDC.PublicKeyPath) == "" {
|
||||||
return nil, errors.New("private key path and public key path are required")
|
return nil, errors.New("private key path and public key path are required")
|
||||||
}
|
}
|
||||||
|
|
||||||
var privateKey *rsa.PrivateKey
|
var privateKey *rsa.PrivateKey
|
||||||
|
|
||||||
fprivateKey, err := os.ReadFile(deps.StaticConfig.OIDC.PrivateKeyPath)
|
fprivateKey, err := os.ReadFile(i.Config.OIDC.PrivateKeyPath)
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -197,8 +207,12 @@ func NewOIDCService(
|
|||||||
Type: "RSA PRIVATE KEY",
|
Type: "RSA PRIVATE KEY",
|
||||||
Bytes: der,
|
Bytes: der,
|
||||||
})
|
})
|
||||||
deps.Log.App.Trace().Str("type", "RSA PRIVATE KEY").Msg("Generated private RSA key")
|
i.Log.App.Trace().Str("type", "RSA PRIVATE KEY").Msg("Generated private RSA key")
|
||||||
err = os.WriteFile(deps.StaticConfig.OIDC.PrivateKeyPath, encoded, 0600)
|
err := os.MkdirAll(filepath.Dir(i.Config.OIDC.PrivateKeyPath), 0700)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create directory for private key: %w", err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile(i.Config.OIDC.PrivateKeyPath, encoded, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to write private key to file: %w", err)
|
return nil, fmt.Errorf("failed to write private key to file: %w", err)
|
||||||
}
|
}
|
||||||
@@ -207,7 +221,7 @@ func NewOIDCService(
|
|||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, errors.New("failed to decode private key")
|
return nil, errors.New("failed to decode private key")
|
||||||
}
|
}
|
||||||
deps.Log.App.Trace().Str("type", block.Type).Msg("Loaded private key")
|
i.Log.App.Trace().Str("type", block.Type).Msg("Loaded private key")
|
||||||
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||||
@@ -216,7 +230,7 @@ func NewOIDCService(
|
|||||||
|
|
||||||
var publicKey crypto.PublicKey
|
var publicKey crypto.PublicKey
|
||||||
|
|
||||||
fpublicKey, err := os.ReadFile(deps.StaticConfig.OIDC.PublicKeyPath)
|
fpublicKey, err := os.ReadFile(i.Config.OIDC.PublicKeyPath)
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
return nil, fmt.Errorf("failed to read public key: %w", err)
|
return nil, fmt.Errorf("failed to read public key: %w", err)
|
||||||
@@ -232,8 +246,12 @@ func NewOIDCService(
|
|||||||
Type: "RSA PUBLIC KEY",
|
Type: "RSA PUBLIC KEY",
|
||||||
Bytes: der,
|
Bytes: der,
|
||||||
})
|
})
|
||||||
deps.Log.App.Trace().Str("type", "RSA PUBLIC KEY").Msg("Generated public RSA key")
|
i.Log.App.Trace().Str("type", "RSA PUBLIC KEY").Msg("Generated public RSA key")
|
||||||
err = os.WriteFile(deps.StaticConfig.OIDC.PublicKeyPath, encoded, 0644)
|
err := os.MkdirAll(filepath.Dir(i.Config.OIDC.PublicKeyPath), 0700)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create directory for public key: %w", err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile(i.Config.OIDC.PublicKeyPath, encoded, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -242,7 +260,7 @@ func NewOIDCService(
|
|||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, errors.New("failed to decode public key")
|
return nil, errors.New("failed to decode public key")
|
||||||
}
|
}
|
||||||
deps.Log.App.Trace().Str("type", block.Type).Msg("Loaded public key")
|
i.Log.App.Trace().Str("type", block.Type).Msg("Loaded public key")
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case "RSA PUBLIC KEY":
|
case "RSA PUBLIC KEY":
|
||||||
publicKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
publicKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
||||||
@@ -272,7 +290,7 @@ func NewOIDCService(
|
|||||||
// We will reorganize the client into a map with the client ID as the key
|
// We will reorganize the client into a map with the client ID as the key
|
||||||
clients := make(map[string]model.OIDCClientConfig)
|
clients := make(map[string]model.OIDCClientConfig)
|
||||||
|
|
||||||
for id, client := range deps.StaticConfig.OIDC.Clients {
|
for id, client := range i.Config.OIDC.Clients {
|
||||||
client.ID = id
|
client.ID = id
|
||||||
if client.Name == "" {
|
if client.Name == "" {
|
||||||
client.Name = utils.Capitalize(client.ID)
|
client.Name = utils.Capitalize(client.ID)
|
||||||
@@ -288,15 +306,15 @@ func NewOIDCService(
|
|||||||
}
|
}
|
||||||
client.ClientSecretFile = ""
|
client.ClientSecretFile = ""
|
||||||
clients[id] = client
|
clients[id] = client
|
||||||
deps.Log.App.Debug().Str("clientId", client.ClientID).Msg("Loaded OIDC client configuration")
|
i.Log.App.Debug().Str("clientId", client.ClientID).Msg("Loaded OIDC client configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the service
|
// Initialize the service
|
||||||
service := &OIDCService{
|
service := &OIDCService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
config: deps.StaticConfig,
|
config: i.Config,
|
||||||
runtime: deps.RuntimeConfig,
|
runtime: i.Runtime,
|
||||||
queries: *deps.Queries,
|
queries: i.Queries,
|
||||||
|
|
||||||
clients: clients,
|
clients: clients,
|
||||||
privateKey: privateKey,
|
privateKey: privateKey,
|
||||||
@@ -305,7 +323,7 @@ func NewOIDCService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start cleanup routine
|
// Start cleanup routine
|
||||||
deps.Ding.Go(service.cleanupRoutine, ding.RingMinor)
|
i.Ding.Go(service.cleanupRoutine, ding.RingMinor)
|
||||||
|
|
||||||
// Create caches
|
// Create caches
|
||||||
codeCash := NewCacheStore[AuthorizeCodeEntry](256)
|
codeCash := NewCacheStore[AuthorizeCodeEntry](256)
|
||||||
@@ -317,7 +335,7 @@ func NewOIDCService(
|
|||||||
service.caches.authorize = authorize
|
service.caches.authorize = authorize
|
||||||
|
|
||||||
// Start cache cleanup routine
|
// Start cache cleanup routine
|
||||||
deps.Ding.Go(func(ctx context.Context) {
|
i.Ding.Go(func(ctx context.Context) {
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
|
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
)
|
)
|
||||||
@@ -67,7 +68,15 @@ func TestCompileUserinfo(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
dg := ding.New(ctx)
|
dg := ding.New(ctx)
|
||||||
|
|
||||||
svc, err := service.NewOIDCService(log, cfg, runtime, nil, dg)
|
store := memory.New()
|
||||||
|
|
||||||
|
svc, err := service.NewOIDCService(service.OIDCServiceInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
Runtime: &runtime,
|
||||||
|
Queries: store,
|
||||||
|
Ding: dg,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Policy string
|
type Policy string
|
||||||
@@ -40,23 +41,28 @@ type PolicyEngine struct {
|
|||||||
policy Policy
|
policy Policy
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPolicyEngine(
|
type PolicyEngineInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) (*PolicyEngine, error) {
|
|
||||||
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPolicyEngine(i PolicyEngineInput) (*PolicyEngine, error) {
|
||||||
engine := PolicyEngine{
|
engine := PolicyEngine{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
rules: make(map[RuleName]Rule),
|
rules: make(map[RuleName]Rule),
|
||||||
}
|
}
|
||||||
|
|
||||||
switch deps.StaticConfig.Auth.ACLs.Policy {
|
switch i.Config.Auth.ACLs.Policy {
|
||||||
case string(PolicyAllow):
|
case string(PolicyAllow):
|
||||||
deps.Log.App.Debug().Msg("Using 'allow' ACL policy: access to apps will be allowed by default unless explicitly blocked")
|
i.Log.App.Debug().Msg("Using 'allow' ACL policy: access to apps will be allowed by default unless explicitly blocked")
|
||||||
engine.policy = PolicyAllow
|
engine.policy = PolicyAllow
|
||||||
case string(PolicyDeny):
|
case string(PolicyDeny):
|
||||||
deps.Log.App.Debug().Msg("Using 'deny' ACL policy: access to apps will be blocked by default unless explicitly allowed")
|
i.Log.App.Debug().Msg("Using 'deny' ACL policy: access to apps will be blocked by default unless explicitly allowed")
|
||||||
engine.policy = PolicyDeny
|
engine.policy = PolicyDeny
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid acl policy: %s", deps.StaticConfig.Auth.ACLs.Policy)
|
return nil, fmt.Errorf("invalid acl policy: %s", i.Config.Auth.ACLs.Policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &engine, nil
|
return &engine, nil
|
||||||
|
|||||||
@@ -33,23 +33,35 @@ func TestPolicyEngine(t *testing.T) {
|
|||||||
|
|
||||||
// Engine should fail with invalid policy
|
// Engine should fail with invalid policy
|
||||||
cfg.Auth.ACLs.Policy = "invalid_policy"
|
cfg.Auth.ACLs.Policy = "invalid_policy"
|
||||||
_, err := service.NewPolicyEngine(cfg, log)
|
_, err := service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
// Engine should initialize with 'allow' policy
|
// Engine should initialize with 'allow' policy
|
||||||
cfg.Auth.ACLs.Policy = string(service.PolicyAllow)
|
cfg.Auth.ACLs.Policy = string(service.PolicyAllow)
|
||||||
engine, err := service.NewPolicyEngine(cfg, log)
|
engine, err := service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, service.PolicyAllow, engine.Policy())
|
assert.Equal(t, service.PolicyAllow, engine.Policy())
|
||||||
|
|
||||||
// Engine should initialize with 'deny' policy
|
// Engine should initialize with 'deny' policy
|
||||||
cfg.Auth.ACLs.Policy = string(service.PolicyDeny)
|
cfg.Auth.ACLs.Policy = string(service.PolicyDeny)
|
||||||
engine, err = service.NewPolicyEngine(cfg, log)
|
engine, err = service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, service.PolicyDeny, engine.Policy())
|
assert.Equal(t, service.PolicyDeny, engine.Policy())
|
||||||
|
|
||||||
// Engine should allow adding rules
|
// Engine should allow adding rules
|
||||||
engine, err = service.NewPolicyEngine(cfg, log)
|
engine, err = service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
engine.RegisterRule("test-rule", testRule)
|
engine.RegisterRule("test-rule", testRule)
|
||||||
_, ok := engine.Rules()["test-rule"]
|
_, ok := engine.Rules()["test-rule"]
|
||||||
@@ -57,7 +69,10 @@ func TestPolicyEngine(t *testing.T) {
|
|||||||
|
|
||||||
// Begin allow policy tests
|
// Begin allow policy tests
|
||||||
cfg.Auth.ACLs.Policy = string(service.PolicyAllow)
|
cfg.Auth.ACLs.Policy = string(service.PolicyAllow)
|
||||||
engine, err = service.NewPolicyEngine(cfg, log)
|
engine, err = service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
engine.RegisterRule("test-rule", testRule)
|
engine.RegisterRule("test-rule", testRule)
|
||||||
|
|
||||||
@@ -75,7 +90,10 @@ func TestPolicyEngine(t *testing.T) {
|
|||||||
|
|
||||||
// Begin deny policy tests
|
// Begin deny policy tests
|
||||||
cfg.Auth.ACLs.Policy = string(service.PolicyDeny)
|
cfg.Auth.ACLs.Policy = string(service.PolicyDeny)
|
||||||
engine, err = service.NewPolicyEngine(cfg, log)
|
engine, err = service.NewPolicyEngine(service.PolicyEngineInput{
|
||||||
|
Log: log,
|
||||||
|
Config: &cfg,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
engine.RegisterRule("test-rule", testRule)
|
engine.RegisterRule("test-rule", testRule)
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/steveiliop56/ding"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Services struct {
|
|
||||||
AccessControlService *AccessControlsService
|
|
||||||
AuthService *AuthService
|
|
||||||
DockerService *DockerService
|
|
||||||
KubernetesService *KubernetesService
|
|
||||||
LDAPService *LdapService
|
|
||||||
OAuthBrokerService *OAuthBrokerService
|
|
||||||
OIDCService *OIDCService
|
|
||||||
TailscaleService *TailscaleService
|
|
||||||
PolicyEngine *PolicyEngine
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServiceDependencies struct {
|
|
||||||
Log *logger.Logger
|
|
||||||
StaticConfig *model.Config
|
|
||||||
RuntimeConfig *model.RuntimeConfig
|
|
||||||
Ctx context.Context
|
|
||||||
Ding *ding.Ding
|
|
||||||
Services *Services
|
|
||||||
LabelProvider LabelProvider
|
|
||||||
Queries *repository.Store
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/steveiliop56/ding"
|
"github.com/steveiliop56/ding"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
|
"go.uber.org/dig"
|
||||||
"tailscale.com/client/local"
|
"tailscale.com/client/local"
|
||||||
"tailscale.com/tsnet"
|
"tailscale.com/tsnet"
|
||||||
)
|
)
|
||||||
@@ -34,24 +35,31 @@ type TailscaleService struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTailscaleService(
|
type TailscaleServiceInput struct {
|
||||||
deps *ServiceDependencies,
|
dig.In
|
||||||
) (*TailscaleService, error) {
|
|
||||||
if !deps.StaticConfig.Tailscale.Enabled {
|
Log *logger.Logger
|
||||||
|
Config *model.Config
|
||||||
|
Ctx context.Context
|
||||||
|
Ding *ding.Ding
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTailscaleService(i TailscaleServiceInput) (*TailscaleService, error) {
|
||||||
|
if !i.Config.Tailscale.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := new(tsnet.Server)
|
srv := new(tsnet.Server)
|
||||||
|
|
||||||
// node options
|
// node options
|
||||||
srv.Dir = deps.StaticConfig.Tailscale.Dir
|
srv.Dir = i.Config.Tailscale.Dir
|
||||||
srv.Hostname = deps.StaticConfig.Tailscale.Hostname
|
srv.Hostname = i.Config.Tailscale.Hostname
|
||||||
srv.AuthKey = deps.StaticConfig.Tailscale.AuthKey
|
srv.AuthKey = i.Config.Tailscale.AuthKey
|
||||||
srv.Ephemeral = deps.StaticConfig.Tailscale.Ephemeral
|
srv.Ephemeral = i.Config.Tailscale.Ephemeral
|
||||||
|
|
||||||
// redirect logs to zerolog
|
// redirect logs to zerolog
|
||||||
srv.Logf = deps.Log.App.Printf
|
srv.Logf = i.Log.App.Printf
|
||||||
srv.UserLogf = deps.Log.App.Printf
|
srv.UserLogf = i.Log.App.Printf
|
||||||
|
|
||||||
err := srv.Start()
|
err := srv.Start()
|
||||||
|
|
||||||
@@ -67,14 +75,14 @@ func NewTailscaleService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
service := &TailscaleService{
|
service := &TailscaleService{
|
||||||
log: deps.Log,
|
log: i.Log,
|
||||||
config: deps.StaticConfig,
|
config: i.Config,
|
||||||
ctx: deps.Ctx,
|
ctx: i.Ctx,
|
||||||
srv: srv,
|
srv: srv,
|
||||||
lc: lc,
|
lc: lc,
|
||||||
}
|
}
|
||||||
|
|
||||||
connectCtx, cancel := context.WithTimeout(deps.Ctx, 2*time.Minute) // large enough timeout to allow for user to manually authenticate with link if needed
|
connectCtx, cancel := context.WithTimeout(i.Ctx, 2*time.Minute) // large enough timeout to allow for user to manually authenticate with link if needed
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
err = service.waitForConn(connectCtx)
|
err = service.waitForConn(connectCtx)
|
||||||
@@ -84,7 +92,7 @@ func NewTailscaleService(
|
|||||||
return nil, fmt.Errorf("failed to connect to tailscale network: %w", err)
|
return nil, fmt.Errorf("failed to connect to tailscale network: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.Ding.Go(service.watchAndClose, ding.RingMajor)
|
i.Ding.Go(service.watchAndClose, ding.RingMajor)
|
||||||
|
|
||||||
return service, nil
|
return service, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,14 +121,6 @@ func CreateTestConfigs(t *testing.T) (model.Config, model.RuntimeConfig) {
|
|||||||
CookieDomain: "example.com",
|
CookieDomain: "example.com",
|
||||||
AppURL: "https://tinyauth.example.com",
|
AppURL: "https://tinyauth.example.com",
|
||||||
SessionCookieName: "tinyauth-session",
|
SessionCookieName: "tinyauth-session",
|
||||||
OIDCClients: func() []model.OIDCClientConfig {
|
|
||||||
var clients []model.OIDCClientConfig
|
|
||||||
for id, client := range config.OIDC.Clients {
|
|
||||||
client.ID = id
|
|
||||||
clients = append(clients, client)
|
|
||||||
}
|
|
||||||
return clients
|
|
||||||
}(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, runtime
|
return config, runtime
|
||||||
|
|||||||
Reference in New Issue
Block a user