@@ -2,13 +2,19 @@ package settings
22
33import (
44 "bytes"
5+ "encoding/json"
56 "net/http"
67 "net/http/httptest"
78 "testing"
89
10+ "github.com/0xJacky/Nginx-UI/internal/cache"
11+ "github.com/0xJacky/Nginx-UI/internal/middleware"
12+ internaluser "github.com/0xJacky/Nginx-UI/internal/user"
13+ "github.com/0xJacky/Nginx-UI/model"
914 appsettings "github.com/0xJacky/Nginx-UI/settings"
1015 "github.com/gin-gonic/gin"
1116 "github.com/stretchr/testify/assert"
17+ cSettings "github.com/uozi-tech/cosy/settings"
1218)
1319
1420func TestSaveSettingsRejectsNegativeLogrotateInterval (t * testing.T ) {
@@ -26,6 +32,163 @@ func TestSaveSettingsRejectsNegativeLogrotateInterval(t *testing.T) {
2632
2733 SaveSettings (c )
2834
29- assert .Equal (t , http .StatusBadRequest , w .Code )
30- assert .Contains (t , w .Body .String (), appsettings .InvalidLogrotateIntervalMessage )
35+ assert .Equal (t , http .StatusNotAcceptable , w .Code )
36+ assert .Contains (t , w .Body .String (), "\" interval\" :\" min\" " )
37+ }
38+
39+ func TestGetSettingsRedactsSensitiveFields (t * testing.T ) {
40+ gin .SetMode (gin .TestMode )
41+
42+ originalJWTSecret := cSettings .AppSettings .JwtSecret
43+ originalPageSize := cSettings .AppSettings .PageSize
44+ originalNodeSecret := appsettings .NodeSettings .Secret
45+ originalNodeName := appsettings .NodeSettings .Name
46+ originalOpenAIToken := appsettings .OpenAISettings .Token
47+ originalReloadCmd := appsettings .NginxSettings .ReloadCmd
48+ originalRestartCmd := appsettings .NginxSettings .RestartCmd
49+ defer func () {
50+ cSettings .AppSettings .JwtSecret = originalJWTSecret
51+ cSettings .AppSettings .PageSize = originalPageSize
52+ appsettings .NodeSettings .Secret = originalNodeSecret
53+ appsettings .NodeSettings .Name = originalNodeName
54+ appsettings .OpenAISettings .Token = originalOpenAIToken
55+ appsettings .NginxSettings .ReloadCmd = originalReloadCmd
56+ appsettings .NginxSettings .RestartCmd = originalRestartCmd
57+ }()
58+
59+ cSettings .AppSettings .JwtSecret = "jwt-secret"
60+ cSettings .AppSettings .PageSize = 50
61+ appsettings .NodeSettings .Secret = "node-secret"
62+ appsettings .NodeSettings .Name = "local-node"
63+ appsettings .OpenAISettings .Token = "openai-secret"
64+ appsettings .NginxSettings .ReloadCmd = "nginx -s reload"
65+ appsettings .NginxSettings .RestartCmd = "nginx -s restart"
66+
67+ w := httptest .NewRecorder ()
68+ c , _ := gin .CreateTestContext (w )
69+ c .Request = httptest .NewRequest (http .MethodGet , "/api/settings" , nil )
70+
71+ GetSettings (c )
72+
73+ assert .Equal (t , http .StatusOK , w .Code )
74+
75+ var body map [string ]map [string ]any
76+ err := json .Unmarshal (w .Body .Bytes (), & body )
77+ assert .NoError (t , err )
78+ assert .Equal (t , redactedSensitiveValue , body ["app" ]["jwt_secret" ])
79+ assert .Equal (t , float64 (50 ), body ["app" ]["page_size" ])
80+ assert .Equal (t , redactedSensitiveValue , body ["node" ]["secret" ])
81+ assert .Equal (t , "local-node" , body ["node" ]["name" ])
82+ assert .Equal (t , redactedSensitiveValue , body ["openai" ]["token" ])
83+ }
84+
85+ func TestRestoreRedactedSensitiveSettings (t * testing.T ) {
86+ originalJWTSecret := cSettings .AppSettings .JwtSecret
87+ originalNodeSecret := appsettings .NodeSettings .Secret
88+ originalOpenAIToken := appsettings .OpenAISettings .Token
89+ defer func () {
90+ cSettings .AppSettings .JwtSecret = originalJWTSecret
91+ appsettings .NodeSettings .Secret = originalNodeSecret
92+ appsettings .OpenAISettings .Token = originalOpenAIToken
93+ }()
94+
95+ cSettings .AppSettings .JwtSecret = "jwt-secret"
96+ appsettings .NodeSettings .Secret = "node-secret"
97+ appsettings .OpenAISettings .Token = "openai-secret"
98+
99+ payload := saveSettingsPayload {}
100+ payload .App .JwtSecret = redactedSensitiveValue
101+ payload .Node .Secret = redactedSensitiveValue
102+ payload .Openai .Token = redactedSensitiveValue
103+
104+ restoreRedactedSensitiveSettings (& payload )
105+
106+ assert .Equal (t , "jwt-secret" , payload .App .JwtSecret )
107+ assert .Equal (t , "node-secret" , payload .Node .Secret )
108+ assert .Equal (t , "openai-secret" , payload .Openai .Token )
109+ }
110+
111+ func TestGetProtectedSetting (t * testing.T ) {
112+ gin .SetMode (gin .TestMode )
113+ cache .InitInMemoryCache ()
114+ defer cache .Shutdown ()
115+
116+ originalJWTSecret := cSettings .AppSettings .JwtSecret
117+ defer func () {
118+ cSettings .AppSettings .JwtSecret = originalJWTSecret
119+ }()
120+ cSettings .AppSettings .JwtSecret = "jwt-secret"
121+
122+ t .Run ("rejects missing secure session" , func (t * testing.T ) {
123+ r := gin .New ()
124+ r .GET ("/api/settings/protected" , func (c * gin.Context ) {
125+ c .Set ("user" , & model.User {
126+ Model : model.Model {ID : 1 },
127+ OTPSecret : []byte ("otp-enabled" ),
128+ })
129+ }, middleware .RequireSecureSession (), GetProtectedSetting )
130+
131+ req := httptest .NewRequest (http .MethodGet , "/api/settings/protected?path=app.jwt_secret" , nil )
132+ w := httptest .NewRecorder ()
133+ r .ServeHTTP (w , req )
134+
135+ assert .Equal (t , http .StatusUnauthorized , w .Code )
136+ })
137+
138+ t .Run ("rejects node secret authentication" , func (t * testing.T ) {
139+ r := gin .New ()
140+ r .GET ("/api/settings/protected" , func (c * gin.Context ) {
141+ c .Set ("user" , & model.User {
142+ Model : model.Model {ID : 1 },
143+ })
144+ c .Set ("Secret" , "node-secret" )
145+ }, middleware .RequireSecureSession (), GetProtectedSetting )
146+
147+ req := httptest .NewRequest (http .MethodGet , "/api/settings/protected?path=app.jwt_secret" , nil )
148+ w := httptest .NewRecorder ()
149+ r .ServeHTTP (w , req )
150+
151+ assert .Equal (t , http .StatusForbidden , w .Code )
152+ })
153+
154+ t .Run ("rejects invalid path" , func (t * testing.T ) {
155+ r := gin .New ()
156+ r .GET ("/api/settings/protected" , func (c * gin.Context ) {
157+ user := & model.User {
158+ Model : model.Model {ID : 2 },
159+ OTPSecret : []byte ("otp-enabled" ),
160+ }
161+ c .Set ("user" , user )
162+ }, middleware .RequireSecureSession (), GetProtectedSetting )
163+
164+ req := httptest .NewRequest (http .MethodGet , "/api/settings/protected?path=node.name" , nil )
165+ req .Header .Set ("X-Secure-Session-ID" , internaluser .SetSecureSessionID (2 ))
166+ w := httptest .NewRecorder ()
167+ r .ServeHTTP (w , req )
168+
169+ assert .Equal (t , http .StatusBadRequest , w .Code )
170+ })
171+
172+ t .Run ("returns protected value" , func (t * testing.T ) {
173+ r := gin .New ()
174+ r .GET ("/api/settings/protected" , func (c * gin.Context ) {
175+ user := & model.User {
176+ Model : model.Model {ID : 3 },
177+ OTPSecret : []byte ("otp-enabled" ),
178+ }
179+ c .Set ("user" , user )
180+ }, middleware .RequireSecureSession (), GetProtectedSetting )
181+
182+ req := httptest .NewRequest (http .MethodGet , "/api/settings/protected?path=app.jwt_secret" , nil )
183+ req .Header .Set ("X-Secure-Session-ID" , internaluser .SetSecureSessionID (3 ))
184+ w := httptest .NewRecorder ()
185+ r .ServeHTTP (w , req )
186+
187+ assert .Equal (t , http .StatusOK , w .Code )
188+
189+ var body map [string ]string
190+ err := json .Unmarshal (w .Body .Bytes (), & body )
191+ assert .NoError (t , err )
192+ assert .Equal (t , "jwt-secret" , body ["value" ])
193+ })
31194}
0 commit comments