diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f4441b7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "paerser"] + path = paerser + url = https://github.com/traefik/paerser diff --git a/Dockerfile.dev b/Dockerfile.dev index 96ea9b9..7efe722 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -2,6 +2,8 @@ FROM golang:1.25-alpine3.21 WORKDIR /tinyauth +COPY ./paerser ./paerser + COPY go.mod ./ COPY go.sum ./ @@ -20,4 +22,4 @@ ENV TINYAUTH_DATABASEPATH=/data/tinyauth.db ENV TINYAUTH_RESOURCESDIR=/data/resources -ENTRYPOINT ["air", "-c", "air.toml"] \ No newline at end of file +ENTRYPOINT ["air", "-c", "air.toml"] diff --git a/go.mod b/go.mod index 5979f36..57f7720 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.24.0 toolchain go1.24.3 +replace github.com/traefik/paerser v0.2.2 => ./paerser + require ( github.com/cenkalti/backoff/v5 v5.0.3 github.com/charmbracelet/huh v0.8.0 diff --git a/go.sum b/go.sum index 4a44d52..b6d649b 100644 --- a/go.sum +++ b/go.sum @@ -271,8 +271,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/traefik/paerser v0.2.2 h1:cpzW/ZrQrBh3mdwD/jnp6aXASiUFKOVr6ldP+keJTcQ= -github.com/traefik/paerser v0.2.2/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= diff --git a/internal/config/config.go b/internal/config/config.go index f892757..d3e00de 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,7 +15,6 @@ var RedirectCookieName = "tinyauth-redirect" // Main app config type Config struct { - Apps AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"` LogLevel string `description:"Log level (trace, debug, info, warn, error)." yaml:"logLevel"` ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"` @@ -26,6 +25,7 @@ type Config struct { LogJSON bool `description:"Enable JSON formatted logs." yaml:"logJSON"` Server ServerConfig `description:"Server configuration." yaml:"server"` Auth AuthConfig `description:"Authentication configuration." yaml:"auth"` + Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"` OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"` UI UIConfig `description:"UI customization." yaml:"ui"` Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"` diff --git a/internal/controller/proxy_controller_test.go b/internal/controller/proxy_controller_test.go index d0de555..54bb888 100644 --- a/internal/controller/proxy_controller_test.go +++ b/internal/controller/proxy_controller_test.go @@ -41,7 +41,7 @@ func setupProxyController(t *testing.T, middlewares *[]gin.HandlerFunc) (*gin.En assert.NilError(t, dockerService.Init()) // Access controls - accessControlsService := service.NewAccessControlsService(dockerService) + accessControlsService := service.NewAccessControlsService(dockerService, map[string]config.App{}) assert.NilError(t, accessControlsService.Init()) diff --git a/internal/service/access_controls_service.go b/internal/service/access_controls_service.go index b9c3dad..74117ea 100644 --- a/internal/service/access_controls_service.go +++ b/internal/service/access_controls_service.go @@ -10,13 +10,13 @@ import ( type AccessControlsService struct { docker *DockerService - config config.Apps + static map[string]config.App } -func NewAccessControlsService(docker *DockerService, config config.Apps) *AccessControlsService { +func NewAccessControlsService(docker *DockerService, static map[string]config.App) *AccessControlsService { return &AccessControlsService{ docker: docker, - config: config, + static: static, } } @@ -24,8 +24,8 @@ func (acls *AccessControlsService) Init() error { return nil // No initialization needed } -func (acls *AccessControlsService) lookupConfigACLs(domain string) (config.App, error) { - for app, config := range acls.config.Apps { +func (acls *AccessControlsService) lookupStaticACLs(domain string) (config.App, error) { + for app, config := range acls.static { if config.Config.Domain == domain { log.Debug().Str("name", app).Msg("Found matching container by domain") return config, nil @@ -41,7 +41,7 @@ func (acls *AccessControlsService) lookupConfigACLs(domain string) (config.App, func (acls *AccessControlsService) GetAccessControls(domain string) (config.App, error) { // First check in the static config - app, err := acls.lookupConfigACLs(domain) + app, err := acls.lookupStaticACLs(domain) if err == nil { log.Debug().Msg("Using ACls from static configuration") diff --git a/paerser b/paerser new file mode 160000 index 0000000..9364ccd --- /dev/null +++ b/paerser @@ -0,0 +1 @@ +Subproject commit 9364ccd8baa484ac402cdd7ba4c40c67f957aa33 diff --git a/patches/nested_maps.diff b/patches/nested_maps.diff new file mode 100644 index 0000000..91c883f --- /dev/null +++ b/patches/nested_maps.diff @@ -0,0 +1,95 @@ +diff --git a/env/env_test.go b/env/env_test.go +index 7045569..365dc00 100644 +--- a/env/env_test.go ++++ b/env/env_test.go +@@ -166,6 +166,38 @@ func TestDecode(t *testing.T) { + Foo: &struct{ Field string }{}, + }, + }, ++ { ++ desc: "map under the root key", ++ environ: []string{"TRAEFIK_FOO_BAR_FOOBAR_BARFOO=foo"}, ++ element: &struct { ++ Foo map[string]struct { ++ Foobar struct { ++ Barfoo string ++ } ++ } ++ }{}, ++ expected: &struct { ++ Foo map[string]struct { ++ Foobar struct { ++ Barfoo string ++ } ++ } ++ }{ ++ Foo: map[string]struct { ++ Foobar struct { ++ Barfoo string ++ } ++ }{ ++ "bar": { ++ Foobar: struct { ++ Barfoo string ++ }{ ++ Barfoo: "foo", ++ }, ++ }, ++ }, ++ }, ++ }, + } + + for _, test := range testCases { +diff --git a/parser/nodes_metadata.go b/parser/nodes_metadata.go +index 36946c1..0279705 100644 +--- a/parser/nodes_metadata.go ++++ b/parser/nodes_metadata.go +@@ -75,8 +75,13 @@ func (m metadata) add(rootType reflect.Type, node *Node) error { + node.Kind = fType.Kind() + node.Tag = field.Tag + +- if fType.Kind() == reflect.Struct || fType.Kind() == reflect.Pointer && fType.Elem().Kind() == reflect.Struct || +- fType.Kind() == reflect.Map { ++ if node.Kind == reflect.String && len(node.Children) > 0 { ++ fType = reflect.TypeOf(struct{}{}) ++ node.Kind = reflect.Struct ++ } ++ ++ if node.Kind == reflect.Struct || node.Kind == reflect.Pointer && fType.Elem().Kind() == reflect.Struct || ++ node.Kind == reflect.Map { + if len(node.Children) == 0 && !(field.Tag.Get(m.TagName) == TagLabelAllowEmpty || field.Tag.Get(m.TagName) == "-") { + return fmt.Errorf("%s cannot be a standalone element (type %s)", node.Name, fType) + } +@@ -90,11 +95,11 @@ func (m metadata) add(rootType reflect.Type, node *Node) error { + return nil + } + +- if fType.Kind() == reflect.Struct || fType.Kind() == reflect.Pointer && fType.Elem().Kind() == reflect.Struct { ++ if node.Kind == reflect.Struct || node.Kind == reflect.Pointer && fType.Elem().Kind() == reflect.Struct { + return m.browseChildren(fType, node) + } + +- if fType.Kind() == reflect.Map { ++ if node.Kind == reflect.Map { + if fType.Elem().Kind() == reflect.Interface { + addRawValue(node) + return nil +@@ -115,7 +120,7 @@ func (m metadata) add(rootType reflect.Type, node *Node) error { + return nil + } + +- if fType.Kind() == reflect.Slice { ++ if node.Kind == reflect.Slice { + if m.AllowSliceAsStruct && field.Tag.Get(TagLabelSliceAsStruct) != "" { + return m.browseChildren(fType.Elem(), node) + } +@@ -129,7 +134,7 @@ func (m metadata) add(rootType reflect.Type, node *Node) error { + return nil + } + +- return fmt.Errorf("invalid node %s: %v", node.Name, fType.Kind()) ++ return fmt.Errorf("invalid node %s: %v", node.Name, node.Kind) + } + + func (m metadata) findTypedField(rType reflect.Type, node *Node) (reflect.StructField, error) {