Compare commits

..

2 Commits

Author SHA1 Message Date
Stavros
80121f2a36 fix: review comments 2025-12-26 18:55:08 +02:00
Stavros
eef674a4e6 feat: forward sub from oidc providers 2025-12-26 18:28:53 +02:00
18 changed files with 147 additions and 317 deletions

View File

@@ -18,16 +18,7 @@ jobs:
- name: Setup go - name: Setup go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.24.0" go-version: "^1.23.2"
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |

View File

@@ -61,16 +61,7 @@ jobs:
- name: Install go - name: Install go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.24.0" go-version: "^1.23.2"
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
@@ -116,16 +107,7 @@ jobs:
- name: Install go - name: Install go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.24.0" go-version: "^1.23.2"
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
@@ -165,15 +147,6 @@ jobs:
with: with:
ref: nightly ref: nightly
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -232,15 +205,6 @@ jobs:
with: with:
ref: nightly ref: nightly
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -299,15 +263,6 @@ jobs:
with: with:
ref: nightly ref: nightly
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -366,15 +321,6 @@ jobs:
with: with:
ref: nightly ref: nightly
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5

View File

@@ -39,16 +39,7 @@ jobs:
- name: Install go - name: Install go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.24.0" go-version: "^1.23.2"
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
@@ -91,16 +82,7 @@ jobs:
- name: Install go - name: Install go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "^1.24.0" go-version: "^1.23.2"
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
@@ -137,15 +119,6 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -201,15 +174,6 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -265,15 +229,6 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -329,15 +284,6 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Initialize submodules
run: |
git submodule init
git submodule update
- name: Apply patches
run: |
git apply --directory paerser/ patches/nested_maps.diff
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5

4
.gitmodules vendored
View File

@@ -1,4 +0,0 @@
[submodule "paerser"]
path = paerser
url = https://github.com/traefik/paerser
ignore = all

View File

