Skip to content

Commit 1fd0673

Browse files
committed
feat: API auth
1 parent ecb9c92 commit 1fd0673

File tree

8 files changed

+254
-13
lines changed

8 files changed

+254
-13
lines changed

go.mod

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module github.com/mirkobrombin/goup
22

3-
go 1.23.0
3+
go 1.24.0
44

5-
toolchain go1.23.7
5+
toolchain go1.24.4
66

77
require (
88
github.com/armon/go-radix v1.0.0
@@ -40,13 +40,14 @@ require (
4040
github.com/valyala/bytebufferpool v1.0.0 // indirect
4141
github.com/yusufpapurcu/wmi v1.2.4 // indirect
4242
go.uber.org/mock v0.5.0 // indirect
43-
golang.org/x/crypto v0.36.0 // indirect
43+
golang.org/x/crypto v0.47.0 // indirect
4444
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
45-
golang.org/x/mod v0.18.0 // indirect
46-
golang.org/x/net v0.38.0 // indirect
47-
golang.org/x/sync v0.12.0 // indirect
48-
golang.org/x/sys v0.31.0 // indirect
49-
golang.org/x/term v0.30.0 // indirect
50-
golang.org/x/text v0.23.0 // indirect
51-
golang.org/x/tools v0.22.0 // indirect
45+
golang.org/x/mod v0.31.0 // indirect
46+
golang.org/x/net v0.48.0 // indirect
47+
golang.org/x/sync v0.19.0 // indirect
48+
golang.org/x/sys v0.40.0 // indirect
49+
golang.org/x/term v0.39.0 // indirect
50+
golang.org/x/text v0.33.0 // indirect
51+
golang.org/x/tools v0.40.0 // indirect
52+
golang.org/x/tools/godoc v0.1.0-deprecated // indirect
5253
)

go.sum

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,17 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
109109
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
110110
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
111111
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
112+
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
113+
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
112114
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
113115
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
114116
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
115117
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
116118
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
117119
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
118120
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
121+
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
122+
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
119123
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
120124
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
121125
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -125,12 +129,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
125129
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
126130
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
127131
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
132+
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
133+
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
128134
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
129135
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
130136
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
131137
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
132138
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
133139
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
140+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
134141
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
135142
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
136143
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -147,19 +154,25 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
147154
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
148155
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
149156
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
157+
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
158+
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
150159
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
151160
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
152161
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
153162
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
154163
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
155164
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
165+
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
166+
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
156167
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
157168
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
158169
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
159170
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
160171
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
161172
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
162173
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
174+
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
175+
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
163176
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
164177
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
165178
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -170,6 +183,10 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
170183
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
171184
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
172185
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
186+
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
187+
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
188+
golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk=
189+
golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg=
173190
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
174191
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
175192
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

internal/api/server.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66

77
"github.com/mirkobrombin/goup/internal/config"
8+
"github.com/mirkobrombin/goup/internal/middleware"
89
)
910

1011
// StartAPIServer starts the GoUp API server.
@@ -19,7 +20,10 @@ func StartAPIServer() {
1920
go func() {
2021
fmt.Printf("[API] Listening on :%d\n", port)
2122

22-
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), router); err != nil {
23+
var handler http.Handler = router
24+
handler = middleware.TokenAuthMiddleware(handler)
25+
26+
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), handler); err != nil {
2327
fmt.Printf("[API] Error: %v\n", err)
2428
}
2529
}()

internal/config/global_config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ type SafeGuardConfig struct {
1313
CheckInterval string `json:"check_interval"` // Interval to check memory (e.g. "10s")
1414
}
1515

16+
// AccountConfig defines the authentication credentials.
17+
type AccountConfig struct {
18+
Username string `json:"username"`
19+
PasswordHash string `json:"password_hash"` // Bcrypt hash
20+
APIToken string `json:"api_token"`
21+
}
22+
1623
// GlobalConfig contains the global settings for GoUP.
1724
type GlobalConfig struct {
25+
Account AccountConfig `json:"account"`
1826
EnableAPI bool `json:"enable_api"`
1927
APIPort int `json:"api_port"`
2028
DashboardPort int `json:"dashboard_port"`

internal/dashboard/handler.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,15 @@ func apiProxy() http.Handler {
2121
if err != nil {
2222
panic(err)
2323
}
24-
return httputil.NewSingleHostReverseProxy(target)
24+
proxy := httputil.NewSingleHostReverseProxy(target)
25+
originalDirector := proxy.Director
26+
proxy.Director = func(req *http.Request) {
27+
originalDirector(req)
28+
if config.GlobalConf != nil && config.GlobalConf.Account.APIToken != "" {
29+
req.Header.Set("X-API-Token", config.GlobalConf.Account.APIToken)
30+
}
31+
}
32+
return proxy
2533
}
2634

2735
// spaFileServer returns an HTTP handler that serves static files from the

internal/dashboard/server.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66

77
"github.com/mirkobrombin/goup/internal/config"
8+
"github.com/mirkobrombin/goup/internal/middleware"
89
)
910

