_binary From 32bf9c98749a711733677cf29118090e5f0655ed Mon Sep 17 00:00:00 2001
From: Pragya Rana <pragya.rana9@gmail.com>
Date: Mon, 17 Apr 2017 22:10:36 -0700
Subject: [PATCH] Add an option to add more privacy to a group.

* Added an external database for adding privacy.
* Added AES algorithm for encryption/decryption..
* Updated table groups with new column 'encryption'.
* Added a field 'Encryption' in the UI when creating a new group. This gives
  an option to whether enable or disable encryption for the new group.
---
 src/configs/Config.php                         |   9 +-
 src/configs/Createdb.php                       |  35 +++++-
 src/controllers/components/SocialComponent.php |  20 +++-
 src/data/default.db                            | Bin 1503232 -> 296960 bytes
 src/data/default2.db                           | Bin 0 -> 3072 bytes
 src/library/Cipher.php                         | 148 +++++++++++++++++++++++++
 src/library/VersionFunctions.php               |  46 +++++++-
 src/locale/en_US/configure.ini                 |   3 +
 src/models/GroupModel.php                      |  99 +++++++++++++++--
 src/models/Model.php                           |  14 +++
 src/models/ProfileModel.php                    |  41 ++++++-
 src/views/elements/ManagegroupsElement.php     |  25 +++++
 12 files changed, 420 insertions(+), 20 deletions(-)
 create mode 100644 src/data/default2.db
 create mode 100644 src/library/Cipher.php

diff --git a/src/configs/Config.php b/src/configs/Config.php
index efafbb75..c9d01b67 100755
--- a/src/configs/Config.php
+++ b/src/configs/Config.php
@@ -112,7 +112,7 @@ function nsconddefine($constant, $value)
  * Version number for upgrade function
  * @var int
  */
-nsdefine('YIOOP_VERSION', 49);
+nsdefine('YIOOP_VERSION', 50);
 /**
  * Minimum Version fo Yioop for which keyword ad script
  * still works with this version
@@ -333,6 +333,7 @@ if (file_exists(WORK_DIRECTORY . PROFILE_FILE_NAME)) {
     /** @ignore */
     nsdefine('PROFILE', false);
     nsdefine('DBMS', 'Sqlite3');
+    nsdefine('PRIVATE_DBMS', 'Sqlite3');
     nsdefine('AUTHENTICATION_MODE', NORMAL_AUTHENTICATION);
     nsdefine('RECOVERY_MODE', EMAIL_RECOVERY);
     nsdefine('DEBUG_LEVEL', NO_DEBUG_INFO);
