Compare commits

..

1 Commits

Author SHA1 Message Date
Stavros a7f5374acc refactor: use one struct for service deps 2026-06-13 17:14:47 +03:00
45 changed files with 424 additions and 912 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with: with:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
+10 -10
View File
@@ -60,7 +60,7 @@ jobs:
ref: nightly ref: nightly
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with: with:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
@@ -105,7 +105,7 @@ jobs:
ref: nightly ref: nightly
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with: with:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
@@ -173,8 +173,8 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-amd64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-amd64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -232,8 +232,8 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-amd64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-distroless-amd64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -289,8 +289,8 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-arm64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-arm64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
@@ -348,8 +348,8 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-arm64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-distroless-arm64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
+14 -14
View File
@@ -36,7 +36,7 @@ jobs:
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with: with:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
@@ -78,7 +78,7 @@ jobs:
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with: with:
package_json_file: ./frontend/package.json package_json_file: ./frontend/package.json
@@ -143,14 +143,14 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-amd64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-amd64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }} COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }}
BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }} BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}
LDFLAGS=-s -w LDFLAGS="-s -w"
- name: Export digest - name: Export digest
run: | run: |
@@ -200,14 +200,14 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-amd64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-distroless-amd64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }} COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }}
BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }} BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}
LDFLAGS=-s -w LDFLAGS="-s -w"
- name: Export digest - name: Export digest
run: | run: |
@@ -255,14 +255,14 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=buildkit-arm64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-arm64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }} COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }}
BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }} BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}
LDFLAGS=-s -w LDFLAGS="-s -w"
- name: Export digest - name: Export digest
run: | run: |
@@ -312,14 +312,14 @@ jobs:
tags: ghcr.io/${{ github.repository_owner }}/tinyauth tags: ghcr.io/${{ github.repository_owner }}/tinyauth
outputs: type=image,push-by-digest=true,name-canonical=true,push=true outputs: type=image,push-by-digest=true,name-canonical=true,push=true
file: Dockerfile.distroless file: Dockerfile.distroless
cache-from: type=gha,scope=buildkit-distroless-arm64 cache-from: type=gha
cache-to: type=gha,mode=max,scope=buildkit-distroless-arm64 cache-to: type=gha,mode=max
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
build-args: | build-args: |
VERSION=${{ needs.generate-metadata.outputs.VERSION }} VERSION=${{ needs.generate-metadata.outputs.VERSION }}
COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }} COMMIT_HASH=${{ needs.generate-metadata.outputs.COMMIT_HASH }}
BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }} BUILD_TIMESTAMP=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}
LDFLAGS=-s -w LDFLAGS="-s -w"
- name: Export digest - name: Export digest
run: | run: |
+1 -1
View File
@@ -38,6 +38,6 @@ jobs:
retention-days: 5 retention-days: 5
- name: Upload to code-scanning - name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 uses: github/codeql-action/upload-sarif@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4
with: with:
sarif_file: results.sarif sarif_file: results.sarif
+1 -1
View File
@@ -46,7 +46,7 @@ RUN CGO_ENABLED=0 go build -ldflags "${LDFLAGS} \
-X github.com/tinyauthapp/tinyauth/internal/model.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth -X github.com/tinyauthapp/tinyauth/internal/model.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth
# Runner # Runner
FROM alpine:3.24 AS runner FROM alpine:3.23 AS runner
WORKDIR /tinyauth WORKDIR /tinyauth
-1
View File
@@ -21,7 +21,6 @@ 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
-2
View File
@@ -485,8 +485,6 @@ 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=
+15 -52
View File
@@ -18,7 +18,6 @@ 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"
@@ -32,23 +31,10 @@ 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 Services services service.Services
log *logger.Logger log *logger.Logger
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
@@ -57,7 +43,9 @@ type BootstrapApp struct {
db *sql.DB db *sql.DB
ding *ding.Ding ding *ding.Ding
listeners []Listener listeners []Listener
dig *dig.Container deps struct {
service *service.ServiceDependencies
}
} }
func NewBootstrapApp(config model.Config) *BootstrapApp { func NewBootstrapApp(config model.Config) *BootstrapApp {
@@ -72,11 +60,7 @@ func (app *BootstrapApp) Setup() error {
app.ctx = ctx app.ctx = ctx
app.cancel = cancel app.cancel = cancel
// create the dig container // Create a ding instance
c := dig.New()
app.dig = c
// create a ding instance
dg := ding.New(ctx) dg := ding.New(ctx)
app.ding = dg app.ding = dg
@@ -163,6 +147,12 @@ 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
@@ -211,33 +201,6 @@ 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()
@@ -260,7 +223,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",
@@ -268,7 +231,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",
@@ -287,8 +250,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
+20 -84
View File
@@ -13,7 +13,6 @@ 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"
) )
@@ -41,94 +40,31 @@ func (app *BootstrapApp) setupRouter() error {
} }
} }
middlewareProvideFor := []any{ contextMiddleware := middleware.NewContextMiddleware(app.log, app.runtime, app.services.AuthService, app.services.OAuthBrokerService, app.services.TailscaleService)
middleware.NewContextMiddleware, engine.Use(contextMiddleware.Middleware())
middleware.NewUIMiddleware,
middleware.NewZerologMiddleware,
}
for _, provider := range middlewareProvideFor { uiMiddleware, err := middleware.NewUIMiddleware()
err := app.dig.Provide(provider)
if err != nil {
return fmt.Errorf("failed to provide middleware: %w", err)
}
}
type middlewareInput struct {
dig.In
ContextMiddleware *middleware.ContextMiddleware
UIMiddleware *middleware.UIMiddleware
ZerologMiddleware *middleware.ZerologMiddleware
}
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 { if err != nil {
return fmt.Errorf("failed to invoke middleware: %w", err) return fmt.Errorf("failed to initialize UI middleware: %w", err)
} }
err = app.dig.Provide(func() *gin.RouterGroup { engine.Use(uiMiddleware.Middleware())
return &engine.RouterGroup
}, dig.Name("mainRouterGroup"))
if err != nil { zerologMiddleware := middleware.NewZerologMiddleware(app.log)
return fmt.Errorf("failed to provide main router group: %w", err)
}
err = app.dig.Provide(func() *gin.RouterGroup { engine.Use(zerologMiddleware.Middleware())
return engine.Group("/api")
}, dig.Name("apiRouterGroup"))
if err != nil { apiRouter := engine.Group("/api")
return fmt.Errorf("failed to provide api router group: %w", err)
}
controllerProvideFor := []any{ controller.NewContextController(app.log, app.config, app.runtime, apiRouter)
controller.NewContextController, controller.NewOAuthController(app.log, app.config, app.runtime, apiRouter, app.services.AuthService)
controller.NewOAuthController, controller.NewOIDCController(app.log, app.services.OIDCService, app.runtime, apiRouter, &engine.RouterGroup)
controller.NewOIDCController, controller.NewProxyController(app.log, app.runtime, apiRouter, app.services.AccessControlService, app.services.AuthService, app.services.PolicyEngine)
controller.NewProxyController, controller.NewUserController(app.log, app.runtime, apiRouter, app.services.AuthService)
controller.NewUserController, controller.NewResourcesController(app.config, &engine.RouterGroup)
controller.NewResourcesController, controller.NewHealthController(apiRouter)
controller.NewHealthController, controller.NewWellKnownController(app.services.OIDCService, &engine.RouterGroup)
controller.NewWellKnownController,
}
for _, provider := range controllerProvideFor {
err := app.dig.Provide(provider)
if err != nil {
return fmt.Errorf("failed to provide 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
@@ -163,7 +99,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
} }
@@ -181,7 +117,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)
} }
@@ -250,9 +186,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)
+75 -103
View File
@@ -5,67 +5,66 @@ 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 {
err := app.setupPolicyEngine() app.deps.service = &service.ServiceDependencies{
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 {
return fmt.Errorf("failed to setup policy engine: %w", err) app.log.App.Warn().Err(err).Msg("Failed to initialize LDAP connection, will continue without it")
} }
app.services.LDAPService = ldap
labelProvider, err := app.getLabelProvider() labelProvider, err := app.getLabelProvider()
if err != nil { if err != nil {
return fmt.Errorf("failed to get label provider: %w", err) return fmt.Errorf("failed to initialize label provider: %w", err)
} }
serviceProvideFor := []any{ app.deps.service.LabelProvider = labelProvider
func() service.LabelProvider {
return labelProvider
},
service.NewLdapService,
service.NewTailscaleService,
service.NewAccessControlsService,
service.NewOAuthBrokerService,
service.NewAuthService,
service.NewOIDCService,
}
for _, provider := range serviceProvideFor { tailscaleService, err := service.NewTailscaleService(app.deps.service)
err = app.dig.Provide(provider)
if err != nil {
return fmt.Errorf("failed to provide 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.oidcService = i.OIDCService
app.services.tailscaleService = i.TailscaleService
return nil
})
if err != nil { if err != nil {
return fmt.Errorf("failed to invoke services: %w", err) app.log.App.Warn().Err(err).Msg("Failed to initialize Tailscale connection, will continue without it")
} }
app.services.TailscaleService = tailscaleService
accessControlsService := service.NewAccessControlsService(app.deps.service)
app.services.AccessControlService = accessControlsService
err = app.setupPolicyEngine()
if err != nil {
return fmt.Errorf("failed to initialize policy engine: %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
} }
@@ -82,93 +81,66 @@ 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")
err := app.dig.Provide(service.NewKubernetesService) kubernetesService, err := service.NewKubernetesService(app.deps.service)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to provide kubernetes service: %w", err) return nil, fmt.Errorf("failed to initialize kubernetes service: %w", err)
} }
err = app.dig.Invoke(func(k *service.KubernetesService) error { app.services.KubernetesService = kubernetesService
app.services.kubernetesService = k return kubernetesService, nil
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")
err := app.dig.Provide(service.NewDockerService) dockerService, err := service.NewDockerService(app.deps.service)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to provide docker service: %w", err) return nil, fmt.Errorf("failed to initialize docker service: %w", err)
} }
err = app.dig.Invoke(func(d *service.DockerService) error { if dockerService == nil {
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
} }
return app.services.dockerService, nil app.services.DockerService = dockerService
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 {
err := app.dig.Provide(service.NewPolicyEngine) policyEngine, err := service.NewPolicyEngine(app.deps.service)
if err != nil { if err != nil {
return fmt.Errorf("failed to create policy engine: %w", err) return fmt.Errorf("failed to initialize policy engine: %w", err)
} }
err = app.dig.Invoke(func(policyEngine *service.PolicyEngine) error { policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{
policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{ Log: app.log,
Log: app.log, })
}) policyEngine.RegisterRule(service.RuleOAuthGroup, &service.OAuthGroupRule{
policyEngine.RegisterRule(service.RuleOAuthGroup, &service.OAuthGroupRule{ Log: app.log,
Log: app.log, })
}) policyEngine.RegisterRule(service.RuleLDAPGroup, &service.LDAPGroupRule{
policyEngine.RegisterRule(service.RuleLDAPGroup, &service.LDAPGroupRule{ Log: app.log,
Log: app.log, })
}) policyEngine.RegisterRule(service.RuleAuthEnabled, &service.AuthEnabledRule{
policyEngine.RegisterRule(service.RuleAuthEnabled, &service.AuthEnabledRule{ Log: app.log,
Log: app.log, })
}) policyEngine.RegisterRule(service.RuleIPAllowed, &service.IPAllowedRule{
policyEngine.RegisterRule(service.RuleIPAllowed, &service.IPAllowedRule{ Log: app.log,
Log: app.log, Config: app.config,
Config: app.config, })
}) policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{ Log: app.log,
Log: app.log, Config: app.config,
Config: app.config,
})
return nil
}) })
return err app.services.PolicyEngine = policyEngine
return nil
} }
+14 -19
View File
@@ -3,7 +3,6 @@ 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"
) )
@@ -72,33 +71,29 @@ type AppContextResponse struct {
App ACRApp `json:"app"` App ACRApp `json:"app"`
} }
type ContextControllerInput struct {
dig.In
Log *logger.Logger
Config *model.Config
Runtime *model.RuntimeConfig
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
}
type ContextController struct { type ContextController struct {
log *logger.Logger log *logger.Logger
config *model.Config config model.Config
runtime *model.RuntimeConfig runtime model.RuntimeConfig
} }
func NewContextController(i ContextControllerInput) *ContextController { func NewContextController(
log *logger.Logger,
config model.Config,
runtimeConfig model.RuntimeConfig,
router *gin.RouterGroup,
) *ContextController {
controller := &ContextController{ controller := &ContextController{
log: i.Log, log: log,
config: i.Config, config: config,
runtime: i.Runtime, runtime: runtimeConfig,
} }
if !i.Config.UI.WarningsEnabled { if !config.UI.WarningsEnabled {
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.") 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 := i.RouterGroup.Group("/context") contextGroup := router.Group("/context")
contextGroup.GET("/user", controller.userContextHandler) contextGroup.GET("/user", controller.userContextHandler)
contextGroup.GET("/app", controller.appContextHandler) contextGroup.GET("/app", controller.appContextHandler)
@@ -121,12 +121,7 @@ func TestContextController(t *testing.T) {
group := router.Group("/api") group := router.Group("/api")
gin.SetMode(gin.TestMode) gin.SetMode(gin.TestMode)
controller.NewContextController(controller.ContextControllerInput{ controller.NewContextController(log, cfg, runtime, group)
Log: log,
Config: &cfg,
Runtime: &runtime,
RouterGroup: group,
})
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
+4 -13
View File
@@ -1,24 +1,15 @@
package controller package controller
import ( import "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
"go.uber.org/dig"
)
type HealthController struct { type HealthController struct {
} }
type HealthControllerInput struct { func NewHealthController(router *gin.RouterGroup) *HealthController {
dig.In
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
}
func NewHealthController(i HealthControllerInput) *HealthController {
controller := &HealthController{} controller := &HealthController{}
i.RouterGroup.GET("/healthz", controller.healthHandler) router.GET("/healthz", controller.healthHandler)
i.RouterGroup.HEAD("/healthz", controller.healthHandler) router.HEAD("/healthz", controller.healthHandler)
return controller return controller
} }
@@ -55,9 +55,7 @@ func TestHealthController(t *testing.T) {
group := router.Group("/api") group := router.Group("/api")
gin.SetMode(gin.TestMode) gin.SetMode(gin.TestMode)
controller.NewHealthController(controller.HealthControllerInput{ controller.NewHealthController(group)
RouterGroup: group,
})
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
+14 -19
View File
@@ -11,7 +11,6 @@ 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"
@@ -23,30 +22,26 @@ 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
} }
type OAuthControllerInput struct { func NewOAuthController(
dig.In log *logger.Logger,
config model.Config,
Log *logger.Logger runtimeConfig model.RuntimeConfig,
Config *model.Config router *gin.RouterGroup,
RuntimeConfig *model.RuntimeConfig auth *service.AuthService,
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"` ) *OAuthController {
AuthService *service.AuthService
}
func NewOAuthController(i OAuthControllerInput) *OAuthController {
controller := &OAuthController{ controller := &OAuthController{
log: i.Log, log: log,
config: i.Config, config: config,
runtime: i.RuntimeConfig, runtime: runtimeConfig,
auth: i.AuthService, auth: auth,
} }
oauthGroup := i.RouterGroup.Group("/oauth") oauthGroup := router.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)
+13 -19
View File
@@ -11,7 +11,6 @@ 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"
@@ -31,7 +30,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 {
@@ -79,27 +78,22 @@ type AuthorizeCompleteRequest struct {
Ticket string `json:"ticket" binding:"required"` Ticket string `json:"ticket" binding:"required"`
} }
type OIDCControllerInput struct { func NewOIDCController(
dig.In log *logger.Logger,
oidcService *service.OIDCService,
Log *logger.Logger runtimeConfig model.RuntimeConfig,
OIDCService *service.OIDCService router *gin.RouterGroup,
RuntimeConfig *model.RuntimeConfig mainRouter *gin.RouterGroup) *OIDCController {
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"`
MainRouter *gin.RouterGroup `name:"mainRouterGroup"`
}
func NewOIDCController(i OIDCControllerInput) *OIDCController {
controller := &OIDCController{ controller := &OIDCController{
log: i.Log, log: log,
oidc: i.OIDCService, oidc: oidcService,
runtime: i.RuntimeConfig, runtime: runtimeConfig,
} }
i.MainRouter.POST("/authorize", controller.authorize) mainRouter.POST("/authorize", controller.authorize)
i.MainRouter.GET("/authorize", controller.authorize) mainRouter.GET("/authorize", controller.authorize)
oidcGroup := i.RouterGroup.Group("/oidc") oidcGroup := router.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)
+2 -14
View File
@@ -35,13 +35,7 @@ func TestOIDCController(t *testing.T) {
store := memory.New() store := memory.New()
oidcService, err := service.NewOIDCService(service.OIDCServiceInput{ oidcService, err := service.NewOIDCService(log, cfg, runtime, store, dg)
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,
@@ -837,13 +831,7 @@ func TestOIDCController(t *testing.T) {
svc = nil svc = nil
} }
controller.NewOIDCController(controller.OIDCControllerInput{ controller.NewOIDCController(log, svc, runtime, group, &router.RouterGroup)
Log: log,
OIDCService: svc,
RuntimeConfig: &runtime,
RouterGroup: group,
MainRouter: &router.RouterGroup,
})
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
+15 -20
View File
@@ -13,7 +13,6 @@ 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"
@@ -54,33 +53,29 @@ 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
} }
type ProxyControllerInput struct { func NewProxyController(
dig.In log *logger.Logger,
runtime model.RuntimeConfig,
Log *logger.Logger router *gin.RouterGroup,
RuntimeConfig *model.RuntimeConfig acls *service.AccessControlsService,
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"` auth *service.AuthService,
ACLsService *service.AccessControlsService policyEngine *service.PolicyEngine,
AuthService *service.AuthService ) *ProxyController {
PolicyEngine *service.PolicyEngine
}
func NewProxyController(i ProxyControllerInput) *ProxyController {
controller := &ProxyController{ controller := &ProxyController{
log: i.Log, log: log,
runtime: i.RuntimeConfig, runtime: runtime,
acls: i.ACLsService, acls: acls,
auth: i.AuthService, auth: auth,
policyEngine: i.PolicyEngine, policyEngine: policyEngine,
} }
proxyGroup := i.RouterGroup.Group("/auth") proxyGroup := router.Group("/auth")
proxyGroup.Any("/:proxy", controller.proxyHandler) proxyGroup.Any("/:proxy", controller.proxyHandler)
return controller return controller
+5 -34
View File
@@ -369,21 +369,10 @@ func TestProxyController(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
dg := ding.New(ctx) dg := ding.New(ctx)
broker := service.NewOAuthBrokerService(service.OAuthBrokerServiceInput{ broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
Log: log, aclsService := service.NewAccessControlsService(log, cfg, nil)
Runtime: &runtime,
Ctx: ctx,
})
aclsService := service.NewAccessControlsService(service.AccessControlServiceInput{
Log: log,
Config: &cfg,
LabelProvider: nil,
})
policyEngine, err := service.NewPolicyEngine(service.PolicyEngineInput{ policyEngine, err := service.NewPolicyEngine(cfg, log)
Log: log,
Config: &cfg,
})
require.NoError(t, err) require.NoError(t, err)
policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{ policyEngine.RegisterRule(service.RuleUserAllowed, &service.UserAllowedRule{
@@ -406,18 +395,7 @@ func TestProxyController(t *testing.T) {
Log: log, Log: log,
}) })
authService := service.NewAuthService(service.AuthServiceInput{ authService := service.NewAuthService(log, cfg, runtime, ctx, dg, nil, store, broker, nil, policyEngine)
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) {
@@ -432,14 +410,7 @@ func TestProxyController(t *testing.T) {
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
controller.NewProxyController(controller.ProxyControllerInput{ controller.NewProxyController(log, runtime, group, aclsService, authService, policyEngine)
Log: log,
RuntimeConfig: &runtime,
RouterGroup: group,
ACLsService: aclsService,
AuthService: authService,
PolicyEngine: policyEngine,
})
test.run(t, router, recorder) test.run(t, router, recorder)
}) })
+8 -13
View File
@@ -5,30 +5,25 @@ 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
} }
type ResourcesControllerInput struct { func NewResourcesController(
dig.In config model.Config,
router *gin.RouterGroup,
RouterGroup *gin.RouterGroup `name:"mainRouterGroup"` ) *ResourcesController {
Config *model.Config fileServer := http.StripPrefix("/resources", http.FileServer(http.Dir(config.Resources.Path)))
}
func NewResourcesController(i ResourcesControllerInput) *ResourcesController {
fileServer := http.StripPrefix("/resources", http.FileServer(http.Dir(i.Config.Resources.Path)))
controller := &ResourcesController{ controller := &ResourcesController{
config: i.Config, config: config,
fileServer: fileServer, fileServer: fileServer,
} }
i.RouterGroup.GET("/resources/*resource", controller.resourcesHandler) router.GET("/resources/*resource", controller.resourcesHandler)
return controller return controller
} }
@@ -69,10 +69,7 @@ func TestResourcesController(t *testing.T) {
group := router.Group("/") group := router.Group("/")
gin.SetMode(gin.TestMode) gin.SetMode(gin.TestMode)
controller.NewResourcesController(controller.ResourcesControllerInput{ controller.NewResourcesController(cfg, group)
RouterGroup: group,
Config: &cfg,
})
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
test.run(t, router, recorder) test.run(t, router, recorder)
+11 -16
View File
@@ -11,7 +11,6 @@ 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"
@@ -28,27 +27,23 @@ 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
} }
type UserControllerInput struct { func NewUserController(
dig.In log *logger.Logger,
runtimeConfig model.RuntimeConfig,
Log *logger.Logger router *gin.RouterGroup,
RuntimeConfig *model.RuntimeConfig auth *service.AuthService,
RouterGroup *gin.RouterGroup `name:"apiRouterGroup"` ) *UserController {
AuthService *service.AuthService
}
func NewUserController(i UserControllerInput) *UserController {
controller := &UserController{ controller := &UserController{
log: i.Log, log: log,
runtime: i.RuntimeConfig, runtime: runtimeConfig,
auth: i.AuthService, auth: auth,
} }
userGroup := i.RouterGroup.Group("/user") userGroup := router.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)
+4 -27
View File
@@ -414,29 +414,11 @@ func TestUserController(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
dg := ding.New(ctx) dg := ding.New(ctx)
policyEngine, err := service.NewPolicyEngine(service.PolicyEngineInput{ policyEngine, err := service.NewPolicyEngine(cfg, log)
Log: log,
Config: &cfg,
})
require.NoError(t, err) require.NoError(t, err)
broker := service.NewOAuthBrokerService(service.OAuthBrokerServiceInput{ broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
Log: log, authService := service.NewAuthService(log, cfg, runtime, ctx, dg, nil, store, broker, nil, policyEngine)
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
@@ -455,12 +437,7 @@ func TestUserController(t *testing.T) {
group := router.Group("/api") group := router.Group("/api")
gin.SetMode(gin.TestMode) gin.SetMode(gin.TestMode)
controller.NewUserController(controller.UserControllerInput{ controller.NewUserController(log, runtime, group, authService)
Log: log,
RuntimeConfig: &runtime,
RouterGroup: group,
AuthService: authService,
})
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
+4 -87
View File
@@ -3,27 +3,11 @@ package controller
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"slices"
"strings"
"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"
) )
const OpenIDConnectRel = "http://openid.net/specs/connect/1.0/issuer"
type WebfingerResponseLink struct {
Rel string `json:"rel,omitempty"`
Href string `json:"href"`
}
type WebfingerResponse struct {
Subject string `json:"subject"`
Links []WebfingerResponseLink `json:"links"`
}
type OpenIDConnectConfiguration struct { type OpenIDConnectConfiguration struct {
Issuer string `json:"issuer"` Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"` AuthorizationEndpoint string `json:"authorization_endpoint"`
@@ -46,21 +30,13 @@ type WellKnownController struct {
oidc *service.OIDCService oidc *service.OIDCService
} }
type WellKnownControllerInput struct { func NewWellKnownController(oidc *service.OIDCService, router *gin.RouterGroup) *WellKnownController {
dig.In
OIDCService *service.OIDCService
RouterGroup *gin.RouterGroup `name:"mainRouterGroup"`
}
func NewWellKnownController(i WellKnownControllerInput) *WellKnownController {
controller := &WellKnownController{ controller := &WellKnownController{
oidc: i.OIDCService, oidc: oidc,
} }
i.RouterGroup.GET("/.well-known/openid-configuration", controller.OpenIDConnectConfiguration) router.GET("/.well-known/openid-configuration", controller.OpenIDConnectConfiguration)
i.RouterGroup.GET("/.well-known/jwks.json", controller.JWKS) router.GET("/.well-known/jwks.json", controller.JWKS)
i.RouterGroup.GET("/.well-known/webfinger", controller.WebFinger)
return controller return controller
} }
@@ -121,62 +97,3 @@ func (controller *WellKnownController) JWKS(c *gin.Context) {
c.Status(http.StatusOK) c.Status(http.StatusOK)
} }
func (controller *WellKnownController) WebFinger(c *gin.Context) {
c.Header("Content-Type", "application/jrd+json")
c.Header("Access-Control-Allow-Origin", "*")
resource := c.Query("resource")
if !controller.validateWebFingerResource(resource) {
c.JSON(400, gin.H{
"status": 400,
"message": "invalid resource",
})
return
}
res := WebfingerResponse{
Subject: resource,
Links: []WebfingerResponseLink{},
}
rel := c.Request.URL.Query()["rel"]
if controller.oidc != nil && (len(rel) == 0 || slices.Contains(rel, OpenIDConnectRel)) {
res.Links = append(res.Links, WebfingerResponseLink{Rel: OpenIDConnectRel, Href: controller.oidc.GetIssuer()})
}
c.JSON(200, res)
}
func (controller *WellKnownController) validateWebFingerResource(resource string) bool {
prefix, suffix, found := strings.Cut(resource, ":")
if !found {
return false
}
switch prefix {
case "acct":
if strings.Count(suffix, "@") != 1 {
return false
}
username, domain, found := strings.Cut(suffix, "@")
if !found || username == "" || domain == "" {
return false
}
case "https", "http":
u, err := url.Parse(resource)
if err != nil {
return false
}
if u.Host == "" {
return false
}
default:
return false
}
return true
}
@@ -93,13 +93,7 @@ func TestWellKnownController(t *testing.T) {
store := memory.New() store := memory.New()
oidcService, err := service.NewOIDCService(service.OIDCServiceInput{ oidcService, err := service.NewOIDCService(log, cfg, runtime, store, dg)
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 {
@@ -109,10 +103,7 @@ func TestWellKnownController(t *testing.T) {
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
controller.NewWellKnownController(controller.WellKnownControllerInput{ controller.NewWellKnownController(oidcService, &router.RouterGroup)
OIDCService: oidcService,
RouterGroup: &router.RouterGroup,
})
test.run(t, router, recorder) test.run(t, router, recorder)
}) })
+13 -18
View File
@@ -11,7 +11,6 @@ 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"
) )
@@ -38,29 +37,25 @@ 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
} }
type ContextMiddlewareInput struct { func NewContextMiddleware(
dig.In log *logger.Logger,
runtime model.RuntimeConfig,
Log *logger.Logger auth *service.AuthService,
RuntimeConfig *model.RuntimeConfig broker *service.OAuthBrokerService,
AuthService *service.AuthService tailscale *service.TailscaleService,
BrokerService *service.OAuthBrokerService ) *ContextMiddleware {
TailscaleService *service.TailscaleService
}
func NewContextMiddleware(i ContextMiddlewareInput) *ContextMiddleware {
return &ContextMiddleware{ return &ContextMiddleware{
log: i.Log, log: log,
runtime: i.RuntimeConfig, runtime: runtime,
auth: i.AuthService, auth: auth,
broker: i.BrokerService, broker: broker,
tailscale: i.TailscaleService, tailscale: tailscale,
} }
} }
+4 -28
View File
@@ -254,37 +254,13 @@ func TestContextMiddleware(t *testing.T) {
store := memory.New() store := memory.New()
policyEngine, err := service.NewPolicyEngine(service.PolicyEngineInput{ policyEngine, err := service.NewPolicyEngine(cfg, log)
Log: log,
Config: &cfg,
})
require.NoError(t, err) require.NoError(t, err)
broker := service.NewOAuthBrokerService(service.OAuthBrokerServiceInput{ broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
Log: log, authService := service.NewAuthService(log, cfg, runtime, ctx, dg, nil, store, broker, nil, policyEngine)
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(middleware.ContextMiddlewareInput{ contextMiddleware := middleware.NewContextMiddleware(log, runtime, authService, broker, nil)
Log: log,
RuntimeConfig: &runtime,
AuthService: authService,
BrokerService: broker,
TailscaleService: nil,
})
for _, test := range tests { for _, test := range tests {
authService.ClearLoginAttempts() authService.ClearLoginAttempts()
+1 -7
View File
@@ -9,7 +9,6 @@ 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"
) )
@@ -19,12 +18,7 @@ type UIMiddleware struct {
uiFileServer http.Handler uiFileServer http.Handler
} }
// for future use if we need to inject dependencies into the middleware func NewUIMiddleware() (*UIMiddleware, error) {
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")
+2 -9
View File
@@ -6,7 +6,6 @@ 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
@@ -22,15 +21,9 @@ type ZerologMiddleware struct {
log *logger.Logger log *logger.Logger
} }
type ZerologMiddlewareInput struct { func NewZerologMiddleware(log *logger.Logger) *ZerologMiddleware {
dig.In
Log *logger.Logger
}
func NewZerologMiddleware(i ZerologMiddlewareInput) *ZerologMiddleware {
return &ZerologMiddleware{ return &ZerologMiddleware{
log: i.Log, log: log,
} }
} }
+1
View File
@@ -12,6 +12,7 @@ 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
} }
+9 -16
View File
@@ -5,7 +5,6 @@ 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 {
@@ -15,23 +14,17 @@ 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
} }
type AccessControlServiceInput struct { func NewAccessControlsService(
dig.In deps *ServiceDependencies,
) *AccessControlsService {
Log *logger.Logger
Config *model.Config
LabelProvider LabelProvider `optional:"true"`
}
func NewAccessControlsService(i AccessControlServiceInput) *AccessControlsService {
return &AccessControlsService{ return &AccessControlsService{
log: i.Log, log: deps.Log,
config: i.Config, config: deps.StaticConfig,
labelProvider: i.LabelProvider, labelProvider: &deps.LabelProvider,
} }
} }
@@ -63,8 +56,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 { if service.labelProvider != nil && *service.labelProvider != nil {
return service.labelProvider.GetLabels(domain) return (*service.labelProvider).GetLabels(domain)
} }
// no labels // no labels
@@ -87,11 +87,7 @@ 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(AccessControlServiceInput{ svc := NewAccessControlsService(log, model.Config{Apps: tt.apps}, nil)
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)
@@ -116,11 +112,7 @@ func TestGetAccessControls(t *testing.T) {
}, },
}, },
} }
svc := NewAccessControlsService(AccessControlServiceInput{ svc := NewAccessControlsService(log, config, nil)
Log: log,
Config: &config,
LabelProvider: nil,
})
got, err := svc.GetAccessControls("foo.example.com") got, err := svc.GetAccessControls("foo.example.com")
@@ -131,11 +123,7 @@ 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(AccessControlServiceInput{ svc := NewAccessControlsService(log, model.Config{}, nil)
Log: log,
Config: &model.Config{},
LabelProvider: nil,
})
got, err := svc.GetAccessControls("unknown.example.com") got, err := svc.GetAccessControls("unknown.example.com")
@@ -145,11 +133,7 @@ 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(AccessControlServiceInput{ svc := NewAccessControlsService(log, model.Config{}, &provider)
Log: log,
Config: &model.Config{},
LabelProvider: provider, // nil provider
})
got, err := svc.GetAccessControls("unknown.example.com") got, err := svc.GetAccessControls("unknown.example.com")
@@ -168,11 +152,7 @@ func TestGetAccessControls(t *testing.T) {
}, },
} }
var provider LabelProvider = mock var provider LabelProvider = mock
svc := NewAccessControlsService(AccessControlServiceInput{ svc := NewAccessControlsService(log, model.Config{}, &provider)
Log: log,
Config: &model.Config{},
LabelProvider: provider,
})
got, err := svc.GetAccessControls("dynamic.example.com") got, err := svc.GetAccessControls("dynamic.example.com")
@@ -190,11 +170,7 @@ func TestGetAccessControls(t *testing.T) {
"foo": {Config: model.AppConfig{Domain: "foo.example.com"}}, "foo": {Config: model.AppConfig{Domain: "foo.example.com"}},
}, },
} }
svc := NewAccessControlsService(AccessControlServiceInput{ svc := NewAccessControlsService(log, config, &provider)
Log: log,
Config: &config,
LabelProvider: provider,
})
got, err := svc.GetAccessControls("foo.example.com") got, err := svc.GetAccessControls("foo.example.com")
@@ -212,11 +188,7 @@ func TestGetAccessControls(t *testing.T) {
}, },
} }
var provider LabelProvider = mock var provider LabelProvider = mock
svc := NewAccessControlsService(AccessControlServiceInput{ svc := NewAccessControlsService(log, model.Config{}, &provider)
Log: log,
Config: &model.Config{},
LabelProvider: provider,
})
got, err := svc.GetAccessControls("dynamic.example.com") got, err := svc.GetAccessControls("dynamic.example.com")
+13 -27
View File
@@ -14,7 +14,6 @@ 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"
@@ -83,32 +82,19 @@ type AuthService struct {
} }
} }
type AuthServiceInput struct { func NewAuthService(
dig.In deps *ServiceDependencies,
) *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: i.Log, log: deps.Log,
runtime: i.Runtime, runtime: deps.RuntimeConfig,
ctx: i.Ctx, ctx: deps.Ctx,
config: i.Config, config: deps.StaticConfig,
ldap: i.LDAP, ldap: deps.Services.LDAPService,
queries: i.Queries, queries: *deps.Queries,
oauthBroker: i.OAuthBroker, oauthBroker: deps.Services.OAuthBrokerService,
tailscale: i.Tailscale, tailscale: deps.Services.TailscaleService,
policyEngine: i.PolicyEngine, policyEngine: deps.Services.PolicyEngine,
} }
// caches setup // caches setup
@@ -120,7 +106,7 @@ func NewAuthService(i AuthServiceInput) *AuthService {
service.caches.login = loginCache service.caches.login = loginCache
service.caches.ldap = ldapCache service.caches.ldap = ldapCache
i.Ding.Go(func(ctx context.Context) { deps.Ding.Go(func(ctx context.Context) {
ticker := time.NewTicker(1 * time.Minute) ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop() defer ticker.Stop()
+1 -16
View File
@@ -4,7 +4,6 @@ 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"
) )
@@ -13,22 +12,9 @@ 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": {
@@ -42,7 +28,6 @@ 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"))
+9 -16
View File
@@ -8,7 +8,6 @@ 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"
@@ -22,40 +21,34 @@ type DockerService struct {
isConnected bool isConnected bool
} }
type DockerServiceInput struct { func NewDockerService(
dig.In deps *ServiceDependencies,
) (*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(i.Ctx) client.NegotiateAPIVersion(deps.Ctx)
_, err = client.Ping(i.Ctx) _, err = client.Ping(deps.Ctx)
if err != nil { if err != nil {
i.Log.App.Debug().Err(err).Msg("Docker not connected") deps.Log.App.Debug().Err(err).Msg("Docker not connected")
return nil, nil return nil, nil
} }
service := &DockerService{ service := &DockerService{
log: i.Log, log: deps.Log,
client: client, client: client,
context: i.Ctx, context: deps.Ctx,
} }
service.isConnected = true service.isConnected = true
service.log.App.Debug().Msg("Docker connected successfully") service.log.App.Debug().Msg("Docker connected successfully")
i.Ding.Go(service.watchAndClose, ding.RingMajor) deps.Ding.Go(service.watchAndClose, ding.RingMajor)
return service, nil return service, nil
} }
+9 -16
View File
@@ -12,7 +12,6 @@ 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"
@@ -49,15 +48,9 @@ type KubernetesService struct {
appNameIndex map[string]ingressAppKey appNameIndex map[string]ingressAppKey
} }
type KubernetesServiceInput struct { func NewKubernetesService(
dig.In deps *ServiceDependencies,
) (*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)
@@ -74,31 +67,31 @@ func NewKubernetesService(i KubernetesServiceInput) (*KubernetesService, error)
Resource: "ingresses", Resource: "ingresses",
} }
accessCtx, accessCancel := context.WithTimeout(i.Ctx, 5*time.Second) accessCtx, accessCancel := context.WithTimeout(deps.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 {
i.Log.App.Warn().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Failed to access Ingress API, Kubernetes label provider will be disabled") deps.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)
} }
i.Log.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Successfully accessed Ingress API, starting watcher") deps.Log.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Successfully accessed Ingress API, starting watcher")
service := &KubernetesService{ service := &KubernetesService{
log: i.Log, log: deps.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),
} }
i.Ding.Go(func(ctx context.Context) { deps.Ding.Go(func(ctx context.Context) {
service.watchGVR(gvr, ctx) service.watchGVR(gvr, ctx)
}, ding.RingMajor) }, ding.RingMajor)
service.started = true service.started = true
i.Log.App.Debug().Msg("Kubernetes label provider started successfully") deps.Log.App.Debug().Msg("Kubernetes label provider started successfully")
return service, nil return service, nil
} }
+18 -24
View File
@@ -13,48 +13,42 @@ 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
bindPw string ldapBindPw string
} }
type LdapServiceInput struct { func NewLdapService(
dig.In deps *ServiceDependencies,
) (*LdapService, error) {
Log *logger.Logger if deps.StaticConfig.LDAP.Address == "" {
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: i.Log, log: deps.Log,
config: i.Config, config: deps.StaticConfig,
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 i.Config.LDAP.AuthCert != "" && i.Config.LDAP.AuthKey != "" { if deps.StaticConfig.LDAP.AuthCert != "" && deps.StaticConfig.LDAP.AuthKey != "" {
cert, err := tls.LoadX509KeyPair(i.Config.LDAP.AuthCert, i.Config.LDAP.AuthKey) cert, err := tls.LoadX509KeyPair(deps.StaticConfig.LDAP.AuthCert, deps.StaticConfig.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)
} }
i.Log.App.Info().Msg("LDAP mTLS authentication configured successfully") ldap.log.App.Info().Msg("LDAP mTLS authentication configured successfully")
ldap.cert = &cert ldap.cert = &cert
@@ -76,7 +70,7 @@ func NewLdapService(i LdapServiceInput) (*LdapService, error) {
return nil, fmt.Errorf("failed to connect to ldap server: %w", err) return nil, fmt.Errorf("failed to connect to ldap server: %w", err)
} }
i.Ding.Go(func(ctx context.Context) { deps.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)
@@ -221,7 +215,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.bindPw) return ldap.conn.Bind(ldap.config.LDAP.BindDN, ldap.config.LDAP.BindPassword)
} }
func (ldap *LdapService) Bind(userDN string, password string) error { func (ldap *LdapService) Bind(userDN string, password string) error {
+7 -14
View File
@@ -5,7 +5,6 @@ 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"
@@ -33,27 +32,21 @@ var presets = map[string]func(config model.OAuthServiceConfig, ctx context.Conte
"google": newGoogleOAuthService, "google": newGoogleOAuthService,
} }
type OAuthBrokerServiceInput struct { func NewOAuthBrokerService(
dig.In deps *ServiceDependencies,
) *OAuthBrokerService {
Log *logger.Logger
Runtime *model.RuntimeConfig
Ctx context.Context
}
func NewOAuthBrokerService(i OAuthBrokerServiceInput) *OAuthBrokerService {
service := &OAuthBrokerService{ service := &OAuthBrokerService{
log: i.Log, log: deps.Log,
services: make(map[string]OAuthServiceImpl), services: make(map[string]OAuthServiceImpl),
configs: i.Runtime.OAuthProviders, configs: deps.RuntimeConfig.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, i.Ctx) service.services[name] = presetFunc(cfg, deps.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, i.Ctx) service.services[name] = NewOAuthService(cfg, name, deps.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")
} }
} }
+23 -41
View File
@@ -14,7 +14,6 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
@@ -27,7 +26,6 @@ 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 (
@@ -151,24 +149,16 @@ type OIDCService struct {
} }
} }
type OIDCServiceInput struct { func NewOIDCService(
dig.In deps *ServiceDependencies,
) (*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(i.Config.OIDC.Clients) == 0 { if len(deps.RuntimeConfig.OIDCClients) == 0 {
return nil, nil return nil, nil
} }
// Ensure issuer is https // Ensure issuer is https
uissuer, err := url.Parse(i.Runtime.AppURL) uissuer, err := url.Parse(deps.RuntimeConfig.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)
@@ -181,14 +171,14 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
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(i.Config.OIDC.PrivateKeyPath) == "" || if strings.TrimSpace(deps.StaticConfig.OIDC.PrivateKeyPath) == "" ||
strings.TrimSpace(i.Config.OIDC.PublicKeyPath) == "" { strings.TrimSpace(deps.StaticConfig.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(i.Config.OIDC.PrivateKeyPath) fprivateKey, err := os.ReadFile(deps.StaticConfig.OIDC.PrivateKeyPath)
if err != nil && !errors.Is(err, os.ErrNotExist) { if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, err return nil, err
@@ -207,12 +197,8 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
Type: "RSA PRIVATE KEY", Type: "RSA PRIVATE KEY",
Bytes: der, Bytes: der,
}) })
i.Log.App.Trace().Str("type", "RSA PRIVATE KEY").Msg("Generated private RSA key") deps.Log.App.Trace().Str("type", "RSA PRIVATE KEY").Msg("Generated private RSA key")
err := os.MkdirAll(filepath.Dir(i.Config.OIDC.PrivateKeyPath), 0700) err = os.WriteFile(deps.StaticConfig.OIDC.PrivateKeyPath, encoded, 0600)
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)
} }
@@ -221,7 +207,7 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
if block == nil { if block == nil {
return nil, errors.New("failed to decode private key") return nil, errors.New("failed to decode private key")
} }
i.Log.App.Trace().Str("type", block.Type).Msg("Loaded private key") deps.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)
@@ -230,7 +216,7 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
var publicKey crypto.PublicKey var publicKey crypto.PublicKey
fpublicKey, err := os.ReadFile(i.Config.OIDC.PublicKeyPath) fpublicKey, err := os.ReadFile(deps.StaticConfig.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)
@@ -246,12 +232,8 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
Type: "RSA PUBLIC KEY", Type: "RSA PUBLIC KEY",
Bytes: der, Bytes: der,
}) })
i.Log.App.Trace().Str("type", "RSA PUBLIC KEY").Msg("Generated public RSA key") deps.Log.App.Trace().Str("type", "RSA PUBLIC KEY").Msg("Generated public RSA key")
err := os.MkdirAll(filepath.Dir(i.Config.OIDC.PublicKeyPath), 0700) err = os.WriteFile(deps.StaticConfig.OIDC.PublicKeyPath, encoded, 0644)
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
} }
@@ -260,7 +242,7 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
if block == nil { if block == nil {
return nil, errors.New("failed to decode public key") return nil, errors.New("failed to decode public key")
} }
i.Log.App.Trace().Str("type", block.Type).Msg("Loaded public key") deps.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)
@@ -290,7 +272,7 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
// 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 i.Config.OIDC.Clients { for id, client := range deps.StaticConfig.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)
@@ -306,15 +288,15 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
} }
client.ClientSecretFile = "" client.ClientSecretFile = ""
clients[id] = client clients[id] = client
i.Log.App.Debug().Str("clientId", client.ClientID).Msg("Loaded OIDC client configuration") deps.Log.App.Debug().Str("clientId", client.ClientID).Msg("Loaded OIDC client configuration")
} }
// Initialize the service // Initialize the service
service := &OIDCService{ service := &OIDCService{
log: i.Log, log: deps.Log,
config: i.Config, config: deps.StaticConfig,
runtime: i.Runtime, runtime: deps.RuntimeConfig,
queries: i.Queries, queries: *deps.Queries,
clients: clients, clients: clients,
privateKey: privateKey, privateKey: privateKey,
@@ -323,7 +305,7 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
} }
// Start cleanup routine // Start cleanup routine
i.Ding.Go(service.cleanupRoutine, ding.RingMinor) deps.Ding.Go(service.cleanupRoutine, ding.RingMinor)
// Create caches // Create caches
codeCash := NewCacheStore[AuthorizeCodeEntry](256) codeCash := NewCacheStore[AuthorizeCodeEntry](256)
@@ -335,7 +317,7 @@ func NewOIDCService(i OIDCServiceInput) (*OIDCService, error) {
service.caches.authorize = authorize service.caches.authorize = authorize
// Start cache cleanup routine // Start cache cleanup routine
i.Ding.Go(func(ctx context.Context) { deps.Ding.Go(func(ctx context.Context) {
ticker := time.NewTicker(1 * time.Minute) ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop() defer ticker.Stop()
+1 -10
View File
@@ -9,7 +9,6 @@ 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"
) )
@@ -68,15 +67,7 @@ func TestCompileUserinfo(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
dg := ding.New(ctx) dg := ding.New(ctx)
store := memory.New() svc, err := service.NewOIDCService(log, cfg, runtime, nil, dg)
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 {
+8 -14
View File
@@ -6,7 +6,6 @@ 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
@@ -41,28 +40,23 @@ type PolicyEngine struct {
policy Policy policy Policy
} }
type PolicyEngineInput struct { func NewPolicyEngine(
dig.In deps *ServiceDependencies,
) (*PolicyEngine, error) {
Log *logger.Logger
Config *model.Config
}
func NewPolicyEngine(i PolicyEngineInput) (*PolicyEngine, error) {
engine := PolicyEngine{ engine := PolicyEngine{
log: i.Log, log: deps.Log,
rules: make(map[RuleName]Rule), rules: make(map[RuleName]Rule),
} }
switch i.Config.Auth.ACLs.Policy { switch deps.StaticConfig.Auth.ACLs.Policy {
case string(PolicyAllow): case string(PolicyAllow):
i.Log.App.Debug().Msg("Using 'allow' ACL policy: access to apps will be allowed by default unless explicitly blocked") deps.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):
i.Log.App.Debug().Msg("Using 'deny' ACL policy: access to apps will be blocked by default unless explicitly allowed") deps.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", i.Config.Auth.ACLs.Policy) return nil, fmt.Errorf("invalid acl policy: %s", deps.StaticConfig.Auth.ACLs.Policy)
} }
return &engine, nil return &engine, nil
+6 -24
View File
@@ -33,35 +33,23 @@ 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(service.PolicyEngineInput{ _, err := service.NewPolicyEngine(cfg, log)
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(service.PolicyEngineInput{ engine, err := service.NewPolicyEngine(cfg, log)
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(service.PolicyEngineInput{ engine, err = service.NewPolicyEngine(cfg, log)
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(service.PolicyEngineInput{ engine, err = service.NewPolicyEngine(cfg, log)
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"]
@@ -69,10 +57,7 @@ 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(service.PolicyEngineInput{ engine, err = service.NewPolicyEngine(cfg, log)
Log: log,
Config: &cfg,
})
assert.NoError(t, err) assert.NoError(t, err)
engine.RegisterRule("test-rule", testRule) engine.RegisterRule("test-rule", testRule)
@@ -90,10 +75,7 @@ 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(service.PolicyEngineInput{ engine, err = service.NewPolicyEngine(cfg, log)
Log: log,
Config: &cfg,
})
assert.NoError(t, err) assert.NoError(t, err)
engine.RegisterRule("test-rule", testRule) engine.RegisterRule("test-rule", testRule)
+33
View File
@@ -0,0 +1,33 @@
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
}
+15 -23
View File
@@ -12,7 +12,6 @@ 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"
) )
@@ -35,31 +34,24 @@ type TailscaleService struct {
mu sync.Mutex mu sync.Mutex
} }
type TailscaleServiceInput struct { func NewTailscaleService(
dig.In deps *ServiceDependencies,
) (*TailscaleService, error) {
Log *logger.Logger if !deps.StaticConfig.Tailscale.Enabled {
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 = i.Config.Tailscale.Dir srv.Dir = deps.StaticConfig.Tailscale.Dir
srv.Hostname = i.Config.Tailscale.Hostname srv.Hostname = deps.StaticConfig.Tailscale.Hostname
srv.AuthKey = i.Config.Tailscale.AuthKey srv.AuthKey = deps.StaticConfig.Tailscale.AuthKey
srv.Ephemeral = i.Config.Tailscale.Ephemeral srv.Ephemeral = deps.StaticConfig.Tailscale.Ephemeral
// redirect logs to zerolog // redirect logs to zerolog
srv.Logf = i.Log.App.Printf srv.Logf = deps.Log.App.Printf
srv.UserLogf = i.Log.App.Printf srv.UserLogf = deps.Log.App.Printf
err := srv.Start() err := srv.Start()
@@ -75,14 +67,14 @@ func NewTailscaleService(i TailscaleServiceInput) (*TailscaleService, error) {
} }
service := &TailscaleService{ service := &TailscaleService{
log: i.Log, log: deps.Log,
config: i.Config, config: deps.StaticConfig,
ctx: i.Ctx, ctx: deps.Ctx,
srv: srv, srv: srv,
lc: lc, lc: lc,
} }
connectCtx, cancel := context.WithTimeout(i.Ctx, 2*time.Minute) // large enough timeout to allow for user to manually authenticate with link if needed connectCtx, cancel := context.WithTimeout(deps.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)
@@ -92,7 +84,7 @@ func NewTailscaleService(i TailscaleServiceInput) (*TailscaleService, error) {
return nil, fmt.Errorf("failed to connect to tailscale network: %w", err) return nil, fmt.Errorf("failed to connect to tailscale network: %w", err)
} }
i.Ding.Go(service.watchAndClose, ding.RingMajor) deps.Ding.Go(service.watchAndClose, ding.RingMajor)
return service, nil return service, nil
} }
+8
View File
@@ -121,6 +121,14 @@ 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