@@ -10,12 +10,17 @@ import (
10
10
"github.com/stretchr/testify/assert"
11
11
"github.com/stretchr/testify/require"
12
12
13
+ "github.com/grafana/grafana/pkg/infra/log/logtest"
13
14
"github.com/grafana/grafana/pkg/infra/tracing"
15
+ "github.com/grafana/grafana/pkg/plugins"
16
+ "github.com/grafana/grafana/pkg/services/accesscontrol/actest"
14
17
"github.com/grafana/grafana/pkg/services/authn"
15
18
"github.com/grafana/grafana/pkg/services/authn/authntest"
16
19
"github.com/grafana/grafana/pkg/services/contexthandler"
17
20
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
18
21
"github.com/grafana/grafana/pkg/services/featuremgmt"
22
+ "github.com/grafana/grafana/pkg/services/org"
23
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
19
24
"github.com/grafana/grafana/pkg/setting"
20
25
"github.com/grafana/grafana/pkg/web"
21
26
)
@@ -150,6 +155,185 @@ func TestAuth_Middleware(t *testing.T) {
150
155
}
151
156
}
152
157
158
+ func TestRoleAppPluginAuth (t * testing.T ) {
159
+ t .Run ("Verify user's role when requesting app route which requires role" , func (t * testing.T ) {
160
+ appSubURL := setting .AppSubUrl
161
+ setting .AppSubUrl = "/grafana/"
162
+ t .Cleanup (func () {
163
+ setting .AppSubUrl = appSubURL
164
+ })
165
+
166
+ tcs := []struct {
167
+ roleRequired org.RoleType
168
+ role org.RoleType
169
+ expStatus int
170
+ expBody string
171
+ expLocation string
172
+ }{
173
+ {roleRequired : org .RoleViewer , role : org .RoleAdmin , expStatus : http .StatusOK , expBody : "" },
174
+ {roleRequired : org .RoleAdmin , role : org .RoleAdmin , expStatus : http .StatusOK , expBody : "" },
175
+ {roleRequired : org .RoleAdmin , role : org .RoleViewer , expStatus : http .StatusFound , expBody : "<a href=\" /grafana/\" >Found</a>.\n \n " , expLocation : "/grafana/" },
176
+ {roleRequired : "" , role : org .RoleViewer , expStatus : http .StatusOK , expBody : "" },
177
+ {roleRequired : org .RoleEditor , role : "" , expStatus : http .StatusFound , expBody : "<a href=\" /grafana/\" >Found</a>.\n \n " , expLocation : "/grafana/" },
178
+ }
179
+
180
+ const path = "/a/test-app/test"
181
+ for i , tc := range tcs {
182
+ t .Run (fmt .Sprintf ("testcase %d" , i ), func (t * testing.T ) {
183
+ ps := pluginstore .NewFakePluginStore (pluginstore.Plugin {
184
+ JSONData : plugins.JSONData {
185
+ ID : "test-app" ,
186
+ Includes : []* plugins.Includes {
187
+ {
188
+ Type : "page" ,
189
+ Role : tc .roleRequired ,
190
+ Path : path ,
191
+ },
192
+ },
193
+ },
194
+ })
195
+
196
+ middlewareScenario (t , t .Name (), func (t * testing.T , sc * scenarioContext ) {
197
+ sc .withIdentity (& authn.Identity {
198
+ OrgRoles : map [int64 ]org.RoleType {
199
+ 0 : tc .role ,
200
+ },
201
+ })
202
+ features := featuremgmt .WithFeatures ()
203
+ logger := & logtest.Fake {}
204
+ ac := & actest.FakeAccessControl {}
205
+
206
+ sc .m .Get ("/a/:id/*" , RoleAppPluginAuth (ac , ps , features , logger ), func (c * contextmodel.ReqContext ) {
207
+ c .JSON (http .StatusOK , map [string ]interface {}{})
208
+ })
209
+ sc .fakeReq ("GET" , path ).exec ()
210
+ assert .Equal (t , tc .expStatus , sc .resp .Code )
211
+ assert .Equal (t , tc .expBody , sc .resp .Body .String ())
212
+ assert .Equal (t , tc .expLocation , sc .resp .Header ().Get ("Location" ))
213
+ })
214
+ })
215
+ }
216
+ })
217
+
218
+ // We return success in this case because the frontend takes care of rendering the 404 page
219
+ middlewareScenario (t , "Plugin is not found returns success" , func (t * testing.T , sc * scenarioContext ) {
220
+ sc .withIdentity (& authn.Identity {
221
+ OrgRoles : map [int64 ]org.RoleType {
222
+ 0 : org .RoleViewer ,
223
+ },
224
+ })
225
+ features := featuremgmt .WithFeatures ()
226
+ logger := & logtest.Fake {}
227
+ ac := & actest.FakeAccessControl {}
228
+ sc .m .Get ("/a/:id/*" , RoleAppPluginAuth (ac , & pluginstore.FakePluginStore {}, features , logger ), func (c * contextmodel.ReqContext ) {
229
+ c .JSON (http .StatusOK , map [string ]interface {}{})
230
+ })
231
+ sc .fakeReq ("GET" , "/a/test-app/test" ).exec ()
232
+ assert .Equal (t , 200 , sc .resp .Code )
233
+ assert .Equal (t , "" , sc .resp .Body .String ())
234
+ })
235
+
236
+ // We return success in this case because the frontend takes care of rendering the right page based on its router
237
+ middlewareScenario (t , "Plugin page not found returns success" , func (t * testing.T , sc * scenarioContext ) {
238
+ sc .withIdentity (& authn.Identity {
239
+ OrgRoles : map [int64 ]org.RoleType {
240
+ 0 : org .RoleViewer ,
241
+ },
242
+ })
243
+ features := featuremgmt .WithFeatures ()
244
+ logger := & logtest.Fake {}
245
+ ac := & actest.FakeAccessControl {}
246
+ sc .m .Get ("/a/:id/*" , RoleAppPluginAuth (ac , pluginstore .NewFakePluginStore (pluginstore.Plugin {
247
+ JSONData : plugins.JSONData {
248
+ ID : "test-app" ,
249
+ Includes : []* plugins.Includes {
250
+ {
251
+ Type : "page" ,
252
+ Role : org .RoleViewer ,
253
+ Path : "/a/test-app/test" ,
254
+ },
255
+ },
256
+ },
257
+ }), features , logger ), func (c * contextmodel.ReqContext ) {
258
+ c .JSON (http .StatusOK , map [string ]interface {}{})
259
+ })
260
+ sc .fakeReq ("GET" , "/a/test-app/notExistingPath" ).exec ()
261
+ assert .Equal (t , 200 , sc .resp .Code )
262
+ assert .Equal (t , "" , sc .resp .Body .String ())
263
+ })
264
+
265
+ t .Run ("Plugin include with RBAC" , func (t * testing.T ) {
266
+ tcs := []struct {
267
+ name string
268
+ evalResult bool
269
+ evalErr error
270
+ expStatus int
271
+ expBody string
272
+ expLocation string
273
+ }{
274
+ {
275
+ name : "Unsuccessful RBAC eval will result in a redirect" ,
276
+ evalResult : false ,
277
+ expStatus : 302 ,
278
+ expBody : "<a href=\" /\" >Found</a>.\n \n " ,
279
+ expLocation : "/" ,
280
+ },
281
+ {
282
+ name : "An RBAC eval error will result in a redirect" ,
283
+ evalErr : errors .New ("eval error" ),
284
+ expStatus : 302 ,
285
+ expBody : "<a href=\" /\" >Found</a>.\n \n " ,
286
+ expLocation : "/" ,
287
+ },
288
+ {
289
+ name : "Successful RBAC eval will result in a successful request" ,
290
+ evalResult : true ,
291
+ expStatus : 200 ,
292
+ expBody : "" ,
293
+ expLocation : "" ,
294
+ },
295
+ }
296
+
297
+ for _ , tc := range tcs {
298
+ middlewareScenario (t , "Plugin include with RBAC" , func (t * testing.T , sc * scenarioContext ) {
299
+ sc .withIdentity (& authn.Identity {
300
+ OrgRoles : map [int64 ]org.RoleType {
301
+ 0 : org .RoleViewer ,
302
+ },
303
+ })
304
+ logger := & logtest.Fake {}
305
+ features := featuremgmt .WithFeatures (featuremgmt .FlagAccessControlOnCall )
306
+ ac := & actest.FakeAccessControl {
307
+ ExpectedEvaluate : tc .evalResult ,
308
+ ExpectedErr : tc .evalErr ,
309
+ }
310
+ path := "/a/test-app/test"
311
+ ps := pluginstore .NewFakePluginStore (pluginstore.Plugin {
312
+ JSONData : plugins.JSONData {
313
+ ID : "test-app" ,
314
+ Includes : []* plugins.Includes {
315
+ {
316
+ Type : "page" ,
317
+ Role : org .RoleViewer ,
318
+ Path : path ,
319
+ Action : "test-app.test:read" ,
320
+ },
321
+ },
322
+ },
323
+ })
324
+
325
+ sc .m .Get ("/a/:id/*" , RoleAppPluginAuth (ac , ps , features , logger ), func (c * contextmodel.ReqContext ) {
326
+ c .JSON (http .StatusOK , map [string ]interface {}{})
327
+ })
328
+ sc .fakeReq ("GET" , path ).exec ()
329
+ assert .Equal (t , tc .expStatus , sc .resp .Code )
330
+ assert .Equal (t , tc .expBody , sc .resp .Body .String ())
331
+ assert .Equal (t , tc .expLocation , sc .resp .Header ().Get ("Location" ))
332
+ })
333
+ }
334
+ })
335
+ }
336
+
153
337
func TestRemoveForceLoginparams (t * testing.T ) {
154
338
tcs := []struct {
155
339
inp string
0 commit comments