@@ -350,8 +351,12 @@ if (file_exists(WORK_DIRECTORY . PROFILE_FILE_NAME)) {
     nsdefine('MEDIA_MODE', 'name_server');
     nsdefine('DB_NAME', "default");
     nsdefine('DB_USER', '');
+    nsdefine('PRIVATE_DB_USER', '');
     nsdefine('DB_PASSWORD', '');
+    nsdefine('PRIVATE_DB_PASSWORD', '');
     nsdefine('DB_HOST', '');
+    nsdefine('PRIVATE_DB_HOST', '');
+    nsdefine('PRIVATE_DB_NAME', "default2");
     /** @ignore */
     nsdefine('CRAWL_DIR', BASE_DIR);
     /** @ignore */
@@ -942,3 +947,5 @@ nsconddefine('AD_DATE_FORMAT','Y-m-d');
 nsconddefine('AD_LOGO','resources/adv-logo.png');
 /** sentence compression enabled or not*/
 nsconddefine('SENTENCE_COMPRESSION_ENABLED', false);
+/** Define cipher to be used in AES */
+nsconddefine('AES_256_CBC', 'aes-256-cbc');
diff --git a/src/configs/Createdb.php b/src/configs/Createdb.php
index 2dda9d0c..de762017 100755
--- a/src/configs/Createdb.php
+++ b/src/configs/Createdb.php
@@ -35,6 +35,7 @@
 namespace seekquarry\yioop\configs;
 
 use seekquarry\yioop\library as L;
+use seekquarry\yioop\library\Cipher;
 use seekquarry\yioop\models\Model;
 use seekquarry\yioop\models\ProfileModel;
 use seekquarry\yioop\models\GroupModel;
@@ -50,33 +51,61 @@ require_once __DIR__."/../library/LocaleFunctions.php";
 /** To make it easy to insert translations */
 require_once __DIR__."/../library/UpgradeFunctions.php";
 $profile_model = new ProfileModel(DB_NAME, false);
+$private_profile_model = new ProfileModel(PRIVATE_DB_NAME, false);
 $db_class = NS_DATASOURCES . ucfirst(DBMS)."Manager";
+$private_db_class = NS_DATASOURCES . ucfirst(PRIVATE_DBMS)."Manager";
 $dbinfo = ["DBMS" => DBMS, "DB_HOST" => DB_HOST, "DB_USER" => DB_USER,
     "DB_PASSWORD" => DB_PASSWORD, "DB_NAME" => DB_NAME];
+$private_dbinfo = ["DBMS" => PRIVATE_DBMS, "DB_HOST" => PRIVATE_DB_HOST,
+    "DB_USER" => PRIVATE_DB_USER, "DB_PASSWORD" => PRIVATE_DB_PASSWORD,
+    "DB_NAME" => PRIVATE_DB_NAME];
 if (!in_array(DBMS, ['sqlite', 'sqlite3'])) {
     $db = new $db_class();
+    $private_db = new $private_db_class();
     $db->connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
+    $private_db->connect(PRIVATE_DB_HOST, PRIVATE_DB_USER,
+        PRIVATE_DB_PASSWORD, PRIVATE_DB_NAME);
     /*  postgres doesn't let you drop a database while connected to it so drop
         tables instead first
      */
     $profile_model->initializeSql($db, $dbinfo);
+    $private_profile_model->initializeSqlPrivate($private_db, $private_dbinfo);
     $database_tables = array_keys($profile_model->create_statements);
+    $private_database_tables = array_keys(
+        $private_profile_model->private_create_statements);
     foreach ($database_tables as $table) {
         $db->execute("DROP TABLE ".$table);
     }
+    foreach ($private_database_tables as $table) {
+        $private_db->execute("DROP TABLE ".$table);
+    }
     $db->execute("DROP DATABASE IF EXISTS " . DB_NAME);
     $db->execute("CREATE DATABASE " . DB_NAME);
     $db->disconnect();
     $db->connect(); // default connection goes to actual DB
+    $private_db->execute("DROP DATABASE IF EXISTS ". PRIVATE_DB_NAME);
+    $private_db->execute("CREATE DATABASE ". PRIVATE_DB_NAME);
+    $private_db->disconnect();
+    $private_db->connect(PRIVATE_DB_HOST, PRIVATE_DB_USER,
+        PRIVATE_DB_PASSWORD, PRIVATE_DB_NAME);
 } else {
     @unlink(CRAWL_DIR."/data/" . DB_NAME . ".db");
     $db = new $db_class();
     $db->connect();
+    @unlink(CRAWL_DIR."/data/" . PRIVATE_DB_NAME . ".db");
+    $private_db = new $private_db_class();
+    $private_db->connect(PRIVATE_DB_HOST, PRIVATE_DB_USER,
+        PRIVATE_DB_PASSWORD, PRIVATE_DB_NAME);
 }
 if (!$profile_model->createDatabaseTables($db, $dbinfo)) {
     echo "\n\nCouldn't create database tables!!!\n\n";
     exit();
 }
+if (!$private_profile_model->createDatabaseTablesPrivate($private_db,
+    $private_dbinfo)) {
+    echo "\n\nCouldn't create database tables!!!\n\n";
+    exit();
+}
 $db->execute("INSERT INTO VERSION VALUES (" . YIOOP_VERSION . ")");
 $creation_time = L\microTimestamp();
 //numerical value of the blank password
@@ -113,7 +142,7 @@ $db->execute($sql);
 $creation_time = L\microTimestamp();
 $sql = "INSERT INTO GROUPS VALUES(".PUBLIC_GROUP_ID.",'Public','".
     $creation_time."','".ROOT_ID."', '".PUBLIC_JOIN."', '".GROUP_READ.
-    "', ".NON_VOTING_GROUP.", " . FOREVER . ")";
+    "', ".NON_VOTING_GROUP.", " . FOREVER . ", 0)";
 $db->execute($sql);
 $now = time();
 $db->execute("INSERT INTO ROLE VALUES (".ADMIN_ROLE.", 'Admin' )");
@@ -129,7 +158,7 @@ $db->execute("INSERT INTO USER_GROUP VALUES (".PUBLIC_USER_ID.", ".
 $sql = "INSERT INTO GROUPS VALUES(" . HELP_GROUP_ID . ",'Help','" .
     $creation_time . "','" . ROOT_ID . "',
     '" . PUBLIC_BROWSE_REQUEST_JOIN . "', '" . GROUP_READ_WIKI .
-    "', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ")";
+    "', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ", 0)";
 $db->execute($sql);
 $now = time();
 $db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
@@ -540,7 +569,9 @@ if (stristr(DB_HOST, "pgsql") !== false) {
 }
 
 $db->disconnect();
+$private_db->disconnect();
 if (in_array(DBMS, ['sqlite','sqlite3'])){
     chmod(CRAWL_DIR."/data/".DB_NAME.".db", 0666);
+    chmod(CRAWL_DIR."/data/".PRIVATE_DB_NAME.".db", 0666);
 }
 echo "Create DB succeeded\n";
diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index e2c7190e..11f8eba1 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -120,11 +120,15 @@ class SocialComponent extends Component implements CrawlConstants
             C\ONE_DAY => tl('social_component_one_day'),
             C\ONE_MONTH => tl('social_component_one_month'),
         ];
