/home/mip/mip/public/img/credit/datatables/EndpointV2.tar
EndpointDefinitionProvider.php000064400000004336151520661260012573 0ustar00<?php

namespace Aws\EndpointV2;

/**
 * Provides Endpoint-related artifacts used for endpoint resolution
 * and testing.
 */
class EndpointDefinitionProvider
{
    public static function getEndpointRuleset($service, $apiVersion, $baseDir = null)
    {
        return self::getData($service, $apiVersion, 'ruleset', $baseDir);
    }

    public static function getEndpointTests($service, $apiVersion, $baseDir = null)
    {
        return self::getData($service, $apiVersion, 'tests', $baseDir);
    }

    public static function getPartitions()
    {
        $basePath = __DIR__ . '/../data';
        $file = '/partitions.json';

        if (file_exists($basePath . $file . '.php')) {
           return require($basePath . $file . '.php');
        } else {
            return json_decode(file_get_contents($basePath . $file));
        }
    }

    private static function getData($service, $apiVersion, $type, $baseDir)
    {
        $basePath = $baseDir ? $baseDir :  __DIR__ . '/../data';
        $serviceDir = $basePath . "/{$service}";
        if (!is_dir($serviceDir)) {
            throw new \InvalidArgumentException(
                'Invalid service name.'
            );
        }

        if ($apiVersion === 'latest') {
            $apiVersion = self::getLatest($service);
        }

        $rulesetPath = $serviceDir . '/' . $apiVersion;
        if (!is_dir($rulesetPath)) {
            throw new \InvalidArgumentException(
                'Invalid api version.'
            );
        }
        $fileName = $type === 'tests' ? '/endpoint-tests-1' : '/endpoint-rule-set-1';

        if (file_exists($rulesetPath . $fileName . '.json.php')) {
            return require($rulesetPath . $fileName . '.json.php');
        } elseif (file_exists($rulesetPath . $fileName . '.json')) {
            return json_decode(file_get_contents($rulesetPath . $fileName . '.json'), true);
        } else {
            throw new \InvalidArgumentException(
                'Specified ' . $type . ' endpoint file for ' . $service . ' with api version ' . $apiVersion . ' does not exist.'
            );
        }
    }

    private static function getLatest($service)
    {
        $manifest = \Aws\manifest();
        return $manifest[$service]['versions']['latest'];
    }
}Rule/TreeRule.php000064400000003042151520661260007716 0ustar00<?php

namespace Aws\EndpointV2\Rule;

use Aws\EndpointV2\Ruleset\RulesetStandardLibrary;

class TreeRule extends AbstractRule
{
    /** @var array */
    private $rules;

    public function __construct(array $definition)
    {
        parent::__construct($definition);
        $this->rules = $this->createRules($definition['rules']);
    }

    /**
     * @return array
     */
    public function getRules()
    {
        return $this->rules;
    }

    /**
     * If a tree rule's conditions evaluate successfully, iterate over its
     * subordinate rules and return a result if there is one. If any of the
     * subsequent rules are trees, the function will recurse until it reaches
     * an error or an endpoint rule
     *
     * @return mixed
     */
    public function evaluate(
        array $inputParameters,
        RulesetStandardLibrary $standardLibrary
    )
    {
        if ($this->evaluateConditions($inputParameters, $standardLibrary)) {
            foreach($this->rules as $rule) {
                $inputParametersCopy = $inputParameters;
                $evaluation = $rule->evaluate($inputParametersCopy, $standardLibrary);
                if ($evaluation !== false) {
                    return $evaluation;
                }
            }
        }
        return false;
    }

    private function createRules(array $rules)
    {
        $rulesList = [];

        forEach($rules as $rule) {
            $ruleType = RuleCreator::create($rule['type'], $rule);
            $rulesList[] = $ruleType;
        }
        return $rulesList;
    }
}
Rule/RuleCreator.php000064400000001225151520661260010417 0ustar00<?php

namespace Aws\EndpointV2\Rule;

use Aws\Exception\UnresolvedEndpointException;

class RuleCreator
{
    public static function create($type, $definition)
    {
        switch ($type) {
            case 'endpoint':
                return new EndpointRule($definition);
            case 'error':
                return new ErrorRule($definition);
            case 'tree':
                return new TreeRule($definition);
            default:
                throw new UnresolvedEndpointException(
                    'Unknown rule type ' . $type .
                    ' must be of type `endpoint`, `tree` or `error`'
                );
        }
    }
}

Rule/AbstractRule.php000064400000002645151520661260010572 0ustar00<?php

namespace Aws\EndpointV2\Rule;

use Aws\EndpointV2\Ruleset\RulesetStandardLibrary;

/**
 *  A rule within a rule set. All rules contain a conditions property,
 * which can be empty, and documentation about the rule.
 */
abstract class AbstractRule
{
    private $conditions;
    private $documentation;

    public function __construct(array $definition)
    {
        $this->conditions = $definition['conditions'];
        $this->documentation = isset($definition['documentation']) ?
            $definition['documentation'] : null;
    }

