1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 08:52:39 +01:00

Provide $request->getUser() for Conduit API Requests

Summary:
Allow Conduit methods to retrieve the authoritative, logged-in user
identity.

Test Plan:
Ran user.whoami (an authenticated method) and got my info back. Ran
conduit.connect (an unauthenticated method) and the world did not explode.

Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen
CC: tuomaspelkonen, epriestley
Differential Revision: 113
This commit is contained in:
epriestley 2011-04-07 18:27:39 -07:00
parent a6b873692f
commit 5e9d053f8e
5 changed files with 155 additions and 34 deletions

View file

@ -87,6 +87,7 @@ phutil_register_library_map(array(
'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision', 'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision',
'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload', 'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find', 'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
'ConduitAPI_user_whoami_Method' => 'applications/conduit/method/user/whoami',
'ConduitException' => 'applications/conduit/protocol/exception', 'ConduitException' => 'applications/conduit/protocol/exception',
'DarkConsole' => 'aphront/console/api', 'DarkConsole' => 'aphront/console/api',
'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config', 'DarkConsoleConfigPlugin' => 'aphront/console/plugin/config',
@ -526,6 +527,7 @@ phutil_register_library_map(array(
'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod', 'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
'ConduitAPI_user_find_Method' => 'ConduitAPIMethod', 'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
'ConduitAPI_user_whoami_Method' => 'ConduitAPIMethod',
'DarkConsoleConfigPlugin' => 'DarkConsolePlugin', 'DarkConsoleConfigPlugin' => 'DarkConsolePlugin',
'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleController' => 'PhabricatorController',
'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',

View file

@ -79,44 +79,16 @@ class PhabricatorConduitAPIController
$metadata = idx($params, '__conduit__', array()); $metadata = idx($params, '__conduit__', array());
unset($params['__conduit__']); unset($params['__conduit__']);
$result = null;
$api_request = new ConduitAPIRequest($params); $api_request = new ConduitAPIRequest($params);
if ($request->getUser()->getPHID()) { $auth_error = null;
$auth_okay = true; if ($method_handler->shouldRequireAuthentication()) {
} else if (!$method_handler->shouldRequireAuthentication()) { $auth_error = $this->authenticateUser($api_request, $metadata);
$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.
} }
if ($auth_okay) { if ($auth_error === null) {
try { try {
$result = $method_handler->executeMethod($api_request); $result = $method_handler->executeMethod($api_request);
$error_code = null; $error_code = null;
@ -126,6 +98,8 @@ class PhabricatorConduitAPIController
$error_code = $ex->getMessage(); $error_code = $ex->getMessage();
$error_info = $method_handler->getErrorDescription($error_code); $error_info = $method_handler->getErrorDescription($error_code);
} }
} else {
list($error_code, $error_info) = $auth_error;
} }
} catch (Exception $ex) { } catch (Exception $ex) {
$result = null; $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 '.
'<http://www.phabricator.com/docs/phabricator/article/'.
'Installing_Arcanist_Certificates.html> 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( private function buildHumanReadableResponse(
$method, $method,
ConduitAPIRequest $request = null, ConduitAPIRequest $request = null,

View file

@ -0,0 +1,48 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ConduitAPI_user_whoami_Method extends ConduitAPIMethod {
public function getMethodDescription() {
return "Retrieve information about the logged-in user.";
}
public function defineParamTypes() {
return array(
);
}
public function defineReturnType() {
return 'nonempty dict<string, wild>';
}
public function defineErrorTypes() {
return array(
);
}
protected function execute(ConduitAPIRequest $request) {
$user = $request->getUser();
return array(
'phid' => $user->getPHID(),
'userName' => $user->getUserName(),
'realName' => $user->getRealName(),
);
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/conduit/method/base');
phutil_require_source('ConduitAPI_user_whoami_Method.php');

View file

@ -19,6 +19,7 @@
class ConduitAPIRequest { class ConduitAPIRequest {
protected $params; protected $params;
private $user;
public function __construct(array $params) { public function __construct(array $params) {
$this->params = $params; $this->params = $params;
@ -32,4 +33,29 @@ class ConduitAPIRequest {
return $this->params; 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;
}
} }