+        $data['ENCRYPTION_CODES'] = [
+            true => tl('social_component_encryption_enable'),
+            false => tl('social_component_encryption_disable'),
+        ];
         $search_array = [];
 
         $default_group = ["name" => "","id" => "", "owner" =>"",
             "register" => -1, "member_access" => -1, 'vote_access' => -1,
-            "post_lifetime" => -1];
+            "post_lifetime" => -1, "encryption" => 0];
         $data['CURRENT_GROUP'] = $default_group;
         $data['PAGING'] = "";
         $name = "";
@@ -158,6 +162,8 @@ class SocialComponent extends Component implements CrawlConstants
                     $group['VOTE_ACCESS'];
                 $data['CURRENT_GROUP']['post_lifetime'] =
                     $group['POST_LIFETIME'];
+                $data['CURRENT_GROUP']['encryption'] =
+                    $group['ENCRYPTION'];
                 $is_owner = true;
             } else if (!in_array($_REQUEST['arg'],
                 ["deletegroup", "joingroup", "unsubscribe"]) &&
@@ -354,7 +360,8 @@ class SocialComponent extends Component implements CrawlConstants
                             "register" => ["REGISTER_CODES", C\REQUEST_JOIN],
                             "vote_access" => ["VOTING_CODES",
                                 C\NON_VOTING_GROUP],
-                            "post_lifetime" => ["VOTING_CODES", C\FOREVER]
+                            "post_lifetime" => ["VOTING_CODES", C\FOREVER],
+                            "encryption" => ["ENCRYPTION_CODES", 0],
                         ];
                         foreach ($group_fields as $field => $info) {
                             if (!isset($_REQUEST[$field]) ||
@@ -367,7 +374,8 @@ class SocialComponent extends Component implements CrawlConstants
                             $_SESSION['USER_ID'], $_REQUEST['register'],
                             $_REQUEST['member_access'],
                             $_REQUEST['vote_access'],
-                            $_REQUEST['post_lifetime']);
+                            $_REQUEST['post_lifetime'],
+                            $_REQUEST['encryption']);
                         //one exception to setting $group_id
                         $group_id = $group_model->getGroupId($name);
                         return $parent->redirectWithMessage(
@@ -443,7 +451,9 @@ class SocialComponent extends Component implements CrawlConstants
                         ['register', 'REGISTER_TYPE','REGISTER_CODES'],
                         ['member_access', 'MEMBER_ACCESS', 'ACCESS_CODES'],
                         ['vote_access', 'VOTE_ACCESS', 'VOTING_CODES'],
-                        ['post_lifetime', 'POST_LIFETIME', 'POST_LIFETIMES']
+                        ['post_lifetime', 'POST_LIFETIME', 'POST_LIFETIMES'],
+                        ['encryption', 'ENCRYPTION', 'ENCRYPTION_CODES']
+
                         ];
                     $this->updateGroup($data, $group, $update_fields);
                     $data['CURRENT_GROUP']['register'] =
@@ -454,6 +464,8 @@ class SocialComponent extends Component implements CrawlConstants
                         $group['VOTE_ACCESS'];
                     $data['CURRENT_GROUP']['post_lifetime'] =
                         $group['POST_LIFETIME'];
+                    $data['CURRENT_GROUP']['encryption'] =
+                        $group['ENCRYPTION'];
                     $data['SCRIPT'] .= "listenAll('input.user-id', 'click',".
                         " updateCheckedUserIds);";
                     $this->getGroupUsersData($data, $group_id);
