I often use this module to download a snapshot of a live site into a dev environment. This to be able to to testing and development of new features. Sometimes I also need to share the snapshot with other people.

A very useful feature addition in these cases would be to be able to clean the passwords for all users. An option to set a default password for all users could also be made available.

Then it would be very easy for me, and my team, to also test new features as various users. Also, sometimes a single user has issues on the site that no other user has and this would then allow me to set up a test site and be able to log in as that user with a current snapshot of the live site.

This option should only be available when doing a manual backup of the site.

Comments

ronan’s picture

This is a good idea, and shouldn't be too tough to implement in theory. There is currently no mechanism whatsoever in B&M to alter the data before it is saved to the backup file. This is more or less by design as I feel like it's a slippery slope from small data tweaks like this (or overriding server-specific variables, altering db prefixes etc.) to a complex and dangerous place where I have to account for an infinite array of different configurations. There's a middle ground, but my strategy so far is to simply treat the data as a black box.

I'll probably have to cross that sacred line at some point as there are lots of helpful features I (or others) could add once I do, but I want to make sure I do so carefully and in a way that won't get me into too much trouble or limit future development.

tsvenson’s picture

Glad you liked my idea. I completely agree with your concerns that tinkering with the data in the tables can lead to unwanted results and should be avoided.

In this case I think the benefit of this feature is big enough to warrant it. As long as it is implemented right, the risk can be almost zero.

My proposal for implementation is:

  • Only available for manual or download.
  • "NoPass" is always added to the file. Maybe even at the beginning to really stand out.
  • The setting for cleaning the password is non-sticky. That is, it has to manually be set for each backup.

I think that would make it practically impossible to screw things up, while still make the process of cleaning the password easy and straightforward.

znerol’s picture

Status: Active » Needs review
StatusFileSize
new2.59 KB

Attached is a patch which introduces a new config option alterdata_tables for the mysql database. A new filter hook backup_row_alter($row, $table, $settings) is invoked for all rows of the selected tables. Filters implementing this hook may alter/remove rows before they go into the dump. Rows of tables which were not selected for alteration will never be sent to the filter.

An example filter class is given here:

/**
 * B&R Filter for removing sensitive data while dumping.
 */
class example_cleanup_filter extends backup_migrate_filter {
  private static $filter_variables = array(
    'cron_key',
    'drupal_private_key',
    // etc...
  );

  /**
   * Change row data on-the-fly.
   */
  public function backup_row_alter($row, $table, $settings) {
    switch ($table['name']) {
    case 'variable':
      $row = $this->_example_cleanup_filter_variable($row);
      break;

    case 'users':
      $row = $this->_example_cleanup_filter_user($row);
      error_log(print_r($row, TRUE));
      break;
    }
    return $row;
  }

  /**
   * Remove variables with sensitive content.
   */
  protected function _example_cleanup_filter_variable($row) {
    if (!in_array($row['name'], static::$filter_variables)) {
      return $row;
    }
  }

  /**
   * Anonymize user records.
   */
  protected function _example_cleanup_filter_user($row) {
    $uid = $row['uid'];

    // Remove personal data and password hash
    if ($uid > 0) {
      $fakename = 'user-' . $uid;
      $fakemail = $fakename . '@example.com';

      $row['uid'] = $uid;
      $row['name'] = $fakename;
      $row['pass'] = '';
      $row['mail'] = $fakemail;
      $row['theme'] = '';
      $row['signature'] = '';
      $row['signature_format'] = '';
      $row['created'] = 0;
      $row['access'] = 0;
      $row['login'] = 0;
      $row['status'] = $row['status'];
      $row['timezone'] = '';
      $row['language'] = '';
      $row['picture'] = 0;
      $row['init'] = '';
      $row['data'] = '';
    }

    // Reset admin account
    if ($uid == 1) {
      require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
      $row['name'] = 'admin';
      $row['mail'] = 'admin@example.com';
      $row['pass'] = user_hash_password('admin');
    }

    return $row;
  }
}

Obviously there is room for improvement. For example the cleanup-filter could expose a configuration form etc.

gisle’s picture

I think that introducing features to alter the data in a backup module is a slippery slope.

And while the solution proposed by znerol goes some way to create a generic method to introduce filter classes in the backup workflow, my feeling is that altering data does not belong in the backup workflow.

While I sometimes need do to this (and purging personal data is only one of many possible use cases), my solution to this has been to set up a intermediary custom Drupal site for the sole purpose of altering some of the data.

There already exists some modules that let you do batch updates of the database. For instance Views Bulk Operations (VBO) and Rules may be used.

However, in most cases, I prefer not to use VBO, but instead to write a small custom module that loops over all the records in the database and alter them.

My workflow is to first empty the intermediary site, then I use B&M to import the data into the site, alter the data (with VBO or a custom module), and finally use B&M to back up the site after altering the data. I just keep this intermediary site around with all the data altering modules I've written over time, and then use the general Drupal module admin interface to enable or disable a particular custom module.

The filter class proposed by znerol in #3 may be used in this context, if it is rerolled into a generic module for altering data (for those who think that this is a better solution than using VBO or writing custom modules for each alteration).

znerol’s picture

@gisle: I share your concerns regarding backup integrity. Nobody should rely solely on B&M in order to be prepared when disaster strikes. The README makes this pretty clear:

MAKE SURE THAT THIS MODULE IS NOT YOUR ONLY FORM OF BACKUP.

I use B&M for scheduled backups of my database and public file directory, however I also have other, more reliable backup mechanisms in place. Creating and saving backups is only one of two use-cases this module aims at. I also often use B&M to quickly migrate a snapshot of the database to my local dev-environment (yes, I know, drush could do that too, but drush is not always an option).

I cannot speak for other people, but for me the benefit of B&M is more the M and not the B.

In the ideal world, developers would work with a database filled with test-records. All the necessary configuration would reside in the SCM, the database would only be used for content and not for configuration. In the Drupal-reality things are different, and developers often need to build new features around existing content.

I do not know how many Drupal developers carry dumps with data from live sites around on their laptopts. But I fear those who do probably did not all follow the procedure gisle outlined before in order to purge sensitive information prior of downloading the data to their computers.

Depending on the infrastructure, the site and the development-practices, the possible damage caused by data-leakage can be bigger for a company than a potentially corrupt backup snapshot from the B&M module.

I would prefer to concentrate on the technical argument now. If exposing a new option is too intrusive, I propose to refactor the backup_migrate_destination_db_mysql class in a way, such that anyone can implement data-alteration in a subclass. A possible solution would be to extract the code which is converting a $row into the $items array in a separate method. Patch attached.

znerol’s picture

StatusFileSize
new1.65 KB

Sorry, correct patch attached.

yannickoo’s picture

Status: Needs review » Closed (fixed)
+++ b/includes/destinations.db.mysql.inc
@@ -360,6 +353,22 @@ class backup_migrate_destination_db_mysql extends backup_migrate_destination_db
+  function _db_prepare_row($row) {

That function name sounds very generally, we should rename that to something starting with backup_migrate.

znerol’s picture

Status: Closed (fixed) » Needs review

@yannickoo: Its not obvious but this is not a function but an instance method. Therefore there is no collision risk and a module-prefix is not required.

yannickoo’s picture

Oh, why I did set that to closed (fixed)? Sorry :/

Sorry for not seeing that ;)

couturier’s picture

Issue summary: View changes
Status: Needs review » Closed (won't fix)

I'm going to close this based on the fact that Backup and Migrate's creator @ronan was very hesitant about this feature request, and resources are extremely limited right now, mainly focused on the port to D8.