1011
// StartDashboardServer starts a dedicated server for the dashboard.
@@ -15,7 +16,9 @@ func StartDashboardServer() {
1516
port := config.GlobalConf.DashboardPort
1617
go func() {
1718
fmt.Printf("[Dashboard] Listening on :%d\n", port)
18-
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), Handler()); err != nil {
19+
handler := Handler()
20+
handler = middleware.BasicAuthMiddleware(handler)
21+
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), handler); err != nil {
1922
fmt.Printf("[Dashboard] Error: %v\n", err)
2023
}
2124
}()

internal/middleware/auth.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package middleware
2+
3+
import (
4+
"crypto/subtle"
5+
"net/http"
6+
"strings"
7+
8+
"github.com/mirkobrombin/goup/internal/config"
9+
"golang.org/x/crypto/bcrypt"
10+
)
11+
12+
// BasicAuthMiddleware enforces Basic Authentication if credentials are configured.
13+
func BasicAuthMiddleware(next http.Handler) http.Handler {
14+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15+
// If auth is not configured, skip
16+
if config.GlobalConf == nil || config.GlobalConf.Account.Username == "" || config.GlobalConf.Account.PasswordHash == "" {
17+
next.ServeHTTP(w, r)
18+
return
19+
}
20+
21+
user, pass, ok := r.BasicAuth()
22+
if !ok || subtle.ConstantTimeCompare([]byte(user), []byte(config.GlobalConf.Account.Username)) != 1 {
23+
w.Header().Set("WWW-Authenticate", `Basic realm="GoUp Dashboard"`)
24+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
25+
return
26+
}
27+
28+
err := bcrypt.CompareHashAndPassword([]byte(config.GlobalConf.Account.PasswordHash), []byte(pass))
29+
if err != nil {
30+
w.Header().Set("WWW-Authenticate", `Basic realm="GoUp Dashboard"`)
31+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
32+
return
33+
}
34+
35+
next.ServeHTTP(w, r)
36+
})
37+
}
38+
39+
// TokenAuthMiddleware enforces Token Authentication if a token is configured.
40+
func TokenAuthMiddleware(next http.Handler) http.Handler {
41+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
42+
// If token is not configured, skip
43+
if config.GlobalConf == nil || config.GlobalConf.Account.APIToken == "" {
44+
next.ServeHTTP(w, r)
45+
return
46+
}
47+
48+
token := r.Header.Get("X-API-Token")
49+
if token == "" {
50+
authHeader := r.Header.Get("Authorization")
51+
if strings.HasPrefix(authHeader, "Bearer ") {
52+
token = strings.TrimPrefix(authHeader, "Bearer ")
53+
}
54+
}
55+
56+
if subtle.ConstantTimeCompare([]byte(token), []byte(config.GlobalConf.Account.APIToken)) != 1 {
57+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
58+
return
59+
}
60+
61+
next.ServeHTTP(w, r)
62+
})
63+
}

