@@ -5,19 +5,88 @@ var common = require('../../../api/utils/common.js'),
5
5
countlyFs = require ( '../../../api/utils/countlyFs.js' ) ,
6
6
_ = require ( 'underscore' ) ,
7
7
taskManager = require ( '../../../api/utils/taskmanager.js' ) ,
8
- { getCollectionName, dbUserHasAccessToCollection, dbLoadEventsData, validateUser, getUserApps, validateGlobalAdmin, hasReadRight } = require ( '../../../api/utils/rights.js' ) ,
8
+ { getCollectionName, dbUserHasAccessToCollection, dbLoadEventsData, validateUser, getUserApps, validateGlobalAdmin, hasReadRight, getBaseAppFilter } = require ( '../../../api/utils/rights.js' ) ,
9
9
exported = { } ;
10
10
const { MongoInvalidArgumentError } = require ( 'mongodb' ) ;
11
11
12
12
const { EJSON } = require ( 'bson' ) ;
13
13
14
14
const FEATURE_NAME = 'dbviewer' ;
15
+ const whiteListedAggregationStages = {
16
+ "$addFields" : true ,
17
+ "$bucket" : true ,
18
+ "$bucketAuto" : true ,
19
+ //"$changeStream": false,
20
+ //"$changeStreamSplitLargeEvents": false,
21
+ //"$collStats": false,
22
+ "$count" : true ,
23
+ //"$currentOp": false,
24
+ "$densify" : true ,
25
+ //"$documents": false
26
+ "$facet" : true ,
27
+ "$fill" : true ,
28
+ "$geoNear" : true ,
29
+ "$graphLookup" : true ,
30
+ "$group" : true ,
31
+ //"$indexStats": false,
32
+ "$limit" : true ,
33
+ //"$listLocalSessions": false
34
+ //"$listSampledQueries": false
35
+ //"$listSearchIndexes": false
36
+ //"$listSessions": false
37
+ //"$lookup": false
38
+ "$match" : true ,
39
+ //"$merge": false
40
+ //"$mergeCursors": false
41
+ //"$out": false
42
+ //"$planCacheStats": false,
43
+ "$project" : true ,
44
+ "$querySettings" : true ,
45
+ "$redact" : true ,
46
+ "$replaceRoot" : true ,
47
+ "$replaceWith" : true ,
48
+ "$sample" : true ,
49
+ "$search" : true ,
50
+ "$searchMeta" : true ,
51
+ "$set" : true ,
52
+ "$setWindowFields" : true ,
53
+ //"$sharedDataDistribution": false,
54
+ "$skip" : true ,
55
+ "$sort" : true ,
56
+ "$sortByCount" : true ,
57
+ //"$unionWith": false,
58
+ "$unset" : true ,
59
+ "$unwind" : true ,
60
+ "$vectorSearch" : true //atlas specific
61
+ } ;
15
62
var spawn = require ( 'child_process' ) . spawn ,
16
63
child ;
64
+
17
65
( function ( ) {
18
66
plugins . register ( "/permissions/features" , function ( ob ) {
19
67
ob . features . push ( FEATURE_NAME ) ;
20
68
} ) ;
69
+ /**
70
+ * Function removes not allowed aggregation stages from the pipeline
71
+ * @param {array } aggregation - current aggregation pipeline
72
+ * @returns {object } changes - object with information which operations were removed
73
+ */
74
+ function escapeNotAllowedAggregationStages ( aggregation ) {
75
+ var changes = { } ;
76
+ for ( var z = 0 ; z < aggregation . length ; z ++ ) {
77
+ for ( var key in aggregation [ z ] ) {
78
+ if ( ! whiteListedAggregationStages [ key ] ) {
79
+ changes [ key ] = true ;
80
+ delete aggregation [ z ] [ key ] ;
81
+ }
82
+ }
83
+ if ( Object . keys ( aggregation [ z ] ) . length === 0 ) {
84
+ aggregation . splice ( z , 1 ) ;
85
+ z -- ;
86
+ }
87
+ }
88
+ return changes ;
89
+ }
21
90
22
91
/**
23
92
* @api {get } /o/db Access database
@@ -179,6 +248,25 @@ var spawn = require('child_process').spawn,
179
248
filter = { } ;
180
249
}
181
250
251
+ var base_filter = { } ;
252
+ if ( ! params . member . global_admin ) {
253
+ base_filter = getBaseAppFilter ( params . member , dbNameOnParam , params . qstring . collection ) ;
254
+ }
255
+
256
+ if ( base_filter && Object . keys ( base_filter ) . length > 0 ) {
257
+ for ( var key in base_filter ) {
258
+ if ( filter [ key ] ) {
259
+ filter . $and = filter . $and || [ ] ;
260
+ filter . $and . push ( { [ key ] : base_filter [ key ] } ) ;
261
+ filter . $and . push ( { [ key ] : filter [ key ] } ) ;
262
+ delete filter [ key ] ;
263
+ }
264
+ else {
265
+ filter [ key ] = base_filter [ key ] ;
266
+ }
267
+ }
268
+ }
269
+
182
270
if ( dbs [ dbNameOnParam ] ) {
183
271
try {
184
272
var cursor = dbs [ dbNameOnParam ] . collection ( params . qstring . collection ) . find ( filter , { projection } ) ;
@@ -191,6 +279,7 @@ var spawn = require('child_process').spawn,
191
279
common . returnMessage ( params , 400 , "Invalid collection name: Collection names can not contain '$' or other invalid characters" ) ;
192
280
}
193
281
else {
282
+ log . e ( error ) ;
194
283
common . returnMessage ( params , 500 , "An unexpected error occurred." ) ;
195
284
}
196
285
return false ;
@@ -291,7 +380,7 @@ var spawn = require('child_process').spawn,
291
380
async . each ( results , function ( col , done ) {
292
381
if ( col . collectionName . indexOf ( "system.indexes" ) === - 1 && col . collectionName . indexOf ( "sessions_" ) === - 1 ) {
293
382
userHasAccess ( params , col . collectionName , params . qstring . app_id , function ( hasAccess ) {
294
- if ( hasAccess ) {
383
+ if ( hasAccess || col . collectionName === "events_data" || col . collectionName === "drill_events" ) {
295
384
ob = parseCollectionName ( col . collectionName , lookup ) ;
296
385
db . collections [ ob . pretty ] = ob . name ;
297
386
}
@@ -318,8 +407,9 @@ var spawn = require('child_process').spawn,
318
407
* Get aggregated result by the parameter on the url
319
408
* @param {string } collection - collection will be applied related query
320
409
* @param {object } aggregation - aggregation object
410
+ * @param {object } changes - object referencing removed stages from pipeline
321
411
* */
322
- function aggregate ( collection , aggregation ) {
412
+ function aggregate ( collection , aggregation , changes ) {
323
413
if ( params . qstring . iDisplayLength ) {
324
414
aggregation . push ( { "$limit" : parseInt ( params . qstring . iDisplayLength ) } ) ;
325
415
}
@@ -339,6 +429,10 @@ var spawn = require('child_process').spawn,
339
429
else if ( collection === 'auth_tokens' ) {
340
430
aggregation . splice ( addProjectionAt , 0 , { "$addFields" : { "_id" : "***redacted***" } } ) ;
341
431
}
432
+ else if ( ( collection === "events_data" || collection === "drill_events" ) && ! params . member . global_admin ) {
433
+ var base_filter = getBaseAppFilter ( params . member , dbNameOnParam , params . qstring . collection ) ;
434
+ aggregation . splice ( 0 , 0 , { "$match" : base_filter } ) ;
435
+ }
342
436
// check task is already running?
343
437
taskManager . checkIfRunning ( {
344
438
db : dbs [ dbNameOnParam ] ,
@@ -375,7 +469,7 @@ var spawn = require('child_process').spawn,
375
469
} ,
376
470
outputData : function ( aggregationErr , result ) {
377
471
if ( ! aggregationErr ) {
378
- common . returnOutput ( params , { sEcho : params . qstring . sEcho , iTotalRecords : 0 , iTotalDisplayRecords : 0 , "aaData" : result } ) ;
472
+ common . returnOutput ( params , { sEcho : params . qstring . sEcho , iTotalRecords : 0 , iTotalDisplayRecords : 0 , "aaData" : result , "removed" : ( changes || { } ) } ) ;
379
473
}
380
474
else {
381
475
common . returnMessage ( params , 500 , aggregationErr ) ;
@@ -409,7 +503,12 @@ var spawn = require('child_process').spawn,
409
503
410
504
if ( appId ) {
411
505
if ( hasReadRight ( FEATURE_NAME , appId , parameters . member ) ) {
412
- return dbUserHasAccessToCollection ( parameters , collection , appId , callback ) ;
506
+ if ( collection === "events_data" || collection === "drill_events" ) {
507
+ return callback ( true ) ;
508
+ }
509
+ else {
510
+ return dbUserHasAccessToCollection ( parameters , collection , appId , callback ) ;
511
+ }
413
512
}
414
513
}
415
514
else {
@@ -485,10 +584,14 @@ var spawn = require('child_process').spawn,
485
584
}
486
585
else {
487
586
userHasAccess ( params , params . qstring . collection , function ( hasAccess ) {
488
- if ( hasAccess ) {
587
+ if ( hasAccess || params . qstring . collection === "events_data" || params . qstring . collection === "drill_events" ) {
489
588
try {
490
589
let aggregation = EJSON . parse ( params . qstring . aggregation ) ;
491
- aggregate ( params . qstring . collection , aggregation ) ;
590
+ var changes = escapeNotAllowedAggregationStages ( aggregation ) ;
591
+ if ( changes && Object . keys ( changes ) . length > 0 ) {
592
+ log . d ( "Removed stages from pipeline: " , JSON . stringify ( changes ) ) ;
593
+ }
594
+ aggregate ( params . qstring . collection , aggregation , changes ) ;
492
595
}
493
596
catch ( e ) {
494
597
common . returnMessage ( params , 500 , 'Aggregation object is not valid.' ) ;
@@ -508,7 +611,7 @@ var spawn = require('child_process').spawn,
508
611
}
509
612
else {
510
613
userHasAccess ( params , params . qstring . collection , function ( hasAccess ) {
511
- if ( hasAccess ) {
614
+ if ( hasAccess || params . qstring . collection === "events_data" || params . qstring . collection === "drill_events" ) {
512
615
dbGetCollection ( ) ;
513
616
}
514
617
else {
0 commit comments