3 final class ConduitConnectConduitAPIMethod
extends ConduitAPIMethod
{
5 public function getAPIMethodName() {
6 return 'conduit.connect';
9 public function shouldRequireAuthentication() {
13 public function shouldAllowUnguardedWrites() {
17 public function getMethodDescription() {
18 return pht('Connect a session-based client.');
21 protected function defineParamTypes() {
23 'client' => 'required string',
24 'clientVersion' => 'required int',
25 'clientDescription' => 'optional string',
26 'user' => 'optional string',
27 'authToken' => 'optional int',
28 'authSignature' => 'optional string',
29 'host' => 'deprecated',
33 protected function defineReturnType() {
34 return 'dict<string, any>';
37 protected function defineErrorTypes() {
39 'ERR-BAD-VERSION' => pht(
40 'Client/server version mismatch. Upgrade your server or downgrade '.
42 'NEW-ARC-VERSION' => pht(
43 'Client/server version mismatch. Upgrade your client.'),
44 'ERR-UNKNOWN-CLIENT' => pht('Client is unknown.'),
45 'ERR-INVALID-USER' => pht(
46 'The username you are attempting to authenticate with is not valid.'),
47 'ERR-INVALID-CERTIFICATE' => pht(
48 'Your authentication certificate for this server is invalid.'),
49 'ERR-INVALID-TOKEN' => pht(
50 "The challenge token you are authenticating with is outside of the ".
51 "allowed time range. Either your system clock is out of whack or ".
52 "you're executing a replay attack."),
53 'ERR-NO-CERTIFICATE' => pht('This server requires authentication.'),
57 protected function execute(ConduitAPIRequest
$request) {
58 $client = $request->getValue('client');
59 $client_version = (int)$request->getValue('clientVersion');
60 $client_description = (string)$request->getValue('clientDescription');
61 $client_description = id(new PhutilUTF8StringTruncator())
62 ->setMaximumBytes(255)
63 ->truncateString($client_description);
64 $username = (string)$request->getValue('user');
69 $supported_versions = array(
70 $server_version => true,
71 // Client version 5 introduced "user.query" call
73 // Client version 6 introduced "diffusion.getlintmessages" call
77 if (empty($supported_versions[$client_version])) {
78 if ($server_version < $client_version) {
79 $ex = new ConduitException('ERR-BAD-VERSION');
80 $ex->setErrorDescription(
82 "Your '%s' client version is '%d', which is newer than the ".
83 "server version, '%d'. Upgrade your Phabricator install.",
88 $ex = new ConduitException('NEW-ARC-VERSION');
89 $ex->setErrorDescription(
91 'A new version of arc is available! You need to upgrade '.
92 'to connect to this server (you are running version '.
93 '%d, the server is running version %d).',
101 // Allow new clients by default.
105 $token = $request->getValue('authToken');
106 $signature = $request->getValue('authSignature');
108 $user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username);
110 throw new ConduitException('ERR-INVALID-USER');
114 if ($token && $signature) {
115 $threshold = 60 * 15;
117 if (abs($token - $now) > $threshold) {
118 throw id(new ConduitException('ERR-INVALID-TOKEN'))
119 ->setErrorDescription(
121 'The request you submitted is signed with a timestamp, but that '.
122 'timestamp is not within %s of the current time. The '.
123 'signed timestamp is %s (%s), and the current server time is '.
124 '%s (%s). This is a difference of %s seconds, but the '.
125 'timestamp must differ from the server time by no more than '.
126 '%s seconds. Your client or server clock may not be set '.
128 phutil_format_relative_time($threshold),
136 $valid = sha1($token.$user->getConduitCertificate());
137 if (!phutil_hashes_are_identical($valid, $signature)) {
138 throw new ConduitException('ERR-INVALID-CERTIFICATE');
140 $session_key = id(new PhabricatorAuthSessionEngine())->establishSession(
141 PhabricatorAuthSession
::TYPE_CONDUIT
,
145 throw new ConduitException('ERR-NO-CERTIFICATE');
149 'connectionID' => mt_rand(),
150 'sessionKey' => $session_key,
151 'userPHID' => $user->getPHID(),