2011-01-24 20:30:10 +01:00
|
|
|
<?php
|
|
|
|
|
Rename Conduit classes
Summary: Ref T5655. Rename Conduit classes and provide a `getAPIMethodName` method to declare the API method.
Test Plan:
```
> echo '{}' | arc --conduit-uri='http://phabricator.joshuaspence.com' call-conduit user.whoami
Waiting for JSON parameters on stdin...
{"error":null,"errorMessage":null,"response":{"phid":"PHID-USER-lioqffnwn6y475mu5ndb","userName":"josh","realName":"Joshua Spence","image":"http:\/\/phabricator.joshuaspence.com\/res\/1404425321T\/phabricator\/3eb28cd9\/rsrc\/image\/avatar.png","uri":"http:\/\/phabricator.joshuaspence.com\/p\/josh\/","roles":["admin","verified","approved","activated"]}}
```
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley, #blessed_reviewers
Subscribers: epriestley, Korvin, hach-que
Maniphest Tasks: T5655
Differential Revision: https://secure.phabricator.com/D9991
2014-07-25 02:54:15 +02:00
|
|
|
final class ConduitConnectConduitAPIMethod extends ConduitAPIMethod {
|
|
|
|
|
|
|
|
public function getAPIMethodName() {
|
|
|
|
return 'conduit.connect';
|
|
|
|
}
|
2011-01-24 20:30:10 +01:00
|
|
|
|
2011-02-06 07:36:21 +01:00
|
|
|
public function shouldRequireAuthentication() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Create AphrontWriteGuard, a backup mechanism for CSRF validation
Summary:
Provide a catchall mechanism to find unprotected writes.
- Depends on D758.
- Similar to WriteOnHTTPGet stuff from Facebook's stack.
- Since we have a small number of storage mechanisms and highly structured
read/write pathways, we can explicitly answer the question "is this page
performing a write?".
- Never allow writes without CSRF checks.
- This will probably break some things. That's fine: they're CSRF
vulnerabilities or weird edge cases that we can fix. But don't push to Facebook
for a few days unless you're prepared to deal with this.
- **>>> MEGADERP: All Conduit write APIs are currently vulnerable to CSRF!
<<<**
Test Plan:
- Ran some scripts that perform writes (scripts/search indexers), no issues.
- Performed normal CSRF submits.
- Added writes to an un-CSRF'd page, got an exception.
- Executed conduit methods.
- Did login/logout (this works because the logged-out user validates the
logged-out csrf "token").
- Did OAuth login.
- Did OAuth registration.
Reviewers: pedram, andrewjcg, erling, jungejason, tuomaspelkonen, aran,
codeblock
Commenters: pedram
CC: aran, epriestley, pedram
Differential Revision: 777
2011-08-03 20:49:27 +02:00
|
|
|
public function shouldAllowUnguardedWrites() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-24 20:30:10 +01:00
|
|
|
public function getMethodDescription() {
|
2014-06-09 20:36:49 +02:00
|
|
|
return 'Connect a session-based client.';
|
2011-01-24 20:30:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function defineParamTypes() {
|
|
|
|
return array(
|
|
|
|
'client' => 'required string',
|
|
|
|
'clientVersion' => 'required int',
|
|
|
|
'clientDescription' => 'optional string',
|
|
|
|
'user' => 'optional string',
|
2011-02-06 07:36:21 +01:00
|
|
|
'authToken' => 'optional int',
|
|
|
|
'authSignature' => 'optional string',
|
2014-06-24 02:41:02 +02:00
|
|
|
'host' => 'deprecated',
|
2011-01-24 20:30:10 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function defineReturnType() {
|
|
|
|
return 'dict<string, any>';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function defineErrorTypes() {
|
|
|
|
return array(
|
2014-06-09 20:36:49 +02:00
|
|
|
'ERR-BAD-VERSION' =>
|
|
|
|
'Client/server version mismatch. Upgrade your server or downgrade '.
|
|
|
|
'your client.',
|
|
|
|
'NEW-ARC-VERSION' =>
|
|
|
|
'Client/server version mismatch. Upgrade your client.',
|
|
|
|
'ERR-UNKNOWN-CLIENT' =>
|
|
|
|
'Client is unknown.',
|
|
|
|
'ERR-INVALID-USER' =>
|
|
|
|
'The username you are attempting to authenticate with is not valid.',
|
|
|
|
'ERR-INVALID-CERTIFICATE' =>
|
|
|
|
'Your authentication certificate for this server is invalid.',
|
|
|
|
'ERR-INVALID-TOKEN' =>
|
2011-02-06 07:36:21 +01:00
|
|
|
"The challenge token you are authenticating with is outside of the ".
|
|
|
|
"allowed time range. Either your system clock is out of whack or ".
|
|
|
|
"you're executing a replay attack.",
|
2014-06-09 20:36:49 +02:00
|
|
|
'ERR-NO-CERTIFICATE' => 'This server requires authentication.',
|
2011-01-24 20:30:10 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function execute(ConduitAPIRequest $request) {
|
|
|
|
$client = $request->getValue('client');
|
|
|
|
$client_version = (int)$request->getValue('clientVersion');
|
|
|
|
$client_description = (string)$request->getValue('clientDescription');
|
2014-08-30 00:15:13 +02:00
|
|
|
$client_description = id(new PhutilUTF8StringTruncator())
|
Fix string truncation calls all over the codebase.
Summary: Fixes T6608, though I'll also clean up the comment for PhutilStringTruncator in another diff. If I understand correctly, before T1191, MySQL column length was by character count and post T1191 its by byte count. Ergo, most of these changes are going from codepoint -> bytes. See test plan for complete list of what was and was not done.
Test Plan:
Thought very carefully about each callsite and made changes as appropos. "Display" means the string is clearly used for display-only purposes and correctly uses "glyph" already.
grep -rn PhutilUTF8StringTruncator *
applications/calendar/query/PhabricatorCalendarEventSearchEngine.php:217: ->addAttribute(id(new PhutilUTF8StringTruncator()) -- display
applications/chatlog/controller/PhabricatorChatLogChannelLogController.php:111: $author = id(new PhutilUTF8StringTruncator()) -- display
applications/conduit/method/ConduitConnectConduitAPIMethod.php:62: $client_description = id(new PhutilUTF8StringTruncator()) -- was codepoint, changed to bytes
applications/conpherence/view/ConpherenceFileWidgetView.php:22: ->setFileName(id(new PhutilUTF8StringTruncator()) -- display
applications/differential/controller/DifferentialDiffViewController.php:65: id(new PhutilUTF8StringTruncator()) -- display
applications/differential/event/DifferentialHovercardEventListener.php:69: id(new PhutilUTF8StringTruncator()) -- display
applications/differential/parser/DifferentialCommitMessageParser.php:144: $short = id(new PhutilUTF8StringTruncator()) -- was glyphs, made to bytes
applications/differential/view/DifferentialLocalCommitsView.php:80: $summary = id(new PhutilUTF8StringTruncator()) -- display
applications/diffusion/controller/DiffusionBrowseFileController.php:686: id(new PhutilUTF8StringTruncator()) -- display
applications/feed/story/PhabricatorFeedStory.php:392: $text = id(new PhutilUTF8StringTruncator()) -- display, unless people are saving the results of renderSummary() somewhere...
applications/harbormaster/storage/build/HarbormasterBuild.php:216: $log_source = id(new PhutilUTF8StringTruncator()) -- was codepoints now bytes
applications/herald/storage/transcript/HeraldObjectTranscript.php:55: // NOTE: PhutilUTF8StringTruncator has huge runtime for giant strings. -- not applicable
applications/maniphest/export/ManiphestExcelDefaultFormat.php:107: id(new PhutilUTF8StringTruncator()) -- bytes
applications/metamta/storage/PhabricatorMetaMTAMail.php:587: $body = id(new PhutilUTF8StringTruncator()) -- bytes
applications/people/event/PhabricatorPeopleHovercardEventListener.php:62: id(new PhutilUTF8StringTruncator()) -- display
applications/phame/conduit/PhameCreatePostConduitAPIMethod.php:93: id(new PhutilUTF8StringTruncator()) -- was codepoints, now bytes
applications/pholio/storage/PholioTransaction.php:300: id(new PhutilUTF8StringTruncator()) -- display
applications/phortune/provider/PhortuneBalancedPaymentProvider.php:147: $charge_as = id(new PhutilUTF8StringTruncator()) -- bytes
applications/ponder/storage/PonderAnswerTransaction.php:86: id(new PhutilUTF8StringTruncator()) -- display
applications/ponder/storage/PonderQuestionTransaction.php:267: id(new PhutilUTF8StringTruncator()) -- display
applications/ponder/storage/PonderQuestionTransaction.php:276: id(new PhutilUTF8StringTruncator()) -- display
applications/repository/storage/PhabricatorRepositoryCommitData.php:43: $summary = id(new PhutilUTF8StringTruncator()) -- was codepoints, now bytes
applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php:20: $data->setAuthorName(id(new PhutilUTF8StringTruncator()) -- was codepoints, now bytes
applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php:158: $item->addAttribute(id(new PhutilUTF8StringTruncator()) -- display
infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php:317: $host = id(new PhutilUTF8StringTruncator()) -- bytes
view/form/control/AphrontFormPolicyControl.php:61: $policy_short_name = id(new PhutilUTF8StringTruncator()) -- glyphs, probably display only
Reviewers: epriestley
Reviewed By: epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T6608
Differential Revision: https://secure.phabricator.com/D11219
2015-01-05 20:14:54 +01:00
|
|
|
->setMaximumBytes(255)
|
2014-08-30 00:15:13 +02:00
|
|
|
->truncateString($client_description);
|
2011-02-06 07:36:21 +01:00
|
|
|
$username = (string)$request->getValue('user');
|
2011-01-24 20:30:10 +01:00
|
|
|
|
|
|
|
// Log the connection, regardless of the outcome of checks below.
|
|
|
|
$connection = new PhabricatorConduitConnectionLog();
|
|
|
|
$connection->setClient($client);
|
|
|
|
$connection->setClientVersion($client_version);
|
|
|
|
$connection->setClientDescription($client_description);
|
2011-02-06 07:36:21 +01:00
|
|
|
$connection->setUsername($username);
|
2011-01-24 20:30:10 +01:00
|
|
|
$connection->save();
|
|
|
|
|
|
|
|
switch ($client) {
|
|
|
|
case 'arc':
|
2012-12-04 02:58:01 +01:00
|
|
|
$server_version = 6;
|
2012-05-22 01:43:43 +02:00
|
|
|
$supported_versions = array(
|
|
|
|
$server_version => true,
|
2012-12-04 02:58:01 +01:00
|
|
|
// Client version 5 introduced "user.query" call
|
2012-05-22 01:43:43 +02:00
|
|
|
4 => true,
|
2012-12-04 02:58:01 +01:00
|
|
|
// Client version 6 introduced "diffusion.getlintmessages" call
|
|
|
|
5 => true,
|
2012-05-22 01:43:43 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
if (empty($supported_versions[$client_version])) {
|
|
|
|
if ($server_version < $client_version) {
|
|
|
|
$ex = new ConduitException('ERR-BAD-VERSION');
|
|
|
|
$ex->setErrorDescription(
|
|
|
|
"Your 'arc' client version is '{$client_version}', which ".
|
|
|
|
"is newer than the server version, '{$server_version}'. ".
|
|
|
|
"Upgrade your Phabricator install.");
|
|
|
|
} else {
|
|
|
|
$ex = new ConduitException('NEW-ARC-VERSION');
|
|
|
|
$ex->setErrorDescription(
|
|
|
|
"A new version of arc is available! You need to upgrade ".
|
|
|
|
"to connect to this server (you are running version ".
|
|
|
|
"{$client_version}, the server is running version ".
|
|
|
|
"{$server_version}).");
|
|
|
|
}
|
|
|
|
throw $ex;
|
2011-01-24 20:30:10 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2011-04-14 03:56:32 +02:00
|
|
|
// Allow new clients by default.
|
|
|
|
break;
|
2011-01-24 20:30:10 +01:00
|
|
|
}
|
|
|
|
|
2011-02-06 07:36:21 +01:00
|
|
|
$token = $request->getValue('authToken');
|
|
|
|
$signature = $request->getValue('authSignature');
|
|
|
|
|
2014-07-10 00:12:48 +02:00
|
|
|
$user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username);
|
2011-02-06 07:36:21 +01:00
|
|
|
if (!$user) {
|
|
|
|
throw new ConduitException('ERR-INVALID-USER');
|
|
|
|
}
|
|
|
|
|
|
|
|
$session_key = null;
|
|
|
|
if ($token && $signature) {
|
2013-08-02 16:38:59 +02:00
|
|
|
$threshold = 60 * 15;
|
|
|
|
$now = time();
|
|
|
|
if (abs($token - $now) > $threshold) {
|
|
|
|
throw id(new ConduitException('ERR-INVALID-TOKEN'))
|
|
|
|
->setErrorDescription(
|
|
|
|
pht(
|
2014-06-09 20:36:49 +02:00
|
|
|
'The request you submitted is signed with a timestamp, but that '.
|
|
|
|
'timestamp is not within %s of the current time. The '.
|
|
|
|
'signed timestamp is %s (%s), and the current server time is '.
|
|
|
|
'%s (%s). This is a difference of %s seconds, but the '.
|
|
|
|
'timestamp must differ from the server time by no more than '.
|
|
|
|
'%s seconds. Your client or server clock may not be set '.
|
|
|
|
'correctly.',
|
2014-07-13 04:03:17 +02:00
|
|
|
phutil_format_relative_time($threshold),
|
2013-08-02 16:38:59 +02:00
|
|
|
$token,
|
|
|
|
date('r', $token),
|
|
|
|
$now,
|
|
|
|
date('r', $now),
|
|
|
|
($token - $now),
|
|
|
|
$threshold));
|
2011-02-06 07:36:21 +01:00
|
|
|
}
|
|
|
|
$valid = sha1($token.$user->getConduitCertificate());
|
|
|
|
if ($valid != $signature) {
|
|
|
|
throw new ConduitException('ERR-INVALID-CERTIFICATE');
|
|
|
|
}
|
2014-05-01 19:23:02 +02:00
|
|
|
$session_key = id(new PhabricatorAuthSessionEngine())->establishSession(
|
|
|
|
PhabricatorAuthSession::TYPE_CONDUIT,
|
|
|
|
$user->getPHID(),
|
|
|
|
$partial = false);
|
2011-02-06 07:36:21 +01:00
|
|
|
} else {
|
|
|
|
throw new ConduitException('ERR-NO-CERTIFICATE');
|
|
|
|
}
|
|
|
|
|
2011-01-24 20:30:10 +01:00
|
|
|
return array(
|
2011-02-06 07:36:21 +01:00
|
|
|
'connectionID' => $connection->getID(),
|
|
|
|
'sessionKey' => $session_key,
|
|
|
|
'userPHID' => $user->getPHID(),
|
2011-01-24 20:30:10 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|