1- import { ObjectID , Db , Binary , InsertOneWriteOpResult , DeleteWriteOpResultObject , ObjectId , MapReduceOptions } from "mongodb" ;
1+ import {
2+ ObjectID , Db , Binary , InsertOneWriteOpResult , DeleteWriteOpResultObject , ObjectId , MapReduceOptions , CollectionInsertOneOptions , UpdateWriteOpResult , WriteOpResult
3+ } from "mongodb" ;
24import { MongoClient } from "mongodb" ;
35import { Base , Rights , WellknownIds } from "./base" ;
46import winston = require( "winston" ) ;
@@ -171,6 +173,8 @@ export class DatabaseConnection {
171173 * Create a new document in the database
172174 * @param {T } item Item to create
173175 * @param {string } collectionname The collection to create item in
176+ * @param {number } w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
177+ * @param {boolean } j Ensure is written to the on-disk journal.
174178 * @param {string } jwt JWT of the user, creating the item, to ensure rights and permission
175179 * @returns Promise<T> Returns the new item added
176180 */
@@ -188,7 +192,7 @@ export class DatabaseConnection {
188192 item . _modifiedbyid = user . _id ;
189193 item . _modified = item . _created ;
190194 var hasUser : Ace = item . _acl . find ( e => e . _id === user . _id ) ;
191- if ( hasUser === null || hasUser === undefined ) {
195+ if ( ( hasUser === null || hasUser === undefined ) && item . _acl . length == 0 ) {
192196 if ( collectionname != "audit" ) { this . _logger . debug ( "Adding self " + user . username + " to object " + ( item . name || item . _name ) ) ; }
193197 item . addRight ( user . _id , user . name , [ Rights . full_control ] ) ;
194198 }
@@ -201,7 +205,8 @@ export class DatabaseConnection {
201205 ( item as any ) . passwordhash = await Crypt . hash ( ( item as any ) . newpassword ) ;
202206 delete ( item as any ) . newpassword ;
203207 }
204- var options = { writeConcern : { w : parseInt ( ( w as any ) ) , j : j } } ;
208+ // var options:CollectionInsertOneOptions = { writeConcern: { w: parseInt((w as any)), j: j } };
209+ var options : CollectionInsertOneOptions = { w : w , j : j } ;
205210 var result : InsertOneWriteOpResult = await this . db . collection ( collectionname ) . insertOne ( item , options ) ;
206211 item = result . ops [ 0 ] ;
207212
@@ -217,61 +222,201 @@ export class DatabaseConnection {
217222 * Update entity in database
218223 * @param {T } item Item to update
219224 * @param {string } collectionname Collection containing item
225+ * @param {number } w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
226+ * @param {boolean } j Ensure is written to the on-disk journal.
220227 * @param {string } jwt JWT of user who is doing the update, ensuring rights
221228 * @returns Promise<T>
222229 */
223- async UpdateOne < T extends Base > ( item : T , collectionname : string , w : number , j : boolean , jwt : string ) : Promise < T > {
230+ async UpdateOne < T extends Base > ( query : any , item : T , collectionname : string , w : number , j : boolean , jwt : string ) : Promise < T > {
231+ var itemUpdate : boolean = true ;
224232 if ( item === null || item === undefined ) { throw Error ( "Cannot update null item" ) ; }
225- if ( ! item . hasOwnProperty ( "_id" ) ) { throw Error ( "Cannot update item without _id" ) ; }
226233 await this . connect ( ) ;
227234 var user : TokenUser = Crypt . verityToken ( jwt ) ;
228235 if ( ! this . hasAuthorization ( user , item , "update" ) ) { throw new Error ( "Access denied" ) ; }
229236 var original : T = await this . getbyid < T > ( item . _id , collectionname , jwt ) ;
230237 if ( ! original ) { throw Error ( "item not found!" ) ; }
231- item . _modifiedby = user . name ;
232- item . _modifiedbyid = user . _id ;
233- item . _modified = new Date ( new Date ( ) . toISOString ( ) ) ;
234- // now add all _ fields to the new object
235- var keys : string [ ] = Object . keys ( original ) ;
236- for ( let i : number = 0 ; i < keys . length ; i ++ ) {
237- let key : string = keys [ i ] ;
238- if ( key === "_created" ) {
239- item [ key ] = new Date ( original [ key ] ) ;
240- } else if ( key === "_createdby" || key === "_createdbyid" ) {
241- item [ key ] = original [ key ] ;
242- } else if ( key === "_modifiedby" || key === "_modifiedbyid" || key === "_modified" ) {
243- // allready updated
244- } else if ( key . indexOf ( "_" ) === 0 ) {
245- if ( ! item . hasOwnProperty ( key ) ) {
246- item [ key ] = original [ key ] ; // add missing key
247- } else if ( item [ key ] === null ) {
248- delete item [ key ] ; // remove key
249- } else {
250- // key allready exists, might been updated since last save
238+
239+ // assume empty query, means full document, else update document
240+ if ( query === null || query === undefined ) {
241+ // this will add an _acl so needs to be after we checked old item
242+ if ( ! item . hasOwnProperty ( "_id" ) ) {
243+ throw Error ( "Cannot update item without _id" ) ;
244+ }
245+ item . _modifiedby = user . name ;
246+ item . _modifiedbyid = user . _id ;
247+ item . _modified = new Date ( new Date ( ) . toISOString ( ) ) ;
248+ // now add all _ fields to the new object
249+ var keys : string [ ] = Object . keys ( original ) ;
250+ for ( let i : number = 0 ; i < keys . length ; i ++ ) {
251+ let key : string = keys [ i ] ;
252+ if ( key === "_created" ) {
253+ item [ key ] = new Date ( original [ key ] ) ;
254+ } else if ( key === "_createdby" || key === "_createdbyid" ) {
255+ item [ key ] = original [ key ] ;
256+ } else if ( key === "_modifiedby" || key === "_modifiedbyid" || key === "_modified" ) {
257+ // allready updated
258+ } else if ( key . indexOf ( "_" ) === 0 ) {
259+ if ( ! item . hasOwnProperty ( key ) ) {
260+ item [ key ] = original [ key ] ; // add missing key
261+ } else if ( item [ key ] === null ) {
262+ delete item [ key ] ; // remove key
263+ } else {
264+ // key allready exists, might been updated since last save
265+ }
251266 }
252267 }
268+ item = this . ensureResource ( item ) ;
269+ this . traversejsonencode ( item ) ;
270+ item = this . encryptentity < T > ( item ) ;
271+ var hasUser : Ace = item . _acl . find ( e => e . _id === user . _id ) ;
272+ if ( ( hasUser === null || hasUser === undefined ) && item . _acl . length == 0 ) {
273+ if ( collectionname != "audit" ) { this . _logger . debug ( "Adding self " + user . username + " to object " + ( item . name || item . _name ) ) ; }
274+ item . addRight ( user . _id , user . name , [ Rights . full_control ] ) ;
275+ }
276+ } else {
277+ itemUpdate = false ;
253278 }
254- // this will add an _acl so needs to be after we checked old item
255- item = this . ensureResource ( item ) ;
256- this . traversejsonencode ( item ) ;
257- item = this . encryptentity < T > ( item ) ;
258279
259- var hasUser : Ace = item . _acl . find ( e => e . _id === user . _id ) ;
260- if ( hasUser === null || hasUser === undefined ) {
261- if ( collectionname != "audit" ) { this . _logger . debug ( "Adding self " + user . username + " to object " + ( item . name || item . _name ) ) ; }
262- item . addRight ( user . _id , user . name , [ Rights . full_control ] ) ;
280+ if ( collectionname === "users" && item . _type === "user" && item . hasOwnProperty ( "newpassword" ) ) {
281+ ( item as any ) . passwordhash = await Crypt . hash ( ( item as any ) . newpassword ) ;
282+ delete ( item as any ) . newpassword ;
283+ }
284+ this . _logger . debug ( "updating " + ( item . name || item . _name ) + " in database" ) ;
285+ // await this.db.collection(collectionname).replaceOne({ _id: item._id }, item, options);
286+
287+ if ( query === null || query === undefined ) {
288+ query = { _id : item . _id } ;
289+ }
290+ var _query : Object = { } ;
291+ if ( collectionname === "files" ) { collectionname = "fs.files" ; }
292+ if ( collectionname === "fs.files" ) {
293+ _query = { $and : [ query , this . getbasequery ( jwt , "metadata._acl" , [ Rights . update ] ) ] } ;
294+ } else {
295+ if ( ! collectionname . endsWith ( "hist" ) ) {
296+ _query = { $and : [ query , this . getbasequery ( jwt , "_acl" , [ Rights . update ] ) ] } ;
297+ } else {
298+ // todo: enforcer permissions when fetching hist ?
299+ _query = query ;
300+ }
301+ }
302+
303+ // var options = { writeConcern: { w: parseInt((w as any)), j: j } };
304+ var options : CollectionInsertOneOptions = { w : w , j : j } ;
305+ var res : UpdateWriteOpResult = null ;
306+ try {
307+ if ( itemUpdate ) {
308+ res = await this . db . collection ( collectionname ) . updateOne ( _query , { $set : item } , options ) ;
309+ } else {
310+ res = await this . db . collection ( collectionname ) . updateOne ( _query , item , options ) ;
311+ }
312+ // var res: ReplaceOneWriteOpResult = await this.db.collection(collectionname).replaceOne(_query, item, options);
313+ if ( res . result . ok == 1 ) {
314+ if ( w > 0 ) {
315+ if ( res . modifiedCount == 0 || res . modifiedCount == undefined ) {
316+ throw Error ( "item not found!" ) ;
317+ } else if ( res . modifiedCount == 1 ) {
318+ item = item ;
319+ } else {
320+ throw Error ( "More than one item was updated !!!" ) ;
321+ }
322+ }
323+ } else {
324+ throw Error ( "UpdateOne failed!!!" ) ;
325+ }
326+ item = this . decryptentity < T > ( item ) ;
327+ this . traversejsondecode ( item ) ;
328+ return item ;
329+ } catch ( error ) {
330+ throw error ;
331+
263332 }
333+ }
334+ /**
335+ * Update multiple documents in database based on update document
336+ * @param {any } query MongoDB Query
337+ * @param {T } item Update document
338+ * @param {string } collectionname Collection containing item
339+ * @param {number } w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
340+ * @param {boolean } j Ensure is written to the on-disk journal.
341+ * @param {string } jwt JWT of user who is doing the update, ensuring rights
342+ * @returns Promise<T>
343+ */
344+ async UpdateMany ( query : any , item : any , collectionname : string , w : number , j : boolean , jwt : string ) : Promise < any [ ] > {
345+ if ( item === null || item === undefined ) { throw Error ( "Cannot update null item" ) ; }
346+ await this . connect ( ) ;
347+ var user : TokenUser = Crypt . verityToken ( jwt ) ;
348+ if ( ! this . hasAuthorization ( user , item , "update" ) ) { throw new Error ( "Access denied" ) ; }
264349
265350 if ( collectionname === "users" && item . _type === "user" && item . hasOwnProperty ( "newpassword" ) ) {
266351 ( item as any ) . passwordhash = await Crypt . hash ( ( item as any ) . newpassword ) ;
267352 delete ( item as any ) . newpassword ;
268353 }
269- this . _logger . debug ( "updating " + ( item . name || item . _name ) + " in database" ) ;
270- var options = { writeConcern : { w : parseInt ( ( w as any ) ) , j : j } } ;
271- await this . db . collection ( collectionname ) . replaceOne ( { _id : item . _id } , item , options ) ;
272- this . traversejsondecode ( item ) ;
273- return item ;
354+ for ( let key in query ) {
355+ if ( key === "_id" ) {
356+ var id : string = query . _id ;
357+ delete query . _id ;
358+ query . $or = [ { _id : id } , { _id : safeObjectID ( id ) } ] ;
359+ }
360+ }
361+ var _query : Object = { } ;
362+ if ( collectionname === "files" ) { collectionname = "fs.files" ; }
363+ if ( collectionname === "fs.files" ) {
364+ _query = { $and : [ query , this . getbasequery ( jwt , "metadata._acl" , [ Rights . read ] ) ] } ;
365+ } else {
366+ if ( ! collectionname . endsWith ( "hist" ) ) {
367+ _query = { $and : [ query , this . getbasequery ( jwt , "_acl" , [ Rights . read ] ) ] } ;
368+ } else {
369+ // todo: enforcer permissions when fetching hist ?
370+ _query = query ;
371+ }
372+ }
373+
374+ if ( item . $set !== undefined ) {
375+ item . $set . _modifiedby = user . name ;
376+ item . $set . _modifiedbyid = user . _id ;
377+ item . $set . _modified = new Date ( new Date ( ) . toISOString ( ) ) ;
378+ }
379+
380+ this . _logger . debug ( "updateMany " + ( item . name || item . _name ) + " in database" ) ;
381+ // var options = { writeConcern: { w: parseInt((w as any)), j: j } };
382+ var options : CollectionInsertOneOptions = { } ;
383+ if ( w > 0 ) { options . w = w ; }
384+ var res : UpdateWriteOpResult = null ;
385+ try {
386+ res = await this . db . collection ( collectionname ) . updateMany ( _query , item , options ) ;
387+ if ( res . modifiedCount == 0 ) {
388+ throw Error ( "item not found!" ) ;
389+ }
390+ if ( res . result . ok == 1 ) {
391+ if ( w > 0 ) {
392+ if ( res . modifiedCount == 0 || res . modifiedCount == undefined ) {
393+ throw Error ( "item not found!" ) ;
394+ } else if ( res . modifiedCount == 1 ) {
395+ item = item ;
396+ } else {
397+ throw Error ( "More than one item was updated !!!" ) ;
398+ }
399+ }
400+ } else {
401+ throw Error ( "UpdateOne failed!!!" ) ;
402+ }
403+ return null ;
404+ } catch ( error ) {
405+ throw error ;
406+ }
407+ // this.traversejsondecode(item);
408+ // return item;
274409 }
410+ /**
411+ * Insert or Update depending on document allready exists.
412+ * @param {T } item Item to insert or update
413+ * @param {string } collectionname Collection containing item
414+ * @param {string } uniqeness List of fields to combine for uniqeness
415+ * @param {number } w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
416+ * @param {boolean } j Ensure is written to the on-disk journal.
417+ * @param {string } jwt JWT of user who is doing the update, ensuring rights
418+ * @returns Promise<T>
419+ */
275420 async InsertOrUpdateOne < T extends Base > ( item : T , collectionname : string , uniqeness : string , w : number , j : boolean , jwt : string ) : Promise < T > {
276421 var query : any = null ;
277422 if ( uniqeness !== null && uniqeness !== undefined && uniqeness !== "" ) {
@@ -295,12 +440,12 @@ export class DatabaseConnection {
295440 var user : TokenUser = Crypt . verityToken ( jwt ) ;
296441 if ( ! this . hasAuthorization ( user , item , "update" ) ) { throw new Error ( "Access denied" ) ; }
297442 var hasUser : Ace = item . _acl . find ( e => e . _id === user . _id ) ;
298- if ( hasUser === null || hasUser === undefined ) {
443+ if ( ( hasUser === null || hasUser === undefined ) && item . _acl . length == 0 ) {
299444 if ( collectionname != "audit" ) { this . _logger . debug ( "Adding self " + user . username + " to object " + ( item . name || item . _name ) ) ; }
300445 item . addRight ( user . _id , user . name , [ Rights . full_control ] ) ;
301446 }
302447 if ( item . _id !== null && item . _id !== undefined && item . _id !== "" ) {
303- item = await this . UpdateOne ( item , collectionname , w , j , jwt ) ;
448+ item = await this . UpdateOne ( null , item , collectionname , w , j , jwt ) ;
304449 } else {
305450 item = await this . InsertOne ( item , collectionname , w , j , jwt ) ;
306451 }
@@ -433,8 +578,11 @@ export class DatabaseConnection {
433578 } ;
434579 finalor . push ( q2 ) ;
435580 }
581+ //
582+ if ( bits . length == 1 && bits [ 0 ] == Rights . read ) {
583+ return { $or : finalor . concat ( isme ) } ;
584+ }
436585 return { $or : finalor . concat ( ) } ;
437- // return { $or: finalor.concat(isme) };
438586 }
439587 /**
440588 * Ensure _type and _acs on object
0 commit comments