@@ -5,7 +5,7 @@ Contributing is relatively easy, you just need to follow the steps below and you
## Requirements ## Requirements
- Bun - Bun
- Golang 1.24.0+ - Golang v1.23.2 and above
- Git - Git
- Docker - Docker
@@ -18,21 +18,12 @@ git clone https://github.com/steveiliop56/tinyauth
cd tinyauth cd tinyauth
``` ```
## Initialize submodules
The project uses Git submodules for some dependencies, so you need to initialize them with:
```sh
git submodule init
git submodule update
```
## Install requirements ## Install requirements
Although you will not need the requirements in your machine since the development will happen in Docker, I still recommend to install them because this way you will not have import errors. To install the Go requirements run: Although you will not need the requirements in your machine since the development will happen in docker, I still recommend to install them because this way you will not have import errors. To install the go requirements run:
```sh ```sh
go mod download go mod tidy
``` ```
You also need to download the frontend dependencies, this can be done like so: You also need to download the frontend dependencies, this can be done like so:
@@ -42,21 +33,13 @@ cd frontend/
bun install bun install
``` ```
## Apply patches
Some of the dependencies need to be patched in order to work correctly with the project, you can apply the patches by running:
```sh
git apply --directory paerser/ patches/nested_maps.diff
```
## Create your `.env` file ## Create your `.env` file
In order to configure the app you need to create an environment file, this can be done by copying the `.env.example` file to `.env` and modifying the environment variables to suit your needs. In order to configure the app you need to create an environment file, this can be done by copying the `.env.example` file to `.env` and modifying the environment variables to suit your needs.
## Developing ## Developing
I have designed the development workflow to be entirely in Docker, this is because it will directly work with Traefik and you will not need to do any building in your host machine. The recommended development setup is to have a subdomain pointing to your machine like this: I have designed the development workflow to be entirely in docker, this is because it will directly work with traefik and you will not need to do any building in your host machine. The recommended development setup is to have a subdomain pointing to your machine like this:
``` ```
*.dev.example.com -> 127.0.0.1 *.dev.example.com -> 127.0.0.1
@@ -66,7 +49,7 @@ dev.example.com -> 127.0.0.1
> [!TIP] > [!TIP]
> You can use [sslip.io](https://sslip.io) as a domain if you don't have one to develop with. > You can use [sslip.io](https://sslip.io) as a domain if you don't have one to develop with.
Then you can just make sure the domains are correct in the development Docker compose file and run: Then you can just make sure the domains are correct in the development docker compose file and run:
```sh ```sh
docker compose -f docker-compose.dev.yml up --build docker compose -f docker-compose.dev.yml up --build

View File

@@ -28,8 +28,6 @@ ARG BUILD_TIMESTAMP
WORKDIR /tinyauth WORKDIR /tinyauth
COPY ./paerser ./paerser
COPY go.mod ./ COPY go.mod ./
COPY go.sum ./ COPY go.sum ./
@@ -40,7 +38,7 @@ COPY ./internal ./internal
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
RUN CGO_ENABLED=0 go build -ldflags "-s -w -X tinyauth/internal/config.Version=${VERSION} -X tinyauth/internal/config.CommitHash=${COMMIT_HASH} -X tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth RUN CGO_ENABLED=0 go build -ldflags "-s -w -X tinyauth/internal/config.Version=${VERSION} -X tinyauth/internal/config.CommitHash=${COMMIT_HASH} -X tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth
# Runner # Runner
FROM alpine:3.23 AS runner FROM alpine:3.23 AS runner
@@ -64,4 +62,4 @@ ENV PATH=$PATH:/tinyauth
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 CMD ["tinyauth", "healthcheck"] HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 CMD ["tinyauth", "healthcheck"]
ENTRYPOINT ["tinyauth"] ENTRYPOINT ["tinyauth"]

View File

@@ -2,8 +2,6 @@ FROM golang:1.25-alpine3.21
WORKDIR /tinyauth WORKDIR /tinyauth
COPY ./paerser ./paerser
COPY go.mod ./ COPY go.mod ./
COPY go.sum ./ COPY go.sum ./
@@ -22,4 +20,4 @@ ENV TINYAUTH_DATABASEPATH=/data/tinyauth.db
ENV TINYAUTH_RESOURCESDIR=/data/resources ENV TINYAUTH_RESOURCESDIR=/data/resources
ENTRYPOINT ["air", "-c", "air.toml"] ENTRYPOINT ["air", "-c", "air.toml"]

View File

@@ -28,8 +28,6 @@ ARG BUILD_TIMESTAMP
WORKDIR /tinyauth WORKDIR /tinyauth
COPY ./paerser ./paerser
COPY go.mod ./ COPY go.mod ./
COPY go.sum ./ COPY go.sum ./
@@ -42,7 +40,7 @@ COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
RUN mkdir -p data RUN mkdir -p data
RUN CGO_ENABLED=0 go build -ldflags "-s -w -X tinyauth/internal/config.Version=${VERSION} -X tinyauth/internal/config.CommitHash=${COMMIT_HASH} -X tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth RUN CGO_ENABLED=0 go build -ldflags "-s -w -X tinyauth/internal/config.Version=${VERSION} -X tinyauth/internal/config.CommitHash=${COMMIT_HASH} -X tinyauth/internal/config.BuildTimestamp=${BUILD_TIMESTAMP}" ./cmd/tinyauth
# Runner # Runner
FROM gcr.io/distroless/static-debian12:latest AS runner FROM gcr.io/distroless/static-debian12:latest AS runner
@@ -67,4 +65,4 @@ ENV PATH=$PATH:/tinyauth
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 CMD ["tinyauth", "healthcheck"] HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 CMD ["tinyauth", "healthcheck"]
ENTRYPOINT ["tinyauth"] ENTRYPOINT ["tinyauth"]

View File

@@ -34,10 +34,6 @@ func NewTinyauthCmdConfiguration() *config.Config {
ForgotPasswordMessage: "You can change your password by changing the configuration.", ForgotPasswordMessage: "You can change your password by changing the configuration.",
BackgroundImage: "/background.jpg", BackgroundImage: "/background.jpg",
}, },
Ldap: config.LdapConfig{
Insecure: false,
SearchFilter: "(uid=%s)",
},
Experimental: config.ExperimentalConfig{ Experimental: config.ExperimentalConfig{
ConfigFile: "", ConfigFile: "",
}, },

View File

@@ -42,6 +42,7 @@ services:
volumes: volumes:
- ./internal:/tinyauth/internal - ./internal:/tinyauth/internal
- ./cmd:/tinyauth/cmd - ./cmd:/tinyauth/cmd
- ./main.go:/tinyauth/main.go
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- ./data:/data - ./data:/data
ports: ports:

2
go.mod
View File

