openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 0909613d3fcf703d5f138ede7d86879ec3ca2352
parent 36cc2b37ba75b38e68275d15a8d8703a942dc0db
Author: dankert <openrat@jandankert.de>
Date:   Sat,  4 Dec 2021 14:05:06 +0100

Fix: Amazon S3 upload is now fully working.

Diffstat:
Mmodules/cms/generator/target/S3Target.class.php | 104++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 82 insertions(+), 22 deletions(-)

diff --git a/modules/cms/generator/target/S3Target.class.php b/modules/cms/generator/target/S3Target.class.php @@ -20,6 +20,7 @@ namespace cms\generator\target; use cms\base\Startup; use logger\Logger; use util\exception\PublisherException; +use util\FileUtils; /** @@ -32,6 +33,7 @@ use util\exception\PublisherException; class S3Target extends BaseTarget { const SERVICE = 's3'; + const AWS_4_HMAC_SHA_256 = 'AWS4-HMAC-SHA256'; /** * @var false|resource @@ -45,14 +47,22 @@ class S3Target extends BaseTarget } + /** + * @param $source String source file + * @param $dest String filename + * @param $time int this is ignored, because the date in S3 must be a current date. + * @return mixed + * @throws PublisherException + */ public function put($source, $dest, $time) { $dateIso = date('r',Startup::getStartTime()); $dateShort = date('Ymd'); - $timeStamp = date('Ymd\THise',Startup::getStartTime()); - $dest = $this->url->path . '/' . $dest; + $timeStamp = date('Ymd\THis\Z',Startup::getStartTime()); + //$destPath = FileUtils::unslashifyBegin($this->url->path . '/' . $dest); + $destPath = $this->url->path . '/' . $dest; - $accessKeyId = $this->url->user; + $accessKeyId = $this->url->user; $secretAccessKey = $this->url->pass; $domain = explode('.',$this->url->host); @@ -61,60 +71,109 @@ class S3Target extends BaseTarget list($bucket,$service,$region) = $domain; $scope = $dateShort.'/'.$region.'/'.$service.'/aws4_request'; - $credential = $this->url->user.'/'.$scope; + $credential = $accessKeyId.'/'.$scope; $headers = []; - $hashedPayload = hash_file('SHA256',$source); + $hashedPayload = hash_file('SHA256', $source); $headers['x-amz-content-sha256'] = $hashedPayload; - //$headers['x-amz-date'] = $timeStamp; - //$content .= "Content-Length: ".filesize($source)."\r\n"; - //$content .= "Connection: Close\r\n"; + $headers['x-amz-date'] = $timeStamp; $headers['Host'] = $this->url->host; $headers['Date'] = $dateIso; + + // Creating the "signedHeaders" $signedHeaders = $this->getSignedHeaders($headers); + + + // Creating the "canonicalHeaders" $canonicalHeaders = $this->getCanonicalHeaders($headers); - $canonicalRequest = "PUT\n".$dest."\n\n".$canonicalHeaders."\n\n".$signedHeaders."\n".$hashedPayload; - $stringToSign = 'AWS4-HMAC-SHA256'."\n".$timeStamp."\n".$scope."\n".hash('SHA256',$canonicalRequest); + $canonicalRequestParts = [ + 'PUT', + utf8_encode($destPath), + '', + $canonicalHeaders, + '', + $signedHeaders, + $hashedPayload + ]; + $canonicalRequest = implode("\n",$canonicalRequestParts); + Logger::debug("S3 Canonical request:\n".$canonicalRequest); + + + // Create the "StringToSign" + $stringToSignParts = [ + self::AWS_4_HMAC_SHA_256, + $timeStamp, + $scope, + hash('SHA256',$canonicalRequest ) + ]; + $stringToSign = implode("\n",$stringToSignParts); + Logger::debug("S3 StringToSign:\n".$stringToSign); + + + // Create the "Signature" $signatureParts = [ - $dateIso, + $dateShort, $region, 's3', 'aws4_request', ]; - $signingKey = array_reduce(array_reverse($signatureParts),function ($initial,$value){ - return hash_hmac('SHA256',$initial,$value); + $signingKey = array_reduce($signatureParts,function ($initialKey, $value){ + Logger::debug('S3 Signing "'.$value.'"'); + return hash_hmac('SHA256',$value,$initialKey,true); },'AWS4'.$secretAccessKey); - $signature = hash_hmac( 'SHA256',$stringToSign, $signingKey ); + Logger::debug("S3 Signature: ".$signature); + + + // Create the "Authorization" header $authorizationParts = [ - 'Credential'=>$credential, - 'SignedHeaders'=>$signedHeaders, - 'Signature'=>$signature, + 'Credential' => $credential, + 'SignedHeaders' => $signedHeaders, + 'Signature' => $signature, ]; array_walk($authorizationParts,function(&$value,$key){$value=$key.'='.$value;}); - $headers['Authorization'] = 'AWS4-HMAC-SHA256'." ".implode(",",$authorizationParts); - $headers['Connection' ] = 'Close'; + $authorization = self::AWS_4_HMAC_SHA_256 ." ".implode(",",$authorizationParts); + $headers['Authorization' ] = $authorization; + Logger::debug("S3 Authorization: ".$authorization); + - $content = "PUT $dest HTTP/1.1\r\n"; + // Add some extra headers + $headers['Connection' ] = 'Close'; + $headers['Content-Length'] = filesize($source); + + + // Creating the HTTP request + $content = "PUT $destPath HTTP/1.1\r\n"; foreach( $headers as $key=>$value ) $content .= $key.': '.$value."\r\n"; + Logger::trace( "S3 Request:\n".$content ); fwrite($this->socket, $content."\r\n".file_get_contents($source)); $response = ''; + if ( !feof($this->socket) ) { + $line = fgets($this->socket, 14); + $status = substr($line,9,3); + }else{ + $status = false; + } while (!feof($this->socket)) { $line = fgets($this->socket, 1028); $response .= $line; } $response .= "\n\n\n\n".$content; - Logger::debug( "S3 Request:\n".$content ); - throw new PublisherException($response); + + Logger::debug( "S3 Response status: ".$status ); + + if ( $status != '200' ) + // Some server error occured. + throw new PublisherException( "Status is ".$status."\n".$response ); } @@ -166,6 +225,7 @@ class S3Target extends BaseTarget $header[ strtolower($key) ] = $value; ksort($header ); + array_walk($header,function(&$value,$key){$value=$key.':'.$value;}); return implode( "\n",$header ); }