. */ namespace SP\Services\Account; use Defuse\Crypto\Exception\CryptoException; use SP\Core\Crypt\Crypt; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; use SP\Core\Exceptions\SPException; use SP\Services\Crypt\UpdateMasterPassRequest; use SP\Services\Service; use SP\Services\ServiceException; use SP\Services\Task\TaskFactory; use SP\Util\Util; /** * Class AccountCryptService * * @package SP\Services\Account */ final class AccountCryptService extends Service { /** * @var AccountService */ protected $accountService; /** * @var AccountHistoryService */ protected $accountHistoryService; /** * @var UpdateMasterPassRequest */ protected $request; /** * Actualiza las claves de todas las cuentas con la nueva clave maestra. * * @param UpdateMasterPassRequest $updateMasterPassRequest * * @throws ServiceException */ public function updateMasterPassword(UpdateMasterPassRequest $updateMasterPassRequest) { $this->request = $updateMasterPassRequest; try { $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.start', new Event($this, EventMessage::factory()->addDescription(__u('Actualizar Clave Maestra'))) ); if ($this->request->useTask()) { $taskId = $this->request->getTask(); TaskFactory::update($taskId, TaskFactory::createMessage($taskId, __u('Actualizar Clave Maestra'))); } $eventMessage = $this->processAccounts($this->accountService->getAccountsPassData(), function ($request) { $this->accountService->updatePasswordMasterPass($request); }); $this->eventDispatcher->notifyEvent('update.masterPassword.accounts.end', new Event($this, $eventMessage)); } catch (\Exception $e) { $this->eventDispatcher->notifyEvent('exception', new Event($e)); throw new ServiceException( __u('Errores al actualizar las claves de las cuentas'), ServiceException::ERROR, null, $e->getCode(), $e); } } /** * @param array $accounts * @param callable $passUpdater * * @return EventMessage * @throws ServiceException */ protected function processAccounts(array $accounts, callable $passUpdater) { set_time_limit(0); $accountsOk = []; $errorCount = 0; $counter = 0; $startTime = time(); $numAccounts = count($accounts); if ($numAccounts === 0) { throw new ServiceException(__u('Error al obtener las claves de las cuentas'), ServiceException::ERROR); } $configData = $this->config->getConfigData(); $currentMasterPassHash = $this->request->getCurrentHash(); if ($this->request->useTask()) { $taskId = $this->request->getTask()->getTaskId(); } $eventMessage = EventMessage::factory(); foreach ($accounts as $account) { // No realizar cambios si está en modo demo if ($configData->isDemoEnabled()) { $accountsOk[] = $account->id; continue; } if ($counter % 100 === 0) { $eta = Util::getETA($startTime, $counter, $numAccounts); if (isset($taskId)) { $taskMessage = TaskFactory::createMessage($taskId, __('Actualizar Clave Maestra')) ->setMessage(sprintf(__('Cuentas actualizadas: %d / %d'), $counter, $numAccounts)) ->setProgress(round(($counter * 100) / $numAccounts, 2)) ->setTime(sprintf('ETA: %ds (%.2f/s)', $eta[0], $eta[1])); TaskFactory::update($taskId, $taskMessage); logger($taskMessage->composeText()); } else { logger( sprintf(__('Cuentas actualizadas: %d / %d - %d%% - ETA: %ds (%.2f/s)'), $counter, $numAccounts, round(($counter * 100) / $numAccounts, 2), $eta[0], $eta[1]) ); } } if (isset($account->mPassHash) && $account->mPassHash !== $currentMasterPassHash) { $eventMessage->addDescription(__u('La clave maestra del registro no coincide')); $eventMessage->addDetail($account->name, $account->id); continue; } $request = new AccountPasswordRequest(); $request->id = $account->id; try { $passData = $this->accountService->getPasswordEncrypted( Crypt::decrypt($account->pass, $account->key, $this->request->getCurrentMasterPass()), $this->request->getNewMasterPass() ); $request->key = $passData['key']; $request->pass = $passData['pass']; // Call the specific updater $passUpdater($request); $accountsOk[] = $account->id; $counter++; } catch (SPException $e) { $errorCount++; $eventMessage->addDescription(__u('Fallo al actualizar la clave de la cuenta')); $eventMessage->addDetail($account->name, $account->id); } catch (CryptoException $e) { $errorCount++; $eventMessage->addDescription(__u('Fallo al actualizar la clave de la cuenta')); $eventMessage->addDetail($account->name, $account->id); } } $eventMessage->addDetail(__u('Cuentas actualizadas'), implode(',', $accountsOk)); $eventMessage->addDetail(__u('Errores'), $errorCount); return $eventMessage; } /** * Actualiza las claves de todas las cuentas con la nueva clave maestra. * * @param UpdateMasterPassRequest $updateMasterPassRequest * * @throws ServiceException */ public function updateHistoryMasterPassword(UpdateMasterPassRequest $updateMasterPassRequest) { $this->request = $updateMasterPassRequest; try { $this->eventDispatcher->notifyEvent('update.masterPassword.accountsHistory.start', new Event($this, EventMessage::factory()->addDescription(__u('Actualizar Clave Maestra (H)'))) ); $taskId = $this->request->getTask(); if ($this->request->useTask()) { TaskFactory::update($taskId, TaskFactory::createMessage($taskId, __u('Actualizar Clave Maestra (H)'))); } $eventMessage = $this->processAccounts($this->accountHistoryService->getAccountsPassData(), function ($request) { /** @var AccountPasswordRequest $request */ $request->hash = $this->request->getHash(); $this->accountHistoryService->updatePasswordMasterPass($request); }); $this->eventDispatcher->notifyEvent('update.masterPassword.accountsHistory.end', new Event($this, $eventMessage)); } catch (\Exception $e) { $this->eventDispatcher->notifyEvent('exception', new Event($e)); throw new ServiceException( __u('Errores al actualizar las claves de las cuentas del histórico'), ServiceException::ERROR, null, $e->getCode(), $e); } } /** * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface */ protected function initialize() { $this->accountService = $this->dic->get(AccountService::class); $this->accountHistoryService = $this->dic->get(AccountHistoryService::class); } }