diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 3ad9d4c2c8..1dbfa2f25f 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -87,6 +87,7 @@ phutil_register_library_map(array( 'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision', 'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload', 'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find', + 'ConduitAPI_user_whoami_Method' => 'applications/conduit/method/user/whoami', 'ConduitException' => 'applications/conduit/protocol/exception', 'DarkConsole' => 'aphront/console/api', 'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config', @@ -526,6 +527,7 @@ phutil_register_library_map(array( 'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod', 'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod', 'ConduitAPI_user_find_Method' => 'ConduitAPIMethod', + 'ConduitAPI_user_whoami_Method' => 'ConduitAPIMethod', 'DarkConsoleConfigPlugin' => 'DarkConsolePlugin', 'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', diff --git a/src/applications/conduit/controller/api/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/api/PhabricatorConduitAPIController.php index c68a4932d5..34f996f630 100644 --- a/src/applications/conduit/controller/api/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/api/PhabricatorConduitAPIController.php @@ -79,44 +79,16 @@ class PhabricatorConduitAPIController $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); + $result = null; + $api_request = new ConduitAPIRequest($params); - if ($request->getUser()->getPHID()) { - $auth_okay = true; - } else if (!$method_handler->shouldRequireAuthentication()) { - $auth_okay = true; - } else { - $session_key = idx($metadata, 'sessionKey'); - if (!$session_key) { - $auth_okay = false; - $error_code = 'ERR-NO-CERTIFICATE'; - $error_info = "This server requires authentication but your client ". - "is not configured with an authentication ". - "certificate. Please refer to ". - "page http://www.phabricator.com/docs/". - "phabricator/article/". - "Installing_Arcanist_Certificates.html for more info."; - } else { - $user = new PhabricatorUser(); - $session = queryfx_one( - $user->establishConnection('r'), - 'SELECT * FROM %T WHERE sessionKey = %s', - PhabricatorUser::SESSION_TABLE, - $session_key); - if (!$session) { - $auth_okay = false; - $result = null; - $error_code = 'ERR-INVALID-SESSION'; - $error_info = 'Session key is invalid.'; - } else { - // TODO: Make sessions timeout. - $auth_okay = true; - } - } - // TODO: When we session, read connectionID from the session table. + $auth_error = null; + if ($method_handler->shouldRequireAuthentication()) { + $auth_error = $this->authenticateUser($api_request, $metadata); } - if ($auth_okay) { + if ($auth_error === null) { try { $result = $method_handler->executeMethod($api_request); $error_code = null; @@ -126,6 +98,8 @@ class PhabricatorConduitAPIController $error_code = $ex->getMessage(); $error_info = $method_handler->getErrorDescription($error_code); } + } else { + list($error_code, $error_info) = $auth_error; } } catch (Exception $ex) { $result = null; @@ -167,6 +141,65 @@ class PhabricatorConduitAPIController } } + /** + * Authenticate the client making the request to a Phabricator user account. + * + * @param ConduitAPIRequest Request being executed. + * @param dict Request metadata. + * @return null|pair Null to indicate successful authentication, or + * an error code and error message pair. + */ + private function authenticateUser( + ConduitAPIRequest $api_request, + array $metadata) { + + $request = $this->getRequest(); + + if ($request->getUser()->getPHID()) { + $api_request->setUser($request->getUser()); + return null; + } + + $session_key = idx($metadata, 'sessionKey'); + if (!$session_key) { + return array( + 'ERR-NO-CERTIFICATE', + 'This server requires authentication but your client is not '. + 'configured with an authentication certificate. Please refer to '. + ' for more info.', + ); + } + + $session = queryfx_one( + id(new PhabricatorUser())->establishConnection('r'), + 'SELECT * FROM %T WHERE sessionKey = %s', + PhabricatorUser::SESSION_TABLE, + $session_key); + if (!$session) { + return array( + 'ERR-INVALID-SESSION', + 'Session key is invalid.', + ); + } + + // TODO: Make sessions timeout. + // TODO: When we pull a session, read connectionID from the session table. + + $user = id(new PhabricatorUser())->loadOneWhere( + 'phid = %s', + $session['userPHID']); + if (!$user) { + return array( + 'ERR-INVALID-SESSION', + 'Session is for nonexistent user.', + ); + } + + $api_request->setUser($user); + return null; + } + private function buildHumanReadableResponse( $method, ConduitAPIRequest $request = null, diff --git a/src/applications/conduit/method/user/whoami/ConduitAPI_user_whoami_Method.php b/src/applications/conduit/method/user/whoami/ConduitAPI_user_whoami_Method.php new file mode 100644 index 0000000000..f2feb82856 --- /dev/null +++ b/src/applications/conduit/method/user/whoami/ConduitAPI_user_whoami_Method.php @@ -0,0 +1,48 @@ +'; + } + + public function defineErrorTypes() { + return array( + ); + } + + protected function execute(ConduitAPIRequest $request) { + $user = $request->getUser(); + return array( + 'phid' => $user->getPHID(), + 'userName' => $user->getUserName(), + 'realName' => $user->getRealName(), + ); + } + +} diff --git a/src/applications/conduit/method/user/whoami/__init__.php b/src/applications/conduit/method/user/whoami/__init__.php new file mode 100644 index 0000000000..5b9ccec4ea --- /dev/null +++ b/src/applications/conduit/method/user/whoami/__init__.php @@ -0,0 +1,12 @@ +params = $params; @@ -32,4 +33,29 @@ class ConduitAPIRequest { return $this->params; } + public function setUser($user) { + $this->user = $user; + return $this; + } + + /** + * Retrieve the authentic identity of the user making the request. If a + * method requires authentication (the default) the user object will always + * be available. If a method does not require authentication (i.e., overrides + * shouldRequireAuthentication() to return false) the user object will NEVER + * be available. + * + * @return PhabricatorUser Authentic user, available ONLY if the method + * requires authentication. + */ + public function getUser() { + if (!$this->user) { + throw new Exception( + "You can not access the user inside the implementation of a Conduit ". + "method which does not require authentication (as per ". + "shouldRequireAuthentication())."); + } + return $this->user; + } + }