Skip to content

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

Merged
mbabker merged 6 commits into
joomla:stagingfrom
shumisha:auth_update
Feb 22, 2014
Merged

Improved Joomla! extensions update handling of paid for extensions#2769
mbabker merged 6 commits into
joomla:stagingfrom
shumisha:auth_update

Conversation

@shumisha
Copy link
Copy Markdown
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
Copy Markdown
Contributor Author

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
Copy Markdown
Contributor

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
Copy Markdown
Contributor Author

Bakual pushed 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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants