@@ -5,19 +5,88 @@ var common = require('../../../api/utils/common.js'),
55 countlyFs = require ( '../../../api/utils/countlyFs.js' ) ,
66 _ = require ( 'underscore' ) ,
77 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' ) ,
99 exported = { } ;
1010const { MongoInvalidArgumentError } = require ( 'mongodb' ) ;
1111
1212const { EJSON } = require ( 'bson' ) ;
1313
1414const 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+ } ;
1562var spawn = require ( 'child_process' ) . spawn ,
1663 child ;
64+
1765( function ( ) {
1866 plugins . register ( "/permissions/features" , function ( ob ) {
1967 ob . features . push ( FEATURE_NAME ) ;
2068 } ) ;
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+ }
2190
2291 /**
2392 * @api {get } /o/db Access database
@@ -179,6 +248,25 @@ var spawn = require('child_process').spawn,
179248 filter = { } ;
180249 }
181250
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+
182270 if ( dbs [ dbNameOnParam ] ) {
183271 try {
184272 var cursor = dbs [ dbNameOnParam ] . collection ( params . qstring . collection ) . find ( filter , { projection } ) ;
@@ -191,6 +279,7 @@ var spawn = require('child_process').spawn,
191279 common . returnMessage ( params , 400 , "Invalid collection name: Collection names can not contain '$' or other invalid characters" ) ;
192280 }
193281 else {
282+ log . e ( error ) ;
194283 common . returnMessage ( params , 500 , "An unexpected error occurred." ) ;
195284 }
196285 return false ;
@@ -291,7 +380,7 @@ var spawn = require('child_process').spawn,
291380 async . each ( results , function ( col , done ) {
292381 if ( col . collectionName . indexOf ( "system.indexes" ) === - 1 && col . collectionName . indexOf ( "sessions_" ) === - 1 ) {
293382 userHasAccess ( params , col . collectionName , params . qstring . app_id , function ( hasAccess ) {
294- if ( hasAccess ) {
383+ if ( hasAccess || col . collectionName === "events_data" || col . collectionName === "drill_events" ) {
295384 ob = parseCollectionName ( col . collectionName , lookup ) ;
296385 db . collections [ ob . pretty ] = ob . name ;
297386 }
@@ -318,8 +407,9 @@ var spawn = require('child_process').spawn,
318407 * Get aggregated result by the parameter on the url
319408 * @param {string } collection - collection will be applied related query
320409 * @param {object } aggregation - aggregation object
410+ * @param {object } changes - object referencing removed stages from pipeline
321411 * */
322- function aggregate ( collection , aggregation ) {
412+ function aggregate ( collection , aggregation , changes ) {
323413 if ( params . qstring . iDisplayLength ) {
324414 aggregation . push ( { "$limit" : parseInt ( params . qstring . iDisplayLength ) } ) ;
325415 }
@@ -339,6 +429,10 @@ var spawn = require('child_process').spawn,
339429 else if ( collection === 'auth_tokens' ) {
340430 aggregation . splice ( addProjectionAt , 0 , { "$addFields" : { "_id" : "***redacted***" } } ) ;
341431 }
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+ }
342436 // check task is already running?
343437 taskManager . checkIfRunning ( {
344438 db : dbs [ dbNameOnParam ] ,
@@ -375,7 +469,7 @@ var spawn = require('child_process').spawn,
375469 } ,
376470 outputData : function ( aggregationErr , result ) {
377471 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 || { } ) } ) ;
379473 }
380474 else {
381475 common . returnMessage ( params , 500 , aggregationErr ) ;
@@ -409,7 +503,12 @@ var spawn = require('child_process').spawn,
409503
410504 if ( appId ) {
411505 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+ }
413512 }
414513 }
415514 else {
@@ -485,10 +584,14 @@ var spawn = require('child_process').spawn,
485584 }
486585 else {
487586 userHasAccess ( params , params . qstring . collection , function ( hasAccess ) {
488- if ( hasAccess ) {
587+ if ( hasAccess || params . qstring . collection === "events_data" || params . qstring . collection === "drill_events" ) {
489588 try {
490589 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 ) ;
492595 }
493596 catch ( e ) {
494597 common . returnMessage ( params , 500 , 'Aggregation object is not valid.' ) ;
@@ -508,7 +611,7 @@ var spawn = require('child_process').spawn,
508611 }
509612 else {
510613 userHasAccess ( params , params . qstring . collection , function ( hasAccess ) {
511- if ( hasAccess ) {
614+ if ( hasAccess || params . qstring . collection === "events_data" || params . qstring . collection === "drill_events" ) {
512615 dbGetCollection ( ) ;
513616 }
514617 else {
0 commit comments