From 673cdcdf8cbc95ffb011ced101adf4e514d90e93 Mon Sep 17 00:00:00 2001
From: SAMI BETTAYEB <sami3639@gmail.com>
Date: Tue, 22 Jun 2021 21:45:11 +0100
Subject: [PATCH 1/6] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Add=20dev=20dependenci?=
 =?UTF-8?q?es?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: SAMI BETTAYEB <sami3639@gmail.com>
---
 package.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/package.json b/package.json
index da6c315b..effabaa6 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,8 @@
   },
   "devDependencies": {
     "eslint": "^7.7.0",
+    "chai": "^4.3.4",
+    "chai-subset": "^1.6.0",
     "eslint-config-loopback": "^13.1.0",
     "juggler-v3": "file:./deps/juggler-v3",
     "juggler-v4": "file:./deps/juggler-v4",

From dcc2d96265c89035ba05ef12b7594c626f2efd72 Mon Sep 17 00:00:00 2001
From: SAMI BETTAYEB <sami3639@gmail.com>
Date: Tue, 22 Jun 2021 21:47:42 +0100
Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=97=83=20Enable=20pgcrypto=20extensio?=
 =?UTF-8?q?n?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: SAMI BETTAYEB <sami3639@gmail.com>
---
 lib/migration.js  |  3 ++-
 lib/postgresql.js | 26 +++++++++++++++++++++++++-
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/lib/migration.js b/lib/migration.js
index 57ad4310..d20f6888 100644
--- a/lib/migration.js
+++ b/lib/migration.js
@@ -351,7 +351,8 @@ function mixinMigration(PostgreSQL) {
     });
     // default extension
     if (!createExtensions) {
-      createExtensions = 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp";';
+      createExtensions = `CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+      CREATE EXTENSION IF NOT EXISTS "pgcrypto";`;
     }
 
     // Please note IF NOT EXISTS is introduced in postgresql v9.3
diff --git a/lib/postgresql.js b/lib/postgresql.js
index 2ac994f2..84086977 100644
--- a/lib/postgresql.js
+++ b/lib/postgresql.js
@@ -112,6 +112,7 @@ PostgreSQL.prototype.connect = function(callback) {
     self.client = client;
     process.nextTick(releaseCb);
     callback && callback(err, client);
+    if (!err) self.execute('CREATE EXTENSION IF NOT EXISTS pgcrypto', function(createExtensionError) {});
   });
 };
 
@@ -588,6 +589,17 @@ PostgreSQL.prototype.buildWhere = function(model, where) {
   return whereClause;
 };
 