    /**
     * @return array
     */
    public function getConditions()
    {
        return $this->conditions;
    }

    /**
     * @return mixed
     */
    public function getDocumentation()
    {
        return $this->documentation;
    }

    /**
     * Determines if all conditions for a given rule are met.
     *
     * @return boolean
     */
    protected function evaluateConditions(
        array &$inputParameters,
        RulesetStandardLibrary $standardLibrary
    )
    {
        foreach($this->getConditions() as $condition) {
            $result = $standardLibrary->callFunction($condition, $inputParameters);
            if (is_null($result) || $result === false) {
                return false;
            }
        }
        return true;
    }

    abstract public function evaluate(
        array $inputParameters,
        RulesetStandardLibrary $standardLibrary
    );
}
Rule/ErrorRule.php000064400000002066151520661260010115 0ustar00<?php

namespace Aws\EndpointV2\Rule;

use Aws\EndpointV2\Ruleset\RulesetStandardLibrary;
use Aws\Exception\UnresolvedEndpointException;

class ErrorRule extends AbstractRule
{
    /** @var array */
    private $error;

    public function __construct($definition)
    {
        parent::__construct($definition);
        $this->error = $definition['error'];
    }

    /**
     * @return array
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * If an error rule's conditions are met, raise an
     * UnresolvedEndpointError containing the fully resolved error string.
     *
     * @return null
     * @throws UnresolvedEndpointException
     */
    public function evaluate(
        array $inputParameters,
        RulesetStandardLibrary $standardLibrary
    )
    {
        if ($this->evaluateConditions($inputParameters, $standardLibrary)) {
            $message = $standardLibrary->resolveValue($this->error, $inputParameters);
            throw new UnresolvedEndpointException($message);
        }
        return false;
    }
}
Rule/EndpointRule.php000064400000006434151520661260010607 0ustar00<?php

namespace Aws\EndpointV2\Rule;

use Aws\EndpointV2\Ruleset\RulesetStandardLibrary;
use Aws\EndpointV2\Ruleset\RulesetEndpoint;

class EndpointRule extends AbstractRule
{
    /** @var array */
    private $endpoint;

    public function __construct(array $definition)
    {
        parent::__construct($definition);
        $this->endpoint = $definition['endpoint'];
    }

    /**
     * @return array
     */
    public function getEndpoint()
    {
        return $this->endpoint;
    }

    /**
     * If all the rule's conditions are met, return the resolved
     * endpoint object.
     *
     * @return RulesetEndpoint | null
     */
    public function evaluate(array $inputParameters, RulesetStandardLibrary $standardLibrary)
    {
        if ($this->evaluateConditions($inputParameters, $standardLibrary)) {
            return $this->resolve($inputParameters, $standardLibrary);
        }
        return false;
    }

    /**
     * Given input parameters, resolve an endpoint in its entirety.
     *
     * @return RulesetEndpoint
     */
    private function resolve(
        array $inputParameters,
        RulesetStandardLibrary $standardLibrary
    )
    {
        $uri = $standardLibrary->resolveValue($this->endpoint['url'], $inputParameters);
        $properties = isset($this->endpoint['properties'])
                ? $this->resolveProperties($this->endpoint['properties'], $inputParameters, $standardLibrary)
                : null;
        $headers = $this->resolveHeaders($inputParameters, $standardLibrary);

        return new RulesetEndpoint($uri, $properties, $headers);
    }

    /**
     * Recurse through an endpoint's `properties` attribute, resolving template
     * strings when found. Return the fully resolved attribute.
     *
     * @return array
     */
    private function resolveProperties(
        $properties,
        array $inputParameters,
        RulesetStandardLibrary $standardLibrary
    )
    {
        if (is_array($properties)) {
           $propertiesArr = [];
           foreach($properties as $key => $val) {
               $propertiesArr[$key] = $this->resolveProperties($val, $inputParameters, $standardLibrary);
           }
           return $propertiesArr;
        } elseif ($standardLibrary->isTemplate($properties)) {
            return $standardLibrary->resolveTemplateString($properties, $inputParameters);
        }
        return $properties;
    }

    /**
     * If present, iterate through an endpoint's headers attribute resolving
     * values along the way. Return the fully resolved attribute.
     *
     * @return array
     */
    private function resolveHeaders(
        array $inputParameters,
        RulesetStandardLibrary $standardLibrary
    )
    {
        $headers = isset($this->endpoint['headers']) ? $this->endpoint['headers'] : null;
        if (is_null($headers)) {
            return null;
        }
        $resolvedHeaders = [];

        foreach($headers as $headerName => $headerValues) {
            $resolvedValues = [];
            foreach($headerValues as $value) {
                $resolvedValue = $standardLibrary->resolveValue($value, $inputParameters, $standardLibrary);
                $resolvedValues[] = $resolvedValue;
            }
            $resolvedHeaders[$headerName] = $resolvedValues;
        }
        return $resolvedHeaders;
    }
}
EndpointV2SerializerTrait.php000064400000004034151520661260012310 0ustar00<?php

namespace Aws\EndpointV2;

