BxDolStorageS3v4alt.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. if (!defined('CURL_SSLVERSION_TLSv1'))
  10. define('CURL_SSLVERSION_TLSv1', 1);
  11. if (!defined('BX_DOL_STORAGE_S3V4_MULTIPART_UPLOAD'))
  12. define('BX_DOL_STORAGE_S3V4_MULTIPART_UPLOAD', 5*1024*1024);
  13. defined('AKEEBAENGINE') or define('AKEEBAENGINE', 1);
  14. /**
  15. * Alternatove file storage implementation for Amazon S3
  16. * compatible storge with Signature v4 support.
  17. * @see BxDolStorage
  18. */
  19. class BxDolStorageS3v4alt extends BxDolStorageS3
  20. {
  21. protected function init ($aObject)
  22. {
  23. $sAccessKey = getParam('sys_storage_s3_access_key');
  24. $sSecretKey = getParam('sys_storage_s3_secret_key');
  25. $aCredentials = [];
  26. if (getParam('sys_storage_s3_amz_iam_role')) {
  27. $sToken = bx_file_get_contents('http://169.254.169.254/latest/api/token', [], 'PUT', ['X-aws-ec2-metadata-token-ttl-seconds: 600']);
  28. $sRole = bx_file_get_contents('http://169.254.169.254/latest/meta-data/iam/security-credentials/', [], 'GET', ["X-aws-ec2-metadata-token: $sToken"]);
  29. $sCredentials = bx_file_get_contents('http://169.254.169.254/latest/meta-data/iam/security-credentials/' . $sRole, [], 'GET', ["X-aws-ec2-metadata-token: $sToken"]);
  30. if ($sCredentials && ($aCredentials = @json_decode($sCredentials, true))) {
  31. $sAccessKey = $aCredentials['AccessKeyId'];
  32. $sSecretKey = $aCredentials['SecretAccessKey'];
  33. }
  34. }
  35. if (!$sAccessKey)
  36. return;
  37. $oConfiguration = new Akeeba\Engine\Postproc\Connector\S3v4\Configuration(
  38. $sAccessKey,
  39. $sSecretKey,
  40. getParam('sys_storage_s3_sig_ver'),
  41. getParam('sys_storage_s3_region')
  42. );
  43. if ($aCredentials && isset($aCredentials['Token']))
  44. $oConfiguration->setToken($aCredentials['Token']);
  45. if ($this->_sEndpoint)
  46. $oConfiguration->setEndpoint($this->_sEndpoint);
  47. $this->_s3 = new Akeeba\Engine\Postproc\Connector\S3v4\Connector($oConfiguration);
  48. }
  49. /**
  50. * Get file url.
  51. * @param $iFileId file
  52. * @return file url
  53. */
  54. public function getFileUrlById($iFileId)
  55. {
  56. $aFile = $this->_oDb->getFileById($iFileId);
  57. if (!$aFile)
  58. return false;
  59. if ($this->isAuthUrl($aFile)) {
  60. $sFileLocation = $this->getObjectBaseDir($aFile['private']) . $aFile['path'];
  61. $sRet = $this->_s3->getAuthenticatedURL($this->_sBucket, $sFileLocation, $this->_aObject['token_life'], $this->_bSSL);
  62. }
  63. else {
  64. $sRet = $this->getObjectBaseUrl($aFile['private']) . $aFile['path'];
  65. }
  66. if ('s3.wasabisys.com' === $this->_sEndpoint)
  67. $sRet = str_replace('//s3.', '//' . $this->_sBucket . '.s3.', $sRet);
  68. return $sRet;
  69. }
  70. public function setFilePrivate($iFileId, $isPrivate = true)
  71. {
  72. // since in S3v4 lib this feature not implemented then we need to get file and upload it back with new ACL
  73. $aFile = $this->_oDb->getFileById($iFileId);
  74. if (!$aFile)
  75. return false;
  76. if (!getParam('sys_storage_s3_acl_enable'))
  77. return parent::setFilePrivate($iFileId, 0);
  78. $sFileLocation = $this->getObjectBaseDir($aFile['private']) . $aFile['path'];
  79. $sTmpFile = tempnam(BX_DIRECTORY_PATH_TMP, $this->_aObject['object']);
  80. if (!$sTmpFile) {
  81. $this->setErrorCode(BX_DOL_STORAGE_ERR_FILESYSTEM_PERM);
  82. return false;
  83. }
  84. try {
  85. $this->_s3->getObject($this->_sBucket, $sFileLocation, $sTmpFile);
  86. } catch (Exception $e) {
  87. @unlink($sTmpFile);
  88. $this->setErrorCode(BX_DOL_STORAGE_ERR_UNLINK);
  89. bx_log('sys_storage_s3v4alt', $e->getMessage(), BX_LOG_ERR);
  90. return false;
  91. }
  92. if (getParam('sys_storage_s3_acl_enable'))
  93. $sACL = $isPrivate ? Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_AUTHENTICATED_READ : Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_PUBLIC_READ;
  94. else
  95. $sACL = '';
  96. $aRequestHeaders = $this->generateHeaders('', $isPrivate, $aFile['mime_type']);
  97. if (!$this->_upload($sTmpFile, $sFileLocation, $sACL, $aRequestHeaders))
  98. return false;
  99. return BxDolStorage::setFilePrivate($iFileId, $isPrivate);
  100. }
  101. // ----------------
  102. protected function addFileToEngine($sTmpFile, $sLocalId, $sName, $isPrivate, $iProfileId)
  103. {
  104. $sExt = $this->getFileExt($sName);
  105. $sPath = $this->genPath($sLocalId, $this->_aObject['levels']);
  106. $sRemoteNamePath = $sPath . $sLocalId . ($sExt ? '.' . $sExt : '');
  107. $aRequestHeaders = $this->generateHeaders($sName, $isPrivate);
  108. if (getParam('sys_storage_s3_acl_enable'))
  109. $sACL = $isPrivate ? Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_AUTHENTICATED_READ : Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_PUBLIC_READ;
  110. else
  111. $sACL = '';
  112. return $this->_upload($sTmpFile, $this->getObjectBaseDir($isPrivate) . $sRemoteNamePath, $sACL, $aRequestHeaders);
  113. }
  114. protected function _upload($sInputFile, $sUri, $sACL, $aRequestHeaders)
  115. {
  116. try {
  117. $oInputFile = Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sInputFile);
  118. if (filesize($sInputFile) < BX_DOL_STORAGE_S3V4_MULTIPART_UPLOAD) {
  119. $this->_s3->putObject($oInputFile, $this->_sBucket, $sUri, $sACL, $aRequestHeaders);
  120. }
  121. else {
  122. $sUploadSessionId = $this->_s3->startMultipart($oInputFile, $this->_sBucket, $sUri, $sACL, $aRequestHeaders);
  123. $sETags = array();
  124. $sETag = null;
  125. $iPartNumber = 0;
  126. do
  127. {
  128. // IMPORTANT: You MUST create the input afresh before each uploadMultipart call
  129. $oInput = Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sInputFile);
  130. $oInput->setUploadID($sUploadSessionId);
  131. $oInput->setPartNumber(++$iPartNumber);
  132. $sETag = $this->_s3->uploadMultipart($oInput, $this->_sBucket, $sUri);
  133. if (!is_null($sETag))
  134. $sETags[] = $sETag;
  135. }
  136. while (!is_null($sETag));
  137. // IMPORTANT: You MUST create the input afresh before finalising the multipart upload
  138. $oInput = Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sInputFile);
  139. $oInput->setUploadID($sUploadSessionId);
  140. $oInput->setEtags($sETags);
  141. $this->_s3->finalizeMultipart($oInput, $this->_sBucket, $sUri);
  142. }
  143. } catch (Exception $e) {
  144. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_ADD);
  145. bx_log('sys_storage_s3v4alt', $e->getMessage(), BX_LOG_ERR);
  146. return false;
  147. }
  148. return true;
  149. }
  150. protected function deleteFileFromEngine($sFilePath, $isPrivate)
  151. {
  152. $sFileLocation = $this->getObjectBaseDir($isPrivate) . $sFilePath;
  153. try {
  154. $this->_s3->deleteObject($this->_sBucket, $sFileLocation);
  155. } catch (Exception $e) {
  156. $this->setErrorCode(BX_DOL_STORAGE_ERR_UNLINK);
  157. bx_log('sys_storage_s3v4alt', $e->getMessage(), BX_LOG_ERR);
  158. return false;
  159. }
  160. return true;
  161. }
  162. }
  163. /** @} */