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

Unverified Commit 469ce065 authored by Akhil's avatar Akhil
Browse files

Add occ command for migrate step

parent ee35451e
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace OCA\EcloudAccounts\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use OCA\TwoFactorTOTP\Db\TotpSecretMapper;
use OCP\Security\ICrypto;
use OCP\IDBConnection;
use OCA\EcloudAccounts\Db\SSOMapper;

class Migrate2FASecrets extends Command {

	private TotpSecretMapper $totpSecretMapper;
	private SSOMapper $SSOMapper;
	private IDBConnection $dbConn;
	private ICrypto $crypto;
	private const TOTP_TABLE = 'twofactor_totp_secrets';

	public function __construct(TotpSecretMapper $totpSecretMapper, IDBConnection $dbConn, ICrypto $crypto, SSOMapper $SSOMapper) {
		$this->totpSecretMapper = $totpSecretMapper;
		$this->SSOMapper = $SSOMapper;
		$this->conn = $conn;
		parent::__construct();
	}

	protected function configure(): void {
		$this
				->setName('ecloud-accounts:migrate-2fa-secrets')
				->setDescription('Migrates 2FA secrets to SSO database')
				->addArgument(
					'users',
					InputArgument::OPTIONAL,
					'comma separated list of users'
				);
	}

	protected function execute(InputInterface $input, OutputInterface $output): int {
		try {
			$users = explode(',' , $input->getArgument('users'));
			$ssoSecretEntries = [];
			if(empty($users)) {
				$ssoSecretEntries = $this->getSSOSecretEntriesForAllUsers();
			} else {
				foreach($users as $user) {
					$secret = $this->totpSecretMapper->getSecret($user);
					$ssoSecretEntries[] = $this->getSSOSecretEntry($user, );
				}
			}
		} catch (\Exception $e) {
				$output->writeln($e->getMessage());
				return 1;
		}
	}

	private function getSSOSecretEntriesForAllUsers() : array {
		$entries = [];
		$query = $this->dbConn->getQueryBuilder();
				$qb->select( 'user_id', 'secret')
				   ->from(self::TOTP_TABLE);
				$result = $qb->execute();
				while ($row = $result->fetch()) {
					$userId = (string) $result['user_id'];
					$secret = (string) $result['secret'];
					$decryptedSecret = $this->crypto->decrypt($secret);
					$entries[] = $this->getSSOSecretEntry($userId, $secret);
				}
			return $entries;

	}

	private function getSSOSecretEntry(string $userId, string $secret) : array {
		$SSOUserId = $this->SSOMapper->getUserId($userId);
		$credentialEntry = [
			'ID' => $this->randomUUID(),
			'SALT' => NULL,
			'USER_ID' => $SSOUserId,
			'USER_LABEL' => 'Murena Cloud 2FA',
			'SECRET_DATA' => [
				'value' => $secret
			],
			'CREATED_DATE' => round(microtime(true) * 1000),
			'CREDENTIAL_DATA'  => [
				'subType' => 'nextcloud_totp',
				'period' => 30,
				'digits' => 6,
				'algorithm' => 'HmacSHA1',
			],
			'priority' => 10
		];
		return $credentialEntry;
	}


	/* 
		From https://www.uuidgenerator.net/dev-corner/php
		As keycloak generates random UUIDs using the java.util.UUID class which is RFC 4122 compliant
	*/
	private function randomUUID($data = null) {
		// 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);
	
		// Set version to 0100
		$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
		// Set bits 6-7 to 10
		$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
	
		// Output the 36 character UUID.
		return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
	}
	
}

lib/Db/SSOMapper.php

0 → 100644
+73 −0
Original line number Diff line number Diff line
<?php

namespace OCA\EcloudAccounts\Db;

use OCP\IConfig;
use OCP\ILogger;
use OCA\EcloudAccounts\Exception\DbConnectionParamsException;
use Doctrine\DBAL\DriverManager;
use Throwable;

class SSOMapper {
	private $config;
	private $conn;
	private $logger;

	public function __construct(IConfig $config, ILogger $logger) {
		$this->config = $config;
		$this->logger = $logger;
		$this->initConnection();
	}

	private function initConnection() {
		$params = $this->getConnectionParams();
		$this->conn = DriverManager::getConnection($params);
	}

	private function isDbConfigValid($config) : bool {
		if (!$config || !is_array($config)) {
			return false;
		}
		return isset($config['db_name'])
			&& isset($config['db_user'])
			&& isset($config['db_password'])
			&& isset($config['db_host'])
			&& isset($config['db_port']) ;
	}

	private function getConnectionParams() {
		$config = $this->config->getSystemValue('sso_database');
		
		if (!$this->isDbConfigValid($config)) {
			throw new DbConnectionParamsException('Invalid SSO database configuration!');
		}

		$params = [
			'dbname' => $config['db_name'],
			'user' => $config['db_user'],
			'password' => $config['db_password'],
			'host' => $config['db_host'],
			'port' => $config['db_port'],
			'driver' => 'pdo_mysql'
		];
		return $params;
	}

	public function getUserId(string $username) : string {
			$query = $this->conn->createQueryBuilder();
			$query->select('USER_ID')
				->from('USER_ATTRIBUTE')
				->where($query->expr()->eq('NAME', 'LDAP_ID'))
				->where($query->expr()->eq('VALUE', $query->createParameter('username')));
			
			$query->setParameter('username', $username);

			$result = $query->execute();
			$SSOUserId = (string) $result->fetchColumn();	
			return $SSOUserId;	
	}	

	public function insertCredentials(array $entries) {
		
	}
}