A ideia de escrever esta biblioteca surgiu quando eu queria aproveitar ao máximo a oferta gratuita do Oracle Cloud Infrastructure, ou seja, 10 GB de armazenamento de objetos e 10 TB de tráfego de saída por mês. A diferença com o AWS S3 é enorme . Infelizmente, o Oracle Cloud não tem um SDK disponível para a linguagem de programação ainda mais popular para desenvolvimento de sites. A boa notícia é que o serviço é parcialmente compatível com Amazon S3, o que significa que você pode usar ferramentas de desenvolvedor existentes e bem documentadas , inclusive para PHP.
Para aqueles que estão ansiosos para ver o código, bem-vindo a https://github.com/hitrov/oci-api-php-request-sign .
De fato, com as ferramentas disponíveis, você pode realizar quase todas as operações imagináveis - criar, ler e excluir buckets e objetos (arquivos). As lixeiras podem ser públicas (com ou sem arquivos de listagem) e privadas. É possível fazer upload de arquivos para uma cesta privada, tendo apenas uma URL "secreta" (gerada manualmente usando o CLI ou a interface web - console Oracle Cloud). Na verdade, isso já pode ser suficiente para muitos cenários, especialmente se você gerar nomes de arquivos de força bruta no caso de não querer expô-los ao público.
Eu estava interessado na capacidade de "compartilhar" arquivos, isto é, compartilhar links públicos para arquivos e, é claro, restringir o acesso, se necessário. Com um pequeno número de arquivos, você pode fazer isso manualmente, mas estamos reunidos aqui para ter acesso programático. O AWS S3 chama isso de URL pré-assinado , enquanto a Oracle o chama de solicitação pré-autenticada .
Instalando AWS PHP SDK
composer require aws/aws-sdk-php
Abaixo, será mostrado onde obter acesso ( AWS_ACCESS_KEY_ID
e AWS_SECRET_ACCESS_KEY
.
O namespace pode ser visto
require('vendor/autoload.php');
$namespaceName = 'frpegp***';
$bucketName = 'test******05';
$region = 'eu-frankfurt-1';
$endpoint = "https://$namespaceName.compat.objectstorage.$region.oraclecloud.com";
$s3 = new Aws\S3\S3Client([
'version' => 'latest',
'region' => $region,
'endpoint' => $endpoint,
'signature_version' => 'v4',
'use_path_style_endpoint' => true,
'credentials' => [
'key' => 'AKI***YYJ', // remove if you have env var AWS_ACCESS_KEY_ID
'secret' => 'ndK***cIf', // remove if you have env var AWS_SECRET_ACCESS_KEY
],
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => $bucketName,
'Key' => 'fff.txt'
]);
$request = $s3->createPresignedRequest($cmd, '+20 minutes');
var_dump($request);
object(GuzzleHttp\Psr7\Request)#146 (7) {
["method":"GuzzleHttp\Psr7\Request":private]=>
string(3) "GET"
["uri":"GuzzleHttp\Psr7\Request":private]=>
object(GuzzleHttp\Psr7\Uri)#148 (7) {
["scheme":"GuzzleHttp\Psr7\Uri":private]=>
string(5) "https"
...
["host":"GuzzleHttp\Psr7\Uri":private]=>
string(64) "{namespace}.compat.objectstorage.eu-frankfurt-1.oraclecloud.com"
...
["path":"GuzzleHttp\Psr7\Uri":private]=>
string(21) "/{bucket}/fff.txt"
["query":"GuzzleHttp\Psr7\Uri":private]=>
string(329) "X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=&&&%2F20210211%2Feu-frankfurt-1%2Fs3%2Faws4_request&X-Amz-Date=20210211T093350Z&X-Amz-SignedHeaders=host&X-Amz-Expires=1200&X-Amz-Signature=1e1b1***6ac992"
...
}
}
Esta operação retorna uma solicitação PSR-7 em resposta , a partir da qual você pode gerar um URL do formulário
https://{namespace}.compat.objectstorage.eu-frankfurt-1.oraclecloud.com/{bucket}/fff.txt?X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=***%2F20210210%2Feu-frankfurt-1%2Fs3%2Faws4_request&X-Amz-Date=20210210T185244Z&X-Amz-SignedHeaders=host&X-Amz-Expires=1200&X-Amz-Signature=a167a***9a857
Mas, infelizmente, isso não permite revogar o acesso, por exemplo, se o link for "long-play".
, \\ , DNS Email. API Reference and Endpoints.
, , , , - () Oracle Cloud User Settings
API Keys — Add API Key
Download private key ( ), Add
,
, AWS PHP SDK, Customer Secret Keys ( AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
Amazon.
Oracle Cloud Infrastructure mini PHP SDK ( !)
composer require hitrov/oci-api-php-request-sign
PSR-4 .
require 'vendor/autoload.php';
use Hitrov\OCI\Signer;
( , , ).
OCI_TENANCY_ID=ocid1.tenancy.oc1..aaaaaaaaba3pv6wkcr4jqae5f15p2b2m2yt2j6rx32uzr4h25vqstifsfdsq OCI_USER_ID=ocid1.user.oc1..aaaaaaaat5nvwcna5j6aqzjcaty5eqbb6qt2jvpkanghtgdaqedqw3rynjq OCI_KEY_FINGERPRINT=20:3b:97:13:55:1c:5b:0d:d3:37:d8:50:4e:c5:3a:34 OCI_PRIVATE_KEY_FILENAME=/path/to/privatekey.pem
.
$signer = new Signer;
https://github.com/hitrov/oci-api-php-request-sign#alternatives-for-providing-credentials , .
CreatePreauthenticatedRequest.
( )
public function getHeaders(
string $url, string $method = 'GET', ?string $body = null, ?string $contentType = 'application/json', string $dateString = null
): array
$curl = curl_init();
$url = 'https://objectstorage.eu-frankfurt-1.oraclecloud.com/n/{namespaceName}/b/{bucketName}/p/';
$method = 'POST';
$body = '{"accessType": "ObjectRead", "name": "read-access-to-image.png", "objectName": "path/to/image.png", "timeExpires": "2021-03-01T00:00:00-00:00"}';
$headers = $signer->getHeaders($url, $method, $body, 'application/json');
var_dump($headers);
$curlOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
];
if ($body) {
// not needed for GET or HEAD requests
$curlOptions[CURLOPT_POSTFIELDS] = $body;
}
curl_setopt_array($curl, $curlOptions);
$response = curl_exec($curl);
echo $response;
curl_close($curl);
array(6) {
[0]=>
string(35) "date: Mon, 08 Feb 2021 20:49:22 GMT"
[1]=>
string(50) "host: objectstorage.eu-frankfurt-1.oraclecloud.com"
[2]=>
string(18) "content-length: 76"
[3]=>
string(30) "content-type: application/json"
[4]=>
string(62) "x-content-sha256: X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="
[5]=>
string(538) "Authorization: Signature version=\"1\",keyId=\"ocid1.tenancy.oc1..aaaaaaaaba3pv6wkcr4jqae5f15p2b2m2yt2j6rx32uzr4h25vqstifsfdsq/ocid1.user.oc1..aaaaaaaat5nvwcna5j6aqzjcaty5eqbb6qt2jvpkanghtgdaqedqw3rynjq/20:3b:97:13:55:1c:5b:0d:d3:37:d8:50:4e:c5:3a:34\",algorithm=\"rsa-sha256\",headers=\"date (request-target) host content-length content-type x-content-sha256\",signature=\"LXWXDA8VmXXc1NRbMmXtW61IS97DfIOMAnlj+Gm+oBPNc2svXYdhcXNJ+oFPoi9qJHLnoUiHqotTzuVPXSG5iyXzFntvkAn3lFIAja52iwwwcJflEIXj/b39eG2dCsOTmmUJguut0FsLhCRSX0eylTSLgxTFGoQi7K/m18nafso=\""
}
{
"accessUri": "/p/AlIlOEsMok7oE7YkN30KJUDjDKQjk493BKbuM-ANUNGdBBAHzHT_5lFlzYC9CQiA/n/{namespaceName}/b/{bucketName}/o/path/to/image.png",
"id": "oHJQWGxpD+2PhDqtoewvLCf8/lYNlaIpbZHYx+mBryAad/q0LnFy37Me/quKhxEi:path/to/image.png",
"name": "read-access-to-image.png",
"accessType": "ObjectRead",
"objectName": "path/to/image.png",
"timeCreated": "2021-02-09T11:52:45.053Z",
"timeExpires": "2021-03-01T00:00:00Z"
}
!
, . , – .
1) , « » (SIGNING_HEADERS_NAMES).
date
· (request-target)
· host
POST|PUT|PATCH
· content-length
· content-type
· x-content-sha256
$signingHeadersNames = $signer->getSigningHeadersNames('POST');
2) SHA256 «» – base64
$bodyHashBase64 = $signer->getBodyHashBase64($body);
3) ,
date: Mon, 08 Feb 2021 20:51:33 GMT (request-target): post /n/{namespaceName}/b/{bucketName}/p/ host: objectstorage.eu-frankfurt-1.oraclecloud.com content-length: 76 content-type: application/json x-content-sha256: X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
$signingString = $signer->getSigningString($url, $method, $body, 'application/json');
(2). , , 5 .
4) (3) RSA-SHA256
$signature = $signer->calculateSignature($signingString, $privateKeyString);
5) KEY_ID , API Key, ,
"{OCITENANCYID}/{OCIUSERID}/{OCIKEY_FINGERPRINT}"
$keyId = $signer->getKeyId();
6) ( 1
Oracle)
Authorization: Signature version=\"1\",keyId=\"{KEY_ID}\",algorithm=\"rsa-sha256\",headers=\"{SIGNING_HEADERS_NAMES_STRING}\",signature=\"{SIGNATURE}\"
SIGNING_HEADERS_NAMES_STRING – (1), .
date (request-target) host content-length content-type x-content-sha256
$signingHeadersNamesString = implode(' ', $signingHeadersNames);
$authorizationHeader = $signer->getAuthorizationHeader($keyId, $signingHeadersNamesString, $signature);
Authorization: Signature version=\"1\",keyId=\"ocid1.tenancy.oc1..aaaaaaaaba3pv6wkcr4jqae5f15p2b2m2yt2j6rx32uzr4h25vqstifsfdsq/ocid1.user.oc1..aaaaaaaat5nvwcna5j6aqzjcaty5eqbb6qt2jvpkanghtgdaqedqw3rynjq/20:3b:97:13:55:1c:5b:0d:d3:37:d8:50:4e:c5:3a:34\",algorithm=\"rsa-sha256\",headers=\"date (request-target) host content-length content-type x-content-sha256\",signature=\"LXWXDA8VmXXc1NRbMmXtW61IS97DfIOMAnlj+Gm+oBPNc2svXYdhcXNJ+oFPoi9qJHLnoUiHqotTzuVPXSG5iyXzFntvkAn3lFIAja52iwwwcJflEIXj/b39eG2dCsOTmmUJguut0FsLhCRSX0eylTSLgxTFGoQi7K/m18nafso=\"
- . var_dump()
- (3), (request-target) . , , (6).
Recebi ajuda do artigo sobre a chamada REST do Oracle Cloud Infrastructure (OCI) com curl . Alguns dos nomes de métodos são emprestados do SDK GoLang oficial . Casos de teste - do mesmo lugar .