diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2983,6 +2983,7 @@ 'PhabricatorDraftEngine' => 'applications/transactions/draft/PhabricatorDraftEngine.php', 'PhabricatorDraftInterface' => 'applications/transactions/draft/PhabricatorDraftInterface.php', 'PhabricatorDrydockApplication' => 'applications/drydock/application/PhabricatorDrydockApplication.php', + 'PhabricatorDuoFuture' => 'applications/auth/future/PhabricatorDuoFuture.php', 'PhabricatorEdgeChangeRecord' => 'infrastructure/edges/util/PhabricatorEdgeChangeRecord.php', 'PhabricatorEdgeChangeRecordTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeChangeRecordTestCase.php', 'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/PhabricatorEdgeConfig.php', @@ -8829,6 +8830,7 @@ 'PhabricatorDraftDAO' => 'PhabricatorLiskDAO', 'PhabricatorDraftEngine' => 'Phobject', 'PhabricatorDrydockApplication' => 'PhabricatorApplication', + 'PhabricatorDuoFuture' => 'FutureProxy', 'PhabricatorEdgeChangeRecord' => 'Phobject', 'PhabricatorEdgeChangeRecordTestCase' => 'PhabricatorTestCase', 'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants', diff --git a/src/applications/auth/future/PhabricatorDuoFuture.php b/src/applications/auth/future/PhabricatorDuoFuture.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/future/PhabricatorDuoFuture.php @@ -0,0 +1,150 @@ +integrationKey = $integration_key; + return $this; + } + + public function setSecretKey(PhutilOpaqueEnvelope $key) { + $this->secretKey = $key; + return $this; + } + + public function setAPIHostname($hostname) { + $this->apiHostname = $hostname; + return $this; + } + + public function setMethod($method, array $parameters) { + $this->method = $method; + $this->parameters = $parameters; + return $this; + } + + public function setTimeout($timeout) { + $this->timeout = $timeout; + return $this; + } + + public function getTimeout() { + return $this->timeout; + } + + public function setHTTPMethod($method) { + $this->httpMethod = $method; + return $this; + } + + public function getHTTPMethod() { + return $this->httpMethod; + } + + protected function getProxiedFuture() { + if (!$this->future) { + if ($this->integrationKey === null) { + throw new PhutilInvalidStateException('setIntegrationKey'); + } + + if ($this->secretKey === null) { + throw new PhutilInvalidStateException('setSecretKey'); + } + + if ($this->apiHostname === null) { + throw new PhutilInvalidStateException('setAPIHostname'); + } + + if ($this->method === null || $this->parameters === null) { + throw new PhutilInvalidStateException('setMethod'); + } + + $path = (string)urisprintf('/auth/v2/%s', $this->method); + + $host = $this->apiHostname; + $host = phutil_utf8_strtolower($host); + + $uri = id(new PhutilURI('')) + ->setProtocol('https') + ->setDomain($host) + ->setPath($path); + + $data = $this->parameters; + $date = date('r'); + + $http_method = $this->getHTTPMethod(); + + ksort($data); + $data_parts = array(); + foreach ($data as $key => $value) { + $data_parts[] = rawurlencode($key).'='.rawurlencode($value); + } + $data_parts = implode('&', $data_parts); + + $corpus = array( + $date, + $http_method, + $host, + $path, + $data_parts, + ); + $corpus = implode("\n", $corpus); + + $signature = hash_hmac( + 'sha1', + $corpus, + $this->secretKey->openEnvelope()); + $signature = new PhutilOpaqueEnvelope($signature); + + $future = id(new HTTPSFuture($uri, $data)) + ->setHTTPBasicAuthCredentials($this->integrationKey, $signature) + ->setMethod($http_method) + ->addHeader('Accept', 'application/json') + ->addHeader('Date', $date); + + $timeout = $this->getTimeout(); + if ($timeout) { + $future->setTimeout($timeout); + } + + $this->future = $future; + } + + return $this->future; + } + + protected function didReceiveResult($result) { + list($status, $body, $headers) = $result; + + if ($status->isError()) { + throw $status; + } + + try { + $data = phutil_json_decode($body); + } catch (PhutilJSONParserException $ex) { + throw new PhutilProxyException( + pht('Expected JSON response from Duo.'), + $ex); + } + + return $data; + } + +} diff --git a/src/applications/metamta/future/PhabricatorTwilioFuture.php b/src/applications/metamta/future/PhabricatorTwilioFuture.php --- a/src/applications/metamta/future/PhabricatorTwilioFuture.php +++ b/src/applications/metamta/future/PhabricatorTwilioFuture.php @@ -58,7 +58,7 @@ $this->accountSID, $this->method); - $uri = id(new PhutilURI('https://api.twilio.com/2010-04-01/accounts/')) + $uri = id(new PhutilURI('https://api.twilio.com/')) ->setPath($path); $data = $this->parameters;