diff --git a/src/data/default.db b/src/data/default.db
index 8c7c02818578c626ecf444c46c12fee3b5ab5679..932601b9b539aeb14951d1d78bff232eddf9d63d 100644
GIT binary patch
diff --git a/src/data/default2.db b/src/data/default2.db
new file mode 100644
index 0000000000000000000000000000000000000000..3ab2a411ecfc57a4314e87efff98ea11cacf6bfd
GIT binary patch
diff --git a/src/library/Cipher.php b/src/library/Cipher.php
new file mode 100644
index 00000000..06301ca2
--- /dev/null
+++ b/src/library/Cipher.php
@@ -0,0 +1,148 @@
+<?php
+namespace seekquarry\yioop\library;
+
+use seekquarry\yioop\configs as C;
+/**
+ * Encryption class for adding encryption and
+ * decryption methods. This class uses AES as
+ * a symmetric encryption algorithm.
+ */
+class Cipher
+{
+  protected $key;
+  protected $data;
+  protected $mode;
+  protected $IV;
+
+    /**
+     *
+     * @param string $data data to encrypt
+     * @param string $key key used for encryption and decryption
+     * @param string $mode mode of operation used by the algorithm
+     */
+    function __construct($data = null, $mode = null, $key = null)
+    {
+      $this->setData($data);
+      $this->setMode($mode);
+      $this->setKey($key);
+      $this->setIV("");
+    }
+    /**
+     *
+     * @param string $data data to set
+     */
+    public function setData($data)
+    {
+        $this->data = $data;
+    }
+    /**
+     *
+     * @return string $data
+     */
+    protected function getData()
+    {
+        return $this->data;
+    }
+    /**
+     * @param string $key set key for encryption/decryption
+     */
+    public function setKey($key)
+    {
+        $this->key = $key;
+    }
+    /**
+     *
+     * @return string $key
+     */
+    protected function getKey()
+    {
+        return $this->key;
+    }
+    /**
+     *
+     * @param string $data data to set
+     */
+    public function setMode($mode)
+    {
+        $this->mode = $mode;
+    }
+    /**
+     *
+     * @return string $mode
+     */
+    protected function getMode()
+    {
+        return $this->mode;
+    }
+    /**
+     * checks if parameters are valid
+     * @return boolean
+     */
+    public function validateParams()
+    {
+        if ($this->data != null &&
+            $this->key != null) {
+            return true;
+        } else {
+          return false;
+        }
+    }
+    /**
+     * Sets initialization vector
+     * @return string $IV initialization vector
+     */
+    public function setIV($IV)
+    {
+        $this->IV = $IV;
+    }
+    /**
+     *
+     * @return string $IV initialization vector
+     */
+    protected function getIV()
+    {
+        if ($this->IV == "") {
+            $this->IV = openssl_random_pseudo_bytes(
+                openssl_cipher_iv_length(C\AES_256_CBC));
+        }
+        return $this->IV;
+    }
+    /**
+     * Encrypts the data using the associated key and some random data
+     * @return string $data encrypted data
+     */
+    public function encrypt()
+    {
+        if ($this->validateParams()) {
+            $encrypted = openssl_encrypt($this->getData(), $this->getMode(),
+                $this->getKey(), 0, $this->getIV());
+            // append value of $IV to the encrypted data with a separator
+            // so this can be used during decryption
+            $encrypted = $this->getIV() .  str_repeat("0", 10)  . $encrypted;
+            return $encrypted;
+        } else {
+            throw new Exception('Invlid params!');
+        }
+    }
+    /**
+     * Decrypts the data using the associated key
+     * @return string $data decrypted data
+     * @throws Exception
+     */
+    public function decrypt()
+    {
+        if ($this->validateParams()) {
+            $pos = strpos($this->getData(), str_repeat("0", 10));
+            if ($pos !== false) {
+                $data_parts = explode(str_repeat("0", 10), $this->getData());
+            } else {
+                throw new Exception('Could not split keys');
+            }
+            $decrypted = openssl_decrypt($data_parts[1], $this->getMode(),
+                $this->getKey(), 0, $data_parts[0]);
+            return $decrypted;
+        } else {
+            throw new Exception('Invlid params!');
+        }
+    }
+}
diff --git a/src/library/VersionFunctions.php b/src/library/VersionFunctions.php
index 2282a009..2cfbf716 100644
--- a/src/library/VersionFunctions.php
+++ b/src/library/VersionFunctions.php
@@ -1347,4 +1347,48 @@ function upgradeDatabaseVersion49(&$db)
         FROM_ID INTEGER, TO_GROUP_ID INTEGER, TO_PAGE_NAME VARCHAR(" .
         C\TITLE_LEN ."), PRIMARY KEY(LINK_TYPE_ID, FROM_ID, TO_GROUP_ID,
         TO_PAGE_NAME))");