use Aws\Api\Serializer\RestSerializer;
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
use GuzzleHttp\Psr7\Uri;

/**
 * Set of helper functions used to set endpoints and endpoint
 * properties derived from dynamic endpoint resolution.
 *
 * @internal
 */
trait EndpointV2SerializerTrait
{
    /**
     * Applies a resolved endpoint, headers and any custom HTTP schemes provided
     * in client configuration to options which are applied to the serialized request.
     *
     * @param $endpoint
     * @param $headers
     *
     * @return void
     */
    private function setEndpointV2RequestOptions(
        RulesetEndpoint $endpoint,
        array &$headers
    ): void
    {
        $this->applyHeaders($endpoint, $headers);
        $resolvedUrl = $endpoint->getUrl();
        $this->applyScheme($resolvedUrl);
        $this->endpoint = $this instanceof RestSerializer
            ? new Uri($resolvedUrl)
            : $resolvedUrl;
    }

    /**
     * Combines modeled headers and headers resolved from an endpoint object.
     *
     * @param $endpoint
     * @param $headers
     * @return void
     */
    private function applyHeaders(RulesetEndpoint $endpoint, array &$headers): void
    {
        if (!is_null($endpoint->getHeaders())) {
           $headers = array_merge(
               $headers,
               $endpoint->getHeaders()
           );
        }
    }

    /**
     * Applies custom HTTP schemes provided in client configuration.
     *
     * @param $resolvedUrl
     * @return void
     */
    private function applyScheme(&$resolvedUrl): void
    {
        $resolvedEndpointScheme = parse_url($resolvedUrl, PHP_URL_SCHEME);
        $scheme = $this->endpoint instanceof Uri
            ? $this->endpoint->getScheme()
            : parse_url($this->endpoint, PHP_URL_SCHEME);

        if (!empty($scheme) && $scheme !== $resolvedEndpointScheme) {
            $resolvedUrl = str_replace(
                $resolvedEndpointScheme,
                $scheme,
                $resolvedUrl
            );
        }
    }
}
Ruleset/RulesetEndpoint.php000064400000002110151520661260012022 0ustar00<?php

namespace Aws\EndpointV2\Ruleset;

/**
 * Represents a fully resolved endpoint that a
 * rule returns if input parameters meet its requirements.
 */
class RulesetEndpoint
{
    /** @var string */
    private $url;

    /** @var array */
    private $properties;

    /** @var array */
    private $headers;

    public function __construct($url, $properties = null, $headers = null)
    {
        $this->url = $url;
        $this->properties = $properties;
        $this->headers = $headers;
    }

    /**
     * @return mixed
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * @param $property
     * @return mixed
     */
    public function getProperty($property)
    {
        if (isset($this->properties[$property])) {
            return $this->properties[$property];
        }

        return null;
    }

    /**
     * @return mixed
     */
    public function getProperties()
    {
        return $this->properties;
    }

    /**
     * @return mixed
     */
    public function getHeaders()
    {
        return $this->headers;
    }
}
Ruleset/RulesetStandardLibrary.php000064400000031326151520661260013342 0ustar00<?php

namespace Aws\EndpointV2\Ruleset;

use Aws\Exception\UnresolvedEndpointException;

/**
 * Provides functions and actions to be performed for endpoint evaluation.
 * This is an internal only class and is not subject to backwards-compatibility guarantees.
 *
 * @internal
 */
