. */ namespace SP\Services\PublicLink; use SP\Bootstrap; use SP\Config\Config; use SP\Core\Crypt\Crypt; use SP\Core\Crypt\Vault; use SP\Core\Exceptions\SPException; use SP\DataModel\ItemSearchData; use SP\DataModel\PublicLinkData; use SP\DataModel\PublicLinkListData; use SP\Http\Request; use SP\Repositories\NoSuchItemException; use SP\Repositories\PublicLink\PublicLinkRepository; use SP\Services\Account\AccountService; use SP\Services\Service; use SP\Services\ServiceException; use SP\Services\ServiceItemTrait; use SP\Storage\Database\QueryResult; /** * Class PublicLinkService * * @package SP\Services\PublicLink */ final class PublicLinkService extends Service { use ServiceItemTrait; /** * Tipos de enlaces */ const TYPE_ACCOUNT = 1; /** * @var PublicLinkRepository */ protected $publicLinkRepository; /** * @var Request */ protected $request; /** * Returns an HTTP URL for given hash * * @param $hash * * @return string */ public static function getLinkForHash($hash) { return Bootstrap::$WEBURI . '/index.php?r=account/viewLink/' . $hash; } /** * Generar el hash para el enlace * * @return string */ public static function createLinkHash() { return hash('sha256', uniqid('sysPassPublicLink', true)); } /** * @param string $salt * @param PublicLinkData $publicLinkData * * @return string */ public static function getKeyForHash($salt, PublicLinkData $publicLinkData) { return sha1($salt . $publicLinkData->getHash()); } /** * @param ItemSearchData $itemSearchData * * @return QueryResult * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function search(ItemSearchData $itemSearchData) { return $this->publicLinkRepository->search($itemSearchData); } /** * @param $id * * @return \SP\DataModel\PublicLinkListData * @throws \SP\Core\Exceptions\SPException */ public function getById($id) { $result = $this->publicLinkRepository->getById($id); if ($result->getNumRows() === 0) { throw new NoSuchItemException(__u('Enlace no encontrado')); } return $result->getData(); } /** * @param $id * * @return bool * @throws SPException * @throws \Defuse\Crypto\Exception\CryptoException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function refresh($id) { $result = $this->publicLinkRepository->getById($id); if ($result->getNumRows() === 0) { throw new NoSuchItemException(__u('Enlace no encontrado')); } $key = $this->getPublicLinkKey(); /** @var PublicLinkData $publicLinkData */ $publicLinkData = $result->getData(); $publicLinkData->setHash($key->getHash()); $publicLinkData->setData($this->getSecuredLinkData($publicLinkData->getItemId(), $key)); $publicLinkData->setDateExpire(self::calcDateExpire($this->config)); $publicLinkData->setCountViews($this->config->getConfigData()->getPublinksMaxViews()); return $this->publicLinkRepository->refresh($publicLinkData); } /** * @param string|null $hash * * @return PublicLinkKey * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException */ public function getPublicLinkKey(string $hash = null) { return new PublicLinkKey($this->config->getConfigData()->getPasswordSalt(), $hash); } /** * Obtener los datos de una cuenta y encriptarlos para el enlace * * @param int $itemId * @param PublicLinkKey $key * * @return Vault * @throws NoSuchItemException * @throws ServiceException * @throws \Defuse\Crypto\Exception\CryptoException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ private function getSecuredLinkData($itemId, PublicLinkKey $key) { // Obtener los datos de la cuenta $accountData = $this->dic->get(AccountService::class)->getDataForLink($itemId); // Desencriptar la clave de la cuenta $accountData->setPass(Crypt::decrypt($accountData->getPass(), $accountData->getKey(), $this->getMasterKeyFromContext())); $accountData->setKey(null); return (new Vault())->saveData(serialize($accountData), $key->getKey())->getSerialized(); } /** * Devolver el tiempo de caducidad del enlace * * @param Config $config * * @return int */ public static function calcDateExpire(Config $config) { return time() + $config->getConfigData()->getPublinksMaxTime(); } /** * @param $id * * @return $this * @throws NoSuchItemException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function delete($id) { if ($this->publicLinkRepository->delete($id) === 0) { throw new NoSuchItemException(__u('Enlace no encontrado'), NoSuchItemException::INFO); } return $this; } /** * Deletes all the items for given ids * * @param array $ids * * @return bool * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException * @throws ServiceException */ public function deleteByIdBatch(array $ids) { if (($count = $this->publicLinkRepository->deleteByIdBatch($ids)) !== count($ids)) { throw new ServiceException(__u('Error al eliminar los enlaces'), ServiceException::WARNING); } return $count; } /** * @param PublicLinkData $itemData * * @return int * @throws SPException * @throws \Defuse\Crypto\Exception\CryptoException * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function create(PublicLinkData $itemData) { $key = $this->getPublicLinkKey(); $itemData->setHash($key->getHash()); $itemData->setData($this->getSecuredLinkData($itemData->getItemId(), $key)); $itemData->setDateExpire(self::calcDateExpire($this->config)); $itemData->setMaxCountViews($this->config->getConfigData()->getPublinksMaxViews()); $itemData->setUserId($this->context->getUserData()->getId()); return $this->publicLinkRepository->create($itemData)->getLastId(); } /** * Get all items from the service's repository * * @return PublicLinkListData[] * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function getAllBasic() { return $this->publicLinkRepository->getAll()->getDataAsArray(); } /** * Incrementar el contador de visitas de un enlace * * @param PublicLinkData $publicLinkData * * @throws NoSuchItemException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function addLinkView(PublicLinkData $publicLinkData) { /** @var array $useInfo */ $useInfo = unserialize($publicLinkData->getUseInfo()); $useInfo[] = self::getUseInfo($publicLinkData->getHash(), $this->request); $publicLinkData->setUseInfo($useInfo); // FIXME // $Log = new Log(); // $LogMessage = $Log->getLogMessage(); // $LogMessage->setAction(__u('Ver Enlace Público')); // $LogMessage->addDescription(__u('Enlace visualizado')); // $LogMessage->addDetails(__u('Tipo'), $publicLinkData->getPublicLinkTypeId()); // $LogMessage->addDetails(__u('Cuenta'), AccountUtil::getAccountNameById($publicLinkData->getPublicLinkItemId())); // $LogMessage->addDetails(__u('Usuario'), UserUtil::getUserLoginById($publicLinkData->getPublicLinkUserId())); // $Log->writeLog(); // // if ($publicLinkData->isPublicLinkNotify()) { // Email::sendEmail($LogMessage); // } if ($this->publicLinkRepository->addLinkView($publicLinkData) === 0) { throw new NoSuchItemException(__u('Enlace no encontrado')); } } /** * Actualizar la información de uso * * @param string $hash * * @param Request $request * * @return array */ public static function getUseInfo($hash, Request $request) { return [ 'who' => $request->getClientAddress(true), 'time' => time(), 'hash' => $hash, 'agent' => $request->getHeader('User-Agent'), 'https' => $request->isHttps() ]; } /** * @param $hash string * * @return bool|PublicLinkData * @throws \SP\Core\Exceptions\SPException */ public function getByHash($hash) { $result = $this->publicLinkRepository->getByHash($hash); if ($result->getNumRows() === 0) { throw new NoSuchItemException(__u('Enlace no encontrado')); } return $result->getData(); } /** * Devolver el hash asociado a un elemento * * @param int $itemId * * @return PublicLinkData * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException * @throws \SP\Repositories\NoSuchItemException */ public function getHashForItem($itemId) { $result = $this->publicLinkRepository->getHashForItem($itemId); if ($result->getNumRows() === 0) { throw new NoSuchItemException(__u('Enlace no encontrado')); } return $result->getData(); } /** * Updates an item * * @param PublicLinkData $itemData * * @return mixed * @throws SPException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ public function update(PublicLinkData $itemData) { return $this->publicLinkRepository->update($itemData); } /** * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface */ protected function initialize() { $this->publicLinkRepository = $this->dic->get(PublicLinkRepository::class); $this->request = $this->dic->get(Request::class); } }