-}
\ No newline at end of file
+}
+/**
+ * Upgrades a Version 49 version of the Yioop! database to a Version 50 version
+ * @param object $db datasource to use to upgrade.
+ */
+function upgradeDatabaseVersion50(&$db)
+{
+    $dbinfo = ["DBMS" => C\DBMS, "DB_HOST" => C\DB_HOST,
+        "DB_NAME" => C\DB_NAME, "DB_PASSWORD" => C\DB_PASSWORD];
+    $auto_increment = $db->autoIncrement($dbinfo);
+    $serial = $db->serialType($dbinfo);
+    $private_db_class = NS_DATASOURCES . ucfirst(PRIVATE_DBMS)."Manager";
+    $private_dbinfo = ["DBMS" => C\PRIVATE_DBMS, "DB_HOST" => C\PRIVATE_DB_HOST,
+        "DB_NAME" => C\PRIVATE_DB_NAME, "DB_PASSWORD" => C\PRIVATE_DB_PASSWORD];
+    $private_db = new $private_db_class();
+    $private_db->connect(C\PRIVATE_DB_HOST, C\PRIVATE_DB_USER,
+        C\PRIVATE_DB_PASSWORD, C\PRIVATE_DB_NAME);
+    $private_auto_increment = $private_db->autoIncrement($private_dbinfo);
+    $private_serial = $private_db->serialType($private_dbinfo);
+    $private_db->execute("CREATE TABLE TYPE_KEYS (KEY_ID
+        $private_serial PRIMARY KEY $private_auto_increment,
+        TYPE_ID INTEGER, KEY_NAME VARCHAR(30)");
+    $db->execute("CREATE TABLE GROUPS_OLD (GROUP_ID $serial PRIMARY KEY
+        $auto_increment, GROUP_NAME VARCHAR(" . C\SHORT_TITLE_LEN."),
+        CREATED_TIME VARCHAR(" . C\MICROSECOND_TIMESTAMP_LEN . "),
+        OWNER_ID INTEGER, REGISTER_TYPE INTEGER,
+        MEMBER_ACCESS INTEGER, VOTE_ACCESS INTEGER DEFAULT ".
+        C\NON_VOTING_GROUP . ", POST_LIFETIME INTEGER DEFAULT ".
+        C\FOREVER . ")");
+    D\DatasourceManager::copyTable("GROUPS", $db,
+        "GROUPS_OLD", $db);
+    $db->execute("DROP TABLE GROUPS");
+    $db->execute("CREATE TABLE GROUPS (GROUP_ID $serial PRIMARY KEY
+        $auto_increment, GROUP_NAME VARCHAR(" . C\SHORT_TITLE_LEN."),
+        CREATED_TIME VARCHAR(" . C\MICROSECOND_TIMESTAMP_LEN . "),
+        OWNER_ID INTEGER, REGISTER_TYPE INTEGER,
+        MEMBER_ACCESS INTEGER, VOTE_ACCESS INTEGER DEFAULT ".
+        C\NON_VOTING_GROUP . ", POST_LIFETIME INTEGER DEFAULT ".
+        C\FOREVER . ", ENCRYPTION INTEGER DEFAULT 0)");
+    $db->execute("INSERT INTO GROUPS (GROUP_ID,
+        GROUP_NAME, CREATED_TIME, OWNER_ID, REGISTER_TYPE, MEMBER_ACCESS,
+        VOTE_ACCESS, POST_LIFETIME)
+        SELECT GROUP_ID, GROUP_NAME, CREATED_TIME, OWNER_ID, REGISTER_TYPE, MEMBER_ACCESS,
+         VOTE_ACCESS, POST_LIFETIME FROM GROUPS_OLD");
+}
diff --git a/src/locale/en_US/configure.ini b/src/locale/en_US/configure.ini
index afa749af..9644bd95 100644
--- a/src/locale/en_US/configure.ini
+++ b/src/locale/en_US/configure.ini
@@ -243,6 +243,8 @@ social_component_forever = "Never Expires"
 social_component_one_hour = "One Hour"
 social_component_one_day = "One Day"
 social_component_one_month = "One Month"
+social_component_encryption_enable = "Enable"
+social_component_encryption_disable = "Disable"
 social_component_user_activated = "User activated!"
 social_component_users_activated = "Users Activated!"
 social_component_no_user_activated = "User was not activated!"
@@ -923,6 +925,7 @@ managegroups_element_num_users = "%s users"
 managegroups_element_statistics = "Statistics"
 managegroups_element_group_wiki = "Wiki"
 managegroups_element_join = "Join"
+managegroups_element_encryption = "Encryption"
 managegroups_element_edit = "Edit"
 managegroups_element_delete = "Delete"
 managegroups_element_decline = "Decline"
diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index cf00ed4e..f86f850d 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -33,11 +33,13 @@ namespace seekquarry\yioop\models;
 use seekquarry\yioop as B;
 use seekquarry\yioop\configs as C;
 use seekquarry\yioop\library as L;
+use seekquarry\yioop\library\Cipher;
 use seekquarry\yioop\library\MediaConstants;
 use seekquarry\yioop\library\WikiParser;
 use seekquarry\yioop\library\processors\ImageProcessor;
 use seekquarry\yioop\models\ImpressionModel;
 
+
 /**
  * This is class is used to handle
  * db results related to Group Administration. Groups are collections of
@@ -243,15 +245,16 @@ class GroupModel extends Model implements MediaConstants
      */
     public function addGroup($group_name, $user_id, $register = C\REQUEST_JOIN,
         $member = C\GROUP_READ, $voting = C\NON_VOTING_GROUP,
-        $post_lifetime = C\FOREVER)
+        $post_lifetime = C\FOREVER, $encryption = 0)
     {
         $db = $this->db;
+        $private_db = $this->private_db;
         $timestamp = L\microTimestamp();
         $sql = "INSERT INTO GROUPS (GROUP_NAME, CREATED_TIME, OWNER_ID,
-            REGISTER_TYPE, MEMBER_ACCESS, VOTE_ACCESS, POST_LIFETIME)
-            VALUES (?, ?, ?, ?, ?, ?, ?);";
+            REGISTER_TYPE, MEMBER_ACCESS, VOTE_ACCESS, POST_LIFETIME,
+            ENCRYPTION) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
         $db->execute($sql, [$group_name, $timestamp, $user_id,
-            $register, $member, $voting, $post_lifetime]);
+            $register, $member, $voting, $post_lifetime, $encryption]);
         $sql = "SELECT G.GROUP_ID AS GROUP_ID FROM ".
             " GROUPS G WHERE G.GROUP_NAME = ?";
         $result = $db->execute($sql, [$group_name]);
@@ -268,6 +271,11 @@ class GroupModel extends Model implements MediaConstants
             $db);
         ImpressionModel::initWithDb(C\PUBLIC_GROUP_ID, $last_id,
             C\GROUP_IMPRESSION, $db);