class RulesetStandardLibrary
{
    const IPV4_RE = '/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/';
    const IPV6_RE = '/([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|
                    . ([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]
                    . {1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:)
                    . {1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|
                    . [0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:
                    . (:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|
                    . 1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]
                    . {1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]
                    . |1{0,1}[0-9]){0,1}[0-9])/';
    const TEMPLATE_ESCAPE_RE = '/{\{\s*(.*?)\s*\}\}/';
    const TEMPLATE_SEARCH_RE = '/\{[a-zA-Z#]+\}/';
    const TEMPLATE_PARSE_RE = '#\{((?>[^\{\}]+)|(?R))*\}#x';
    const HOST_LABEL_RE = '/^(?!-)[a-zA-Z\d-]{1,63}(?<!-)$/';

    private $partitions;

    public function __construct($partitions)
    {
        $this->partitions = $partitions;
    }

    /**
     * Determines if a value is set.
     *
     * @return boolean
     */
    public function is_set($value)
    {
        return isset($value);
    }

    /**
     * Function implementation of logical operator `not`
     *
     * @return boolean
     */
    public function not($value)
    {
        return !$value;
    }

    /**
     * Find an attribute within a value given a path string.
     *
     * @return mixed
     */
    public function getAttr($from, $path)
    {
        $parts = explode('.', $path);
        foreach ($parts as $part) {
            $sliceIdx = strpos($part, '[');
            if ($sliceIdx !== false) {
                if (substr($part, -1) !== ']') {
                    return null;
                }
                $slice = intval(substr($part, $sliceIdx + 1, strlen($part) - 1));
                $from = isset($from[substr($part,0, $sliceIdx)][$slice])
                    ? $from[substr($part,0, $sliceIdx)][$slice]
                    : null;
            } else {
                $from = $from[$part];
            }
        }
        return $from;
    }

    /**
     * Computes a substring given the start index and end index. If `reverse` is
     * true, slice the string from the end instead.
     *
     * @return mixed
     */
    public function substring($input, $start, $stop, $reverse)
    {
        if (!is_string($input)) {
            throw new UnresolvedEndpointException(
                'Input passed to `substring` must be `string`.'
            );
        }

        if (preg_match('/[^\x00-\x7F]/', $input)) {
            return null;
        }
        if ($start >= $stop or strlen($input) < $stop) {
            return null;
        }
        if (!$reverse) {
            return substr($input, $start, $stop - $start);
        } else {
            $offset = strlen($input) - $stop;
            $length = $stop - $start;
            return substr($input, $offset, $length);
        }
    }

    /**
     * Evaluates two strings for equality.
     *
     * @return boolean
     */
    public function stringEquals($string1, $string2)
    {
        if (!is_string($string1) || !is_string($string2)) {
            throw new UnresolvedEndpointException(
                'Values passed to StringEquals must be `string`.'
            );
        }
        return $string1 === $string2;
    }

    /**
     * Evaluates two booleans for equality.
     *
     * @return boolean
     */
    public function booleanEquals($boolean1, $boolean2)
    {
        return
            filter_var($boolean1, FILTER_VALIDATE_BOOLEAN)
            === filter_var($boolean2, FILTER_VALIDATE_BOOLEAN);
    }

    /**
     * Percent-encodes an input string.
     *
     * @return mixed
     */
    public function uriEncode($input)
    {
        if (is_null($input)) {
            return null;
        }
        return str_replace('%7E', '~', rawurlencode($input));
    }

    /**
     * Parses URL string into components.
     *
     * @return mixed
     */
    public function parseUrl($url)
    {
        if (is_null($url)) {
            return null;
        }

        $parsed = parse_url($url);

        if ($parsed === false || !empty($parsed['query'])) {
            return null;
        } elseif (!isset($parsed['scheme'])) {
            return null;
        }

        if ($parsed['scheme'] !== 'http'
            && $parsed['scheme'] !== 'https'
        ) {
            return null;
        }

        $urlInfo = [];
        $urlInfo['scheme'] = $parsed['scheme'];
        $urlInfo['authority'] = isset($parsed['host']) ? $parsed['host'] : '';
        if (isset($parsed['port'])) {
            $urlInfo['authority'] = $urlInfo['authority'] . ":" . $parsed['port'];
        }
        $urlInfo['path'] = isset($parsed['path']) ? $parsed['path'] : '';
        $urlInfo['normalizedPath'] = !empty($parsed['path'])
            ? rtrim($urlInfo['path'] ?: '', '/' .  "/") . '/'
            : '/';
        $urlInfo['isIp'] = !isset($parsed['host']) ?
            'false' : $this->isValidIp($parsed['host']);

        return $urlInfo;
    }

    /**
     * Evaluates whether a value is a valid host label per
     * RFC 1123. If allow_subdomains is true, split on `.` and validate
     * each subdomain separately.
     *
     * @return boolean
     */
    public function isValidHostLabel($hostLabel, $allowSubDomains)
    {
        if (!isset($hostLabel)
            || (!$allowSubDomains && strpos($hostLabel, '.') != false)
        ) {
            return false;
        }

        if ($allowSubDomains) {
            foreach (explode('.', $hostLabel) as $subdomain) {
                if (!$this->validateHostLabel($subdomain)) {
                    return false;
                }
            }
            return true;
        } else {
            return $this->validateHostLabel($hostLabel);
        }
    }

    /**
     * Parse and validate string for ARN components.
     *
     * @return array|null
     */
    public function parseArn($arnString)
    {
        if (is_null($arnString)
            || substr( $arnString, 0, 3 ) !== "arn"
        ) {
            return null;
        }

        $arn = [];
        $parts = explode(':', $arnString, 6);
        if (sizeof($parts) < 6) {
            return null;
        }

        $arn['partition'] = isset($parts[1]) ? $parts[1] : null;
        $arn['service'] = isset($parts[2]) ? $parts[2] : null;
        $arn['region'] = isset($parts[3]) ? $parts[3] : null;
        $arn['accountId'] = isset($parts[4]) ? $parts[4] : null;
        $arn['resourceId'] = isset($parts[5]) ? $parts[5] : null;

        if (empty($arn['partition'])
            || empty($arn['service'])
            || empty($arn['resourceId'])
        ) {
            return null;
        }
        $resource = $arn['resourceId'];
        $arn['resourceId'] = preg_split("/[:\/]/", $resource);

        return $arn;
    }

    /**
     * Matches a region string to an AWS partition.
     *
     * @return mixed
     */
    public function partition($region)
    {
        if (!is_string($region)) {
            throw new UnresolvedEndpointException(
                'Value passed to `partition` must be `string`.'
            );
        }

        $partitions = $this->partitions;
        foreach ($partitions['partitions'] as $partition) {
            if (array_key_exists($region, $partition['regions'])
                || preg_match("/{$partition['regionRegex']}/", $region)
            ) {
                return $partition['outputs'];
            }
        }
        //return `aws` partition if no match is found.
        return $partitions['partitions'][0]['outputs'];
    }

    /**
     * Evaluates whether a value is a valid bucket name for virtual host
     * style bucket URLs.
     *
     * @return boolean
     */
    public function isVirtualHostableS3Bucket($bucketName, $allowSubdomains)
    {
        if ((is_null($bucketName)
            || (strlen($bucketName) < 3 || strlen($bucketName) > 63))
            || preg_match(self::IPV4_RE, $bucketName)
            || strtolower($bucketName) !== $bucketName
        ) {
            return false;
        }

        if ($allowSubdomains) {
            $labels = explode('.', $bucketName);
            $results = [];
            forEach($labels as $label) {
                $results[] = $this->isVirtualHostableS3Bucket($label, false);
            }
            return !in_array(false, $results);
        }
        return $this->isValidHostLabel($bucketName, false);
    }

    public function callFunction($funcCondition, &$inputParameters)
    {
        $funcArgs = [];

        forEach($funcCondition['argv'] as $arg) {
            $funcArgs[] = $this->resolveValue($arg, $inputParameters);
        }

        $funcName = str_replace('aws.', '', $funcCondition['fn']);
        if ($funcName === 'isSet') {
            $funcName = 'is_set';
        }

        $result = call_user_func_array(
            [RulesetStandardLibrary::class, $funcName],
            $funcArgs
        );

        if (isset($funcCondition['assign'])) {
            $assign = $funcCondition['assign'];
            if (isset($inputParameters[$assign])){
                throw new UnresolvedEndpointException(
                    "Assignment `{$assign}` already exists in input parameters" .
                    " or has already been assigned by an endpoint rule and cannot be overwritten."
                );
            }
            $inputParameters[$assign] = $result;
        }
        return $result;
    }

    public function resolveValue($value, $inputParameters)
    {
        //Given a value, check if it's a function, reference or template.
        //returns resolved value
        if ($this->isFunc($value)) {
            return $this->callFunction($value, $inputParameters);
        } elseif ($this->isRef($value)) {
            return isset($inputParameters[$value['ref']]) ? $inputParameters[$value['ref']] : null;
        } elseif ($this->isTemplate($value)) {
            return $this->resolveTemplateString($value, $inputParameters);
        }
        return $value;
    }

    public function isFunc($arg)
    {
        return is_array($arg) && isset($arg['fn']);
    }

    public function isRef($arg)
    {
        return is_array($arg) && isset($arg['ref']);
    }

    public function isTemplate($arg)
    {
        return is_string($arg) && !empty(preg_match(self::TEMPLATE_SEARCH_RE, $arg));
    }

    public function resolveTemplateString($value, $inputParameters)
    {
        return preg_replace_callback(
            self::TEMPLATE_PARSE_RE,
            function ($match) use ($inputParameters) {
                if (preg_match(self::TEMPLATE_ESCAPE_RE, $match[0])) {
                    return $match[1];
                }

                $notFoundMessage = 'Resolved value was null.  Please check rules and ' .
                    'input parameters and try again.';

                $parts = explode("#", $match[1]);
                if (count($parts) > 1) {
                    $resolvedValue = $inputParameters;
                    foreach($parts as $part) {
                        if (!isset($resolvedValue[$part])) {
                            throw new UnresolvedEndpointException($notFoundMessage);
                        }
                        $resolvedValue = $resolvedValue[$part];
                    }
                    return $resolvedValue;
                } else {
                    if (!isset($inputParameters[$parts[0]])) {
                        throw new UnresolvedEndpointException($notFoundMessage);
                    }
                    return $inputParameters[$parts[0]];
                }
            },
            $value
        );
    }

    private function validateHostLabel ($hostLabel)
    {
        if (empty($hostLabel) || strlen($hostLabel) > 63) {
            return false;
        }
        if (preg_match(self::HOST_LABEL_RE, $hostLabel)) {
            return true;
        }
        return false;
    }

    private function isValidIp($hostName)
    {
        $isWrapped = strpos($hostName, '[') === 0
            && strrpos($hostName, ']') === strlen($hostName) - 1;

        return preg_match(
                self::IPV4_RE,
            $hostName
        )
        //IPV6 enclosed in brackets
        || ($isWrapped && preg_match(
            self::IPV6_RE,
            $hostName
        ))
            ? 'true' : 'false';
    }
}
Ruleset/RulesetParameter.php000064400000006716151520661260012202 0ustar00<?php

namespace Aws\EndpointV2\Ruleset;

use Aws\Exception\UnresolvedEndpointException;

/**
 * Houses properties of an individual parameter definition.
 */
class RulesetParameter
{
    /** @var string */
    private $name;

