apps/files_external/appinfo/Migrations/Version20220329110116.php
<?php
namespace OCA\files_external\Migrations;
use OC\NeedsUpdateException;
use OCA\Files_External\Lib\Backend\SFTP;
use OCA\Files_External\Lib\Auth\PublicKey\RSA;
use OCA\Files_External\Lib\RSAStore;
use OCP\Migration\ISimpleMigration;
use OCP\Migration\IOutput;
use OCP\Files\External\Service\IGlobalStoragesService;
use OCP\Files\External\IStorageConfig;
use OCP\ILogger;
use OCP\IConfig;
use phpseclib3\Crypt\RSA as RSACrypt;
class Version20220329110116 implements ISimpleMigration {
/** @var IGlobalStoragesService */
private $storageService;
/** @var ILogger */
private $logger;
/** @var IConfig */
private $config;
public function __construct(IGlobalStoragesService $storageService, ILogger $logger, IConfig $config) {
$this->storageService = $storageService;
$this->logger = $logger;
$this->config = $config;
}
/**
* @param IOutput $out
*/
public function run(IOutput $out) {
if (!$this->config->getSystemValue('installed', false)) {
// Skip the migration for new installations -> nothing to migrate
return;
}
$this->loadFSApps();
\OC_Util::setupFS(); // this should load additional backends and auth mechanisms
$storageConfigs = $this->storageService->getStorageForAllUsers();
$pass = $this->config->getSystemValue('secret', '');
$rsaStore = RSAStore::getGlobalInstance();
foreach ($storageConfigs as $storageConfig) {
if ($storageConfig->getBackend() instanceof SFTP && $storageConfig->getAuthMechanism() instanceof RSA) {
$encPubKey = $storageConfig->getBackendOption('public_key');
$encPrivKey = $storageConfig->getBackendOption('private_key');
$pubKey = \base64_decode($encPubKey, true);
$privKey = \base64_decode($encPrivKey, true);
$configId = $storageConfig->getId();
if ($pubKey === false || $privKey === false) {
$out->warning("Storage configuration with id = {$configId}: Cannot decode either public or private key, skipping");
continue;
}
try {
/** @phan-suppress-next-line PhanUndeclaredMethod */
$rsaKey = RSACrypt::load($privKey, $pass)->withHash('sha1');
} catch (\phpseclib3\Exception\NoKeyLoadedException $e) {
$out->warning("Storage configuration with id = {$configId}: Cannot load private key, skipping");
continue;
}
$targetUserId = '';
if ($storageConfig->getType() === IStorageConfig::MOUNT_TYPE_PERSONAl) {
$applicableUsers = $storageConfig->getApplicableUsers();
$targetUserId = $applicableUsers[0]; // it must have one user.
}
$token = $rsaStore->storeData($rsaKey, $targetUserId);
$storageConfig->setBackendOption('public_key', $pubKey);
$storageConfig->setBackendOption('private_key', $token);
$this->storageService->updateStorage($storageConfig);
$out->info("Storage configuration with id = {$configId}: keys migrated successfully");
}
}
}
/**
* Load the FS apps. This is required because the FS apps might not be loaded during the
* migration.
*/
private function loadFSApps() {
$enabledApps = \OC_App::getEnabledApps();
foreach ($enabledApps as $enabledApp) {
if ($enabledApp !== 'files_external' && \OC_App::isType($enabledApp, ['filesystem'])) {
try {
\OC_App::loadApp($enabledApp);
} catch (NeedsUpdateException $ex) {
if (\OC_App::updateApp($enabledApp)) {
// update successful.
// We can load the app without checking if the should upgrade or not.
\OC_App::loadApp($enabledApp, false);
} else {
$this->logger->error("Error during files_external migration. $enabledApp couldn't be loaded nor updated.", ['app' => 'files_external']);
$this->logger->logException($ex, ['app' => 'files_external']);
$this->logger->error("Mount points using $enabledApp might not be migrated properly. You might need to re-enter the passwords for those mount points", ['app' => 'files_external']);
}
}
}
}
}
}