Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved Joomla! extensions update handling of paid for extensions #2769

Merged
merged 6 commits into from Feb 22, 2014

Conversation

@shumisha
Copy link
Contributor

@shumisha shumisha commented Jan 8, 2014

Hi,

In starting to use the paid-for extensions updater recently introduced in 3.x (tracker 32684, #2508), I came across some limitations that make it difficult for us to actually use it.
Therefore I'm proposing the following small contribution to make this feature a bit more universal.

What could be improved:

  • cannot use in J! 2.5 (w/o backporting, ie lots of work and maintenance)
  • pass all credentials into the url, which means credentials are stored in servers and proxies log files along the way, for all to view
  • not easily made dynamic: ie download credentials are "hardcoded" in the update sites DB table, which allow "replaying", ie: once somebody has sniffed credentials, they can use them at will, an unlimited number of times, and pass them along to anyone
  • cannot easily share credentials between extensions, ie one main component, controlling updates of various plugins or language files for instance

The proposal is simply to add firing an event at the right time and place, to allow an extension-supplied plugin to alter the url and/or headers on the fly as required by the extension supplier authorization mechanism.

Drawbacks:

  • need to add a plugin to respond to the event, easy for larger extension which may already have one or more plugins, more work for those who don't

Benefits:

  • simple, backward compatible change (4 lines added in one file)
  • same 4 lines and same plugin can be used the same on J! 2.5, providing Joomla update for paid extension on 2.5 as well
  • fairly simple plugin, I'll provide sample one
  • same plugin can control update for several extensions
  • can use urls var or headers to pass credentials, less data stored in log files in the later case
  • can use dynamic, time stamped requests to avoid replay

This PR is backward compatible and doesn't touch existing code, it only adds a bit more flexibility.

Also see tracker item: http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&tracker_item_id=33096&start=0

@shumisha
Copy link
Contributor Author

@shumisha shumisha commented Jan 8, 2014

Here is a sample plugin to respond the download event, and insert credentials in the download request. The plugin should be in the "installer" group, but any other plugin type is fine as long as you make sure the plugin is loaded when needed.

<?php
/**
 * @ant_title_ant@
 *
 * @author      @ant_author_ant@
 * @copyright   @ant_copyright_ant@
 * @package     @ant_package_ant@
 * @license     @ant_license_ant@
 * @version     @ant_version_ant@
 * @date        @ant_current_date_ant@
 */

defined('_JEXEC') or die;

/**
 * Handle commercial extension update authorization
 *
 * @package     MyPackage
 * @subpackage  MyPackage.update
 * @since       2.5
 */
class PlgInstallerSample extends JPlugin
{
    /**
     * @var    String  base update url, to decide whether to process the event or not
     * @since  2.5
     */
    private $baseUrl = 'http://update.example.com/subscribers/com_sample';

    /**
     * @var    String  your extension identifier, to retrieve its params
     * @since  2.5
     */
    private $extension = 'com_sample';

    /**
     * Handle adding credentials to package download request
     *
     * @param   string  $url        url from which package is going to be downloaded
     * @param   array   $headers    headers to be sent along the download request (key => value format)
     *
     * @return  boolean true        Always true, regardless of success
     *
     * @since   2.5
     */
    public function onInstallerBeforePackageDownload(&$url, &$headers)
    {
        // are we trying to update our extension?
        if (strpos($url, $this->baseUrl) !== 0)
        {
            return true;
        }

        // read credentials from extension params or any other source
        $credentials = $this->fetchCredentials($url, $headers);

        // bind credentials to request, either in the urls, or using headers
        // or a combination of both
        $this->bindCredentials($credentials, $url, $headers);

        return true;
    }

    /**
     * Retrieve user credentials, adjust as needed based on extension being processed
     *
     * @return mixed an array with credentials (access, secret)
     */
    private function fetchCredentials($url, $headers)
    {
        // fetch credentials from extension parameters, or
        // wherever you want to store them
        JLoader::import('joomla.application.component.helper');
        $component = JComponentHelper::getComponent($this->extension);
        $credentials = array('access' => $component->params->get('update_credentials_access', ''),
            'secret' => $component->params->get('update_credentials_secret', ''));
        return $credentials;
    }

    /**
     * Bind credentials to the download request. Can be done by adding them to 
     * the download url, or using headers, or any other method you like
     * As a sample, below is (close to) what we use, ie authorization headers, so that access and secrets are 
     * not stored in web servers and proxies log files
     *
     * @param   array   $credentials    whatever credentials were retrieved for the current user/website
     * @param   string  $url            url from which package is going to be downloaded
     * @param   array   $headers        headers to be sent along the download request (key => value format)
     * 
     * @return void
     */
    private function bindCredentials($credentials, &$url, &$headers)
    {
        $headers['X-download-auth-ts'] = time();
        $headers['X-download-auth-access'] = $credentials['access'];
        $headers['X-download-auth-token'] = sha1($headers['X-download-auth-ts'] . mt_rand() . $credentials['secret'] . $url);
        $headers['X-download-auth-sig'] = sha1(
            $credentials['access'] . $headers['X-download-auth-token'] . $credentials['secret'] . $headers['X-download-auth-ts'] . $this->extension);
    }
}
@spignataro
Copy link
Contributor

@spignataro spignataro commented Jan 16, 2014

I tested this pull request and it doesn't seem to effect anything. My component still updated properly.

However I did not create a plugin to test the additional install plugin trigger.

@shumisha
Copy link
Contributor Author

@shumisha shumisha commented Jan 16, 2014

Steven, FYI, I have attached a plugin implementing Akeeba Release system to the tracker item: http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&tracker_item_id=33133&start=0

(same plugin works both on 2.x and 3.x)

mbabker added a commit that referenced this pull request Feb 22, 2014
Improved Joomla! extensions update handling of paid for extensions
@mbabker mbabker merged commit 4d1d991 into joomla:staging Feb 22, 2014
1 check passed
1 check passed
default The Travis CI build passed
Details
Bakual added a commit to Bakual/joomla-cms that referenced this pull request May 12, 2014
Improved Joomla! extensions update handling of paid for extensions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

4 participants
You can’t perform that action at this time.