    /** @var string */
    private $type;

    /** @var string */
    private $builtIn;

    /** @var string */
    private $default;

    /** @var array */
    private $required;

    /** @var string */
    private $documentation;

    /** @var boolean */
    private $deprecated;

    public function __construct($name, array $definition)
    {
        $type = ucfirst($definition['type']);
        if ($this->isValidType($type)) {
            $this->type = $type;
        } else {
            throw new UnresolvedEndpointException(
                'Unknown parameter type ' . "`{$type}`" .
                '. Parameters must be of type `String` or `Boolean`.'
            );
        }
        $this->name = $name;
        $this->builtIn = isset($definition['builtIn']) ? $definition['builtIn'] : null;
        $this->default = isset($definition['default']) ? $definition['default'] : null;
        $this->required =  isset($definition['required']) ?
            $definition['required'] : false;
        $this->documentation =  isset($definition['documentation']) ?
            $definition['documentation'] : null;
        $this->deprecated =  isset($definition['deprecated']) ?
            $definition['deprecated'] : false;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return mixed
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @return mixed
     */
    public function getBuiltIn()
    {
        return $this->builtIn;
    }

    /**
     * @return mixed
     */
    public function getDefault()
    {
        return $this->default;
    }

    /**
     * @return boolean
     */
    public function getRequired()
    {
        return $this->required;
    }

    /**
     * @return string
     */
    public function getDocumentation()
    {
        return $this->documentation;
    }

    /**
     * @return boolean
     */
    public function getDeprecated()
    {
        return $this->deprecated;
    }

    /**
     * Validates that an input parameter matches the type provided in its definition.
     *
     * @return void
     * @throws InvalidArgumentException
     */
    public function validateInputParam($inputParam)
    {
        $typeMap = [
            'String' => 'is_string',
            'Boolean' => 'is_bool'
        ];

        if ($typeMap[$this->type]($inputParam) === false) {
            throw new UnresolvedEndpointException(
                "Input parameter `{$this->name}` is the wrong type. Must be a {$this->type}."
            );
        }

        if ($this->deprecated) {
            $deprecated = $this->deprecated;
            $deprecationString = "{$this->name} has been deprecated ";
            $msg = isset($deprecated['message']) ? $deprecated['message'] : null;
            $since = isset($deprecated['since']) ? $deprecated['since'] : null;

            if (!is_null($since)) $deprecationString = $deprecationString
                . 'since '. $since . '. ';
            if (!is_null($msg)) $deprecationString = $deprecationString . $msg;

            trigger_error($deprecationString, E_USER_WARNING);
        }
    }

    private function isValidType($type)
    {
        return in_array($type, ['String', 'Boolean']);
    }
}
Ruleset/Ruleset.php000064400000005466151520661260010342 0ustar00<?php

namespace Aws\EndpointV2\Ruleset;

use Aws\EndpointV2\Rule\RuleCreator;

/**
 * A collection of rules, parameter definitions and a class of helper functions
 * used to resolve either an endpoint or an error.
 */
class Ruleset
{
    /** @var string */
    private $version;

