<?php

// Previously, MFA factors for individual users were bound to raw factor types.
// The only factor type ever implemented in the upstream was "totp".

// Going forward, individual factors are bound to a provider instead. This
// allows factor types to have some configuration, like API keys for
// service-based MFA. It also allows installs to select which types of factors
// they want users to be able to set up.

// Migrate all existing TOTP factors to the first available TOTP provider,
// creating one if none exists. This migration is a little bit messy, but
// gives us a clean slate going forward with no "builtin" providers.

$table = new PhabricatorAuthFactorConfig();
$conn = $table->establishConnection('w');

$provider_table = new PhabricatorAuthFactorProvider();
$provider_phid = null;
$iterator = new LiskRawMigrationIterator($conn, $table->getTableName());
$totp_key = 'totp';
foreach ($iterator as $row) {

  // This wasn't a TOTP factor, so skip it.
  if ($row['factorKey'] !== $totp_key) {
    continue;
  }

  // This factor already has an associated provider.
  if (strlen($row['factorProviderPHID'])) {
    continue;
  }

  // Find (or create) a suitable TOTP provider. Note that we can't "save()"
  // an object or this migration will break if the object ever gets new
  // columns; just INSERT the raw fields instead.

  if ($provider_phid === null) {
    $provider_row = queryfx_one(
      $conn,
      'SELECT phid FROM %R WHERE providerFactorKey = %s LIMIT 1',
      $provider_table,
      $totp_key);

    if ($provider_row) {
      $provider_phid = $provider_row['phid'];
    } else {
      $provider_phid = $provider_table->generatePHID();
      queryfx(
        $conn,
        'INSERT INTO %R
          (phid, providerFactorKey, name, status, properties,
            dateCreated, dateModified)
          VALUES (%s, %s, %s, %s, %s, %d, %d)',
        $provider_table,
        $provider_phid,
        $totp_key,
        '',
        'active',
        '{}',
        PhabricatorTime::getNow(),
        PhabricatorTime::getNow());
    }
  }

  queryfx(
    $conn,
    'UPDATE %R SET factorProviderPHID = %s WHERE id = %d',
    $table,
    $provider_phid,
    $row['id']);
}