public function processRequest() { $time_start = microtime(true); $request = $this->getRequest(); $method = $this->method; $api_request = null; $method_implementation = null; $log = new PhabricatorConduitMethodCallLog(); $log->setMethod($method); $metadata = array(); $multimeter = MultimeterControl::getInstance(); if ($multimeter) { $multimeter->setEventContext('api.' . $method); } try { list($metadata, $params) = $this->decodeConduitParams($request, $method); $call = new ConduitCall($method, $params); $method_implementation = $call->getMethodImplementation(); $result = null; // TODO: The relationship between ConduitAPIRequest and ConduitCall is a // little odd here and could probably be improved. Specifically, the // APIRequest is a sub-object of the Call, which does not parallel the // role of AphrontRequest (which is an indepenent object). // In particular, the setUser() and getUser() existing independently on // the Call and APIRequest is very awkward. $api_request = $call->getAPIRequest(); $allow_unguarded_writes = false; $auth_error = null; $conduit_username = '******'; if ($call->shouldRequireAuthentication()) { $metadata['scope'] = $call->getRequiredScope(); $auth_error = $this->authenticateUser($api_request, $metadata); // If we've explicitly authenticated the user here and either done // CSRF validation or are using a non-web authentication mechanism. $allow_unguarded_writes = true; if ($auth_error === null) { $conduit_user = $api_request->getUser(); if ($conduit_user && $conduit_user->getPHID()) { $conduit_username = $conduit_user->getUsername(); } $call->setUser($api_request->getUser()); } } $access_log = PhabricatorAccessLog::getLog(); if ($access_log) { $access_log->setData(array('u' => $conduit_username, 'm' => $method)); } if ($call->shouldAllowUnguardedWrites()) { $allow_unguarded_writes = true; } if ($auth_error === null) { if ($allow_unguarded_writes) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); } try { $result = $call->execute(); $error_code = null; $error_info = null; } catch (ConduitException $ex) { $result = null; $error_code = $ex->getMessage(); if ($ex->getErrorDescription()) { $error_info = $ex->getErrorDescription(); } else { $error_info = $call->getErrorDescription($error_code); } } if ($allow_unguarded_writes) { unset($unguarded); } } else { list($error_code, $error_info) = $auth_error; } } catch (Exception $ex) { if (!$ex instanceof ConduitMethodNotFoundException) { phlog($ex); } $result = null; $error_code = $ex instanceof ConduitException ? 'ERR-CONDUIT-CALL' : 'ERR-CONDUIT-CORE'; $error_info = $ex->getMessage(); } $time_end = microtime(true); $connection_id = null; if (idx($metadata, 'connectionID')) { $connection_id = $metadata['connectionID']; } else { if ($method == 'conduit.connect' && $result) { $connection_id = idx($result, 'connectionID'); } } $log->setCallerPHID(isset($conduit_user) ? $conduit_user->getPHID() : null)->setConnectionID($connection_id)->setError((string) $error_code)->setDuration(1000000 * ($time_end - $time_start)); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $log->save(); unset($unguarded); $response = id(new ConduitAPIResponse())->setResult($result)->setErrorCode($error_code)->setErrorInfo($error_info); switch ($request->getStr('output')) { case 'human': return $this->buildHumanReadableResponse($method, $api_request, $response->toDictionary(), $method_implementation); case 'json': default: return id(new AphrontJSONResponse())->setAddJSONShield(false)->setContent($response->toDictionary()); } }
public function processRequest() { $time_start = microtime(true); $request = $this->getRequest(); $method = $this->method; $api_request = null; $log = new PhabricatorConduitMethodCallLog(); $log->setMethod($method); $metadata = array(); try { $params = $this->decodeConduitParams($request, $method); $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); $call = new ConduitCall($method, $params, idx($metadata, 'isProxied', false)); $result = null; // TODO: Straighten out the auth pathway here. We shouldn't be creating // a ConduitAPIRequest at this level, but some of the auth code expects // it. Landing a halfway version of this to unblock T945. $api_request = new ConduitAPIRequest($params); $allow_unguarded_writes = false; $auth_error = null; $conduit_username = '******'; if ($call->shouldRequireAuthentication()) { $metadata['scope'] = $call->getRequiredScope(); $auth_error = $this->authenticateUser($api_request, $metadata); // If we've explicitly authenticated the user here and either done // CSRF validation or are using a non-web authentication mechanism. $allow_unguarded_writes = true; if (isset($metadata['actAsUser'])) { $this->actAsUser($api_request, $metadata['actAsUser']); } if ($auth_error === null) { $conduit_user = $api_request->getUser(); if ($conduit_user && $conduit_user->getPHID()) { $conduit_username = $conduit_user->getUsername(); } $call->setUser($api_request->getUser()); } } $access_log = PhabricatorAccessLog::getLog(); if ($access_log) { $access_log->setData(array('u' => $conduit_username, 'm' => $method)); } if ($call->shouldAllowUnguardedWrites()) { $allow_unguarded_writes = true; } if ($auth_error === null) { if ($allow_unguarded_writes) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); } try { $result = $call->execute(); $error_code = null; $error_info = null; } catch (ConduitException $ex) { $result = null; $error_code = $ex->getMessage(); if ($ex->getErrorDescription()) { $error_info = $ex->getErrorDescription(); } else { $error_info = $call->getErrorDescription($error_code); } } if ($allow_unguarded_writes) { unset($unguarded); } } else { list($error_code, $error_info) = $auth_error; } } catch (Exception $ex) { if (!$ex instanceof ConduitMethodNotFoundException) { phlog($ex); } $result = null; $error_code = $ex instanceof ConduitException ? 'ERR-CONDUIT-CALL' : 'ERR-CONDUIT-CORE'; $error_info = $ex->getMessage(); } $time_end = microtime(true); $connection_id = null; if (idx($metadata, 'connectionID')) { $connection_id = $metadata['connectionID']; } else { if ($method == 'conduit.connect' && $result) { $connection_id = idx($result, 'connectionID'); } } $log->setCallerPHID(isset($conduit_user) ? $conduit_user->getPHID() : null)->setConnectionID($connection_id)->setError((string) $error_code)->setDuration(1000000 * ($time_end - $time_start)); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $log->save(); unset($unguarded); $response = id(new ConduitAPIResponse())->setResult($result)->setErrorCode($error_code)->setErrorInfo($error_info); switch ($request->getStr('output')) { case 'human': return $this->buildHumanReadableResponse($method, $api_request, $response->toDictionary()); case 'json': default: return id(new AphrontJSONResponse())->setAddJSONShield(false)->setContent($response->toDictionary()); } }