    /** @var array */
    private $parameters;

    /** @var array */
    private $rules;

    /** @var RulesetStandardLibrary */
    public $standardLibrary;

    public function __construct(array $ruleset, array $partitions)
    {
        $this->version = $ruleset['version'];
        $this->parameters = $this->createParameters($ruleset['parameters']);
        $this->rules = $this->createRules($ruleset['rules']);
        $this->standardLibrary = new RulesetStandardLibrary($partitions);
    }

    /**
     * @return mixed
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * @return array
     */
    public function getParameters()
    {
        return $this->parameters;
    }

    /**
     * @return array
     */
    public function getRules()
    {
        return $this->rules;
    }

    /**
     * Evaluate the ruleset against the input parameters.
     * Return the first rule the parameters match against.
     *
     * @return mixed
     */
    public function evaluate(array $inputParameters)
    {
        $this->validateInputParameters($inputParameters);

        foreach($this->rules as $rule) {
            $evaluation = $rule->evaluate($inputParameters, $this->standardLibrary);
            if ($evaluation !== false) {
                return $evaluation;
            }
        }
        return false;
    }

    /**
     * Ensures all corresponding client-provided parameters match
     * the Ruleset parameter's specified type.
     *
     * @return void
     */
    private function validateInputParameters(array &$inputParameters)
    {
        foreach($this->parameters as $paramName => $param) {
            $inputParam = isset($inputParameters[$paramName]) ? $inputParameters[$paramName] : null;

            if (is_null($inputParam) && !is_null($param->getDefault())) {
                $inputParameters[$paramName] = $param->getDefault();
            } elseif (!is_null($inputParam)) {
                $param->validateInputParam($inputParam);
            }
        }
    }

    private function createParameters(array $parameters)
    {
        $parameterList = [];

        foreach($parameters as $name => $definition) {
            $parameterList[$name] = new RulesetParameter($name, $definition);
        }

        return $parameterList;
    }

    private function createRules(array $rules)
    {
        $rulesList = [];

        forEach($rules as $rule) {
            $ruleObj = RuleCreator::create($rule['type'], $rule);
            $rulesList[] = $ruleObj;
        }
        return $rulesList;
    }
}

EndpointProviderV2.php000064400000003317151520661260010770 0ustar00<?php

namespace Aws\EndpointV2;

use Aws\EndpointV2\Ruleset\Ruleset;
use Aws\Exception\UnresolvedEndpointException;
use Aws\LruArrayCache;

/**
 * Given a service's Ruleset and client-provided input parameters, provides
 * either an object reflecting the properties of a resolved endpoint,
 * or throws an error.
 */
class EndpointProviderV2
{
    /** @var Ruleset */
    private $ruleset;

