@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class ConduitConnectConduitAPIMethod extends ConduitAPIMethod {
4
5 public function getAPIMethodName() {
6 return 'conduit.connect';
7 }
8
9 public function shouldRequireAuthentication() {
10 return false;
11 }
12
13 public function shouldAllowUnguardedWrites() {
14 return true;
15 }
16
17 public function getMethodStatus() {
18 return self::METHOD_STATUS_DEPRECATED;
19 }
20
21 public function getMethodStatusDescription() {
22 return pht(
23 'This method has been deprecated since %s in favor of token-based '.
24 'authentication.',
25 '02/2026');
26 }
27
28 public function getMethodDescription() {
29 return pht('Connect a session-based client.');
30 }
31
32 protected function defineParamTypes() {
33 return array(
34 'client' => 'required string',
35 'clientVersion' => 'required int',
36 'clientDescription' => 'optional string',
37 'user' => 'optional string',
38 'authToken' => 'optional int',
39 'authSignature' => 'optional string',
40 'host' => 'deprecated',
41 );
42 }
43
44 protected function defineReturnType() {
45 return 'dict<string, any>';
46 }
47
48 protected function defineErrorTypes() {
49 return array(
50 'ERR-BAD-VERSION' => pht(
51 'Client/server version mismatch. Upgrade your server or downgrade '.
52 'your client.'),
53 'NEW-ARC-VERSION' => pht(
54 'Client/server version mismatch. Upgrade your client.'),
55 'ERR-UNKNOWN-CLIENT' => pht('Client is unknown.'),
56 'ERR-INVALID-USER' => pht(
57 'The username you are attempting to authenticate with is not valid.'),
58 'ERR-INVALID-CERTIFICATE' => pht(
59 'Your authentication certificate for this server is invalid.'),
60 'ERR-INVALID-TOKEN' => pht(
61 "The challenge token you are authenticating with is outside of the ".
62 "allowed time range. Either your system clock is out of whack or ".
63 "you're executing a replay attack."),
64 'ERR-NO-CERTIFICATE' => pht('This server requires authentication.'),
65 );
66 }
67
68 protected function execute(ConduitAPIRequest $request) {
69 $client = $request->getValue('client');
70 $client_version = (int)$request->getValue('clientVersion');
71 $client_description = (string)$request->getValue('clientDescription');
72 $client_description = id(new PhutilUTF8StringTruncator())
73 ->setMaximumBytes(255)
74 ->truncateString($client_description);
75 $username = (string)$request->getValue('user');
76
77 switch ($client) {
78 case 'arc':
79 $server_version = 6;
80 $supported_versions = array(
81 $server_version => true,
82 // Client version 5 introduced "user.query" call
83 4 => true,
84 // Client version 6 introduced "diffusion.getlintmessages" call
85 5 => true,
86 );
87
88 if (empty($supported_versions[$client_version])) {
89 if ($server_version < $client_version) {
90 $ex = new ConduitException('ERR-BAD-VERSION');
91 $ex->setErrorDescription(
92 pht(
93 "Your '%s' client version is '%d', which is newer than the ".
94 "server version, '%d'. Upgrade your server.",
95 'arc',
96 $client_version,
97 $server_version));
98 } else {
99 $ex = new ConduitException('NEW-ARC-VERSION');
100 $ex->setErrorDescription(
101 pht(
102 'A new version of arc is available! You need to upgrade '.
103 'to connect to this server (you are running version '.
104 '%d, the server is running version %d).',
105 $client_version,
106 $server_version));
107 }
108 throw $ex;
109 }
110 break;
111 default:
112 // Allow new clients by default.
113 break;
114 }
115
116 $token = $request->getValue('authToken');
117 $signature = $request->getValue('authSignature');
118
119 $user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username);
120 if (!$user) {
121 throw new ConduitException('ERR-INVALID-USER');
122 }
123
124 $session_key = null;
125 if ($token && $signature) {
126 $threshold = 60 * 15;
127 $now = time();
128 if (abs($token - $now) > $threshold) {
129 throw id(new ConduitException('ERR-INVALID-TOKEN'))
130 ->setErrorDescription(
131 pht(
132 'The request you submitted is signed with a timestamp, but that '.
133 'timestamp is not within %s of the current time. The '.
134 'signed timestamp is %s (%s), and the current server time is '.
135 '%s (%s). This is a difference of %s seconds, but the '.
136 'timestamp must differ from the server time by no more than '.
137 '%s seconds. Your client or server clock may not be set '.
138 'correctly.',
139 phutil_format_relative_time($threshold),
140 $token,
141 date('r', $token),
142 $now,
143 date('r', $now),
144 ($token - $now),
145 $threshold));
146 }
147 $valid = sha1($token.$user->getConduitCertificate());
148 if (!phutil_hashes_are_identical($valid, $signature)) {
149 throw new ConduitException('ERR-INVALID-CERTIFICATE');
150 }
151 $session_key = id(new PhabricatorAuthSessionEngine())->establishSession(
152 PhabricatorAuthSession::TYPE_CONDUIT,
153 $user->getPHID(),
154 $partial = false);
155 } else {
156 throw new ConduitException('ERR-NO-CERTIFICATE');
157 }
158
159 return array(
160 'connectionID' => mt_rand(),
161 'sessionKey' => $session_key,
162 'userPHID' => $user->getPHID(),
163 );
164 }
165
166}