2017-08-28 19:27:45 +03:00
/ * *
2018-01-04 23:15:21 +03:00
* @ description MeshCentral database module
2017-08-28 19:27:45 +03:00
* @ author Ylian Saint - Hilaire
2019-01-04 03:22:15 +03:00
* @ copyright Intel Corporation 2018 - 2019
2018-01-04 23:15:21 +03:00
* @ license Apache - 2.0
2017-08-28 19:27:45 +03:00
* @ version v0 . 0.2
* /
2018-08-30 03:40:30 +03:00
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
2018-08-27 22:24:15 +03:00
2017-08-28 19:27:45 +03:00
//
// Construct Meshcentral database object
//
// The default database is NeDB
// https://github.com/louischatriot/nedb
//
// Alternativety, MongoDB can be used
// https://www.mongodb.com/
// Just run with --mongodb [connectionstring], where the connection string is documented here: https://docs.mongodb.com/manual/reference/connection-string/
// The default collection is "meshcentral", but you can override it using --mongodbcol [collection]
//
2019-05-09 04:14:30 +03:00
module . exports . CreateDB = function ( parent , func ) {
2017-08-28 19:27:45 +03:00
var obj = { } ;
2018-08-30 03:40:30 +03:00
var Datastore = null ;
2019-02-19 01:32:55 +03:00
var expireEventsSeconds = ( 60 * 60 * 24 * 20 ) ; // By default, expire events after 20 days. (Seconds * Minutes * Hours * Days)
var expirePowerEventsSeconds = ( 60 * 60 * 24 * 10 ) ; // By default, expire power events after 10 days. (Seconds * Minutes * Hours * Days)
2019-03-26 05:59:04 +03:00
var expireServerStatsSeconds = ( 60 * 60 * 24 * 30 ) ; // By default, expire power events after 30 days. (Seconds * Minutes * Hours * Days)
2019-07-11 00:27:38 +03:00
const common = require ( './common.js' ) ;
2017-09-13 21:25:57 +03:00
obj . identifier = null ;
2019-01-24 23:08:48 +03:00
obj . dbKey = null ;
2019-10-03 03:19:33 +03:00
obj . dbRecordsEncryptKey = null ;
obj . dbRecordsDecryptKey = null ;
2019-05-29 03:25:23 +03:00
obj . changeStream = false ;
2017-09-13 21:25:57 +03:00
2019-05-09 04:14:30 +03:00
obj . SetupDatabase = function ( func ) {
// Check if the database unique identifier is present
// This is used to check that in server peering mode, everyone is using the same database.
obj . Get ( 'DatabaseIdentifier' , function ( err , docs ) {
if ( ( docs . length == 1 ) && ( docs [ 0 ] . value != null ) ) {
obj . identifier = docs [ 0 ] . value ;
} else {
obj . identifier = Buffer . from ( require ( 'crypto' ) . randomBytes ( 48 ) , 'binary' ) . toString ( 'hex' ) ;
obj . Set ( { _id : 'DatabaseIdentifier' , value : obj . identifier } ) ;
}
} ) ;
// Load database schema version and check if we need to update
obj . Get ( 'SchemaVersion' , function ( err , docs ) {
var ver = 0 ;
if ( docs && docs . length == 1 ) { ver = docs [ 0 ] . value ; }
if ( ver == 1 ) { console . log ( 'This is an unsupported beta 1 database, delete it to create a new one.' ) ; process . exit ( 0 ) ; }
// TODO: Any schema upgrades here...
obj . Set ( { _id : 'SchemaVersion' , value : 2 } ) ;
func ( ver ) ;
} ) ;
} ;
obj . cleanup = function ( func ) {
// TODO: Remove all mesh links to invalid users
// TODO: Remove all meshes that dont have any links
// Remove all events, power events and SMBIOS data from the main collection. They are all in seperate collections now.
2019-05-09 23:41:14 +03:00
if ( obj . databaseType == 3 ) {
// MongoDB
obj . file . deleteMany ( { type : 'event' } , { multi : true } ) ;
obj . file . deleteMany ( { type : 'power' } , { multi : true } ) ;
obj . file . deleteMany ( { type : 'smbios' } , { multi : true } ) ;
} else {
// NeDB or MongoJS
obj . file . remove ( { type : 'event' } , { multi : true } ) ;
obj . file . remove ( { type : 'power' } , { multi : true } ) ;
obj . file . remove ( { type : 'smbios' } , { multi : true } ) ;
}
2019-05-09 04:14:30 +03:00
// Remove all objects that have a "meshid" that no longer points to a valid mesh.
obj . GetAllType ( 'mesh' , function ( err , docs ) {
var meshlist = [ ] ;
if ( ( err == null ) && ( docs . length > 0 ) ) { for ( var i in docs ) { meshlist . push ( docs [ i ] . _id ) ; } }
2019-05-09 23:41:14 +03:00
if ( obj . databaseType == 3 ) {
// MongoDB
obj . file . deleteMany ( { meshid : { $exists : true , $nin : meshlist } } , { multi : true } ) ;
} else {
// NeDB or MongoJS
obj . file . remove ( { meshid : { $exists : true , $nin : meshlist } } , { multi : true } ) ;
}
2019-05-09 04:14:30 +03:00
// Fix all of the creating & login to ticks by seconds, not milliseconds.
obj . GetAllType ( 'user' , function ( err , docs ) {
if ( err == null && docs . length > 0 ) {
for ( var i in docs ) {
var fixed = false ;
2019-07-15 20:24:31 +03:00
// Fix email address capitalization
if ( docs [ i ] . email && ( docs [ i ] . email != docs [ i ] . email . toLowerCase ( ) ) ) {
docs [ i ] . email = docs [ i ] . email . toLowerCase ( ) ; fixed = true ;
}
2019-05-09 04:14:30 +03:00
// Fix account creation
if ( docs [ i ] . creation ) {
if ( docs [ i ] . creation > 1300000000000 ) { docs [ i ] . creation = Math . floor ( docs [ i ] . creation / 1000 ) ; fixed = true ; }
if ( ( docs [ i ] . creation % 1 ) != 0 ) { docs [ i ] . creation = Math . floor ( docs [ i ] . creation ) ; fixed = true ; }
}
// Fix last account login
if ( docs [ i ] . login ) {
if ( docs [ i ] . login > 1300000000000 ) { docs [ i ] . login = Math . floor ( docs [ i ] . login / 1000 ) ; fixed = true ; }
if ( ( docs [ i ] . login % 1 ) != 0 ) { docs [ i ] . login = Math . floor ( docs [ i ] . login ) ; fixed = true ; }
}
// Fix last password change
if ( docs [ i ] . passchange ) {
if ( docs [ i ] . passchange > 1300000000000 ) { docs [ i ] . passchange = Math . floor ( docs [ i ] . passchange / 1000 ) ; fixed = true ; }
if ( ( docs [ i ] . passchange % 1 ) != 0 ) { docs [ i ] . passchange = Math . floor ( docs [ i ] . passchange ) ; fixed = true ; }
}
// Fix subscriptions
if ( docs [ i ] . subscriptions != null ) { delete docs [ i ] . subscriptions ; fixed = true ; }
// Save the user if needed
if ( fixed ) { obj . Set ( docs [ i ] ) ; }
// We are done
if ( func ) { func ( ) ; }
}
}
} ) ;
} ) ;
} ;
// Get encryption key
obj . getEncryptDataKey = function ( password ) {
if ( typeof password != 'string' ) return null ;
2019-05-24 20:33:40 +03:00
return parent . crypto . createHash ( 'sha384' ) . update ( password ) . digest ( "raw" ) . slice ( 0 , 32 ) ;
2019-05-09 04:14:30 +03:00
}
// Encrypt data
obj . encryptData = function ( password , plaintext ) {
var key = obj . getEncryptDataKey ( password ) ;
if ( key == null ) return null ;
2019-05-24 20:33:40 +03:00
const iv = parent . crypto . randomBytes ( 16 ) ;
const aes = parent . crypto . createCipheriv ( 'aes-256-cbc' , key , iv ) ;
2019-05-09 04:14:30 +03:00
var ciphertext = aes . update ( plaintext ) ;
ciphertext = Buffer . concat ( [ iv , ciphertext , aes . final ( ) ] ) ;
return ciphertext . toString ( 'base64' ) ;
}
// Decrypt data
obj . decryptData = function ( password , ciphertext ) {
try {
var key = obj . getEncryptDataKey ( password ) ;
if ( key == null ) return null ;
const ciphertextBytes = Buffer . from ( ciphertext , 'base64' ) ;
const iv = ciphertextBytes . slice ( 0 , 16 ) ;
const data = ciphertextBytes . slice ( 16 ) ;
2019-05-24 20:33:40 +03:00
const aes = parent . crypto . createDecipheriv ( 'aes-256-cbc' , key , iv ) ;
2019-05-09 04:14:30 +03:00
var plaintextBytes = Buffer . from ( aes . update ( data ) ) ;
plaintextBytes = Buffer . concat ( [ plaintextBytes , aes . final ( ) ] ) ;
return plaintextBytes ;
} catch ( ex ) { return null ; }
}
// Get the number of records in the database for various types, this is the slow NeDB way.
// WARNING: This is a terrible query for database performance. Only do this when needed. This query will look at almost every document in the database.
obj . getStats = function ( func ) {
2019-12-09 07:46:25 +03:00
if ( obj . databaseType == 3 ) {
// MongoDB
obj . file . aggregate ( [ { "$group" : { _id : "$type" , count : { $sum : 1 } } } ] ) . toArray ( function ( err , docs ) {
var counters = { } , totalCount = 0 ;
for ( var i in docs ) { if ( docs [ i ] . _id != null ) { counters [ docs [ i ] . _id ] = docs [ i ] . count ; totalCount += docs [ i ] . count ; } }
func ( counters ) ;
} ) ;
} else if ( obj . databaseType == 2 ) {
// MongoJS
2019-05-09 04:14:30 +03:00
obj . file . aggregate ( [ { "$group" : { _id : "$type" , count : { $sum : 1 } } } ] , function ( err , docs ) {
var counters = { } , totalCount = 0 ;
for ( var i in docs ) { if ( docs [ i ] . _id != null ) { counters [ docs [ i ] . _id ] = docs [ i ] . count ; totalCount += docs [ i ] . count ; } }
2019-12-09 07:46:25 +03:00
func ( counters ) ;
} ) ;
} else if ( obj . databaseType == 1 ) {
2019-05-09 04:14:30 +03:00
// NeDB version
obj . file . count ( { type : 'node' } , function ( err , nodeCount ) {
obj . file . count ( { type : 'mesh' } , function ( err , meshCount ) {
obj . file . count ( { type : 'user' } , function ( err , userCount ) {
2019-12-09 07:46:25 +03:00
obj . file . count ( { type : 'sysinfo' } , function ( err , sysinfoCount ) {
obj . file . count ( { type : 'note' } , function ( err , noteCount ) {
obj . file . count ( { type : 'iploc' } , function ( err , iplocCount ) {
obj . file . count ( { type : 'ifinfo' } , function ( err , ifinfoCount ) {
obj . file . count ( { type : 'cfile' } , function ( err , cfileCount ) {
obj . file . count ( { type : 'lastconnect' } , function ( err , lastconnectCount ) {
obj . file . count ( { } , function ( err , totalCount ) {
func ( { node : nodeCount , mesh : meshCount , user : userCount , sysinfo : sysinfoCount , iploc : iplocCount , note : noteCount , ifinfo : ifinfoCount , cfile : cfileCount , lastconnect : lastconnectCount , total : totalCount } ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
2019-05-09 04:14:30 +03:00
} ) ;
} ) ;
} ) ;
} ) ;
}
}
// This is used to rate limit a number of operation per day. Returns a startValue each new days, but you can substract it and save the value in the db.
obj . getValueOfTheDay = function ( id , startValue , func ) { obj . Get ( id , function ( err , docs ) { var date = new Date ( ) , t = date . toLocaleDateString ( ) ; if ( docs . length == 1 ) { var r = docs [ 0 ] ; if ( r . day == t ) { func ( { _id : id , value : r . value , day : t } ) ; return ; } } func ( { _id : id , value : startValue , day : t } ) ; } ) ; } ;
obj . escapeBase64 = function escapeBase64 ( val ) { return ( val . replace ( /\+/g , '@' ) . replace ( /\//g , '$' ) ) ; }
2019-10-03 23:32:54 +03:00
// Encrypt an database object
obj . performRecordEncryptionRecode = function ( func ) {
var count = 0 ;
obj . GetAllType ( 'user' , function ( err , docs ) {
if ( err == null ) { for ( var i in docs ) { count ++ ; obj . Set ( docs [ i ] ) ; } }
obj . GetAllType ( 'node' , function ( err , docs ) {
if ( err == null ) { for ( var i in docs ) { count ++ ; obj . Set ( docs [ i ] ) ; } }
func ( count ) ;
} ) ;
} ) ;
}
2019-10-03 03:19:33 +03:00
// Encrypt an database object
function performTypedRecordDecrypt ( data ) {
if ( ( obj . dbRecordsDecryptKey == null ) || ( typeof data != 'object' ) ) return data ;
for ( var i in data ) {
if ( data [ i ] . type == 'user' ) {
data [ i ] = performPartialRecordDecrypt ( data [ i ] ) ;
} else if ( ( data [ i ] . type == 'node' ) && ( data [ i ] . intelamt != null ) ) {
data [ i ] . intelamt = performPartialRecordDecrypt ( data [ i ] . intelamt ) ;
}
}
return data ;
}
2019-05-09 04:14:30 +03:00
2019-10-03 03:19:33 +03:00
// Encrypt an database object
function performTypedRecordEncrypt ( data ) {
if ( obj . dbRecordsEncryptKey == null ) return data ;
if ( data . type == 'user' ) { return performPartialRecordEncrypt ( Clone ( data ) , [ 'otpkeys' , 'otphkeys' , 'otpsecret' , 'salt' , 'hash' ] ) ; }
else if ( ( data . type == 'node' ) && ( data . intelamt != null ) ) { var xdata = Clone ( data ) ; xdata . intelamt = performPartialRecordEncrypt ( xdata . intelamt , [ 'user' , 'pass' ] ) ; return xdata ; }
return data ;
}
// Encrypt an object and return a buffer.
function performPartialRecordEncrypt ( plainobj , encryptNames ) {
if ( typeof plainobj != 'object' ) return plainobj ;
var enc = { } , enclen = 0 ;
for ( var i in encryptNames ) { if ( plainobj [ encryptNames [ i ] ] != null ) { enclen ++ ; enc [ encryptNames [ i ] ] = plainobj [ encryptNames [ i ] ] ; delete plainobj [ encryptNames [ i ] ] ; } }
if ( enclen > 0 ) { plainobj . _CRYPT = performRecordEncrypt ( enc ) ; } else { delete plainobj . _CRYPT ; }
return plainobj ;
}
// Encrypt an object and return a buffer.
function performPartialRecordDecrypt ( plainobj ) {
if ( ( typeof plainobj != 'object' ) || ( plainobj . _CRYPT == null ) ) return plainobj ;
var enc = performRecordDecrypt ( plainobj . _CRYPT ) ;
if ( enc != null ) { for ( var i in enc ) { plainobj [ i ] = enc [ i ] ; } }
delete plainobj . _CRYPT ;
return plainobj ;
}
// Encrypt an object and return a base64.
function performRecordEncrypt ( plainobj ) {
if ( obj . dbRecordsEncryptKey == null ) return null ;
2019-10-03 23:32:54 +03:00
const iv = parent . crypto . randomBytes ( 12 ) ;
const aes = parent . crypto . createCipheriv ( 'aes-256-gcm' , obj . dbRecordsEncryptKey , iv ) ;
2019-10-03 03:19:33 +03:00
var ciphertext = aes . update ( JSON . stringify ( plainobj ) ) ;
2019-10-03 23:32:54 +03:00
var cipherfinal = aes . final ( ) ;
ciphertext = Buffer . concat ( [ iv , aes . getAuthTag ( ) , ciphertext , cipherfinal ] ) ;
2019-10-03 03:19:33 +03:00
return ciphertext . toString ( 'base64' ) ;
}
// Takes a base64 and return an object.
function performRecordDecrypt ( ciphertext ) {
if ( obj . dbRecordsDecryptKey == null ) return null ;
const ciphertextBytes = Buffer . from ( ciphertext , 'base64' ) ;
2019-10-03 23:32:54 +03:00
const iv = ciphertextBytes . slice ( 0 , 12 ) ;
const data = ciphertextBytes . slice ( 28 ) ;
const aes = parent . crypto . createDecipheriv ( 'aes-256-gcm' , obj . dbRecordsDecryptKey , iv ) ;
aes . setAuthTag ( ciphertextBytes . slice ( 12 , 16 ) ) ;
var plaintextBytes , r ;
try {
plaintextBytes = Buffer . from ( aes . update ( data ) ) ;
plaintextBytes = Buffer . concat ( [ plaintextBytes , aes . final ( ) ] ) ;
r = JSON . parse ( plaintextBytes . toString ( ) ) ;
} catch ( e ) { throw "Incorrect DbRecordsDecryptKey/DbRecordsEncryptKey or invalid database _CRYPT data: " + e ; }
return r ;
2019-10-03 03:19:33 +03:00
}
// Clone an object (TODO: Make this more efficient)
function Clone ( v ) { return JSON . parse ( JSON . stringify ( v ) ) ; }
2019-05-09 04:14:30 +03:00
2019-02-19 09:20:25 +03:00
// Read expiration time from configuration file
2019-05-24 20:33:40 +03:00
if ( typeof parent . args . dbexpire == 'object' ) {
if ( typeof parent . args . dbexpire . events == 'number' ) { expireEventsSeconds = parent . args . dbexpire . events ; }
if ( typeof parent . args . dbexpire . powerevents == 'number' ) { expirePowerEventsSeconds = parent . args . dbexpire . powerevents ; }
if ( typeof parent . args . dbexpire . statsevents == 'number' ) { expireServerStatsSeconds = parent . args . dbexpire . statsevents ; }
2019-02-19 09:20:25 +03:00
}
2019-10-03 03:19:33 +03:00
// If a DB record encryption key is provided, perform database record encryption
if ( ( typeof parent . args . dbrecordsencryptkey == 'string' ) && ( parent . args . dbrecordsencryptkey . length != 0 ) ) {
// Hash the database password into a AES256 key and setup encryption and decryption.
obj . dbRecordsEncryptKey = obj . dbRecordsDecryptKey = parent . crypto . createHash ( 'sha384' ) . update ( parent . args . dbrecordsencryptkey ) . digest ( "raw" ) . slice ( 0 , 32 ) ;
}
// If a DB record decryption key is provided, perform database record decryption
if ( ( typeof parent . args . dbrecordsdecryptkey == 'string' ) && ( parent . args . dbrecordsdecryptkey . length != 0 ) ) {
// Hash the database password into a AES256 key and setup encryption and decryption.
obj . dbRecordsDecryptKey = parent . crypto . createHash ( 'sha384' ) . update ( parent . args . dbrecordsdecryptkey ) . digest ( "raw" ) . slice ( 0 , 32 ) ;
}
2019-05-24 20:33:40 +03:00
if ( parent . args . mongodb ) {
2017-08-28 19:27:45 +03:00
// Use MongoDB
2019-05-09 04:14:30 +03:00
obj . databaseType = 3 ;
2019-09-21 01:20:59 +03:00
require ( 'mongodb' ) . MongoClient . connect ( parent . args . mongodb , { useNewUrlParser : true , useUnifiedTopology : true } , function ( err , client ) {
2019-05-09 04:14:30 +03:00
if ( err != null ) { console . log ( "Unable to connect to database: " + err ) ; process . exit ( ) ; return ; }
2019-05-09 23:41:14 +03:00
Datastore = client ;
2019-05-21 04:03:14 +03:00
// Get the database name and setup the database client
var dbname = 'meshcentral' ;
2019-05-24 20:33:40 +03:00
if ( parent . args . mongodbname ) { dbname = parent . args . mongodbname ; }
const dbcollectionname = ( parent . args . mongodbcol ) ? ( parent . args . mongodbcol ) : 'meshcentral' ;
2019-05-09 04:14:30 +03:00
const db = client . db ( dbname ) ;
// Setup MongoDB main collection and indexes
2019-05-09 23:41:14 +03:00
obj . file = db . collection ( dbcollectionname ) ;
obj . file . indexes ( function ( err , indexes ) {
2019-05-09 04:14:30 +03:00
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 4 ) || ( indexesByName [ 'TypeDomainMesh1' ] == null ) || ( indexesByName [ 'Email1' ] == null ) || ( indexesByName [ 'Mesh1' ] == null ) ) {
console . log ( 'Resetting main indexes...' ) ;
obj . file . dropIndexes ( function ( err ) {
obj . file . createIndex ( { type : 1 , domain : 1 , meshid : 1 } , { sparse : 1 , name : 'TypeDomainMesh1' } ) ; // Speeds up GetAllTypeNoTypeField() and GetAllTypeNoTypeFieldMeshFiltered()
obj . file . createIndex ( { email : 1 } , { sparse : 1 , name : 'Email1' } ) ; // Speeds up GetUserWithEmail() and GetUserWithVerifiedEmail()
obj . file . createIndex ( { meshid : 1 } , { sparse : 1 , name : 'Mesh1' } ) ; // Speeds up RemoveMesh()
} ) ;
}
} ) ;
2019-05-29 03:25:23 +03:00
// Setup the changeStream on the MongoDB main collection if possible
2019-05-29 03:42:11 +03:00
if ( parent . args . mongodbchangestream == true ) {
2019-08-14 03:06:45 +03:00
if ( typeof obj . file . watch != 'function' ) {
console . log ( 'WARNING: watch() is not a function, MongoDB ChangeStream not supported.' ) ;
} else {
2020-01-02 11:30:14 +03:00
obj . fileChangeStream = obj . file . watch ( [ { $match : { $or : [ { 'fullDocument.type' : { $in : [ 'node' , 'mesh' , 'user' , 'ugrp' ] } } , { 'operationType' : 'delete' } ] } } ] , { fullDocument : 'updateLookup' } ) ;
2019-08-14 03:06:45 +03:00
obj . fileChangeStream . on ( 'change' , function ( change ) {
if ( change . operationType == 'update' ) {
switch ( change . fullDocument . type ) {
case 'node' : { dbNodeChange ( change , false ) ; break ; } // A node has changed
case 'mesh' : { dbMeshChange ( change , false ) ; break ; } // A device group has changed
case 'user' : { dbUserChange ( change , false ) ; break ; } // A user account has changed
2020-01-02 11:30:14 +03:00
case 'ugrp' : { dbUGrpChange ( change , false ) ; break ; } // A user account has changed
2019-05-30 00:36:14 +03:00
}
2019-08-14 03:06:45 +03:00
} else if ( change . operationType == 'insert' ) {
switch ( change . fullDocument . type ) {
case 'node' : { dbNodeChange ( change , true ) ; break ; } // A node has added
case 'mesh' : { dbMeshChange ( change , true ) ; break ; } // A device group has created
case 'user' : { dbUserChange ( change , true ) ; break ; } // A user account has created
2020-01-02 11:30:14 +03:00
case 'ugrp' : { dbUGrpChange ( change , true ) ; break ; } // A user account has created
2019-05-30 00:36:14 +03:00
}
2019-08-14 03:06:45 +03:00
} else if ( change . operationType == 'delete' ) {
var splitId = change . documentKey . _id . split ( '/' ) ;
switch ( splitId [ 0 ] ) {
case 'node' : {
//Not Good: Problem here is that we don't know what meshid the node belonged to before the delete.
//parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', action: 'removenode', nodeid: change.documentKey._id, domain: splitId[1] });
break ;
}
case 'mesh' : {
2020-01-02 11:30:14 +03:00
parent . DispatchEvent ( [ '*' , change . documentKey . _id ] , obj , { etype : 'mesh' , action : 'deletemesh' , meshid : change . documentKey . _id , domain : splitId [ 1 ] } ) ;
2019-08-14 03:06:45 +03:00
break ;
}
case 'user' : {
//Not Good: This is not a perfect user removal because we don't know what groups the user was in.
//parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', action: 'accountremove', userid: change.documentKey._id, domain: splitId[1], username: splitId[2] });
break ;
}
2020-01-02 11:30:14 +03:00
case 'ugrp' : {
parent . DispatchEvent ( [ '*' , change . documentKey . _id ] , obj , { etype : 'ugrp' , action : 'deleteusergroup' , ugrpid : change . documentKey . _id , domain : splitId [ 1 ] } ) ;
break ;
}
2019-05-30 00:36:14 +03:00
}
}
2019-08-14 03:06:45 +03:00
} ) ;
obj . changeStream = true ;
}
2019-05-29 03:42:11 +03:00
}
2019-05-09 04:14:30 +03:00
// Setup MongoDB events collection and indexes
2019-05-30 00:36:14 +03:00
obj . eventsfile = db . collection ( 'events' ) ; // Collection containing all events
2019-05-09 23:41:14 +03:00
obj . eventsfile . indexes ( function ( err , indexes ) {
2019-05-09 04:14:30 +03:00
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 5 ) || ( indexesByName [ 'Username1' ] == null ) || ( indexesByName [ 'DomainNodeTime1' ] == null ) || ( indexesByName [ 'IdsAndTime1' ] == null ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
// Reset all indexes
console . log ( 'Resetting events indexes...' ) ;
obj . eventsfile . dropIndexes ( function ( err ) {
obj . eventsfile . createIndex ( { username : 1 } , { sparse : 1 , name : 'Username1' } ) ;
obj . eventsfile . createIndex ( { domain : 1 , nodeid : 1 , time : - 1 } , { sparse : 1 , name : 'DomainNodeTime1' } ) ;
obj . eventsfile . createIndex ( { ids : 1 , time : - 1 } , { sparse : 1 , name : 'IdsAndTime1' } ) ;
obj . eventsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expireEventsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting events expire index...' ) ;
obj . eventsfile . dropIndex ( "ExpireTime1" , function ( err ) {
obj . eventsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
// Setup MongoDB power events collection and indexes
obj . powerfile = db . collection ( 'power' ) ; // Collection containing all power events
2019-05-09 23:41:14 +03:00
obj . powerfile . indexes ( function ( err , indexes ) {
2019-05-09 04:14:30 +03:00
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 3 ) || ( indexesByName [ 'NodeIdAndTime1' ] == null ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
// Reset all indexes
console . log ( 'Resetting power events indexes...' ) ;
obj . powerfile . dropIndexes ( function ( err ) {
// Create all indexes
obj . powerfile . createIndex ( { nodeid : 1 , time : 1 } , { sparse : 1 , name : 'NodeIdAndTime1' } ) ;
obj . powerfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expirePowerEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expirePowerEventsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting power events expire index...' ) ;
obj . powerfile . dropIndex ( "ExpireTime1" , function ( err ) {
// Reset the expire power events index
obj . powerfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expirePowerEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
// Setup MongoDB smbios collection, no indexes needed
obj . smbiosfile = db . collection ( 'smbios' ) ; // Collection containing all smbios information
// Setup MongoDB server stats collection
obj . serverstatsfile = db . collection ( 'serverstats' ) ; // Collection of server stats
2019-05-09 23:41:14 +03:00
obj . serverstatsfile . indexes ( function ( err , indexes ) {
2019-05-09 04:14:30 +03:00
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 3 ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
// Reset all indexes
console . log ( 'Resetting server stats indexes...' ) ;
obj . serverstatsfile . dropIndexes ( function ( err ) {
// Create all indexes
obj . serverstatsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireServerStatsSeconds , name : 'ExpireTime1' } ) ;
obj . serverstatsfile . createIndex ( { "expire" : 1 } , { expireAfterSeconds : 0 , name : 'ExpireTime2' } ) ; // Auto-expire events
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expireServerStatsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting server stats expire index...' ) ;
obj . serverstatsfile . dropIndex ( "ExpireTime1" , function ( err ) {
// Reset the expire server stats index
obj . serverstatsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireServerStatsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
2019-11-26 01:12:43 +03:00
2019-10-30 11:17:17 +03:00
// Setup plugin info collection
2019-11-26 01:12:43 +03:00
if ( parent . config . settings != null ) { obj . pluginsfile = db . collection ( 'plugins' ) ; }
2019-05-09 04:14:30 +03:00
2019-05-09 23:41:14 +03:00
setupFunctions ( func ) ; // Completed setup of MongoDB
2019-05-09 04:14:30 +03:00
} ) ;
2019-05-24 20:33:40 +03:00
} else if ( parent . args . xmongodb ) {
2019-05-21 04:03:14 +03:00
// Use MongoJS, this is the old system.
2017-08-28 19:27:45 +03:00
obj . databaseType = 2 ;
2018-08-30 03:40:30 +03:00
Datastore = require ( 'mongojs' ) ;
2019-05-24 20:33:40 +03:00
var db = Datastore ( parent . args . xmongodb ) ;
2017-08-28 19:27:45 +03:00
var dbcollection = 'meshcentral' ;
2019-05-24 20:33:40 +03:00
if ( parent . args . mongodbcol ) { dbcollection = parent . args . mongodbcol ; }
2019-02-19 01:32:55 +03:00
// Setup MongoDB main collection and indexes
2017-08-28 19:27:45 +03:00
obj . file = db . collection ( dbcollection ) ;
2019-02-19 01:32:55 +03:00
obj . file . getIndexes ( function ( err , indexes ) {
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 4 ) || ( indexesByName [ 'TypeDomainMesh1' ] == null ) || ( indexesByName [ 'Email1' ] == null ) || ( indexesByName [ 'Mesh1' ] == null ) ) {
console . log ( 'Resetting main indexes...' ) ;
obj . file . dropIndexes ( function ( err ) {
obj . file . createIndex ( { type : 1 , domain : 1 , meshid : 1 } , { sparse : 1 , name : 'TypeDomainMesh1' } ) ; // Speeds up GetAllTypeNoTypeField() and GetAllTypeNoTypeFieldMeshFiltered()
obj . file . createIndex ( { email : 1 } , { sparse : 1 , name : 'Email1' } ) ; // Speeds up GetUserWithEmail() and GetUserWithVerifiedEmail()
obj . file . createIndex ( { meshid : 1 } , { sparse : 1 , name : 'Mesh1' } ) ; // Speeds up RemoveMesh()
} ) ;
}
} ) ;
// Setup MongoDB events collection and indexes
2019-02-19 05:29:13 +03:00
obj . eventsfile = db . collection ( 'events' ) ; // Collection containing all events
2019-02-19 01:32:55 +03:00
obj . eventsfile . getIndexes ( function ( err , indexes ) {
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
2019-02-21 02:26:27 +03:00
if ( ( indexCount != 5 ) || ( indexesByName [ 'Username1' ] == null ) || ( indexesByName [ 'DomainNodeTime1' ] == null ) || ( indexesByName [ 'IdsAndTime1' ] == null ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
2019-02-19 01:32:55 +03:00
// Reset all indexes
console . log ( 'Resetting events indexes...' ) ;
obj . eventsfile . dropIndexes ( function ( err ) {
2019-02-21 02:26:27 +03:00
obj . eventsfile . createIndex ( { username : 1 } , { sparse : 1 , name : 'Username1' } ) ;
obj . eventsfile . createIndex ( { domain : 1 , nodeid : 1 , time : - 1 } , { sparse : 1 , name : 'DomainNodeTime1' } ) ;
2019-02-19 01:32:55 +03:00
obj . eventsfile . createIndex ( { ids : 1 , time : - 1 } , { sparse : 1 , name : 'IdsAndTime1' } ) ;
obj . eventsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expireEventsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting events expire index...' ) ;
obj . eventsfile . dropIndex ( "ExpireTime1" , function ( err ) {
obj . eventsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
// Setup MongoDB power events collection and indexes
2019-02-19 05:29:13 +03:00
obj . powerfile = db . collection ( 'power' ) ; // Collection containing all power events
2019-02-19 01:32:55 +03:00
obj . powerfile . getIndexes ( function ( err , indexes ) {
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 3 ) || ( indexesByName [ 'NodeIdAndTime1' ] == null ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
// Reset all indexes
console . log ( 'Resetting power events indexes...' ) ;
obj . powerfile . dropIndexes ( function ( err ) {
// Create all indexes
obj . powerfile . createIndex ( { nodeid : 1 , time : 1 } , { sparse : 1 , name : 'NodeIdAndTime1' } ) ;
obj . powerfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expirePowerEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expirePowerEventsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting power events expire index...' ) ;
obj . powerfile . dropIndex ( "ExpireTime1" , function ( err ) {
// Reset the expire power events index
obj . powerfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expirePowerEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
2019-02-16 23:56:33 +03:00
2019-02-25 22:13:13 +03:00
// Setup MongoDB smbios collection, no indexes needed
obj . smbiosfile = db . collection ( 'smbios' ) ; // Collection containing all smbios information
2019-03-26 05:59:04 +03:00
// Setup MongoDB server stats collection
obj . serverstatsfile = db . collection ( 'serverstats' ) ; // Collection of server stats
obj . serverstatsfile . getIndexes ( function ( err , indexes ) {
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
2019-03-27 00:11:51 +03:00
if ( ( indexCount != 3 ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
2019-03-26 05:59:04 +03:00
// Reset all indexes
console . log ( 'Resetting server stats indexes...' ) ;
obj . serverstatsfile . dropIndexes ( function ( err ) {
// Create all indexes
obj . serverstatsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireServerStatsSeconds , name : 'ExpireTime1' } ) ;
2019-03-27 00:11:51 +03:00
obj . serverstatsfile . createIndex ( { "expire" : 1 } , { expireAfterSeconds : 0 , name : 'ExpireTime2' } ) ; // Auto-expire events
2019-03-26 05:59:04 +03:00
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expireServerStatsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting server stats expire index...' ) ;
obj . serverstatsfile . dropIndex ( "ExpireTime1" , function ( err ) {
// Reset the expire server stats index
obj . serverstatsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireServerStatsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
2019-10-30 11:17:17 +03:00
// Setup plugin info collection
2019-11-26 01:12:43 +03:00
if ( parent . config . settings != null ) { obj . pluginsfile = db . collection ( 'plugins' ) ; }
2019-05-09 04:14:30 +03:00
2019-05-09 23:41:14 +03:00
setupFunctions ( func ) ; // Completed setup of MongoJS
2017-08-28 19:27:45 +03:00
} else {
// Use NeDB (The default)
obj . databaseType = 1 ;
2018-08-30 03:40:30 +03:00
Datastore = require ( 'nedb' ) ;
2019-05-24 20:33:40 +03:00
var datastoreOptions = { filename : parent . getConfigFilePath ( 'meshcentral.db' ) , autoload : true } ;
2019-01-24 23:08:48 +03:00
// If a DB encryption key is provided, perform database encryption
2019-05-24 20:33:40 +03:00
if ( ( typeof parent . args . dbencryptkey == 'string' ) && ( parent . args . dbencryptkey . length != 0 ) ) {
2019-01-24 23:08:48 +03:00
// Hash the database password into a AES256 key and setup encryption and decryption.
2019-05-24 20:33:40 +03:00
obj . dbKey = parent . crypto . createHash ( 'sha384' ) . update ( parent . args . dbencryptkey ) . digest ( "raw" ) . slice ( 0 , 32 ) ;
2019-01-24 23:08:48 +03:00
datastoreOptions . afterSerialization = function ( plaintext ) {
2019-05-24 20:33:40 +03:00
const iv = parent . crypto . randomBytes ( 16 ) ;
const aes = parent . crypto . createCipheriv ( 'aes-256-cbc' , obj . dbKey , iv ) ;
2019-01-24 23:08:48 +03:00
var ciphertext = aes . update ( plaintext ) ;
ciphertext = Buffer . concat ( [ iv , ciphertext , aes . final ( ) ] ) ;
return ciphertext . toString ( 'base64' ) ;
}
datastoreOptions . beforeDeserialization = function ( ciphertext ) {
const ciphertextBytes = Buffer . from ( ciphertext , 'base64' ) ;
const iv = ciphertextBytes . slice ( 0 , 16 ) ;
const data = ciphertextBytes . slice ( 16 ) ;
2019-05-24 20:33:40 +03:00
const aes = parent . crypto . createDecipheriv ( 'aes-256-cbc' , obj . dbKey , iv ) ;
2019-01-24 23:08:48 +03:00
var plaintextBytes = Buffer . from ( aes . update ( data ) ) ;
plaintextBytes = Buffer . concat ( [ plaintextBytes , aes . final ( ) ] ) ;
return plaintextBytes . toString ( ) ;
}
}
2019-02-19 01:32:55 +03:00
// Start NeDB main collection and setup indexes
2019-01-24 23:08:48 +03:00
obj . file = new Datastore ( datastoreOptions ) ;
2019-02-19 01:32:55 +03:00
obj . file . persistence . setAutocompactionInterval ( 36000 ) ;
2019-02-16 23:56:33 +03:00
obj . file . ensureIndex ( { fieldName : 'type' } ) ;
obj . file . ensureIndex ( { fieldName : 'domain' } ) ;
2019-02-19 01:32:55 +03:00
obj . file . ensureIndex ( { fieldName : 'meshid' , sparse : true } ) ;
obj . file . ensureIndex ( { fieldName : 'nodeid' , sparse : true } ) ;
obj . file . ensureIndex ( { fieldName : 'email' , sparse : true } ) ;
// Setup the events collection and setup indexes
2019-05-24 20:33:40 +03:00
obj . eventsfile = new Datastore ( { filename : parent . getConfigFilePath ( 'meshcentral-events.db' ) , autoload : true } ) ;
2019-02-19 01:32:55 +03:00
obj . eventsfile . persistence . setAutocompactionInterval ( 36000 ) ;
obj . eventsfile . ensureIndex ( { fieldName : 'ids' } ) ; // TODO: Not sure if this is a good index, this is a array field.
obj . eventsfile . ensureIndex ( { fieldName : 'nodeid' , sparse : true } ) ;
obj . eventsfile . ensureIndex ( { fieldName : 'time' , expireAfterSeconds : 60 * 60 * 24 * 20 } ) ; // Limit the power event log to 20 days (Seconds * Minutes * Hours * Days)
// Setup the power collection and setup indexes
2019-05-24 20:33:40 +03:00
obj . powerfile = new Datastore ( { filename : parent . getConfigFilePath ( 'meshcentral-power.db' ) , autoload : true } ) ;
2019-02-19 01:32:55 +03:00
obj . powerfile . persistence . setAutocompactionInterval ( 36000 ) ;
obj . powerfile . ensureIndex ( { fieldName : 'nodeid' } ) ;
obj . powerfile . ensureIndex ( { fieldName : 'time' , expireAfterSeconds : 60 * 60 * 24 * 10 } ) ; // Limit the power event log to 10 days (Seconds * Minutes * Hours * Days)
2019-02-25 22:13:13 +03:00
// Setup the SMBIOS collection
2019-05-24 20:33:40 +03:00
obj . smbiosfile = new Datastore ( { filename : parent . getConfigFilePath ( 'meshcentral-smbios.db' ) , autoload : true } ) ;
2019-03-26 05:59:04 +03:00
// Setup the server stats collection and setup indexes
2019-05-24 20:33:40 +03:00
obj . serverstatsfile = new Datastore ( { filename : parent . getConfigFilePath ( 'meshcentral-stats.db' ) , autoload : true } ) ;
2019-03-26 05:59:04 +03:00
obj . serverstatsfile . persistence . setAutocompactionInterval ( 36000 ) ;
obj . serverstatsfile . ensureIndex ( { fieldName : 'time' , expireAfterSeconds : 60 * 60 * 24 * 30 } ) ; // Limit the server stats log to 30 days (Seconds * Minutes * Hours * Days)
2019-03-27 00:11:51 +03:00
obj . serverstatsfile . ensureIndex ( { fieldName : 'expire' , expireAfterSeconds : 0 } ) ; // Auto-expire events
2019-02-02 02:16:46 +03:00
2019-10-30 11:17:17 +03:00
// Setup plugin info collection
2019-11-26 01:12:43 +03:00
if ( parent . config . settings != null ) {
obj . pluginsfile = new Datastore ( { filename : parent . getConfigFilePath ( 'meshcentral-plugins.db' ) , autoload : true } ) ;
obj . pluginsfile . persistence . setAutocompactionInterval ( 36000 ) ;
}
2019-10-30 11:17:17 +03:00
2019-05-09 23:41:14 +03:00
setupFunctions ( func ) ; // Completed setup of NeDB
}
2019-10-10 23:46:50 +03:00
// Check the object names for a "."
2019-10-11 00:06:14 +03:00
function checkObjectNames ( r , tag ) {
2019-10-10 23:46:50 +03:00
if ( typeof r != 'object' ) return ;
for ( var i in r ) {
2019-10-11 00:06:14 +03:00
if ( i . indexOf ( '.' ) >= 0 ) { throw ( 'BadDbName (' + tag + '): ' + JSON . stringify ( r ) ) ; }
checkObjectNames ( r [ i ] , tag ) ;
2019-10-10 23:46:50 +03:00
}
}
2019-05-09 23:41:14 +03:00
function setupFunctions ( func ) {
if ( obj . databaseType == 3 ) {
// Database actions on the main collection (MongoDB)
2019-12-09 22:30:08 +03:00
obj . Set = function ( data , func ) { obj . file . replaceOne ( { _id : data . _id } , performTypedRecordEncrypt ( data ) , { upsert : true } , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . Get = function ( id , func ) {
if ( arguments . length > 2 ) {
var parms = [ func ] ;
for ( var parmx = 2 ; parmx < arguments . length ; ++ parmx ) { parms . push ( arguments [ parmx ] ) ; }
var func2 = function _func2 ( arg1 , arg2 ) {
var userCallback = _func2 . userArgs . shift ( ) ;
_func2 . userArgs . unshift ( arg2 ) ;
_func2 . userArgs . unshift ( arg1 ) ;
userCallback . apply ( obj , _func2 . userArgs ) ;
} ;
func2 . userArgs = parms ;
2019-10-03 03:19:33 +03:00
obj . file . find ( { _id : id } ) . toArray ( function ( err , docs ) { func2 ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ;
2019-05-09 23:41:14 +03:00
} else {
2019-10-03 03:19:33 +03:00
obj . file . find ( { _id : id } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ;
2019-05-09 23:41:14 +03:00
}
} ;
2019-10-03 03:19:33 +03:00
obj . GetAll = function ( func ) { obj . file . find ( { } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetHash = function ( id , func ) { obj . file . find ( { _id : id } ) . project ( { _id : 0 , hash : 1 } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetAllTypeNoTypeField = function ( type , domain , func ) { obj . file . find ( { type : type , domain : domain } ) . project ( { type : 0 } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetAllTypeNoTypeFieldMeshFiltered = function ( meshes , domain , type , id , func ) { var x = { type : type , domain : domain , meshid : { $in : meshes } } ; if ( id ) { x . _id = id ; } obj . file . find ( x , { type : 0 } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetAllType = function ( type , func ) { obj . file . find ( { type : type } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetAllIdsOfType = function ( ids , domain , type , func ) { obj . file . find ( { type : type , domain : domain , _id : { $in : ids } } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetUserWithEmail = function ( domain , email , func ) { obj . file . find ( { type : 'user' , domain : domain , email : email } ) . project ( { type : 0 } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetUserWithVerifiedEmail = function ( domain , email , func ) { obj . file . find ( { type : 'user' , domain : domain , email : email , emailVerified : true } ) . project ( { type : 0 } ) . toArray ( function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
2019-05-09 23:41:14 +03:00
obj . Remove = function ( id ) { obj . file . deleteOne ( { _id : id } ) ; } ;
obj . RemoveAll = function ( func ) { obj . file . deleteMany ( { } , { multi : true } , func ) ; } ;
obj . RemoveAllOfType = function ( type , func ) { obj . file . deleteMany ( { type : type } , { multi : true } , func ) ; } ;
2019-12-09 22:30:08 +03:00
obj . InsertMany = function ( data , func ) { obj . file . insertMany ( data , func ) ; } ;
2019-05-21 04:03:14 +03:00
obj . RemoveMeshDocuments = function ( id ) { obj . file . deleteMany ( { meshid : id } , { multi : true } ) ; obj . file . deleteOne ( { _id : 'nt' + id } ) ; } ;
2019-12-09 22:30:08 +03:00
obj . MakeSiteAdmin = function ( username , domain ) { obj . Get ( 'user/' + domain + '/' + username , function ( err , docs ) { if ( docs . length == 1 ) { docs [ 0 ] . siteadmin = 0xFFFFFFFF ; obj . Set ( docs [ 0 ] ) ; } } ) ; } ;
2019-05-09 23:41:14 +03:00
obj . DeleteDomain = function ( domain , func ) { obj . file . deleteMany ( { domain : domain } , { multi : true } , func ) ; } ;
2019-12-10 00:43:02 +03:00
obj . SetUser = function ( user ) { if ( user . subscriptions != null ) { var u = Clone ( user ) ; if ( u . subscriptions ) { delete u . subscriptions ; } obj . Set ( u ) ; } else { obj . Set ( user ) ; } } ;
2019-05-09 23:41:14 +03:00
obj . dispose = function ( ) { for ( var x in obj ) { if ( obj [ x ] . close ) { obj [ x ] . close ( ) ; } delete obj [ x ] ; } } ;
obj . getLocalAmtNodes = function ( func ) { obj . file . find ( { type : 'node' , host : { $exists : true , $ne : null } , intelamt : { $exists : true } } ) . toArray ( func ) ; } ;
2019-10-11 02:07:32 +03:00
obj . getAmtUuidMeshNode = function ( meshid , uuid , func ) { obj . file . find ( { type : 'node' , meshid : meshid , 'intelamt.uuid' : uuid } ) . toArray ( func ) ; } ;
obj . getAmtUuidNode = function ( uuid , func ) { obj . file . find ( { type : 'node' , 'intelamt.uuid' : uuid } ) . toArray ( func ) ; } ;
2019-05-30 22:40:10 +03:00
// TODO: Starting in MongoDB 4.0.3, you should use countDocuments() instead of count() that is deprecated. We should detect MongoDB version and switch.
// https://docs.mongodb.com/manual/reference/method/db.collection.countDocuments/
//obj.isMaxType = function (max, type, domainid, func) { if (max == null) { func(false); } else { obj.file.countDocuments({ type: type, domain: domainid }, function (err, count) { func((err != null) || (count > max)); }); } }
2019-11-19 04:31:42 +03:00
obj . isMaxType = function ( max , type , domainid , func ) {
if ( obj . eventsfile . countDocuments ) {
if ( max == null ) { func ( false ) ; } else { obj . file . countDocuments ( { type : type , domain : domainid } , function ( err , count ) { func ( ( err != null ) || ( count > max ) , count ) ; } ) ; }
} else {
if ( max == null ) { func ( false ) ; } else { obj . file . count ( { type : type , domain : domainid } , function ( err , count ) { func ( ( err != null ) || ( count > max ) , count ) ; } ) ; }
}
}
2019-05-09 23:41:14 +03:00
// Database actions on the events collection
obj . GetAllEvents = function ( func ) { obj . eventsfile . find ( { } ) . toArray ( func ) ; } ;
2019-10-10 23:46:50 +03:00
obj . StoreEvent = function ( event ) {
2019-10-11 00:06:14 +03:00
checkObjectNames ( event , 'x5' ) ; // DEBUG CHECKING
2019-10-10 23:46:50 +03:00
obj . eventsfile . insertOne ( event ) ;
} ;
2019-08-01 02:49:23 +03:00
obj . GetEvents = function ( ids , domain , func ) { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } ) . project ( { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . toArray ( func ) ; } ;
obj . GetEventsWithLimit = function ( ids , domain , limit , func ) { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } ) . project ( { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . toArray ( func ) ; } ;
obj . GetUserEvents = function ( ids , domain , username , func ) { obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } ) . project ( { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . toArray ( func ) ; } ;
obj . GetUserEventsWithLimit = function ( ids , domain , username , limit , func ) { obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } ) . project ( { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . toArray ( func ) ; } ;
obj . GetNodeEventsWithLimit = function ( nodeid , domain , limit , func ) { obj . eventsfile . find ( { domain : domain , nodeid : nodeid } ) . project ( { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . toArray ( func ) ; } ;
2019-09-18 22:05:33 +03:00
obj . GetNodeEventsSelfWithLimit = function ( nodeid , domain , userid , limit , func ) { obj . eventsfile . find ( { domain : domain , nodeid : nodeid , userid : { $in : [ userid , null ] } } ) . project ( { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . toArray ( func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . RemoveAllEvents = function ( domain ) { obj . eventsfile . deleteMany ( { domain : domain } , { multi : true } ) ; } ;
obj . RemoveAllNodeEvents = function ( domain , nodeid ) { obj . eventsfile . deleteMany ( { domain : domain , nodeid : nodeid } , { multi : true } ) ; } ;
2019-12-27 00:52:09 +03:00
obj . RemoveAllUserEvents = function ( domain , userid ) { obj . eventsfile . deleteMany ( { domain : domain , userid : userid } , { multi : true } ) ; } ;
2019-11-19 04:31:42 +03:00
obj . GetFailedLoginCount = function ( username , domainid , lastlogin , func ) {
if ( obj . eventsfile . countDocuments ) {
obj . eventsfile . countDocuments ( { action : 'authfail' , username : username , domain : domainid , time : { "$gte" : lastlogin } } , function ( err , count ) { func ( ( err == null ) ? count : 0 ) ; } ) ;
} else {
obj . eventsfile . count ( { action : 'authfail' , username : username , domain : domainid , time : { "$gte" : lastlogin } } , function ( err , count ) { func ( ( err == null ) ? count : 0 ) ; } ) ;
}
}
2019-05-09 23:41:14 +03:00
// Database actions on the power collection
obj . getAllPower = function ( func ) { obj . powerfile . find ( { } ) . toArray ( func ) ; } ;
2019-12-09 22:30:08 +03:00
obj . storePowerEvent = function ( event , multiServer , func ) { if ( multiServer != null ) { event . server = multiServer . serverid ; } obj . powerfile . insertOne ( event , func ) ; } ;
2019-08-01 02:49:23 +03:00
obj . getPowerTimeline = function ( nodeid , func ) { obj . powerfile . find ( { nodeid : { $in : [ '*' , nodeid ] } } ) . project ( { _id : 0 , nodeid : 0 , s : 0 } ) . sort ( { time : 1 } ) . toArray ( func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . removeAllPowerEvents = function ( ) { obj . powerfile . deleteMany ( { } , { multi : true } ) ; } ;
obj . removeAllPowerEventsForNode = function ( nodeid ) { obj . powerfile . deleteMany ( { nodeid : nodeid } , { multi : true } ) ; } ;
// Database actions on the SMBIOS collection
2019-12-09 07:46:25 +03:00
obj . GetAllSMBIOS = function ( func ) { obj . smbiosfile . find ( { } ) . toArray ( func ) ; } ;
2019-12-09 22:30:08 +03:00
obj . SetSMBIOS = function ( smbios , func ) { obj . smbiosfile . updateOne ( { _id : smbios . _id } , { $set : smbios } , { upsert : true } , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . RemoveSMBIOS = function ( id ) { obj . smbiosfile . deleteOne ( { _id : id } ) ; } ;
obj . GetSMBIOS = function ( id , func ) { obj . smbiosfile . find ( { _id : id } ) . toArray ( func ) ; } ;
// Database actions on the Server Stats collection
2019-12-09 22:30:08 +03:00
obj . SetServerStats = function ( data , func ) { obj . serverstatsfile . insertOne ( data , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . GetServerStats = function ( hours , func ) { var t = new Date ( ) ; t . setTime ( t . getTime ( ) - ( 60 * 60 * 1000 * hours ) ) ; obj . serverstatsfile . find ( { time : { $gt : t } } , { _id : 0 , cpu : 0 } ) . toArray ( func ) ; } ;
// Read a configuration file from the database
obj . getConfigFile = function ( path , func ) { obj . Get ( 'cfile/' + path , func ) ; }
// Write a configuration file to the database
obj . setConfigFile = function ( path , data , func ) { obj . Set ( { _id : 'cfile/' + path , type : 'cfile' , data : data . toString ( 'base64' ) } , func ) ; }
// List all configuration files
obj . listConfigFiles = function ( func ) { obj . file . find ( { type : 'cfile' } ) . sort ( { _id : 1 } ) . toArray ( func ) ; }
// Get all configuration files
obj . getAllConfigFiles = function ( password , func ) {
obj . file . find ( { type : 'cfile' } ) . toArray ( function ( err , docs ) {
if ( err != null ) { func ( null ) ; return ; }
var r = null ;
for ( var i = 0 ; i < docs . length ; i ++ ) {
var name = docs [ i ] . _id . split ( '/' ) [ 1 ] ;
var data = obj . decryptData ( password , docs [ i ] . data ) ;
if ( data != null ) { if ( r == null ) { r = { } ; } r [ name ] = data ; }
}
func ( r ) ;
} ) ;
}
2019-11-26 01:12:43 +03:00
2019-12-09 07:46:25 +03:00
// Get database information
obj . getDbStats = function ( func ) {
obj . stats = { c : 6 } ;
obj . getStats ( function ( r ) { obj . stats . recordTypes = r ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } )
2019-12-09 22:30:08 +03:00
obj . file . stats ( ) . then ( function ( stats ) { obj . stats [ stats . ns ] = { size : stats . size , count : stats . count , avgObjSize : stats . avgObjSize , capped : stats . capped } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } , function ( ) { if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . eventsfile . stats ( ) . then ( function ( stats ) { obj . stats [ stats . ns ] = { size : stats . size , count : stats . count , avgObjSize : stats . avgObjSize , capped : stats . capped } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } , function ( ) { if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . powerfile . stats ( ) . then ( function ( stats ) { obj . stats [ stats . ns ] = { size : stats . size , count : stats . count , avgObjSize : stats . avgObjSize , capped : stats . capped } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } , function ( ) { if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . smbiosfile . stats ( ) . then ( function ( stats ) { obj . stats [ stats . ns ] = { size : stats . size , count : stats . count , avgObjSize : stats . avgObjSize , capped : stats . capped } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } , function ( ) { if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . serverstatsfile . stats ( ) . then ( function ( stats ) { obj . stats [ stats . ns ] = { size : stats . size , count : stats . count , avgObjSize : stats . avgObjSize , capped : stats . capped } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } , function ( ) { if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
2019-12-09 07:46:25 +03:00
}
2019-11-26 01:12:43 +03:00
// Plugin operations
if ( parent . config . settings . plugins != null ) {
obj . addPlugin = function ( plugin , func ) { plugin . type = "plugin" ; obj . pluginsfile . insertOne ( plugin , func ) ; } ; // Add a plugin
obj . getPlugins = function ( func ) { obj . pluginsfile . find ( { "type" : "plugin" } ) . project ( { "type" : 0 } ) . sort ( { name : 1 } ) . toArray ( func ) ; } ; // Get all plugins
obj . getPlugin = function ( id , func ) { id = require ( 'mongodb' ) . ObjectID ( id ) ; obj . pluginsfile . find ( { _id : id } ) . sort ( { name : 1 } ) . toArray ( func ) ; } ; // Get plugin
obj . deletePlugin = function ( id , func ) { id = require ( 'mongodb' ) . ObjectID ( id ) ; obj . pluginsfile . deleteOne ( { _id : id } , func ) ; } ; // Delete plugin
obj . setPluginStatus = function ( id , status , func ) { id = require ( 'mongodb' ) . ObjectID ( id ) ; obj . pluginsfile . updateOne ( { _id : id } , { $set : { status : status } } , func ) ; } ;
obj . updatePlugin = function ( id , args , func ) { delete args . _id ; id = require ( 'mongodb' ) . ObjectID ( id ) ; obj . pluginsfile . updateOne ( { _id : id } , { $set : args } , func ) ; } ;
}
2019-11-22 22:25:13 +03:00
2019-05-09 23:41:14 +03:00
} else {
// Database actions on the main collection (NeDB and MongoJS)
2019-12-09 22:30:08 +03:00
obj . Set = function ( data , func ) { var xdata = performTypedRecordEncrypt ( data ) ; obj . file . update ( { _id : xdata . _id } , xdata , { upsert : true } , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . Get = function ( id , func ) {
if ( arguments . length > 2 ) {
var parms = [ func ] ;
for ( var parmx = 2 ; parmx < arguments . length ; ++ parmx ) { parms . push ( arguments [ parmx ] ) ; }
var func2 = function _func2 ( arg1 , arg2 ) {
var userCallback = _func2 . userArgs . shift ( ) ;
_func2 . userArgs . unshift ( arg2 ) ;
_func2 . userArgs . unshift ( arg1 ) ;
userCallback . apply ( obj , _func2 . userArgs ) ;
} ;
func2 . userArgs = parms ;
2019-10-03 03:19:33 +03:00
obj . file . find ( { _id : id } , function ( err , docs ) { func2 ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ;
} else {
obj . file . find ( { _id : id } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ;
2019-05-09 23:41:14 +03:00
}
} ;
2019-10-03 03:19:33 +03:00
obj . GetAll = function ( func ) { obj . file . find ( { } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
2019-08-07 03:58:29 +03:00
obj . GetHash = function ( id , func ) { obj . file . find ( { _id : id } , { _id : 0 , hash : 1 } , func ) ; } ;
2019-10-03 03:19:33 +03:00
obj . GetAllTypeNoTypeField = function ( type , domain , func ) { obj . file . find ( { type : type , domain : domain } , { type : 0 } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetAllTypeNoTypeFieldMeshFiltered = function ( meshes , domain , type , id , func ) { var x = { type : type , domain : domain , meshid : { $in : meshes } } ; if ( id ) { x . _id = id ; } obj . file . find ( x , { type : 0 } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetAllType = function ( type , func ) { obj . file . find ( { type : type } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetAllIdsOfType = function ( ids , domain , type , func ) { obj . file . find ( { type : type , domain : domain , _id : { $in : ids } } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetUserWithEmail = function ( domain , email , func ) { obj . file . find ( { type : 'user' , domain : domain , email : email } , { type : 0 } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
obj . GetUserWithVerifiedEmail = function ( domain , email , func ) { obj . file . find ( { type : 'user' , domain : domain , email : email , emailVerified : true } , { type : 0 } , function ( err , docs ) { func ( err , performTypedRecordDecrypt ( docs ) ) ; } ) ; } ;
2019-05-09 23:41:14 +03:00
obj . Remove = function ( id ) { obj . file . remove ( { _id : id } ) ; } ;
obj . RemoveAll = function ( func ) { obj . file . remove ( { } , { multi : true } , func ) ; } ;
obj . RemoveAllOfType = function ( type , func ) { obj . file . remove ( { type : type } , { multi : true } , func ) ; } ;
2019-12-09 22:30:08 +03:00
obj . InsertMany = function ( data , func ) { obj . file . insert ( data , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . RemoveMeshDocuments = function ( id ) { obj . file . remove ( { meshid : id } , { multi : true } ) ; obj . file . remove ( { _id : 'nt' + id } ) ; } ;
2019-12-09 22:30:08 +03:00
obj . MakeSiteAdmin = function ( username , domain ) { obj . Get ( 'user/' + domain + '/' + username , function ( err , docs ) { if ( docs . length == 1 ) { docs [ 0 ] . siteadmin = 0xFFFFFFFF ; obj . Set ( docs [ 0 ] ) ; } } ) ; } ;
2019-05-09 23:41:14 +03:00
obj . DeleteDomain = function ( domain , func ) { obj . file . remove ( { domain : domain } , { multi : true } , func ) ; } ;
2019-12-10 00:43:02 +03:00
obj . SetUser = function ( user ) { if ( user . subscriptions != null ) { var u = Clone ( user ) ; if ( u . subscriptions ) { delete u . subscriptions ; } obj . Set ( u ) ; } else { obj . Set ( user ) ; } } ;
2019-05-09 23:41:14 +03:00
obj . dispose = function ( ) { for ( var x in obj ) { if ( obj [ x ] . close ) { obj [ x ] . close ( ) ; } delete obj [ x ] ; } } ;
obj . getLocalAmtNodes = function ( func ) { obj . file . find ( { type : 'node' , host : { $exists : true , $ne : null } , intelamt : { $exists : true } } , func ) ; } ;
2019-10-11 02:07:32 +03:00
obj . getAmtUuidMeshNode = function ( meshid , uuid , func ) { obj . file . find ( { type : 'node' , meshid : meshid , 'intelamt.uuid' : uuid } , func ) ; } ;
obj . getAmtUuidNode = function ( uuid , func ) { obj . file . find ( { type : 'node' , 'intelamt.uuid' : uuid } , func ) ; } ;
2019-06-06 01:24:07 +03:00
obj . isMaxType = function ( max , type , domainid , func ) { if ( max == null ) { func ( false ) ; } else { obj . file . count ( { type : type , domain : domainid } , function ( err , count ) { func ( ( err != null ) || ( count > max ) , count ) ; } ) ; } }
2019-05-09 23:41:14 +03:00
// Database actions on the events collection
obj . GetAllEvents = function ( func ) { obj . eventsfile . find ( { } , func ) ; } ;
2019-12-09 22:30:08 +03:00
obj . StoreEvent = function ( event ) { obj . eventsfile . insert ( event ) ; } ;
2019-05-09 23:41:14 +03:00
obj . GetEvents = function ( ids , domain , func ) { if ( obj . databaseType == 1 ) { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . exec ( func ) ; } else { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } , func ) ; } } ;
obj . GetEventsWithLimit = function ( ids , domain , limit , func ) { if ( obj . databaseType == 1 ) { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . exec ( func ) ; } else { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit , func ) ; } } ;
obj . GetUserEvents = function ( ids , domain , username , func ) {
if ( obj . databaseType == 1 ) {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . exec ( func ) ;
} else {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } , func ) ;
}
} ;
obj . GetUserEventsWithLimit = function ( ids , domain , username , limit , func ) {
if ( obj . databaseType == 1 ) {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . exec ( func ) ;
} else {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit , func ) ;
}
} ;
obj . GetNodeEventsWithLimit = function ( nodeid , domain , limit , func ) { if ( obj . databaseType == 1 ) { obj . eventsfile . find ( { domain : domain , nodeid : nodeid } , { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . exec ( func ) ; } else { obj . eventsfile . find ( { domain : domain , nodeid : nodeid } , { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit , func ) ; } } ;
2019-09-18 22:05:33 +03:00
obj . GetNodeEventsSelfWithLimit = function ( nodeid , domain , userid , limit , func ) { if ( obj . databaseType == 1 ) { obj . eventsfile . find ( { domain : domain , nodeid : nodeid , userid : { $in : [ userid , null ] } } , { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . exec ( func ) ; } else { obj . eventsfile . find ( { domain : domain , nodeid : nodeid } , { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit , func ) ; } } ;
2019-05-09 23:41:14 +03:00
obj . RemoveAllEvents = function ( domain ) { obj . eventsfile . remove ( { domain : domain } , { multi : true } ) ; } ;
obj . RemoveAllNodeEvents = function ( domain , nodeid ) { obj . eventsfile . remove ( { domain : domain , nodeid : nodeid } , { multi : true } ) ; } ;
2019-12-27 00:52:09 +03:00
obj . RemoveAllUserEvents = function ( domain , userid ) { obj . eventsfile . remove ( { domain : domain , userid : userid } , { multi : true } ) ; } ;
2019-11-18 03:20:53 +03:00
obj . GetFailedLoginCount = function ( username , domainid , lastlogin , func ) { obj . eventsfile . count ( { action : 'authfail' , username : username , domain : domainid , time : { "$gte" : lastlogin } } , function ( err , count ) { func ( ( err == null ) ? count : 0 ) ; } ) ; }
2019-05-09 23:41:14 +03:00
// Database actions on the power collection
obj . getAllPower = function ( func ) { obj . powerfile . find ( { } , func ) ; } ;
2019-12-09 22:30:08 +03:00
obj . storePowerEvent = function ( event , multiServer , func ) { if ( multiServer != null ) { event . server = multiServer . serverid ; } obj . powerfile . insert ( event , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . getPowerTimeline = function ( nodeid , func ) { if ( obj . databaseType == 1 ) { obj . powerfile . find ( { nodeid : { $in : [ '*' , nodeid ] } } , { _id : 0 , nodeid : 0 , s : 0 } ) . sort ( { time : 1 } ) . exec ( func ) ; } else { obj . powerfile . find ( { nodeid : { $in : [ '*' , nodeid ] } } , { _id : 0 , nodeid : 0 , s : 0 } ) . sort ( { time : 1 } , func ) ; } } ;
obj . removeAllPowerEvents = function ( ) { obj . powerfile . remove ( { } , { multi : true } ) ; } ;
obj . removeAllPowerEventsForNode = function ( nodeid ) { obj . powerfile . remove ( { nodeid : nodeid } , { multi : true } ) ; } ;
// Database actions on the SMBIOS collection
2019-12-09 07:46:25 +03:00
obj . GetAllSMBIOS = function ( func ) { obj . smbiosfile . find ( { } , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . SetSMBIOS = function ( smbios , func ) { obj . smbiosfile . update ( { _id : smbios . _id } , smbios , { upsert : true } , func ) ; } ;
obj . RemoveSMBIOS = function ( id ) { obj . smbiosfile . remove ( { _id : id } ) ; } ;
obj . GetSMBIOS = function ( id , func ) { obj . smbiosfile . find ( { _id : id } , func ) ; } ;
// Database actions on the Server Stats collection
2019-12-09 22:30:08 +03:00
obj . SetServerStats = function ( data , func ) { obj . serverstatsfile . insert ( data , func ) ; } ;
2019-05-09 23:41:14 +03:00
obj . GetServerStats = function ( hours , func ) { var t = new Date ( ) ; t . setTime ( t . getTime ( ) - ( 60 * 60 * 1000 * hours ) ) ; obj . serverstatsfile . find ( { time : { $gt : t } } , { _id : 0 , cpu : 0 } , func ) ; } ;
// Read a configuration file from the database
obj . getConfigFile = function ( path , func ) { obj . Get ( 'cfile/' + path , func ) ; }
// Write a configuration file to the database
obj . setConfigFile = function ( path , data , func ) { obj . Set ( { _id : 'cfile/' + path , type : 'cfile' , data : data . toString ( 'base64' ) } , func ) ; }
// List all configuration files
obj . listConfigFiles = function ( func ) { obj . file . find ( { type : 'cfile' } ) . sort ( { _id : 1 } ) . exec ( func ) ; }
// Get all configuration files
obj . getAllConfigFiles = function ( password , func ) {
obj . file . find ( { type : 'cfile' } , function ( err , docs ) {
if ( err != null ) { func ( null ) ; return ; }
var r = null ;
for ( var i = 0 ; i < docs . length ; i ++ ) {
var name = docs [ i ] . _id . split ( '/' ) [ 1 ] ;
var data = obj . decryptData ( password , docs [ i ] . data ) ;
if ( data != null ) { if ( r == null ) { r = { } ; } r [ name ] = data ; }
}
func ( r ) ;
} ) ;
}
2019-11-26 01:12:43 +03:00
2019-12-09 07:46:25 +03:00
// Get database information
obj . getDbStats = function ( func ) {
obj . stats = { c : 6 } ;
obj . getStats ( function ( r ) { obj . stats . recordTypes = r ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } )
obj . file . count ( { } , function ( err , count ) { obj . stats . meshcentral = { count : count } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . eventsfile . count ( { } , function ( err , count ) { obj . stats . events = { count : count } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . powerfile . count ( { } , function ( err , count ) { obj . stats . power = { count : count } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . smbiosfile . count ( { } , function ( err , count ) { obj . stats . smbios = { count : count } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
obj . serverstatsfile . count ( { } , function ( err , count ) { obj . stats . serverstats = { count : count } ; if ( -- obj . stats . c == 0 ) { delete obj . stats . c ; func ( obj . stats ) ; } } ) ;
}
2019-11-26 01:12:43 +03:00
// Plugin operations
if ( parent . config . settings . plugins != null ) {
2019-12-09 07:46:25 +03:00
obj . addPlugin = function ( plugin , func ) { plugin . type = 'plugin' ; obj . pluginsfile . insert ( plugin , func ) ; } ; // Add a plugin
obj . getPlugins = function ( func ) { obj . pluginsfile . find ( { 'type' : 'plugin' } , { 'type' : 0 } ) . sort ( { name : 1 } ) . exec ( func ) ; } ; // Get all plugins
2019-11-26 01:12:43 +03:00
obj . getPlugin = function ( id , func ) { obj . pluginsfile . find ( { _id : id } ) . sort ( { name : 1 } ) . exec ( func ) ; } ; // Get plugin
obj . deletePlugin = function ( id , func ) { obj . pluginsfile . remove ( { _id : id } , func ) ; } ; // Delete plugin
obj . setPluginStatus = function ( id , status , func ) { obj . pluginsfile . update ( { _id : id } , { $set : { status : status } } , func ) ; } ;
obj . updatePlugin = function ( id , args , func ) { delete args . _id ; obj . pluginsfile . update ( { _id : id } , { $set : args } , func ) ; } ;
}
2019-11-22 22:25:13 +03:00
2019-05-09 23:41:14 +03:00
}
func ( obj ) ; // Completed function setup
2019-02-03 01:54:36 +03:00
}
2019-02-02 02:16:46 +03:00
2019-06-08 02:44:00 +03:00
// Return a human readable string with current backup configuration
obj . getBackupConfig = function ( ) {
var r = '' , backupPath = parent . backuppath ;
if ( parent . config . settings . autobackup && parent . config . settings . autobackup . backuppath ) { backupPath = parent . config . settings . autobackup . backuppath ; }
const dbname = ( parent . args . mongodbname ) ? ( parent . args . mongodbname ) : 'meshcentral' ;
const currentDate = new Date ( ) ;
const fileSuffix = currentDate . getFullYear ( ) + '-' + padNumber ( currentDate . getMonth ( ) + 1 , 2 ) + '-' + padNumber ( currentDate . getDate ( ) , 2 ) + '-' + padNumber ( currentDate . getHours ( ) , 2 ) + '-' + padNumber ( currentDate . getMinutes ( ) , 2 ) ;
const newAutoBackupFile = 'meshcentral-autobackup-' + fileSuffix ;
const newAutoBackupPath = parent . path . join ( backupPath , newAutoBackupFile ) ;
r += 'DB Name: ' + dbname + '\r\n' ;
r += 'DB Type: ' + [ 'None' , 'NeDB' , 'MongoJS' , 'MongoDB' ] [ obj . databaseType ] + '\r\n' ;
r += 'BackupPath: ' + backupPath + '\r\n' ;
r += 'newAutoBackupFile: ' + newAutoBackupFile + '\r\n' ;
r += 'newAutoBackupPath: ' + newAutoBackupPath + '\r\n' ;
if ( parent . config . settings . autobackup == null ) {
r += 'No Settings/AutoBackup\r\n' ;
} else {
if ( parent . config . settings . autobackup . backupintervalhours != null ) {
if ( typeof parent . config . settings . autobackup . backupintervalhours != 'number' ) { r += 'Bad backupintervalhours type\r\n' ; }
else { r += 'Backup Interval (Hours): ' + parent . config . settings . autobackup . backupintervalhours + '\r\n' ; }
}
if ( parent . config . settings . autobackup . keeplastdaysbackup != null ) {
if ( typeof parent . config . settings . autobackup . keeplastdaysbackup != 'number' ) { r += 'Bad keeplastdaysbackup type\r\n' ; }
else { r += 'Keep Last Backups (Days): ' + parent . config . settings . autobackup . keeplastdaysbackup + '\r\n' ; }
}
if ( parent . config . settings . autobackup . zippassword != null ) {
if ( typeof parent . config . settings . autobackup . zippassword != 'string' ) { r += 'Bad zippassword type\r\n' ; }
else { r += 'ZIP Password Set\r\n' ; }
}
if ( parent . config . settings . autobackup . mongodumppath != null ) {
if ( typeof parent . config . settings . autobackup . mongodumppath != 'string' ) { r += 'Bad mongodumppath type\r\n' ; }
else { r += 'MongoDump Path: ' + parent . config . settings . autobackup . mongodumppath + '\r\n' ; }
}
}
return r ;
}
2019-05-18 01:44:01 +03:00
obj . performingBackup = false ;
2019-05-17 22:40:15 +03:00
obj . performBackup = function ( ) {
2019-05-21 02:00:33 +03:00
try {
if ( obj . performingBackup ) return 1 ;
obj . performingBackup = true ;
//console.log('Performing backup...');
2019-05-23 01:22:10 +03:00
2019-05-24 20:33:40 +03:00
var backupPath = parent . backuppath ;
if ( parent . config . settings . autobackup && parent . config . settings . autobackup . backuppath ) { backupPath = parent . config . settings . autobackup . backuppath ; }
try { parent . fs . mkdirSync ( backupPath ) ; } catch ( e ) { }
const dbname = ( parent . args . mongodbname ) ? ( parent . args . mongodbname ) : 'meshcentral' ;
2019-05-21 02:00:33 +03:00
const currentDate = new Date ( ) ;
const fileSuffix = currentDate . getFullYear ( ) + '-' + padNumber ( currentDate . getMonth ( ) + 1 , 2 ) + '-' + padNumber ( currentDate . getDate ( ) , 2 ) + '-' + padNumber ( currentDate . getHours ( ) , 2 ) + '-' + padNumber ( currentDate . getMinutes ( ) , 2 ) ;
const newAutoBackupFile = 'meshcentral-autobackup-' + fileSuffix ;
2019-05-24 20:33:40 +03:00
const newAutoBackupPath = parent . path . join ( backupPath , newAutoBackupFile ) ;
2019-05-21 02:00:33 +03:00
if ( ( obj . databaseType == 2 ) || ( obj . databaseType == 3 ) ) {
// Perform a MongoDump backup
const newBackupFile = 'mongodump-' + fileSuffix ;
2019-05-24 20:33:40 +03:00
var newBackupPath = parent . path . join ( backupPath , newBackupFile ) ;
2019-05-21 02:00:33 +03:00
var mongoDumpPath = 'mongodump' ;
2019-05-24 20:33:40 +03:00
if ( parent . config . settings . autobackup && parent . config . settings . autobackup . mongodumppath ) { mongoDumpPath = parent . config . settings . autobackup . mongodumppath ; }
2019-05-21 02:00:33 +03:00
const child _process = require ( 'child_process' ) ;
2019-06-01 02:59:48 +03:00
const cmd = '\"' + mongoDumpPath + '\" --db \"' + dbname + '\" --archive=\"' + newBackupPath + '.archive\"' ;
2019-05-23 01:22:10 +03:00
var backupProcess = child _process . exec ( cmd , { cwd : backupPath } , function ( error , stdout , stderr ) {
2019-05-21 02:00:33 +03:00
try {
backupProcess = null ;
2019-05-23 01:22:10 +03:00
if ( ( error != null ) && ( error != '' ) ) { console . log ( 'ERROR: Unable to perform database backup: ' + error + '\r\n' ) ; obj . performingBackup = false ; return ; }
2019-05-21 02:00:33 +03:00
// Perform archive compression
var archiver = require ( 'archiver' ) ;
2019-05-24 20:33:40 +03:00
var output = parent . fs . createWriteStream ( newAutoBackupPath + '.zip' ) ;
2019-05-21 02:00:33 +03:00
var archive = null ;
2019-05-24 20:33:40 +03:00
if ( parent . config . settings . autobackup && ( typeof parent . config . settings . autobackup . zippassword == 'string' ) ) {
2019-05-21 02:00:33 +03:00
try { archiver . registerFormat ( 'zip-encrypted' , require ( "archiver-zip-encrypted" ) ) ; } catch ( ex ) { }
2019-05-24 20:33:40 +03:00
archive = archiver . create ( 'zip-encrypted' , { zlib : { level : 9 } , encryptionMethod : 'aes256' , password : parent . config . settings . autobackup . zippassword } ) ;
2019-05-21 02:00:33 +03:00
} else {
archive = archiver ( 'zip' , { zlib : { level : 9 } } ) ;
}
2019-05-24 20:33:40 +03:00
output . on ( 'close' , function ( ) { obj . performingBackup = false ; setTimeout ( function ( ) { try { parent . fs . unlink ( newBackupPath + '.archive' , function ( ) { } ) ; } catch ( ex ) { console . log ( ex ) ; } } , 5000 ) ; } ) ;
2019-05-21 02:00:33 +03:00
output . on ( 'end' , function ( ) { } ) ;
archive . on ( 'warning' , function ( err ) { console . log ( 'Backup warning: ' + err ) ; } ) ;
archive . on ( 'error' , function ( err ) { console . log ( 'Backup error: ' + err ) ; } ) ;
archive . pipe ( output ) ;
archive . file ( newBackupPath + '.archive' , { name : newBackupFile + '.archive' } ) ;
2019-05-24 20:33:40 +03:00
archive . directory ( parent . datapath , 'meshcentral-data' ) ;
2019-05-21 02:00:33 +03:00
archive . finalize ( ) ;
} catch ( ex ) { console . log ( ex ) ; }
} ) ;
} else {
// Perform a NeDB backup
2019-05-17 22:40:15 +03:00
var archiver = require ( 'archiver' ) ;
2019-05-24 20:33:40 +03:00
var output = parent . fs . createWriteStream ( newAutoBackupPath + '.zip' ) ;
2019-05-17 22:40:15 +03:00
var archive = null ;
2019-05-24 20:33:40 +03:00
if ( parent . config . settings . autobackup && ( typeof parent . config . settings . autobackup . zippassword == 'string' ) ) {
2019-05-17 22:40:15 +03:00
try { archiver . registerFormat ( 'zip-encrypted' , require ( "archiver-zip-encrypted" ) ) ; } catch ( ex ) { }
2019-05-24 20:33:40 +03:00
archive = archiver . create ( 'zip-encrypted' , { zlib : { level : 9 } , encryptionMethod : 'aes256' , password : parent . config . settings . autobackup . zippassword } ) ;
2019-05-17 22:40:15 +03:00
} else {
archive = archiver ( 'zip' , { zlib : { level : 9 } } ) ;
}
2019-05-21 02:00:33 +03:00
output . on ( 'close' , function ( ) { obj . performingBackup = false ; } ) ;
2019-05-17 22:40:15 +03:00
output . on ( 'end' , function ( ) { } ) ;
archive . on ( 'warning' , function ( err ) { console . log ( 'Backup warning: ' + err ) ; } ) ;
archive . on ( 'error' , function ( err ) { console . log ( 'Backup error: ' + err ) ; } ) ;
archive . pipe ( output ) ;
2019-05-24 20:33:40 +03:00
archive . directory ( parent . datapath , 'meshcentral-data' ) ;
2019-05-17 22:40:15 +03:00
archive . finalize ( ) ;
}
2019-05-21 02:00:33 +03:00
// Remove old backups
2019-05-24 20:33:40 +03:00
if ( parent . config . settings . autobackup && ( typeof parent . config . settings . autobackup . keeplastdaysbackup == 'number' ) ) {
2019-05-21 02:00:33 +03:00
var cutoffDate = new Date ( ) ;
2019-05-24 20:33:40 +03:00
cutoffDate . setDate ( cutoffDate . getDate ( ) - parent . config . settings . autobackup . keeplastdaysbackup ) ;
parent . fs . readdir ( parent . backuppath , function ( err , dir ) {
2019-05-21 02:00:33 +03:00
try {
if ( ( err == null ) && ( dir . length > 0 ) ) {
for ( var i in dir ) {
var name = dir [ i ] ;
if ( name . startsWith ( 'meshcentral-autobackup-' ) && name . endsWith ( '.zip' ) ) {
var timex = name . substring ( 23 , name . length - 4 ) . split ( '-' ) ;
if ( timex . length == 5 ) {
var fileDate = new Date ( parseInt ( timex [ 0 ] ) , parseInt ( timex [ 1 ] ) - 1 , parseInt ( timex [ 2 ] ) , parseInt ( timex [ 3 ] ) , parseInt ( timex [ 4 ] ) ) ;
2019-05-24 20:33:40 +03:00
if ( fileDate && ( cutoffDate > fileDate ) ) { try { parent . fs . unlink ( parent . path . join ( parent . backuppath , name ) , function ( ) { } ) ; } catch ( ex ) { } }
2019-05-21 02:00:33 +03:00
}
}
2019-05-17 22:40:15 +03:00
}
}
2019-05-21 02:00:33 +03:00
} catch ( ex ) { console . log ( ex ) ; }
} ) ;
}
} catch ( ex ) { console . log ( ex ) ; }
2019-05-18 01:44:01 +03:00
return 0 ;
2019-05-17 22:40:15 +03:00
}
function padNumber ( number , digits ) { return Array ( Math . max ( digits - String ( number ) . length + 1 , 0 ) ) . join ( 0 ) + number ; }
2019-05-29 03:25:23 +03:00
// Called when a node has changed
2019-05-30 00:36:14 +03:00
function dbNodeChange ( nodeChange , added ) {
2019-05-29 03:25:23 +03:00
const node = nodeChange . fullDocument ;
if ( node . intelamt && node . intelamt . pass ) { delete node . intelamt . pass ; } // Remove the Intel AMT password before eventing this.
2019-05-30 00:36:14 +03:00
parent . DispatchEvent ( [ '*' , node . meshid ] , obj , { etype : 'node' , action : ( added ? 'addnode' : 'changenode' ) , node : node , nodeid : node . _id , domain : node . domain , nolog : 1 } ) ;
2019-05-29 03:25:23 +03:00
}
// Called when a device group has changed
2019-05-30 00:36:14 +03:00
function dbMeshChange ( meshChange , added ) {
2019-07-15 20:24:31 +03:00
if ( parent . webserver == null ) return ;
2019-07-11 00:27:38 +03:00
common . unEscapeLinksFieldName ( meshChange . fullDocument ) ;
2019-05-29 03:25:23 +03:00
const mesh = meshChange . fullDocument ;
2019-05-31 21:38:35 +03:00
// Update the mesh object in memory
const mmesh = parent . webserver . meshes [ mesh . _id ] ;
for ( var i in mesh ) { mmesh [ i ] = mesh [ i ] ; }
for ( var i in mmesh ) { if ( mesh [ i ] == null ) { delete mmesh [ i ] ; } }
// Send the mesh update
2019-05-30 00:36:14 +03:00
if ( mesh . deleted ) { mesh . action = 'deletemesh' ; } else { mesh . action = ( added ? 'createmesh' : 'meshchange' ) ; }
2019-05-29 03:25:23 +03:00
mesh . meshid = mesh . _id ;
mesh . nolog = 1 ;
delete mesh . type ;
delete mesh . _id ;
2019-06-25 04:56:14 +03:00
if ( mesh . amt ) { delete mesh . amt . password ; } // Remove the Intel AMT password if present
2019-07-11 00:27:38 +03:00
parent . DispatchEvent ( [ '*' , mesh . meshid ] , obj , mesh ) ;
2019-05-29 03:25:23 +03:00
}
// Called when a user account has changed
2019-05-30 00:36:14 +03:00
function dbUserChange ( userChange , added ) {
2019-07-15 20:24:31 +03:00
if ( parent . webserver == null ) return ;
2019-05-29 03:25:23 +03:00
const user = userChange . fullDocument ;
2019-05-31 21:38:35 +03:00
// Update the user object in memory
const muser = parent . webserver . users [ user . _id ] ;
for ( var i in user ) { muser [ i ] = user [ i ] ; }
for ( var i in muser ) { if ( user [ i ] == null ) { delete muser [ i ] ; } }
// Send the user update
2019-06-11 21:33:44 +03:00
var targets = [ '*' , 'server-users' , user . _id ] ;
if ( user . groups ) { for ( var i in user . groups ) { targets . push ( 'server-users:' + i ) ; } }
parent . DispatchEvent ( targets , obj , { etype : 'user' , username : user . name , account : parent . webserver . CloneSafeUser ( user ) , action : ( added ? 'accountcreate' : 'accountchange' ) , domain : user . domain , nolog : 1 } ) ;
2019-05-29 03:25:23 +03:00
}
2020-01-02 11:30:14 +03:00
// Called when a user group has changed
function dbUGrpChange ( ugrpChange , added ) {
if ( parent . webserver == null ) return ;
common . unEscapeLinksFieldName ( ugrpChange . fullDocument ) ;
const usergroup = ugrpChange . fullDocument ;
// Update the user group object in memory
const uusergroup = parent . webserver . usergroups [ usergroup . _id ] ;
for ( var i in usergroup ) { uusergroup [ i ] = usergroup [ i ] ; }
for ( var i in uusergroup ) { if ( usergroup [ i ] == null ) { delete uusergroup [ i ] ; } }
// Send the user group update
usergroup . action = ( added ? 'createusergroup' : 'usergroupchange' ) ;
usergroup . ugrpid = usergroup . _id ;
usergroup . nolog = 1 ;
delete usergroup . type ;
delete usergroup . _id ;
parent . DispatchEvent ( [ '*' , usergroup . ugrpid ] , obj , usergroup ) ;
}
2017-08-28 19:27:45 +03:00
return obj ;
2018-08-30 03:40:30 +03:00
} ;