+        if ($this->isGroupEncrypted($last_id)) {
+            $sql = "INSERT INTO TYPE_KEYS (TYPE_ID, KEY_NAME) VALUES (?, ?)";
+            $encrypt_key = openssl_random_pseudo_bytes(32);
+            $private_db->execute($sql, [$last_id, $encrypt_key]);
+        }
         return $last_id;
     }
     /**
@@ -377,6 +385,23 @@ class GroupModel extends Model implements MediaConstants
         return $row['GROUP_NAME'];
     }
     /**
+     * Check whether group's encryption is enabled or not
+     *
+     * @param int $group_id to check for encryption value
+     * @return boolean isEncryptionEnabled value of encryption
+     */
+    public function isGroupEncrypted($group_id)
+    {
+        $db = $this->db;
+        $sql = "SELECT G.ENCRYPTION AS ENCRYPTION FROM ".
+            "GROUPS G WHERE G.GROUP_ID = ? ";
+        $result = $db->execute($sql, [$group_id]);
+        if (!$row = $db->fetchArray($result)) {
+            return false;
+        }
+        return $row['ENCRYPTION'];
+    }
+    /**
      * Delete a group from the database and any associated data in
      * GROUP_ITEM and USER_GROUP tables.
      *
@@ -385,6 +410,7 @@ class GroupModel extends Model implements MediaConstants
     public function deleteGroup($group_id)
     {
         $db = $this->db;
+        $private_db = $this->private_db;
         $params = [$group_id];
         $sql = "DELETE FROM GROUPS WHERE GROUP_ID=?";
         $db->execute($sql, $params);
@@ -394,6 +420,8 @@ class GroupModel extends Model implements MediaConstants
         $db->execute($sql, $params);
         $sql = "DELETE FROM GROUP_PAGE_HISTORY WHERE GROUP_ID=?";
         $db->execute($sql, $params);
+        $sql = "DELETE FROM TYPE_KEYS WHERE TYPE_ID=?";
+        $private_db->execute($sql, $params);
     }
     /**
      * Return the type of the registration for a group given by $group_id
@@ -452,10 +480,11 @@ class GroupModel extends Model implements MediaConstants
         $where .= " UG.GROUP_ID= :group_id".
             " AND  UG.GROUP_ID=G.GROUP_ID AND OWNER_ID = O.USER_ID";
         $sql = "SELECT G.GROUP_ID AS GROUP_ID,
-            G.GROUP_NAME AS GROUP_NAME, G.OWNER_ID AS OWNER_ID,
-            O.USER_NAME AS OWNER, REGISTER_TYPE, UG.STATUS AS STATUS,
-            G.MEMBER_ACCESS AS MEMBER_ACCESS, G.VOTE_ACCESS AS VOTE_ACCESS,
-            G.POST_LIFETIME AS POST_LIFETIME, UG.JOIN_DATE AS JOIN_DATE
+            G.GROUP_NAME AS GROUP_NAME, G.ENCRYPTION AS ENCRYPTION,
+            G.OWNER_ID AS OWNER_ID, O.USER_NAME AS OWNER, REGISTER_TYPE,
+            UG.STATUS AS STATUS, G.MEMBER_ACCESS AS MEMBER_ACCESS,
+            G.VOTE_ACCESS AS VOTE_ACCESS, G.POST_LIFETIME AS POST_LIFETIME,
+            UG.JOIN_DATE AS JOIN_DATE
             FROM GROUPS G, USERS O, USER_GROUP UG $where " .
             $db->limitOffset(1);
         $result = $db->execute($sql, $params);
@@ -567,6 +596,22 @@ class GroupModel extends Model implements MediaConstants
         return $row['NUM'];
     }
     /**
+     * Get the key of the group.
+     *
+     * @param int $group_id to get key for
+     * @return string key of the group
+     */
+    public function getGroupKey($group_id)
+    {
+        $private_db = $this->private_db;
+        $sql = "SELECT KEY_NAME FROM TYPE_KEYS WHERE TYPE_ID=?";
+        $result = $private_db->execute($sql, [$group_id]);
+        if ($result) {
+            $row = $private_db->fetchArray($result);
+        }
+        return $row['KEY_NAME'];
+    }
+    /**
      * To update the OWNER_ID of a group
      *
      * @param string $user_id the id of the user who becomes the admin of group
@@ -643,6 +688,12 @@ class GroupModel extends Model implements MediaConstants
             return false;
         }
         $row = $db->fetchArray($result);
+        if ($this->isGroupEncrypted($row['GROUP_ID'])) {
+            // Decrypt group's title and description
+            $key = $this->getGroupKey($row['GROUP_ID']);
+            $row['TITLE'] = $this->cipher($row['TITLE'], $key, 1);
+            $row['DESCRIPTION'] = $this->cipher($row['DESCRIPTION'], $key, 1);
+        }
         return $row;
     }
     /**
@@ -718,6 +769,11 @@ class GroupModel extends Model implements MediaConstants
         if ($post_time == 0) {
             $post_time = time();
         }
+        if ($this->isGroupEncrypted($group_id)) {
+            $keys = $this->getGroupKey($group_id);
+            $title = $this->cipher($title, $keys);
+            $description = $this->cipher($description, $keys);
+        }
         $sql = "INSERT INTO GROUP_ITEM (PARENT_ID, GROUP_ID, USER_ID, TITLE,
             DESCRIPTION, PUBDATE, EDIT_DATE, TYPE) VALUES
             (?, ?, ?, ?, ?, ?, ?, ?)";
@@ -732,6 +788,16 @@ class GroupModel extends Model implements MediaConstants
         }
         return $id;
     }
+    public function cipher($data, $key, $type = 0)
+    {
+        $cipher = new Cipher($data, C\AES_256_CBC, $key);
+        if ($type == 0) {
+            $data = $cipher->encrypt();
+        } else {
+            $data = $cipher->decrypt();
+        }
+        return $data;
+    }
     /**
      * Updates a group feed item's title and description. This assumes
      * the given item already exists.
@@ -744,6 +810,12 @@ class GroupModel extends Model implements MediaConstants
     {
         $db = $this->db;
         $edit_date = time();
+        $group_item = $this->getGroupItem($id);
+        if ($this->isGroupEncrypted($group_item['GROUP_ID'])) {
+            $keys = $this->getGroupKey($group_item['GROUP_ID']);
+            $title = $this->cipher($title, $keys);
+            $description = $this->cipher($description, $keys);
+        }
         $sql = "UPDATE GROUP_ITEM SET TITLE=?, DESCRIPTION=?,
             EDIT_DATE=? WHERE ID=?";
         $db->execute($sql, [$title, $description, $edit_date, $id]);
@@ -895,6 +967,17 @@ class GroupModel extends Model implements MediaConstants
             }
         }
         unset($groups[$i]); //last one will be null
+        $i = 0;
+        foreach ($groups as $group_key => $group_value) {
+            if ($this->isGroupEncrypted($group_value['GROUP_ID'])) {
+                $keys = $this->getGroupKey($group_value['GROUP_ID']);
+                $groups[$i]['TITLE'] = $this->cipher(
+                    $group_value['TITLE'], $keys, 1);
+                $groups[$i]['DESCRIPTION'] = $this->cipher(
+                    $group_value['DESCRIPTION'], $keys, 1);
+            }
+            $i++;
+        }
         return $groups;
     }
     /**
diff --git a/src/models/Model.php b/src/models/Model.php
index 17f2d9b1..6ad32664 100755
--- a/src/models/Model.php
+++ b/src/models/Model.php
@@ -67,6 +67,14 @@ class Model implements CrawlConstants
      * @var string
      */
     public $db_name;