@@ -4,8 +4,6 @@ go 1.24.0
toolchain go1.24.3 toolchain go1.24.3
replace github.com/traefik/paerser v0.2.2 => ./paerser
require ( require (
github.com/cenkalti/backoff/v5 v5.0.3 github.com/cenkalti/backoff/v5 v5.0.3
github.com/charmbracelet/huh v0.8.0 github.com/charmbracelet/huh v0.8.0

2
go.sum
View File

@@ -271,6 +271,8 @@ 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.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 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 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= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=

View File

@@ -57,7 +57,7 @@ func (app *BootstrapApp) initServices() (Services, error) {
services.dockerService = dockerService services.dockerService = dockerService
accessControlsService := service.NewAccessControlsService(dockerService, app.config.Apps) accessControlsService := service.NewAccessControlsService(dockerService)
err = accessControlsService.Init() err = accessControlsService.Init()

View File

@@ -25,7 +25,6 @@ type Config struct {
LogJSON bool `description:"Enable JSON formatted logs." yaml:"logJSON"` LogJSON bool `description:"Enable JSON formatted logs." yaml:"logJSON"`
Server ServerConfig `description:"Server configuration." yaml:"server"` Server ServerConfig `description:"Server configuration." yaml:"server"`
Auth AuthConfig `description:"Authentication configuration." yaml:"auth"` Auth AuthConfig `description:"Authentication configuration." yaml:"auth"`
Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"`
OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"` OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"`
UI UIConfig `description:"UI customization." yaml:"ui"` UI UIConfig `description:"UI customization." yaml:"ui"`
Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"` Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"`
@@ -157,55 +156,61 @@ type RedirectQuery struct {
RedirectURI string `url:"redirect_uri"` RedirectURI string `url:"redirect_uri"`
} }
// ACLs // Labels
type Apps struct { type Apps struct {
Apps map[string]App `description:"App ACLs configuration." yaml:"apps"` Apps map[string]App
} }
type App struct { type App struct {
Config AppConfig `description:"App configuration." yaml:"config"` Config AppConfig
Users AppUsers `description:"User access configuration." yaml:"users"` Users AppUsers
OAuth AppOAuth `description:"OAuth access configuration." yaml:"oauth"` OAuth AppOAuth
IP AppIP `description:"IP access configuration." yaml:"ip"` IP AppIP
Response AppResponse `description:"Response customization." yaml:"response"` Response AppResponse
Path AppPath `description:"Path access configuration." yaml:"path"` Path AppPath
} }
type AppConfig struct { type AppConfig struct {
Domain string `description:"The domain of the app." yaml:"domain"` Domain string
} }
type AppUsers struct { type AppUsers struct {
Allow string `description:"Comma-separated list of allowed users." yaml:"allow"` Allow string
Block string `description:"Comma-separated list of blocked users." yaml:"block"` Block string
} }
type AppOAuth struct { type AppOAuth struct {
Whitelist string `description:"Comma-separated list of allowed OAuth groups." yaml:"whitelist"` Whitelist string
Groups string `description:"Comma-separated list of required OAuth groups." yaml:"groups"` Groups string
} }
type AppIP struct { type AppIP struct {
Allow []string `description:"List of allowed IPs or CIDR ranges." yaml:"allow"` Allow []string
Block []string `description:"List of blocked IPs or CIDR ranges." yaml:"block"` Block []string
Bypass []string `description:"List of IPs or CIDR ranges that bypass authentication." yaml:"bypass"` Bypass []string
} }
type AppResponse struct { type AppResponse struct {
Headers []string `description:"Custom headers to add to the response." yaml:"headers"` Headers []string
BasicAuth AppBasicAuth `description:"Basic authentication for the app." yaml:"basicAuth"` BasicAuth AppBasicAuth
} }
type AppBasicAuth struct { type AppBasicAuth struct {
Username string `description:"Basic auth username." yaml:"username"` Username string
Password string `description:"Basic auth password." yaml:"password"` Password string
PasswordFile string `description:"Path to the file containing the basic auth password." yaml:"passwordFile"` PasswordFile string
} }
type AppPath struct { type AppPath struct {
Allow string `description:"Comma-separated list of allowed paths." yaml:"allow"` Allow string
Block string `description:"Comma-separated list of blocked paths." yaml:"block"` Block string
}
// Flags
type Providers struct {
Providers map[string]OAuthServiceConfig
} }
// API server // API server

View File

@@ -41,7 +41,7 @@ func setupProxyController(t *testing.T, middlewares *[]gin.HandlerFunc) (*gin.En
assert.NilError(t, dockerService.Init()) assert.NilError(t, dockerService.Init())
// Access controls // Access controls
accessControlsService := service.NewAccessControlsService(dockerService, map[string]config.App{}) accessControlsService := service.NewAccessControlsService(dockerService)
assert.NilError(t, accessControlsService.Init()) assert.NilError(t, accessControlsService.Init())

View File

@@ -1,54 +1,122 @@
package service package service
import ( import (
"errors"
"strings"
"github.com/rs/zerolog/log"
"github.com/steveiliop56/tinyauth/internal/config" "github.com/steveiliop56/tinyauth/internal/config"
) )
/*
Environment variable/flag based ACLs are disabled until v5 due to a technical challenge
with the current parsing logic.
The current parser works for simple OAuth provider configs like:
- PROVIDERS_MY_AMAZING_PROVIDER_CLIENT_ID
However, it breaks down when handling nested structs required for ACLs. The custom parsing
solution that worked for v4 OAuth providers is incompatible with the ACL parsing logic,
making the codebase unmaintainable and fragile.
A solution is being considered for v5 that would standardize the format to something like:
- TINYAUTH_PROVIDERS_GOOGLE_CLIENTSECRET
- TINYAUTH_APPS_MYAPP_CONFIG_DOMAIN
This would allow the Traefik parser to handle everything consistently, but requires a
config migration. Until this is resolved, environment-based ACLs are disabled and only
Docker label-based ACLs are supported.
See: https://discord.com/channels/1337450123600465984/1337459086270271538/1434986689935179838 for more information
*/
type AccessControlsService struct { type AccessControlsService struct {
docker *DockerService docker *DockerService
static map[string]config.App // envACLs config.Apps
} }
func NewAccessControlsService(docker *DockerService, static map[string]config.App) *AccessControlsService { func NewAccessControlsService(docker *DockerService) *AccessControlsService {
return &AccessControlsService{ return &AccessControlsService{
docker: docker, docker: docker,
static: static,
} }
} }
func (acls *AccessControlsService) Init() error { func (acls *AccessControlsService) Init() error {
return nil // No initialization needed // acls.envACLs = config.Apps{}
// env := os.Environ()
// appEnvVars := []string{}
// for _, e := range env {
// if strings.HasPrefix(e, "TINYAUTH_APPS_") {
// appEnvVars = append(appEnvVars, e)
// }
// }
// err := acls.loadEnvACLs(appEnvVars)
// if err != nil {
// return err
// }
// return nil
return nil
} }
func (acls *AccessControlsService) lookupStaticACLs(domain string) (config.App, error) { // func (acls *AccessControlsService) loadEnvACLs(appEnvVars []string) error {
for app, config := range acls.static { // if len(appEnvVars) == 0 {
if config.Config.Domain == domain { // return nil
log.Debug().Str("name", app).Msg("Found matching container by domain") // }
return config, nil
}
if strings.SplitN(domain, ".", 2)[0] == app { // envAcls := map[string]string{}
log.Debug().Str("name", app).Msg("Found matching container by app name")
return config, nil
}
}
return config.App{}, errors.New("no results")
}
func (acls *AccessControlsService) GetAccessControls(domain string) (config.App, error) { // for _, e := range appEnvVars {
// First check in the static config // parts := strings.SplitN(e, "=", 2)
app, err := acls.lookupStaticACLs(domain) // if len(parts) != 2 {
// continue
// }
if err == nil { // key := parts[0]
log.Debug().Msg("Using ACls from static configuration") // key = strings.ToLower(key)
return app, nil // key = strings.ReplaceAll(key, "_", ".")
} // value := parts[1]
// envAcls[key] = value
// }
// apps, err := decoders.DecodeLabels(envAcls)
// if err != nil {
// return err
// }
// acls.envACLs = apps
// return nil
// }
// func (acls *AccessControlsService) lookupEnvACLs(appDomain string) *config.App {
// if len(acls.envACLs.Apps) == 0 {
// return nil
// }
// for appName, appACLs := range acls.envACLs.Apps {
// if appACLs.Config.Domain == appDomain {
// return &appACLs
// }
// if strings.SplitN(appDomain, ".", 2)[0] == appName {
// return &appACLs
// }
// }
// return nil
// }
func (acls *AccessControlsService) GetAccessControls(appDomain string) (config.App, error) {
// First check environment variables
// envACLs := acls.lookupEnvACLs(appDomain)
// if envACLs != nil {
// log.Debug().Str("domain", appDomain).Msg("Found matching access controls in environment variables")
// return *envACLs, nil
// }
// Fallback to Docker labels // Fallback to Docker labels
log.Debug().Msg("Falling back to Docker labels for ACLs") return acls.docker.GetLabels(appDomain)
return acls.docker.GetLabels(domain)
} }

Submodule paerser deleted from 7e1b633ba9

View File

@@ -1,95 +0,0 @@
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) {