    /** @var LruArrayCache */
    private $cache;

    public function __construct(array $ruleset, array $partitions)
    {
        $this->ruleset = new Ruleset($ruleset, $partitions);
        $this->cache = new LruArrayCache(100);
    }

    /**
     * @return Ruleset
     */
    public function getRuleset()
    {
        return $this->ruleset;
    }

    /**
     * Given a Ruleset and input parameters, determines the correct endpoint
     * or an error to be thrown for a given request.
     *
     * @return RulesetEndpoint
     * @throws UnresolvedEndpointException
     */
    public function resolveEndpoint(array $inputParameters)
    {
        $hashedParams = $this->hashInputParameters($inputParameters);
        $match = $this->cache->get($hashedParams);

        if (!is_null($match)) {
            return $match;
        }

        $endpoint = $this->ruleset->evaluate($inputParameters);
        if ($endpoint === false) {
            throw new UnresolvedEndpointException(
                'Unable to resolve an endpoint using the provider arguments: '
                . json_encode($inputParameters)
            );
        }
        $this->cache->set($hashedParams, $endpoint);

        return $endpoint;
    }

    private function hashInputParameters($inputParameters)
    {
        return md5(serialize($inputParameters));
    }
}
EndpointV2Middleware.php000064400000021547151520661260011260 0ustar00<?php
namespace Aws\EndpointV2;

use Aws\Api\Operation;
use Aws\Api\Service;
use Aws\Auth\Exception\UnresolvedAuthSchemeException;
use Aws\CommandInterface;
use Closure;
use GuzzleHttp\Promise\Promise;

/**
 * Handles endpoint rule evaluation and endpoint resolution.
 *
 * IMPORTANT: this middleware must be added to the "build" step.
 * Specifically, it must precede the 'builder' step.
 *
 * @internal
 */
class EndpointV2Middleware
{
    private static $validAuthSchemes = [
        'sigv4' => 'v4',
        'sigv4a' => 'v4a',
        'none' => 'anonymous',
        'bearer' => 'bearer',
        'sigv4-s3express' => 'v4-s3express'
    ];

    /** @var callable */
    private $nextHandler;

    /** @var EndpointProviderV2 */
    private $endpointProvider;

    /** @var Service */
    private $api;

    /** @var array */
    private $clientArgs;

    /**
     * Create a middleware wrapper function
     *
     * @param EndpointProviderV2 $endpointProvider
     * @param Service $api
     * @param array $args
     *
     * @return Closure
     */
    public static function wrap(
        EndpointProviderV2 $endpointProvider,
        Service $api,
        array $args
    ) : Closure
    {
        return function (callable $handler) use ($endpointProvider, $api, $args) {
            return new self($handler, $endpointProvider, $api, $args);
        };
    }

    /**
     * @param callable $nextHandler
     * @param EndpointProviderV2 $endpointProvider
     * @param Service $api
     * @param array $args
     */
    public function __construct(
        callable $nextHandler,
        EndpointProviderV2 $endpointProvider,
        Service $api,
        array $args
    )
    {
        $this->nextHandler = $nextHandler;
        $this->endpointProvider = $endpointProvider;
        $this->api = $api;
        $this->clientArgs = $args;
    }

    /**
     * @param CommandInterface $command
     *
     * @return Promise
     */
    public function __invoke(CommandInterface $command)
    {
        $nextHandler = $this->nextHandler;
        $operation = $this->api->getOperation($command->getName());
        $commandArgs = $command->toArray();

        $providerArgs = $this->resolveArgs($commandArgs, $operation);
        $endpoint = $this->endpointProvider->resolveEndpoint($providerArgs);

        if (!empty($authSchemes = $endpoint->getProperty('authSchemes'))) {
            $this->applyAuthScheme(
                $authSchemes,
                $command
            );
        }

        return $nextHandler($command, $endpoint);
    }

    /**
     * Resolves client, context params, static context params and endpoint provider
     * arguments provided at the command level.
     *
     * @param array $commandArgs
     * @param Operation $operation
     *
     * @return array
     */
    private function resolveArgs(array $commandArgs, Operation $operation) : array
    {
        $rulesetParams = $this->endpointProvider->getRuleset()->getParameters();
        $endpointCommandArgs = $this->filterEndpointCommandArgs(
            $rulesetParams,
            $commandArgs
        );
        $staticContextParams = $this->bindStaticContextParams(
            $operation->getStaticContextParams()
        );
        $contextParams = $this->bindContextParams(
            $commandArgs, $operation->getContextParams()
        );

        return array_merge(
            $this->clientArgs,
            $contextParams,
            $staticContextParams,
            $endpointCommandArgs
        );
    }