+    /** Reference to a private DatasourceManager
+     * @var object
+     */
+    public $private_db;
+    /** Name of the private search engine database
+     * @var string
+     */
+    public $private_db_name;
     /**
      * Associative array of page summaries which might be used to
      * override default page summaries if set.
@@ -102,11 +110,17 @@ class Model implements CrawlConstants
     public function __construct($db_name = C\DB_NAME, $connect = true)
     {
         $db_class = C\NS_DATASOURCES . ucfirst(C\DBMS). "Manager";
+        $private_db_class = C\NS_DATASOURCES . ucfirst(C\PRIVATE_DBMS).
+            "Manager";
         $this->db = new $db_class();
+        $this->private_db = new $private_db_class();
         if ($connect) {
             $this->db->connect();
+            $this->private_db->connect(C\PRIVATE_DB_HOST, C\PRIVATE_DB_USER,
+                C\PRIVATE_DB_PASSWORD, C\PRIVATE_DB_NAME);
         }
         $this->db_name = $db_name;
+        $this->private_db_name = C\PRIVATE_DB_NAME;
     }
     /**
      * Given an array page summaries, for each summary extracts snippets which
diff --git a/src/models/ProfileModel.php b/src/models/ProfileModel.php
index ba988af0..d1bb2c0c 100755
--- a/src/models/ProfileModel.php
+++ b/src/models/ProfileModel.php
@@ -60,8 +60,9 @@ class ProfileModel extends Model
         'LINK_WEIGHT', 'LOGO', 'M_LOGO', 'MAIL_PASSWORD',  'MAIL_SECURITY',
         'MAIL_SENDER', 'MAIL_SERVER', 'MAIL_SERVERPORT', 'MAIL_USERNAME',
         'MEMCACHE_SERVERS', 'MIN_RESULTS_TO_GROUP', 'NAME_SERVER',
-        'MEDIA_MODE',
-        'PROXY_SERVERS', 'RECOVERY_MODE', 'REGISTRATION_TYPE', 'RESULT_SCORE',
+        'MEDIA_MODE', 'PRIVATE_DB_NAME', 'PRIVATE_DB_HOST', 'PRIVATE_DBMS',
+        'PRIVATE_DB_PASSWORD', 'PRIVATE_DB_USER', 'PROXY_SERVERS',
+        'RECOVERY_MODE', 'REGISTRATION_TYPE', 'RESULT_SCORE',
         'ROBOT_INSTANCE','RSS_ACCESS',
         'SEARCH_ANALYTICS_MODE', 'SEARCHBAR_PATH',
         'SEND_MAIL_MEDIA_UPDATER', 'SERVER_ALPHA', 'SESSION_NAME',
@@ -83,6 +84,7 @@ class ProfileModel extends Model
      * @var array
      */
     public $create_statements;
