2011-01-24 18:00:29 +01:00
|
|
|
<?php
|
|
|
|
|
2011-07-04 21:03:36 +02:00
|
|
|
/**
|
|
|
|
* @group conduit
|
|
|
|
*/
|
2012-03-10 00:46:25 +01:00
|
|
|
final class PhabricatorConduitConsoleController
|
2011-01-24 18:00:29 +01:00
|
|
|
extends PhabricatorConduitController {
|
|
|
|
|
|
|
|
private $method;
|
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
2012-04-27 07:25:05 +02:00
|
|
|
$this->method = $data['method'];
|
2011-01-24 18:00:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
2011-01-31 03:52:29 +01:00
|
|
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
|
2011-01-24 18:00:29 +01:00
|
|
|
$methods = $this->getAllMethods();
|
|
|
|
if (empty($methods[$this->method])) {
|
2012-04-27 07:25:05 +02:00
|
|
|
return new Aphront404Response();
|
2011-01-24 18:00:29 +01:00
|
|
|
}
|
2012-01-26 21:47:23 +01:00
|
|
|
$this->setFilter('method/'.$this->method);
|
2011-01-24 18:00:29 +01:00
|
|
|
|
|
|
|
$method_class = $methods[$this->method];
|
|
|
|
$method_object = newv($method_class, array());
|
|
|
|
|
2012-04-18 23:25:27 +02:00
|
|
|
$status = $method_object->getMethodStatus();
|
|
|
|
$reason = $method_object->getMethodStatusDescription();
|
|
|
|
|
|
|
|
$status_view = null;
|
2012-04-27 07:25:05 +02:00
|
|
|
if ($status != ConduitAPIMethod::METHOD_STATUS_STABLE) {
|
2012-04-18 23:25:27 +02:00
|
|
|
$status_view = new AphrontErrorView();
|
|
|
|
switch ($status) {
|
|
|
|
case ConduitAPIMethod::METHOD_STATUS_DEPRECATED:
|
|
|
|
$status_view->setTitle('Deprecated Method');
|
|
|
|
$status_view->appendChild(
|
2013-02-07 01:53:49 +01:00
|
|
|
nonempty($reason, "This method is deprecated."));
|
2012-04-18 23:25:27 +02:00
|
|
|
break;
|
|
|
|
case ConduitAPIMethod::METHOD_STATUS_UNSTABLE:
|
|
|
|
$status_view->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
|
|
|
$status_view->setTitle('Unstable Method');
|
|
|
|
$status_view->appendChild(
|
2013-02-07 01:53:49 +01:00
|
|
|
nonempty(
|
|
|
|
$reason,
|
|
|
|
"This method is new and unstable. Its interface is subject ".
|
|
|
|
"to change."));
|
2012-04-18 23:25:27 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-01-24 18:00:29 +01:00
|
|
|
|
|
|
|
$error_types = $method_object->defineErrorTypes();
|
|
|
|
if ($error_types) {
|
2013-02-05 23:30:29 +01:00
|
|
|
$error_description = array();
|
2011-01-24 18:00:29 +01:00
|
|
|
foreach ($error_types as $error => $meaning) {
|
2013-02-05 21:49:46 +01:00
|
|
|
$error_description[] = hsprintf(
|
|
|
|
'<li><strong>%s:</strong> %s</li>',
|
|
|
|
$error,
|
|
|
|
$meaning);
|
2011-01-24 18:00:29 +01:00
|
|
|
}
|
2013-02-05 23:30:29 +01:00
|
|
|
$error_description = phutil_tag('ul', array(), $error_description);
|
2011-01-24 18:00:29 +01:00
|
|
|
} else {
|
|
|
|
$error_description = "This method does not raise any specific errors.";
|
|
|
|
}
|
|
|
|
|
|
|
|
$form = new AphrontFormView();
|
|
|
|
$form
|
2011-01-31 03:52:29 +01:00
|
|
|
->setUser($request->getUser())
|
2011-01-24 18:00:29 +01:00
|
|
|
->setAction('/api/'.$this->method)
|
Detect missing 'params' in Conduit calls
Summary:
Suhosin has about 50 options for filtering input variables, doucmented here:
http://www.hardened-php.net/suhosin/configuration.html
The default behavior of Suhosin is to drop the variable entirely if it violates any of the rules, then continue with the request. It doesn't affect 'php://input' and doesn't drop other variables, so it evades existing detection, and we can't figure out that it's happened at runtime. We could add blanket checks (Suhosin enabled + suhosin.filter.action set to nothing means this may happen, and will be undetectable if it does happen) but can't tailor a check or recovery to this specific problem.
Instead, raise a better error in the specific case where we encounter this, which is Conduit calls of "arc diff" of files over 1MB (the default POST limit). In these cases, Suhosin drops the variable entirely. If there is no 'params', scream. We never encounter this case normall (`arc`, including `arc call-conduit`, always sends this parameter) although other clients might omit it. The only exception is the web console with `conduit.ping`, which submits nothing; make it submit something so it keeps working.
See also https://github.com/facebook/phabricator/issues/233#issuecomment-11186074
Test Plan: Brought up a Debian + Suhosin box, verified the behavior of Suhosin, made requests with and without 'params'.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D4144
2012-12-11 23:01:18 +01:00
|
|
|
->addHiddenInput('allowEmptyParams', 1)
|
2011-01-24 18:00:29 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel('Description')
|
2011-01-24 20:36:53 +01:00
|
|
|
->setValue($method_object->getMethodDescription()))
|
2011-01-24 18:00:29 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel('Returns')
|
2011-01-24 20:36:53 +01:00
|
|
|
->setValue($method_object->defineReturnType()))
|
2011-01-24 18:00:29 +01:00
|
|
|
->appendChild(
|
2011-01-24 20:36:53 +01:00
|
|
|
id(new AphrontFormMarkupControl())
|
2011-01-24 18:00:29 +01:00
|
|
|
->setLabel('Errors')
|
|
|
|
->setValue($error_description))
|
2013-02-07 23:39:04 +01:00
|
|
|
->appendChild(hsprintf(
|
2011-01-24 18:00:29 +01:00
|
|
|
'<p class="aphront-form-instructions">Enter parameters using '.
|
|
|
|
'<strong>JSON</strong>. For instance, to enter a list, type: '.
|
2013-02-07 23:39:04 +01:00
|
|
|
'<tt>["apple", "banana", "cherry"]</tt>'));
|
2011-01-24 18:00:29 +01:00
|
|
|
|
|
|
|
$params = $method_object->defineParamTypes();
|
|
|
|
foreach ($params as $param => $desc) {
|
|
|
|
$form->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setLabel($param)
|
|
|
|
->setName("params[{$param}]")
|
2013-02-05 22:23:05 +01:00
|
|
|
->setCaption($desc));
|
2011-01-24 18:00:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
|
|
|
->setLabel('Output Format')
|
|
|
|
->setName('output')
|
|
|
|
->setOptions(
|
|
|
|
array(
|
|
|
|
'human' => 'Human Readable',
|
|
|
|
'json' => 'JSON',
|
|
|
|
)))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue('Call Method'));
|
|
|
|
|
|
|
|
$panel = new AphrontPanelView();
|
|
|
|
$panel->setHeader('Conduit API: '.phutil_escape_html($this->method));
|
|
|
|
$panel->appendChild($form);
|
2011-07-29 06:32:11 +02:00
|
|
|
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
|
2011-01-24 18:00:29 +01:00
|
|
|
|
|
|
|
return $this->buildStandardPageResponse(
|
2012-04-18 23:25:27 +02:00
|
|
|
array(
|
|
|
|
$status_view,
|
|
|
|
$panel,
|
|
|
|
),
|
2011-01-24 18:00:29 +01:00
|
|
|
array(
|
2012-04-27 07:25:05 +02:00
|
|
|
'title' => 'Conduit Console - '.$this->method,
|
2011-01-24 18:00:29 +01:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getAllMethods() {
|
|
|
|
$classes = $this->getAllMethodImplementationClasses();
|
|
|
|
$methods = array();
|
|
|
|
foreach ($classes as $class) {
|
|
|
|
$name = ConduitAPIMethod::getAPIMethodNameFromClassName($class);
|
|
|
|
$methods[$name] = $class;
|
|
|
|
}
|
|
|
|
return $methods;
|
|
|
|
}
|
|
|
|
}
|