Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 9b914dcf authored by Akhil's avatar Akhil
Browse files

Use single command to export

parent 8a70896e
Loading
Loading
Loading
Loading
Loading
+51 −52
Original line number Diff line number Diff line
@@ -8,27 +8,24 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use OCA\TwoFactorTOTP\Db\TotpSecretMapper;
use OCP\Security\ICrypto;
use OCP\IDBConnection;
use OCP\IUserManager;
use OCP\IUser;
use OCA\EcloudAccounts\Db\SSOMapper;
use OCA\EcloudAccounts\Exception\DbConnectionParamsException;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Connection;

class Migrate2FASecrets extends Command {
	private TotpSecretMapper $totpSecretMapper;
	private SSOMapper $ssoMapper;
	private IDBConnection $dbConn;
	private Connection $ssoDbConn;
	private ICrypto $crypto;
	private IUserManager $userManager;
	private OutputInterface $commandOutput;
	private const TOTP_SECRET_TABLE = 'twofactor_totp_secrets';

	public function __construct(TotpSecretMapper $totpSecretMapper, IDBConnection $dbConn, ICrypto $crypto, SSOMapper $ssoMapper, IUserManager $userManager) {
		$this->totpSecretMapper = $totpSecretMapper;
	public function __construct(IDBConnection $dbConn, ICrypto $crypto, SSOMapper $ssoMapper, IUserManager $userManager) {
		$this->ssoMapper = $ssoMapper;
		$this->userManager = $userManager;
		$this->dbConn = $dbConn;
@@ -82,15 +79,12 @@ class Migrate2FASecrets extends Command {

	protected function execute(InputInterface $input, OutputInterface $output): int {
		try {
			$this->commandOutput = $output;
			$dbName = $input->getOption('sso-db-name');
			$dbHost = $input->getOption('sso-db-host');
			$dbPort = $input->getOption('sso-db-port');
			$dbPassword = $input->getOption('sso-db-password');
			$dbUser = $input->getOption('sso-db-user');
			if (empty($dbName) || empty($dbHost) || empty($dbPort) || empty($dbPassword) || empty($dbUser)) {
				throw new DbConnectionParamsException('Invalid database parameters!');
			}

			$this->ssoDbConn = $this->getDatabaseConnection($dbName, $dbHost, $dbPort, $dbPassword, $dbUser);
			
			$usernames = [];
@@ -98,57 +92,51 @@ class Migrate2FASecrets extends Command {
			if (!empty($usernameList)) {
				$usernames = explode(',', $usernameList);
			}

			$ssoSecretEntries = $this->getSSOSecretEntries($usernames);
			foreach ($ssoSecretEntries as $username => $entry) {
				try {
					$this->ssoMapper->insertCredential($entry, $this->ssoDbConn);
				} catch(\Exception $e) {
					$output->writeln('Error inserting entry for user ' . $username . ' message: ' . $e->getMessage());
					continue;
				}
			}
			$this->migrateUsers($usernames);
			return 0;
		} catch (\Exception $e) {
			$output->writeln($e->getMessage());
			$this->commandOutput->writeln($e->getMessage());
			return 1;
		}
	}

	private function getSSOSecretEntries(array $usernames) : array {
		if (!empty($usernames)) {
			$entries = [];
			foreach ($usernames as $username) {
				$user = $this->userManager->get($username);
				if (!$user instanceof IUser) {
					continue;
				}
				$dbSecret = $this->totpSecretMapper->getSecret($user);
				$decryptedSecret = $this->crypto->decrypt($dbSecret->getSecret());
				$ssoUserId = $this->ssoMapper->getUserId($username, $this->ssoDbConn);
				$entries[$username] = $this->getSSOSecretEntry($decryptedSecret, $ssoUserId);
			}
			return $entries;
		}
		return $this->getAllSSOSecretEntries();
	}

	private function getAllSSOSecretEntries() : array {
	/**
	 * Migrate user secrets to the SSO database
	 *
	 * @return void
	 */
	private function migrateUsers(array $usernames = []) : void {
		$entries = [];
		$qb = $this->dbConn->getQueryBuilder();
		$qb->select('user_id', 'secret')
			->from(self::TOTP_SECRET_TABLE);

		if (!empty($usernames)) {
			$qb->where('user_id IN (:usernames)')
				->setParameter('usernames', implode(',', $usernames));
		}

		$result = $qb->execute();
		while ($row = $result->fetch()) {
			try {
				$username = (string) $row['user_id'];
				$secret = (string) $row['secret'];
				$decryptedSecret = $this->crypto->decrypt($secret);
				$ssoUserId = $this->ssoMapper->getUserId($username, $this->ssoDbConn);
			$entries[$username] = $this->getSSOSecretEntry($decryptedSecret, $ssoUserId);
				$entry = $this->getSSOSecretEntry($decryptedSecret, $ssoUserId);
				$this->ssoMapper->insertCredential($entry, $this->ssoDbConn);
			} catch(\Exception $e) {
				$this->commandOutput->writeln('Error inserting entry for user ' . $username . ' message: ' . $e->getMessage());
				continue;
			}
		}
		return $entries;
	}

	/**
	 * Create secret entry compatible with Keycloak schema
	 *
	 * @return array
	 */

	private function getSSOSecretEntry(string $secret, string $ssoUserId) : array {
		$credentialEntry = [
@@ -176,7 +164,16 @@ class Migrate2FASecrets extends Command {
		return $credentialEntry;
	}

	private function getDatabaseConnection(string $dbName, string $dbHost, int $dbPort, string $dbPassword, string $dbUser) {
	/**
	 * Attempt to connect to a non-NC database
	 *
	 * @return Connection
	 */
	private function getDatabaseConnection(string $dbName, string $dbHost, int $dbPort, string $dbPassword, string $dbUser) : Connection {
		if (empty($dbName) || empty($dbHost) || empty($dbPort) || empty($dbPassword) || empty($dbUser)) {
			throw new DbConnectionParamsException('Invalid database parameters!');
		}
		
		$params = [
			'dbname' => $dbName,
			'user' => $dbUser,
@@ -189,11 +186,13 @@ class Migrate2FASecrets extends Command {
		return  DriverManager::getConnection($params);
	}

	/*
		From https://www.uuidgenerator.net/dev-corner/php
		As keycloak generates random UUIDs using the java.util.UUID class which is RFC 4122 compliant
	/**
	 *	From https://www.uuidgenerator.net/dev-corner/php
	 *	As keycloak generates random UUIDs using the java.util.UUID class which is RFC 4122 compliant
	 *
	 *   @return string
	 */
	private function randomUUID($data = null) {
	private function randomUUID($data = null) : string {
		// Generate 16 bytes (128 bits) of random data or use the data passed into the function.
		$data = $data ?? random_bytes(16);
		assert(strlen($data) == 16);