+PostgreSQL.prototype.getEncryptionFields = function(modelDefinition) {
+  if (modelDefinition
+    && modelDefinition.settings
+    && modelDefinition.settings.mixins
+    && modelDefinition.settings.mixins.Encryption
+    && modelDefinition.settings.mixins.Encryption.fields) {
+    return modelDefinition.settings.mixins.Encryption.fields;
+  }
+  return [];
+};
+
 /**
  * @private
  * @param model
@@ -606,6 +618,7 @@ PostgreSQL.prototype._buildWhere = function(model, where) {
   const self = this;
   const props = self.getModelDefinition(model).properties;
 
+  const encryptedFields = this.getEncryptionFields(this.getModelDefinition(model));
   const whereStmts = [];
   for (const key in where) {
     const stmt = new ParameterizedSQL('', []);
@@ -646,7 +659,18 @@ PostgreSQL.prototype._buildWhere = function(model, where) {
     }
     // eslint-disable one-var
     let expression = where[key];
-    const columnName = self.columnEscaped(model, key);
+    let columnName = self.columnEscaped(model, key);
+    if (encryptedFields.includes(key)) {
+      columnName = `convert_from(
+        decrypt_iv(
+          DECODE(${key},'hex')::bytea,
+          decode('${process.env.ENCRYPTION_HEX_KEY}','hex')::bytea,
+          decode('${process.env.ENCRYPTION_HEX_IV}','hex')::bytea,
+          'aes'
+        ),
+        'utf8'
+      )`;
+    }
     // eslint-enable one-var
     if (expression === null || expression === undefined) {
       stmt.merge(columnName + ' IS NULL');

From 490cf87bad987e79bb76b93870ee0b9a13819f89 Mon Sep 17 00:00:00 2001
From: SAMI BETTAYEB <sami3639@gmail.com>
Date: Tue, 22 Jun 2021 21:50:47 +0100
Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=94=A7=20Add=20required=20crypto=20en?=
 =?UTF-8?q?v=20variables?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: SAMI BETTAYEB <sami3639@gmail.com>
---
 test/init.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/test/init.js b/test/init.js
index d2fa8feb..514c5fa0 100644
--- a/test/init.js
+++ b/test/init.js
@@ -21,6 +21,9 @@ process.env.PGUSER = process.env.POSTGRESQL_USER ||
 process.env.PGPASSWORD = process.env.POSTGRESQL_PASSWORD ||
     process.env.PGPASSWORD ||
     '';
+process.env.ENCRYPTION_HEX_KEY = process.env.ENCRYPTION_HEX_KEY || 'abcdef0123456789abcdef0123456789';
+process.env.ENCRYPTION_HEX_IV = process.env.ENCRYPTION_HEX_IV || '0123456789abcdef0123456789abcdef';
+
 config = {
   host: process.env.PGHOST,
   port: process.env.PGPORT,

From 6abaed843aa35455db1d0e1d1f5e4e401d244612 Mon Sep 17 00:00:00 2001
From: SAMI BETTAYEB <sami3639@gmail.com>
Date: Tue, 22 Jun 2021 21:51:47 +0100
Subject: [PATCH 4/6] =?UTF-8?q?=E2=9C=85=20=F0=9F=97=83=20Add=20encrypted?=
 =?UTF-8?q?=5Fdata=20table=20for=20testing=20purposes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: SAMI BETTAYEB <sami3639@gmail.com>
---
 test/schema.sql | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/test/schema.sql b/test/schema.sql
index 7020812c..cd59447b 100644
--- a/test/schema.sql
+++ b/test/schema.sql
@@ -377,6 +377,16 @@ CREATE TABLE reservation (
 );
 
 
+--
+-- Name: encrypted_data; Type: TABLE; Schema: strongloop; Owner: strongloop
+--
+
+CREATE TABLE encrypted_data (
+    id character varying(64),
+    data text
+);
+
+
 --
 -- Name: session; Type: TABLE; Schema: strongloop; Owner: strongloop
 --
@@ -1207,6 +1217,8 @@ INSERT INTO product VALUES ('87', 'NV Goggles', NULL, NULL, NULL, NULL, NULL);
 INSERT INTO product VALUES ('2', 'G17', 53, 75, 15, 'Flashlight', 'Single');
 INSERT INTO product VALUES ('5', 'M9 SD', 0, 75, 15, 'Silenced', 'Single');
 
+INSERT INTO encrypted_data VALUES('1', '1c93722e6cf53f93dd4eb15a18444dc3e910fded18239db612794059af1fa5e8');
+
 
 --
 -- Data for Name: reservation; Type: TABLE DATA; Schema: strongloop; Owner: strongloop

From 827354a5a886654a177135eb0cb94e1b8769790c Mon Sep 17 00:00:00 2001
From: SAMI BETTAYEB <sami3639@gmail.com>
Date: Tue, 22 Jun 2021 21:53:08 +0100
Subject: [PATCH 5/6] =?UTF-8?q?=E2=9C=A8=20=E2=9C=85=20Add=20encrypted=20d?=
 =?UTF-8?q?ata=20tests?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: SAMI BETTAYEB <sami3639@gmail.com>
---
 test/postgresql.encrypted.test.js | 79 +++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 test/postgresql.encrypted.test.js

diff --git a/test/postgresql.encrypted.test.js b/test/postgresql.encrypted.test.js
new file mode 100644
index 00000000..4dd7e5cd
--- /dev/null
+++ b/test/postgresql.encrypted.test.js
@@ -0,0 +1,79 @@
+// Copyright IBM Corp. 2014,2019. All Rights Reserved.
+// Node module: loopback-connector-postgresql
+// This file is licensed under the Artistic License 2.0.
+// License text available at https://opensource.org/licenses/Artistic-2.0
+
+'use strict';
+process.env.NODE_ENV = 'test';
+require('should');
+const expect = require('chai').expect;
+const async = require('async');
+const chai = require('chai');
+const chaiSubset = require('chai-subset');
+chai.use(chaiSubset);
+
+let db;
+
+before(function() {
+  db = global.getSchema();
+});
+
+describe('Mapping models', function() {
+  it('should return encrypted data by filter', function(done) {
+    const schema =
+      {
+        'name': 'EncryptedData',
+        'options': {
+          'idInjection': false,
+          'postgresql': {
+            'schema': 'public', 'table': 'encrypted_data',
+          },
+        },
+        'properties': {
+          'id': {
+            'type': 'String',
+            'id': true,
+          },
+          'data': {
+            'type': 'String',
+          },
+        },
+        'mixins': {
+          'Encryption': {
+            'fields': [
+              'data',
+            ],
+          },
+        },
+      };
+
+    const EncryptedData = db.createModel(schema.name, schema.properties, schema.options);
+    EncryptedData.settings.mixins = schema.mixins;
+
+    db.automigrate('EncryptedData', function(err) {
+      if (err) console.error({err});
+      EncryptedData.create({
+        id: '2',
+        data: '1c93722e6cf53f93dd4eb15a18444dc3e910fded18239db612794059af1fa5e8',
+      }, function(err, encryptedData) {
+        if (err) console.log({err2: err});
+        async.series([
+          function(callback) {
+            EncryptedData.findOne({where: {data: {ilike: '%test%'}}}, function(err, retreivedData) {
+              if (err) console.error({err111: err});
+              expect(retreivedData).to.containSubset(encryptedData);
+              callback(null, retreivedData);
+            });
+          },
+          function(callback) {
+            EncryptedData.find({where: {data: {ilike: '%not found%'}}}, function(err, retreivedData) {
+              if (err) console.error({err111: err});
+              expect(retreivedData.length).to.equal(0);
+              callback(null, retreivedData);
+            });
+          },
+        ], done);
+      });
+    });
+  });
+});

From af6500b4d3293989e61120161ff91dbf4c6e9292 Mon Sep 17 00:00:00 2001
From: Rifa Achrinza <25147899+achrinza@users.noreply.github.com>
Date: Sun, 12 Sep 2021 23:09:10 +0800
Subject: [PATCH 6/6] chore: fix package lock

Signed-off-by: Rifa Achrinza <25147899+achrinza@users.noreply.github.com>
---
 package-lock.json | 130 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/package-lock.json b/package-lock.json
index 5f2b6d03..39d2199e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "loopback-connector-postgresql",
       "version": "5.4.0",
       "license": "Artistic-2.0",
       "dependencies": {
@@ -19,6 +20,8 @@
       },
       "devDependencies": {
         "@commitlint/config-conventional": "^12.1.4",
+        "chai": "^4.3.4",
+        "chai-subset": "^1.6.0",
         "eslint": "^7.7.0",
         "eslint-config-loopback": "^13.1.0",
         "juggler-v3": "file:./deps/juggler-v3",
@@ -730,6 +733,15 @@
       "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=",
       "dev": true
     },
+    "node_modules/assertion-error": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+      "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/astral-regex": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
@@ -870,6 +882,32 @@
         "upper-case-first": "^2.0.2"
       }
     },
+    "node_modules/chai": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz",
+      "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==",
+      "dev": true,
+      "dependencies": {
+        "assertion-error": "^1.1.0",
+        "check-error": "^1.0.2",
+        "deep-eql": "^3.0.1",
+        "get-func-name": "^2.0.0",
+        "pathval": "^1.1.1",
+        "type-detect": "^4.0.5"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/chai-subset": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz",
+      "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/chalk": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
@@ -913,6 +951,15 @@
         "node": "*"
       }
     },
+    "node_modules/check-error": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+      "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/chokidar": {
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
@@ -1060,6 +1107,18 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/deep-eql": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+      "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+      "dev": true,
+      "dependencies": {
+        "type-detect": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=0.12"
+      }
+    },
     "node_modules/deep-extend": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -1545,6 +1604,15 @@
         "node": "6.* || 8.* || >= 10.*"
       }
     },
+    "node_modules/get-func-name": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+      "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/get-intrinsic": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
@@ -2517,6 +2585,15 @@
       "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
       "dev": true
     },
+    "node_modules/pathval": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+      "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/pg": {
       "version": "8.6.0",
       "resolved": "https://registry.npmjs.org/pg/-/pg-8.6.0.tgz",
@@ -3760,6 +3837,12 @@
       "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=",
       "dev": true
     },
+    "assertion-error": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+      "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+      "dev": true
+    },
     "astral-regex": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
@@ -3873,6 +3956,26 @@
         "upper-case-first": "^2.0.2"
       }
     },
+    "chai": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz",
+      "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==",
+      "dev": true,
+      "requires": {
+        "assertion-error": "^1.1.0",
+        "check-error": "^1.0.2",
+        "deep-eql": "^3.0.1",
+        "get-func-name": "^2.0.0",
+        "pathval": "^1.1.1",
+        "type-detect": "^4.0.5"
+      }
+    },
+    "chai-subset": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz",
+      "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=",
+      "dev": true
+    },
     "chalk": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
@@ -3907,6 +4010,12 @@
       "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
       "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
     },
+    "check-error": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+      "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+      "dev": true
+    },
     "chokidar": {
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
@@ -4023,6 +4132,15 @@
       "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
       "dev": true
     },
+    "deep-eql": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+      "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+      "dev": true,
+      "requires": {
+        "type-detect": "^4.0.0"
+      }
+    },
     "deep-extend": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -4394,6 +4512,12 @@
       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
       "dev": true
     },
+    "get-func-name": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+      "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+      "dev": true
+    },
     "get-intrinsic": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
@@ -5454,6 +5578,12 @@
         }
       }
     },
+    "pathval": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+      "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+      "dev": true
+    },
     "pg": {
       "version": "8.6.0",
       "resolved": "https://registry.npmjs.org/pg/-/pg-8.6.0.tgz",