Skip to content

Commit ef9e442

Browse files
committed
add versions
1 parent 54e115c commit ef9e442

13 files changed

Lines changed: 624 additions & 71 deletions

OpenFlow/src/Config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export class Config {
3131
public static mongodb_db: string = Config.getEnv("mongodb_db", "openflow");
3232

3333
public static aes_secret: string = Config.getEnv("aes_secret", "");
34+
public static skip_history_collections: string = Config.getEnv("skip_history_collections", "");
3435

3536
public static baseurl(): string {
3637
if (Config.tls_crt != '' && Config.tls_key != '') {

OpenFlow/src/DatabaseConnection.ts

Lines changed: 143 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ export class DatabaseConnection {
100100
if (collectionname === "fs.files") {
101101
_query = { $and: [query, this.getbasequery(jwt, "metadata._acl", [Rights.read])] };
102102
} else {
103-
if (!collectionname.endsWith("hist")) {
103+
if (!collectionname.endsWith("_hist")) {
104104
_query = { $and: [query, this.getbasequery(jwt, "_acl", [Rights.read])] };
105105
} else {
106-
// todo: enforcer permissions when fetching hist ?
106+
// todo: enforcer permissions when fetching _hist ?
107107
_query = query;
108108
}
109109
}
@@ -259,12 +259,15 @@ export class DatabaseConnection {
259259
j = ((j as any) === 'true' || j === true);
260260
w = parseInt((w as any));
261261

262+
item._version = await this.SaveDiff(collectionname, null, item);
263+
262264
// var options:CollectionInsertOneOptions = { writeConcern: { w: parseInt((w as any)), j: j } };
263265
var options: CollectionInsertOneOptions = { w: w, j: j };
264266
//var options: CollectionInsertOneOptions = { w: "majority" };
265267
var result: InsertOneWriteOpResult = await this.db.collection(collectionname).insertOne(item, options);
266268
item = result.ops[0];
267269

270+
268271
if (collectionname === "users" && item._type === "user") {
269272
var users: Role = await Role.FindByNameOrId("users", jwt);
270273
users.AddMember(item);
@@ -307,13 +310,14 @@ export class DatabaseConnection {
307310
var user: TokenUser = Crypt.verityToken(q.jwt);
308311
if (!this.hasAuthorization(user, q.item, "update")) { throw new Error("Access denied"); }
309312

313+
var original: T = null;
310314
// assume empty query, means full document, else update document
311315
if (q.query === null || q.query === undefined) {
312316
// this will add an _acl so needs to be after we checked old item
313317
if (!q.item.hasOwnProperty("_id")) {
314318
throw Error("Cannot update item without _id");
315319
}
316-
var original: T = await this.getbyid<T>(q.item._id, q.collectionname, q.jwt);
320+
original = await this.getbyid<T>(q.item._id, q.collectionname, q.jwt);
317321
if (!original) { throw Error("item not found!"); }
318322
q.item._modifiedby = user.name;
319323
q.item._modifiedbyid = user._id;
@@ -346,8 +350,12 @@ export class DatabaseConnection {
346350
if (q.collectionname != "audit") { this._logger.debug("Adding self " + user.username + " to object " + (q.item.name || q.item._name)); }
347351
q.item.addRight(user._id, user.name, [Rights.full_control]);
348352
}
353+
q.item._version = await this.SaveDiff(q.collectionname, original, q.item);
349354
} else {
350355
itemReplace = false;
356+
var _version = await this.SaveUpdateDiff(q, user);
357+
if ((q.item["$set"]) === undefined) { (q.item["$set"]) = {} };
358+
(q.item["$set"])._version = _version;
351359
}
352360

353361
if (q.collectionname === "users" && q.item._type === "user" && q.item.hasOwnProperty("newpassword")) {
@@ -365,10 +373,10 @@ export class DatabaseConnection {
365373
if (q.collectionname === "fs.files") {
366374
_query = { $and: [q.query, this.getbasequery(q.jwt, "metadata._acl", [Rights.update])] };
367375
} else {
368-
if (!q.collectionname.endsWith("hist")) {
376+
if (!q.collectionname.endsWith("_hist")) {
369377
_query = { $and: [q.query, this.getbasequery(q.jwt, "_acl", [Rights.update])] };
370378
} else {
371-
// todo: enforcer permissions when fetching hist ?
379+
// todo: enforcer permissions when fetching _hist ?
372380
_query = q.query;
373381
}
374382
}
@@ -386,15 +394,17 @@ export class DatabaseConnection {
386394
(q.item["$set"])._modifiedby = user.name;
387395
(q.item["$set"])._modifiedbyid = user._id;
388396
(q.item["$set"])._modified = new Date(new Date().toISOString());
397+
if ((q.item["$inc"]) === undefined) { (q.item["$inc"]) = {} };
398+
(q.item["$inc"])._version = 1;
389399
q.opresult = await this.db.collection(q.collectionname).updateOne(_query, q.item, options);
390400
}
391401
q.item = this.decryptentity<T>(q.item);
392402
this.traversejsondecode(q.item);
393403
q.result = q.item;
394-
return q;
395404
} catch (error) {
396405
throw error;
397406
}
407+
return q;
398408
}
399409
/**
400410
* Update multiple documents in database based on update document
@@ -429,10 +439,10 @@ export class DatabaseConnection {
429439
if (q.collectionname === "fs.files") {
430440
_query = { $and: [q.query, this.getbasequery(q.jwt, "metadata._acl", [Rights.read])] };
431441
} else {
432-
if (!q.collectionname.endsWith("hist")) {
442+
if (!q.collectionname.endsWith("_hist")) {
433443
_query = { $and: [q.query, this.getbasequery(q.jwt, "_acl", [Rights.read])] };
434444
} else {
435-
// todo: enforcer permissions when fetching hist ?
445+
// todo: enforcer permissions when fetching _hist ?
436446
_query = q.query;
437447
}
438448
}
@@ -540,8 +550,6 @@ export class DatabaseConnection {
540550
this._logger.debug("deleting " + id + " in database");
541551
var res: DeleteWriteOpResultObject = await this.db.collection(collectionname).deleteOne(_query);
542552

543-
544-
545553
// var res:DeleteWriteOpResultObject = await this.db.collection(collectionname).deleteOne({_id:id});
546554
// var res:DeleteWriteOpResultObject = await this.db.collection(collectionname).deleteOne(id);
547555
if (res.deletedCount === 0) { throw Error("item not found!"); }
@@ -780,4 +788,129 @@ export class DatabaseConnection {
780788

781789
}
782790

791+
async SaveUpdateDiff<T extends Base>(q: UpdateOneMessage<T>, user: TokenUser) {
792+
var _skip_array: string[] = Config.skip_history_collections.split(",");
793+
var skip_array: string[] = [];
794+
_skip_array.forEach(x => skip_array.push(x.trim()));
795+
if (skip_array.indexOf(q.collectionname) > -1) { return 0; }
796+
var res = await this.query<T>(q.query, null, 1, 0, null, q.collectionname, q.jwt);
797+
if (res.length > 0) {
798+
var _version = 1;
799+
var original = res[0];
800+
801+
delete original._modifiedby;
802+
delete original._modifiedbyid;
803+
delete original._modified;
804+
if (original._version != undefined && original._version != null) {
805+
_version = original._version + 1;
806+
}
807+
}
808+
var updatehist = {
809+
_modified: new Date(new Date().toISOString()),
810+
_modifiedby: user.name,
811+
_modifiedbyid: user._id,
812+
_created: new Date(new Date().toISOString()),
813+
_createdby: user.name,
814+
_createdbyid: user._id,
815+
name: original.name,
816+
id: original._id,
817+
update: q.item,
818+
_version: _version,
819+
reason: ""
820+
}
821+
await this.db.collection(q.collectionname + '_hist').insertOne(updatehist);
822+
}
823+
async SaveDiff(collectionname: string, original: any, item: any) {
824+
if (item._type == 'instance' && collectionname == 'workflows') return 0;
825+
if (item._type == 'instance' && collectionname == 'workflows') return 0;
826+
var _modified = item._modified;
827+
var _modifiedby = item._modifiedby;
828+
var _modifiedbyid = item._modifiedbyid;
829+
var _version = 0;
830+
var _acl = item._acl;
831+
var _type = item._type;
832+
var reason = item._updatereason;
833+
try {
834+
var _skip_array: string[] = Config.skip_history_collections.split(",");
835+
var skip_array: string[] = [];
836+
_skip_array.forEach(x => skip_array.push(x.trim()));
837+
if (skip_array.indexOf(collectionname) > -1) { return 0; }
838+
839+
if (original != null) {
840+
delete original._modifiedby;
841+
delete original._modifiedbyid;
842+
delete original._modified;
843+
if (original._version != undefined && original._version != null) {
844+
_version = original._version + 1;
845+
}
846+
}
847+
var jsondiffpatch = require('jsondiffpatch').create({
848+
objectHash: function (obj, index) {
849+
// try to find an id property, otherwise just use the index in the array
850+
return obj.name || obj.id || obj._id || '$$index:' + index;
851+
}
852+
});
853+
var delta: any = null;
854+
// for backward comp, we cannot assume all objects have an history
855+
// we create diff from version 0
856+
// var delta_collections = Config.history_delta_collections.split(',');
857+
// var full_collections = Config.history_full_collections.split(',');
858+
// if (delta_collections.indexOf(collectionname) == -1 && full_collections.indexOf(collectionname) == -1) return 0;
859+
860+
item._version = _version;
861+
delete item._modifiedby;
862+
delete item._modifiedbyid;
863+
delete item._modified;
864+
delete item._updatereason;
865+
866+
// if (original != null && _version > 0 && delta_collections.indexOf(collectionname) > -1) {
867+
if (original != null && _version > 0) {
868+
delta = jsondiffpatch.diff(original, item);
869+
if (delta == undefined || delta == null) return 0;
870+
var deltahist = {
871+
_acl: _acl,
872+
_type: _type,
873+
_modified: _modified,
874+
_modifiedby: _modifiedby,
875+
_modifiedbyid: _modifiedbyid,
876+
_created: _modified,
877+
_createdby: _modifiedby,
878+
_createdbyid: _modifiedbyid,
879+
name: item.name,
880+
id: item._id,
881+
item: original,
882+
delta: delta,
883+
_version: _version,
884+
reason: reason
885+
}
886+
await this.db.collection(collectionname + '_hist').insertOne(deltahist);
887+
}
888+
else {
889+
var fullhist = {
890+
_acl: _acl,
891+
_type: _type,
892+
_modified: _modified,
893+
_modifiedby: _modifiedby,
894+
_modifiedbyid: _modifiedbyid,
895+
_created: _modified,
896+
_createdby: _modifiedby,
897+
_createdbyid: _modifiedbyid,
898+
name: item.name,
899+
id: item._id,
900+
item: item,
901+
_version: _version,
902+
reason: reason
903+
}
904+
await this.db.collection(collectionname + '_hist').insertOne(fullhist);
905+
}
906+
item._modifiedby = _modifiedby;
907+
item._modifiedbyid = _modifiedbyid;
908+
item._modified = _modified;
909+
} catch (error) {
910+
this._logger.error(error);
911+
}
912+
return _version;
913+
}
914+
915+
783916
}

OpenFlow/src/base.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class WellknownIds {
99
static nodered_users: string = "5a23f18a2e8987292ddbe061";
1010
static nodered_admins: string = "5a17f157c4815318c8536c20";
1111
static nodered_api_users: string = "5a17f157c4815318c8536c22";
12-
12+
1313
static filestore_users: string = "5b6ab63c8d4a64b7c47f4a8f";
1414
static filestore_admins: string = "5b6ab63c8d4a64b7c47f4a8e";
1515
static robot_users: string = "5aef0142f3683977b0aa3dd3";
@@ -25,28 +25,28 @@ export class Rights {
2525
static full_control = -1;
2626
}
2727
interface IBase {
28-
_id:string;
29-
_type:string;
30-
name:string;
31-
getRight(_id:string, deny:boolean):Ace;
32-
addRight(_id:string,name:string, Rights:number[],deny:boolean):void;
33-
removeRight(_id:string,Rights:number[],deny:boolean):void;
28+
_id: string;
29+
_type: string;
30+
name: string;
31+
getRight(_id: string, deny: boolean): Ace;
32+
addRight(_id: string, name: string, Rights: number[], deny: boolean): void;
33+
removeRight(_id: string, Rights: number[], deny: boolean): void;
3434
}
3535
export class Base implements IBase {
36-
_id:string;
37-
_type:string = "unknown";
38-
_acl:Ace[] = [];
39-
name:string;
40-
_name:string;
41-
_encrypt:string[] = [];
36+
_id: string;
37+
_type: string = "unknown";
38+
_acl: Ace[] = [];
39+
name: string;
40+
_name: string;
41+
_encrypt: string[] = [];
4242

4343
_createdbyid: string;
4444
_createdby: string;
4545
_created: Date;
4646
_modifiedbyid: string;
4747
_modifiedby: string;
4848
_modified: Date;
49-
_version: Number = 0;
49+
_version: number = 0;
5050
constructor() {
5151
this.addRight(WellknownIds.admins, "admins", [Rights.full_control]);
5252
}
@@ -55,7 +55,7 @@ export class Base implements IBase {
5555
* @param {T} o Base object
5656
* @returns T New object as Type
5757
*/
58-
static assign<T>(o:T): T {
58+
static assign<T>(o: T): T {
5959
return Object.assign(new Base(), o);
6060
}
6161
/**
@@ -64,16 +64,16 @@ export class Base implements IBase {
6464
* @param {boolean=false} deny look for deny or allow permission
6565
* @returns Ace Ace if found, else null
6666
*/
67-
getRight(_id:string, deny:boolean=false):Ace {
68-
var result:Ace = null;
69-
if(!this._acl) { this._acl = []; }
67+
getRight(_id: string, deny: boolean = false): Ace {
68+
var result: Ace = null;
69+
if (!this._acl) { this._acl = []; }
7070
this._acl.forEach((a, index) => {
71-
if(a._id===_id && a.deny===deny) {
71+
if (a._id === _id && a.deny === deny) {
7272
this._acl[index] = Ace.assign(a);
7373
result = this._acl[index];
7474
}
7575
});
76-
if(result) {
76+
if (result) {
7777
result = Ace.assign(result);
7878
}
7979
return result;
@@ -83,10 +83,10 @@ export class Base implements IBase {
8383
* @param {Ace} x
8484
* @returns void
8585
*/
86-
setRight(x:Ace): void {
87-
if(!this._acl) { this._acl = []; }
86+
setRight(x: Ace): void {
87+
if (!this._acl) { this._acl = []; }
8888
this._acl.forEach((a, index) => {
89-
if(a._id===x._id && a.deny===x.deny) {
89+
if (a._id === x._id && a.deny === x.deny) {
9090
this._acl[index] = x;
9191
}
9292
});
@@ -99,10 +99,10 @@ export class Base implements IBase {
9999
* @param {boolean=false} deny Deny the right
100100
* @returns void
101101
*/
102-
addRight(_id:string,name:string, rights:number[],deny:boolean=false):void {
103-
var right:Ace = this.getRight(_id, deny);
104-
if(!right) { right = new Ace(); this._acl.push(right); }
105-
right.deny = deny;right._id = _id; right.name = name;
102+
addRight(_id: string, name: string, rights: number[], deny: boolean = false): void {
103+
var right: Ace = this.getRight(_id, deny);
104+
if (!right) { right = new Ace(); this._acl.push(right); }
105+
right.deny = deny; right._id = _id; right.name = name;
106106
rights.forEach(bit => {
107107
right.setBit(bit);
108108
});
@@ -115,10 +115,10 @@ export class Base implements IBase {
115115
* @param {boolean=false} deny Deny right
116116
* @returns void
117117
*/
118-
removeRight(_id:string,rights:number[]=null,deny:boolean=false): void {
119-
if(!this._acl) { this._acl = []; }
120-
var right:Ace = this.getRight(_id, deny);
121-
if(!right) { return; }
118+
removeRight(_id: string, rights: number[] = null, deny: boolean = false): void {
119+
if (!this._acl) { this._acl = []; }
120+
var right: Ace = this.getRight(_id, deny);
121+
if (!right) { return; }
122122
rights.forEach(bit => {
123123
right.unsetBit(bit);
124124
});

0 commit comments

Comments
 (0)