f7cloud_client/3rdparty/microsoft/azure-storage-common/src/Common/SharedAccessSignatureHelper.php

332 lines
12 KiB
PHP

<?php
/**
* LICENSE: The MIT License (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://github.com/azure/azure-storage-php/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* PHP version 5
*
* @category Microsoft
* @package MicrosoftAzure\Storage\Common\Internal\Authentication
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
* @copyright Microsoft Corporation
* @license https://github.com/azure/azure-storage-php/LICENSE
* @link https://github.com/azure/azure-storage-php
*/
namespace MicrosoftAzure\Storage\Common;
use MicrosoftAzure\Storage\Common\Internal\Resources;
use MicrosoftAzure\Storage\Common\Internal\Utilities;
use MicrosoftAzure\Storage\Common\Internal\Validate;
/**
* Provides methods to generate Azure Storage Shared Access Signature
*
* @category Microsoft
* @package MicrosoftAzure\Storage\Common
* @author Azure Storage PHP SDK <dmsh@microsoft.com>
* @copyright 2017 Microsoft Corporation
* @license https://github.com/azure/azure-storage-php/LICENSE
* @link https://github.com/azure/azure-storage-php
*/
class SharedAccessSignatureHelper
{
protected $accountName;
protected $accountKey;
/**
* Constructor.
*
* @param string $accountName the name of the storage account.
* @param string $accountKey the shared key of the storage account
*
*/
public function __construct($accountName, $accountKey)
{
Validate::canCastAsString($accountName, 'accountName');
Validate::notNullOrEmpty($accountName, 'accountName');
Validate::canCastAsString($accountKey, 'accountKey');
Validate::notNullOrEmpty($accountKey, 'accountKey');
$this->accountName = urldecode($accountName);
$this->accountKey = $accountKey;
}
/**
* Generates a shared access signature at the account level.
*
* @param string $signedVersion Specifies the signed version to use.
* @param string $signedPermissions Specifies the signed permissions for
* the account SAS.
* @param string $signedService Specifies the signed services
* accessible with the account SAS.
* @param string $signedResourceType Specifies the signed resource types
* that are accessible with the account
* SAS.
* @param \Datetime|string $signedExpiry The time at which the shared access
* signature becomes invalid, in an ISO
* 8601 format.
* @param \Datetime|string $signedStart The time at which the SAS becomes
* valid, in an ISO 8601 format.
* @param string $signedIP Specifies an IP address or a range
* of IP addresses from which to accept
* requests.
* @param string $signedProtocol Specifies the protocol permitted for
* a request made with the account SAS.
*
* @see Constructing an account SAS at
* https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/constructing-an-account-sas
*
* @return string
*/
public function generateAccountSharedAccessSignatureToken(
$signedVersion,
$signedPermissions,
$signedService,
$signedResourceType,
$signedExpiry,
$signedStart = "",
$signedIP = "",
$signedProtocol = ""
) {
// check that version is valid
Validate::canCastAsString($signedVersion, 'signedVersion');
Validate::notNullOrEmpty($signedVersion, 'signedVersion');
Validate::isDateString($signedVersion, 'signedVersion');
// validate and sanitize signed service
$signedService = $this->validateAndSanitizeSignedService($signedService);
// validate and sanitize signed resource type
$signedResourceType = $this->validateAndSanitizeSignedResourceType($signedResourceType);
// validate and sanitize signed permissions
$signedPermissions = $this->validateAndSanitizeSignedPermissions($signedPermissions);
// check that expiracy is valid
if ($signedExpiry instanceof \Datetime) {
$signedExpiry = Utilities::isoDate($signedExpiry);
}
Validate::canCastAsString($signedExpiry, 'signedExpiry');
Validate::notNullOrEmpty($signedExpiry, 'signedExpiry');
Validate::isDateString($signedExpiry, 'signedExpiry');
// check that signed start is valid
if ($signedStart instanceof \Datetime) {
$signedStart = Utilities::isoDate($signedStart);
}
Validate::canCastAsString($signedStart, 'signedStart');
if (strlen($signedStart) > 0) {
Validate::isDateString($signedStart, 'signedStart');
}
// check that signed IP is valid
Validate::canCastAsString($signedIP, 'signedIP');
// validate and sanitize signed protocol
$signedProtocol = $this->validateAndSanitizeSignedProtocol($signedProtocol);
// construct an array with the parameters to generate the shared access signature at the account level
$parameters = array();
$parameters[] = $this->accountName;
$parameters[] = $signedPermissions;
$parameters[] = $signedService;
$parameters[] = $signedResourceType;
$parameters[] = $signedStart;
$parameters[] = $signedExpiry;
$parameters[] = $signedIP;
$parameters[] = $signedProtocol;
$parameters[] = $signedVersion;
// implode the parameters into a string
$stringToSign = utf8_encode(implode("\n", $parameters) . "\n");
// decode the account key from base64
$decodedAccountKey = base64_decode($this->accountKey);
// create the signature with hmac sha256
$signature = hash_hmac("sha256", $stringToSign, $decodedAccountKey, true);
// encode the signature as base64 and url encode.
$sig = urlencode(base64_encode($signature));
//adding all the components for account SAS together.
$sas = 'sv=' . $signedVersion;
$sas .= '&ss=' . $signedService;
$sas .= '&srt=' . $signedResourceType;
$sas .= '&sp=' . $signedPermissions;
$sas .= '&se=' . $signedExpiry;
$sas .= $signedStart === ''? '' : '&st=' . $signedStart;
$sas .= $signedIP === ''? '' : '&sip=' . $signedIP;
$sas .= '&spr=' . $signedProtocol;
$sas .= '&sig=' . $sig;
// return the signature
return $sas;
}
/**
* Validates and sanitizes the signed service parameter
*
* @param string $signedService Specifies the signed services accessible
* with the account SAS.
*
* @return string
*/
protected function validateAndSanitizeSignedService($signedService)
{
// validate signed service is not null or empty
Validate::canCastAsString($signedService, 'signedService');
Validate::notNullOrEmpty($signedService, 'signedService');
// The signed service should only be a combination of the letters b(lob) q(ueue) t(able) or f(ile)
$validServices = ['b', 'q', 't', 'f'];
return $this->validateAndSanitizeStringWithArray(
strtolower($signedService),
$validServices
);
}
/**
* Validates and sanitizes the signed resource type parameter
*
* @param string $signedResourceType Specifies the signed resource types
* that are accessible with the account
* SAS.
*
* @return string
*/
protected function validateAndSanitizeSignedResourceType($signedResourceType)
{
// validate signed resource type is not null or empty
Validate::canCastAsString($signedResourceType, 'signedResourceType');
Validate::notNullOrEmpty($signedResourceType, 'signedResourceType');
// The signed resource type should only be a combination of the letters s(ervice) c(container) or o(bject)
$validResourceTypes = ['s', 'c', 'o'];
return $this->validateAndSanitizeStringWithArray(
strtolower($signedResourceType),
$validResourceTypes
);
}
/**
* Validates and sanitizes the signed permissions parameter
*
* @param string $signedPermissions Specifies the signed permissions for the
* account SAS.
*
* @return string
*/
protected function validateAndSanitizeSignedPermissions(
$signedPermissions
) {
// validate signed permissions are not null or empty
Validate::canCastAsString($signedPermissions, 'signedPermissions');
Validate::notNullOrEmpty($signedPermissions, 'signedPermissions');
$validPermissions = ['r', 'w', 'd', 'l', 'a', 'c', 'u', 'p'];
return $this->validateAndSanitizeStringWithArray(
strtolower($signedPermissions),
$validPermissions
);
}
/**
* Validates and sanitizes the signed protocol parameter
*
* @param string $signedProtocol Specifies the signed protocol for the
* account SAS.
* @return string
*/
protected function validateAndSanitizeSignedProtocol($signedProtocol)
{
Validate::canCastAsString($signedProtocol, 'signedProtocol');
// sanitize string
$sanitizedSignedProtocol = strtolower($signedProtocol);
if (strlen($sanitizedSignedProtocol) > 0) {
if (strcmp($sanitizedSignedProtocol, "https") != 0 && strcmp($sanitizedSignedProtocol, "https,http") != 0) {
throw new \InvalidArgumentException(Resources::SIGNED_PROTOCOL_INVALID_VALIDATION_MSG);
}
}
return $sanitizedSignedProtocol;
}
/**
* Removes duplicate characters from a string
*
* @param string $input The input string.
* @return string
*/
protected function validateAndSanitizeStringWithArray($input, array $array)
{
$result = '';
foreach ($array as $value) {
if (strpos($input, $value) !== false) {
//append the valid permission to result.
$result .= $value;
//remove all the character that represents the permission.
$input = str_replace(
$value,
'',
$input
);
}
}
Validate::isTrue(
strlen($input) == 0,
sprintf(
Resources::STRING_NOT_WITH_GIVEN_COMBINATION,
implode(', ', $array)
)
);
return $result;
}
/**
* Generate the canonical resource using the given account name, service
* type and resource.
*
* @param string $accountName The account name of the service.
* @param string $service The service name of the service.
* @param string $resource The name of the resource.
*
* @return string
*/
protected static function generateCanonicalResource(
$accountName,
$service,
$resource
) {
static $serviceMap = array(
Resources::RESOURCE_TYPE_BLOB => 'blob',
Resources::RESOURCE_TYPE_FILE => 'file',
Resources::RESOURCE_TYPE_QUEUE => 'queue',
Resources::RESOURCE_TYPE_TABLE => 'table',
);
$serviceName = $serviceMap[$service];
if (Utilities::startsWith($resource, '/')) {
$resource = substr($resource, 1);
}
return urldecode(sprintf('/%s/%s/%s', $serviceName, $accountName, $resource));
}
}