Jomres framework and Encryption/Decryption

Introduction

Jomres, as a plugin for both Joomla and Wordpress, does not stand in isolation. Instead it is part of a larger set of moving parts that make up your Content Management System.

It's not possible (for us) to lock down an installation of Jomres so that malicious or faulty code in other plugins cannot access the data in Jomres tables. I can secure the system as much as possible, however vulnerabilities in other areas of the CMS or it's plugins are outside of my control and there's always a risk that guest or manager data might be exposed to unauthorised users.

Article 33 of the GDPR outlines personal data breach reporting requirements, specifically addressing when organisations must report breaches and within what timescales. Failure to report breaches, and address those breaches after the fact, can result in heavy fines. As the developer of GPL software that offers absolutely no warrenties whatsoever I would not be affected, however my clients would be and I consider it both a professional and moral duty to ensure that the risks to them are minimised as much as possible.

There are cases where reporting won’t be required e.g. where personal data would not result in a risk or is not accessible e.g. in the case of annoymization or encryption (yes, that’s right – if your personal data is encrypted using a suitably strong mechanism and is not feasibly accessible then you may not have to report it!).

GDPR breach reporting

While planning how Jomres would be compliant with the GDPR I realised that this was the right time to introduce the user encryption that I had wanted for a long time to implement. The goal was that, even if guest and manager PII (personally identifyable information) should somehow be exposed, this exposure should do no harm because this information would be encrypted and of no use to attackers.

As a result, in the spring of 2018 Jomres was modified so that all existing guest data was encrypted in new installations and any sites that were updated with the newest version. This brings it's own set of problems in that coders find it more difficult to access guest data when generating their own reporting tools. In this article I'll describe how those coders should use the Jomres framework to extract the information that they want.


An example module

To decrypt PII for your own use, you cannot simply access the Jomres tables that contain this data. Instead you need to include the Jomres framework and use it's built-in functionality to decode an individual's data.

Given that the majority of Jomres users are hosted on Joomla, this example will refer to a "module", however Wordpress developers will be able to use similar concepts when building widgets or other applications for Wordpress. If you want to see one such example, open up the "Jomres Search" widget included in the Jomres Wordpress Quickstarts. The principles are largely the same.

Including the framework is simple enough, however I'll break down the steps, for clarity. I will assume that you already have sufficient coding skills to build a basic module or application for Joomla or Wordpress.

if (!defined('_JOMRES_INITCHECK'))
    define('_JOMRES_INITCHECK', 1 );

if (!defined('JOMRES_ROOT_DIRECTORY'))
    {
    if (file_exists(dirname(__FILE__).'/../../jomres_root.php'))
        require_once (dirname(__FILE__).'/../../jomres_root.php');
    else
        define ( 'JOMRES_ROOT_DIRECTORY' , "jomres" ) ;
   }
   
require_once(JOMRES_ROOT_DIRECTORY.DIRECTORY_SEPARATOR.'core-plugins'.DIRECTORY_SEPARATOR.'alternative_init'.DIRECTORY_SEPARATOR.'alt_init.php');


If not already defined, we will define _JOMRES_INITCHECK otherwise Jomres scripts will not run.

Next we will check for the existance of a definition called JOMRES_ROOT_DIRECTORY. Before getting permission to be listed on the Wordpress plugin directory it was their insistence that whilst Jomres might be able to run from it's own unique path, that path must be changable by the site administrator. Although I thought it was an unnecessary complication, those were the rules and I have to abide by them.

Finally, we will include a Jomres plugin called alt_init.php. This plugin is able to include the entire Jomres framework without you needing to do any more work.

Decrypting guest data

Before we start, you will find that there are two guest tables : xxx_jomres_guests and xxx_jomres_guest_profile. Why, you ask?

The reason is primarily historic. Jomres was first conceived in 2005, and since then millions of lines of code have been added and removed from it. The jomres_guests table was the first one added, and the guest_profile table was added some years later as a way for guests to change their own data and have that change trickle down to hotels that they had previously booked at. At the same time, I didn't want changes made by one hotel to be reflected back to the guest profile table and thence to other hotels, perhaps the hotel has rather unprofessionally left some comments that should stay with just that hotel?

