@@ -10,7 +10,10 @@ import (
10
10
"testing"
11
11
12
12
"github.com/grafana/grafana/pkg/api/dtos"
13
+ "github.com/grafana/grafana/pkg/bus"
14
+ "github.com/grafana/grafana/pkg/components/simplejson"
13
15
"github.com/grafana/grafana/pkg/infra/log"
16
+ "github.com/grafana/grafana/pkg/login"
14
17
"github.com/grafana/grafana/pkg/models"
15
18
"github.com/grafana/grafana/pkg/services/auth"
16
19
"github.com/grafana/grafana/pkg/setting"
@@ -53,6 +56,22 @@ func getBody(resp *httptest.ResponseRecorder) (string, error) {
53
56
return string (responseData ), nil
54
57
}
55
58
59
+ type FakeLogger struct {
60
+ log.Logger
61
+ }
62
+
63
+ func (stub * FakeLogger ) Info (testMessage string , ctx ... interface {}) {
64
+ }
65
+
66
+ type redirectCase struct {
67
+ desc string
68
+ url string
69
+ status int
70
+ err error
71
+ appURL string
72
+ appSubURL string
73
+ }
74
+
56
75
func TestLoginErrorCookieApiEndpoint (t * testing.T ) {
57
76
mockSetIndexViewData ()
58
77
defer resetSetIndexViewData ()
@@ -100,10 +119,201 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
100
119
assert .Equal (t , sc .resp .Code , 200 )
101
120
102
121
responseString , err := getBody (sc .resp )
103
- assert .Nil (t , err )
122
+ assert .NoError (t , err )
104
123
assert .True (t , strings .Contains (responseString , oauthError .Error ()))
105
124
}
106
125
126
+ func TestLoginViewRedirect (t * testing.T ) {
127
+ mockSetIndexViewData ()
128
+ defer resetSetIndexViewData ()
129
+
130
+ mockViewIndex ()
131
+ defer resetViewIndex ()
132
+ sc := setupScenarioContext ("/login" )
133
+ hs := & HTTPServer {
134
+ Cfg : setting .NewCfg (),
135
+ License : models.OSSLicensingService {},
136
+ }
137
+
138
+ sc .defaultHandler = Wrap (func (w http.ResponseWriter , c * models.ReqContext ) {
139
+ c .IsSignedIn = true
140
+ c .SignedInUser = & models.SignedInUser {
141
+ UserId : 10 ,
142
+ }
143
+ hs .LoginView (c )
144
+ })
145
+
146
+ setting .OAuthService = & setting.OAuther {}
147
+ setting .OAuthService .OAuthInfos = make (map [string ]* setting.OAuthInfo )
148
+
149
+ redirectCases := []redirectCase {
150
+ {
151
+ desc : "grafana relative url without subpath" ,
152
+ url : "/profile" ,
153
+ appURL : "http://localhost:3000" ,
154
+ status : 302 ,
155
+ },
156
+ {
157
+ desc : "grafana relative url with subpath" ,
158
+ url : "/grafana/profile" ,
159
+ appURL : "http://localhost:3000" ,
160
+ appSubURL : "grafana" ,
161
+ status : 302 ,
162
+ },
163
+ {
164
+ desc : "relative url with missing subpath" ,
165
+ url : "/profile" ,
166
+ appURL : "http://localhost:3000" ,
167
+ appSubURL : "grafana" ,
168
+ status : 200 ,
169
+ err : login .ErrInvalidRedirectTo ,
170
+ },
171
+ {
172
+ desc : "grafana absolute url" ,
173
+ url : "http://localhost:3000/profile" ,
174
+ appURL : "http://localhost:3000" ,
175
+ status : 200 ,
176
+ err : login .ErrAbsoluteRedirectTo ,
177
+ },
178
+ {
179
+ desc : "non grafana absolute url" ,
180
+ url : "http://example.com" ,
181
+ appURL : "http://localhost:3000" ,
182
+ status : 200 ,
183
+ err : login .ErrAbsoluteRedirectTo ,
184
+ },
185
+ {
186
+ desc : "invalid url" ,
187
+ url : ":foo" ,
188
+ appURL : "http://localhost:3000" ,
189
+ status : 200 ,
190
+ err : login .ErrInvalidRedirectTo ,
191
+ },
192
+ }
193
+
194
+ for _ , c := range redirectCases {
195
+ setting .AppUrl = c .appURL
196
+ setting .AppSubUrl = c .appSubURL
197
+ t .Run (c .desc , func (t * testing.T ) {
198
+ cookie := http.Cookie {
199
+ Name : "redirect_to" ,
200
+ MaxAge : 60 ,
201
+ Value : c .url ,
202
+ HttpOnly : true ,
203
+ Path : setting .AppSubUrl + "/" ,
204
+ Secure : hs .Cfg .CookieSecure ,
205
+ SameSite : hs .Cfg .CookieSameSite ,
206
+ }
207
+ sc .m .Get (sc .url , sc .defaultHandler )
208
+ sc .fakeReqNoAssertionsWithCookie ("GET" , sc .url , cookie ).exec ()
209
+ assert .Equal (t , c .status , sc .resp .Code )
210
+ if c .status == 302 {
211
+ location , ok := sc .resp .Header ()["Location" ]
212
+ assert .True (t , ok )
213
+ assert .Equal (t , location [0 ], c .url )
214
+ }
215
+
216
+ responseString , err := getBody (sc .resp )
217
+ assert .NoError (t , err )
218
+ if c .err != nil {
219
+ assert .True (t , strings .Contains (responseString , c .err .Error ()))
220
+ }
221
+ })
222
+ }
223
+ }
224
+
225
+ func TestLoginPostRedirect (t * testing.T ) {
226
+ mockSetIndexViewData ()
227
+ defer resetSetIndexViewData ()
228
+
229
+ mockViewIndex ()
230
+ defer resetViewIndex ()
231
+ sc := setupScenarioContext ("/login" )
232
+ hs := & HTTPServer {
233
+ log : & FakeLogger {},
234
+ Cfg : setting .NewCfg (),
235
+ License : models.OSSLicensingService {},
236
+ AuthTokenService : auth .NewFakeUserAuthTokenService (),
237
+ }
238
+
239
+ sc .defaultHandler = Wrap (func (w http.ResponseWriter , c * models.ReqContext ) Response {
240
+ cmd := dtos.LoginCommand {
241
+ User : "admin" ,
242
+ Password : "admin" ,
243
+ }
244
+ return hs .LoginPost (c , cmd )
245
+ })
246
+
247
+ bus .AddHandler ("grafana-auth" , func (query * models.LoginUserQuery ) error {
248
+ query .User = & models.User {
249
+ Id : 42 ,
250
+ Email : "" ,
251
+ }
252
+ return nil
253
+ })
254
+
255
+ redirectCases := []redirectCase {
256
+ {
257
+ desc : "grafana relative url without subpath" ,
258
+ url : "/profile" ,
259
+ appURL : "https://localhost:3000" ,
260
+ },
261
+ {
262
+ desc : "grafana relative url with subpath" ,
263
+ url : "/grafana/profile" ,
264
+ appURL : "https://localhost:3000" ,
265
+ appSubURL : "grafana" ,
266
+ },
267
+ {
268
+ desc : "relative url with missing subpath" ,
269
+ url : "/profile" ,
270
+ appURL : "https://localhost:3000" ,
271
+ appSubURL : "grafana" ,
272
+ err : login .ErrInvalidRedirectTo ,
273
+ },
274
+ {
275
+ desc : "grafana absolute url" ,
276
+ url : "http://localhost:3000/profile" ,
277
+ appURL : "http://localhost:3000" ,
278
+ err : login .ErrAbsoluteRedirectTo ,
279
+ },
280
+ {
281
+ desc : "non grafana absolute url" ,
282
+ url : "http://example.com" ,
283
+ appURL : "https://localhost:3000" ,
284
+ err : login .ErrAbsoluteRedirectTo ,
285
+ },
286
+ }
287
+
288
+ for _ , c := range redirectCases {
289
+ setting .AppUrl = c .appURL
290
+ setting .AppSubUrl = c .appSubURL
291
+ t .Run (c .desc , func (t * testing.T ) {
292
+ cookie := http.Cookie {
293
+ Name : "redirect_to" ,
294
+ MaxAge : 60 ,
295
+ Value : c .url ,
296
+ HttpOnly : true ,
297
+ Path : setting .AppSubUrl + "/" ,
298
+ Secure : hs .Cfg .CookieSecure ,
299
+ SameSite : hs .Cfg .CookieSameSite ,
300
+ }
301
+ sc .m .Post (sc .url , sc .defaultHandler )
302
+ sc .fakeReqNoAssertionsWithCookie ("POST" , sc .url , cookie ).exec ()
303
+ assert .Equal (t , sc .resp .Code , 200 )
304
+
305
+ respJSON , err := simplejson .NewJson (sc .resp .Body .Bytes ())
306
+ assert .NoError (t , err )
307
+ redirectURL := respJSON .Get ("redirectUrl" ).MustString ()
308
+ if c .err != nil {
309
+ assert .Equal (t , "" , redirectURL )
310
+ } else {
311
+ assert .Equal (t , c .url , redirectURL )
312
+ }
313
+ })
314
+ }
315
+ }
316
+
107
317
func TestLoginOAuthRedirect (t * testing.T ) {
108
318
mockSetIndexViewData ()
109
319
defer resetSetIndexViewData ()
0 commit comments