internal/middleware/auth_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package middleware
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
8+
"github.com/mirkobrombin/goup/internal/config"
9+
"golang.org/x/crypto/bcrypt"
10+
)
11+
12+
func TestBasicAuthMiddleware(t *testing.T) {
13+
// Setup
14+
password := "secret"
15+
hash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
16+
config.GlobalConf = &config.GlobalConfig{
17+
Account: config.AccountConfig{
18+
Username: "admin",
19+
PasswordHash: string(hash),
20+
},
21+
}
22+
23+
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24+
w.WriteHeader(http.StatusOK)
25+
})
26+
27+
middleware := BasicAuthMiddleware(nextHandler)
28+
29+
tests := []struct {
30+
name string
31+
user string
32+
pass string
33+
expectedStatus int
34+
}{
35+
{"ValidCredentials", "admin", "secret", http.StatusOK},
36+
{"InvalidUsername", "wrong", "secret", http.StatusUnauthorized},
37+
{"InvalidPassword", "admin", "wrong", http.StatusUnauthorized},
38+
{"NoCredentials", "", "", http.StatusUnauthorized},
39+
}
40+
41+
for _, tt := range tests {
42+
t.Run(tt.name, func(t *testing.T) {
43+
req := httptest.NewRequest("GET", "/", nil)
44+
if tt.user != "" {
45+
req.SetBasicAuth(tt.user, tt.pass)
46+
}
47+
rec := httptest.NewRecorder()
48+
middleware.ServeHTTP(rec, req)
49+
50+
if rec.Code != tt.expectedStatus {
51+
t.Errorf("expected status %v, got %v", tt.expectedStatus, rec.Code)
52+
}
53+
})
54+
}
55+
}
56+
57+
func TestTokenAuthMiddleware(t *testing.T) {
58+
// Setup
59+
token := "my-secret-token"
60+
config.GlobalConf = &config.GlobalConfig{
61+
Account: config.AccountConfig{
62+
APIToken: token,
63+
},
64+
}
65+
66+
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
67+
w.WriteHeader(http.StatusOK)
68+
})
69+
70+
middleware := TokenAuthMiddleware(nextHandler)
71+
72+
tests := []struct {
73+
name string
74+
headers map[string]string
75+
expectedStatus int
76+
}{
77+
{"ValidTokenHeader", map[string]string{"X-API-Token": token}, http.StatusOK},
78+
{"ValidBearerToken", map[string]string{"Authorization": "Bearer " + token}, http.StatusOK},
79+
{"InvalidToken", map[string]string{"X-API-Token": "wrong"}, http.StatusUnauthorized},
80+
{"NoToken", map[string]string{}, http.StatusOK},
81+
}
82+
83+
tests = []struct {
84+
name string
85+
headers map[string]string
86+
expectedStatus int
87+
}{
88+
{"ValidTokenHeader", map[string]string{"X-API-Token": token}, http.StatusOK},
89+
{"ValidBearerToken", map[string]string{"Authorization": "Bearer " + token}, http.StatusOK},
90+
{"InvalidToken", map[string]string{"X-API-Token": "wrong"}, http.StatusUnauthorized},
91+
{"NoToken", map[string]string{}, http.StatusUnauthorized},
92+
}
93+
94+
for _, tt := range tests {
95+
t.Run(tt.name, func(t *testing.T) {
96+
req := httptest.NewRequest("GET", "/", nil)
97+
for k, v := range tt.headers {
98+
req.Header.Set(k, v)
99+
}
100+
rec := httptest.NewRecorder()
101+
middleware.ServeHTTP(rec, req)
102+
103+
if rec.Code != tt.expectedStatus {
104+
t.Errorf("expected status %v, got %v", tt.expectedStatus, rec.Code)
105+
}
106+
})
107+
}
108+
}
109+
110+
func TestAuthSkippedIfUnconfigured(t *testing.T) {
111+
// Setup: Empty config
112+
config.GlobalConf = &config.GlobalConfig{}
113+
114+
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
115+
w.WriteHeader(http.StatusOK)
116+
})
117+
118+
t.Run("BasicAuthSkipped", func(t *testing.T) {
119+
middleware := BasicAuthMiddleware(nextHandler)
120+
req := httptest.NewRequest("GET", "/", nil)
121+
rec := httptest.NewRecorder()
122+
middleware.ServeHTTP(rec, req)
123+
if rec.Code != http.StatusOK {
124+
t.Errorf("expected status %v, got %v", http.StatusOK, rec.Code)
125+
}
126+
})
127+
128+
t.Run("TokenAuthSkipped", func(t *testing.T) {
129+
middleware := TokenAuthMiddleware(nextHandler)
130+
req := httptest.NewRequest("GET", "/", nil)
131+
rec := httptest.NewRecorder()
132+
middleware.ServeHTTP(rec, req)
133+
if rec.Code != http.StatusOK {
134+
t.Errorf("expected status %v, got %v", http.StatusOK, rec.Code)
135+
}
136+
})
137+
}

0 commit comments

Comments
 (0)