If you want to access guest data, if the user you are accessing is a registered user in the system, you can query them by their cms id and look for their details in the guest profile table. See Method 2.

If the guest might not be a registered user you must first know the id of the property that they are a guest of, and of course their id. Once known you can use the jrportal_guests class to pull their data out. This class will handle the decryption for you.

Method 1

    jr_import( 'jrportal_guests' );
    $jrportal_guests = new jrportal_guests();
    $jrportal_guests->id = $id;
    $jrportal_guests->property_uid = $property_uid;
    if ($id > 0 && $jrportal_guests->get_guest()) {
        $firstname =  $jrportal_guests->firstname;
    }

Method 2

In the next scenario, the user is a registered user, perhaps they're logged in and you want to pull their information for display into a module or widget.
    
In this module, we will assume that you already know the guest's cms id. If you don't, you can get it by doing

        $user = JFactory::getUser();
        $id = $user->get('id');
        
in Joomla, or

        $user = wp_get_current_user();
        $id = $user->get('ID');
        
in Wordpress.

We will include the encryption class from Jomres, and create a new instance.

    jr_import('jomres_encryption');
    $jomres_encryption = new jomres_encryption();


Now we are ready to pull the data from the guest profile table.

    $query = 'SELECT enc_firstname,enc_surname,enc_house,enc_street,enc_town,enc_county,enc_country,enc_postcode,enc_tel_landline,enc_tel_mobile,enc_email FROM #__jomres_guest_profile WHERE cms_user_id = '.(int) $id.' LIMIT 1';
    $guestData = doSelectSql($query);

This pulls the encrytpted data from the database. Next we need to decrypt it.

    if (!empty($guestData)) {
        foreach ($guestData as $data) {
            
            $firstname = $jomres_encryption->decrypt($data->enc_firstname);
            
        }

You now have the guest's first name as stored in the database for use in your own code.


Encrypting guest data

Now that you know how to decrypt guest data, encryption you will see is the opposite.

To save a guest as a guest of a given property :

        jr_import( 'jrportal_guests' );
        $jrportal_guests = new jrportal_guests();
        $jrportal_guests->id = $id;
        $jrportal_guests->property_uid = $property_uid;
        
        if ($id > 0 ) {
            $jrportal_guests->get_guest(); // if we don't get_guest then the mos_id ( cms_id) will get reset when the guest is saved
        }
        
        $jrportal_guests->firstname = jomresGetParam($_REQUEST, 'firstname', '');
        $jrportal_guests->surname = jomresGetParam($_REQUEST, 'surname', '');
        $jrportal_guests->house = jomresGetParam($_REQUEST, 'house', '');
        $jrportal_guests->street = jomresGetParam($_REQUEST, 'street', '');
        $jrportal_guests->town = jomresGetParam($_REQUEST, 'town', '');
        $jrportal_guests->region = jomresGetParam($_REQUEST, 'region', '');
        $jrportal_guests->country = jomresGetParam($_REQUEST, 'guest_country', '');
        $jrportal_guests->postcode = jomresGetParam($_REQUEST, 'postcode', '');
        $jrportal_guests->tel_landline = jomresGetParam($_REQUEST, 'landline', '');
        $jrportal_guests->tel_mobile = jomresGetParam($_REQUEST, 'mobile', '');
        $jrportal_guests->email = jomresGetParam($_REQUEST, 'email', '');
        $jrportal_guests->vat_number = jomresGetParam($_REQUEST, 'vat_number', '');
        $jrportal_guests->discount = (int) jomresGetParam($_REQUEST, 'discount', 0);
        $jrportal_guests->blacklisted = (int) jomresGetParam($_REQUEST, 'blacklisted', 0);

        if ( $id > 0 )
            $jrportal_guests->commit_update_guest();
        else
            $jrportal_guests->commit_new_guest();
        
        
Guest profiles table

Unfortunately I don't yet have a class for guest profiles, so have a look in j06005save_my_account.class.php to see how guest profile records are saved.