vendor/hwi/oauth-bundle/Security/OAuthUtils.php line 101

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the HWIOAuthBundle package.
  4.  *
  5.  * (c) Hardware Info <opensource@hardware.info>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace HWI\Bundle\OAuthBundle\Security;
  11. use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface;
  12. use HWI\Bundle\OAuthBundle\OAuth\State\State;
  13. use HWI\Bundle\OAuthBundle\Security\Http\ResourceOwnerMapInterface;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  16. use Symfony\Component\Security\Http\HttpUtils;
  17. /**
  18.  * @author Alexander <iam.asm89@gmail.com>
  19.  * @author Joseph Bielawski <stloyd@gmail.com>
  20.  * @author Francisco Facioni <fran6co@gmail.com>
  21.  *
  22.  * @final since 1.4
  23.  */
  24. class OAuthUtils
  25. {
  26.     public const SIGNATURE_METHOD_HMAC 'HMAC-SHA1';
  27.     public const SIGNATURE_METHOD_RSA 'RSA-SHA1';
  28.     public const SIGNATURE_METHOD_PLAINTEXT 'PLAINTEXT';
  29.     /**
  30.      * @var bool
  31.      */
  32.     protected $connect;
  33.     /**
  34.      * @var string
  35.      */
  36.     protected $grantRule;
  37.     /**
  38.      * @var HttpUtils
  39.      */
  40.     protected $httpUtils;
  41.     /**
  42.      * @var ResourceOwnerMapInterface[]
  43.      */
  44.     protected $ownerMaps = [];
  45.     /**
  46.      * @var AuthorizationCheckerInterface
  47.      */
  48.     protected $authorizationChecker;
  49.     /**
  50.      * @param bool   $connect
  51.      * @param string $grantRule
  52.      */
  53.     public function __construct(
  54.         HttpUtils $httpUtils,
  55.         AuthorizationCheckerInterface $authorizationChecker,
  56.         $connect,
  57.         $grantRule
  58.     ) {
  59.         $this->httpUtils $httpUtils;
  60.         $this->authorizationChecker $authorizationChecker;
  61.         $this->connect $connect;
  62.         $this->grantRule $grantRule;
  63.     }
  64.     public function addResourceOwnerMap(ResourceOwnerMapInterface $ownerMap)
  65.     {
  66.         $this->ownerMaps[] = $ownerMap;
  67.     }
  68.     /**
  69.      * @return array
  70.      */
  71.     public function getResourceOwners()
  72.     {
  73.         $resourceOwners = [];
  74.         foreach ($this->ownerMaps as $ownerMap) {
  75.             $resourceOwners array_merge($resourceOwners$ownerMap->getResourceOwners());
  76.         }
  77.         return array_keys($resourceOwners);
  78.     }
  79.     /**
  80.      * @param string $name
  81.      * @param string $redirectUrl     Optional
  82.      * @param array  $extraParameters Optional
  83.      *
  84.      * @return string
  85.      */
  86.     public function getAuthorizationUrl(Request $request$name$redirectUrl null, array $extraParameters = [])
  87.     {
  88.         $resourceOwner $this->getResourceOwner($name);
  89.         if (null === $redirectUrl) {
  90.             if (!$this->connect || !$this->authorizationChecker->isGranted($this->grantRule)) {
  91.                 $redirectUrl $this->httpUtils->generateUri($request$this->getResourceOwnerCheckPath($name));
  92.             } else {
  93.                 $redirectUrl $this->getServiceAuthUrl($request$resourceOwner);
  94.             }
  95.         }
  96.         if ($request->query->has('state')) {
  97.             $this->addQueryParameterToState($request->query->get('state'), $resourceOwner);
  98.         }
  99.         return $resourceOwner->getAuthorizationUrl($redirectUrl$extraParameters);
  100.     }
  101.     /**
  102.      * @return string
  103.      */
  104.     public function getServiceAuthUrl(Request $requestResourceOwnerInterface $resourceOwner)
  105.     {
  106.         if ($resourceOwner->getOption('auth_with_one_url')) {
  107.             return $this->httpUtils->generateUri($request$this->getResourceOwnerCheckPath($resourceOwner->getName()));
  108.         }
  109.         $request->attributes->set('service'$resourceOwner->getName());
  110.         if ($request->query->has('state')) {
  111.             $this->addQueryParameterToState($request->query->get('state'), $resourceOwner);
  112.         }
  113.         return $this->httpUtils->generateUri($request'hwi_oauth_connect_service');
  114.     }
  115.     /**
  116.      * @param string $name
  117.      *
  118.      * @return string
  119.      */
  120.     public function getLoginUrl(Request $request$name)
  121.     {
  122.         // Just to check that this resource owner exists
  123.         $this->getResourceOwner($name);
  124.         $request->attributes->set('service'$name);
  125.         $url $this->httpUtils->generateUri($request'hwi_oauth_service_redirect');
  126.         if ($request->query->has('state')) {
  127.             $data = ['state' => $request->query->all()['state']];
  128.             $url .= '?'.http_build_query($data);
  129.         }
  130.         return $url;
  131.     }
  132.     /**
  133.      * Sign the request parameters.
  134.      *
  135.      * @param string $method          Request method
  136.      * @param string $url             Request url
  137.      * @param array  $parameters      Parameters for the request
  138.      * @param string $clientSecret    Client secret to use as key part of signing
  139.      * @param string $tokenSecret     Optional token secret to use with signing
  140.      * @param string $signatureMethod Optional signature method used to sign token
  141.      *
  142.      * @return string
  143.      *
  144.      * @throws \RuntimeException
  145.      */
  146.     public static function signRequest($method$url$parameters$clientSecret$tokenSecret ''$signatureMethod self::SIGNATURE_METHOD_HMAC)
  147.     {
  148.         // Validate required parameters
  149.         foreach (['oauth_consumer_key''oauth_timestamp''oauth_nonce''oauth_version''oauth_signature_method'] as $parameter) {
  150.             if (!isset($parameters[$parameter])) {
  151.                 throw new \RuntimeException(sprintf('Parameter "%s" must be set.'$parameter));
  152.             }
  153.         }
  154.         // Remove oauth_signature if present
  155.         // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
  156.         if (isset($parameters['oauth_signature'])) {
  157.             unset($parameters['oauth_signature']);
  158.         }
  159.         // Parse & add query params as base string parameters if they exists
  160.         $url parse_url($url);
  161.         if (isset($url['query'])) {
  162.             parse_str($url['query'], $queryParams);
  163.             $parameters += $queryParams;
  164.         }
  165.         // Remove default ports
  166.         // Ref: Spec: 9.1.2
  167.         $explicitPort $url['port'] ?? null;
  168.         if (('https' === $url['scheme'] && 443 === $explicitPort) || ('http' === $url['scheme'] && 80 === $explicitPort)) {
  169.             $explicitPort null;
  170.         }
  171.         // Remove query params from URL
  172.         // Ref: Spec: 9.1.2
  173.         $url sprintf('%s://%s%s%s'$url['scheme'], $url['host'], ($explicitPort ':'.$explicitPort ''), $url['path'] ?? '');
  174.         // Parameters are sorted by name, using lexicographical byte value ordering.
  175.         // Ref: Spec: 9.1.1 (1)
  176.         uksort($parameters'strcmp');
  177.         // http_build_query should use RFC3986
  178.         $parts = [
  179.             // HTTP method name must be uppercase
  180.             // Ref: Spec: 9.1.3 (1)
  181.             strtoupper($method),
  182.             rawurlencode($url),
  183.             rawurlencode(str_replace(['%7E''+'], ['~''%20'], http_build_query($parameters'''&'))),
  184.         ];
  185.         $baseString implode('&'$parts);
  186.         switch ($signatureMethod) {
  187.             case self::SIGNATURE_METHOD_HMAC:
  188.                 $keyParts = [
  189.                     rawurlencode($clientSecret),
  190.                     rawurlencode($tokenSecret),
  191.                 ];
  192.                 $signature hash_hmac('sha1'$baseStringimplode('&'$keyParts), true);
  193.                 break;
  194.             case self::SIGNATURE_METHOD_RSA:
  195.                 if (!\function_exists('openssl_pkey_get_private')) {
  196.                     throw new \RuntimeException('RSA-SHA1 signature method requires the OpenSSL extension.');
  197.                 }
  198.                 if (=== strpos($clientSecret'-----BEGIN')) {
  199.                     $privateKey openssl_pkey_get_private($clientSecret$tokenSecret);
  200.                 } else {
  201.                     $privateKey openssl_pkey_get_private(file_get_contents($clientSecret), $tokenSecret);
  202.                 }
  203.                 $signature false;
  204.                 openssl_sign($baseString$signature$privateKey);
  205.                 openssl_free_key($privateKey);
  206.                 break;
  207.             case self::SIGNATURE_METHOD_PLAINTEXT:
  208.                 $signature $baseString;
  209.                 break;
  210.             default:
  211.                 throw new \RuntimeException(sprintf('Unknown signature method selected %s.'$signatureMethod));
  212.         }
  213.         return base64_encode($signature);
  214.     }
  215.     /**
  216.      * @param string $name
  217.      *
  218.      * @return ResourceOwnerInterface
  219.      *
  220.      * @throws \RuntimeException
  221.      */
  222.     protected function getResourceOwner($name)
  223.     {
  224.         foreach ($this->ownerMaps as $ownerMap) {
  225.             $resourceOwner $ownerMap->getResourceOwnerByName($name);
  226.             if ($resourceOwner instanceof ResourceOwnerInterface) {
  227.                 return $resourceOwner;
  228.             }
  229.         }
  230.         throw new \RuntimeException(sprintf("No resource owner with name '%s'."$name));
  231.     }
  232.     /**
  233.      * @param string $name
  234.      *
  235.      * @return string|null
  236.      */
  237.     protected function getResourceOwnerCheckPath($name)
  238.     {
  239.         foreach ($this->ownerMaps as $ownerMap) {
  240.             if ($potentialResourceOwnerCheckPath $ownerMap->getResourceOwnerCheckPath($name)) {
  241.                 return $potentialResourceOwnerCheckPath;
  242.             }
  243.         }
  244.         return null;
  245.     }
  246.     /**
  247.      * @param string|array<string, string>|null $queryParameter The query parameter to parse and add to the State
  248.      * @param ResourceOwnerInterface            $resourceOwner  The resource owner holding the state to be added to
  249.      */
  250.     private function addQueryParameterToState($queryParameterResourceOwnerInterface $resourceOwner): void
  251.     {
  252.         if (null === $queryParameter) {
  253.             return;
  254.         }
  255.         $additionalState = new State($queryParameter);
  256.         foreach ($additionalState->getAll() as $key => $value) {
  257.             $resourceOwner->addStateParameter($key$value);
  258.         }
  259.     }
  260. }