    /**
     * Compares Ruleset parameters against Command arguments
     * to create a mapping of arguments to pass into the
     * endpoint provider for endpoint resolution.
     *
     * @param array $rulesetParams
     * @param array $commandArgs
     * @return array
     */
    private function filterEndpointCommandArgs(
        array $rulesetParams,
        array $commandArgs
    ) : array
    {
        $endpointMiddlewareOpts = [
            '@use_dual_stack_endpoint' => 'UseDualStack',
            '@use_accelerate_endpoint' => 'Accelerate',
            '@use_path_style_endpoint' => 'ForcePathStyle'
        ];

        $filteredArgs = [];

        foreach($rulesetParams as $name => $value) {
            if (isset($commandArgs[$name])) {
                if (!empty($value->getBuiltIn())) {
                    continue;
                }
                $filteredArgs[$name] = $commandArgs[$name];
            }
        }

        if ($this->api->getServiceName() === 's3') {
            foreach($endpointMiddlewareOpts as $optionName => $newValue) {
                if (isset($commandArgs[$optionName])) {
                    $filteredArgs[$newValue] = $commandArgs[$optionName];
                }
            }
        }

        return $filteredArgs;
    }

    /**
     * Binds static context params to their corresponding values.
     *
     * @param $staticContextParams
     *
     * @return array
     */
    private function bindStaticContextParams($staticContextParams) : array
    {
        $scopedParams = [];

        forEach($staticContextParams as $paramName => $paramValue) {
            $scopedParams[$paramName] = $paramValue['value'];
        }

        return $scopedParams;
    }

    /**
     * Binds context params to their corresponding values found in
     * command arguments.
     *
     * @param array $commandArgs
     * @param array $contextParams
     *
     * @return array
     */
    private function bindContextParams(
        array $commandArgs,
        array $contextParams
    ) : array
    {
        $scopedParams = [];

        foreach($contextParams as $name => $spec) {
            if (isset($commandArgs[$spec['shape']])) {
                $scopedParams[$name] = $commandArgs[$spec['shape']];
            }
        }

        return $scopedParams;
    }

    /**
     * Applies resolved auth schemes to the command object.
     *
     * @param $authSchemes
     * @param $command
     *
     * @return void
     */
    private function applyAuthScheme(
        array $authSchemes,
        CommandInterface $command
    ) : void
    {
        $authScheme = $this->resolveAuthScheme($authSchemes);
        $command->setAuthSchemes($authScheme);
    }

    /**
     * Returns the first compatible auth scheme in an endpoint object's
     * auth schemes.
     *
     * @param array $authSchemes
     *
     * @return array
     */
    private function resolveAuthScheme(array $authSchemes) : array
    {
        $invalidAuthSchemes = [];

        foreach($authSchemes as $authScheme) {
            if ($this->isValidAuthScheme($authScheme['name'])) {
                return $this->normalizeAuthScheme($authScheme);
            }
            $invalidAuthSchemes[$authScheme['name']] = false;
        }

        $invalidAuthSchemesString = '`' . implode(
            '`, `',
            array_keys($invalidAuthSchemes))
            . '`';
        $validAuthSchemesString = '`'
            . implode('`, `', array_keys(
                array_diff_key(self::$validAuthSchemes, $invalidAuthSchemes))
            )
            . '`';
        throw new UnresolvedAuthSchemeException(
            "This operation requests {$invalidAuthSchemesString}"
            . " auth schemes, but the client currently supports {$validAuthSchemesString}."
        );
    }

    /**
     * Normalizes an auth scheme's name, signing region or signing region set
     * to the auth keys recognized by the SDK.
     *
     * @param array $authScheme
     * @return array
     */
    private function normalizeAuthScheme(array $authScheme) : array
    {
        /*
            sigv4a will contain a regionSet property. which is guaranteed to be `*`
            for now.  The SigV4 class handles this automatically for now. It seems
            complexity will be added here in the future.
       */
        $normalizedAuthScheme = [];

        if (isset($authScheme['disableDoubleEncoding'])
            && $authScheme['disableDoubleEncoding'] === true
            && $authScheme['name'] !== 'sigv4a'
            && $authScheme['name'] !== 'sigv4-s3express'
        ) {
            $normalizedAuthScheme['version'] = 's3v4';
        } else {
            $normalizedAuthScheme['version'] = self::$validAuthSchemes[$authScheme['name']];
        }

        $normalizedAuthScheme['name'] = isset($authScheme['signingName']) ?
            $authScheme['signingName'] : null;
        $normalizedAuthScheme['region'] = isset($authScheme['signingRegion']) ?
            $authScheme['signingRegion'] : null;
        $normalizedAuthScheme['signingRegionSet'] = isset($authScheme['signingRegionSet']) ?
            $authScheme['signingRegionSet'] : null;

        return $normalizedAuthScheme;
    }

    private function isValidAuthScheme($signatureVersion): bool
    {
        if (isset(self::$validAuthSchemes[$signatureVersion])) {
              if ($signatureVersion === 'sigv4a') {
                  return extension_loaded('awscrt');
              }
              return true;
        }
        return false;
    }
}