+    public $private_create_statements;
     /**
      * {@inheritDoc}
      *
@@ -94,6 +96,7 @@ class ProfileModel extends Model
     {
         parent::__construct($db_name, $connect);
         $this->create_statements = [];
+        $this->private_create_statements = [];
     }
     /**
      * Used to construct $this->create_statements, the list of all SQL
@@ -216,7 +219,7 @@ class ProfileModel extends Model
                 OWNER_ID INTEGER, REGISTER_TYPE INTEGER,
                 MEMBER_ACCESS INTEGER, VOTE_ACCESS INTEGER DEFAULT ".
                 C\NON_VOTING_GROUP . ", POST_LIFETIME INTEGER DEFAULT ".
-                C\FOREVER . ")",
+                C\FOREVER . ", ENCRYPTION INTEGER DEFAULT 0)",
             /* NOTE: We are not using singular name GROUP for GROUPS as
                GROUP is a reserved SQL keyword
              */
@@ -331,6 +334,21 @@ class ProfileModel extends Model
             "VERSION" => "CREATE TABLE VERSION(ID INTEGER PRIMARY KEY)",
             ];
     }
+    public function initializeSqlPrivate($dbm, $dbinfo)
+    {
+        $auto_increment = $dbm->autoIncrement($dbinfo);
+        $serial = $dbm->serialType($dbinfo);
+        /**
+         * SQL statements used to create the Yioop database. Some of the
+         * these statements could use UNIQUE on some the columns that are
+         * later used in CREATE INDEX statements. However, because of
+         * restrictions on the number of bytes (not chars) in MYSQL for keys
+         * this has not been done.
+         */
+        $this->private_create_statements = ["TYPE_KEYS" =>
+            "CREATE TABLE TYPE_KEYS (KEY_ID $serial PRIMARY KEY
+            $auto_increment, TYPE_ID INTEGER, KEY_NAME VARCHAR(30))"];
+    }
     /**
      * Creates a folder to be used to maintain local information about this
      * instance of the Yioop/SeekQuarry engine
@@ -615,7 +633,7 @@ EOT;
      */
     public function createDatabaseTables($dbm, $dbinfo, $skip_list = [])
     {
-        $this->initializeSQL($dbm, $dbinfo);
+        $this->initializeSql($dbm, $dbinfo);
         $create_statements = $this->create_statements;
         foreach ($create_statements as $table_or_index => $statement) {
             if (in_array($table_or_index, $skip_list)) {
@@ -628,6 +646,21 @@ EOT;
         }
         return true;
     }
+    public function createDatabaseTablesPrivate($dbm, $dbinfo, $skip_list = [])
+    {
+        $this->initializeSqlPrivate($dbm, $dbinfo);
+        $private_create_statements = $this->private_create_statements;
+        foreach ($private_create_statements as $table_or_index => $statement) {
+            if (in_array($table_or_index, $skip_list)) {
+                continue;
+            }
+            if (!$result = $dbm->execute($statement)) {
+                echo $statement." ERROR!";
+                return false;
+            }
+        }
+        return true;
+    }
     /**
      * Checks if $dbinfo provides info to connect to an working instance of
      * app db.
diff --git a/src/views/elements/ManagegroupsElement.php b/src/views/elements/ManagegroupsElement.php
index cbee1cac..6f9907eb 100755
--- a/src/views/elements/ManagegroupsElement.php
+++ b/src/views/elements/ManagegroupsElement.php
@@ -418,6 +418,31 @@ class ManagegroupsElement extends Element
                         $data["POST_LIFETIMES"],
                         $data['CURRENT_GROUP']['post_lifetime']);
                     ?></td></tr>
+            <tr><th class="table-label"><label for="encryption"><?=
+                tl('managegroups_element_encryption')?></label>:</th>
+                <?php
+                if ($editgroup) {
+                    ?>
+                    <td><input type="text" id="encryption"
+                        name="encryption"
+                        value="<?= ($data['CURRENT_GROUP']['encryption'] == 1) ?
+                              "Enabled" : "Disabled" ?>"
+                        class="narrow-field" <?php
+                        if (isset($data['CURRENT_GROUP']['encryption'])) {
+                            e(' disabled="disabled" ');
+                        }
+                    ?> /></td><?php
+                }
+                if ($creategroup) { ?>
+                <td><?php
+                    $this->view->helper("options")->render(
+                        "encryption", "encryption",
+                        $data["ENCRYPTION_CODES"],
+                        $data['CURRENT_GROUP']['encryption']);
+                    ?></td>
+                <?php
+                }
+            ?></tr>
         <?php
         }
         if ($editgroup) {
-- 
2.11.0 (Apple Git-81)

