Search code examples
node.jsuser-accounts

Node: How to change keys stored in LevelDB for accounts using the Accountdown module


I'm using accountdown, with accountdown-basic as the plugin (https://www.npmjs.com/package/accountdown), and each account is stored using the LevelDB (https://www.npmjs.com/package/level) key-value store. Right now, the key for each account is a username:

    { key: 'user1',
      value:
       { admin: true,
         email: 'user1@gmail.com',
         username: 'user1',
         color: 'rgb(155, 26, 2)' } }
    { key: 'test',
      value:
       { admin: true,
         color: 'rgb(58, 48, 48)',
         email: 'test@test.com',
         username: 'test' } }
    { key: 'test2',
      value:
       { admin: false,
         color: 'rgb(75, 77, 154)',
         email: 'test2@test.com',
         username: 'test2' } }

but I want to use uuid's instead of a username:

   { key: '03b49d10-ee20-11e4-b966-bfcbf13ae8c2',
     value:
      { admin: true,
        email: 'user1@gmail.com',
        username: 'user1',
        color: 'rgb(155, 26, 2)' } }
   { key: '0485a8b0-ee20-11e4-b966-bfcbf13ae8c2',
     value:
      { admin: true,
        color: 'rgb(58, 48, 48)',
        email: 'test@test.com',
        username: 'test' } }
   { key: '04eea3b0-ee20-11e4-b966-bfcbf13ae8c2',
     value:
      { admin: false,
        color: 'rgb(75, 77, 154)',
        email: 'test2@test.com',
        username: 'test2' } }

How can I change these keys on existing accounts (ie perform a migration)? I'm stuck in my attempt to delete each account and re-create the accounts using uuid as the key, because I must provide the raw password string for my account-basic plugin. It seems that I only have access to each account's salted password's hash. Any suggestions? I'm happy to provide more clarification.

Here is my attempt at writing a script that checks whether each account starts with a uuid, and if so, creates a new account using the uuid as key and deletes the existing account:

var level = require('level');
var uuid = require('uuid').v1;
var each = require('each-async');
var accountdown = require('accountdown');
var sublevel = require('subleveldown');
var accountHandler = require('../handlers/accounts');

var db = level(__dirname + '/../data/db', { valueEncoding: 'json' });
var sheets = require('../models/sheets')(db);

var accountdownAccounts = accountdown(sublevel(db, 'accounts'), {
  login: { basic: require('accountdown-basic') }
});

var accountStream = accountdownAccounts.list();

var accountList = [];
// Convert our stream of accounts into a list:
accountStream
  .on('data', function (data) {
    accountList.push(data);
  })
  .on('error', function (err) {
    return console.log(err);
  })
  .on('end', function () {
    var uuidRegex = /^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$/;
    // Iterate through the list of accounts, deleting and re-creating all accounts that do not have a uuid as a key
    accountList.forEach(function (account) {
      // example of account:
      // { key: 'user1',
      //   value:
      //   { admin: true,
      //     email: 'user1@gmail.com',
      //     username: 'user1',
      //     color: 'rgb(155, 26, 2)' } 
      //  }
      if (!uuidRegex.test(account.key) ) {
        // Since the account key dd a new account using the uuid key, and remove old account
        accountdownAccounts.get(account.key, function (err, accountValue) {
          if (err) return console.log("error retrieving test account:", err);

          var opts = {
           login: {
             basic: {
               username: accountValue.username,
               password: password // I cannot access the password to create a new account!!!
             }
           },
           value: accountValue
          };

          accountdownAccounts.create(uuid(), opts, function (err) {
           if (err) return console.log("err while putting in new account:", err);
           accountdownAccounts.remove(account.key, function (err) {
             if (err) return console.log("err while deleting old account:", err);
           });
          });
        });

      } else {
        console.log("Account is valid:", account);
      }
    });
  });

Solution

  • I believe the only way to alter the key in a key-value store like levelDB is to delete and recreate the row.

    That being said, accountdown-basic now has the ability to save accounts under an arbitrary key instead of the default username. (That option is fairly recent, implement just 2 days ago)